@grafana/faro-react-native-tracing 1.0.0-alpha.1
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/LICENSE +201 -0
- package/README.md +833 -0
- package/dist/cjs/exporters/faroTraceExporter.js +101 -0
- package/dist/cjs/exporters/faroTraceExporter.js.map +1 -0
- package/dist/cjs/exporters/faroTraceExporter.utils.js +99 -0
- package/dist/cjs/exporters/faroTraceExporter.utils.js.map +1 -0
- package/dist/cjs/index.js +17 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/instrumentation.js +251 -0
- package/dist/cjs/instrumentation.js.map +1 -0
- package/dist/cjs/instrumentations/getDefaultOTELInstrumentations.js +77 -0
- package/dist/cjs/instrumentations/getDefaultOTELInstrumentations.js.map +1 -0
- package/dist/cjs/instrumentations/instrumentationUtils.js +89 -0
- package/dist/cjs/instrumentations/instrumentationUtils.js.map +1 -0
- package/dist/cjs/processors/faroMetaAttributesSpanProcessor.js +59 -0
- package/dist/cjs/processors/faroMetaAttributesSpanProcessor.js.map +1 -0
- package/dist/cjs/processors/httpRequestMonitorSpanProcessor.js +98 -0
- package/dist/cjs/processors/httpRequestMonitorSpanProcessor.js.map +1 -0
- package/dist/cjs/semconv.js +29 -0
- package/dist/cjs/semconv.js.map +1 -0
- package/dist/cjs/types.js +3 -0
- package/dist/cjs/types.js.map +1 -0
- package/dist/cjs/utils/sampler.js +21 -0
- package/dist/cjs/utils/sampler.js.map +1 -0
- package/dist/esm/exporters/faroTraceExporter.js +65 -0
- package/dist/esm/exporters/faroTraceExporter.js.map +1 -0
- package/dist/esm/exporters/faroTraceExporter.utils.js +88 -0
- package/dist/esm/exporters/faroTraceExporter.utils.js.map +1 -0
- package/dist/esm/index.js +7 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/instrumentation.js +183 -0
- package/dist/esm/instrumentation.js.map +1 -0
- package/dist/esm/instrumentations/getDefaultOTELInstrumentations.js +62 -0
- package/dist/esm/instrumentations/getDefaultOTELInstrumentations.js.map +1 -0
- package/dist/esm/instrumentations/instrumentationUtils.js +83 -0
- package/dist/esm/instrumentations/instrumentationUtils.js.map +1 -0
- package/dist/esm/processors/faroMetaAttributesSpanProcessor.js +54 -0
- package/dist/esm/processors/faroMetaAttributesSpanProcessor.js.map +1 -0
- package/dist/esm/processors/httpRequestMonitorSpanProcessor.js +93 -0
- package/dist/esm/processors/httpRequestMonitorSpanProcessor.js.map +1 -0
- package/dist/esm/semconv.js +26 -0
- package/dist/esm/semconv.js.map +1 -0
- package/dist/esm/types.js +2 -0
- package/dist/esm/types.js.map +1 -0
- package/dist/esm/utils/sampler.js +17 -0
- package/dist/esm/utils/sampler.js.map +1 -0
- package/dist/types/exporters/faroTraceExporter.d.ts +21 -0
- package/dist/types/exporters/faroTraceExporter.utils.d.ts +17 -0
- package/dist/types/index.d.ts +7 -0
- package/dist/types/instrumentation.d.ts +46 -0
- package/dist/types/instrumentations/getDefaultOTELInstrumentations.d.ts +17 -0
- package/dist/types/instrumentations/instrumentationUtils.d.ts +26 -0
- package/dist/types/processors/faroMetaAttributesSpanProcessor.d.ts +22 -0
- package/dist/types/processors/httpRequestMonitorSpanProcessor.d.ts +18 -0
- package/dist/types/semconv.d.ts +19 -0
- package/dist/types/types.d.ts +31 -0
- package/dist/types/utils/sampler.d.ts +12 -0
- package/package.json +77 -0
- package/src/exporters/faroTraceExporter.test.ts +110 -0
- package/src/exporters/faroTraceExporter.ts +64 -0
- package/src/exporters/faroTraceExporter.utils.ts +105 -0
- package/src/index.ts +16 -0
- package/src/instrumentation.ts +241 -0
- package/src/instrumentations/getDefaultOTELInstrumentations.test.ts +70 -0
- package/src/instrumentations/getDefaultOTELInstrumentations.ts +83 -0
- package/src/instrumentations/instrumentationUtils.test.ts +107 -0
- package/src/instrumentations/instrumentationUtils.ts +106 -0
- package/src/processors/faroMetaAttributesSpanProcessor.test.ts +127 -0
- package/src/processors/faroMetaAttributesSpanProcessor.ts +71 -0
- package/src/processors/httpRequestMonitorSpanProcessor.ts +106 -0
- package/src/semconv.ts +31 -0
- package/src/types.ts +39 -0
- package/src/utils/sampler.test.ts +51 -0
- package/src/utils/sampler.ts +19 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { context, trace } from '@opentelemetry/api';
|
|
11
|
+
import { registerInstrumentations } from '@opentelemetry/instrumentation';
|
|
12
|
+
import { defaultResource, resourceFromAttributes } from '@opentelemetry/resources';
|
|
13
|
+
import { BatchSpanProcessor, BasicTracerProvider as ReactNativeTracerProvider } from '@opentelemetry/sdk-trace-base';
|
|
14
|
+
import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION, SEMRESATTRS_DEPLOYMENT_ENVIRONMENT, } from '@opentelemetry/semantic-conventions';
|
|
15
|
+
import { BaseInstrumentation, getInternalFaroFromGlobalObject, VERSION } from '@grafana/faro-core';
|
|
16
|
+
import { FaroTraceExporter } from './exporters/faroTraceExporter';
|
|
17
|
+
import { getDefaultOTELInstrumentations } from './instrumentations/getDefaultOTELInstrumentations';
|
|
18
|
+
import { FaroMetaAttributesSpanProcessor } from './processors/faroMetaAttributesSpanProcessor';
|
|
19
|
+
import { HttpRequestMonitorSpanProcessor } from './processors/httpRequestMonitorSpanProcessor';
|
|
20
|
+
import { ATTR_APP_VERSION, ATTR_DEPLOYMENT_ENVIRONMENT_NAME, ATTR_DEVICE_BRAND, ATTR_DEVICE_LOCALE, ATTR_DEVICE_MODEL, ATTR_DEVICE_OS_VERSION, ATTR_DEVICE_PLATFORM, ATTR_PROCESS_RUNTIME_NAME, ATTR_PROCESS_RUNTIME_VERSION, ATTR_SERVICE_NAMESPACE, ATTR_TELEMETRY_DISTRO_NAME, ATTR_TELEMETRY_DISTRO_VERSION, } from './semconv';
|
|
21
|
+
import { getSamplingDecision } from './utils/sampler';
|
|
22
|
+
// Import React Native TracerProvider
|
|
23
|
+
// Note: We use the base provider since React Native doesn't have a specific one
|
|
24
|
+
/**
|
|
25
|
+
* TracingInstrumentation for React Native
|
|
26
|
+
*
|
|
27
|
+
* Enables distributed tracing with OpenTelemetry for React Native applications.
|
|
28
|
+
*
|
|
29
|
+
* IMPORTANT: Infinite loop prevention
|
|
30
|
+
* - Uses internalLogger for debugging instead of console
|
|
31
|
+
* - Collector URLs are added to ignoreUrls in HTTP instrumentation
|
|
32
|
+
* - BatchSpanProcessor delays span export to avoid blocking
|
|
33
|
+
* - No console logging during trace export
|
|
34
|
+
*
|
|
35
|
+
* Example usage:
|
|
36
|
+
* ```ts
|
|
37
|
+
* import { initializeFaro } from '@grafana/faro-react-native';
|
|
38
|
+
* import { TracingInstrumentation } from '@grafana/faro-react-native-tracing';
|
|
39
|
+
*
|
|
40
|
+
* initializeFaro({
|
|
41
|
+
* // ... other config
|
|
42
|
+
* instrumentations: [
|
|
43
|
+
* new TracingInstrumentation({
|
|
44
|
+
* propagateTraceHeaderCorsUrls: [/https:\\/\\/my-api\\.com/],
|
|
45
|
+
* }),
|
|
46
|
+
* ],
|
|
47
|
+
* });
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
export class TracingInstrumentation extends BaseInstrumentation {
|
|
51
|
+
constructor(options = {}) {
|
|
52
|
+
super();
|
|
53
|
+
this.options = options;
|
|
54
|
+
this.name = '@grafana/faro-react-native-tracing';
|
|
55
|
+
this.version = VERSION;
|
|
56
|
+
}
|
|
57
|
+
initialize() {
|
|
58
|
+
var _a, _b, _c, _d;
|
|
59
|
+
const options = this.options;
|
|
60
|
+
const attributes = {};
|
|
61
|
+
// App attributes
|
|
62
|
+
if (this.config.app.name) {
|
|
63
|
+
attributes[ATTR_SERVICE_NAME] = this.config.app.name;
|
|
64
|
+
}
|
|
65
|
+
if (this.config.app.namespace) {
|
|
66
|
+
attributes[ATTR_SERVICE_NAMESPACE] = this.config.app.namespace;
|
|
67
|
+
}
|
|
68
|
+
if (this.config.app.version) {
|
|
69
|
+
attributes[ATTR_SERVICE_VERSION] = this.config.app.version;
|
|
70
|
+
attributes[ATTR_APP_VERSION] = this.config.app.version;
|
|
71
|
+
}
|
|
72
|
+
if (this.config.app.environment) {
|
|
73
|
+
attributes[ATTR_DEPLOYMENT_ENVIRONMENT_NAME] = this.config.app.environment;
|
|
74
|
+
/**
|
|
75
|
+
* @deprecated will be removed in the future and has been replaced by ATTR_DEPLOYMENT_ENVIRONMENT_NAME (deployment.environment.name)
|
|
76
|
+
* We need to keep this for compatibility with some internal services for now.
|
|
77
|
+
*/
|
|
78
|
+
attributes[SEMRESATTRS_DEPLOYMENT_ENVIRONMENT] = this.config.app.environment;
|
|
79
|
+
}
|
|
80
|
+
// Device/Platform attributes from React Native
|
|
81
|
+
// Note: metas.value contains all meta providers, we need to check if device meta exists
|
|
82
|
+
const allMetas = this.metas.value;
|
|
83
|
+
const deviceMeta = allMetas['device'];
|
|
84
|
+
if (deviceMeta === null || deviceMeta === void 0 ? void 0 : deviceMeta.model) {
|
|
85
|
+
attributes[ATTR_DEVICE_MODEL] = deviceMeta.model;
|
|
86
|
+
}
|
|
87
|
+
if (deviceMeta === null || deviceMeta === void 0 ? void 0 : deviceMeta.brand) {
|
|
88
|
+
attributes[ATTR_DEVICE_BRAND] = deviceMeta.brand;
|
|
89
|
+
}
|
|
90
|
+
if (deviceMeta === null || deviceMeta === void 0 ? void 0 : deviceMeta.osName) {
|
|
91
|
+
attributes[ATTR_DEVICE_PLATFORM] = deviceMeta.osName;
|
|
92
|
+
}
|
|
93
|
+
if (deviceMeta === null || deviceMeta === void 0 ? void 0 : deviceMeta.osVersion) {
|
|
94
|
+
attributes[ATTR_DEVICE_OS_VERSION] = deviceMeta.osVersion;
|
|
95
|
+
}
|
|
96
|
+
if (deviceMeta === null || deviceMeta === void 0 ? void 0 : deviceMeta.locale) {
|
|
97
|
+
attributes[ATTR_DEVICE_LOCALE] = deviceMeta.locale;
|
|
98
|
+
}
|
|
99
|
+
attributes[ATTR_PROCESS_RUNTIME_NAME] = 'react-native';
|
|
100
|
+
attributes[ATTR_PROCESS_RUNTIME_VERSION] = (_a = deviceMeta === null || deviceMeta === void 0 ? void 0 : deviceMeta.osVersion) !== null && _a !== void 0 ? _a : 'unknown';
|
|
101
|
+
attributes[ATTR_TELEMETRY_DISTRO_NAME] = 'faro-react-native-sdk';
|
|
102
|
+
attributes[ATTR_TELEMETRY_DISTRO_VERSION] = VERSION;
|
|
103
|
+
// Merge with user-provided attributes
|
|
104
|
+
Object.assign(attributes, options.resourceAttributes);
|
|
105
|
+
const resource = defaultResource().merge(resourceFromAttributes(attributes));
|
|
106
|
+
// Create tracer provider with span processors
|
|
107
|
+
this.provider = new ReactNativeTracerProvider({
|
|
108
|
+
resource,
|
|
109
|
+
sampler: {
|
|
110
|
+
shouldSample: () => {
|
|
111
|
+
return {
|
|
112
|
+
decision: getSamplingDecision(this.api.getSession()),
|
|
113
|
+
};
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
spanProcessors: [
|
|
117
|
+
(_b = options.spanProcessor) !== null && _b !== void 0 ? _b : new HttpRequestMonitorSpanProcessor(new FaroMetaAttributesSpanProcessor(new BatchSpanProcessor(new FaroTraceExporter({ api: this.api }), {
|
|
118
|
+
scheduledDelayMillis: TracingInstrumentation.SCHEDULED_BATCH_DELAY_MS,
|
|
119
|
+
maxExportBatchSize: 30,
|
|
120
|
+
}), this.metas)),
|
|
121
|
+
],
|
|
122
|
+
});
|
|
123
|
+
// Register the provider as the global tracer provider
|
|
124
|
+
// This is CRITICAL for the tracer to generate real trace IDs instead of all zeros
|
|
125
|
+
trace.setGlobalTracerProvider(this.provider);
|
|
126
|
+
const { propagateTraceHeaderCorsUrls, fetchInstrumentationOptions, xhrInstrumentationOptions } = (_c = this.options.instrumentationOptions) !== null && _c !== void 0 ? _c : {};
|
|
127
|
+
// Get ignore URLs from transports to prevent infinite loops
|
|
128
|
+
const ignoreUrls = this.getIgnoreUrls();
|
|
129
|
+
// Register instrumentations
|
|
130
|
+
registerInstrumentations({
|
|
131
|
+
instrumentations: (_d = options.instrumentations) !== null && _d !== void 0 ? _d : getDefaultOTELInstrumentations({
|
|
132
|
+
ignoreUrls,
|
|
133
|
+
propagateTraceHeaderCorsUrls,
|
|
134
|
+
fetchInstrumentationOptions,
|
|
135
|
+
xhrInstrumentationOptions,
|
|
136
|
+
}),
|
|
137
|
+
});
|
|
138
|
+
// Expose OTEL API on the global Faro instance for manual span creation
|
|
139
|
+
// This allows users to access trace and context APIs via faro.otel
|
|
140
|
+
const globalFaroInstance = getInternalFaroFromGlobalObject();
|
|
141
|
+
if (globalFaroInstance) {
|
|
142
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Extending Faro instance with OTEL API
|
|
143
|
+
globalFaroInstance.otel = {
|
|
144
|
+
trace,
|
|
145
|
+
context,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Get ignore URLs from all transports to avoid tracing collector requests
|
|
151
|
+
* CRITICAL: This prevents infinite loops where trace exports trigger more traces
|
|
152
|
+
*/
|
|
153
|
+
getIgnoreUrls() {
|
|
154
|
+
// Get URLs from transports' getIgnoreUrls() method
|
|
155
|
+
const transportUrls = this.transports.transports.flatMap((transport) => {
|
|
156
|
+
return transport.getIgnoreUrls();
|
|
157
|
+
});
|
|
158
|
+
// Create regex patterns that match both with and without trailing slashes
|
|
159
|
+
// This is critical because fetch() might add trailing slashes
|
|
160
|
+
const regexPatterns = transportUrls.map((url) => {
|
|
161
|
+
if (typeof url === 'string') {
|
|
162
|
+
// Escape special regex characters and make trailing slash optional
|
|
163
|
+
const escapedUrl = url.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
164
|
+
return new RegExp(`^${escapedUrl}/?$`);
|
|
165
|
+
}
|
|
166
|
+
return url;
|
|
167
|
+
});
|
|
168
|
+
// Return both original URLs and regex patterns for maximum coverage
|
|
169
|
+
return [...transportUrls, ...regexPatterns];
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Shutdown the tracer provider
|
|
173
|
+
*/
|
|
174
|
+
shutdown() {
|
|
175
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
176
|
+
if (this.provider) {
|
|
177
|
+
yield this.provider.shutdown();
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
TracingInstrumentation.SCHEDULED_BATCH_DELAY_MS = 1000;
|
|
183
|
+
//# sourceMappingURL=instrumentation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instrumentation.js","sourceRoot":"","sources":["../../src/instrumentation.ts"],"names":[],"mappings":"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","sourcesContent":["import { context, trace } from '@opentelemetry/api';\nimport type { Attributes } from '@opentelemetry/api';\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 {\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 { 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 const { propagateTraceHeaderCorsUrls, fetchInstrumentationOptions, 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 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 both original URLs and regex patterns for maximum coverage\n return [...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"]}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
|
+
var t = {};
|
|
3
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
4
|
+
t[p] = s[p];
|
|
5
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
6
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
7
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
8
|
+
t[p[i]] = s[p[i]];
|
|
9
|
+
}
|
|
10
|
+
return t;
|
|
11
|
+
};
|
|
12
|
+
import { FetchInstrumentation } from '@opentelemetry/instrumentation-fetch';
|
|
13
|
+
import { XMLHttpRequestInstrumentation } from '@opentelemetry/instrumentation-xml-http-request';
|
|
14
|
+
import { faro, UserActionState } from '@grafana/faro-core';
|
|
15
|
+
import { fetchCustomAttributeFunctionWithDefaults, xhrCustomAttributeFunctionWithDefaults, } from './instrumentationUtils';
|
|
16
|
+
/**
|
|
17
|
+
* Get default OTEL instrumentations for React Native
|
|
18
|
+
*
|
|
19
|
+
* This function creates the default OpenTelemetry instrumentations for React Native:
|
|
20
|
+
* - FetchInstrumentation: Traces fetch() API calls
|
|
21
|
+
* - XMLHttpRequestInstrumentation: Traces XMLHttpRequest and axios (which uses XHR)
|
|
22
|
+
*
|
|
23
|
+
* IMPORTANT: Infinite loop prevention
|
|
24
|
+
* - ignoreUrls is used to exclude Faro collector URLs
|
|
25
|
+
* - ignoreNetworkEvents is true to avoid duplicate events
|
|
26
|
+
* - No console logging during instrumentation
|
|
27
|
+
*
|
|
28
|
+
* @param options - Configuration options
|
|
29
|
+
* @returns Array of OTEL instrumentations
|
|
30
|
+
*/
|
|
31
|
+
export function getDefaultOTELInstrumentations(options = {}) {
|
|
32
|
+
const { fetchInstrumentationOptions, xhrInstrumentationOptions } = options, sharedOptions = __rest(options, ["fetchInstrumentationOptions", "xhrInstrumentationOptions"]);
|
|
33
|
+
const fetchOpts = createFetchInstrumentationOptions(fetchInstrumentationOptions, sharedOptions);
|
|
34
|
+
const xhrOpts = createXhrInstrumentationOptions(xhrInstrumentationOptions, sharedOptions);
|
|
35
|
+
return [new FetchInstrumentation(fetchOpts), new XMLHttpRequestInstrumentation(xhrOpts)];
|
|
36
|
+
}
|
|
37
|
+
function createFetchInstrumentationOptions(fetchInstrumentationOptions, sharedOptions) {
|
|
38
|
+
return Object.assign(Object.assign(Object.assign(Object.assign({}, sharedOptions), {
|
|
39
|
+
// Ignore network performance events to avoid duplicates
|
|
40
|
+
ignoreNetworkEvents: true }), fetchInstrumentationOptions), {
|
|
41
|
+
// Always keep this function
|
|
42
|
+
applyCustomAttributesOnSpan: fetchCustomAttributeFunctionWithDefaults(fetchInstrumentationOptions === null || fetchInstrumentationOptions === void 0 ? void 0 : fetchInstrumentationOptions.applyCustomAttributesOnSpan),
|
|
43
|
+
// Request hook to add user action context
|
|
44
|
+
requestHook: (span, _) => {
|
|
45
|
+
try {
|
|
46
|
+
const currentAction = faro.api.getActiveUserAction();
|
|
47
|
+
if (currentAction &&
|
|
48
|
+
(currentAction === null || currentAction === void 0 ? void 0 : currentAction.getState()) === UserActionState.Started) {
|
|
49
|
+
span.setAttribute('faro.action.user.name', currentAction.name);
|
|
50
|
+
span.setAttribute('faro.action.user.parentId', currentAction.parentId);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
catch (_error) {
|
|
54
|
+
// Silently fail - don't log to avoid infinite loops
|
|
55
|
+
// The span will just not have user action context
|
|
56
|
+
}
|
|
57
|
+
} });
|
|
58
|
+
}
|
|
59
|
+
function createXhrInstrumentationOptions(xhrInstrumentationOptions, sharedOptions) {
|
|
60
|
+
return Object.assign(Object.assign(Object.assign(Object.assign({}, sharedOptions), { ignoreNetworkEvents: true }), xhrInstrumentationOptions), { applyCustomAttributesOnSpan: xhrCustomAttributeFunctionWithDefaults(xhrInstrumentationOptions === null || xhrInstrumentationOptions === void 0 ? void 0 : xhrInstrumentationOptions.applyCustomAttributesOnSpan) });
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=getDefaultOTELInstrumentations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getDefaultOTELInstrumentations.js","sourceRoot":"","sources":["../../../src/instrumentations/getDefaultOTELInstrumentations.ts"],"names":[],"mappings":";;;;;;;;;;;AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,sCAAsC,CAAC;AAC5E,OAAO,EAAE,6BAA6B,EAAE,MAAM,iDAAiD,CAAC;AAEhG,OAAO,EAAE,IAAI,EAAoC,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAI7F,OAAO,EACL,wCAAwC,EACxC,sCAAsC,GACvC,MAAM,wBAAwB,CAAC;AAEhC;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,8BAA8B,CAAC,UAA0C,EAAE;IACzF,MAAM,EAAE,2BAA2B,EAAE,yBAAyB,KAAuB,OAAO,EAAzB,aAAa,UAAK,OAAO,EAAtF,4DAA4E,CAAU,CAAC;IAE7F,MAAM,SAAS,GAAG,iCAAiC,CAAC,2BAA2B,EAAE,aAAa,CAAC,CAAC;IAChG,MAAM,OAAO,GAAG,+BAA+B,CAAC,yBAAyB,EAAE,aAAa,CAAC,CAAC;IAE1F,OAAO,CAAC,IAAI,oBAAoB,CAAC,SAAS,CAAC,EAAE,IAAI,6BAA6B,CAAC,OAAO,CAAC,CAAC,CAAC;AAC3F,CAAC;AAED,SAAS,iCAAiC,CACxC,2BAA0F,EAC1F,aAAsC;IAEtC,mEACK,aAAa;QAChB,wDAAwD;QACxD,mBAAmB,EAAE,IAAI,KAEtB,2BAA2B;QAC9B,4BAA4B;QAC5B,2BAA2B,EAAE,wCAAwC,CACnE,2BAA2B,aAA3B,2BAA2B,uBAA3B,2BAA2B,CAAE,2BAA2B,CACzD;QACD,0CAA0C;QAC1C,WAAW,EAAE,CAAC,IAAU,EAAE,CAAwB,EAAE,EAAE;YACpD,IAAI,CAAC;gBACH,MAAM,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,mBAAmB,EAAE,CAAC;gBACrD,IACE,aAAa;oBACb,CAAC,aAAwD,aAAxD,aAAa,uBAAb,aAAa,CAA6C,QAAQ,EAAE,MAAK,eAAe,CAAC,OAAO,EACjG,CAAC;oBACD,IAAI,CAAC,YAAY,CAAC,uBAAuB,EAAE,aAAa,CAAC,IAAI,CAAC,CAAC;oBAC/D,IAAI,CAAC,YAAY,CAAC,2BAA2B,EAAE,aAAa,CAAC,QAAQ,CAAC,CAAC;gBACzE,CAAC;YACH,CAAC;YAAC,OAAO,MAAM,EAAE,CAAC;gBAChB,oDAAoD;gBACpD,kDAAkD;YACpD,CAAC;QACH,CAAC,IACD;AACJ,CAAC;AAED,SAAS,+BAA+B,CACtC,yBAAsF,EACtF,aAAsC;IAEtC,mEACK,aAAa,KAChB,mBAAmB,EAAE,IAAI,KACtB,yBAAyB,KAC5B,2BAA2B,EAAE,sCAAsC,CACjE,yBAAyB,aAAzB,yBAAyB,uBAAzB,yBAAyB,CAAE,2BAA2B,CACvD,IACD;AACJ,CAAC","sourcesContent":["import type { Span } from '@opentelemetry/api';\nimport { FetchInstrumentation } from '@opentelemetry/instrumentation-fetch';\nimport { XMLHttpRequestInstrumentation } from '@opentelemetry/instrumentation-xml-http-request';\n\nimport { faro, type UserActionInternalInterface, UserActionState } from '@grafana/faro-core';\n\nimport type { DefaultInstrumentationsOptions, InstrumentationOption } from '../types';\n\nimport {\n fetchCustomAttributeFunctionWithDefaults,\n xhrCustomAttributeFunctionWithDefaults,\n} from './instrumentationUtils';\n\n/**\n * Get default OTEL instrumentations for React Native\n *\n * This function creates the default OpenTelemetry instrumentations for React Native:\n * - FetchInstrumentation: Traces fetch() API calls\n * - XMLHttpRequestInstrumentation: Traces XMLHttpRequest and axios (which uses XHR)\n *\n * IMPORTANT: Infinite loop prevention\n * - ignoreUrls is used to exclude Faro collector URLs\n * - ignoreNetworkEvents is true to avoid duplicate events\n * - No console logging during instrumentation\n *\n * @param options - Configuration options\n * @returns Array of OTEL instrumentations\n */\nexport function getDefaultOTELInstrumentations(options: DefaultInstrumentationsOptions = {}): InstrumentationOption[] {\n const { fetchInstrumentationOptions, xhrInstrumentationOptions, ...sharedOptions } = options;\n\n const fetchOpts = createFetchInstrumentationOptions(fetchInstrumentationOptions, sharedOptions);\n const xhrOpts = createXhrInstrumentationOptions(xhrInstrumentationOptions, sharedOptions);\n\n return [new FetchInstrumentation(fetchOpts), new XMLHttpRequestInstrumentation(xhrOpts)];\n}\n\nfunction createFetchInstrumentationOptions(\n fetchInstrumentationOptions: DefaultInstrumentationsOptions['fetchInstrumentationOptions'],\n sharedOptions: Record<string, unknown>\n) {\n return {\n ...sharedOptions,\n // Ignore network performance events to avoid duplicates\n ignoreNetworkEvents: true,\n // Keep this here to overwrite the defaults above if provided by the users\n ...fetchInstrumentationOptions,\n // Always keep this function\n applyCustomAttributesOnSpan: fetchCustomAttributeFunctionWithDefaults(\n fetchInstrumentationOptions?.applyCustomAttributesOnSpan\n ),\n // Request hook to add user action context\n requestHook: (span: Span, _: Request | RequestInit) => {\n try {\n const currentAction = faro.api.getActiveUserAction();\n if (\n currentAction &&\n (currentAction as unknown as UserActionInternalInterface)?.getState() === UserActionState.Started\n ) {\n span.setAttribute('faro.action.user.name', currentAction.name);\n span.setAttribute('faro.action.user.parentId', currentAction.parentId);\n }\n } catch (_error) {\n // Silently fail - don't log to avoid infinite loops\n // The span will just not have user action context\n }\n },\n };\n}\n\nfunction createXhrInstrumentationOptions(\n xhrInstrumentationOptions: DefaultInstrumentationsOptions['xhrInstrumentationOptions'],\n sharedOptions: Record<string, unknown>\n) {\n return {\n ...sharedOptions,\n ignoreNetworkEvents: true,\n ...xhrInstrumentationOptions,\n applyCustomAttributesOnSpan: xhrCustomAttributeFunctionWithDefaults(\n xhrInstrumentationOptions?.applyCustomAttributesOnSpan\n ),\n };\n}\n"]}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { SpanStatusCode } from '@opentelemetry/api';
|
|
2
|
+
import { faro, UserActionState } from '@grafana/faro-core';
|
|
3
|
+
/**
|
|
4
|
+
* Set span status to ERROR when fetch fails
|
|
5
|
+
*
|
|
6
|
+
* This ensures that failed HTTP requests are marked as errors in traces.
|
|
7
|
+
*/
|
|
8
|
+
export function setSpanStatusOnFetchError(span, error) {
|
|
9
|
+
const message = typeof error === 'string' ? error : error.message;
|
|
10
|
+
span.setStatus({
|
|
11
|
+
code: SpanStatusCode.ERROR,
|
|
12
|
+
message,
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Type guard to check if result is a FetchError
|
|
17
|
+
*/
|
|
18
|
+
function isFetchError(result) {
|
|
19
|
+
return 'message' in result && typeof result.message === 'string';
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Custom attribute function for fetch instrumentation with defaults
|
|
23
|
+
*
|
|
24
|
+
* Combines user-provided custom attributes with default handling.
|
|
25
|
+
*
|
|
26
|
+
* @param userFunction - Optional user-provided custom attribute function
|
|
27
|
+
* @returns Combined custom attribute function
|
|
28
|
+
*/
|
|
29
|
+
export function fetchCustomAttributeFunctionWithDefaults(userFunction) {
|
|
30
|
+
const fn = (span, request, result) => {
|
|
31
|
+
// Call user function first if provided
|
|
32
|
+
if (userFunction) {
|
|
33
|
+
userFunction(span, request, result);
|
|
34
|
+
}
|
|
35
|
+
// Add default error handling
|
|
36
|
+
if (isFetchError(result)) {
|
|
37
|
+
setSpanStatusOnFetchError(span, result.message);
|
|
38
|
+
}
|
|
39
|
+
else if (result instanceof Response && result.status >= 400 && result.status < 600) {
|
|
40
|
+
setSpanStatusOnFetchError(span, `HTTP ${result.status}: ${result.statusText}`);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
return fn;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Set span status to ERROR for XHR failures (status 0 or 4xx/5xx).
|
|
47
|
+
*/
|
|
48
|
+
export function setSpanStatusOnXMLHttpRequestError(span, xhr) {
|
|
49
|
+
const status = xhr.status;
|
|
50
|
+
if (status == null)
|
|
51
|
+
return;
|
|
52
|
+
if (status === 0 || (status >= 400 && status < 600)) {
|
|
53
|
+
span.setStatus({ code: SpanStatusCode.ERROR });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Add user action context to span when active (for HTTP Errors column in user action table).
|
|
58
|
+
*/
|
|
59
|
+
function addUserActionContextToSpan(span) {
|
|
60
|
+
var _a, _b, _c;
|
|
61
|
+
try {
|
|
62
|
+
const currentAction = (_b = (_a = faro.api) === null || _a === void 0 ? void 0 : _a.getActiveUserAction) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
63
|
+
const state = (_c = currentAction === null || currentAction === void 0 ? void 0 : currentAction.getState) === null || _c === void 0 ? void 0 : _c.call(currentAction);
|
|
64
|
+
if (currentAction && (state === UserActionState.Started || state === UserActionState.Halted)) {
|
|
65
|
+
span.setAttribute('faro.action.user.name', currentAction.name);
|
|
66
|
+
span.setAttribute('faro.action.user.parentId', currentAction.parentId);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
catch (_) {
|
|
70
|
+
// Silently fail - don't log to avoid instrumentation loops
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Custom attribute function for XHR instrumentation with defaults.
|
|
75
|
+
*/
|
|
76
|
+
export function xhrCustomAttributeFunctionWithDefaults(userFunction) {
|
|
77
|
+
return (span, xhr) => {
|
|
78
|
+
setSpanStatusOnXMLHttpRequestError(span, xhr);
|
|
79
|
+
addUserActionContextToSpan(span);
|
|
80
|
+
userFunction === null || userFunction === void 0 ? void 0 : userFunction(span, xhr);
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=instrumentationUtils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instrumentationUtils.js","sourceRoot":"","sources":["../../../src/instrumentations/instrumentationUtils.ts"],"names":[],"mappings":"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","sourcesContent":["import { SpanStatusCode } from '@opentelemetry/api';\nimport type { Span } from '@opentelemetry/api';\nimport type { FetchCustomAttributeFunction } from '@opentelemetry/instrumentation-fetch';\nimport type { XHRCustomAttributeFunction } from '@opentelemetry/instrumentation-xml-http-request';\n\nimport { faro, type UserActionInternalInterface, UserActionState } from '@grafana/faro-core';\n\n/**\n * FetchError interface matching OpenTelemetry's internal type\n * Note: FetchError is not exported from @opentelemetry/instrumentation-fetch\n */\ninterface FetchError {\n status?: number;\n message: string;\n}\n\n/**\n * Set span status to ERROR when fetch fails\n *\n * This ensures that failed HTTP requests are marked as errors in traces.\n */\nexport function setSpanStatusOnFetchError(span: Span, error: Error | string): void {\n const message = typeof error === 'string' ? error : error.message;\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message,\n });\n}\n\n/**\n * Type guard to check if result is a FetchError\n */\nfunction isFetchError(result: Response | FetchError): result is FetchError {\n return 'message' in result && typeof (result as FetchError).message === 'string';\n}\n\n/**\n * Custom attribute function for fetch instrumentation with defaults\n *\n * Combines user-provided custom attributes with default handling.\n *\n * @param userFunction - Optional user-provided custom attribute function\n * @returns Combined custom attribute function\n */\nexport function fetchCustomAttributeFunctionWithDefaults(\n userFunction?: FetchCustomAttributeFunction\n): FetchCustomAttributeFunction {\n const fn: FetchCustomAttributeFunction = (\n span: Span,\n request: Request | RequestInit,\n result: Response | FetchError\n ) => {\n // Call user function first if provided\n if (userFunction) {\n userFunction(span, request, result);\n }\n\n // Add default error handling\n if (isFetchError(result)) {\n setSpanStatusOnFetchError(span, result.message);\n } else if (result instanceof Response && result.status >= 400 && result.status < 600) {\n setSpanStatusOnFetchError(span, `HTTP ${result.status}: ${result.statusText}`);\n }\n };\n return fn;\n}\n\n/**\n * Set span status to ERROR for XHR failures (status 0 or 4xx/5xx).\n */\nexport function setSpanStatusOnXMLHttpRequestError(span: Span, xhr: XMLHttpRequest): void {\n const status = xhr.status;\n if (status == null) return;\n if (status === 0 || (status >= 400 && status < 600)) {\n span.setStatus({ code: SpanStatusCode.ERROR });\n }\n}\n\n/**\n * Add user action context to span when active (for HTTP Errors column in user action table).\n */\nfunction addUserActionContextToSpan(span: Span): void {\n try {\n const currentAction = faro.api?.getActiveUserAction?.();\n const state = (currentAction as unknown as UserActionInternalInterface)?.getState?.();\n if (currentAction && (state === UserActionState.Started || state === UserActionState.Halted)) {\n span.setAttribute('faro.action.user.name', currentAction.name);\n span.setAttribute('faro.action.user.parentId', currentAction.parentId);\n }\n } catch (_) {\n // Silently fail - don't log to avoid instrumentation loops\n }\n}\n\n/**\n * Custom attribute function for XHR instrumentation with defaults.\n */\nexport function xhrCustomAttributeFunctionWithDefaults(\n userFunction?: XHRCustomAttributeFunction\n): XHRCustomAttributeFunction {\n return (span: Span, xhr: XMLHttpRequest) => {\n setSpanStatusOnXMLHttpRequestError(span, xhr);\n addUserActionContextToSpan(span);\n userFunction?.(span, xhr);\n };\n}\n"]}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { ATTR_SESSION_ID } from '../semconv';
|
|
2
|
+
/**
|
|
3
|
+
* Span processor that adds Faro meta attributes to spans
|
|
4
|
+
*
|
|
5
|
+
* This processor enriches spans with:
|
|
6
|
+
* - Session ID
|
|
7
|
+
* - User information (email, id, username, etc.)
|
|
8
|
+
*
|
|
9
|
+
* IMPORTANT: This processor delegates to a wrapped processor and does NOT log
|
|
10
|
+
* to avoid infinite loops.
|
|
11
|
+
*/
|
|
12
|
+
export class FaroMetaAttributesSpanProcessor {
|
|
13
|
+
constructor(processor, metas) {
|
|
14
|
+
this.processor = processor;
|
|
15
|
+
this.metas = metas;
|
|
16
|
+
}
|
|
17
|
+
forceFlush() {
|
|
18
|
+
return this.processor.forceFlush();
|
|
19
|
+
}
|
|
20
|
+
onStart(span, parentContext) {
|
|
21
|
+
var _a;
|
|
22
|
+
const session = this.metas.value.session;
|
|
23
|
+
if (session === null || session === void 0 ? void 0 : session.id) {
|
|
24
|
+
span.attributes[ATTR_SESSION_ID] = session.id;
|
|
25
|
+
}
|
|
26
|
+
const user = (_a = this.metas.value.user) !== null && _a !== void 0 ? _a : {};
|
|
27
|
+
if (user.email) {
|
|
28
|
+
span.attributes['user.email'] = user.email;
|
|
29
|
+
}
|
|
30
|
+
if (user.id) {
|
|
31
|
+
span.attributes['user.id'] = user.id;
|
|
32
|
+
}
|
|
33
|
+
if (user.username) {
|
|
34
|
+
span.attributes['user.name'] = user.username;
|
|
35
|
+
}
|
|
36
|
+
if (user.fullName) {
|
|
37
|
+
span.attributes['user.full_name'] = user.fullName;
|
|
38
|
+
}
|
|
39
|
+
if (user.roles) {
|
|
40
|
+
span.attributes['user.roles'] = user.roles.split(',').map((role) => role.trim());
|
|
41
|
+
}
|
|
42
|
+
if (user.hash) {
|
|
43
|
+
span.attributes['user.hash'] = user.hash;
|
|
44
|
+
}
|
|
45
|
+
this.processor.onStart(span, parentContext);
|
|
46
|
+
}
|
|
47
|
+
onEnd(span) {
|
|
48
|
+
this.processor.onEnd(span);
|
|
49
|
+
}
|
|
50
|
+
shutdown() {
|
|
51
|
+
return this.processor.shutdown();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=faroMetaAttributesSpanProcessor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"faroMetaAttributesSpanProcessor.js","sourceRoot":"","sources":["../../../src/processors/faroMetaAttributesSpanProcessor.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE7C;;;;;;;;;GASG;AACH,MAAM,OAAO,+BAA+B;IAC1C,YACU,SAAwB,EACxB,KAAY;QADZ,cAAS,GAAT,SAAS,CAAe;QACxB,UAAK,GAAL,KAAK,CAAO;IACnB,CAAC;IAEJ,UAAU;QACR,OAAO,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,CAAC;IACrC,CAAC;IAED,OAAO,CAAC,IAAU,EAAE,aAAsB;;QACxC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC;QAEzC,IAAI,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,EAAE,EAAE,CAAC;YAChB,IAAI,CAAC,UAAU,CAAC,eAAe,CAAC,GAAG,OAAO,CAAC,EAAE,CAAC;QAChD,CAAC;QAED,MAAM,IAAI,GAAG,MAAA,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,mCAAI,EAAE,CAAC;QAEzC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC;QAC7C,CAAC;QAED,IAAI,IAAI,CAAC,EAAE,EAAE,CAAC;YACZ,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC,EAAE,CAAC;QACvC,CAAC;QAED,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClB,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC;QAC/C,CAAC;QAED,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClB,IAAI,CAAC,UAAU,CAAC,gBAAgB,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC;QACpD,CAAC;QAED,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QACnF,CAAC;QAED,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YACd,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC;QAC3C,CAAC;QAED,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;IAC9C,CAAC;IAED,KAAK,CAAC,IAAkB;QACtB,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC7B,CAAC;IAED,QAAQ;QACN,OAAO,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,CAAC;IACnC,CAAC;CACF","sourcesContent":["import type { Context } from '@opentelemetry/api';\nimport type { ReadableSpan, Span, SpanProcessor } from '@opentelemetry/sdk-trace-base';\n\nimport type { Metas } from '@grafana/faro-core';\n\nimport { ATTR_SESSION_ID } from '../semconv';\n\n/**\n * Span processor that adds Faro meta attributes to spans\n *\n * This processor enriches spans with:\n * - Session ID\n * - User information (email, id, username, etc.)\n *\n * IMPORTANT: This processor delegates to a wrapped processor and does NOT log\n * to avoid infinite loops.\n */\nexport class FaroMetaAttributesSpanProcessor implements SpanProcessor {\n constructor(\n private processor: SpanProcessor,\n private metas: Metas\n ) {}\n\n forceFlush(): Promise<void> {\n return this.processor.forceFlush();\n }\n\n onStart(span: Span, parentContext: Context): void {\n const session = this.metas.value.session;\n\n if (session?.id) {\n span.attributes[ATTR_SESSION_ID] = session.id;\n }\n\n const user = this.metas.value.user ?? {};\n\n if (user.email) {\n span.attributes['user.email'] = user.email;\n }\n\n if (user.id) {\n span.attributes['user.id'] = user.id;\n }\n\n if (user.username) {\n span.attributes['user.name'] = user.username;\n }\n\n if (user.fullName) {\n span.attributes['user.full_name'] = user.fullName;\n }\n\n if (user.roles) {\n span.attributes['user.roles'] = user.roles.split(',').map((role) => role.trim());\n }\n\n if (user.hash) {\n span.attributes['user.hash'] = user.hash;\n }\n\n this.processor.onStart(span, parentContext);\n }\n\n onEnd(span: ReadableSpan): void {\n this.processor.onEnd(span);\n }\n\n shutdown(): Promise<void> {\n return this.processor.shutdown();\n }\n}\n"]}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { notifyHttpRequestEnd, notifyHttpRequestStart, } from '@grafana/faro-react-native';
|
|
2
|
+
const ATTR_HTTP_METHOD = 'http.method';
|
|
3
|
+
const ATTR_HTTP_STATUS_CODE = 'http.status_code';
|
|
4
|
+
const ATTR_HTTP_URL = 'http.url';
|
|
5
|
+
/**
|
|
6
|
+
* Get string attribute from span.
|
|
7
|
+
* Handles both SDK attribute format and OTLP-style attribute values.
|
|
8
|
+
*/
|
|
9
|
+
function getAttr(span, key) {
|
|
10
|
+
var _a;
|
|
11
|
+
const attrs = span.attributes;
|
|
12
|
+
if (attrs == null)
|
|
13
|
+
return undefined;
|
|
14
|
+
const raw = typeof attrs['get'] === 'function' ? attrs['get'](key) : attrs[key];
|
|
15
|
+
if (raw == null)
|
|
16
|
+
return undefined;
|
|
17
|
+
if (typeof raw === 'string')
|
|
18
|
+
return raw.length > 0 ? raw : undefined;
|
|
19
|
+
if (typeof raw === 'object' && raw !== null && 'stringValue' in raw) {
|
|
20
|
+
const s = String((_a = raw.stringValue) !== null && _a !== void 0 ? _a : '');
|
|
21
|
+
return s.length > 0 ? s : undefined;
|
|
22
|
+
}
|
|
23
|
+
const str = String(raw);
|
|
24
|
+
return str.length > 0 ? str : undefined;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Check if a span is an HTTP span (from FetchInstrumentation).
|
|
28
|
+
*/
|
|
29
|
+
function isHttpSpan(span) {
|
|
30
|
+
return getAttr(span, ATTR_HTTP_URL) != null || getAttr(span, ATTR_HTTP_METHOD) != null;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* SpanProcessor that notifies httpRequestMonitor when HTTP spans start and end.
|
|
34
|
+
*
|
|
35
|
+
* This enables user action correlation (UserActionController halt logic) when
|
|
36
|
+
* TracingInstrumentation is used instead of HttpInstrumentation.
|
|
37
|
+
*
|
|
38
|
+
* IMPORTANT: Must not use console or trigger any instrumentation to avoid loops.
|
|
39
|
+
*/
|
|
40
|
+
export class HttpRequestMonitorSpanProcessor {
|
|
41
|
+
constructor(processor) {
|
|
42
|
+
this.processor = processor;
|
|
43
|
+
}
|
|
44
|
+
onStart(span, parentContext) {
|
|
45
|
+
var _a, _b;
|
|
46
|
+
if (isHttpSpan(span)) {
|
|
47
|
+
const url = (_a = getAttr(span, ATTR_HTTP_URL)) !== null && _a !== void 0 ? _a : '';
|
|
48
|
+
const method = (_b = getAttr(span, ATTR_HTTP_METHOD)) !== null && _b !== void 0 ? _b : 'GET';
|
|
49
|
+
const requestId = span.spanContext().spanId;
|
|
50
|
+
const startTimeMs = Date.now();
|
|
51
|
+
const payload = {
|
|
52
|
+
requestId,
|
|
53
|
+
url,
|
|
54
|
+
method,
|
|
55
|
+
startTime: startTimeMs,
|
|
56
|
+
};
|
|
57
|
+
notifyHttpRequestStart(payload);
|
|
58
|
+
}
|
|
59
|
+
this.processor.onStart(span, parentContext);
|
|
60
|
+
}
|
|
61
|
+
onEnd(span) {
|
|
62
|
+
var _a, _b;
|
|
63
|
+
if (isHttpSpan(span)) {
|
|
64
|
+
const url = (_a = getAttr(span, ATTR_HTTP_URL)) !== null && _a !== void 0 ? _a : '';
|
|
65
|
+
const method = (_b = getAttr(span, ATTR_HTTP_METHOD)) !== null && _b !== void 0 ? _b : 'GET';
|
|
66
|
+
const requestId = span.spanContext().spanId;
|
|
67
|
+
const statusAttr = getAttr(span, ATTR_HTTP_STATUS_CODE);
|
|
68
|
+
const status = statusAttr != null ? parseInt(statusAttr, 10) : undefined;
|
|
69
|
+
const spanWithTime = span;
|
|
70
|
+
const startNs = spanWithTime.startTimeUnixNano;
|
|
71
|
+
const endNs = spanWithTime.endTimeUnixNano;
|
|
72
|
+
const startTimeMs = startNs != null && !Number.isNaN(Number(startNs)) ? Number(startNs) / 1000000 : Date.now();
|
|
73
|
+
const endTimeMs = endNs != null && !Number.isNaN(Number(endNs)) ? Number(endNs) / 1000000 : Date.now();
|
|
74
|
+
const payload = {
|
|
75
|
+
requestId,
|
|
76
|
+
url,
|
|
77
|
+
method,
|
|
78
|
+
startTime: startTimeMs,
|
|
79
|
+
endTime: endTimeMs,
|
|
80
|
+
status: !Number.isNaN(status) ? status : undefined,
|
|
81
|
+
};
|
|
82
|
+
notifyHttpRequestEnd(payload);
|
|
83
|
+
}
|
|
84
|
+
this.processor.onEnd(span);
|
|
85
|
+
}
|
|
86
|
+
forceFlush() {
|
|
87
|
+
return this.processor.forceFlush();
|
|
88
|
+
}
|
|
89
|
+
shutdown() {
|
|
90
|
+
return this.processor.shutdown();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=httpRequestMonitorSpanProcessor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"httpRequestMonitorSpanProcessor.js","sourceRoot":"","sources":["../../../src/processors/httpRequestMonitorSpanProcessor.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Context } from '@opentelemetry/api';\nimport type { ReadableSpan, Span, SpanProcessor } from '@opentelemetry/sdk-trace-base';\n\nimport {\n type HttpRequestMessagePayload,\n notifyHttpRequestEnd,\n notifyHttpRequestStart,\n} from '@grafana/faro-react-native';\n\nconst ATTR_HTTP_METHOD = 'http.method';\nconst ATTR_HTTP_STATUS_CODE = 'http.status_code';\nconst ATTR_HTTP_URL = 'http.url';\n\n/**\n * Get string attribute from span.\n * Handles both SDK attribute format and OTLP-style attribute values.\n */\nfunction getAttr(span: Span | ReadableSpan, key: string): string | undefined {\n const attrs = span.attributes as Record<string, unknown> | undefined;\n if (attrs == null) return undefined;\n const raw = typeof attrs['get'] === 'function' ? (attrs as { get: (k: string) => unknown })['get'](key) : attrs[key];\n if (raw == null) return undefined;\n if (typeof raw === 'string') return raw.length > 0 ? raw : undefined;\n if (typeof raw === 'object' && raw !== null && 'stringValue' in raw) {\n const s = String((raw as { stringValue?: string }).stringValue ?? '');\n return s.length > 0 ? s : undefined;\n }\n const str = String(raw);\n return str.length > 0 ? str : undefined;\n}\n\n/**\n * Check if a span is an HTTP span (from FetchInstrumentation).\n */\nfunction isHttpSpan(span: Span | ReadableSpan): boolean {\n return getAttr(span, ATTR_HTTP_URL) != null || getAttr(span, ATTR_HTTP_METHOD) != null;\n}\n\n/**\n * SpanProcessor that notifies httpRequestMonitor when HTTP spans start and end.\n *\n * This enables user action correlation (UserActionController halt logic) when\n * TracingInstrumentation is used instead of HttpInstrumentation.\n *\n * IMPORTANT: Must not use console or trigger any instrumentation to avoid loops.\n */\nexport class HttpRequestMonitorSpanProcessor implements SpanProcessor {\n constructor(private readonly processor: SpanProcessor) {}\n\n onStart(span: Span, parentContext: Context): void {\n if (isHttpSpan(span)) {\n const url = getAttr(span, ATTR_HTTP_URL) ?? '';\n const method = getAttr(span, ATTR_HTTP_METHOD) ?? 'GET';\n const requestId = span.spanContext().spanId;\n const startTimeMs = Date.now();\n\n const payload: HttpRequestMessagePayload = {\n requestId,\n url,\n method,\n startTime: startTimeMs,\n };\n notifyHttpRequestStart(payload);\n }\n\n this.processor.onStart(span, parentContext);\n }\n\n onEnd(span: ReadableSpan): void {\n if (isHttpSpan(span)) {\n const url = getAttr(span, ATTR_HTTP_URL) ?? '';\n const method = getAttr(span, ATTR_HTTP_METHOD) ?? 'GET';\n const requestId = span.spanContext().spanId;\n const statusAttr = getAttr(span, ATTR_HTTP_STATUS_CODE);\n const status = statusAttr != null ? parseInt(statusAttr, 10) : undefined;\n const spanWithTime = span as ReadableSpan & {\n startTimeUnixNano?: number | bigint;\n endTimeUnixNano?: number | bigint;\n };\n const startNs = spanWithTime.startTimeUnixNano;\n const endNs = spanWithTime.endTimeUnixNano;\n const startTimeMs = startNs != null && !Number.isNaN(Number(startNs)) ? Number(startNs) / 1_000_000 : Date.now();\n const endTimeMs = endNs != null && !Number.isNaN(Number(endNs)) ? Number(endNs) / 1_000_000 : Date.now();\n\n const payload: HttpRequestMessagePayload = {\n requestId,\n url,\n method,\n startTime: startTimeMs,\n endTime: endTimeMs,\n status: !Number.isNaN(status) ? status : undefined,\n };\n notifyHttpRequestEnd(payload);\n }\n\n this.processor.onEnd(span);\n }\n\n forceFlush(): Promise<void> {\n return this.processor.forceFlush();\n }\n\n shutdown(): Promise<void> {\n return this.processor.shutdown();\n }\n}\n"]}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Semantic convention attributes for React Native tracing
|
|
3
|
+
* Based on OpenTelemetry semantic conventions with React Native-specific additions
|
|
4
|
+
*/
|
|
5
|
+
// Session attributes
|
|
6
|
+
export const ATTR_SESSION_ID = 'session.id';
|
|
7
|
+
// Device/Platform attributes for React Native
|
|
8
|
+
export const ATTR_DEVICE_MODEL = 'device.model';
|
|
9
|
+
export const ATTR_DEVICE_BRAND = 'device.brand';
|
|
10
|
+
export const ATTR_DEVICE_PLATFORM = 'device.platform';
|
|
11
|
+
export const ATTR_DEVICE_OS_VERSION = 'device.os.version';
|
|
12
|
+
export const ATTR_DEVICE_TYPE = 'device.type';
|
|
13
|
+
export const ATTR_DEVICE_LOCALE = 'device.locale';
|
|
14
|
+
// App attributes
|
|
15
|
+
export const ATTR_APP_VERSION = 'app.version';
|
|
16
|
+
export const ATTR_APP_BUILD = 'app.build';
|
|
17
|
+
// Service attributes
|
|
18
|
+
export const ATTR_SERVICE_NAMESPACE = 'service.namespace';
|
|
19
|
+
export const ATTR_DEPLOYMENT_ENVIRONMENT_NAME = 'deployment.environment.name';
|
|
20
|
+
// Telemetry attributes
|
|
21
|
+
export const ATTR_TELEMETRY_DISTRO_NAME = 'telemetry.distro.name';
|
|
22
|
+
export const ATTR_TELEMETRY_DISTRO_VERSION = 'telemetry.distro.version';
|
|
23
|
+
// Process/Runtime attributes
|
|
24
|
+
export const ATTR_PROCESS_RUNTIME_NAME = 'process.runtime.name';
|
|
25
|
+
export const ATTR_PROCESS_RUNTIME_VERSION = 'process.runtime.version';
|
|
26
|
+
//# sourceMappingURL=semconv.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"semconv.js","sourceRoot":"","sources":["../../src/semconv.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,qBAAqB;AACrB,MAAM,CAAC,MAAM,eAAe,GAAG,YAAY,CAAC;AAE5C,8CAA8C;AAC9C,MAAM,CAAC,MAAM,iBAAiB,GAAG,cAAc,CAAC;AAChD,MAAM,CAAC,MAAM,iBAAiB,GAAG,cAAc,CAAC;AAChD,MAAM,CAAC,MAAM,oBAAoB,GAAG,iBAAiB,CAAC;AACtD,MAAM,CAAC,MAAM,sBAAsB,GAAG,mBAAmB,CAAC;AAC1D,MAAM,CAAC,MAAM,gBAAgB,GAAG,aAAa,CAAC;AAC9C,MAAM,CAAC,MAAM,kBAAkB,GAAG,eAAe,CAAC;AAElD,iBAAiB;AACjB,MAAM,CAAC,MAAM,gBAAgB,GAAG,aAAa,CAAC;AAC9C,MAAM,CAAC,MAAM,cAAc,GAAG,WAAW,CAAC;AAE1C,qBAAqB;AACrB,MAAM,CAAC,MAAM,sBAAsB,GAAG,mBAAmB,CAAC;AAC1D,MAAM,CAAC,MAAM,gCAAgC,GAAG,6BAA6B,CAAC;AAE9E,uBAAuB;AACvB,MAAM,CAAC,MAAM,0BAA0B,GAAG,uBAAuB,CAAC;AAClE,MAAM,CAAC,MAAM,6BAA6B,GAAG,0BAA0B,CAAC;AAExE,6BAA6B;AAC7B,MAAM,CAAC,MAAM,yBAAyB,GAAG,sBAAsB,CAAC;AAChE,MAAM,CAAC,MAAM,4BAA4B,GAAG,yBAAyB,CAAC","sourcesContent":["/**\n * Semantic convention attributes for React Native tracing\n * Based on OpenTelemetry semantic conventions with React Native-specific additions\n */\n\n// Session attributes\nexport const ATTR_SESSION_ID = 'session.id';\n\n// Device/Platform attributes for React Native\nexport const ATTR_DEVICE_MODEL = 'device.model';\nexport const ATTR_DEVICE_BRAND = 'device.brand';\nexport const ATTR_DEVICE_PLATFORM = 'device.platform';\nexport const ATTR_DEVICE_OS_VERSION = 'device.os.version';\nexport const ATTR_DEVICE_TYPE = 'device.type';\nexport const ATTR_DEVICE_LOCALE = 'device.locale';\n\n// App attributes\nexport const ATTR_APP_VERSION = 'app.version';\nexport const ATTR_APP_BUILD = 'app.build';\n\n// Service attributes\nexport const ATTR_SERVICE_NAMESPACE = 'service.namespace';\nexport const ATTR_DEPLOYMENT_ENVIRONMENT_NAME = 'deployment.environment.name';\n\n// Telemetry attributes\nexport const ATTR_TELEMETRY_DISTRO_NAME = 'telemetry.distro.name';\nexport const ATTR_TELEMETRY_DISTRO_VERSION = 'telemetry.distro.version';\n\n// Process/Runtime attributes\nexport const ATTR_PROCESS_RUNTIME_NAME = 'process.runtime.name';\nexport const ATTR_PROCESS_RUNTIME_VERSION = 'process.runtime.version';\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { Attributes, ContextManager, TextMapPropagator } from '@opentelemetry/api';\nimport type { Instrumentation } from '@opentelemetry/instrumentation';\nimport type { FetchCustomAttributeFunction } from '@opentelemetry/instrumentation-fetch';\nimport type { XHRCustomAttributeFunction } from '@opentelemetry/instrumentation-xml-http-request';\nimport type { SpanProcessor } from '@opentelemetry/sdk-trace-base';\n\nimport type { API, Patterns } from '@grafana/faro-core';\n\nexport type InstrumentationOption = Instrumentation | Instrumentation[];\n\nexport interface FaroTraceExporterConfig {\n api: API;\n}\n\nexport interface TracingInstrumentationOptions {\n resourceAttributes?: Attributes;\n propagator?: TextMapPropagator;\n contextManager?: ContextManager;\n instrumentations?: InstrumentationOption[];\n spanProcessor?: SpanProcessor;\n instrumentationOptions?: Omit<DefaultInstrumentationsOptions, 'ignoreUrls'>;\n}\n\nexport type MatchUrlDefinitions = Patterns;\n\nexport type DefaultInstrumentationsOptions = {\n ignoreUrls?: MatchUrlDefinitions;\n propagateTraceHeaderCorsUrls?: MatchUrlDefinitions;\n\n fetchInstrumentationOptions?: {\n applyCustomAttributesOnSpan?: FetchCustomAttributeFunction;\n ignoreNetworkEvents?: boolean;\n };\n\n xhrInstrumentationOptions?: {\n applyCustomAttributesOnSpan?: XHRCustomAttributeFunction;\n ignoreNetworkEvents?: boolean;\n };\n};\n"]}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { SamplingDecision } from '@opentelemetry/sdk-trace-base';
|
|
2
|
+
/**
|
|
3
|
+
* Get sampling decision based on session configuration
|
|
4
|
+
*
|
|
5
|
+
* If the session is sampled, traces will be collected.
|
|
6
|
+
* If the session is not sampled, traces will be dropped.
|
|
7
|
+
*
|
|
8
|
+
* @param sessionMeta - Current Faro session meta
|
|
9
|
+
* @returns OTEL sampling decision
|
|
10
|
+
*/
|
|
11
|
+
export function getSamplingDecision(sessionMeta = {}) {
|
|
12
|
+
var _a;
|
|
13
|
+
const isSessionSampled = ((_a = sessionMeta.attributes) === null || _a === void 0 ? void 0 : _a['isSampled']) === 'true';
|
|
14
|
+
const samplingDecision = isSessionSampled ? SamplingDecision.RECORD_AND_SAMPLED : SamplingDecision.NOT_RECORD;
|
|
15
|
+
return samplingDecision;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=sampler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sampler.js","sourceRoot":"","sources":["../../../src/utils/sampler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAIjE;;;;;;;;GAQG;AACH,MAAM,UAAU,mBAAmB,CAAC,cAA2B,EAAE;;IAC/D,MAAM,gBAAgB,GAAG,CAAA,MAAA,WAAW,CAAC,UAAU,0CAAG,WAAW,CAAC,MAAK,MAAM,CAAC;IAC1E,MAAM,gBAAgB,GAAG,gBAAgB,CAAC,CAAC,CAAC,gBAAgB,CAAC,kBAAkB,CAAC,CAAC,CAAC,gBAAgB,CAAC,UAAU,CAAC;IAE9G,OAAO,gBAAgB,CAAC;AAC1B,CAAC","sourcesContent":["import { SamplingDecision } from '@opentelemetry/sdk-trace-base';\n\nimport type { MetaSession } from '@grafana/faro-core';\n\n/**\n * Get sampling decision based on session configuration\n *\n * If the session is sampled, traces will be collected.\n * If the session is not sampled, traces will be dropped.\n *\n * @param sessionMeta - Current Faro session meta\n * @returns OTEL sampling decision\n */\nexport function getSamplingDecision(sessionMeta: MetaSession = {}): SamplingDecision {\n const isSessionSampled = sessionMeta.attributes?.['isSampled'] === 'true';\n const samplingDecision = isSessionSampled ? SamplingDecision.RECORD_AND_SAMPLED : SamplingDecision.NOT_RECORD;\n\n return samplingDecision;\n}\n"]}
|