@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.
Files changed (74) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +833 -0
  3. package/dist/cjs/exporters/faroTraceExporter.js +101 -0
  4. package/dist/cjs/exporters/faroTraceExporter.js.map +1 -0
  5. package/dist/cjs/exporters/faroTraceExporter.utils.js +99 -0
  6. package/dist/cjs/exporters/faroTraceExporter.utils.js.map +1 -0
  7. package/dist/cjs/index.js +17 -0
  8. package/dist/cjs/index.js.map +1 -0
  9. package/dist/cjs/instrumentation.js +251 -0
  10. package/dist/cjs/instrumentation.js.map +1 -0
  11. package/dist/cjs/instrumentations/getDefaultOTELInstrumentations.js +77 -0
  12. package/dist/cjs/instrumentations/getDefaultOTELInstrumentations.js.map +1 -0
  13. package/dist/cjs/instrumentations/instrumentationUtils.js +89 -0
  14. package/dist/cjs/instrumentations/instrumentationUtils.js.map +1 -0
  15. package/dist/cjs/processors/faroMetaAttributesSpanProcessor.js +59 -0
  16. package/dist/cjs/processors/faroMetaAttributesSpanProcessor.js.map +1 -0
  17. package/dist/cjs/processors/httpRequestMonitorSpanProcessor.js +98 -0
  18. package/dist/cjs/processors/httpRequestMonitorSpanProcessor.js.map +1 -0
  19. package/dist/cjs/semconv.js +29 -0
  20. package/dist/cjs/semconv.js.map +1 -0
  21. package/dist/cjs/types.js +3 -0
  22. package/dist/cjs/types.js.map +1 -0
  23. package/dist/cjs/utils/sampler.js +21 -0
  24. package/dist/cjs/utils/sampler.js.map +1 -0
  25. package/dist/esm/exporters/faroTraceExporter.js +65 -0
  26. package/dist/esm/exporters/faroTraceExporter.js.map +1 -0
  27. package/dist/esm/exporters/faroTraceExporter.utils.js +88 -0
  28. package/dist/esm/exporters/faroTraceExporter.utils.js.map +1 -0
  29. package/dist/esm/index.js +7 -0
  30. package/dist/esm/index.js.map +1 -0
  31. package/dist/esm/instrumentation.js +183 -0
  32. package/dist/esm/instrumentation.js.map +1 -0
  33. package/dist/esm/instrumentations/getDefaultOTELInstrumentations.js +62 -0
  34. package/dist/esm/instrumentations/getDefaultOTELInstrumentations.js.map +1 -0
  35. package/dist/esm/instrumentations/instrumentationUtils.js +83 -0
  36. package/dist/esm/instrumentations/instrumentationUtils.js.map +1 -0
  37. package/dist/esm/processors/faroMetaAttributesSpanProcessor.js +54 -0
  38. package/dist/esm/processors/faroMetaAttributesSpanProcessor.js.map +1 -0
  39. package/dist/esm/processors/httpRequestMonitorSpanProcessor.js +93 -0
  40. package/dist/esm/processors/httpRequestMonitorSpanProcessor.js.map +1 -0
  41. package/dist/esm/semconv.js +26 -0
  42. package/dist/esm/semconv.js.map +1 -0
  43. package/dist/esm/types.js +2 -0
  44. package/dist/esm/types.js.map +1 -0
  45. package/dist/esm/utils/sampler.js +17 -0
  46. package/dist/esm/utils/sampler.js.map +1 -0
  47. package/dist/types/exporters/faroTraceExporter.d.ts +21 -0
  48. package/dist/types/exporters/faroTraceExporter.utils.d.ts +17 -0
  49. package/dist/types/index.d.ts +7 -0
  50. package/dist/types/instrumentation.d.ts +46 -0
  51. package/dist/types/instrumentations/getDefaultOTELInstrumentations.d.ts +17 -0
  52. package/dist/types/instrumentations/instrumentationUtils.d.ts +26 -0
  53. package/dist/types/processors/faroMetaAttributesSpanProcessor.d.ts +22 -0
  54. package/dist/types/processors/httpRequestMonitorSpanProcessor.d.ts +18 -0
  55. package/dist/types/semconv.d.ts +19 -0
  56. package/dist/types/types.d.ts +31 -0
  57. package/dist/types/utils/sampler.d.ts +12 -0
  58. package/package.json +77 -0
  59. package/src/exporters/faroTraceExporter.test.ts +110 -0
  60. package/src/exporters/faroTraceExporter.ts +64 -0
  61. package/src/exporters/faroTraceExporter.utils.ts +105 -0
  62. package/src/index.ts +16 -0
  63. package/src/instrumentation.ts +241 -0
  64. package/src/instrumentations/getDefaultOTELInstrumentations.test.ts +70 -0
  65. package/src/instrumentations/getDefaultOTELInstrumentations.ts +83 -0
  66. package/src/instrumentations/instrumentationUtils.test.ts +107 -0
  67. package/src/instrumentations/instrumentationUtils.ts +106 -0
  68. package/src/processors/faroMetaAttributesSpanProcessor.test.ts +127 -0
  69. package/src/processors/faroMetaAttributesSpanProcessor.ts +71 -0
  70. package/src/processors/httpRequestMonitorSpanProcessor.ts +106 -0
  71. package/src/semconv.ts +31 -0
  72. package/src/types.ts +39 -0
  73. package/src/utils/sampler.test.ts +51 -0
  74. package/src/utils/sampler.ts +19 -0
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.setSpanStatusOnFetchError = setSpanStatusOnFetchError;
4
+ exports.fetchCustomAttributeFunctionWithDefaults = fetchCustomAttributeFunctionWithDefaults;
5
+ exports.setSpanStatusOnXMLHttpRequestError = setSpanStatusOnXMLHttpRequestError;
6
+ exports.xhrCustomAttributeFunctionWithDefaults = xhrCustomAttributeFunctionWithDefaults;
7
+ var api_1 = require("@opentelemetry/api");
8
+ var faro_core_1 = require("@grafana/faro-core");
9
+ /**
10
+ * Set span status to ERROR when fetch fails
11
+ *
12
+ * This ensures that failed HTTP requests are marked as errors in traces.
13
+ */
14
+ function setSpanStatusOnFetchError(span, error) {
15
+ var message = typeof error === 'string' ? error : error.message;
16
+ span.setStatus({
17
+ code: api_1.SpanStatusCode.ERROR,
18
+ message: message,
19
+ });
20
+ }
21
+ /**
22
+ * Type guard to check if result is a FetchError
23
+ */
24
+ function isFetchError(result) {
25
+ return 'message' in result && typeof result.message === 'string';
26
+ }
27
+ /**
28
+ * Custom attribute function for fetch instrumentation with defaults
29
+ *
30
+ * Combines user-provided custom attributes with default handling.
31
+ *
32
+ * @param userFunction - Optional user-provided custom attribute function
33
+ * @returns Combined custom attribute function
34
+ */
35
+ function fetchCustomAttributeFunctionWithDefaults(userFunction) {
36
+ var fn = function (span, request, result) {
37
+ // Call user function first if provided
38
+ if (userFunction) {
39
+ userFunction(span, request, result);
40
+ }
41
+ // Add default error handling
42
+ if (isFetchError(result)) {
43
+ setSpanStatusOnFetchError(span, result.message);
44
+ }
45
+ else if (result instanceof Response && result.status >= 400 && result.status < 600) {
46
+ setSpanStatusOnFetchError(span, "HTTP ".concat(result.status, ": ").concat(result.statusText));
47
+ }
48
+ };
49
+ return fn;
50
+ }
51
+ /**
52
+ * Set span status to ERROR for XHR failures (status 0 or 4xx/5xx).
53
+ */
54
+ function setSpanStatusOnXMLHttpRequestError(span, xhr) {
55
+ var status = xhr.status;
56
+ if (status == null)
57
+ return;
58
+ if (status === 0 || (status >= 400 && status < 600)) {
59
+ span.setStatus({ code: api_1.SpanStatusCode.ERROR });
60
+ }
61
+ }
62
+ /**
63
+ * Add user action context to span when active (for HTTP Errors column in user action table).
64
+ */
65
+ function addUserActionContextToSpan(span) {
66
+ var _a, _b, _c;
67
+ try {
68
+ var currentAction = (_b = (_a = faro_core_1.faro.api) === null || _a === void 0 ? void 0 : _a.getActiveUserAction) === null || _b === void 0 ? void 0 : _b.call(_a);
69
+ var state = (_c = currentAction === null || currentAction === void 0 ? void 0 : currentAction.getState) === null || _c === void 0 ? void 0 : _c.call(currentAction);
70
+ if (currentAction && (state === faro_core_1.UserActionState.Started || state === faro_core_1.UserActionState.Halted)) {
71
+ span.setAttribute('faro.action.user.name', currentAction.name);
72
+ span.setAttribute('faro.action.user.parentId', currentAction.parentId);
73
+ }
74
+ }
75
+ catch (_) {
76
+ // Silently fail - don't log to avoid instrumentation loops
77
+ }
78
+ }
79
+ /**
80
+ * Custom attribute function for XHR instrumentation with defaults.
81
+ */
82
+ function xhrCustomAttributeFunctionWithDefaults(userFunction) {
83
+ return function (span, xhr) {
84
+ setSpanStatusOnXMLHttpRequestError(span, xhr);
85
+ addUserActionContextToSpan(span);
86
+ userFunction === null || userFunction === void 0 ? void 0 : userFunction(span, xhr);
87
+ };
88
+ }
89
+ //# 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,59 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FaroMetaAttributesSpanProcessor = void 0;
4
+ var semconv_1 = require("../semconv");
5
+ /**
6
+ * Span processor that adds Faro meta attributes to spans
7
+ *
8
+ * This processor enriches spans with:
9
+ * - Session ID
10
+ * - User information (email, id, username, etc.)
11
+ *
12
+ * IMPORTANT: This processor delegates to a wrapped processor and does NOT log
13
+ * to avoid infinite loops.
14
+ */
15
+ var FaroMetaAttributesSpanProcessor = /** @class */ (function () {
16
+ function FaroMetaAttributesSpanProcessor(processor, metas) {
17
+ this.processor = processor;
18
+ this.metas = metas;
19
+ }
20
+ FaroMetaAttributesSpanProcessor.prototype.forceFlush = function () {
21
+ return this.processor.forceFlush();
22
+ };
23
+ FaroMetaAttributesSpanProcessor.prototype.onStart = function (span, parentContext) {
24
+ var _a;
25
+ var session = this.metas.value.session;
26
+ if (session === null || session === void 0 ? void 0 : session.id) {
27
+ span.attributes[semconv_1.ATTR_SESSION_ID] = session.id;
28
+ }
29
+ var user = (_a = this.metas.value.user) !== null && _a !== void 0 ? _a : {};
30
+ if (user.email) {
31
+ span.attributes['user.email'] = user.email;
32
+ }
33
+ if (user.id) {
34
+ span.attributes['user.id'] = user.id;
35
+ }
36
+ if (user.username) {
37
+ span.attributes['user.name'] = user.username;
38
+ }
39
+ if (user.fullName) {
40
+ span.attributes['user.full_name'] = user.fullName;
41
+ }
42
+ if (user.roles) {
43
+ span.attributes['user.roles'] = user.roles.split(',').map(function (role) { return role.trim(); });
44
+ }
45
+ if (user.hash) {
46
+ span.attributes['user.hash'] = user.hash;
47
+ }
48
+ this.processor.onStart(span, parentContext);
49
+ };
50
+ FaroMetaAttributesSpanProcessor.prototype.onEnd = function (span) {
51
+ this.processor.onEnd(span);
52
+ };
53
+ FaroMetaAttributesSpanProcessor.prototype.shutdown = function () {
54
+ return this.processor.shutdown();
55
+ };
56
+ return FaroMetaAttributesSpanProcessor;
57
+ }());
58
+ exports.FaroMetaAttributesSpanProcessor = FaroMetaAttributesSpanProcessor;
59
+ //# sourceMappingURL=faroMetaAttributesSpanProcessor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"faroMetaAttributesSpanProcessor.js","sourceRoot":"","sources":["../../../src/processors/faroMetaAttributesSpanProcessor.ts"],"names":[],"mappings":";;;AAKA,sCAA6C;AAE7C;;;;;;;;;GASG;AACH;IACE,yCACU,SAAwB,EACxB,KAAY;QADZ,cAAS,GAAT,SAAS,CAAe;QACxB,UAAK,GAAL,KAAK,CAAO;IACnB,CAAC;IAEJ,oDAAU,GAAV;QACE,OAAO,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,CAAC;IACrC,CAAC;IAED,iDAAO,GAAP,UAAQ,IAAU,EAAE,aAAsB;;QACxC,IAAM,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,yBAAe,CAAC,GAAG,OAAO,CAAC,EAAE,CAAC;QAChD,CAAC;QAED,IAAM,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,UAAC,IAAI,IAAK,OAAA,IAAI,CAAC,IAAI,EAAE,EAAX,CAAW,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,+CAAK,GAAL,UAAM,IAAkB;QACtB,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC7B,CAAC;IAED,kDAAQ,GAAR;QACE,OAAO,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,CAAC;IACnC,CAAC;IACH,sCAAC;AAAD,CAAC,AArDD,IAqDC;AArDY,0EAA+B","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,98 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HttpRequestMonitorSpanProcessor = void 0;
4
+ var faro_react_native_1 = require("@grafana/faro-react-native");
5
+ var ATTR_HTTP_METHOD = 'http.method';
6
+ var ATTR_HTTP_STATUS_CODE = 'http.status_code';
7
+ var ATTR_HTTP_URL = 'http.url';
8
+ /**
9
+ * Get string attribute from span.
10
+ * Handles both SDK attribute format and OTLP-style attribute values.
11
+ */
12
+ function getAttr(span, key) {
13
+ var _a;
14
+ var attrs = span.attributes;
15
+ if (attrs == null)
16
+ return undefined;
17
+ var raw = typeof attrs['get'] === 'function' ? attrs['get'](key) : attrs[key];
18
+ if (raw == null)
19
+ return undefined;
20
+ if (typeof raw === 'string')
21
+ return raw.length > 0 ? raw : undefined;
22
+ if (typeof raw === 'object' && raw !== null && 'stringValue' in raw) {
23
+ var s = String((_a = raw.stringValue) !== null && _a !== void 0 ? _a : '');
24
+ return s.length > 0 ? s : undefined;
25
+ }
26
+ var str = String(raw);
27
+ return str.length > 0 ? str : undefined;
28
+ }
29
+ /**
30
+ * Check if a span is an HTTP span (from FetchInstrumentation).
31
+ */
32
+ function isHttpSpan(span) {
33
+ return getAttr(span, ATTR_HTTP_URL) != null || getAttr(span, ATTR_HTTP_METHOD) != null;
34
+ }
35
+ /**
36
+ * SpanProcessor that notifies httpRequestMonitor when HTTP spans start and end.
37
+ *
38
+ * This enables user action correlation (UserActionController halt logic) when
39
+ * TracingInstrumentation is used instead of HttpInstrumentation.
40
+ *
41
+ * IMPORTANT: Must not use console or trigger any instrumentation to avoid loops.
42
+ */
43
+ var HttpRequestMonitorSpanProcessor = /** @class */ (function () {
44
+ function HttpRequestMonitorSpanProcessor(processor) {
45
+ this.processor = processor;
46
+ }
47
+ HttpRequestMonitorSpanProcessor.prototype.onStart = function (span, parentContext) {
48
+ var _a, _b;
49
+ if (isHttpSpan(span)) {
50
+ var url = (_a = getAttr(span, ATTR_HTTP_URL)) !== null && _a !== void 0 ? _a : '';
51
+ var method = (_b = getAttr(span, ATTR_HTTP_METHOD)) !== null && _b !== void 0 ? _b : 'GET';
52
+ var requestId = span.spanContext().spanId;
53
+ var startTimeMs = Date.now();
54
+ var payload = {
55
+ requestId: requestId,
56
+ url: url,
57
+ method: method,
58
+ startTime: startTimeMs,
59
+ };
60
+ (0, faro_react_native_1.notifyHttpRequestStart)(payload);
61
+ }
62
+ this.processor.onStart(span, parentContext);
63
+ };
64
+ HttpRequestMonitorSpanProcessor.prototype.onEnd = function (span) {
65
+ var _a, _b;
66
+ if (isHttpSpan(span)) {
67
+ var url = (_a = getAttr(span, ATTR_HTTP_URL)) !== null && _a !== void 0 ? _a : '';
68
+ var method = (_b = getAttr(span, ATTR_HTTP_METHOD)) !== null && _b !== void 0 ? _b : 'GET';
69
+ var requestId = span.spanContext().spanId;
70
+ var statusAttr = getAttr(span, ATTR_HTTP_STATUS_CODE);
71
+ var status_1 = statusAttr != null ? parseInt(statusAttr, 10) : undefined;
72
+ var spanWithTime = span;
73
+ var startNs = spanWithTime.startTimeUnixNano;
74
+ var endNs = spanWithTime.endTimeUnixNano;
75
+ var startTimeMs = startNs != null && !Number.isNaN(Number(startNs)) ? Number(startNs) / 1000000 : Date.now();
76
+ var endTimeMs = endNs != null && !Number.isNaN(Number(endNs)) ? Number(endNs) / 1000000 : Date.now();
77
+ var payload = {
78
+ requestId: requestId,
79
+ url: url,
80
+ method: method,
81
+ startTime: startTimeMs,
82
+ endTime: endTimeMs,
83
+ status: !Number.isNaN(status_1) ? status_1 : undefined,
84
+ };
85
+ (0, faro_react_native_1.notifyHttpRequestEnd)(payload);
86
+ }
87
+ this.processor.onEnd(span);
88
+ };
89
+ HttpRequestMonitorSpanProcessor.prototype.forceFlush = function () {
90
+ return this.processor.forceFlush();
91
+ };
92
+ HttpRequestMonitorSpanProcessor.prototype.shutdown = function () {
93
+ return this.processor.shutdown();
94
+ };
95
+ return HttpRequestMonitorSpanProcessor;
96
+ }());
97
+ exports.HttpRequestMonitorSpanProcessor = HttpRequestMonitorSpanProcessor;
98
+ //# 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,29 @@
1
+ "use strict";
2
+ /**
3
+ * Semantic convention attributes for React Native tracing
4
+ * Based on OpenTelemetry semantic conventions with React Native-specific additions
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.ATTR_PROCESS_RUNTIME_VERSION = exports.ATTR_PROCESS_RUNTIME_NAME = exports.ATTR_TELEMETRY_DISTRO_VERSION = exports.ATTR_TELEMETRY_DISTRO_NAME = exports.ATTR_DEPLOYMENT_ENVIRONMENT_NAME = exports.ATTR_SERVICE_NAMESPACE = exports.ATTR_APP_BUILD = exports.ATTR_APP_VERSION = exports.ATTR_DEVICE_LOCALE = exports.ATTR_DEVICE_TYPE = exports.ATTR_DEVICE_OS_VERSION = exports.ATTR_DEVICE_PLATFORM = exports.ATTR_DEVICE_BRAND = exports.ATTR_DEVICE_MODEL = exports.ATTR_SESSION_ID = void 0;
8
+ // Session attributes
9
+ exports.ATTR_SESSION_ID = 'session.id';
10
+ // Device/Platform attributes for React Native
11
+ exports.ATTR_DEVICE_MODEL = 'device.model';
12
+ exports.ATTR_DEVICE_BRAND = 'device.brand';
13
+ exports.ATTR_DEVICE_PLATFORM = 'device.platform';
14
+ exports.ATTR_DEVICE_OS_VERSION = 'device.os.version';
15
+ exports.ATTR_DEVICE_TYPE = 'device.type';
16
+ exports.ATTR_DEVICE_LOCALE = 'device.locale';
17
+ // App attributes
18
+ exports.ATTR_APP_VERSION = 'app.version';
19
+ exports.ATTR_APP_BUILD = 'app.build';
20
+ // Service attributes
21
+ exports.ATTR_SERVICE_NAMESPACE = 'service.namespace';
22
+ exports.ATTR_DEPLOYMENT_ENVIRONMENT_NAME = 'deployment.environment.name';
23
+ // Telemetry attributes
24
+ exports.ATTR_TELEMETRY_DISTRO_NAME = 'telemetry.distro.name';
25
+ exports.ATTR_TELEMETRY_DISTRO_VERSION = 'telemetry.distro.version';
26
+ // Process/Runtime attributes
27
+ exports.ATTR_PROCESS_RUNTIME_NAME = 'process.runtime.name';
28
+ exports.ATTR_PROCESS_RUNTIME_VERSION = 'process.runtime.version';
29
+ //# sourceMappingURL=semconv.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"semconv.js","sourceRoot":"","sources":["../../src/semconv.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH,qBAAqB;AACR,QAAA,eAAe,GAAG,YAAY,CAAC;AAE5C,8CAA8C;AACjC,QAAA,iBAAiB,GAAG,cAAc,CAAC;AACnC,QAAA,iBAAiB,GAAG,cAAc,CAAC;AACnC,QAAA,oBAAoB,GAAG,iBAAiB,CAAC;AACzC,QAAA,sBAAsB,GAAG,mBAAmB,CAAC;AAC7C,QAAA,gBAAgB,GAAG,aAAa,CAAC;AACjC,QAAA,kBAAkB,GAAG,eAAe,CAAC;AAElD,iBAAiB;AACJ,QAAA,gBAAgB,GAAG,aAAa,CAAC;AACjC,QAAA,cAAc,GAAG,WAAW,CAAC;AAE1C,qBAAqB;AACR,QAAA,sBAAsB,GAAG,mBAAmB,CAAC;AAC7C,QAAA,gCAAgC,GAAG,6BAA6B,CAAC;AAE9E,uBAAuB;AACV,QAAA,0BAA0B,GAAG,uBAAuB,CAAC;AACrD,QAAA,6BAA6B,GAAG,0BAA0B,CAAC;AAExE,6BAA6B;AAChB,QAAA,yBAAyB,GAAG,sBAAsB,CAAC;AACnD,QAAA,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,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=types.js.map
@@ -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,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getSamplingDecision = getSamplingDecision;
4
+ var sdk_trace_base_1 = require("@opentelemetry/sdk-trace-base");
5
+ /**
6
+ * Get sampling decision based on session configuration
7
+ *
8
+ * If the session is sampled, traces will be collected.
9
+ * If the session is not sampled, traces will be dropped.
10
+ *
11
+ * @param sessionMeta - Current Faro session meta
12
+ * @returns OTEL sampling decision
13
+ */
14
+ function getSamplingDecision(sessionMeta) {
15
+ var _a;
16
+ if (sessionMeta === void 0) { sessionMeta = {}; }
17
+ var isSessionSampled = ((_a = sessionMeta.attributes) === null || _a === void 0 ? void 0 : _a['isSampled']) === 'true';
18
+ var samplingDecision = isSessionSampled ? sdk_trace_base_1.SamplingDecision.RECORD_AND_SAMPLED : sdk_trace_base_1.SamplingDecision.NOT_RECORD;
19
+ return samplingDecision;
20
+ }
21
+ //# sourceMappingURL=sampler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sampler.js","sourceRoot":"","sources":["../../../src/utils/sampler.ts"],"names":[],"mappings":";;AAaA,kDAKC;AAlBD,gEAAiE;AAIjE;;;;;;;;GAQG;AACH,SAAgB,mBAAmB,CAAC,WAA6B;;IAA7B,4BAAA,EAAA,gBAA6B;IAC/D,IAAM,gBAAgB,GAAG,CAAA,MAAA,WAAW,CAAC,UAAU,0CAAG,WAAW,CAAC,MAAK,MAAM,CAAC;IAC1E,IAAM,gBAAgB,GAAG,gBAAgB,CAAC,CAAC,CAAC,iCAAgB,CAAC,kBAAkB,CAAC,CAAC,CAAC,iCAAgB,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"]}
@@ -0,0 +1,65 @@
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 { ExportResultCode } from '@opentelemetry/core';
11
+ import { createExportTraceServiceRequest } from '@opentelemetry/otlp-transformer/build/src/trace/internal';
12
+ import { createInternalLogger } from '@grafana/faro-core';
13
+ const internalLogger = createInternalLogger();
14
+ import { sendFaroEvents } from './faroTraceExporter.utils';
15
+ /**
16
+ * FaroTraceExporter for React Native
17
+ *
18
+ * Exports OpenTelemetry spans to Faro backend using pushTraces API.
19
+ *
20
+ * IMPORTANT: To avoid infinite loops:
21
+ * - Uses internalLogger instead of console
22
+ * - Does NOT log during export (except errors)
23
+ * - Relies on Faro's internal deduplication
24
+ */
25
+ export class FaroTraceExporter {
26
+ constructor(config) {
27
+ this.config = config;
28
+ this._isShutdown = false;
29
+ }
30
+ export(spans, resultCallback) {
31
+ if (this._isShutdown) {
32
+ internalLogger.error('FaroTraceExporter: Cannot export spans, exporter is shut down');
33
+ resultCallback({ code: ExportResultCode.FAILED });
34
+ return;
35
+ }
36
+ try {
37
+ // Convert spans to OTLP format
38
+ const traceEvent = createExportTraceServiceRequest(spans, { useHex: true, useLongBits: false });
39
+ // Send traces to Faro
40
+ this.config.api.pushTraces(traceEvent);
41
+ // Send Faro events for CLIENT spans (HTTP requests, etc.)
42
+ // This is done WITHOUT logging to avoid infinite loops
43
+ sendFaroEvents(traceEvent.resourceSpans);
44
+ resultCallback({ code: ExportResultCode.SUCCESS });
45
+ }
46
+ catch (error) {
47
+ // Only log critical errors
48
+ internalLogger.error('FaroTraceExporter: Failed to export spans', error);
49
+ resultCallback({ code: ExportResultCode.FAILED });
50
+ }
51
+ }
52
+ shutdown() {
53
+ return __awaiter(this, void 0, void 0, function* () {
54
+ this._isShutdown = true;
55
+ return Promise.resolve(undefined);
56
+ });
57
+ }
58
+ forceFlush() {
59
+ return __awaiter(this, void 0, void 0, function* () {
60
+ // No-op for now - spans are sent immediately via pushTraces
61
+ return Promise.resolve(undefined);
62
+ });
63
+ }
64
+ }
65
+ //# sourceMappingURL=faroTraceExporter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"faroTraceExporter.js","sourceRoot":"","sources":["../../../src/exporters/faroTraceExporter.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAEvD,OAAO,EAAE,+BAA+B,EAAE,MAAM,0DAA0D,CAAC;AAG3G,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAE1D,MAAM,cAAc,GAAG,oBAAoB,EAAE,CAAC;AAI9C,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAE3D;;;;;;;;;GASG;AACH,MAAM,OAAO,iBAAiB;IAG5B,YAAoB,MAA+B;QAA/B,WAAM,GAAN,MAAM,CAAyB;QAF3C,gBAAW,GAAG,KAAK,CAAC;IAE0B,CAAC;IAEvD,MAAM,CAAC,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,gBAAgB,CAAC,MAAM,EAAE,CAAC,CAAC;YAClD,OAAO;QACT,CAAC;QAED,IAAI,CAAC;YACH,+BAA+B;YAC/B,MAAM,UAAU,GAAG,+BAA+B,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC,CAAC;YAEhG,sBAAsB;YACtB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;YAEvC,0DAA0D;YAC1D,uDAAuD;YACvD,cAAc,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;YAEzC,cAAc,CAAC,EAAE,IAAI,EAAE,gBAAgB,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,gBAAgB,CAAC,MAAM,EAAE,CAAC,CAAC;QACpD,CAAC;IACH,CAAC;IAEK,QAAQ;;YACZ,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;YACxB,OAAO,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QACpC,CAAC;KAAA;IAEK,UAAU;;YACd,4DAA4D;YAC5D,OAAO,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QACpC,CAAC;KAAA;CACF","sourcesContent":["import { ExportResultCode } from '@opentelemetry/core';\nimport type { ExportResult } from '@opentelemetry/core';\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, { useHex: true, useLongBits: false });\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"]}
@@ -0,0 +1,88 @@
1
+ import { ESpanKind } from '@opentelemetry/otlp-transformer/build/src/trace/internal-types';
2
+ import { createInternalLogger, faro, unknownString } from '@grafana/faro-core';
3
+ const internalLogger = createInternalLogger();
4
+ const DURATION_NS_KEY = 'duration_ns';
5
+ /**
6
+ * Send Faro events for CLIENT spans (HTTP requests, navigation, etc.)
7
+ *
8
+ * IMPORTANT: This function is called during trace export and must be careful to avoid infinite loops.
9
+ *
10
+ * Infinite loop prevention strategy:
11
+ * 1. Only process SPAN_KIND_CLIENT spans (HTTP requests, etc.)
12
+ * 2. Use faro.api.pushEvent which:
13
+ * - Does NOT trigger console logs if ConsoleInstrumentation is configured properly
14
+ * - Does NOT trigger HTTP instrumentation for collector URLs (they're ignored)
15
+ * 3. Use internalLogger for debugging instead of console
16
+ * 4. Never call console.log/warn/error in this function
17
+ *
18
+ * @param resourceSpans - OTLP resource spans from trace exporter
19
+ */
20
+ export function sendFaroEvents(resourceSpans = []) {
21
+ var _a, _b;
22
+ try {
23
+ for (const resourceSpan of resourceSpans) {
24
+ const { scopeSpans } = resourceSpan;
25
+ for (const scopeSpan of scopeSpans) {
26
+ const { scope, spans = [] } = scopeSpan;
27
+ for (const span of spans) {
28
+ // Only process CLIENT spans (HTTP requests, external calls)
29
+ // This avoids processing internal spans which could cause loops
30
+ if (span.kind !== ESpanKind.SPAN_KIND_CLIENT) {
31
+ continue;
32
+ }
33
+ const spanContext = {
34
+ traceId: span.traceId.toString(),
35
+ spanId: span.spanId.toString(),
36
+ };
37
+ const faroEventAttributes = {};
38
+ for (const attribute of span.attributes) {
39
+ faroEventAttributes[attribute.key] = String(Object.values(attribute.value)[0]);
40
+ }
41
+ // Add span duration in nanoseconds
42
+ if (!Number.isNaN(span.endTimeUnixNano) && !Number.isNaN(span.startTimeUnixNano)) {
43
+ faroEventAttributes[DURATION_NS_KEY] = String(Number(span.endTimeUnixNano) - Number(span.startTimeUnixNano));
44
+ }
45
+ const index = ((_a = scope === null || scope === void 0 ? void 0 : scope.name) !== null && _a !== void 0 ? _a : '').indexOf('-');
46
+ let eventName = unknownString;
47
+ if (scope === null || scope === void 0 ? void 0 : scope.name) {
48
+ if (index === -1) {
49
+ eventName = (_b = scope.name.split('/')[1]) !== null && _b !== void 0 ? _b : scope.name;
50
+ }
51
+ if (index > -1) {
52
+ eventName = scope === null || scope === void 0 ? void 0 : scope.name.substring(index + 1);
53
+ }
54
+ }
55
+ // Push event to Faro
56
+ // This should NOT cause infinite loops because:
57
+ // 1. The collector URL is in ignoreUrls for HttpInstrumentation
58
+ // 2. ConsoleInstrumentation won't log this event
59
+ // 3. No console.log calls are made here
60
+ faro.api.pushEvent(`faro.tracing.${eventName}`, faroEventAttributes, undefined, {
61
+ spanContext,
62
+ // Convert nanoseconds to milliseconds
63
+ timestampOverwriteMs: Number(span.endTimeUnixNano) / 1000000,
64
+ customPayloadTransformer: (payload) => {
65
+ var _a, _b;
66
+ if (faroEventAttributes['faro.action.user.name'] != null &&
67
+ faroEventAttributes['faro.action.user.parentId'] != null) {
68
+ payload.action = {
69
+ name: faroEventAttributes['faro.action.user.name'],
70
+ parentId: faroEventAttributes['faro.action.user.parentId'],
71
+ };
72
+ (_a = payload.attributes) === null || _a === void 0 ? true : delete _a['faro.action.user.name'];
73
+ (_b = payload.attributes) === null || _b === void 0 ? true : delete _b['faro.action.user.parentId'];
74
+ }
75
+ return payload;
76
+ },
77
+ });
78
+ }
79
+ }
80
+ }
81
+ }
82
+ catch (error) {
83
+ // Only log critical errors using internal logger
84
+ // Do NOT use console.log here as it could cause infinite loops
85
+ internalLogger.error('sendFaroEvents: Failed to process spans', error);
86
+ }
87
+ }
88
+ //# sourceMappingURL=faroTraceExporter.utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"faroTraceExporter.utils.js","sourceRoot":"","sources":["../../../src/exporters/faroTraceExporter.utils.ts"],"names":[],"mappings":"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","sourcesContent":["import type { SpanContext } from '@opentelemetry/api';\nimport { ESpanKind, type IResourceSpans } from '@opentelemetry/otlp-transformer/build/src/trace/internal-types';\n\nimport { createInternalLogger, faro, unknownString } from '@grafana/faro-core';\nimport type { EventAttributes as FaroEventAttributes } from '@grafana/faro-core';\n\nconst internalLogger = createInternalLogger();\n\nconst DURATION_NS_KEY = 'duration_ns';\n\n/**\n * Send Faro events for CLIENT spans (HTTP requests, navigation, etc.)\n *\n * IMPORTANT: This function is called during trace export and must be careful to avoid infinite loops.\n *\n * Infinite loop prevention strategy:\n * 1. Only process SPAN_KIND_CLIENT spans (HTTP requests, etc.)\n * 2. Use faro.api.pushEvent which:\n * - Does NOT trigger console logs if ConsoleInstrumentation is configured properly\n * - Does NOT trigger HTTP instrumentation for collector URLs (they're ignored)\n * 3. Use internalLogger for debugging instead of console\n * 4. Never call console.log/warn/error in this function\n *\n * @param resourceSpans - OTLP resource spans from trace exporter\n */\nexport function sendFaroEvents(resourceSpans: IResourceSpans[] = []) {\n try {\n for (const resourceSpan of resourceSpans) {\n const { scopeSpans } = resourceSpan;\n\n for (const scopeSpan of scopeSpans) {\n const { scope, spans = [] } = scopeSpan;\n\n for (const span of spans) {\n // Only process CLIENT spans (HTTP requests, external calls)\n // This avoids processing internal spans which could cause loops\n if (span.kind !== ESpanKind.SPAN_KIND_CLIENT) {\n continue;\n }\n\n const spanContext: Pick<SpanContext, 'traceId' | 'spanId'> = {\n traceId: span.traceId.toString(),\n spanId: span.spanId.toString(),\n };\n\n const faroEventAttributes: FaroEventAttributes = {};\n for (const attribute of span.attributes) {\n faroEventAttributes[attribute.key] = String(Object.values(attribute.value)[0]);\n }\n\n // Add span duration in nanoseconds\n if (!Number.isNaN(span.endTimeUnixNano) && !Number.isNaN(span.startTimeUnixNano)) {\n faroEventAttributes[DURATION_NS_KEY] = String(\n Number(span.endTimeUnixNano) - Number(span.startTimeUnixNano)\n );\n }\n\n const index = (scope?.name ?? '').indexOf('-');\n let eventName = unknownString;\n\n if (scope?.name) {\n if (index === -1) {\n eventName = scope.name.split('/')[1] ?? scope.name;\n }\n\n if (index > -1) {\n eventName = scope?.name.substring(index + 1);\n }\n }\n\n // Push event to Faro\n // This should NOT cause infinite loops because:\n // 1. The collector URL is in ignoreUrls for HttpInstrumentation\n // 2. ConsoleInstrumentation won't log this event\n // 3. No console.log calls are made here\n faro.api.pushEvent(`faro.tracing.${eventName}`, faroEventAttributes, undefined, {\n spanContext,\n // Convert nanoseconds to milliseconds\n timestampOverwriteMs: Number(span.endTimeUnixNano) / 1_000_000,\n customPayloadTransformer: (payload) => {\n if (\n faroEventAttributes['faro.action.user.name'] != null &&\n faroEventAttributes['faro.action.user.parentId'] != null\n ) {\n payload.action = {\n name: faroEventAttributes['faro.action.user.name'],\n parentId: faroEventAttributes['faro.action.user.parentId'],\n };\n\n delete payload.attributes?.['faro.action.user.name'];\n delete payload.attributes?.['faro.action.user.parentId'];\n }\n\n return payload;\n },\n });\n }\n }\n }\n } catch (error) {\n // Only log critical errors using internal logger\n // Do NOT use console.log here as it could cause infinite loops\n internalLogger.error('sendFaroEvents: Failed to process spans', error);\n }\n}\n"]}
@@ -0,0 +1,7 @@
1
+ export { FaroTraceExporter } from './exporters/faroTraceExporter';
2
+ export { getDefaultOTELInstrumentations } from './instrumentations/getDefaultOTELInstrumentations';
3
+ export { TracingInstrumentation } from './instrumentation';
4
+ export { getSamplingDecision } from './utils/sampler';
5
+ export { setSpanStatusOnFetchError, fetchCustomAttributeFunctionWithDefaults, } from './instrumentations/instrumentationUtils';
6
+ export { FaroMetaAttributesSpanProcessor } from './processors/faroMetaAttributesSpanProcessor';
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAElE,OAAO,EAAE,8BAA8B,EAAE,MAAM,mDAAmD,CAAC;AAEnG,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAE3D,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAItD,OAAO,EACL,yBAAyB,EACzB,wCAAwC,GACzC,MAAM,yCAAyC,CAAC;AAEjD,OAAO,EAAE,+BAA+B,EAAE,MAAM,8CAA8C,CAAC","sourcesContent":["export { FaroTraceExporter } from './exporters/faroTraceExporter';\n\nexport { getDefaultOTELInstrumentations } from './instrumentations/getDefaultOTELInstrumentations';\n\nexport { TracingInstrumentation } from './instrumentation';\n\nexport { getSamplingDecision } from './utils/sampler';\n\nexport type { FaroTraceExporterConfig, TracingInstrumentationOptions } from './types';\n\nexport {\n setSpanStatusOnFetchError,\n fetchCustomAttributeFunctionWithDefaults,\n} from './instrumentations/instrumentationUtils';\n\nexport { FaroMetaAttributesSpanProcessor } from './processors/faroMetaAttributesSpanProcessor';\n"]}