@coralogix/browser 2.11.1 → 2.13.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,15 @@
1
+ ## 2.13.0 (2025-12-07)
2
+
3
+ ### 🩹 Fixes
4
+
5
+ - **traces-exporter:** improve trace metadata encoding
6
+
7
+ ## 2.12.0 (2025-11-30)
8
+
9
+ ### 🚀 Features
10
+
11
+ - Introduced the tracesExporter callback, enabling custom processing or exporting of collected trace events.
12
+
1
13
  ## 2.11.1 (2025-11-24)
2
14
 
3
15
  ### 🩹 Fixes
package/README.md CHANGED
@@ -660,6 +660,7 @@ CoralogixRum.init({
660
660
  ```
661
661
 
662
662
  #### Custom Propagation
663
+
663
664
  ```javascript
664
665
  CoralogixRum.init({
665
666
  // ...
@@ -773,8 +774,10 @@ CoralogixRum.init({
773
774
  ```
774
775
 
775
776
  #### ignoreProxyUrlParams
777
+
776
778
  If you want the SDK to ignore the cxforward parameter and always send data to the proxy URL directly, you can use the ignoreProxyUrlParams option.
777
779
  By default, this option is disabled.
780
+
778
781
  ```javascript
779
782
  CoralogixRum.init({
780
783
  // ...
@@ -901,6 +904,26 @@ const customTracer = CoralogixRum.getCustomTracer({
901
904
  // ... your code
902
905
  ```
903
906
 
907
+ ### Traces Exporter
908
+
909
+ The `tracesExporter` callback gives you full control over how collected trace events are handled.
910
+ It receives a `TraceExporterData` object containing all trace data that the SDK has collected.
911
+
912
+ ```ts
913
+ CoralogixRum.init({
914
+ tracesExporter: (data: TraceExporterData) => {
915
+ // Example: forward traces to your own backend endpoint
916
+ fetch('https://api.mycompany.com/rum-traces', {
917
+ method: 'POST',
918
+ headers: {
919
+ 'Content-Type': 'application/json',
920
+ },
921
+ body: JSON.stringify(data),
922
+ });
923
+ },
924
+ });
925
+ ```
926
+
904
927
  ### Soft Navigations — Experimental
905
928
 
906
929
  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,157 @@ 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 mapStatusCodeToOtlp(code) {
3431
+ var _a;
3432
+ if (code == null)
3433
+ return undefined;
3434
+ var STATUS_CODE_MAP = {
3435
+ 0: 'STATUS_CODE_UNSET',
3436
+ 1: 'STATUS_CODE_OK',
3437
+ 2: 'STATUS_CODE_ERROR',
3438
+ };
3439
+ return (_a = STATUS_CODE_MAP[code]) !== null && _a !== void 0 ? _a : 'STATUS_CODE_UNSET';
3440
+ }
3441
+ function toOtlpStatus(spanStatus) {
3442
+ if (!spanStatus)
3443
+ return undefined;
3444
+ var code = mapStatusCodeToOtlp(spanStatus.code);
3445
+ var message = spanStatus.message;
3446
+ if (!code && !message)
3447
+ return undefined;
3448
+ return __assign({ code: code !== null && code !== void 0 ? code : 'STATUS_CODE_UNSET' }, (message ? { message: message } : {}));
3449
+ }
3450
+ function isWithinAllowedWindow(start, now) {
3451
+ return start <= now + MAX_FUTURE_NANOS && start >= now - MAX_PAST_NANOS;
3452
+ }
3453
+
3454
+ function buildExporterPayload(logs) {
3455
+ var e_1, _a;
3456
+ var _b;
3457
+ var resourceSpans = [];
3458
+ var nowUnixNano = Date.now() * NANOS_PER_MILLISECOND;
3459
+ try {
3460
+ for (var logs_1 = __values$1(logs), logs_1_1 = logs_1.next(); !logs_1_1.done; logs_1_1 = logs_1.next()) {
3461
+ var entry = logs_1_1.value;
3462
+ var instrumentationData = entry.instrumentation_data;
3463
+ if (!instrumentationData)
3464
+ continue;
3465
+ var spanJson = mapCxSpanToOtlpSpan(instrumentationData.otelSpan);
3466
+ if (!spanJson)
3467
+ continue;
3468
+ var startNanos = timestampToNanosNumber(instrumentationData.otelSpan.startTime);
3469
+ if (!isWithinAllowedWindow(startNanos, nowUnixNano))
3470
+ continue;
3471
+ var resource = mapEntryToOtlpResource(entry, (_b = instrumentationData.otelResource) === null || _b === void 0 ? void 0 : _b.attributes);
3472
+ var scopeSpans = {
3473
+ spans: [spanJson],
3474
+ };
3475
+ resourceSpans.push({ resource: resource, scope_spans: [scopeSpans] });
3476
+ }
3477
+ }
3478
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
3479
+ finally {
3480
+ try {
3481
+ if (logs_1_1 && !logs_1_1.done && (_a = logs_1.return)) _a.call(logs_1);
3482
+ }
3483
+ finally { if (e_1) throw e_1.error; }
3484
+ }
3485
+ return { resource_spans: resourceSpans };
3486
+ }
3487
+ function stripOtelFieldsFromLogs(logs) {
3488
+ return logs.map(function (_a) {
3489
+ _a.instrumentation_data; var rest = __rest(_a, ["instrumentation_data"]);
3490
+ return rest;
3491
+ });
3492
+ }
3493
+ function mapCxSpanToOtlpSpan(span) {
3494
+ var _a;
3495
+ if (!span.traceId || !span.spanId || !span.name) {
3496
+ if (getSdkConfig().debug) {
3497
+ console.debug('Missing required span fields: traceId, spanId, name', span);
3498
+ return null;
3499
+ }
3500
+ }
3501
+ if (!span.startTime || !span.endTime) {
3502
+ if (getSdkConfig().debug) {
3503
+ console.debug('Missing span timestamps', span);
3504
+ }
3505
+ return null;
3506
+ }
3507
+ var startUnixNano = timestampToNanosString(span.startTime);
3508
+ var endUnixNano = timestampToNanosString(span.endTime);
3509
+ return __assign(__assign(__assign({ trace_id: span.traceId, span_id: span.spanId }, (span.parentSpanId ? { parentSpanId: span.parentSpanId } : {})), { 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) } : {}));
3510
+ }
3511
+ function mapEntryToOtlpResource(entry, extra) {
3512
+ var _a;
3513
+ var base = {
3514
+ 'cx.application.name': entry.applicationName,
3515
+ 'cx.subsystem.name': entry.subsystemName,
3516
+ };
3517
+ var merged = __assign(__assign({}, base), (extra !== null && extra !== void 0 ? extra : {}));
3518
+ if (!('service.name' in merged)) {
3519
+ merged['service.name'] = entry.applicationName;
3520
+ }
3521
+ var version = (_a = entry.version_metadata) === null || _a === void 0 ? void 0 : _a.app_version;
3522
+ if (version && !('service.version' in merged)) {
3523
+ merged['service.version'] = version;
3524
+ }
3525
+ var attributes = toOtlpAttributes(merged);
3526
+ return attributes.length ? { attributes: attributes } : {};
3527
+ }
3528
+
3529
+ function buildCustomTraces(logs) {
3530
+ var tracesPayload = buildExporterPayload(logs);
3531
+ var strippedLogs = stripOtelFieldsFromLogs(logs);
3532
+ return { tracesPayload: tracesPayload, strippedLogs: strippedLogs };
3533
+ }
3534
+
3384
3535
  var CoralogixExporter = (function () {
3385
3536
  function CoralogixExporter() {
3386
3537
  var _this = this;
@@ -3393,6 +3544,9 @@ var CoralogixExporter = (function () {
3393
3544
  this.batchTimeDelay = BATCH_TIME_DELAY;
3394
3545
  this.invokeLogRequest = function (logs, markCachedLogsSent) {
3395
3546
  if (logs === null || logs === void 0 ? void 0 : logs.length) {
3547
+ if (_this.sdkConfig.tracesExporter) {
3548
+ logs = _this.buildAndExportOtelTraces(logs);
3549
+ }
3396
3550
  _this.request
3397
3551
  .send(JSON.stringify({
3398
3552
  logs: logs,
@@ -3463,6 +3617,14 @@ var CoralogixExporter = (function () {
3463
3617
  }
3464
3618
  }
3465
3619
  };
3620
+ CoralogixExporter.prototype.buildAndExportOtelTraces = function (logs) {
3621
+ var hasOtelSpanData = logs.some(function (log) { return !!log.instrumentation_data; });
3622
+ if (!hasOtelSpanData)
3623
+ return logs;
3624
+ var _a = buildCustomTraces(logs), tracesPayload = _a.tracesPayload, strippedLogs = _a.strippedLogs;
3625
+ this.sdkConfig.tracesExporter(tracesPayload);
3626
+ return strippedLogs;
3627
+ };
3466
3628
  CoralogixExporter.prototype.clearCachedDataForSessionWithError = function () {
3467
3629
  CxGlobal.cachedLogs = [];
3468
3630
  this.batchTimeDelay = BATCH_TIME_DELAY;
@@ -3968,7 +4130,7 @@ function resolveUrlBlueprinters(urlBlueprinters) {
3968
4130
  return resolvedUrlBlueprinters;
3969
4131
  }
3970
4132
 
3971
- var SDK_VERSION = '2.11.1';
4133
+ var SDK_VERSION = '2.13.0';
3972
4134
 
3973
4135
  function shouldDropEvent(cxRumEvent, options) {
3974
4136
  if (isDocumentErrorWithoutMessage(cxRumEvent)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coralogix/browser",
3
- "version": "2.11.1",
3
+ "version": "2.13.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
  }
@@ -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,9 @@
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 mapStatusCodeToOtlp(code?: SpanStatusCode | number): OtlpStatusCodeJson | undefined;
8
+ export declare function toOtlpStatus(spanStatus?: CxSpanOtelSpan['status']): OtlpStatusJson | undefined;
9
+ 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.1";
1
+ export declare const SDK_VERSION = "2.13.0";