@grafana/faro-react-native-tracing 1.3.0 → 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.
- package/README.md +33 -0
- package/dist/cjs/exporters/faroTraceExporter.js +2 -1
- package/dist/cjs/exporters/faroTraceExporter.js.map +1 -1
- package/dist/cjs/exporters/faroTraceExporter.utils.js +33 -0
- package/dist/cjs/exporters/faroTraceExporter.utils.js.map +1 -1
- package/dist/cjs/instrumentation.js +259 -44
- package/dist/cjs/instrumentation.js.map +1 -1
- package/dist/cjs/instrumentations/getDefaultOTELInstrumentations.js +28 -20
- package/dist/cjs/instrumentations/getDefaultOTELInstrumentations.js.map +1 -1
- package/dist/cjs/instrumentations/instrumentationUtils.js +15 -0
- package/dist/cjs/instrumentations/instrumentationUtils.js.map +1 -1
- package/dist/cjs/processors/httpRequestMonitorSpanProcessor.js +27 -13
- package/dist/cjs/processors/httpRequestMonitorSpanProcessor.js.map +1 -1
- package/dist/cjs/types.js.map +1 -1
- package/dist/esm/exporters/faroTraceExporter.js +2 -1
- package/dist/esm/exporters/faroTraceExporter.js.map +1 -1
- package/dist/esm/exporters/faroTraceExporter.utils.js +32 -0
- package/dist/esm/exporters/faroTraceExporter.utils.js.map +1 -1
- package/dist/esm/instrumentation.js +237 -43
- package/dist/esm/instrumentation.js.map +1 -1
- package/dist/esm/instrumentations/getDefaultOTELInstrumentations.js +27 -21
- package/dist/esm/instrumentations/getDefaultOTELInstrumentations.js.map +1 -1
- package/dist/esm/instrumentations/instrumentationUtils.js +14 -0
- package/dist/esm/instrumentations/instrumentationUtils.js.map +1 -1
- package/dist/esm/processors/httpRequestMonitorSpanProcessor.js +27 -13
- package/dist/esm/processors/httpRequestMonitorSpanProcessor.js.map +1 -1
- package/dist/esm/types.js.map +1 -1
- package/dist/types/instrumentation.d.ts +12 -4
- package/dist/types/instrumentations/getDefaultOTELInstrumentations.d.ts +2 -0
- package/dist/types/instrumentations/instrumentationUtils.d.ts +2 -0
- package/dist/types/types.d.ts +4 -0
- package/package.json +13 -13
- package/src/exporters/faroTraceExporter.events.test.ts +90 -0
- package/src/exporters/faroTraceExporter.ts +2 -1
- package/src/exporters/faroTraceExporter.utils.ts +32 -0
- package/src/httpSemantics.test.ts +300 -0
- package/src/instrumentation.test.ts +740 -0
- package/src/instrumentation.ts +299 -67
- package/src/instrumentations/getDefaultOTELInstrumentations.test.ts +28 -1
- package/src/instrumentations/getDefaultOTELInstrumentations.ts +41 -32
- package/src/instrumentations/instrumentationUtils.ts +13 -0
- package/src/processors/httpRequestMonitorSpanProcessor.ts +27 -15
- 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
|
|
@@ -38,6 +38,7 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
39
|
exports.FaroTraceExporter = void 0;
|
|
40
40
|
var core_1 = require("@opentelemetry/core");
|
|
41
|
+
var utils_1 = require("@opentelemetry/otlp-transformer/build/src/common/utils");
|
|
41
42
|
var internal_1 = require("@opentelemetry/otlp-transformer/build/src/trace/internal");
|
|
42
43
|
var faro_core_1 = require("@grafana/faro-core");
|
|
43
44
|
var internalLogger = (0, faro_core_1.createInternalLogger)();
|
|
@@ -65,7 +66,7 @@ var FaroTraceExporter = /** @class */ (function () {
|
|
|
65
66
|
}
|
|
66
67
|
try {
|
|
67
68
|
// Convert spans to OTLP format
|
|
68
|
-
var traceEvent = (0, internal_1.createExportTraceServiceRequest)(spans,
|
|
69
|
+
var traceEvent = (0, internal_1.createExportTraceServiceRequest)(spans, utils_1.JSON_ENCODER);
|
|
69
70
|
// Send traces to Faro
|
|
70
71
|
this.config.api.pushTraces(traceEvent);
|
|
71
72
|
// Send Faro events for CLIENT spans (HTTP requests, etc.)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"faroTraceExporter.js","sourceRoot":"","sources":["../../../src/exporters/faroTraceExporter.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,4CAAuD;AAEvD,qFAA2G;AAG3G,gDAA0D;AAE1D,IAAM,cAAc,GAAG,IAAA,gCAAoB,GAAE,CAAC;AAI9C,qEAA2D;AAE3D;;;;;;;;;GASG;AACH;IAGE,2BAAoB,MAA+B;QAA/B,WAAM,GAAN,MAAM,CAAyB;QAF3C,gBAAW,GAAG,KAAK,CAAC;IAE0B,CAAC;IAEvD,kCAAM,GAAN,UAAO,KAAqB,EAAE,cAA8C;QAC1E,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACrB,cAAc,CAAC,KAAK,CAAC,+DAA+D,CAAC,CAAC;YACtF,cAAc,CAAC,EAAE,IAAI,EAAE,uBAAgB,CAAC,MAAM,EAAE,CAAC,CAAC;YAClD,OAAO;QACT,CAAC;QAED,IAAI,CAAC;YACH,+BAA+B;YAC/B,IAAM,UAAU,GAAG,IAAA,0CAA+B,EAAC,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"faroTraceExporter.js","sourceRoot":"","sources":["../../../src/exporters/faroTraceExporter.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,4CAAuD;AAEvD,gFAAsF;AACtF,qFAA2G;AAG3G,gDAA0D;AAE1D,IAAM,cAAc,GAAG,IAAA,gCAAoB,GAAE,CAAC;AAI9C,qEAA2D;AAE3D;;;;;;;;;GASG;AACH;IAGE,2BAAoB,MAA+B;QAA/B,WAAM,GAAN,MAAM,CAAyB;QAF3C,gBAAW,GAAG,KAAK,CAAC;IAE0B,CAAC;IAEvD,kCAAM,GAAN,UAAO,KAAqB,EAAE,cAA8C;QAC1E,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACrB,cAAc,CAAC,KAAK,CAAC,+DAA+D,CAAC,CAAC;YACtF,cAAc,CAAC,EAAE,IAAI,EAAE,uBAAgB,CAAC,MAAM,EAAE,CAAC,CAAC;YAClD,OAAO;QACT,CAAC;QAED,IAAI,CAAC;YACH,+BAA+B;YAC/B,IAAM,UAAU,GAAG,IAAA,0CAA+B,EAAC,KAAK,EAAE,oBAAY,CAAC,CAAC;YAExE,sBAAsB;YACtB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;YAEvC,0DAA0D;YAC1D,uDAAuD;YACvD,IAAA,wCAAc,EAAC,UAAU,CAAC,aAAa,CAAC,CAAC;YAEzC,cAAc,CAAC,EAAE,IAAI,EAAE,uBAAgB,CAAC,OAAO,EAAE,CAAC,CAAC;QACrD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,2BAA2B;YAC3B,cAAc,CAAC,KAAK,CAAC,2CAA2C,EAAE,KAAK,CAAC,CAAC;YACzE,cAAc,CAAC,EAAE,IAAI,EAAE,uBAAgB,CAAC,MAAM,EAAE,CAAC,CAAC;QACpD,CAAC;IACH,CAAC;IAEK,oCAAQ,GAAd;;;gBACE,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;gBACxB,sBAAO,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,EAAC;;;KACnC;IAEK,sCAAU,GAAhB;;;gBACE,4DAA4D;gBAC5D,sBAAO,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,EAAC;;;KACnC;IACH,wBAAC;AAAD,CAAC,AAxCD,IAwCC;AAxCY,8CAAiB","sourcesContent":["import { ExportResultCode } from '@opentelemetry/core';\nimport type { ExportResult } from '@opentelemetry/core';\nimport { JSON_ENCODER } from '@opentelemetry/otlp-transformer/build/src/common/utils';\nimport { createExportTraceServiceRequest } from '@opentelemetry/otlp-transformer/build/src/trace/internal';\nimport type { ReadableSpan, SpanExporter } from '@opentelemetry/sdk-trace-base';\n\nimport { createInternalLogger } from '@grafana/faro-core';\n\nconst internalLogger = createInternalLogger();\n\nimport type { FaroTraceExporterConfig } from '../types';\n\nimport { sendFaroEvents } from './faroTraceExporter.utils';\n\n/**\n * FaroTraceExporter for React Native\n *\n * Exports OpenTelemetry spans to Faro backend using pushTraces API.\n *\n * IMPORTANT: To avoid infinite loops:\n * - Uses internalLogger instead of console\n * - Does NOT log during export (except errors)\n * - Relies on Faro's internal deduplication\n */\nexport class FaroTraceExporter implements SpanExporter {\n private _isShutdown = false;\n\n constructor(private config: FaroTraceExporterConfig) {}\n\n export(spans: ReadableSpan[], resultCallback: (result: ExportResult) => void): void {\n if (this._isShutdown) {\n internalLogger.error('FaroTraceExporter: Cannot export spans, exporter is shut down');\n resultCallback({ code: ExportResultCode.FAILED });\n return;\n }\n\n try {\n // Convert spans to OTLP format\n const traceEvent = createExportTraceServiceRequest(spans, JSON_ENCODER);\n\n // Send traces to Faro\n this.config.api.pushTraces(traceEvent);\n\n // Send Faro events for CLIENT spans (HTTP requests, etc.)\n // This is done WITHOUT logging to avoid infinite loops\n sendFaroEvents(traceEvent.resourceSpans);\n\n resultCallback({ code: ExportResultCode.SUCCESS });\n } catch (error) {\n // Only log critical errors\n internalLogger.error('FaroTraceExporter: Failed to export spans', error);\n resultCallback({ code: ExportResultCode.FAILED });\n }\n }\n\n async shutdown(): Promise<void> {\n this._isShutdown = true;\n return Promise.resolve(undefined);\n }\n\n async forceFlush(): Promise<void> {\n // No-op for now - spans are sent immediately via pushTraces\n return Promise.resolve(undefined);\n }\n}\n"]}
|
|
@@ -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":";;
|
|
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
|
-
|
|
80
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
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
|
-
|
|
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
|
-
|
|
256
|
-
|
|
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
|
-
|
|
424
|
+
if (!(_i < pendingShutdowns_1.length)) return [3 /*break*/, 6];
|
|
425
|
+
shutdownPromise = pendingShutdowns_1[_i];
|
|
259
426
|
_a.label = 2;
|
|
260
|
-
case 2:
|
|
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));
|