@multiplayer-app/session-recorder-common 1.3.27 → 1.3.28

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 (68) hide show
  1. package/dist/esm/SessionRecorderTraceIdRatioBasedSampler.d.ts +2 -1
  2. package/dist/esm/SessionRecorderTraceIdRatioBasedSampler.d.ts.map +1 -1
  3. package/dist/esm/SessionRecorderTraceIdRatioBasedSampler.js +10 -5
  4. package/dist/esm/SessionRecorderTraceIdRatioBasedSampler.js.map +1 -1
  5. package/dist/esm/exporters/SessionRecorderBrowserTraceExporter.d.ts +6 -2
  6. package/dist/esm/exporters/SessionRecorderBrowserTraceExporter.d.ts.map +1 -1
  7. package/dist/esm/exporters/SessionRecorderBrowserTraceExporter.js +57 -21
  8. package/dist/esm/exporters/SessionRecorderBrowserTraceExporter.js.map +1 -1
  9. package/dist/esm/sdk/capture-exception.d.ts +1 -1
  10. package/dist/esm/sdk/capture-exception.d.ts.map +1 -1
  11. package/dist/esm/sdk/capture-exception.js +59 -5
  12. package/dist/esm/sdk/capture-exception.js.map +1 -1
  13. package/dist/esm/sdk/index.d.ts +1 -0
  14. package/dist/esm/sdk/index.d.ts.map +1 -1
  15. package/dist/esm/sdk/index.js +1 -0
  16. package/dist/esm/sdk/index.js.map +1 -1
  17. package/dist/esm/sdk/set-resource-attributes.d.ts +3 -0
  18. package/dist/esm/sdk/set-resource-attributes.d.ts.map +1 -0
  19. package/dist/esm/sdk/set-resource-attributes.js +8 -0
  20. package/dist/esm/sdk/set-resource-attributes.js.map +1 -0
  21. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  22. package/dist/esnext/SessionRecorderTraceIdRatioBasedSampler.d.ts +2 -1
  23. package/dist/esnext/SessionRecorderTraceIdRatioBasedSampler.d.ts.map +1 -1
  24. package/dist/esnext/SessionRecorderTraceIdRatioBasedSampler.js +10 -5
  25. package/dist/esnext/SessionRecorderTraceIdRatioBasedSampler.js.map +1 -1
  26. package/dist/esnext/exporters/SessionRecorderBrowserTraceExporter.d.ts +6 -2
  27. package/dist/esnext/exporters/SessionRecorderBrowserTraceExporter.d.ts.map +1 -1
  28. package/dist/esnext/exporters/SessionRecorderBrowserTraceExporter.js +57 -20
  29. package/dist/esnext/exporters/SessionRecorderBrowserTraceExporter.js.map +1 -1
  30. package/dist/esnext/sdk/capture-exception.d.ts +1 -1
  31. package/dist/esnext/sdk/capture-exception.d.ts.map +1 -1
  32. package/dist/esnext/sdk/capture-exception.js +30 -5
  33. package/dist/esnext/sdk/capture-exception.js.map +1 -1
  34. package/dist/esnext/sdk/index.d.ts +1 -0
  35. package/dist/esnext/sdk/index.d.ts.map +1 -1
  36. package/dist/esnext/sdk/index.js +1 -0
  37. package/dist/esnext/sdk/index.js.map +1 -1
  38. package/dist/esnext/sdk/set-resource-attributes.d.ts +3 -0
  39. package/dist/esnext/sdk/set-resource-attributes.d.ts.map +1 -0
  40. package/dist/esnext/sdk/set-resource-attributes.js +8 -0
  41. package/dist/esnext/sdk/set-resource-attributes.js.map +1 -0
  42. package/dist/esnext/tsconfig.esnext.tsbuildinfo +1 -1
  43. package/dist/src/SessionRecorderTraceIdRatioBasedSampler.d.ts +2 -1
  44. package/dist/src/SessionRecorderTraceIdRatioBasedSampler.d.ts.map +1 -1
  45. package/dist/src/SessionRecorderTraceIdRatioBasedSampler.js +8 -2
  46. package/dist/src/SessionRecorderTraceIdRatioBasedSampler.js.map +1 -1
  47. package/dist/src/exporters/SessionRecorderBrowserTraceExporter.d.ts +6 -2
  48. package/dist/src/exporters/SessionRecorderBrowserTraceExporter.d.ts.map +1 -1
  49. package/dist/src/exporters/SessionRecorderBrowserTraceExporter.js +56 -19
  50. package/dist/src/exporters/SessionRecorderBrowserTraceExporter.js.map +1 -1
  51. package/dist/src/sdk/capture-exception.d.ts +1 -1
  52. package/dist/src/sdk/capture-exception.d.ts.map +1 -1
  53. package/dist/src/sdk/capture-exception.js +30 -5
  54. package/dist/src/sdk/capture-exception.js.map +1 -1
  55. package/dist/src/sdk/index.d.ts +1 -0
  56. package/dist/src/sdk/index.d.ts.map +1 -1
  57. package/dist/src/sdk/index.js +1 -0
  58. package/dist/src/sdk/index.js.map +1 -1
  59. package/dist/src/sdk/set-resource-attributes.d.ts +3 -0
  60. package/dist/src/sdk/set-resource-attributes.d.ts.map +1 -0
  61. package/dist/src/sdk/set-resource-attributes.js +13 -0
  62. package/dist/src/sdk/set-resource-attributes.js.map +1 -0
  63. package/package.json +1 -1
  64. package/src/SessionRecorderTraceIdRatioBasedSampler.ts +29 -4
  65. package/src/exporters/SessionRecorderBrowserTraceExporter.ts +57 -24
  66. package/src/sdk/capture-exception.ts +49 -4
  67. package/src/sdk/index.ts +1 -0
  68. package/src/sdk/set-resource-attributes.ts +9 -0
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SessionRecorderTraceIdRatioBasedSampler = void 0;
4
4
  const api_1 = require("@opentelemetry/api");
5
5
  const sdk_trace_base_1 = require("@opentelemetry/sdk-trace-base");
6
+ const semantic_conventions_1 = require("@opentelemetry/semantic-conventions");
6
7
  const constants_base_1 = require("./constants/constants.base");
