@grafana/faro-react-native-tracing 1.3.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +33 -0
  2. package/dist/cjs/exporters/faroTraceExporter.utils.js +33 -0
  3. package/dist/cjs/exporters/faroTraceExporter.utils.js.map +1 -1
  4. package/dist/cjs/instrumentation.js +259 -44
  5. package/dist/cjs/instrumentation.js.map +1 -1
  6. package/dist/cjs/instrumentations/getDefaultOTELInstrumentations.js +28 -20
  7. package/dist/cjs/instrumentations/getDefaultOTELInstrumentations.js.map +1 -1
  8. package/dist/cjs/instrumentations/instrumentationUtils.js +15 -0
  9. package/dist/cjs/instrumentations/instrumentationUtils.js.map +1 -1
  10. package/dist/cjs/processors/httpRequestMonitorSpanProcessor.js +27 -13
  11. package/dist/cjs/processors/httpRequestMonitorSpanProcessor.js.map +1 -1
  12. package/dist/cjs/types.js.map +1 -1
  13. package/dist/esm/exporters/faroTraceExporter.utils.js +32 -0
  14. package/dist/esm/exporters/faroTraceExporter.utils.js.map +1 -1
  15. package/dist/esm/instrumentation.js +237 -43
  16. package/dist/esm/instrumentation.js.map +1 -1
  17. package/dist/esm/instrumentations/getDefaultOTELInstrumentations.js +27 -21
  18. package/dist/esm/instrumentations/getDefaultOTELInstrumentations.js.map +1 -1
  19. package/dist/esm/instrumentations/instrumentationUtils.js +14 -0
  20. package/dist/esm/instrumentations/instrumentationUtils.js.map +1 -1
  21. package/dist/esm/processors/httpRequestMonitorSpanProcessor.js +27 -13
  22. package/dist/esm/processors/httpRequestMonitorSpanProcessor.js.map +1 -1
  23. package/dist/esm/types.js.map +1 -1
  24. package/dist/types/instrumentation.d.ts +12 -4
  25. package/dist/types/instrumentations/getDefaultOTELInstrumentations.d.ts +2 -0
  26. package/dist/types/instrumentations/instrumentationUtils.d.ts +2 -0
  27. package/dist/types/types.d.ts +4 -0
  28. package/package.json +3 -3
  29. package/src/exporters/faroTraceExporter.events.test.ts +90 -0
  30. package/src/exporters/faroTraceExporter.utils.ts +32 -0
  31. package/src/httpSemantics.test.ts +300 -0
  32. package/src/instrumentation.test.ts +740 -0
  33. package/src/instrumentation.ts +299 -67
  34. package/src/instrumentations/getDefaultOTELInstrumentations.test.ts +28 -1
  35. package/src/instrumentations/getDefaultOTELInstrumentations.ts +41 -32
  36. package/src/instrumentations/instrumentationUtils.ts +13 -0
  37. package/src/processors/httpRequestMonitorSpanProcessor.ts +27 -15
  38. package/src/types.ts +4 -0
