@coralogix/browser 2.11.0 → 2.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,16 @@
1
+ ## 2.12.0 (2025-11-30)
2
+
3
+ ### 🚀 Features
4
+
5
+ - Introduced the tracesExporter callback, enabling custom processing or exporting of collected trace events.
6
+
7
+
8
+ ## 2.11.1 (2025-11-24)
9
+
10
+ ### 🩹 Fixes
11
+
12
+ - Trigger snapshot context when setUserContext is called
13
+
1
14
  ## 2.11.0 (2025-11-23)
2
15
 
3
16
  ### 🚀 Features
package/README.md CHANGED
@@ -901,6 +901,28 @@ const customTracer = CoralogixRum.getCustomTracer({
901
901
  // ... your code
902
902
  ```
903
903
 
904
+ ### Traces Exporter
905
+
906
+ The `tracesExporter` callback gives you full control over how collected trace events are handled.
907
+ It receives a `TracesExporterPayload` object containing all trace data that the SDK has collected.
908
+
909
+ ```ts
910
+ CoralogixRum.init({
911
+ tracesExporter: (data: TraceExporterData) => {
912
+ // Example: forward traces to your own backend endpoint
913
+ fetch('https://api.mycompany.com/rum-traces', {
914
+ method: 'POST',
915
+ headers: {
916
+ 'Content-Type': 'application/json',
917
+ },
918
+ body: JSON.stringify(data),
919
+ });
920
+
921
+ },
922
+ });
923
+ ```
924
+
925
+
904
926
  ### Soft Navigations — Experimental
905
927
 
906
928
  Soft navigations are navigations that do not trigger a full page reload, such as SPA navigations. Defaults to false.
package/index.esm2.js CHANGED
@@ -3381,6 +3381,179 @@ function isProcessorShouldStop(isActive) {
3381
3381
  return !isActive || !!((_a = getSessionManager()) === null || _a === void 0 ? void 0 : _a.isIdleActive);
3382
3382
  }
3383
3383
 
3384
+ var NANOS_IN_SECOND = 1000000000;
3385
+ var NANOS_PER_MILLISECOND = 1000000;
3386
+ var MAX_FUTURE_NANOS = 60 * 60 * NANOS_IN_SECOND;
3387
+ var MAX_PAST_NANOS = 24 * 60 * 60 * NANOS_IN_SECOND;
3388
+
3389
+ function toOtlpKeyValue(key, value) {
3390
+ if (!value) {
3391
+ return { key: key, value: { string_value: '' } };
3392
+ }
3393
+ switch (typeof value) {
3394
+ case 'string':
3395
+ return { key: key, value: { string_value: value } };
3396
+ case 'number':
3397
+ if (!Number.isFinite(value)) {
3398
+ return {
3399
+ key: key,
3400
+ value: { string_value: String(value) },
3401
+ };
3402
+ }
3403
+ return Number.isInteger(value)
3404
+ ? { key: key, value: { int_value: value } }
3405
+ : { key: key, value: { double_value: value } };
3406
+ case 'boolean':
3407
+ return { key: key, value: { bool_value: value } };
3408
+ default:
3409
+ return { key: key, value: { string_value: '' } };
3410
+ }
3411
+ }
3412
+ function toOtlpAttributes(attributes) {
3413
+ if (!attributes)
3414
+ return [];
3415
+ return Object.entries(attributes).map(function (_a) {
3416
+ var _b = __read$1(_a, 2), k = _b[0], v = _b[1];
3417
+ return toOtlpKeyValue(k, v);
3418
+ });
3419
+ }
3420
+ function timestampToNanosNumber(timestamp) {
3421
+ var _a = __read$1(timestamp, 2), seconds = _a[0], nanos = _a[1];
3422
+ return seconds * NANOS_IN_SECOND + nanos;
3423
+ }
3424
+ function timestampToNanosString(timestamp) {
3425
+ var _a = __read$1(timestamp, 2), seconds = _a[0], nanos = _a[1];
3426
+ var secondsPart = String(seconds);
3427
+ var nanosPart = String(nanos).padStart(9, '0');
3428
+ return secondsPart + nanosPart;
3429
+ }
3430
+ function hexToBase64(hex) {
3431
+ if (!hex)
3432
+ return '';
3433
+ var binary = '';
3434
+ for (var i = 0; i < hex.length; i += 2) {
3435
+ var byte = Number.parseInt(hex.slice(i, i + 2), 16);
3436
+ binary += String.fromCharCode(byte);
3437
+ }
3438
+ return btoa(binary);
3439
+ }
3440
+ function mapStatusCodeToOtlp(code) {
3441
+ var _a;
3442
+ if (code == null)
3443
+ return undefined;
3444
+ var STATUS_CODE_MAP = {
3445
+ 0: 'STATUS_CODE_UNSET',
3446
+ 1: 'STATUS_CODE_OK',
3447
+ 2: 'STATUS_CODE_ERROR',
3448
+ };
3449
+ return (_a = STATUS_CODE_MAP[code]) !== null && _a !== void 0 ? _a : 'STATUS_CODE_UNSET';
3450
+ }
3451
+ function toOtlpStatus(spanStatus) {
3452
+ if (!spanStatus)
3453
+ return undefined;
3454
+ var code = mapStatusCodeToOtlp(spanStatus.code);
3455
+ var message = spanStatus.message;
3456
+ if (!code && !message)
3457
+ return undefined;
3458
+ return __assign({ code: code !== null && code !== void 0 ? code : 'STATUS_CODE_UNSET' }, (message ? { message: message } : {}));
3459
+ }
3460
+ function isWithinAllowedWindow(start, now) {
3461
+ return start <= now + MAX_FUTURE_NANOS && start >= now - MAX_PAST_NANOS;
3462
+ }
3463
+
3464
+ function buildExporterPayload(logs) {
3465
+ var e_1, _a;
3466
+ var _b;
3467
+ var resourceSpans = [];
3468
+ var nowUnixNano = Date.now() * NANOS_PER_MILLISECOND;
3469
+ try {
3470
+ for (var logs_1 = __values$1(logs), logs_1_1 = logs_1.next(); !logs_1_1.done; logs_1_1 = logs_1.next()) {
3471
+ var entry = logs_1_1.value;
3472
+ var instrumentationData = entry.instrumentation_data;
3473
+ if (!instrumentationData)
3474
+ continue;
3475
+ var spanJson = mapCxSpanToOtlpSpan(instrumentationData.otelSpan);
3476
+ if (!spanJson)
3477
+ continue;
3478
+ var startNanos = timestampToNanosNumber(instrumentationData.otelSpan.startTime);
3479
+ if (!isWithinAllowedWindow(startNanos, nowUnixNano))
3480
+ continue;
3481
+ var resource = mapEntryToOtlpResource(entry, (_b = instrumentationData.otelResource) === null || _b === void 0 ? void 0 : _b.attributes);
3482
+ var scopeSpans = {
3483
+ spans: [spanJson],
3484
+ };
3485
+ resourceSpans.push({ resource: resource, scope_spans: [scopeSpans] });
3486
+ }
3487
+ }
3488
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
3489
+ finally {
3490
+ try {
3491
+ if (logs_1_1 && !logs_1_1.done && (_a = logs_1.return)) _a.call(logs_1);
3492
+ }
3493
+ finally { if (e_1) throw e_1.error; }
3494
+ }
3495
+ return { resource_spans: resourceSpans };
3496
+ }
3497
+ function stripOtelFieldsFromLogs(logs) {
3498
+ return logs.map(function (_a) {
3499
+ _a.instrumentation_data; var rest = __rest(_a, ["instrumentation_data"]);
3500
+ return rest;
3501
+ });
3502
+ }
3503
+ function mapCxSpanToOtlpSpan(span) {
3504
+ var _a;
3505
+ if (!span.traceId || !span.spanId || !span.name) {
3506
+ if (getSdkConfig().debug) {
3507
+ console.debug('Missing required span fields: traceId, spanId, name', span);
3508
+ return null;
3509
+ }
3510
+ }
3511
+ if (!span.startTime || !span.endTime) {
3512
+ if (getSdkConfig().debug) {
3513
+ console.debug('Missing span timestamps', span);
3514
+ }
3515
+ return null;
3516
+ }
3517
+ var ids = encodeSpanIds(span);
3518
+ if (!ids)
3519
+ return null;
3520
+ var startUnixNano = timestampToNanosString(span.startTime);
3521
+ var endUnixNano = timestampToNanosString(span.endTime);
3522
+ return __assign(__assign(__assign({ trace_id: ids.traceIdB64, span_id: ids.spanIdB64 }, (ids.parentSpanIdB64 ? { parentSpanId: ids.parentSpanIdB64 } : {})), { name: (_a = span.name) !== null && _a !== void 0 ? _a : '', kind: 'SPAN_KIND_CLIENT', start_time_unix_nano: startUnixNano, end_time_unix_nano: endUnixNano, attributes: toOtlpAttributes(span.attributes) }), (span.status ? { status: toOtlpStatus(span.status) } : {}));
3523
+ }
3524
+ function mapEntryToOtlpResource(entry, extra) {
3525
+ var _a;
3526
+ var base = {
3527
+ 'cx.application.name': entry.applicationName,
3528
+ 'cx.subsystem.name': entry.subsystemName,
3529
+ };
3530
+ var merged = __assign(__assign({}, base), (extra !== null && extra !== void 0 ? extra : {}));
3531
+ if (!('service.name' in merged)) {
3532
+ merged['service.name'] = entry.applicationName;
3533
+ }
3534
+ var version = (_a = entry.version_metadata) === null || _a === void 0 ? void 0 : _a.app_version;
3535
+ if (version && !('service.version' in merged)) {
3536
+ merged['service.version'] = version;
3537
+ }
3538
+ var attributes = toOtlpAttributes(merged);
3539
+ return attributes.length ? { attributes: attributes } : {};
3540
+ }
3541
+ function encodeSpanIds(span) {
3542
+ return {
3543
+ traceIdB64: hexToBase64(span.traceId),
3544
+ spanIdB64: hexToBase64(span.spanId),
3545
+ parentSpanIdB64: span.parentSpanId
3546
+ ? hexToBase64(span.parentSpanId)
3547
+ : undefined,
3548
+ };
3549
+ }
3550
+
3551
+ function buildCustomTraces(logs) {
3552
+ var tracesPayload = buildExporterPayload(logs);
3553
+ var strippedLogs = stripOtelFieldsFromLogs(logs);
3554
+ return { tracesPayload: tracesPayload, strippedLogs: strippedLogs };
3555
+ }
3556
+
3384
3557
  var CoralogixExporter = (function () {
3385
3558
  function CoralogixExporter() {
3386
3559
  var _this = this;
@@ -3393,6 +3566,9 @@ var CoralogixExporter = (function () {
3393
3566
  this.batchTimeDelay = BATCH_TIME_DELAY;
3394
3567
  this.invokeLogRequest = function (logs, markCachedLogsSent) {
3395
3568
  if (logs === null || logs === void 0 ? void 0 : logs.length) {
3569
+ if (_this.sdkConfig.tracesExporter) {
3570
+ logs = _this.buildAndExportOtelTraces(logs);
3571
+ }
3396
3572
  _this.request
3397
3573
  .send(JSON.stringify({
3398
3574
  logs: logs,
@@ -3463,6 +3639,14 @@ var CoralogixExporter = (function () {
3463
3639
  }
3464
3640
  }
3465
3641
  };
3642
+ CoralogixExporter.prototype.buildAndExportOtelTraces = function (logs) {
3643
+ var hasOtelSpanData = logs.some(function (log) { return !!log.instrumentation_data; });
3644
+ if (!hasOtelSpanData)
3645
+ return logs;
3646
+ var _a = buildCustomTraces(logs), tracesPayload = _a.tracesPayload, strippedLogs = _a.strippedLogs;
3647
+ this.sdkConfig.tracesExporter(tracesPayload);
3648
+ return strippedLogs;
3649
+ };
3466
3650
  CoralogixExporter.prototype.clearCachedDataForSessionWithError = function () {
3467
3651
  CxGlobal.cachedLogs = [];
3468
3652
  this.batchTimeDelay = BATCH_TIME_DELAY;
@@ -3968,7 +4152,7 @@ function resolveUrlBlueprinters(urlBlueprinters) {
3968
4152
  return resolvedUrlBlueprinters;
3969
4153
  }
3970
4154
 
3971
- var SDK_VERSION = '2.11.0';
4155
+ var SDK_VERSION = '2.12.0';
3972
4156
 
3973
4157
  function shouldDropEvent(cxRumEvent, options) {
3974
4158
  if (isDocumentErrorWithoutMessage(cxRumEvent)) {
@@ -4378,6 +4562,10 @@ var CoralogixSnapshotSpanProcessor = (function () {
4378
4562
  if (spanType === CoralogixEventType.USER_INTERACTION) {
4379
4563
  updateSnapshot({ actionCount: actionCount + 1 });
4380
4564
  }
4565
+ if (getSnapshotManager().shouldTriggerSnapshotContext) {
4566
+ shouldCreateSnapshot = true;
4567
+ getSnapshotManager().shouldTriggerSnapshotContext = false;
4568
+ }
4381
4569
  return shouldCreateSnapshot;
4382
4570
  };
4383
4571
  return CoralogixSnapshotSpanProcessor;
@@ -4386,6 +4574,7 @@ var CoralogixSnapshotSpanProcessor = (function () {
4386
4574
  var SnapshotManager = (function () {
4387
4575
  function SnapshotManager() {
4388
4576
  var _this = this;
4577
+ this._shouldTriggerSnapshotContext = false;
4389
4578
  this.updateSnapshot = function (overrides) {
4390
4579
  _this._currentSnapshot = __assign(__assign({}, _this._currentSnapshot), overrides);
4391
4580
  };
@@ -4421,6 +4610,16 @@ var SnapshotManager = (function () {
4421
4610
  enumerable: false,
4422
4611
  configurable: true
4423
4612
  });
4613
+ Object.defineProperty(SnapshotManager.prototype, "shouldTriggerSnapshotContext", {
4614
+ get: function () {
4615
+ return this._shouldTriggerSnapshotContext;
4616
+ },
4617
+ set: function (value) {
4618
+ this._shouldTriggerSnapshotContext = value;
4619
+ },
4620
+ enumerable: false,
4621
+ configurable: true
4622
+ });
4424
4623
  return SnapshotManager;
4425
4624
  }());
4426
4625
 
@@ -4793,6 +4992,7 @@ var CoralogixRum = {
4793
4992
  console.debug('CoralogixRum must be initiated before setting user context');
4794
4993
  return;
4795
4994
  }
4995
+ getSnapshotManager().shouldTriggerSnapshotContext = true;
4796
4996
  attributesProcessor === null || attributesProcessor === void 0 ? void 0 : attributesProcessor.setInternalLabels(__assign(__assign({}, attributesProcessor.getInternalLabels()), (_a = {}, _a[CoralogixAttributes.USER_CONTEXT] = userContext, _a)));
4797
4997
  },
4798
4998
  getUserContext: function () {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coralogix/browser",
3
- "version": "2.11.0",
3
+ "version": "2.12.0",
4
4
  "description": "Official Coralogix SDK for browsers",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Coralogix",
@@ -14,6 +14,7 @@ export declare class CoralogixExporter implements SpanExporter, ProcessorBaseMod
14
14
  export(spans: CxReadableSpan[], resultCallback: (result: ExportResult) => void): void;
15
15
  private handleSessionWithError;
16
16
  private invokeLogRequest;
17
+ private buildAndExportOtelTraces;
17
18
  clearCachedDataForSessionWithError(): void;
18
19
  shutdown(): Promise<void>;
19
20
  }
@@ -3,11 +3,14 @@ export declare class SnapshotManager {
3
3
  private _currentSnapshot;
4
4
  private _fragmentsState;
5
5
  private _isSnapshotSentDueToRecording;
6
+ private _shouldTriggerSnapshotContext;
6
7
  constructor();
7
8
  get fragmentsState(): Set<string>;
8
9
  get currentSnapshot(): SnapshotContext;
9
10
  get isSnapshotSentDueToRecording(): boolean;
11
+ get shouldTriggerSnapshotContext(): boolean;
10
12
  set isSnapshotSentDueToRecording(value: boolean);
13
+ set shouldTriggerSnapshotContext(value: boolean);
11
14
  updateSnapshot: (overrides: Partial<SnapshotContext>) => void;
12
15
  resetSnapshot: () => void;
13
16
  }
@@ -0,0 +1,4 @@
1
+ export declare const NANOS_IN_SECOND = 1000000000;
2
+ export declare const NANOS_PER_MILLISECOND = 1000000;
3
+ export declare const MAX_FUTURE_NANOS: number;
4
+ export declare const MAX_PAST_NANOS: number;
@@ -0,0 +1,3 @@
1
+ import { CxSpan } from '../types';
2
+ import { BuildCustomTracesResult } from './traces-exporter.types';
3
+ export declare function buildCustomTraces(logs: CxSpan[]): BuildCustomTracesResult;
@@ -0,0 +1,4 @@
1
+ import { TraceExporterData } from './traces-exporter.types';
2
+ import { CxSpan } from '../types';
3
+ export declare function buildExporterPayload(logs: CxSpan[]): TraceExporterData;
4
+ export declare function stripOtelFieldsFromLogs(logs: CxSpan[]): Omit<CxSpan, 'instrumentation_data'>[];
@@ -0,0 +1,71 @@
1
+ import type { HrTime, SpanStatusCode } from '@opentelemetry/api';
2
+ import { CxSpan } from '../types';
3
+ export interface OtlpAnyValueJson {
4
+ string_value?: string;
5
+ bool_value?: boolean;
6
+ int_value?: number | string;
7
+ double_value?: number | string;
8
+ }
9
+ export interface OtlpKeyValueJson {
10
+ key: string;
11
+ value?: OtlpAnyValueJson;
12
+ }
13
+ export type OtlpStatusCodeJson = 'STATUS_CODE_UNSET' | 'STATUS_CODE_OK' | 'STATUS_CODE_ERROR';
14
+ export interface OtlpStatusJson {
15
+ code?: OtlpStatusCodeJson;
16
+ message?: string;
17
+ }
18
+ export type OtlpSpanKindJson = 'SPAN_KIND_INTERNAL' | 'SPAN_KIND_SERVER' | 'SPAN_KIND_CLIENT' | 'SPAN_KIND_PRODUCER' | 'SPAN_KIND_CONSUMER';
19
+ export interface OtlpSpanJson {
20
+ trace_id: string;
21
+ span_id: string;
22
+ parent_span_id?: string;
23
+ name: string;
24
+ kind: OtlpSpanKindJson;
25
+ start_time_unix_nano: string;
26
+ end_time_unix_nano: string;
27
+ attributes?: OtlpKeyValueJson[];
28
+ status?: OtlpStatusJson;
29
+ }
30
+ export interface OtlpScopeJson {
31
+ name?: string;
32
+ version?: string;
33
+ attributes?: OtlpKeyValueJson[];
34
+ }
35
+ export interface OtlpScopeSpansJson {
36
+ scope?: OtlpScopeJson;
37
+ spans: OtlpSpanJson[];
38
+ }
39
+ export interface OtlpResourceJson {
40
+ attributes?: OtlpKeyValueJson[];
41
+ }
42
+ export interface OtlpResourceSpansJson {
43
+ resource?: OtlpResourceJson;
44
+ scope_spans: OtlpScopeSpansJson[];
45
+ }
46
+ export type CustomTimestamp = [seconds: number, nanos: number];
47
+ export type ResourceAttributes = Record<string, unknown>;
48
+ export interface TraceExporterData {
49
+ resource_spans: OtlpResourceSpansJson[];
50
+ }
51
+ export interface CxSpanOtelSpan {
52
+ spanId: string;
53
+ traceId: string;
54
+ sessionId?: string;
55
+ parentSpanId?: string;
56
+ name?: string;
57
+ attributes?: Record<string, unknown>;
58
+ startTime?: HrTime;
59
+ endTime?: HrTime;
60
+ status?: {
61
+ code: SpanStatusCode | number;
62
+ message?: string;
63
+ };
64
+ }
65
+ export interface CxSpanOtelResource {
66
+ attributes?: Record<string, unknown>;
67
+ }
68
+ export interface BuildCustomTracesResult {
69
+ tracesPayload: TraceExporterData;
70
+ strippedLogs: CxSpan[];
71
+ }
@@ -0,0 +1,10 @@
1
+ import { SpanStatusCode } from '@opentelemetry/api';
2
+ import { CustomTimestamp, CxSpanOtelSpan, OtlpKeyValueJson, OtlpStatusCodeJson, OtlpStatusJson } from './traces-exporter.types';
3
+ export declare function toOtlpKeyValue(key: string, value: unknown): OtlpKeyValueJson;
4
+ export declare function toOtlpAttributes(attributes?: Record<string, unknown>): OtlpKeyValueJson[];
5
+ export declare function timestampToNanosNumber(timestamp: CustomTimestamp): number;
6
+ export declare function timestampToNanosString(timestamp: CustomTimestamp): string;
7
+ export declare function hexToBase64(hex: string): string;
8
+ export declare function mapStatusCodeToOtlp(code?: SpanStatusCode | number): OtlpStatusCodeJson | undefined;
9
+ export declare function toOtlpStatus(spanStatus?: CxSpanOtelSpan['status']): OtlpStatusJson | undefined;
10
+ export declare function isWithinAllowedWindow(start: number, now: number): boolean;
package/src/types.d.ts CHANGED
@@ -17,6 +17,7 @@ import { MemoryUsageContext } from './instrumentations/memory-usage';
17
17
  import { MFEMetadata } from './mfe/mfe.types';
18
18
  import { UrlBasedLabelProvider } from './label-providers/url-based-label-provider';
19
19
  import { CustomTracer, CustomTracerIgnoredInstruments } from './custom-spans';
20
+ import { TraceExporterData } from './traces-exporter/traces-exporter.types';
20
21
  export interface CxStackFrame extends Partial<MFEMetadata> {
21
22
  fileName?: string;
22
23
  columnNumber?: number;
@@ -157,6 +158,7 @@ export interface CoralogixBrowserSdkConfig {
157
158
  networkExtraConfig?: NetworkExtraConfig[];
158
159
  supportMfe?: boolean;
159
160
  workerSupport?: boolean;
161
+ tracesExporter?: (data: TraceExporterData) => void;
160
162
  }
161
163
  export interface CoralogixOtelWebType extends SendLog {
162
164
  init: (options: CoralogixBrowserSdkConfig) => void;
package/src/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const SDK_VERSION = "2.11.0";
1
+ export declare const SDK_VERSION = "2.12.0";