7
8
  class SessionRecorderTraceIdRatioBasedSampler {
8
9
  constructor(_ratio = 0) {
@@ -10,10 +11,15 @@ class SessionRecorderTraceIdRatioBasedSampler {
10
11
  this._ratio = this._normalize(_ratio);
11
12
  this._upperBound = Math.floor(this._ratio * 0xffffffff);
12
13
  }
13
- shouldSample(context, traceId) {
14
+ shouldSample(context, traceId, spanName, spanKind, attributes, links) {
15
+ if (attributes[semantic_conventions_1.ATTR_EXCEPTION_MESSAGE] || attributes[semantic_conventions_1.ATTR_EXCEPTION_STACKTRACE] || attributes[semantic_conventions_1.ATTR_EXCEPTION_TYPE]) {
16
+ return {
17
+ decision: sdk_trace_base_1.SamplingDecision.RECORD_AND_SAMPLED,
18
+ };
19
+ }
14
20
  if (traceId.startsWith(constants_base_1.MULTIPLAYER_TRACE_DEBUG_PREFIX)
15
21
  || traceId.startsWith(constants_base_1.MULTIPLAYER_TRACE_CONTINUOUS_DEBUG_PREFIX)
16
- // || traceId.startsWith(MULTIPLAYER_TRACE_SESSION_CACHE_PREFIX)
22
+ || traceId.startsWith(constants_base_1.MULTIPLAYER_TRACE_SESSION_CACHE_PREFIX)
17
23
  // || traceId.startsWith(MULTIPLAYER_TRACE_CONTINUOUS_SESSION_CACHE_PREFIX)
18
24
  ) {
19
25
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"SessionRecorderTraceIdRatioBasedSampler.js","sourceRoot":"","sources":["../../src/SessionRecorderTraceIdRatioBasedSampler.ts"],"names":[],"mappings":";;;AAAA,4CAAmD;AACnD,kEAIsC;AACtC,+DAKmC;AAEnC,MAAa,uCAAuC;IAGlD,YAA6B,SAAiB,CAAC;QAAlB,WAAM,GAAN,MAAM,CAAY;QAC7C,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAA;QACrC,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,UAAU,CAAC,CAAA;IACzD,CAAC;IAED,YAAY,CAAC,OAAgB,EAAE,OAAe;QAC5C,IACE,OAAO,CAAC,UAAU,CAAC,+CAA8B,CAAC;eAC/C,OAAO,CAAC,UAAU,CAAC,0DAAyC,CAAC;QAChE,gEAAgE;QAChE,2EAA2E;UAC3E,CAAC;YACD,OAAO;gBACL,QAAQ,EAAE,iCAAgB,CAAC,kBAAkB;aAC9C,CAAA;QACH,CAAC;QAED,IAAI,QAAQ,GAAqB,iCAAgB,CAAC,UAAU,CAAA;QAE5D,IACE,IAAA,oBAAc,EAAC,OAAO,CAAC;eACpB,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,WAAW,EAC/C,CAAC;YACD,QAAQ,GAAG,iCAAgB,CAAC,kBAAkB,CAAA;QAChD,CAAC;QAED,OAAO,EAAE,QAAQ,EAAE,CAAA;IACrB,CAAC;IAED,QAAQ;QACN,OAAO,2CAA2C,IAAI,CAAC,MAAM,GAAG,CAAA;IAClE,CAAC;IAEO,UAAU,CAAC,KAAa;QAC9B,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,KAAK,CAAC;YAAE,OAAO,CAAC,CAAA;QACvD,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAA;IAChD,CAAC;IAEO,WAAW,CAAC,OAAe;QACjC,IAAI,YAAY,GAAG,CAAC,CAAA;QACpB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YAC5C,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,CAAA;YACjB,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;YACtD,YAAY,GAAG,CAAC,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,CAAA;QAC5C,CAAC;QACD,OAAO,YAAY,CAAA;IACrB,CAAC;CACF;AAlDD,0FAkDC","sourcesContent":["import { isValidTraceId } from '@opentelemetry/api'\nimport {\n Sampler,\n SamplingDecision,\n SamplingResult,\n} from '@opentelemetry/sdk-trace-base'\nimport {\n MULTIPLAYER_TRACE_DEBUG_PREFIX,\n MULTIPLAYER_TRACE_CONTINUOUS_DEBUG_PREFIX,\n // MULTIPLAYER_TRACE_SESSION_CACHE_PREFIX,\n // MULTIPLAYER_TRACE_CONTINUOUS_SESSION_CACHE_PREFIX,\n} from './constants/constants.base'\n\nexport class SessionRecorderTraceIdRatioBasedSampler implements Sampler {\n private _upperBound: number\n\n constructor(private readonly _ratio: number = 0) {\n this._ratio = this._normalize(_ratio)\n this._upperBound = Math.floor(this._ratio * 0xffffffff)\n }\n\n shouldSample(context: unknown, traceId: string): SamplingResult {\n if (\n traceId.startsWith(MULTIPLAYER_TRACE_DEBUG_PREFIX)\n || traceId.startsWith(MULTIPLAYER_TRACE_CONTINUOUS_DEBUG_PREFIX)\n // || traceId.startsWith(MULTIPLAYER_TRACE_SESSION_CACHE_PREFIX)\n // || traceId.startsWith(MULTIPLAYER_TRACE_CONTINUOUS_SESSION_CACHE_PREFIX)\n ) {\n return {\n decision: SamplingDecision.RECORD_AND_SAMPLED,\n }\n }\n\n let decision: SamplingDecision = SamplingDecision.NOT_RECORD\n\n if (\n isValidTraceId(traceId)\n && this._accumulate(traceId) < this._upperBound\n ) {\n decision = SamplingDecision.RECORD_AND_SAMPLED\n }\n\n return { decision }\n }\n\n toString(): string {\n return `SessionRecorderTraceIdRatioBasedSampler{${this._ratio}}`\n }\n\n private _normalize(ratio: number): number {\n if (typeof ratio !== 'number' || isNaN(ratio)) return 0\n return ratio >= 1 ? 1 : ratio <= 0 ? 0 : ratio\n }\n\n private _accumulate(traceId: string): number {\n let accumulation = 0\n for (let i = 0; i < traceId.length / 8; i++) {\n const pos = i * 8\n const part = parseInt(traceId.slice(pos, pos + 8), 16)\n accumulation = (accumulation ^ part) >>> 0\n }\n return accumulation\n }\n}\n"]}
1
+ {"version":3,"file":"SessionRecorderTraceIdRatioBasedSampler.js","sourceRoot":"","sources":["../../src/SessionRecorderTraceIdRatioBasedSampler.ts"],"names":[],"mappings":";;;AACA,4CAM2B;AAC3B,kEAIsC;AACtC,8EAI4C;AAC5C,+DAKmC;AAEnC,MAAa,uCAAuC;IAGlD,YAA6B,SAAiB,CAAC;QAAlB,WAAM,GAAN,MAAM,CAAY;QAC7C,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAA;QACrC,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,UAAU,CAAC,CAAA;IACzD,CAAC;IAED,YAAY,CACV,OAAgB,EAChB,OAAe,EACf,QAAgB,EAChB,QAAkB,EAClB,UAAsB,EACtB,KAAa;QAEb,IAAI,UAAU,CAAC,6CAAsB,CAAC,IAAI,UAAU,CAAC,gDAAyB,CAAC,IAAI,UAAU,CAAC,0CAAmB,CAAC,EAAE,CAAC;YACnH,OAAO;gBACL,QAAQ,EAAE,iCAAgB,CAAC,kBAAkB;aAC9C,CAAA;QACH,CAAC;QAED,IACE,OAAO,CAAC,UAAU,CAAC,+CAA8B,CAAC;eAC/C,OAAO,CAAC,UAAU,CAAC,0DAAyC,CAAC;eAC7D,OAAO,CAAC,UAAU,CAAC,uDAAsC,CAAC;QAC7D,2EAA2E;UAC3E,CAAC;YACD,OAAO;gBACL,QAAQ,EAAE,iCAAgB,CAAC,kBAAkB;aAC9C,CAAA;QACH,CAAC;QAED,IAAI,QAAQ,GAAqB,iCAAgB,CAAC,UAAU,CAAA;QAE5D,IACE,IAAA,oBAAc,EAAC,OAAO,CAAC;eACpB,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,WAAW,EAC/C,CAAC;YACD,QAAQ,GAAG,iCAAgB,CAAC,kBAAkB,CAAA;QAChD,CAAC;QAED,OAAO,EAAE,QAAQ,EAAE,CAAA;IACrB,CAAC;IAED,QAAQ;QACN,OAAO,2CAA2C,IAAI,CAAC,MAAM,GAAG,CAAA;IAClE,CAAC;IAEO,UAAU,CAAC,KAAa;QAC9B,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,KAAK,CAAC;YAAE,OAAO,CAAC,CAAA;QACvD,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAA;IAChD,CAAC;IAEO,WAAW,CAAC,OAAe;QACjC,IAAI,YAAY,GAAG,CAAC,CAAA;QACpB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YAC5C,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,CAAA;YACjB,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;YACtD,YAAY,GAAG,CAAC,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,CAAA;QAC5C,CAAC;QACD,OAAO,YAAY,CAAA;IACrB,CAAC;CACF;AA/DD,0FA+DC","sourcesContent":["\nimport {\n isValidTraceId,\n Context,\n SpanKind,\n Attributes,\n Link,\n} from '@opentelemetry/api'\nimport {\n Sampler,\n SamplingDecision,\n SamplingResult,\n} from '@opentelemetry/sdk-trace-base'\nimport {\n ATTR_EXCEPTION_MESSAGE,\n ATTR_EXCEPTION_STACKTRACE,\n ATTR_EXCEPTION_TYPE,\n} from '@opentelemetry/semantic-conventions'\nimport {\n MULTIPLAYER_TRACE_DEBUG_PREFIX,\n MULTIPLAYER_TRACE_CONTINUOUS_DEBUG_PREFIX,\n MULTIPLAYER_TRACE_SESSION_CACHE_PREFIX,\n // MULTIPLAYER_TRACE_CONTINUOUS_SESSION_CACHE_PREFIX,\n} from './constants/constants.base'\n\nexport class SessionRecorderTraceIdRatioBasedSampler implements Sampler {\n private _upperBound: number\n\n constructor(private readonly _ratio: number = 0) {\n this._ratio = this._normalize(_ratio)\n this._upperBound = Math.floor(this._ratio * 0xffffffff)\n }\n\n shouldSample(\n context: Context,\n traceId: string,\n spanName: string,\n spanKind: SpanKind,\n attributes: Attributes,\n links: Link[],\n ): SamplingResult {\n if (attributes[ATTR_EXCEPTION_MESSAGE] || attributes[ATTR_EXCEPTION_STACKTRACE] || attributes[ATTR_EXCEPTION_TYPE]) {\n return {\n decision: SamplingDecision.RECORD_AND_SAMPLED,\n }\n }\n\n if (\n traceId.startsWith(MULTIPLAYER_TRACE_DEBUG_PREFIX)\n || traceId.startsWith(MULTIPLAYER_TRACE_CONTINUOUS_DEBUG_PREFIX)\n || traceId.startsWith(MULTIPLAYER_TRACE_SESSION_CACHE_PREFIX)\n // || traceId.startsWith(MULTIPLAYER_TRACE_CONTINUOUS_SESSION_CACHE_PREFIX)\n ) {\n return {\n decision: SamplingDecision.RECORD_AND_SAMPLED,\n }\n }\n\n let decision: SamplingDecision = SamplingDecision.NOT_RECORD\n\n if (\n isValidTraceId(traceId)\n && this._accumulate(traceId) < this._upperBound\n ) {\n decision = SamplingDecision.RECORD_AND_SAMPLED\n }\n\n return { decision }\n }\n\n toString(): string {\n return `SessionRecorderTraceIdRatioBasedSampler{${this._ratio}}`\n }\n\n private _normalize(ratio: number): number {\n if (typeof ratio !== 'number' || isNaN(ratio)) return 0\n return ratio >= 1 ? 1 : ratio <= 0 ? 0 : ratio\n }\n\n private _accumulate(traceId: string): number {\n let accumulation = 0\n for (let i = 0; i < traceId.length / 8; i++) {\n const pos = i * 8\n const part = parseInt(traceId.slice(pos, pos + 8), 16)\n accumulation = (accumulation ^ part) >>> 0\n }\n return accumulation\n }\n}\n"]}
@@ -1,5 +1,4 @@
1
1
  import { ReadableSpan, SpanExporter } from '@opentelemetry/sdk-trace-base';
2
- import { ExportResult } from '@opentelemetry/core';
3
2
  export interface SessionRecorderBrowserTraceExporterConfig {
4
3
  /** URL for the OTLP endpoint. Defaults to Multiplayer's default traces endpoint. */
5
4
  url?: string;
@@ -32,11 +31,16 @@ export declare class SessionRecorderBrowserTraceExporter implements SpanExporter
32
31
  private readonly postMessageTargetOrigin;
33
32
  private readonly config;
34
33
  constructor(config?: SessionRecorderBrowserTraceExporterConfig);
34
+ _export(spans: ReadableSpan[], resultCallback: (result: {
35
+ code: number;
36
+ }) => void): void;
35
37
  export(spans: ReadableSpan[], resultCallback: (result: {
36
38
  code: number;
37
39
  }) => void): void;
40
+ exportBuffer(spans: ReadableSpan[], resultCallback: (result: {
41
+ code: number;
42
+ }) => void): void;
38
43
  shutdown(): Promise<void>;
39
- exportBuffer(spans: ReadableSpan[]): Promise<ExportResult | undefined>;
40
44
  private exportViaPostMessage;
41
45
  serializeSpan(span: ReadableSpan): any;
42
46
  private _createExporter;
@@ -1 +1 @@
1
- {"version":3,"file":"SessionRecorderBrowserTraceExporter.d.ts","sourceRoot":"","sources":["../../../src/exporters/SessionRecorderBrowserTraceExporter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAA;AAC1E,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AASlD,MAAM,WAAW,yCAAyC;IACxD,oFAAoF;IACpF,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,4CAA4C;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,gDAAgD;IAChD,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC,sCAAsC;IACtC,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,4CAA4C;IAC5C,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,4CAA4C;IAC5C,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,oEAAoE;IACpE,sBAAsB,CAAC,EAAE,OAAO,CAAA;IAChC,kCAAkC;IAClC,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,gCAAgC;IAChC,uBAAuB,CAAC,EAAE,MAAM,CAAA;CACjC;AAED;;;;GAIG;AACH,qBAAa,mCAAoC,YAAW,YAAY;IACtE,OAAO,CAAC,QAAQ,CAAmB;IACnC,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAQ;IACxC,OAAO,CAAC,QAAQ,CAAC,uBAAuB,CAAQ;IAChD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA2C;gBAEtD,MAAM,GAAE,yCAA8C;IA2BlE,MAAM,CAAC,KAAK,EAAE,YAAY,EAAE,EAAE,cAAc,EAAE,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,GAAG,IAAI;IAiCvF,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAIzB,YAAY,CAAC,KAAK,EAAE,YAAY,EAAE,GAAG,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC;IAQtE,OAAO,CAAC,oBAAoB;IAqB5B,aAAa,CAAC,IAAI,EAAE,YAAY,GAAG,GAAG;IAQtC,OAAO,CAAC,eAAe;IAcvB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;CAIhC"}
1
+ {"version":3,"file":"SessionRecorderBrowserTraceExporter.d.ts","sourceRoot":"","sources":["../../../src/exporters/SessionRecorderBrowserTraceExporter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAA;AAU1E,MAAM,WAAW,yCAAyC;IACxD,oFAAoF;IACpF,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,4CAA4C;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,gDAAgD;IAChD,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC,sCAAsC;IACtC,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,4CAA4C;IAC5C,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,4CAA4C;IAC5C,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,oEAAoE;IACpE,sBAAsB,CAAC,EAAE,OAAO,CAAA;IAChC,kCAAkC;IAClC,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,gCAAgC;IAChC,uBAAuB,CAAC,EAAE,MAAM,CAAA;CACjC;AAED;;;;GAIG;AACH,qBAAa,mCAAoC,YAAW,YAAY;IACtE,OAAO,CAAC,QAAQ,CAAmB;IACnC,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAQ;IACxC,OAAO,CAAC,QAAQ,CAAC,uBAAuB,CAAQ;IAChD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA2C;gBAEtD,MAAM,GAAE,yCAA8C;IA0BlE,OAAO,CAAC,KAAK,EAAE,YAAY,EAAE,EAAE,cAAc,EAAE,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,GAAG,IAAI;IAwBxF,MAAM,CAAC,KAAK,EAAE,YAAY,EAAE,EAAE,cAAc,EAAE,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,GAAG,IAAI;IAYvF,YAAY,CAAC,KAAK,EAAE,YAAY,EAAE,EAAE,cAAc,EAAE,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,GAAG,IAAI;IAQ7F,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAIzB,OAAO,CAAC,oBAAoB;IAqB5B,aAAa,CAAC,IAAI,EAAE,YAAY,GAAG,GAAG;IAuCtC,OAAO,CAAC,eAAe;IAcvB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;CAIhC"}
@@ -22,44 +22,47 @@ class SessionRecorderBrowserTraceExporter {
22
22
  this.postMessageTargetOrigin = postMessageTargetOrigin;
23
23
  this.exporter = this._createExporter();
24
24
  }
25
- export(spans, resultCallback) {
26
- // Filter spans to only include those with Multiplayer trace prefixes
27
- const filteredSpans = spans.filter((span) => {
28
- const traceId = span.spanContext().traceId;
29
- return (traceId.startsWith(constants_base_1.MULTIPLAYER_TRACE_DEBUG_PREFIX) ||
30
- traceId.startsWith(constants_base_1.MULTIPLAYER_TRACE_CONTINUOUS_DEBUG_PREFIX));
31
- });
25
+ _export(spans, resultCallback) {
32
26
  // Only proceed if there are filtered spans
33
- if (filteredSpans.length === 0) {
27
+ if (spans.length === 0) {
34
28
  resultCallback({ code: 0 });
35
29
  return;
36
30
  }
37
31
  if (this.usePostMessage) {
38
- this.exportViaPostMessage(filteredSpans, resultCallback);
32
+ this.exportViaPostMessage(spans, resultCallback);
39
33
  return;
40
34
  }
41
- this.exporter.export(filteredSpans, (result) => {
35
+ this.exporter.export(spans, (result) => {
42
36
  if (result.code === 0) {
43
37
  resultCallback(result);
44
38
  }
45
39
  else if (this.config.usePostMessageFallback) {
46
40
  this.usePostMessage = true;
47
- this.exportViaPostMessage(filteredSpans, resultCallback);
41
+ this.exportViaPostMessage(spans, resultCallback);
48
42
  }
49
43
  else {
50
44
  resultCallback(result);
51
45
  }
52
46
  });
53
47
  }
54
- shutdown() {
55
- return this.exporter.shutdown();
48
+ export(spans, resultCallback) {
49
+ // Filter spans to only include those with Multiplayer trace prefixes
50
+ const filteredSpans = spans.filter((span) => {
51
+ const traceId = span.spanContext().traceId;
52
+ return (traceId.startsWith(constants_base_1.MULTIPLAYER_TRACE_DEBUG_PREFIX) ||
53
+ traceId.startsWith(constants_base_1.MULTIPLAYER_TRACE_CONTINUOUS_DEBUG_PREFIX));
54
+ });
55
+ this._export(filteredSpans, resultCallback);
56
56
  }
57
- exportBuffer(spans) {
58
- return new Promise((resolve) => {
59
- this.exporter.export(spans, (result) => {
60
- resolve(result);
61
- });
57
+ exportBuffer(spans, resultCallback) {
58
+ const filteredSpans = spans.filter((span) => {
59
+ const traceId = span.spanContext().traceId;
60
+ return traceId.startsWith(constants_base_1.MULTIPLAYER_TRACE_SESSION_CACHE_PREFIX);
62
61
  });
62
+ this._export(filteredSpans, resultCallback);
63
+ }
64
+ shutdown() {
65
+ return this.exporter.shutdown();
63
66
  }
64
67
  exportViaPostMessage(spans, resultCallback) {
65
68
  if (typeof window === 'undefined') {
@@ -79,8 +82,42 @@ class SessionRecorderBrowserTraceExporter {
79
82
  }
80
83
  }
81
84
  serializeSpan(span) {
85
+ var _a;
82
86
  const spanContext = span.spanContext();
83
- return Object.assign(Object.assign({}, span), { _spanContext: spanContext });
87
+ const instrumentationScope =
88
+ // OTel SDK (modern)
89
+ span.instrumentationScope ||
90
+ // Older SDKs
91
+ span.instrumentationLibrary || { name: 'unknown', version: undefined, schemaUrl: undefined };
92
+ const normalizedScope = {
93
+ name: (instrumentationScope === null || instrumentationScope === void 0 ? void 0 : instrumentationScope.name) || 'unknown',
94
+ version: instrumentationScope === null || instrumentationScope === void 0 ? void 0 : instrumentationScope.version,
95
+ schemaUrl: instrumentationScope === null || instrumentationScope === void 0 ? void 0 : instrumentationScope.schemaUrl,
96
+ };
97
+ return {
98
+ _spanContext: spanContext,
99
+ traceId: spanContext.traceId,
100
+ spanId: spanContext.spanId,
101
+ name: span.name,
102
+ kind: span.kind,
103
+ links: span.links,
104
+ ended: span.ended,
105
+ events: span.events,
106
+ status: span.status,
107
+ endTime: span.endTime,
108
+ startTime: span.startTime,
109
+ duration: span.duration,
110
+ attributes: span.attributes,
111
+ parentSpanId: (_a = span.parentSpanContext) === null || _a === void 0 ? void 0 : _a.spanId,
112
+ droppedAttributesCount: span.droppedAttributesCount,
113
+ droppedEventsCount: span.droppedEventsCount,
114
+ droppedLinksCount: span.droppedLinksCount,
115
+ instrumentationScope: normalizedScope,
116
+ resource: {
117
+ attributes: span.resource.attributes,
118
+ asyncAttributesPending: span.resource.asyncAttributesPending,
119
+ },
120
+ };
84
121
  }
85
122
  _createExporter() {
86
123
  return new exporter_trace_otlp_http_1.OTLPTraceExporter({
@@ -1 +1 @@
1
- {"version":3,"file":"SessionRecorderBrowserTraceExporter.js","sourceRoot":"","sources":["../../../src/exporters/SessionRecorderBrowserTraceExporter.ts"],"names":[],"mappings":"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","sourcesContent":["import { ReadableSpan, SpanExporter } from '@opentelemetry/sdk-trace-base'\nimport { ExportResult } from '@opentelemetry/core'\n\nimport { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'\nimport {\n MULTIPLAYER_OTEL_DEFAULT_TRACES_EXPORTER_HTTP_URL,\n MULTIPLAYER_TRACE_DEBUG_PREFIX,\n MULTIPLAYER_TRACE_CONTINUOUS_DEBUG_PREFIX,\n} from '../constants/constants.base'\n\nexport interface SessionRecorderBrowserTraceExporterConfig {\n /** URL for the OTLP endpoint. Defaults to Multiplayer's default traces endpoint. */\n url?: string\n /** API key for authentication. Required. */\n apiKey?: string\n /** Additional headers to include in requests */\n headers?: Record<string, string>\n /** Request timeout in milliseconds */\n timeoutMillis?: number\n /** Whether to use keep-alive connections */\n keepAlive?: boolean\n /** Maximum number of concurrent requests */\n concurrencyLimit?: number\n /** Whether to use postMessage fallback for cross-origin requests */\n usePostMessageFallback?: boolean\n /** PostMessage type identifier */\n postMessageType?: string\n /** PostMessage target origin */\n postMessageTargetOrigin?: string\n}\n\n/**\n * Browser-specific trace exporter for Session Recorder\n * Exports traces via HTTP to Multiplayer's OTLP endpoint with browser-specific optimizations\n * Only exports spans with trace IDs starting with Multiplayer prefixes\n */\nexport class SessionRecorderBrowserTraceExporter implements SpanExporter {\n private exporter: OTLPTraceExporter\n private usePostMessage: boolean = false\n private readonly postMessageType: string\n private readonly postMessageTargetOrigin: string\n private readonly config: SessionRecorderBrowserTraceExporterConfig\n\n constructor(config: SessionRecorderBrowserTraceExporterConfig = {}) {\n const {\n url = MULTIPLAYER_OTEL_DEFAULT_TRACES_EXPORTER_HTTP_URL,\n apiKey,\n headers = {},\n timeoutMillis = 30000,\n keepAlive = true,\n concurrencyLimit = 20,\n postMessageType = 'MULTIPLAYER_SESSION_DEBUGGER_LIB',\n postMessageTargetOrigin = '*',\n } = config\n\n this.config = {\n ...config,\n url,\n apiKey,\n headers,\n keepAlive,\n timeoutMillis,\n concurrencyLimit,\n }\n this.postMessageType = postMessageType\n this.postMessageTargetOrigin = postMessageTargetOrigin\n\n this.exporter = this._createExporter()\n }\n\n export(spans: ReadableSpan[], resultCallback: (result: { code: number }) => void): void {\n // Filter spans to only include those with Multiplayer trace prefixes\n const filteredSpans = spans.filter((span) => {\n const traceId = span.spanContext().traceId\n return (\n traceId.startsWith(MULTIPLAYER_TRACE_DEBUG_PREFIX) ||\n traceId.startsWith(MULTIPLAYER_TRACE_CONTINUOUS_DEBUG_PREFIX)\n )\n })\n\n // Only proceed if there are filtered spans\n if (filteredSpans.length === 0) {\n resultCallback({ code: 0 })\n return\n }\n\n if (this.usePostMessage) {\n this.exportViaPostMessage(filteredSpans, resultCallback)\n return\n }\n\n this.exporter.export(filteredSpans, (result) => {\n if (result.code === 0) {\n resultCallback(result)\n } else if (this.config.usePostMessageFallback) {\n this.usePostMessage = true\n this.exportViaPostMessage(filteredSpans, resultCallback)\n } else {\n resultCallback(result)\n }\n })\n }\n\n shutdown(): Promise<void> {\n return this.exporter.shutdown()\n }\n\n exportBuffer(spans: ReadableSpan[]): Promise<ExportResult | undefined> {\n return new Promise((resolve) => {\n this.exporter.export(spans, (result) => {\n resolve(result)\n })\n })\n }\n\n private exportViaPostMessage(spans: ReadableSpan[], resultCallback: (result: { code: number }) => void): void {\n if (typeof window === 'undefined') {\n resultCallback({ code: 1 })\n return\n }\n\n try {\n window.postMessage(\n {\n action: 'traces',\n type: this.postMessageType,\n payload: spans.map((span) => this.serializeSpan(span)),\n },\n this.postMessageTargetOrigin,\n )\n resultCallback({ code: 0 })\n } catch (e) {\n resultCallback({ code: 1 })\n }\n }\n\n serializeSpan(span: ReadableSpan): any {\n const spanContext = span.spanContext()\n return {\n ...span,\n _spanContext: spanContext,\n }\n }\n\n private _createExporter(): OTLPTraceExporter {\n return new OTLPTraceExporter({\n url: this.config.url,\n headers: {\n 'Content-Type': 'application/json',\n ...(this.config.apiKey ? { Authorization: this.config.apiKey } : {}),\n ...(this.config.headers || {}),\n },\n timeoutMillis: this.config.timeoutMillis,\n keepAlive: this.config.keepAlive,\n concurrencyLimit: this.config.concurrencyLimit,\n })\n }\n\n setApiKey(apiKey: string): void {\n this.config.apiKey = apiKey\n this.exporter = this._createExporter()\n }\n}\n"]}
1
+ {"version":3,"file":"SessionRecorderBrowserTraceExporter.js","sourceRoot":"","sources":["../../../src/exporters/SessionRecorderBrowserTraceExporter.ts"],"names":[],"mappings":"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","sourcesContent":["import { ReadableSpan, SpanExporter } from '@opentelemetry/sdk-trace-base'\n\nimport { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'\nimport {\n MULTIPLAYER_OTEL_DEFAULT_TRACES_EXPORTER_HTTP_URL,\n MULTIPLAYER_TRACE_DEBUG_PREFIX,\n MULTIPLAYER_TRACE_CONTINUOUS_DEBUG_PREFIX,\n MULTIPLAYER_TRACE_SESSION_CACHE_PREFIX,\n} from '../constants/constants.base'\n\nexport interface SessionRecorderBrowserTraceExporterConfig {\n /** URL for the OTLP endpoint. Defaults to Multiplayer's default traces endpoint. */\n url?: string\n /** API key for authentication. Required. */\n apiKey?: string\n /** Additional headers to include in requests */\n headers?: Record<string, string>\n /** Request timeout in milliseconds */\n timeoutMillis?: number\n /** Whether to use keep-alive connections */\n keepAlive?: boolean\n /** Maximum number of concurrent requests */\n concurrencyLimit?: number\n /** Whether to use postMessage fallback for cross-origin requests */\n usePostMessageFallback?: boolean\n /** PostMessage type identifier */\n postMessageType?: string\n /** PostMessage target origin */\n postMessageTargetOrigin?: string\n}\n\n/**\n * Browser-specific trace exporter for Session Recorder\n * Exports traces via HTTP to Multiplayer's OTLP endpoint with browser-specific optimizations\n * Only exports spans with trace IDs starting with Multiplayer prefixes\n */\nexport class SessionRecorderBrowserTraceExporter implements SpanExporter {\n private exporter: OTLPTraceExporter\n private usePostMessage: boolean = false\n private readonly postMessageType: string\n private readonly postMessageTargetOrigin: string\n private readonly config: SessionRecorderBrowserTraceExporterConfig\n\n constructor(config: SessionRecorderBrowserTraceExporterConfig = {}) {\n const {\n url = MULTIPLAYER_OTEL_DEFAULT_TRACES_EXPORTER_HTTP_URL,\n apiKey,\n headers = {},\n timeoutMillis = 30000,\n keepAlive = true,\n concurrencyLimit = 20,\n postMessageType = 'MULTIPLAYER_SESSION_DEBUGGER_LIB',\n postMessageTargetOrigin = '*',\n } = config\n\n this.config = {\n ...config,\n url,\n apiKey,\n headers,\n keepAlive,\n timeoutMillis,\n concurrencyLimit,\n }\n this.postMessageType = postMessageType\n this.postMessageTargetOrigin = postMessageTargetOrigin\n\n this.exporter = this._createExporter()\n }\n _export(spans: ReadableSpan[], resultCallback: (result: { code: number }) => void): void {\n // Only proceed if there are filtered spans\n if (spans.length === 0) {\n resultCallback({ code: 0 })\n return\n }\n\n if (this.usePostMessage) {\n this.exportViaPostMessage(spans, resultCallback)\n return\n }\n\n this.exporter.export(spans, (result) => {\n if (result.code === 0) {\n resultCallback(result)\n } else if (this.config.usePostMessageFallback) {\n this.usePostMessage = true\n this.exportViaPostMessage(spans, resultCallback)\n } else {\n resultCallback(result)\n }\n })\n }\n\n export(spans: ReadableSpan[], resultCallback: (result: { code: number }) => void): void {\n // Filter spans to only include those with Multiplayer trace prefixes\n const filteredSpans = spans.filter((span) => {\n const traceId = span.spanContext().traceId\n return (\n traceId.startsWith(MULTIPLAYER_TRACE_DEBUG_PREFIX) ||\n traceId.startsWith(MULTIPLAYER_TRACE_CONTINUOUS_DEBUG_PREFIX)\n )\n })\n this._export(filteredSpans, resultCallback)\n }\n\n exportBuffer(spans: ReadableSpan[], resultCallback: (result: { code: number }) => void): void {\n const filteredSpans = spans.filter((span) => {\n const traceId = span.spanContext().traceId\n return traceId.startsWith(MULTIPLAYER_TRACE_SESSION_CACHE_PREFIX)\n })\n this._export(filteredSpans, resultCallback)\n }\n\n shutdown(): Promise<void> {\n return this.exporter.shutdown()\n }\n\n private exportViaPostMessage(spans: ReadableSpan[], resultCallback: (result: { code: number }) => void): void {\n if (typeof window === 'undefined') {\n resultCallback({ code: 1 })\n return\n }\n\n try {\n window.postMessage(\n {\n action: 'traces',\n type: this.postMessageType,\n payload: spans.map((span) => this.serializeSpan(span)),\n },\n this.postMessageTargetOrigin,\n )\n resultCallback({ code: 0 })\n } catch (e) {\n resultCallback({ code: 1 })\n }\n }\n\n serializeSpan(span: ReadableSpan): any {\n const spanContext = span.spanContext()\n const instrumentationScope: any =\n // OTel SDK (modern)\n (span as any).instrumentationScope ||\n // Older SDKs\n (span as any).instrumentationLibrary || { name: 'unknown', version: undefined, schemaUrl: undefined }\n\n const normalizedScope = {\n name: instrumentationScope?.name || 'unknown',\n version: instrumentationScope?.version,\n schemaUrl: instrumentationScope?.schemaUrl,\n }\n return {\n _spanContext: spanContext,\n traceId: spanContext.traceId,\n spanId: spanContext.spanId,\n name: span.name,\n kind: span.kind,\n links: span.links,\n ended: span.ended,\n events: span.events,\n status: span.status,\n endTime: span.endTime,\n startTime: span.startTime,\n duration: span.duration,\n attributes: span.attributes,\n parentSpanId: span.parentSpanContext?.spanId,\n droppedAttributesCount: span.droppedAttributesCount,\n droppedEventsCount: span.droppedEventsCount,\n droppedLinksCount: span.droppedLinksCount,\n instrumentationScope: normalizedScope,\n resource: {\n attributes: span.resource.attributes,\n asyncAttributesPending: span.resource.asyncAttributesPending,\n },\n }\n }\n\n private _createExporter(): OTLPTraceExporter {\n return new OTLPTraceExporter({\n url: this.config.url,\n headers: {\n 'Content-Type': 'application/json',\n ...(this.config.apiKey ? { Authorization: this.config.apiKey } : {}),\n ...(this.config.headers || {}),\n },\n timeoutMillis: this.config.timeoutMillis,\n keepAlive: this.config.keepAlive,\n concurrencyLimit: this.config.concurrencyLimit,\n })\n }\n\n setApiKey(apiKey: string): void {\n this.config.apiKey = apiKey\n this.exporter = this._createExporter()\n }\n}\n"]}
@@ -3,5 +3,5 @@
3
3
  * @param {Error} error
4
4
  * @returns {void}
5
5
  */
6
- export declare const captureException: (error: Error) => void;
6
+ export declare const captureException: (error: Error, errorInfo?: Record<string, any>) => void;
7
7
  //# sourceMappingURL=capture-exception.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"capture-exception.d.ts","sourceRoot":"","sources":["../../../src/sdk/capture-exception.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,GAAI,OAAO,KAAK,SAW5C,CAAA"}
1
+ {"version":3,"file":"capture-exception.d.ts","sourceRoot":"","sources":["../../../src/sdk/capture-exception.ts"],"names":[],"mappings":"AAQA;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,GAC3B,OAAO,KAAK,EACZ,YAAY,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,SAgDhC,CAAA"}
@@ -2,22 +2,47 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.captureException = void 0;
4
4
  const api_1 = require("@opentelemetry/api");
5
+ const semantic_conventions_1 = require("@opentelemetry/semantic-conventions");
6
+ const set_resource_attributes_1 = require("./set-resource-attributes");
5
7
  /**
6
8
  * @description Add error to current span
7
9
  * @param {Error} error
8
10
  * @returns {void}
9
11
  */
10
- const captureException = (error) => {
11
- if (!error)
12
- return;
13
- const span = api_1.trace.getSpan(api_1.context.active());
14
- if (!span)
12
+ const captureException = (error, errorInfo) => {
13
+ if (!error) {
15
14
  return;
15
+ }
16
+ const activeContext = api_1.context.active();
17
+ let span = api_1.trace.getSpan(activeContext);
18
+ let isNewSpan = false;
19
+ if (!span || !span.isRecording()) {
20
+ span = api_1.trace.getTracer('exception').startSpan(error.name || 'Error', {
21
+ attributes: Object.assign({ [semantic_conventions_1.ATTR_EXCEPTION_MESSAGE]: error.message, [semantic_conventions_1.ATTR_EXCEPTION_STACKTRACE]: error.stack, [semantic_conventions_1.ATTR_EXCEPTION_TYPE]: error.name }, (0, set_resource_attributes_1.getResourceAttributes)()),
22
+ });
23
+ api_1.trace.setSpan(activeContext, span);
24
+ isNewSpan = true;
25
+ }
26
+ else {
27
+ span.setAttributes({
28
+ [semantic_conventions_1.ATTR_EXCEPTION_MESSAGE]: error.message,
29
+ [semantic_conventions_1.ATTR_EXCEPTION_STACKTRACE]: error.stack,
30
+ [semantic_conventions_1.ATTR_EXCEPTION_TYPE]: error.name,
31
+ });
32
+ }
33
+ if (errorInfo) {
34
+ Object.entries(errorInfo).forEach(([key, value]) => {
35
+ span.setAttribute(`error_info.${key}`, value);
36
+ });
37
+ }
16
38
  span.recordException(error);
17
39
  span.setStatus({
18
40
  code: api_1.SpanStatusCode.ERROR,
19
41
  message: error.message,
20
42
  });
43
+ if (isNewSpan) {
44
+ span.end();
45
+ }
21
46
  };
22
47
  exports.captureException = captureException;
23
48
  //# sourceMappingURL=capture-exception.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"capture-exception.js","sourceRoot":"","sources":["../../../src/sdk/capture-exception.ts"],"names":[],"mappings":";;;AAAA,4CAAmE;AAEnE;;;;GAIG;AACI,MAAM,gBAAgB,GAAG,CAAC,KAAY,EAAE,EAAE;IAC/C,IAAI,CAAC,KAAK;QAAE,OAAM;IAElB,MAAM,IAAI,GAAG,WAAK,CAAC,OAAO,CAAC,aAAO,CAAC,MAAM,EAAE,CAAC,CAAA;IAC5C,IAAI,CAAC,IAAI;QAAE,OAAM;IAEjB,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,CAAA;IAC3B,IAAI,CAAC,SAAS,CAAC;QACb,IAAI,EAAE,oBAAc,CAAC,KAAK;QAC1B,OAAO,EAAE,KAAK,CAAC,OAAO;KACvB,CAAC,CAAA;AACJ,CAAC,CAAA;AAXY,QAAA,gBAAgB,oBAW5B","sourcesContent":["import { context, trace, SpanStatusCode } from '@opentelemetry/api'\n\n/**\n * @description Add error to current span\n * @param {Error} error\n * @returns {void}\n */\nexport const captureException = (error: Error) => {\n if (!error) return\n\n const span = trace.getSpan(context.active())\n if (!span) return\n\n span.recordException(error)\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: error.message,\n })\n}\n"]}
1
+ {"version":3,"file":"capture-exception.js","sourceRoot":"","sources":["../../../src/sdk/capture-exception.ts"],"names":[],"mappings":";;;AAAA,4CAAmE;AACnE,8EAI4C;AAC5C,uEAAiE;AAEjE;;;;GAIG;AACI,MAAM,gBAAgB,GAAG,CAC9B,KAAY,EACZ,SAA+B,EAC/B,EAAE;IACF,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAM;IACR,CAAC;IAED,MAAM,aAAa,GAAG,aAAO,CAAC,MAAM,EAAE,CAAA;IAEtC,IAAI,IAAI,GAAG,WAAK,CAAC,OAAO,CAAC,aAAa,CAAC,CAAA;IACvC,IAAI,SAAS,GAAG,KAAK,CAAA;IAErB,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;QACjC,IAAI,GAAG,WAAK,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,SAAS,CAC3C,KAAK,CAAC,IAAI,IAAI,OAAO,EACrB;YACE,UAAU,kBACR,CAAC,6CAAsB,CAAC,EAAE,KAAK,CAAC,OAAO,EACvC,CAAC,gDAAyB,CAAC,EAAE,KAAK,CAAC,KAAK,EACxC,CAAC,0CAAmB,CAAC,EAAE,KAAK,CAAC,IAAI,IAC9B,IAAA,+CAAqB,GAAE,CAC3B;SACF,CACF,CAAA;QACD,WAAK,CAAC,OAAO,CAAC,aAAa,EAAE,IAAI,CAAC,CAAA;QAClC,SAAS,GAAG,IAAI,CAAA;IAClB,CAAC;SAAM,CAAC;QACN,IAAI,CAAC,aAAa,CAAC;YACjB,CAAC,6CAAsB,CAAC,EAAE,KAAK,CAAC,OAAO;YACvC,CAAC,gDAAyB,CAAC,EAAE,KAAK,CAAC,KAAK;YACxC,CAAC,0CAAmB,CAAC,EAAE,KAAK,CAAC,IAAI;SAClC,CAAC,CAAA;IACJ,CAAC;IAED,IAAI,SAAS,EAAE,CAAC;QACd,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE;YACjD,IAAI,CAAC,YAAY,CAAC,cAAc,GAAG,EAAE,EAAE,KAAK,CAAC,CAAA;QAC/C,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,CAAA;IAC3B,IAAI,CAAC,SAAS,CAAC;QACb,IAAI,EAAE,oBAAc,CAAC,KAAK;QAC1B,OAAO,EAAE,KAAK,CAAC,OAAO;KACvB,CAAC,CAAA;IAEF,IAAI,SAAS,EAAE,CAAC;QACd,IAAI,CAAC,GAAG,EAAE,CAAA;IACZ,CAAC;AACH,CAAC,CAAA;AAlDY,QAAA,gBAAgB,oBAkD5B","sourcesContent":["import { context, trace, SpanStatusCode } from '@opentelemetry/api'\nimport {\n ATTR_EXCEPTION_MESSAGE,\n ATTR_EXCEPTION_STACKTRACE,\n ATTR_EXCEPTION_TYPE,\n} from '@opentelemetry/semantic-conventions'\nimport { getResourceAttributes } from './set-resource-attributes'\n\n/**\n * @description Add error to current span\n * @param {Error} error\n * @returns {void}\n */\nexport const captureException = (\n error: Error,\n errorInfo?: Record<string, any>,\n) => {\n if (!error) {\n return\n }\n\n const activeContext = context.active()\n\n let span = trace.getSpan(activeContext)\n let isNewSpan = false\n\n if (!span || !span.isRecording()) {\n span = trace.getTracer('exception').startSpan(\n error.name || 'Error',\n {\n attributes: {\n [ATTR_EXCEPTION_MESSAGE]: error.message,\n [ATTR_EXCEPTION_STACKTRACE]: error.stack,\n [ATTR_EXCEPTION_TYPE]: error.name,\n ...getResourceAttributes(),\n },\n },\n )\n trace.setSpan(activeContext, span)\n isNewSpan = true\n } else {\n span.setAttributes({\n [ATTR_EXCEPTION_MESSAGE]: error.message,\n [ATTR_EXCEPTION_STACKTRACE]: error.stack,\n [ATTR_EXCEPTION_TYPE]: error.name,\n })\n }\n\n if (errorInfo) {\n Object.entries(errorInfo).forEach(([key, value]) => {\n span.setAttribute(`error_info.${key}`, value)\n })\n }\n\n span.recordException(error)\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: error.message,\n })\n\n if (isNewSpan) {\n span.end()\n }\n}\n"]}
@@ -5,4 +5,5 @@ export * from './id-generator';
5
5
  export * from './capture-exception';
6
6
  export * from './set-attribute';
7
7
  export * from './save-continuous-deb-session';
8
+ export * from './set-resource-attributes';
8
9
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/sdk/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,QAAQ,CAAA;AAC3E,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,YAAY,CAAA;AAChD,cAAc,WAAW,CAAA;AACzB,cAAc,gBAAgB,CAAA;AAC9B,cAAc,qBAAqB,CAAA;AACnC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,+BAA+B,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/sdk/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,QAAQ,CAAA;AAC3E,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,YAAY,CAAA;AAChD,cAAc,WAAW,CAAA;AACzB,cAAc,gBAAgB,CAAA;AAC9B,cAAc,qBAAqB,CAAA;AACnC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,+BAA+B,CAAA;AAC7C,cAAc,2BAA2B,CAAA"}
@@ -26,4 +26,5 @@ __exportStar(require("./id-generator"), exports);
26
26
  __exportStar(require("./capture-exception"), exports);
27
27
  __exportStar(require("./set-attribute"), exports);
28
28
  __exportStar(require("./save-continuous-deb-session"), exports);
29
+ __exportStar(require("./set-resource-attributes"), exports);
29
30
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/sdk/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,+BAA2E;AAAlE,4FAAA,OAAO,OAAQ;AAAE,uGAAA,eAAe,OAAA;AAAE,wGAAA,gBAAgB,OAAA;AAC3D,uCAAgD;AAAvC,oGAAA,OAAO,OAAY;AAC5B,4CAAyB;AACzB,iDAA8B;AAC9B,sDAAmC;AACnC,kDAA+B;AAC/B,gEAA6C","sourcesContent":["export { default as mask, sensitiveFields, sensitiveHeaders } from './mask'\nexport { default as schemify } from './schemify'\nexport * from './is-gzip'\nexport * from './id-generator'\nexport * from './capture-exception'\nexport * from './set-attribute'\nexport * from './save-continuous-deb-session'\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/sdk/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,+BAA2E;AAAlE,4FAAA,OAAO,OAAQ;AAAE,uGAAA,eAAe,OAAA;AAAE,wGAAA,gBAAgB,OAAA;AAC3D,uCAAgD;AAAvC,oGAAA,OAAO,OAAY;AAC5B,4CAAyB;AACzB,iDAA8B;AAC9B,sDAAmC;AACnC,kDAA+B;AAC/B,gEAA6C;AAC7C,4DAAyC","sourcesContent":["export { default as mask, sensitiveFields, sensitiveHeaders } from './mask'\nexport { default as schemify } from './schemify'\nexport * from './is-gzip'\nexport * from './id-generator'\nexport * from './capture-exception'\nexport * from './set-attribute'\nexport * from './save-continuous-deb-session'\nexport * from './set-resource-attributes'\n"]}
@@ -0,0 +1,3 @@
1
+ export declare const setResourceAttributes: (attributes: Record<string, any>) => void;
2
+ export declare const getResourceAttributes: () => Record<string, any>;
3
+ //# sourceMappingURL=set-resource-attributes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"set-resource-attributes.d.ts","sourceRoot":"","sources":["../../../src/sdk/set-resource-attributes.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,qBAAqB,GAAI,YAAY,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,SAEpE,CAAA;AAED,eAAO,MAAM,qBAAqB,2BAEjC,CAAA"}
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getResourceAttributes = exports.setResourceAttributes = void 0;
4
+ let resourceAttributes = {};
5
+ const setResourceAttributes = (attributes) => {
6
+ resourceAttributes = attributes;
7
+ };
8
+ exports.setResourceAttributes = setResourceAttributes;
9
+ const getResourceAttributes = () => {
10
+ return resourceAttributes;
11
+ };
12
+ exports.getResourceAttributes = getResourceAttributes;
13
+ //# sourceMappingURL=set-resource-attributes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"set-resource-attributes.js","sourceRoot":"","sources":["../../../src/sdk/set-resource-attributes.ts"],"names":[],"mappings":";;;AAAA,IAAI,kBAAkB,GAAwB,EAAE,CAAA;AAEzC,MAAM,qBAAqB,GAAG,CAAC,UAA+B,EAAE,EAAE;IACvE,kBAAkB,GAAG,UAAU,CAAA;AACjC,CAAC,CAAA;AAFY,QAAA,qBAAqB,yBAEjC;AAEM,MAAM,qBAAqB,GAAG,GAAG,EAAE;IACxC,OAAO,kBAAkB,CAAA;AAC3B,CAAC,CAAA;AAFY,QAAA,qBAAqB,yBAEjC","sourcesContent":["let resourceAttributes: Record<string, any> = {}\n\nexport const setResourceAttributes = (attributes: Record<string, any>) => {\n resourceAttributes = attributes\n}\n\nexport const getResourceAttributes = () => {\n return resourceAttributes\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@multiplayer-app/session-recorder-common",
3
- "version": "1.3.27",
3
+ "version": "1.3.28",
4
4
  "description": "Multiplayer Fullstack Session Recorder - opentelemetry",
5
5
  "author": {
6
6
  "name": "Multiplayer Software, Inc.",
@@ -1,13 +1,25 @@
1
- import { isValidTraceId } from '@opentelemetry/api'
1
+
2
+ import {
3
+ isValidTraceId,
4
+ Context,
5
+ SpanKind,
6
+ Attributes,
7
+ Link,
8
+ } from '@opentelemetry/api'
2
9
  import {
3
10
  Sampler,
4
11
  SamplingDecision,
5
12
  SamplingResult,
6
13
  } from '@opentelemetry/sdk-trace-base'
14
+ import {
15
+ ATTR_EXCEPTION_MESSAGE,
16
+ ATTR_EXCEPTION_STACKTRACE,
17
+ ATTR_EXCEPTION_TYPE,
18
+ } from '@opentelemetry/semantic-conventions'
7
19
  import {
8
20
  MULTIPLAYER_TRACE_DEBUG_PREFIX,
9
21
  MULTIPLAYER_TRACE_CONTINUOUS_DEBUG_PREFIX,
10
- // MULTIPLAYER_TRACE_SESSION_CACHE_PREFIX,
22
+ MULTIPLAYER_TRACE_SESSION_CACHE_PREFIX,
11
23
  // MULTIPLAYER_TRACE_CONTINUOUS_SESSION_CACHE_PREFIX,
12
24
  } from './constants/constants.base'
13
25
 
@@ -19,11 +31,24 @@ export class SessionRecorderTraceIdRatioBasedSampler implements Sampler {
19
31
  this._upperBound = Math.floor(this._ratio * 0xffffffff)
20
32
  }
21
33
 
22
- shouldSample(context: unknown, traceId: string): SamplingResult {
34
+ shouldSample(
35
+ context: Context,
36
+ traceId: string,
37
+ spanName: string,
38
+ spanKind: SpanKind,
39
+ attributes: Attributes,
40
+ links: Link[],
41
+ ): SamplingResult {
42
+ if (attributes[ATTR_EXCEPTION_MESSAGE] || attributes[ATTR_EXCEPTION_STACKTRACE] || attributes[ATTR_EXCEPTION_TYPE]) {
43
+ return {
44
+ decision: SamplingDecision.RECORD_AND_SAMPLED,
45
+ }
46
+ }
47
+
23
48
  if (
24
49
  traceId.startsWith(MULTIPLAYER_TRACE_DEBUG_PREFIX)
25
50
  || traceId.startsWith(MULTIPLAYER_TRACE_CONTINUOUS_DEBUG_PREFIX)
26
- // || traceId.startsWith(MULTIPLAYER_TRACE_SESSION_CACHE_PREFIX)
51
+ || traceId.startsWith(MULTIPLAYER_TRACE_SESSION_CACHE_PREFIX)
27
52
  // || traceId.startsWith(MULTIPLAYER_TRACE_CONTINUOUS_SESSION_CACHE_PREFIX)
28
53
  ) {
29
54
  return {
@@ -1,11 +1,11 @@
1
1
  import { ReadableSpan, SpanExporter } from '@opentelemetry/sdk-trace-base'
2
- import { ExportResult } from '@opentelemetry/core'
3
2
 
4
3
  import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'
5
4
  import {
6
5
  MULTIPLAYER_OTEL_DEFAULT_TRACES_EXPORTER_HTTP_URL,
7
6
  MULTIPLAYER_TRACE_DEBUG_PREFIX,
8
7
  MULTIPLAYER_TRACE_CONTINUOUS_DEBUG_PREFIX,
8
+ MULTIPLAYER_TRACE_SESSION_CACHE_PREFIX,
9
9
  } from '../constants/constants.base'
10
10
 
11
11
  export interface SessionRecorderBrowserTraceExporterConfig {
@@ -67,50 +67,52 @@ export class SessionRecorderBrowserTraceExporter implements SpanExporter {
67
67
 
68
68
  this.exporter = this._createExporter()
69
69
  }
70
-
71
- export(spans: ReadableSpan[], resultCallback: (result: { code: number }) => void): void {
72
- // Filter spans to only include those with Multiplayer trace prefixes
73
- const filteredSpans = spans.filter((span) => {
74
- const traceId = span.spanContext().traceId
75
- return (
76
- traceId.startsWith(MULTIPLAYER_TRACE_DEBUG_PREFIX) ||
77
- traceId.startsWith(MULTIPLAYER_TRACE_CONTINUOUS_DEBUG_PREFIX)
78
- )
79
- })
80
-
70
+ _export(spans: ReadableSpan[], resultCallback: (result: { code: number }) => void): void {
81
71
  // Only proceed if there are filtered spans
82
- if (filteredSpans.length === 0) {
72
+ if (spans.length === 0) {
83
73
  resultCallback({ code: 0 })
84
74
  return
85
75
  }
86
76
 
87
77
  if (this.usePostMessage) {
88
- this.exportViaPostMessage(filteredSpans, resultCallback)
78
+ this.exportViaPostMessage(spans, resultCallback)
89
79
  return
90
80
  }
91
81
 
92
- this.exporter.export(filteredSpans, (result) => {
82
+ this.exporter.export(spans, (result) => {
93
83
  if (result.code === 0) {
94
84
  resultCallback(result)
95
85
  } else if (this.config.usePostMessageFallback) {
96
86
  this.usePostMessage = true
97
- this.exportViaPostMessage(filteredSpans, resultCallback)
87
+ this.exportViaPostMessage(spans, resultCallback)
98
88
  } else {
99
89
  resultCallback(result)
100
90
  }
101
91
  })
102
92
  }
103
93
 
104
- shutdown(): Promise<void> {
105
- return this.exporter.shutdown()
94
+ export(spans: ReadableSpan[], resultCallback: (result: { code: number }) => void): void {
95
+ // Filter spans to only include those with Multiplayer trace prefixes
96
+ const filteredSpans = spans.filter((span) => {
97
+ const traceId = span.spanContext().traceId
98
+ return (
99
+ traceId.startsWith(MULTIPLAYER_TRACE_DEBUG_PREFIX) ||
100
+ traceId.startsWith(MULTIPLAYER_TRACE_CONTINUOUS_DEBUG_PREFIX)
101
+ )
102
+ })
103
+ this._export(filteredSpans, resultCallback)
106
104
  }
107
105
 
108
- exportBuffer(spans: ReadableSpan[]): Promise<ExportResult | undefined> {
109
- return new Promise((resolve) => {
110
- this.exporter.export(spans, (result) => {
111
- resolve(result)
112
- })
106
+ exportBuffer(spans: ReadableSpan[], resultCallback: (result: { code: number }) => void): void {
107
+ const filteredSpans = spans.filter((span) => {
108
+ const traceId = span.spanContext().traceId
109
+ return traceId.startsWith(MULTIPLAYER_TRACE_SESSION_CACHE_PREFIX)
113
110
  })
111
+ this._export(filteredSpans, resultCallback)
112
+ }
113
+
114
+ shutdown(): Promise<void> {
115
+ return this.exporter.shutdown()
114
116
  }
115
117
 
116
118
  private exportViaPostMessage(spans: ReadableSpan[], resultCallback: (result: { code: number }) => void): void {
@@ -136,9 +138,40 @@ export class SessionRecorderBrowserTraceExporter implements SpanExporter {
136
138
 
137
139
  serializeSpan(span: ReadableSpan): any {
138
140
  const spanContext = span.spanContext()
141
+ const instrumentationScope: any =
142
+ // OTel SDK (modern)
143
+ (span as any).instrumentationScope ||
144
+ // Older SDKs
145
+ (span as any).instrumentationLibrary || { name: 'unknown', version: undefined, schemaUrl: undefined }
146
+
147
+ const normalizedScope = {
148
+ name: instrumentationScope?.name || 'unknown',
149
+ version: instrumentationScope?.version,
150
+ schemaUrl: instrumentationScope?.schemaUrl,
151
+ }
139
152
  return {
140
- ...span,
141
153
  _spanContext: spanContext,
154
+ traceId: spanContext.traceId,
155
+ spanId: spanContext.spanId,
156
+ name: span.name,
157
+ kind: span.kind,
158
+ links: span.links,
159
+ ended: span.ended,
160
+ events: span.events,
161
+ status: span.status,
162
+ endTime: span.endTime,
163
+ startTime: span.startTime,
164
+ duration: span.duration,
165
+ attributes: span.attributes,
166
+ parentSpanId: span.parentSpanContext?.spanId,
167
+ droppedAttributesCount: span.droppedAttributesCount,
168
+ droppedEventsCount: span.droppedEventsCount,
169
+ droppedLinksCount: span.droppedLinksCount,
170
+ instrumentationScope: normalizedScope,
171
+ resource: {
172
+ attributes: span.resource.attributes,
173
+ asyncAttributesPending: span.resource.asyncAttributesPending,
174
+ },
142
175
  }
143
176
  }
144
177
 
@@ -1,19 +1,64 @@
1
1
  import { context, trace, SpanStatusCode } from '@opentelemetry/api'
2
+ import {
3
+ ATTR_EXCEPTION_MESSAGE,
4
+ ATTR_EXCEPTION_STACKTRACE,
5
+ ATTR_EXCEPTION_TYPE,
6
+ } from '@opentelemetry/semantic-conventions'
7
+ import { getResourceAttributes } from './set-resource-attributes'
2
8
 
3
9
  /**
4
10
  * @description Add error to current span
5
11
  * @param {Error} error
6
12
  * @returns {void}
7
13
  */
8
- export const captureException = (error: Error) => {
9
- if (!error) return
14
+ export const captureException = (
15
+ error: Error,
16
+ errorInfo?: Record<string, any>,
17
+ ) => {
18
+ if (!error) {
19
+ return
20
+ }
10
21
 
11
- const span = trace.getSpan(context.active())
12
- if (!span) return
22
+ const activeContext = context.active()
23
+
24
+ let span = trace.getSpan(activeContext)
25
+ let isNewSpan = false
26
+
27
+ if (!span || !span.isRecording()) {
28
+ span = trace.getTracer('exception').startSpan(
29
+ error.name || 'Error',
30
+ {
31
+ attributes: {
32
+ [ATTR_EXCEPTION_MESSAGE]: error.message,
33
+ [ATTR_EXCEPTION_STACKTRACE]: error.stack,
34
+ [ATTR_EXCEPTION_TYPE]: error.name,
35
+ ...getResourceAttributes(),
36
+ },
37
+ },
38
+ )
39
+ trace.setSpan(activeContext, span)
40
+ isNewSpan = true
41
+ } else {
42
+ span.setAttributes({
43
+ [ATTR_EXCEPTION_MESSAGE]: error.message,
44
+ [ATTR_EXCEPTION_STACKTRACE]: error.stack,
45
+ [ATTR_EXCEPTION_TYPE]: error.name,
46
+ })
47
+ }
48
+
49
+ if (errorInfo) {
50
+ Object.entries(errorInfo).forEach(([key, value]) => {
51
+ span.setAttribute(`error_info.${key}`, value)
52
+ })
53
+ }
13
54
 
14
55
  span.recordException(error)
15
56
  span.setStatus({
16
57
  code: SpanStatusCode.ERROR,
17
58
  message: error.message,
18
59
  })
60
+
61
+ if (isNewSpan) {
62
+ span.end()
63
+ }
19
64
  }