package/README.md CHANGED
@@ -41,6 +41,23 @@ const faro = initializeFaro({
41
41
 
42
42
  That's it! HTTP requests via `fetch()` are now automatically traced (and correlated with user actions when user action tracking is enabled) and sent to your Faro collector.
43
43
 
44
+ ## HTTP span and event conventions
45
+
46
+ Built-in fetch tracing uses OpenTelemetry's [stable HTTP conventions](https://opentelemetry.io/docs/specs/semconv/http/http-spans/#name),
47
+ with span names such as `GET` and `POST`. Attributes include `http.request.method`,
48
+ `url.full`, `server.address`, `server.port`, and `http.response.status_code`.
49
+ Successful responses leave status UNSET; ordinary 4xx/5xx responses set ERROR
50
+ and a string `error.type`. There is no fetch convention mode setting.
51
+
52
+ The `faro.tracing.fetch` event keeps `http.method`, `http.url`, `http.status_code`,
53
+ `http.host`, and `http.scheme`. Event attributes, including `duration_ns`, are
54
+ strings. Events retain session and trace/span context and use the shared event
55
+ pipeline for deduplication and transport hooks. Optional XHR tracing and custom
56
+ instrumentations use their own conventions.
57
+
58
+ Fetch spans capture the active user action at request start. A delayed fetch
59
+ event can still be associated with a later action through event buffering.
60
+
44
61
  ## Fetch vs XHR in React Native
45
62
 
46
63
  By default, this package traces `fetch()` requests only.
@@ -209,6 +226,10 @@ new TracingInstrumentation({
209
226
  });
210
227
  ```
211
228
 
229
+ A custom span processor remains reusable when Faro removes and later re-adds the tracing instrumentation. Removal
230
+ flushes the processor without shutting it down. Calling `TracingInstrumentation.shutdown()` shuts it down and is
231
+ terminal for that processor; use a new processor before initializing the instrumentation again.
232
+
212
233
  ## Usage Examples
213
234
 
214
235
  ### Automatic HTTP Tracing
@@ -665,6 +686,10 @@ const { trace } = faro.otel; // Error!
665
686
 
666
687
  **Solution:** This should be automatic. If you're using a custom span processor or custom instrumentations, ensure you're not overriding the tracer provider registration.
667
688
 
689
+ If another global OpenTelemetry tracer provider is already registered, tracing initialization fails with a clear
690
+ error instead of installing request instrumentation that cannot export through Faro. Remove the duplicate provider
691
+ registration before initializing `TracingInstrumentation`.
692
+
668
693
  ### Spans missing user/session/device context
669
694
 
670
695
  **Cause:** FaroMetaAttributesSpanProcessor is not wrapping your span processor.
@@ -727,9 +752,17 @@ class TracingInstrumentation extends BaseInstrumentation {
727
752
  constructor(options?: TracingInstrumentationOptions);
728
753
  initialize(): void;
729
754
  shutdown(): Promise<void>;
755
+ destroy(): void;
730
756
  }
731
757
  ```
732
758
 
759
+ `shutdown()` detaches HTTP instrumentation before waiting for the tracer provider to flush. Faro calls `destroy()`
760
+ when the instrumentation is removed; it detaches immediately and completes provider shutdown in the background.
761
+ Both methods are safe to call more than once.
762
+
763
+ When tracing is removed and later re-added, reuse the same `TracingInstrumentation` instance so the default fetch
764
+ and XHR patches are reused as well.
765
+
733
766
  **Options:**
734
767
 
735
768
  ```typescript
@@ -5,6 +5,36 @@ var internal_types_1 = require("@opentelemetry/otlp-transformer/build/src/trace/
5
5
  var faro_core_1 = require("@grafana/faro-core");
6
6
  var internalLogger = (0, faro_core_1.createInternalLogger)();
7
7
  var DURATION_NS_KEY = 'duration_ns';
8
+ /** Map stable OTel span attributes to Faro's fetch event contract. */
9
+ function projectFetchEventAttributes(attributes) {
10
+ var _a, _b, _c;
11
+ var fields = {
12
+ 'http.request.method': 'http.method',
13
+ 'url.full': 'http.url',
14
+ 'http.response.status_code': 'http.status_code',
15
+ };
16
+ for (var _i = 0, _d = Object.entries(fields); _i < _d.length; _i++) {
17
+ var _e = _d[_i], spanField = _e[0], eventField = _e[1];
18
+ if (attributes[spanField] != null) {
19
+ attributes[eventField] = attributes[spanField];
20
+ delete attributes[spanField];
21
+ }
22
+ }
23
+ var url = attributes['http.url'];
24
+ if (url) {
25
+ try {
26
+ var parsed = new URL(url);
27
+ attributes['http.host'] = (_a = attributes['http.host']) !== null && _a !== void 0 ? _a : parsed.host;
28
+ attributes['http.scheme'] = (_b = attributes['http.scheme']) !== null && _b !== void 0 ? _b : parsed.protocol.replace(':', '');
29
+ }
30
+ catch (_f) {
31
+ // Keep the captured URL even if it cannot supply host/scheme metadata.
32
+ }
33
+ }
34
+ delete attributes['server.address'];
35
+ delete attributes['server.port'];
36
+ attributes['component'] = (_c = attributes['component']) !== null && _c !== void 0 ? _c : 'fetch';
37
+ }
8
38
  /**
9
39
  * Send Faro events for CLIENT spans (HTTP requests, navigation, etc.)
10
40
  *
@@ -45,6 +75,9 @@ function sendFaroEvents(resourceSpans) {
45
75
  var attribute = _g[_f];
46
76
  faroEventAttributes[attribute.key] = String(Object.values(attribute.value)[0]);
47
77
  }
78
+ if ((scope === null || scope === void 0 ? void 0 : scope.name) === '@opentelemetry/instrumentation-fetch') {
79
+ projectFetchEventAttributes(faroEventAttributes);
80
+ }
48
81
  // Add span duration in nanoseconds
49
82
  if (!Number.isNaN(span.endTimeUnixNano) && !Number.isNaN(span.startTimeUnixNano)) {
50
83
  faroEventAttributes[DURATION_NS_KEY] = String(Number(span.endTimeUnixNano) - Number(span.startTimeUnixNano));
@@ -1 +1 @@
1
- {"version":3,"file":"faroTraceExporter.utils.js","sourceRoot":"","sources":["../../../src/exporters/faroTraceExporter.utils.ts"],"names":[],"mappings":"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","sourcesContent":["import type { SpanContext } from '@opentelemetry/api';\nimport { ESpanKind, type IResourceSpans } from '@opentelemetry/otlp-transformer/build/src/trace/internal-types';\n\nimport { createInternalLogger, faro, unknownString } from '@grafana/faro-core';\nimport type { EventAttributes as FaroEventAttributes } from '@grafana/faro-core';\n\nconst internalLogger = createInternalLogger();\n\nconst DURATION_NS_KEY = 'duration_ns';\n\n/**\n * Send Faro events for CLIENT spans (HTTP requests, navigation, etc.)\n *\n * IMPORTANT: This function is called during trace export and must be careful to avoid infinite loops.\n *\n * Infinite loop prevention strategy:\n * 1. Only process SPAN_KIND_CLIENT spans (HTTP requests, etc.)\n * 2. Use faro.api.pushEvent which:\n * - Does NOT trigger console logs if ConsoleInstrumentation is configured properly\n * - Does NOT trigger HTTP instrumentation for collector URLs (they're ignored)\n * 3. Use internalLogger for debugging instead of console\n * 4. Never call console.log/warn/error in this function\n *\n * @param resourceSpans - OTLP resource spans from trace exporter\n */\nexport function sendFaroEvents(resourceSpans: IResourceSpans[] = []) {\n try {\n for (const resourceSpan of resourceSpans) {\n const { scopeSpans } = resourceSpan;\n\n for (const scopeSpan of scopeSpans) {\n const { scope, spans = [] } = scopeSpan;\n\n for (const span of spans) {\n // Only process CLIENT spans (HTTP requests, external calls)\n // This avoids processing internal spans which could cause loops\n if (span.kind !== ESpanKind.SPAN_KIND_CLIENT) {\n continue;\n }\n\n const spanContext: Pick<SpanContext, 'traceId' | 'spanId'> = {\n traceId: span.traceId.toString(),\n spanId: span.spanId.toString(),\n };\n\n const faroEventAttributes: FaroEventAttributes = {};\n for (const attribute of span.attributes) {\n faroEventAttributes[attribute.key] = String(Object.values(attribute.value)[0]);\n }\n\n // Add span duration in nanoseconds\n if (!Number.isNaN(span.endTimeUnixNano) && !Number.isNaN(span.startTimeUnixNano)) {\n faroEventAttributes[DURATION_NS_KEY] = String(\n Number(span.endTimeUnixNano) - Number(span.startTimeUnixNano)\n );\n }\n\n const index = (scope?.name ?? '').indexOf('-');\n let eventName = unknownString;\n\n if (scope?.name) {\n if (index === -1) {\n eventName = scope.name.split('/')[1] ?? scope.name;\n }\n\n if (index > -1) {\n eventName = scope?.name.substring(index + 1);\n }\n }\n\n // Push event to Faro\n // This should NOT cause infinite loops because:\n // 1. The collector URL is in ignoreUrls for HttpInstrumentation\n // 2. ConsoleInstrumentation won't log this event\n // 3. No console.log calls are made here\n faro.api.pushEvent(`faro.tracing.${eventName}`, faroEventAttributes, undefined, {\n spanContext,\n // Convert nanoseconds to milliseconds\n timestampOverwriteMs: Number(span.endTimeUnixNano) / 1_000_000,\n customPayloadTransformer: (payload) => {\n if (\n faroEventAttributes['faro.action.user.name'] != null &&\n faroEventAttributes['faro.action.user.parentId'] != null\n ) {\n payload.action = {\n name: faroEventAttributes['faro.action.user.name'],\n parentId: faroEventAttributes['faro.action.user.parentId'],\n };\n\n delete payload.attributes?.['faro.action.user.name'];\n delete payload.attributes?.['faro.action.user.parentId'];\n }\n\n return payload;\n },\n });\n }\n }\n }\n } catch (error) {\n // Only log critical errors using internal logger\n // Do NOT use console.log here as it could cause infinite loops\n internalLogger.error('sendFaroEvents: Failed to process spans', error);\n }\n}\n"]}
1
+ {"version":3,"file":"faroTraceExporter.utils.js","sourceRoot":"","sources":["../../../src/exporters/faroTraceExporter.utils.ts"],"names":[],"mappings":"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","sourcesContent":["import type { SpanContext } from '@opentelemetry/api';\nimport { ESpanKind, type IResourceSpans } from '@opentelemetry/otlp-transformer/build/src/trace/internal-types';\n\nimport { createInternalLogger, faro, unknownString } from '@grafana/faro-core';\nimport type { EventAttributes as FaroEventAttributes } from '@grafana/faro-core';\n\nconst internalLogger = createInternalLogger();\n\nconst DURATION_NS_KEY = 'duration_ns';\n/** Map stable OTel span attributes to Faro's fetch event contract. */\nfunction projectFetchEventAttributes(attributes: FaroEventAttributes): void {\n const fields = {\n 'http.request.method': 'http.method',\n 'url.full': 'http.url',\n 'http.response.status_code': 'http.status_code',\n };\n for (const [spanField, eventField] of Object.entries(fields)) {\n if (attributes[spanField] != null) {\n attributes[eventField] = attributes[spanField];\n delete attributes[spanField];\n }\n }\n\n const url = attributes['http.url'];\n if (url) {\n try {\n const parsed = new URL(url);\n attributes['http.host'] = attributes['http.host'] ?? parsed.host;\n attributes['http.scheme'] = attributes['http.scheme'] ?? parsed.protocol.replace(':', '');\n } catch {\n // Keep the captured URL even if it cannot supply host/scheme metadata.\n }\n }\n delete attributes['server.address'];\n delete attributes['server.port'];\n attributes['component'] = attributes['component'] ?? 'fetch';\n}\n\n/**\n * Send Faro events for CLIENT spans (HTTP requests, navigation, etc.)\n *\n * IMPORTANT: This function is called during trace export and must be careful to avoid infinite loops.\n *\n * Infinite loop prevention strategy:\n * 1. Only process SPAN_KIND_CLIENT spans (HTTP requests, etc.)\n * 2. Use faro.api.pushEvent which:\n * - Does NOT trigger console logs if ConsoleInstrumentation is configured properly\n * - Does NOT trigger HTTP instrumentation for collector URLs (they're ignored)\n * 3. Use internalLogger for debugging instead of console\n * 4. Never call console.log/warn/error in this function\n *\n * @param resourceSpans - OTLP resource spans from trace exporter\n */\nexport function sendFaroEvents(resourceSpans: IResourceSpans[] = []) {\n try {\n for (const resourceSpan of resourceSpans) {\n const { scopeSpans } = resourceSpan;\n\n for (const scopeSpan of scopeSpans) {\n const { scope, spans = [] } = scopeSpan;\n\n for (const span of spans) {\n // Only process CLIENT spans (HTTP requests, external calls)\n // This avoids processing internal spans which could cause loops\n if (span.kind !== ESpanKind.SPAN_KIND_CLIENT) {\n continue;\n }\n\n const spanContext: Pick<SpanContext, 'traceId' | 'spanId'> = {\n traceId: span.traceId.toString(),\n spanId: span.spanId.toString(),\n };\n\n const faroEventAttributes: FaroEventAttributes = {};\n for (const attribute of span.attributes) {\n faroEventAttributes[attribute.key] = String(Object.values(attribute.value)[0]);\n }\n\n if (scope?.name === '@opentelemetry/instrumentation-fetch') {\n projectFetchEventAttributes(faroEventAttributes);\n }\n\n // Add span duration in nanoseconds\n if (!Number.isNaN(span.endTimeUnixNano) && !Number.isNaN(span.startTimeUnixNano)) {\n faroEventAttributes[DURATION_NS_KEY] = String(\n Number(span.endTimeUnixNano) - Number(span.startTimeUnixNano)\n );\n }\n\n const index = (scope?.name ?? '').indexOf('-');\n let eventName = unknownString;\n\n if (scope?.name) {\n if (index === -1) {\n eventName = scope.name.split('/')[1] ?? scope.name;\n }\n\n if (index > -1) {\n eventName = scope?.name.substring(index + 1);\n }\n }\n\n // Push event to Faro\n // This should NOT cause infinite loops because:\n // 1. The collector URL is in ignoreUrls for HttpInstrumentation\n // 2. ConsoleInstrumentation won't log this event\n // 3. No console.log calls are made here\n faro.api.pushEvent(`faro.tracing.${eventName}`, faroEventAttributes, undefined, {\n spanContext,\n // Convert nanoseconds to milliseconds\n timestampOverwriteMs: Number(span.endTimeUnixNano) / 1_000_000,\n customPayloadTransformer: (payload) => {\n if (\n faroEventAttributes['faro.action.user.name'] != null &&\n faroEventAttributes['faro.action.user.parentId'] != null\n ) {\n payload.action = {\n name: faroEventAttributes['faro.action.user.name'],\n parentId: faroEventAttributes['faro.action.user.parentId'],\n };\n\n delete payload.attributes?.['faro.action.user.name'];\n delete payload.attributes?.['faro.action.user.parentId'];\n }\n\n return payload;\n },\n });\n }\n }\n }\n } catch (error) {\n // Only log critical errors using internal logger\n // Do NOT use console.log here as it could cause infinite loops\n internalLogger.error('sendFaroEvents: Failed to process spans', error);\n }\n}\n"]}
@@ -76,8 +76,34 @@ var faroMetaAttributesSpanProcessor_1 = require("./processors/faroMetaAttributes
76
76
  var httpRequestMonitorSpanProcessor_1 = require("./processors/httpRequestMonitorSpanProcessor");
77
77
  var semconv_1 = require("./semconv");
78
78
  var sampler_1 = require("./utils/sampler");
79
- // Import React Native TracerProvider
80
- // Note: We use the base provider since React Native doesn't have a specific one
79
+ var detachedInstrumentations = new WeakSet();
80
+ /**
81
+ * Keeps a caller-supplied processor reusable across Faro remove/add cycles.
82
+ * Explicit TracingInstrumentation.shutdown() remains responsible for the
83
+ * processor's terminal shutdown.
84
+ */
85
+ var BorrowedSpanProcessor = /** @class */ (function () {
86
+ function BorrowedSpanProcessor(processor) {
87
+ this.processor = processor;
88
+ }
89
+ BorrowedSpanProcessor.prototype.forceFlush = function () {
90
+ return this.processor.forceFlush();
91
+ };
92
+ BorrowedSpanProcessor.prototype.onStart = function (span, parentContext) {
93
+ this.processor.onStart(span, parentContext);
94
+ };
95
+ BorrowedSpanProcessor.prototype.onEnding = function (span) {
96
+ var _a, _b;
97
+ (_b = (_a = this.processor).onEnding) === null || _b === void 0 ? void 0 : _b.call(_a, span);
98
+ };
99
+ BorrowedSpanProcessor.prototype.onEnd = function (span) {
100
+ this.processor.onEnd(span);
101
+ };
102
+ BorrowedSpanProcessor.prototype.shutdown = function () {
103
+ return Promise.resolve();
104
+ };
105
+ return BorrowedSpanProcessor;
106
+ }());
81
107
  /**
82
108
  * TracingInstrumentation for React Native
83
109
  *
@@ -112,11 +138,13 @@ var TracingInstrumentation = /** @class */ (function (_super) {
112
138
  _this.options = options;
113
139
  _this.name = '@grafana/faro-react-native-tracing';
114
140
  _this.version = faro_core_1.VERSION;
141
+ _this.pendingShutdowns = new Set();
115
142
  return _this;
116
143
  }
117
144
  TracingInstrumentation.prototype.initialize = function () {
118
145
  var _this = this;
119
- var _a, _b, _c, _d, _e, _f;
146
+ var _a, _b, _c, _d, _e;
147
+ this.destroy();
120
148
  var options = this.options;
121
149
  var attributes = {};
122
150
  // App attributes
@@ -165,7 +193,7 @@ var TracingInstrumentation = /** @class */ (function (_super) {
165
193
  Object.assign(attributes, options.resourceAttributes);
166
194
  var resource = (0, resources_1.defaultResource)().merge((0, resources_1.resourceFromAttributes)(attributes));
167
195
  // Create tracer provider with span processors
168
- this.provider = new sdk_trace_base_1.BasicTracerProvider({
196
+ var provider = new sdk_trace_base_1.BasicTracerProvider({
169
197
  resource: resource,
170
198
  sampler: {
171
199
  shouldSample: function () {
@@ -175,51 +203,90 @@ var TracingInstrumentation = /** @class */ (function (_super) {
175
203
  },
176
204
  },
177
205
  spanProcessors: [
178
- (_b = options.spanProcessor) !== null && _b !== void 0 ? _b : new httpRequestMonitorSpanProcessor_1.HttpRequestMonitorSpanProcessor(new faroMetaAttributesSpanProcessor_1.FaroMetaAttributesSpanProcessor(new sdk_trace_base_1.BatchSpanProcessor(new faroTraceExporter_1.FaroTraceExporter({ api: this.api }), {
206
+ (_b = (options.spanProcessor && new BorrowedSpanProcessor(options.spanProcessor))) !== null && _b !== void 0 ? _b : new httpRequestMonitorSpanProcessor_1.HttpRequestMonitorSpanProcessor(new faroMetaAttributesSpanProcessor_1.FaroMetaAttributesSpanProcessor(new sdk_trace_base_1.BatchSpanProcessor(new faroTraceExporter_1.FaroTraceExporter({ api: this.api }), {
179
207
  scheduledDelayMillis: TracingInstrumentation.SCHEDULED_BATCH_DELAY_MS,
180
208
  maxExportBatchSize: 30,
181
209
  }), this.metas)),
182
210
  ],
183
211
  });
184
- // Register the provider as the global tracer provider
185
- // This is CRITICAL for the tracer to generate real trace IDs instead of all zeros
186
- api_1.trace.setGlobalTracerProvider(this.provider);
187
- // Register a global ContextManager. Without one, OTel falls back to the NoopContextManager,
188
- // which always returns ROOT_CONTEXT — so when `@opentelemetry/instrumentation-fetch` does
189
- // `context.with(setSpan(active(), createdSpan), () => _addHeaders(...))` the span set on the
190
- // wrapped context is invisible inside `_addHeaders` and `propagation.inject` writes nothing.
191
- // `StackContextManager` is pure JS (no DOM/Zone deps) and works in React Native.
192
- api_1.context.setGlobalContextManager((_c = options.contextManager) !== null && _c !== void 0 ? _c : new sdk_trace_web_1.StackContextManager().enable());
193
- // Register the global text-map propagator. Without this, OTel falls back to a
194
- // NoopTextMapPropagator and `propagation.inject(...)` becomes a no-op, meaning
195
- // `traceparent` / `tracestate` (and `baggage`) headers are never written on the
196
- // outbound fetch/XHR — so the backend receives no context and starts a new trace.
197
- api_1.propagation.setGlobalPropagator((_d = options.propagator) !== null && _d !== void 0 ? _d : new core_1.CompositePropagator({
198
- propagators: [new core_1.W3CTraceContextPropagator(), new core_1.W3CBaggagePropagator()],
199
- }));
200
- var _g = (_e = this.options.instrumentationOptions) !== null && _e !== void 0 ? _e : {}, enableFetchInstrumentation = _g.enableFetchInstrumentation, enableXhrInstrumentation = _g.enableXhrInstrumentation, propagateTraceHeaderCorsUrls = _g.propagateTraceHeaderCorsUrls, fetchInstrumentationOptions = _g.fetchInstrumentationOptions, xhrInstrumentationOptions = _g.xhrInstrumentationOptions;
201
- // Get ignore URLs from transports to prevent infinite loops
202
- var ignoreUrls = this.getIgnoreUrls();
203
- // Register instrumentations
204
- (0, instrumentation_1.registerInstrumentations)({
205
- instrumentations: (_f = options.instrumentations) !== null && _f !== void 0 ? _f : (0, getDefaultOTELInstrumentations_1.getDefaultOTELInstrumentations)({
212
+ var registration = {
213
+ instrumentations: [],
214
+ ownsContextManager: false,
215
+ ownsContextManagerInstance: false,
216
+ ownsPropagator: false,
217
+ ownsTracerProvider: false,
218
+ provider: provider,
219
+ };
220
+ this.activeRegistration = registration;
221
+ try {
222
+ // Register the provider as the global tracer provider
223
+ // This is CRITICAL for the tracer to generate real trace IDs instead of all zeros
224
+ if (!api_1.trace.setGlobalTracerProvider(provider)) {
225
+ throw new Error('Unable to register the Faro OpenTelemetry tracer provider. Remove the existing global tracer provider before initializing Faro tracing.');
226
+ }
227
+ registration.ownsTracerProvider = true;
228
+ // Register a global ContextManager. Without one, OTel falls back to the NoopContextManager,
229
+ // which always returns ROOT_CONTEXT — so when `@opentelemetry/instrumentation-fetch` does
230
+ // `context.with(setSpan(active(), createdSpan), () => _addHeaders(...))` the span set on the
231
+ // wrapped context is invisible inside `_addHeaders` and `propagation.inject` writes nothing.
232
+ // `StackContextManager` is pure JS (no DOM/Zone deps) and works in React Native.
233
+ var contextManager = ((_c = options.contextManager) !== null && _c !== void 0 ? _c : new sdk_trace_web_1.StackContextManager()).enable();
234
+ registration.contextManager = contextManager;
235
+ registration.ownsContextManagerInstance = options.contextManager == null;
236
+ registration.ownsContextManager = api_1.context.setGlobalContextManager(contextManager);
237
+ // Register the global text-map propagator. Without this, OTel falls back to a
238
+ // NoopTextMapPropagator and `propagation.inject(...)` becomes a no-op, meaning
239
+ // `traceparent` / `tracestate` (and `baggage`) headers are never written on the
240
+ // outbound fetch/XHR — so the backend receives no context and starts a new trace.
241
+ registration.ownsPropagator = api_1.propagation.setGlobalPropagator((_d = options.propagator) !== null && _d !== void 0 ? _d : new core_1.CompositePropagator({
242
+ propagators: [new core_1.W3CTraceContextPropagator(), new core_1.W3CBaggagePropagator()],
243
+ }));
244
+ var _f = (_e = this.options.instrumentationOptions) !== null && _e !== void 0 ? _e : {}, enableFetchInstrumentation = _f.enableFetchInstrumentation, enableXhrInstrumentation = _f.enableXhrInstrumentation, propagateTraceHeaderCorsUrls = _f.propagateTraceHeaderCorsUrls, fetchInstrumentationOptions = _f.fetchInstrumentationOptions, xhrInstrumentationOptions = _f.xhrInstrumentationOptions;
245
+ // Get ignore URLs from transports to prevent infinite loops
246
+ var ignoreUrls = this.getIgnoreUrls();
247
+ var defaultInstrumentationOptions = {
206
248
  ignoreUrls: ignoreUrls,
207
249
  enableFetchInstrumentation: enableFetchInstrumentation,
208
250
  enableXhrInstrumentation: enableXhrInstrumentation,
209
251
  propagateTraceHeaderCorsUrls: propagateTraceHeaderCorsUrls,
210
252
  fetchInstrumentationOptions: fetchInstrumentationOptions,
211
253
  xhrInstrumentationOptions: xhrInstrumentationOptions,
212
- }),
213
- });
214
- // Expose OTEL API on the global Faro instance for manual span creation
215
- // This allows users to access trace and context APIs via faro.otel
216
- var globalFaroInstance = (0, faro_core_1.getInternalFaroFromGlobalObject)();
217
- if (globalFaroInstance) {
218
- // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Extending Faro instance with OTEL API
219
- globalFaroInstance.otel = {
220
- trace: api_1.trace,
221
- context: api_1.context,
222
254
  };
255
+ if (options.instrumentations) {
256
+ registration.instrumentations = options.instrumentations.flat();
257
+ }
258
+ else if (this.defaultInstrumentations) {
259
+ (0, getDefaultOTELInstrumentations_1.updateDefaultOTELInstrumentations)(this.defaultInstrumentations, defaultInstrumentationOptions);
260
+ registration.instrumentations = this.defaultInstrumentations;
261
+ }
262
+ else {
263
+ this.defaultInstrumentations = (0, getDefaultOTELInstrumentations_1.getDefaultOTELInstrumentations)(defaultInstrumentationOptions).flat();
264
+ registration.instrumentations = this.defaultInstrumentations;
265
+ }
266
+ var instrumentationsNeedingExplicitEnable = registration.instrumentations.filter(function (instrumentation) {
267
+ return detachedInstrumentations.has(instrumentation) && instrumentation.getConfig().enabled === true;
268
+ });
269
+ registration.unregisterInstrumentations = (0, instrumentation_1.registerInstrumentations)({
270
+ instrumentations: registration.instrumentations,
271
+ });
272
+ registration.instrumentations.forEach(function (instrumentation) {
273
+ detachedInstrumentations.delete(instrumentation);
274
+ });
275
+ instrumentationsNeedingExplicitEnable.forEach(function (instrumentation) { return instrumentation.enable(); });
276
+ // Expose OTEL API on the global Faro instance for manual span creation
277
+ // This allows users to access trace and context APIs via faro.otel
278
+ var globalFaroInstance = (0, faro_core_1.getInternalFaroFromGlobalObject)();
279
+ if (globalFaroInstance) {
280
+ var faroOtel = { trace: api_1.trace, context: api_1.context };
281
+ registration.faro = globalFaroInstance;
282
+ registration.faroOtel = faroOtel;
283
+ registration.previousFaroOtel = globalFaroInstance.otel;
284
+ globalFaroInstance.otel = faroOtel;
285
+ }
286
+ }
287
+ catch (error) {
288
+ this.destroy();
289
+ throw error;
223
290
  }
224
291
  };
225
292
  /**
@@ -244,24 +311,172 @@ var TracingInstrumentation = /** @class */ (function (_super) {
244
311
  // Return dev-server, original transport URLs, and regex patterns for maximum coverage.
245
312
  return __spreadArray(__spreadArray(__spreadArray([], (0, devServerIgnoreUrls_1.getReactNativeDevServerIgnoreUrls)(), true), transportUrls, true), regexPatterns, true);
246
313
  };
247
- /**
248
- * Shutdown the tracer provider
249
- */
314
+ TracingInstrumentation.prototype.detachRegistration = function () {
315
+ var _this = this;
316
+ var registration = this.activeRegistration;
317
+ if (!registration) {
318
+ return undefined;
319
+ }
320
+ this.activeRegistration = undefined;
321
+ registration.instrumentations.forEach(function (instrumentation) { return detachedInstrumentations.add(instrumentation); });
322
+ var unregistered = false;
323
+ if (registration.unregisterInstrumentations) {
324
+ try {
325
+ registration.unregisterInstrumentations();
326
+ unregistered = true;
327
+ }
328
+ catch (error) {
329
+ this.logCleanupError('unregister OpenTelemetry instrumentations', error);
330
+ }
331
+ }
332
+ if (!unregistered) {
333
+ registration.instrumentations.forEach(function (instrumentation) {
334
+ _this.runCleanup("disable ".concat(instrumentation.instrumentationName), function () { return instrumentation.disable(); });
335
+ });
336
+ }
337
+ var faro = registration.faro, faroOtel = registration.faroOtel;
338
+ if (faro && faroOtel && faro.otel === faroOtel) {
339
+ this.runCleanup('restore faro.otel', function () {
340
+ if (registration.previousFaroOtel === undefined) {
341
+ delete faro.otel;
342
+ }
343
+ else {
344
+ faro.otel = registration.previousFaroOtel;
345
+ }
346
+ });
347
+ }
348
+ if (registration.ownsPropagator) {
349
+ this.runCleanup('clear the OpenTelemetry propagator', function () { return api_1.propagation.disable(); });
350
+ }
351
+ if (registration.ownsContextManager) {
352
+ this.runCleanup('clear the OpenTelemetry context manager', function () { return api_1.context.disable(); });
353
+ }
354
+ else if (registration.ownsContextManagerInstance && registration.contextManager) {
355
+ this.runCleanup('disable the unused OpenTelemetry context manager', function () { var _a; return (_a = registration.contextManager) === null || _a === void 0 ? void 0 : _a.disable(); });
356
+ }
357
+ if (registration.ownsTracerProvider) {
358
+ this.runCleanup('clear the OpenTelemetry tracer provider', function () { return api_1.trace.disable(); });
359
+ }
360
+ return registration;
361
+ };
362
+ TracingInstrumentation.prototype.runCleanup = function (action, cleanup) {
363
+ try {
364
+ cleanup();
365
+ }
366
+ catch (error) {
367
+ this.logCleanupError(action, error);
368
+ }
369
+ };
370
+ TracingInstrumentation.prototype.logCleanupError = function (action, error) {
371
+ try {
372
+ this.logError("Failed to ".concat(action, ":"), error);
373
+ }
374
+ catch (_loggingError) {
375
+ // Cleanup must not fail because a custom logger threw.
376
+ }
377
+ };
378
+ TracingInstrumentation.prototype.startProviderShutdown = function (registration) {
379
+ var _this = this;
380
+ if (!registration.shutdownPromise) {
381
+ var shutdownPromise_1 = Promise.resolve()
382
+ .then(function () { return registration.provider.forceFlush(); })
383
+ .catch(function (error) {
384
+ _this.logCleanupError('flush the OpenTelemetry tracer provider', error);
385
+ })
386
+ .then(function () { return registration.provider.shutdown(); });
387
+ registration.shutdownPromise = shutdownPromise_1;
388
+ this.pendingShutdowns.add(shutdownPromise_1);
389
+ void shutdownPromise_1.then(function () { return _this.pendingShutdowns.delete(shutdownPromise_1); }, function () { return _this.pendingShutdowns.delete(shutdownPromise_1); });
390
+ }
391
+ return registration.shutdownPromise;
392
+ };
393
+ TracingInstrumentation.prototype.startSuppliedSpanProcessorShutdown = function () {
394
+ var spanProcessor = this.options.spanProcessor;
395
+ if (!spanProcessor) {
396
+ return undefined;
397
+ }
398
+ if (!this.suppliedSpanProcessorShutdown) {
399
+ try {
400
+ this.suppliedSpanProcessorShutdown = spanProcessor.shutdown();
401
+ }
402
+ catch (error) {
403
+ this.suppliedSpanProcessorShutdown = Promise.reject(error);
404
+ }
405
+ }
406
+ return this.suppliedSpanProcessorShutdown;
407
+ };
408
+ /** Detach request instrumentations and wait for the tracer provider to flush. */
250
409
  TracingInstrumentation.prototype.shutdown = function () {
251
410
  return __awaiter(this, void 0, void 0, function () {
411
+ var pendingShutdowns, registration, firstError, shutdownFailed, _i, pendingShutdowns_1, shutdownPromise, error_1, spanProcessorShutdown, error_2;
252
412
  return __generator(this, function (_a) {
253
413
  switch (_a.label) {
254
414
  case 0:
255
- if (!this.provider) return [3 /*break*/, 2];
256
- return [4 /*yield*/, this.provider.shutdown()];
415
+ pendingShutdowns = Array.from(this.pendingShutdowns);
416
+ registration = this.detachRegistration();
417
+ if (registration) {
418
+ pendingShutdowns.push(this.startProviderShutdown(registration));
419
+ }
420
+ shutdownFailed = false;
421
+ _i = 0, pendingShutdowns_1 = pendingShutdowns;
422
+ _a.label = 1;
257
423
  case 1:
258
- _a.sent();
424
+ if (!(_i < pendingShutdowns_1.length)) return [3 /*break*/, 6];
425
+ shutdownPromise = pendingShutdowns_1[_i];
259
426
  _a.label = 2;
260
- case 2: return [2 /*return*/];
427
+ case 2:
428
+ _a.trys.push([2, 4, , 5]);
429
+ return [4 /*yield*/, shutdownPromise];
430
+ case 3:
431
+ _a.sent();
432
+ return [3 /*break*/, 5];
433
+ case 4:
434
+ error_1 = _a.sent();
435
+ if (!shutdownFailed) {
436
+ firstError = error_1;
437
+ shutdownFailed = true;
438
+ }
439
+ return [3 /*break*/, 5];
440
+ case 5:
441
+ _i++;
442
+ return [3 /*break*/, 1];
443
+ case 6:
444
+ spanProcessorShutdown = this.startSuppliedSpanProcessorShutdown();
445
+ if (!spanProcessorShutdown) return [3 /*break*/, 10];
446
+ _a.label = 7;
447
+ case 7:
448
+ _a.trys.push([7, 9, , 10]);
449
+ return [4 /*yield*/, spanProcessorShutdown];
450
+ case 8:
451
+ _a.sent();
452
+ return [3 /*break*/, 10];
453
+ case 9:
454
+ error_2 = _a.sent();
455
+ if (!shutdownFailed) {
456
+ firstError = error_2;
457
+ shutdownFailed = true;
458
+ }
459
+ return [3 /*break*/, 10];
460
+ case 10:
461
+ if (shutdownFailed) {
462
+ throw firstError;
463
+ }
464
+ return [2 /*return*/];
261
465
  }
262
466
  });
263
467
  });
264
468
  };
469
+ /** Detach synchronously when Faro removes this instrumentation. */
470
+ TracingInstrumentation.prototype.destroy = function () {
471
+ var _this = this;
472
+ var registration = this.detachRegistration();
473
+ if (!registration) {
474
+ return;
475
+ }
476
+ void this.startProviderShutdown(registration).then(undefined, function (error) {
477
+ _this.logCleanupError('shut down the OpenTelemetry tracer provider', error);
478
+ });
479
+ };
265
480
  TracingInstrumentation.SCHEDULED_BATCH_DELAY_MS = 1000;
266
481
  return TracingInstrumentation;
267
482
  }(faro_core_1.BaseInstrumentation));
@@ -1 +1 @@
1
- {"version":3,"file":"instrumentation.js","sourceRoot":"","sources":["../../src/instrumentation.ts"],"names":[],"mappings":"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","sourcesContent":["import { context, propagation, trace } from '@opentelemetry/api';\nimport type { Attributes } from '@opentelemetry/api';\nimport { CompositePropagator, W3CBaggagePropagator, W3CTraceContextPropagator } from '@opentelemetry/core';\nimport { registerInstrumentations } from '@opentelemetry/instrumentation';\nimport { defaultResource, resourceFromAttributes } from '@opentelemetry/resources';\nimport { BatchSpanProcessor, BasicTracerProvider as ReactNativeTracerProvider } from '@opentelemetry/sdk-trace-base';\nimport type { BasicTracerProvider } from '@opentelemetry/sdk-trace-base';\nimport { StackContextManager } from '@opentelemetry/sdk-trace-web';\nimport {\n ATTR_SERVICE_NAME,\n ATTR_SERVICE_VERSION,\n SEMRESATTRS_DEPLOYMENT_ENVIRONMENT,\n} from '@opentelemetry/semantic-conventions';\n\nimport { BaseInstrumentation, getInternalFaroFromGlobalObject, VERSION } from '@grafana/faro-core';\nimport type { Transport } from '@grafana/faro-core';\n\nimport { FaroTraceExporter } from './exporters/faroTraceExporter';\nimport { getReactNativeDevServerIgnoreUrls } from './instrumentations/devServerIgnoreUrls';\nimport { getDefaultOTELInstrumentations } from './instrumentations/getDefaultOTELInstrumentations';\nimport { FaroMetaAttributesSpanProcessor } from './processors/faroMetaAttributesSpanProcessor';\nimport { HttpRequestMonitorSpanProcessor } from './processors/httpRequestMonitorSpanProcessor';\nimport {\n ATTR_APP_VERSION,\n ATTR_DEPLOYMENT_ENVIRONMENT_NAME,\n ATTR_DEVICE_BRAND,\n ATTR_DEVICE_LOCALE,\n ATTR_DEVICE_MODEL,\n ATTR_DEVICE_OS_VERSION,\n ATTR_DEVICE_PLATFORM,\n ATTR_PROCESS_RUNTIME_NAME,\n ATTR_PROCESS_RUNTIME_VERSION,\n ATTR_SERVICE_NAMESPACE,\n ATTR_TELEMETRY_DISTRO_NAME,\n ATTR_TELEMETRY_DISTRO_VERSION,\n} from './semconv';\nimport type { TracingInstrumentationOptions } from './types';\nimport { getSamplingDecision } from './utils/sampler';\n\n// Import React Native TracerProvider\n// Note: We use the base provider since React Native doesn't have a specific one\n\n/**\n * TracingInstrumentation for React Native\n *\n * Enables distributed tracing with OpenTelemetry for React Native applications.\n *\n * IMPORTANT: Infinite loop prevention\n * - Uses internalLogger for debugging instead of console\n * - Collector URLs are added to ignoreUrls in HTTP instrumentation\n * - BatchSpanProcessor delays span export to avoid blocking\n * - No console logging during trace export\n *\n * Example usage:\n * ```ts\n * import { initializeFaro } from '@grafana/faro-react-native';\n * import { TracingInstrumentation } from '@grafana/faro-react-native-tracing';\n *\n * initializeFaro({\n * // ... other config\n * instrumentations: [\n * new TracingInstrumentation({\n * propagateTraceHeaderCorsUrls: [/https:\\\\/\\\\/my-api\\\\.com/],\n * }),\n * ],\n * });\n * ```\n */\nexport class TracingInstrumentation extends BaseInstrumentation {\n name = '@grafana/faro-react-native-tracing';\n version = VERSION;\n\n static SCHEDULED_BATCH_DELAY_MS = 1000;\n\n private provider?: BasicTracerProvider;\n\n constructor(private options: TracingInstrumentationOptions = {}) {\n super();\n }\n\n initialize(): void {\n const options = this.options;\n const attributes: Attributes = {};\n\n // App attributes\n if (this.config.app.name) {\n attributes[ATTR_SERVICE_NAME] = this.config.app.name;\n }\n\n if (this.config.app.namespace) {\n attributes[ATTR_SERVICE_NAMESPACE] = this.config.app.namespace;\n }\n\n if (this.config.app.version) {\n attributes[ATTR_SERVICE_VERSION] = this.config.app.version;\n attributes[ATTR_APP_VERSION] = this.config.app.version;\n }\n\n if (this.config.app.environment) {\n attributes[ATTR_DEPLOYMENT_ENVIRONMENT_NAME] = this.config.app.environment;\n\n /**\n * @deprecated will be removed in the future and has been replaced by ATTR_DEPLOYMENT_ENVIRONMENT_NAME (deployment.environment.name)\n * We need to keep this for compatibility with some internal services for now.\n */\n attributes[SEMRESATTRS_DEPLOYMENT_ENVIRONMENT] = this.config.app.environment;\n }\n\n // Device/Platform attributes from React Native\n // Note: metas.value contains all meta providers, we need to check if device meta exists\n const allMetas = this.metas.value as Record<string, unknown>;\n const deviceMeta = allMetas['device'] as\n | {\n model?: string;\n brand?: string;\n osName?: string;\n osVersion?: string;\n locale?: string;\n }\n | undefined;\n\n if (deviceMeta?.model) {\n attributes[ATTR_DEVICE_MODEL] = deviceMeta.model;\n }\n\n if (deviceMeta?.brand) {\n attributes[ATTR_DEVICE_BRAND] = deviceMeta.brand;\n }\n\n if (deviceMeta?.osName) {\n attributes[ATTR_DEVICE_PLATFORM] = deviceMeta.osName;\n }\n\n if (deviceMeta?.osVersion) {\n attributes[ATTR_DEVICE_OS_VERSION] = deviceMeta.osVersion;\n }\n\n if (deviceMeta?.locale) {\n attributes[ATTR_DEVICE_LOCALE] = deviceMeta.locale;\n }\n\n attributes[ATTR_PROCESS_RUNTIME_NAME] = 'react-native';\n attributes[ATTR_PROCESS_RUNTIME_VERSION] = deviceMeta?.osVersion ?? 'unknown';\n\n attributes[ATTR_TELEMETRY_DISTRO_NAME] = 'faro-react-native-sdk';\n attributes[ATTR_TELEMETRY_DISTRO_VERSION] = VERSION;\n\n // Merge with user-provided attributes\n Object.assign(attributes, options.resourceAttributes);\n\n const resource = defaultResource().merge(resourceFromAttributes(attributes));\n\n // Create tracer provider with span processors\n this.provider = new ReactNativeTracerProvider({\n resource,\n sampler: {\n shouldSample: () => {\n return {\n decision: getSamplingDecision(this.api.getSession()),\n };\n },\n },\n spanProcessors: [\n options.spanProcessor ??\n new HttpRequestMonitorSpanProcessor(\n new FaroMetaAttributesSpanProcessor(\n new BatchSpanProcessor(new FaroTraceExporter({ api: this.api }), {\n scheduledDelayMillis: TracingInstrumentation.SCHEDULED_BATCH_DELAY_MS,\n maxExportBatchSize: 30,\n }),\n this.metas\n )\n ),\n ],\n });\n\n // Register the provider as the global tracer provider\n // This is CRITICAL for the tracer to generate real trace IDs instead of all zeros\n trace.setGlobalTracerProvider(this.provider);\n\n // Register a global ContextManager. Without one, OTel falls back to the NoopContextManager,\n // which always returns ROOT_CONTEXT — so when `@opentelemetry/instrumentation-fetch` does\n // `context.with(setSpan(active(), createdSpan), () => _addHeaders(...))` the span set on the\n // wrapped context is invisible inside `_addHeaders` and `propagation.inject` writes nothing.\n // `StackContextManager` is pure JS (no DOM/Zone deps) and works in React Native.\n context.setGlobalContextManager(options.contextManager ?? new StackContextManager().enable());\n\n // Register the global text-map propagator. Without this, OTel falls back to a\n // NoopTextMapPropagator and `propagation.inject(...)` becomes a no-op, meaning\n // `traceparent` / `tracestate` (and `baggage`) headers are never written on the\n // outbound fetch/XHR — so the backend receives no context and starts a new trace.\n propagation.setGlobalPropagator(\n options.propagator ??\n new CompositePropagator({\n propagators: [new W3CTraceContextPropagator(), new W3CBaggagePropagator()],\n })\n );\n\n const {\n enableFetchInstrumentation,\n enableXhrInstrumentation,\n propagateTraceHeaderCorsUrls,\n fetchInstrumentationOptions,\n xhrInstrumentationOptions,\n } = this.options.instrumentationOptions ?? {};\n\n // Get ignore URLs from transports to prevent infinite loops\n const ignoreUrls = this.getIgnoreUrls();\n\n // Register instrumentations\n registerInstrumentations({\n instrumentations:\n options.instrumentations ??\n getDefaultOTELInstrumentations({\n ignoreUrls,\n enableFetchInstrumentation,\n enableXhrInstrumentation,\n propagateTraceHeaderCorsUrls,\n fetchInstrumentationOptions,\n xhrInstrumentationOptions,\n }),\n });\n\n // Expose OTEL API on the global Faro instance for manual span creation\n // This allows users to access trace and context APIs via faro.otel\n const globalFaroInstance = getInternalFaroFromGlobalObject();\n if (globalFaroInstance) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Extending Faro instance with OTEL API\n (globalFaroInstance as any).otel = {\n trace,\n context,\n };\n }\n }\n\n /**\n * Get ignore URLs from all transports to avoid tracing collector requests\n * CRITICAL: This prevents infinite loops where trace exports trigger more traces\n */\n private getIgnoreUrls(): Array<string | RegExp> {\n // Get URLs from transports' getIgnoreUrls() method\n const transportUrls = this.transports.transports.flatMap((transport: Transport) => {\n return transport.getIgnoreUrls();\n });\n\n // Create regex patterns that match both with and without trailing slashes\n // This is critical because fetch() might add trailing slashes\n const regexPatterns = transportUrls.map((url) => {\n if (typeof url === 'string') {\n // Escape special regex characters and make trailing slash optional\n const escapedUrl = url.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n return new RegExp(`^${escapedUrl}/?$`);\n }\n return url;\n });\n\n // Return dev-server, original transport URLs, and regex patterns for maximum coverage.\n return [...getReactNativeDevServerIgnoreUrls(), ...transportUrls, ...regexPatterns];\n }\n\n /**\n * Shutdown the tracer provider\n */\n async shutdown(): Promise<void> {\n if (this.provider) {\n await this.provider.shutdown();\n }\n }\n}\n"]}
1
+ {"version":3,"file":"instrumentation.js","sourceRoot":"","sources":["../../src/instrumentation.ts"],"names":[],"mappings":"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","sourcesContent":["import { context, propagation, trace } from '@opentelemetry/api';\nimport type { Attributes, Context, ContextManager } from '@opentelemetry/api';\nimport { CompositePropagator, W3CBaggagePropagator, W3CTraceContextPropagator } from '@opentelemetry/core';\nimport { registerInstrumentations } from '@opentelemetry/instrumentation';\nimport type { Instrumentation } from '@opentelemetry/instrumentation';\nimport { defaultResource, resourceFromAttributes } from '@opentelemetry/resources';\nimport { BatchSpanProcessor, BasicTracerProvider as ReactNativeTracerProvider } from '@opentelemetry/sdk-trace-base';\nimport type { BasicTracerProvider, ReadableSpan, Span, SpanProcessor } from '@opentelemetry/sdk-trace-base';\nimport { StackContextManager } from '@opentelemetry/sdk-trace-web';\nimport {\n ATTR_SERVICE_NAME,\n ATTR_SERVICE_VERSION,\n SEMRESATTRS_DEPLOYMENT_ENVIRONMENT,\n} from '@opentelemetry/semantic-conventions';\n\nimport { BaseInstrumentation, getInternalFaroFromGlobalObject, VERSION } from '@grafana/faro-core';\nimport type { Faro, OTELApi, Transport } from '@grafana/faro-core';\n\nimport { FaroTraceExporter } from './exporters/faroTraceExporter';\nimport { getReactNativeDevServerIgnoreUrls } from './instrumentations/devServerIgnoreUrls';\nimport {\n getDefaultOTELInstrumentations,\n updateDefaultOTELInstrumentations,\n} from './instrumentations/getDefaultOTELInstrumentations';\nimport { FaroMetaAttributesSpanProcessor } from './processors/faroMetaAttributesSpanProcessor';\nimport { HttpRequestMonitorSpanProcessor } from './processors/httpRequestMonitorSpanProcessor';\nimport {\n ATTR_APP_VERSION,\n ATTR_DEPLOYMENT_ENVIRONMENT_NAME,\n ATTR_DEVICE_BRAND,\n ATTR_DEVICE_LOCALE,\n ATTR_DEVICE_MODEL,\n ATTR_DEVICE_OS_VERSION,\n ATTR_DEVICE_PLATFORM,\n ATTR_PROCESS_RUNTIME_NAME,\n ATTR_PROCESS_RUNTIME_VERSION,\n ATTR_SERVICE_NAMESPACE,\n ATTR_TELEMETRY_DISTRO_NAME,\n ATTR_TELEMETRY_DISTRO_VERSION,\n} from './semconv';\nimport type { TracingInstrumentationOptions } from './types';\nimport { getSamplingDecision } from './utils/sampler';\n\n// Import React Native TracerProvider\n// Note: We use the base provider since React Native doesn't have a specific one\n\ntype FaroWithOtel = Faro & { otel?: unknown };\nconst detachedInstrumentations = new WeakSet<Instrumentation>();\n\n/**\n * Keeps a caller-supplied processor reusable across Faro remove/add cycles.\n * Explicit TracingInstrumentation.shutdown() remains responsible for the\n * processor's terminal shutdown.\n */\nclass BorrowedSpanProcessor implements SpanProcessor {\n constructor(private readonly processor: SpanProcessor) {}\n\n forceFlush(): Promise<void> {\n return this.processor.forceFlush();\n }\n\n onStart(span: Span, parentContext: Context): void {\n this.processor.onStart(span, parentContext);\n }\n\n onEnding(span: Span): void {\n this.processor.onEnding?.(span);\n }\n\n onEnd(span: ReadableSpan): void {\n this.processor.onEnd(span);\n }\n\n shutdown(): Promise<void> {\n return Promise.resolve();\n }\n}\n\ninterface TracingRegistration {\n contextManager?: ContextManager;\n faro?: FaroWithOtel;\n faroOtel?: OTELApi;\n instrumentations: Instrumentation[];\n ownsContextManager: boolean;\n ownsContextManagerInstance: boolean;\n ownsPropagator: boolean;\n ownsTracerProvider: boolean;\n previousFaroOtel?: unknown;\n provider: BasicTracerProvider;\n shutdownPromise?: Promise<void>;\n unregisterInstrumentations?: VoidFunction;\n}\n\n/**\n * TracingInstrumentation for React Native\n *\n * Enables distributed tracing with OpenTelemetry for React Native applications.\n *\n * IMPORTANT: Infinite loop prevention\n * - Uses internalLogger for debugging instead of console\n * - Collector URLs are added to ignoreUrls in HTTP instrumentation\n * - BatchSpanProcessor delays span export to avoid blocking\n * - No console logging during trace export\n *\n * Example usage:\n * ```ts\n * import { initializeFaro } from '@grafana/faro-react-native';\n * import { TracingInstrumentation } from '@grafana/faro-react-native-tracing';\n *\n * initializeFaro({\n * // ... other config\n * instrumentations: [\n * new TracingInstrumentation({\n * propagateTraceHeaderCorsUrls: [/https:\\\\/\\\\/my-api\\\\.com/],\n * }),\n * ],\n * });\n * ```\n */\nexport class TracingInstrumentation extends BaseInstrumentation {\n name = '@grafana/faro-react-native-tracing';\n version = VERSION;\n\n static SCHEDULED_BATCH_DELAY_MS = 1000;\n\n private activeRegistration?: TracingRegistration;\n private defaultInstrumentations?: Instrumentation[];\n private pendingShutdowns = new Set<Promise<void>>();\n private suppliedSpanProcessorShutdown?: Promise<void>;\n\n constructor(private options: TracingInstrumentationOptions = {}) {\n super();\n }\n\n initialize(): void {\n this.destroy();\n\n const options = this.options;\n const attributes: Attributes = {};\n\n // App attributes\n if (this.config.app.name) {\n attributes[ATTR_SERVICE_NAME] = this.config.app.name;\n }\n\n if (this.config.app.namespace) {\n attributes[ATTR_SERVICE_NAMESPACE] = this.config.app.namespace;\n }\n\n if (this.config.app.version) {\n attributes[ATTR_SERVICE_VERSION] = this.config.app.version;\n attributes[ATTR_APP_VERSION] = this.config.app.version;\n }\n\n if (this.config.app.environment) {\n attributes[ATTR_DEPLOYMENT_ENVIRONMENT_NAME] = this.config.app.environment;\n\n /**\n * @deprecated will be removed in the future and has been replaced by ATTR_DEPLOYMENT_ENVIRONMENT_NAME (deployment.environment.name)\n * We need to keep this for compatibility with some internal services for now.\n */\n attributes[SEMRESATTRS_DEPLOYMENT_ENVIRONMENT] = this.config.app.environment;\n }\n\n // Device/Platform attributes from React Native\n // Note: metas.value contains all meta providers, we need to check if device meta exists\n const allMetas = this.metas.value as Record<string, unknown>;\n const deviceMeta = allMetas['device'] as\n | {\n model?: string;\n brand?: string;\n osName?: string;\n osVersion?: string;\n locale?: string;\n }\n | undefined;\n\n if (deviceMeta?.model) {\n attributes[ATTR_DEVICE_MODEL] = deviceMeta.model;\n }\n\n if (deviceMeta?.brand) {\n attributes[ATTR_DEVICE_BRAND] = deviceMeta.brand;\n }\n\n if (deviceMeta?.osName) {\n attributes[ATTR_DEVICE_PLATFORM] = deviceMeta.osName;\n }\n\n if (deviceMeta?.osVersion) {\n attributes[ATTR_DEVICE_OS_VERSION] = deviceMeta.osVersion;\n }\n\n if (deviceMeta?.locale) {\n attributes[ATTR_DEVICE_LOCALE] = deviceMeta.locale;\n }\n\n attributes[ATTR_PROCESS_RUNTIME_NAME] = 'react-native';\n attributes[ATTR_PROCESS_RUNTIME_VERSION] = deviceMeta?.osVersion ?? 'unknown';\n\n attributes[ATTR_TELEMETRY_DISTRO_NAME] = 'faro-react-native-sdk';\n attributes[ATTR_TELEMETRY_DISTRO_VERSION] = VERSION;\n\n // Merge with user-provided attributes\n Object.assign(attributes, options.resourceAttributes);\n\n const resource = defaultResource().merge(resourceFromAttributes(attributes));\n\n // Create tracer provider with span processors\n const provider = new ReactNativeTracerProvider({\n resource,\n sampler: {\n shouldSample: () => {\n return {\n decision: getSamplingDecision(this.api.getSession()),\n };\n },\n },\n spanProcessors: [\n (options.spanProcessor && new BorrowedSpanProcessor(options.spanProcessor)) ??\n new HttpRequestMonitorSpanProcessor(\n new FaroMetaAttributesSpanProcessor(\n new BatchSpanProcessor(new FaroTraceExporter({ api: this.api }), {\n scheduledDelayMillis: TracingInstrumentation.SCHEDULED_BATCH_DELAY_MS,\n maxExportBatchSize: 30,\n }),\n this.metas\n )\n ),\n ],\n });\n\n const registration: TracingRegistration = {\n instrumentations: [],\n ownsContextManager: false,\n ownsContextManagerInstance: false,\n ownsPropagator: false,\n ownsTracerProvider: false,\n provider,\n };\n this.activeRegistration = registration;\n\n try {\n // Register the provider as the global tracer provider\n // This is CRITICAL for the tracer to generate real trace IDs instead of all zeros\n if (!trace.setGlobalTracerProvider(provider)) {\n throw new Error(\n 'Unable to register the Faro OpenTelemetry tracer provider. Remove the existing global tracer provider before initializing Faro tracing.'\n );\n }\n registration.ownsTracerProvider = true;\n\n // Register a global ContextManager. Without one, OTel falls back to the NoopContextManager,\n // which always returns ROOT_CONTEXT — so when `@opentelemetry/instrumentation-fetch` does\n // `context.with(setSpan(active(), createdSpan), () => _addHeaders(...))` the span set on the\n // wrapped context is invisible inside `_addHeaders` and `propagation.inject` writes nothing.\n // `StackContextManager` is pure JS (no DOM/Zone deps) and works in React Native.\n const contextManager = (options.contextManager ?? new StackContextManager()).enable();\n registration.contextManager = contextManager;\n registration.ownsContextManagerInstance = options.contextManager == null;\n registration.ownsContextManager = context.setGlobalContextManager(contextManager);\n\n // Register the global text-map propagator. Without this, OTel falls back to a\n // NoopTextMapPropagator and `propagation.inject(...)` becomes a no-op, meaning\n // `traceparent` / `tracestate` (and `baggage`) headers are never written on the\n // outbound fetch/XHR — so the backend receives no context and starts a new trace.\n registration.ownsPropagator = propagation.setGlobalPropagator(\n options.propagator ??\n new CompositePropagator({\n propagators: [new W3CTraceContextPropagator(), new W3CBaggagePropagator()],\n })\n );\n\n const {\n enableFetchInstrumentation,\n enableXhrInstrumentation,\n propagateTraceHeaderCorsUrls,\n fetchInstrumentationOptions,\n xhrInstrumentationOptions,\n } = this.options.instrumentationOptions ?? {};\n\n // Get ignore URLs from transports to prevent infinite loops\n const ignoreUrls = this.getIgnoreUrls();\n\n const defaultInstrumentationOptions = {\n ignoreUrls,\n enableFetchInstrumentation,\n enableXhrInstrumentation,\n propagateTraceHeaderCorsUrls,\n fetchInstrumentationOptions,\n xhrInstrumentationOptions,\n };\n\n if (options.instrumentations) {\n registration.instrumentations = options.instrumentations.flat();\n } else if (this.defaultInstrumentations) {\n updateDefaultOTELInstrumentations(this.defaultInstrumentations, defaultInstrumentationOptions);\n registration.instrumentations = this.defaultInstrumentations;\n } else {\n this.defaultInstrumentations = getDefaultOTELInstrumentations(defaultInstrumentationOptions).flat();\n registration.instrumentations = this.defaultInstrumentations;\n }\n\n const instrumentationsNeedingExplicitEnable = registration.instrumentations.filter(\n (instrumentation) =>\n detachedInstrumentations.has(instrumentation) && instrumentation.getConfig().enabled === true\n );\n registration.unregisterInstrumentations = registerInstrumentations({\n instrumentations: registration.instrumentations,\n });\n registration.instrumentations.forEach((instrumentation) => {\n detachedInstrumentations.delete(instrumentation);\n });\n instrumentationsNeedingExplicitEnable.forEach((instrumentation) => instrumentation.enable());\n\n // Expose OTEL API on the global Faro instance for manual span creation\n // This allows users to access trace and context APIs via faro.otel\n const globalFaroInstance = getInternalFaroFromGlobalObject() as FaroWithOtel | undefined;\n if (globalFaroInstance) {\n const faroOtel: OTELApi = { trace, context };\n registration.faro = globalFaroInstance;\n registration.faroOtel = faroOtel;\n registration.previousFaroOtel = globalFaroInstance.otel;\n globalFaroInstance.otel = faroOtel;\n }\n } catch (error) {\n this.destroy();\n throw error;\n }\n }\n\n /**\n * Get ignore URLs from all transports to avoid tracing collector requests\n * CRITICAL: This prevents infinite loops where trace exports trigger more traces\n */\n private getIgnoreUrls(): Array<string | RegExp> {\n // Get URLs from transports' getIgnoreUrls() method\n const transportUrls = this.transports.transports.flatMap((transport: Transport) => {\n return transport.getIgnoreUrls();\n });\n\n // Create regex patterns that match both with and without trailing slashes\n // This is critical because fetch() might add trailing slashes\n const regexPatterns = transportUrls.map((url) => {\n if (typeof url === 'string') {\n // Escape special regex characters and make trailing slash optional\n const escapedUrl = url.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n return new RegExp(`^${escapedUrl}/?$`);\n }\n return url;\n });\n\n // Return dev-server, original transport URLs, and regex patterns for maximum coverage.\n return [...getReactNativeDevServerIgnoreUrls(), ...transportUrls, ...regexPatterns];\n }\n\n private detachRegistration(): TracingRegistration | undefined {\n const registration = this.activeRegistration;\n if (!registration) {\n return undefined;\n }\n this.activeRegistration = undefined;\n registration.instrumentations.forEach((instrumentation) => detachedInstrumentations.add(instrumentation));\n\n let unregistered = false;\n if (registration.unregisterInstrumentations) {\n try {\n registration.unregisterInstrumentations();\n unregistered = true;\n } catch (error) {\n this.logCleanupError('unregister OpenTelemetry instrumentations', error);\n }\n }\n if (!unregistered) {\n registration.instrumentations.forEach((instrumentation) => {\n this.runCleanup(`disable ${instrumentation.instrumentationName}`, () => instrumentation.disable());\n });\n }\n\n const { faro, faroOtel } = registration;\n if (faro && faroOtel && faro.otel === faroOtel) {\n this.runCleanup('restore faro.otel', () => {\n if (registration.previousFaroOtel === undefined) {\n delete faro.otel;\n } else {\n faro.otel = registration.previousFaroOtel;\n }\n });\n }\n\n if (registration.ownsPropagator) {\n this.runCleanup('clear the OpenTelemetry propagator', () => propagation.disable());\n }\n if (registration.ownsContextManager) {\n this.runCleanup('clear the OpenTelemetry context manager', () => context.disable());\n } else if (registration.ownsContextManagerInstance && registration.contextManager) {\n this.runCleanup('disable the unused OpenTelemetry context manager', () => registration.contextManager?.disable());\n }\n if (registration.ownsTracerProvider) {\n this.runCleanup('clear the OpenTelemetry tracer provider', () => trace.disable());\n }\n\n return registration;\n }\n\n private runCleanup(action: string, cleanup: VoidFunction): void {\n try {\n cleanup();\n } catch (error) {\n this.logCleanupError(action, error);\n }\n }\n\n private logCleanupError(action: string, error: unknown): void {\n try {\n this.logError(`Failed to ${action}:`, error);\n } catch (_loggingError) {\n // Cleanup must not fail because a custom logger threw.\n }\n }\n\n private startProviderShutdown(registration: TracingRegistration): Promise<void> {\n if (!registration.shutdownPromise) {\n const shutdownPromise = Promise.resolve()\n .then(() => registration.provider.forceFlush())\n .catch((error) => {\n this.logCleanupError('flush the OpenTelemetry tracer provider', error);\n })\n .then(() => registration.provider.shutdown());\n registration.shutdownPromise = shutdownPromise;\n this.pendingShutdowns.add(shutdownPromise);\n void shutdownPromise.then(\n () => this.pendingShutdowns.delete(shutdownPromise),\n () => this.pendingShutdowns.delete(shutdownPromise)\n );\n }\n return registration.shutdownPromise;\n }\n\n private startSuppliedSpanProcessorShutdown(): Promise<void> | undefined {\n const spanProcessor = this.options.spanProcessor;\n if (!spanProcessor) {\n return undefined;\n }\n if (!this.suppliedSpanProcessorShutdown) {\n try {\n this.suppliedSpanProcessorShutdown = spanProcessor.shutdown();\n } catch (error) {\n this.suppliedSpanProcessorShutdown = Promise.reject(error);\n }\n }\n return this.suppliedSpanProcessorShutdown;\n }\n\n /** Detach request instrumentations and wait for the tracer provider to flush. */\n async shutdown(): Promise<void> {\n const pendingShutdowns = Array.from(this.pendingShutdowns);\n const registration = this.detachRegistration();\n if (registration) {\n pendingShutdowns.push(this.startProviderShutdown(registration));\n }\n\n let firstError: unknown;\n let shutdownFailed = false;\n for (const shutdownPromise of pendingShutdowns) {\n try {\n await shutdownPromise;\n } catch (error) {\n if (!shutdownFailed) {\n firstError = error;\n shutdownFailed = true;\n }\n }\n }\n const spanProcessorShutdown = this.startSuppliedSpanProcessorShutdown();\n if (spanProcessorShutdown) {\n try {\n await spanProcessorShutdown;\n } catch (error) {\n if (!shutdownFailed) {\n firstError = error;\n shutdownFailed = true;\n }\n }\n }\n if (shutdownFailed) {\n throw firstError;\n }\n }\n\n /** Detach synchronously when Faro removes this instrumentation. */\n destroy(): void {\n const registration = this.detachRegistration();\n if (!registration) {\n return;\n }\n void this.startProviderShutdown(registration).then(undefined, (error) => {\n this.logCleanupError('shut down the OpenTelemetry tracer provider', error);\n });\n }\n}\n"]}