Line data Source code
1 : // Copyright The OpenTelemetry Authors
2 : // SPDX-License-Identifier: Apache-2.0
3 :
4 : /// Callback that transforms a raw exception into sanitized span data.
5 : ///
6 : /// Used by [SpanExceptionOptions.exceptionSanitizer] to control exactly what
7 : /// exception information is recorded on a span, for example to strip PII,
8 : /// tokens, request URLs, or user IDs from an error message before it is
9 : /// recorded.
10 : typedef ExceptionSanitizer = SanitizedSpanException Function(
11 : Object error,
12 : StackTrace stackTrace,
13 : );
14 :
15 : /// Controls how exceptions thrown from the function passed to
16 : /// [Tracer.withSpan] / [Tracer.withSpanAsync] (and the convenience methods
17 : /// that route through them) are recorded.
18 : ///
19 : /// Can be configured globally via `OTel.initialize(spanExceptionOptions: ...)`
20 : /// (or per [TracerProvider] via [TracerProvider.spanExceptionOptions]) and
21 : /// overridden per call via the `exceptionOptions` parameter of the withSpan
22 : /// family. Per-call options are merged field-by-field over the global
23 : /// configuration (see [mergeWith]), so overriding a single flag preserves the
24 : /// globally configured sanitizer.
25 : ///
26 : /// The defaults preserve the library's historical behavior: a thrown
27 : /// exception is recorded on the span and the span status is set to
28 : /// [SpanStatusCode.Error]. The original exception is always rethrown
29 : /// regardless of these options.
30 : ///
31 : /// Use this to opt out of automatic recording or status updates, or to
32 : /// sanitize exception details via [exceptionSanitizer].
33 : ///
34 : /// Example:
35 : /// ```dart
36 : /// await tracer.withSpanAsync(
37 : /// span,
38 : /// () async => await doWork(),
39 : /// exceptionOptions: SpanExceptionOptions(
40 : /// exceptionSanitizer: (error, stackTrace) {
41 : /// return SanitizedSpanException(
42 : /// type: error.runtimeType.toString(),
43 : /// message: redact(error.toString()),
44 : /// stackTrace: sanitizeStackTrace(stackTrace),
45 : /// statusDescription: 'sanitized exception',
46 : /// );
47 : /// },
48 : /// ),
49 : /// );
50 : /// ```
51 : class SpanExceptionOptions {
52 : /// Creates span exception options.
53 : ///
54 : /// Omitted parameters inherit from global configuration when these options
55 : /// are used as a per-call override (see [mergeWith]). Without global
56 : /// configuration, omitted parameters default to the SDK's standard
57 : /// behavior:
58 : /// - [recordException]: `true` — auto-record exceptions on the span
59 : /// - [setStatusOnException]: `true` — auto-set span status to error
60 : /// - [exceptionSanitizer]: `null` — record the raw exception as-is
61 141 : const SpanExceptionOptions({
62 : bool? recordException,
63 : bool? setStatusOnException,
64 : this.exceptionSanitizer,
65 : }) : _recordException = recordException,
66 : _setStatusOnException = setStatusOnException;
67 :
68 : /// The SDK default span exception behavior: record the exception and set
69 : /// the span status to error.
70 : static const SpanExceptionOptions defaults = SpanExceptionOptions(
71 : recordException: true,
72 : setStatusOnException: true,
73 : );
74 :
75 : final bool? _recordException;
76 : final bool? _setStatusOnException;
77 :
78 : /// Whether the SDK should automatically record the exception on the span.
79 : ///
80 : /// When `true` (default), the SDK records an `exception` event on the span.
81 : /// When `false`, the SDK skips automatic exception recording.
82 10 : bool get recordException => _recordException ?? true;
83 :
84 : /// Whether the SDK should automatically set the span status to
85 : /// [SpanStatusCode.Error] when the wrapped function throws.
86 : ///
87 : /// When `true` (the default) the status is set; when `false` it is left
88 : /// untouched.
89 10 : bool get setStatusOnException => _setStatusOnException ?? true;
90 :
91 : /// Optional callback to sanitize exception data before it is recorded.
92 : ///
93 : /// When provided, the SDK uses the returned [SanitizedSpanException] to
94 : /// record the exception instead of the raw error object — the original
95 : /// exception's type, message, and stack trace are never recorded, so
96 : /// unsanitized data cannot leak. This is useful for removing PII or other
97 : /// sensitive data from error messages.
98 : ///
99 : /// The sanitizer is only invoked when [recordException] or
100 : /// [setStatusOnException] is `true`. If the sanitizer itself throws, the
101 : /// original exception is not recorded and the span is marked with
102 : /// [SpanStatusCode.Error] using a generic description.
103 : final ExceptionSanitizer? exceptionSanitizer;
104 :
105 : /// Returns a new options object with [overrides] applied field-by-field.
106 : ///
107 : /// This allows a per-call override like
108 : /// `SpanExceptionOptions(recordException: false)` to keep a globally
109 : /// configured [exceptionSanitizer] while changing only one flag.
110 : ///
111 : /// Note: passing `exceptionSanitizer: null` in [overrides] is
112 : /// indistinguishable from not setting it — to intentionally clear a global
113 : /// sanitizer, use separate configuration.
114 10 : SpanExceptionOptions mergeWith(SpanExceptionOptions? overrides) {
115 : if (overrides == null) {
116 : return this;
117 : }
118 1 : return SpanExceptionOptions(
119 2 : recordException: overrides._recordException ?? _recordException,
120 : setStatusOnException:
121 2 : overrides._setStatusOnException ?? _setStatusOnException,
122 2 : exceptionSanitizer: overrides.exceptionSanitizer ?? exceptionSanitizer,
123 : );
124 : }
125 : }
126 :
127 : /// Sanitized exception data to record on a span.
128 : ///
129 : /// Returned by an [ExceptionSanitizer] to control exactly what gets recorded
130 : /// as exception attributes on the span. The fields map directly to the
131 : /// OpenTelemetry exception semantic conventions:
132 : ///
133 : /// - [type] -> `exception.type`
134 : /// - [message] -> `exception.message`
135 : /// - [stackTrace] -> `exception.stacktrace` (omitted when null)
136 : class SanitizedSpanException {
137 : /// Creates a sanitized span exception.
138 1 : const SanitizedSpanException({
139 : required this.type,
140 : required this.message,
141 : this.stackTrace,
142 : this.statusDescription,
143 : });
144 :
145 : /// The exception type, recorded as the `exception.type` attribute.
146 : final String type;
147 :
148 : /// The sanitized error message, recorded as the `exception.message`
149 : /// attribute.
150 : final String message;
151 :
152 : /// Optional sanitized stack trace, recorded as the `exception.stacktrace`
153 : /// attribute. When `null`, no stack trace is recorded. The original
154 : /// (unsanitized) stack trace is never recorded when a sanitizer is used.
155 : final StackTrace? stackTrace;
156 :
157 : /// Optional status description used when setting the span status to
158 : /// [SpanStatusCode.Error]. Falls back to [message] when not provided.
159 : final String? statusDescription;
160 : }
|