@multiplayer-app/session-recorder-common 1.0.1-alpha.1 → 1.0.1-alpha.2
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/dist/esm/exporters/SessionRecorderBrowserTraceExporter.d.ts +15 -3
- package/dist/esm/exporters/SessionRecorderBrowserTraceExporter.d.ts.map +1 -1
- package/dist/esm/exporters/SessionRecorderBrowserTraceExporter.js +26 -126
- package/dist/esm/exporters/SessionRecorderBrowserTraceExporter.js.map +1 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/esnext/exporters/SessionRecorderBrowserTraceExporter.d.ts +15 -3
- package/dist/esnext/exporters/SessionRecorderBrowserTraceExporter.d.ts.map +1 -1
- package/dist/esnext/exporters/SessionRecorderBrowserTraceExporter.js +27 -76
- package/dist/esnext/exporters/SessionRecorderBrowserTraceExporter.js.map +1 -1
- package/dist/esnext/tsconfig.esnext.tsbuildinfo +1 -1
- package/dist/src/exporters/SessionRecorderBrowserTraceExporter.d.ts +15 -3
- package/dist/src/exporters/SessionRecorderBrowserTraceExporter.d.ts.map +1 -1
- package/dist/src/exporters/SessionRecorderBrowserTraceExporter.js +27 -76
- package/dist/src/exporters/SessionRecorderBrowserTraceExporter.js.map +1 -1
- package/package.json +3 -3
- package/src/exporters/SessionRecorderBrowserTraceExporter.ts +55 -94
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
import { ReadableSpan, SpanExporter } from '@opentelemetry/sdk-trace-base';
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
export interface SessionRecorderBrowserTraceExporterConfig {
|
|
3
|
+
/** URL for the OTLP endpoint. Defaults to Multiplayer's default traces endpoint. */
|
|
4
|
+
url?: string;
|
|
4
5
|
/** API key for authentication. Required. */
|
|
5
6
|
apiKey: string;
|
|
7
|
+
/** Additional headers to include in requests */
|
|
8
|
+
headers?: Record<string, string>;
|
|
9
|
+
/** Request timeout in milliseconds */
|
|
10
|
+
timeoutMillis?: number;
|
|
11
|
+
/** Whether to use keep-alive connections */
|
|
12
|
+
keepAlive?: boolean;
|
|
13
|
+
/** Maximum number of concurrent requests */
|
|
14
|
+
concurrencyLimit?: number;
|
|
6
15
|
/** Whether to use postMessage fallback for cross-origin requests */
|
|
7
16
|
usePostMessageFallback?: boolean;
|
|
8
17
|
/** PostMessage type identifier */
|
|
@@ -15,7 +24,8 @@ export interface SessionRecorderBrowserTraceExporterConfig extends OTLPExporterC
|
|
|
15
24
|
* Exports traces via HTTP to Multiplayer's OTLP endpoint with browser-specific optimizations
|
|
16
25
|
* Only exports spans with trace IDs starting with Multiplayer prefixes
|
|
17
26
|
*/
|
|
18
|
-
export declare class SessionRecorderBrowserTraceExporter
|
|
27
|
+
export declare class SessionRecorderBrowserTraceExporter implements SpanExporter {
|
|
28
|
+
private exporter;
|
|
19
29
|
private usePostMessage;
|
|
20
30
|
private readonly postMessageType;
|
|
21
31
|
private readonly postMessageTargetOrigin;
|
|
@@ -27,5 +37,7 @@ export declare class SessionRecorderBrowserTraceExporter extends OTLPExporterBas
|
|
|
27
37
|
shutdown(): Promise<void>;
|
|
28
38
|
private exportViaPostMessage;
|
|
29
39
|
private _serializeSpan;
|
|
40
|
+
private _createExporter;
|
|
41
|
+
setApiKey(apiKey: string): void;
|
|
30
42
|
}
|
|
31
43
|
//# sourceMappingURL=SessionRecorderBrowserTraceExporter.d.ts.map
|
|
@@ -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;
|
|
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;AAQ1E,MAAM,WAAW,yCAAyC;IACxD,oFAAoF;IACpF,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,4CAA4C;IAC5C,MAAM,EAAE,MAAM,CAAA;IACd,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,EAAE,yCAAyC;IA2B7D,MAAM,CACJ,KAAK,EAAE,YAAY,EAAE,EACrB,cAAc,EAAE,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,GACjD,IAAI;IA+BP,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAIzB,OAAO,CAAC,oBAAoB;IAqB5B,OAAO,CAAC,cAAc;IAyBtB,OAAO,CAAC,eAAe;IAevB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;CAKhC"}
|
|
@@ -1,89 +1,26 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __rest = (this && this.__rest) || function (s, e) {
|
|
3
|
-
var t = {};
|
|
4
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
5
|
-
t[p] = s[p];
|
|
6
|
-
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
7
|
-
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
8
|
-
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
9
|
-
t[p[i]] = s[p[i]];
|
|
10
|
-
}
|
|
11
|
-
return t;
|
|
12
|
-
};
|
|
13
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
3
|
exports.SessionRecorderBrowserTraceExporter = void 0;
|
|
15
|
-
const
|
|
16
|
-
const browser_http_1 = require("@opentelemetry/otlp-exporter-base/browser-http");
|
|
4
|
+
const exporter_trace_otlp_http_1 = require("@opentelemetry/exporter-trace-otlp-http");
|
|
17
5
|
const constants_base_1 = require("../constants/constants.base");
|
|
18
6
|
/**
|
|
19
7
|
* Browser-specific trace exporter for Session Recorder
|
|
20
8
|
* Exports traces via HTTP to Multiplayer's OTLP endpoint with browser-specific optimizations
|
|
21
9
|
* Only exports spans with trace IDs starting with Multiplayer prefixes
|
|
22
10
|
*/
|
|
23
|
-
class SessionRecorderBrowserTraceExporter
|
|
11
|
+
class SessionRecorderBrowserTraceExporter {
|
|
24
12
|
constructor(config) {
|
|
25
|
-
const { url = constants_base_1.MULTIPLAYER_OTEL_DEFAULT_TRACES_EXPORTER_HTTP_URL, apiKey, headers = {}, postMessageType = 'MULTIPLAYER_SESSION_DEBUGGER_LIB', postMessageTargetOrigin = '*' } = config, restConfig = __rest(config, ["url", "apiKey", "headers", "postMessageType", "postMessageTargetOrigin"]);
|
|
26
|
-
const _config = Object.assign(Object.assign({}, restConfig), { url, headers: Object.assign({ 'Content-Type': 'application/x-protobuf', 'User-Agent': '@multiplayer-app/session-recorder-common/1.0.0', 'Authorization': apiKey }, headers) });
|
|
27
|
-
super((0, browser_http_1.createLegacyOtlpBrowserExportDelegate)(_config, {
|
|
28
|
-
serializeRequest: (spans) => {
|
|
29
|
-
// Create a simple trace service request structure
|
|
30
|
-
const request = {
|
|
31
|
-
resourceSpans: spans.map(span => ({
|
|
32
|
-
resource: {
|
|
33
|
-
attributes: Object.entries(span.resource.attributes).map(([key, value]) => ({
|
|
34
|
-
key,
|
|
35
|
-
value: { stringValue: String(value) },
|
|
36
|
-
})),
|
|
37
|
-
},
|
|
38
|
-
scopeSpans: [{
|
|
39
|
-
spans: [{
|
|
40
|
-
traceId: span.spanContext().traceId,
|
|
41
|
-
spanId: span.spanContext().spanId,
|
|
42
|
-
parentSpanId: span.spanContext().spanId, // Using spanId as fallback
|
|
43
|
-
name: span.name,
|
|
44
|
-
kind: span.kind,
|
|
45
|
-
startTimeUnixNano: span.startTime[0] * 1e9 + span.startTime[1],
|
|
46
|
-
endTimeUnixNano: span.endTime ? span.endTime[0] * 1e9 + span.endTime[1] : undefined,
|
|
47
|
-
attributes: Object.entries(span.attributes).map(([key, value]) => ({
|
|
48
|
-
key,
|
|
49
|
-
value: { stringValue: String(value) },
|
|
50
|
-
})),
|
|
51
|
-
events: span.events.map(event => ({
|
|
52
|
-
timeUnixNano: event.time[0] * 1e9 + event.time[1],
|
|
53
|
-
name: event.name,
|
|
54
|
-
attributes: Object.entries(event.attributes || {}).map(([key, value]) => ({
|
|
55
|
-
key,
|
|
56
|
-
value: { stringValue: String(value) },
|
|
57
|
-
})),
|
|
58
|
-
})),
|
|
59
|
-
links: span.links.map(link => ({
|
|
60
|
-
traceId: link.context.traceId,
|
|
61
|
-
spanId: link.context.spanId,
|
|
62
|
-
attributes: Object.entries(link.attributes || {}).map(([key, value]) => ({
|
|
63
|
-
key,
|
|
64
|
-
value: { stringValue: String(value) },
|
|
65
|
-
})),
|
|
66
|
-
})),
|
|
67
|
-
status: {
|
|
68
|
-
code: span.status.code,
|
|
69
|
-
message: span.status.message || '',
|
|
70
|
-
},
|
|
71
|
-
}],
|
|
72
|
-
}],
|
|
73
|
-
})),
|
|
74
|
-
};
|
|
75
|
-
const encoder = new TextEncoder();
|
|
76
|
-
return encoder.encode(JSON.stringify(request));
|
|
77
|
-
},
|
|
78
|
-
deserializeResponse: (arg) => {
|
|
79
|
-
const decoder = new TextDecoder();
|
|
80
|
-
return JSON.parse(decoder.decode(arg));
|
|
81
|
-
},
|
|
82
|
-
}, 'v1/traces', { 'Content-Type': 'application/json' }));
|
|
83
13
|
this.usePostMessage = false;
|
|
84
|
-
|
|
14
|
+
const { url = constants_base_1.MULTIPLAYER_OTEL_DEFAULT_TRACES_EXPORTER_HTTP_URL, apiKey, headers = {}, timeoutMillis = 30000, keepAlive = true, concurrencyLimit = 20, postMessageType = 'MULTIPLAYER_SESSION_DEBUGGER_LIB', postMessageTargetOrigin = '*', } = config;
|
|
15
|
+
this.config = Object.assign(Object.assign({}, config), { url,
|
|
16
|
+
apiKey,
|
|
17
|
+
headers,
|
|
18
|
+
timeoutMillis,
|
|
19
|
+
keepAlive,
|
|
20
|
+
concurrencyLimit });
|
|
85
21
|
this.postMessageType = postMessageType;
|
|
86
22
|
this.postMessageTargetOrigin = postMessageTargetOrigin;
|
|
23
|
+
this.exporter = this._createExporter();
|
|
87
24
|
}
|
|
88
25
|
export(spans, resultCallback) {
|
|
89
26
|
// Filter spans to only include those with Multiplayer trace prefixes
|
|
@@ -101,7 +38,7 @@ class SessionRecorderBrowserTraceExporter extends otlp_exporter_base_1.OTLPExpor
|
|
|
101
38
|
this.exportViaPostMessage(filteredSpans, resultCallback);
|
|
102
39
|
return;
|
|
103
40
|
}
|
|
104
|
-
|
|
41
|
+
this.exporter.export(filteredSpans, (result) => {
|
|
105
42
|
if (result.code === 0) {
|
|
106
43
|
resultCallback(result);
|
|
107
44
|
}
|
|
@@ -115,7 +52,7 @@ class SessionRecorderBrowserTraceExporter extends otlp_exporter_base_1.OTLPExpor
|
|
|
115
52
|
});
|
|
116
53
|
}
|
|
117
54
|
shutdown() {
|
|
118
|
-
return
|
|
55
|
+
return this.exporter.shutdown();
|
|
119
56
|
}
|
|
120
57
|
exportViaPostMessage(spans, resultCallback) {
|
|
121
58
|
if (typeof window === 'undefined') {
|
|
@@ -135,6 +72,7 @@ class SessionRecorderBrowserTraceExporter extends otlp_exporter_base_1.OTLPExpor
|
|
|
135
72
|
}
|
|
136
73
|
}
|
|
137
74
|
_serializeSpan(span) {
|
|
75
|
+
var _a;
|
|
138
76
|
const spanContext = span.spanContext();
|
|
139
77
|
return {
|
|
140
78
|
_spanContext: spanContext,
|
|
@@ -148,7 +86,7 @@ class SessionRecorderBrowserTraceExporter extends otlp_exporter_base_1.OTLPExpor
|
|
|
148
86
|
startTime: span.startTime,
|
|
149
87
|
duration: span.duration,
|
|
150
88
|
attributes: span.attributes,
|
|
151
|
-
parentSpanId:
|
|
89
|
+
parentSpanId: (_a = span.parentSpanContext) === null || _a === void 0 ? void 0 : _a.spanId,
|
|
152
90
|
droppedAttributesCount: span.droppedAttributesCount,
|
|
153
91
|
droppedEventsCount: span.droppedEventsCount,
|
|
154
92
|
droppedLinksCount: span.droppedLinksCount,
|
|
@@ -158,6 +96,19 @@ class SessionRecorderBrowserTraceExporter extends otlp_exporter_base_1.OTLPExpor
|
|
|
158
96
|
},
|
|
159
97
|
};
|
|
160
98
|
}
|
|
99
|
+
_createExporter() {
|
|
100
|
+
return new exporter_trace_otlp_http_1.OTLPTraceExporter({
|
|
101
|
+
url: this.config.url,
|
|
102
|
+
headers: Object.assign({ 'Content-Type': 'application/x-protobuf', 'User-Agent': '@multiplayer-app/session-recorder-common/1.0.0', 'authorization': this.config.apiKey }, (this.config.headers || {})),
|
|
103
|
+
timeoutMillis: this.config.timeoutMillis,
|
|
104
|
+
keepAlive: this.config.keepAlive,
|
|
105
|
+
concurrencyLimit: this.config.concurrencyLimit,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
setApiKey(apiKey) {
|
|
109
|
+
this.config.apiKey = apiKey;
|
|
110
|
+
this.exporter = this._createExporter();
|
|
111
|
+
}
|
|
161
112
|
}
|
|
162
113
|
exports.SessionRecorderBrowserTraceExporter = SessionRecorderBrowserTraceExporter;
|
|
163
114
|
//# sourceMappingURL=SessionRecorderBrowserTraceExporter.js.map
|
|
@@ -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 {\n OTLPExporterConfigBase,\n OTLPExporterBase,\n} from '@opentelemetry/otlp-exporter-base'\nimport {\n createLegacyOtlpBrowserExportDelegate,\n} from '@opentelemetry/otlp-exporter-base/browser-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 extends OTLPExporterConfigBase {\n /** API key for authentication. Required. */\n apiKey: string\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\n extends OTLPExporterBase<ReadableSpan[]>\n implements SpanExporter {\n\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 postMessageType = 'MULTIPLAYER_SESSION_DEBUGGER_LIB',\n postMessageTargetOrigin = '*',\n ...restConfig\n } = config\n\n const _config = {\n ...restConfig,\n url,\n headers: {\n 'Content-Type': 'application/x-protobuf',\n 'User-Agent': '@multiplayer-app/session-recorder-common/1.0.0',\n 'Authorization': apiKey,\n ...headers,\n },\n }\n\n super(\n createLegacyOtlpBrowserExportDelegate(\n _config,\n {\n serializeRequest: (spans: ReadableSpan[]) => {\n // Create a simple trace service request structure\n const request = {\n resourceSpans: spans.map(span => ({\n resource: {\n attributes: Object.entries(span.resource.attributes).map(([key, value]) => ({\n key,\n value: { stringValue: String(value) },\n })),\n },\n scopeSpans: [{\n spans: [{\n traceId: span.spanContext().traceId,\n spanId: span.spanContext().spanId,\n parentSpanId: span.spanContext().spanId, // Using spanId as fallback\n name: span.name,\n kind: span.kind,\n startTimeUnixNano: span.startTime[0] * 1e9 + span.startTime[1],\n endTimeUnixNano: span.endTime ? span.endTime[0] * 1e9 + span.endTime[1] : undefined,\n attributes: Object.entries(span.attributes).map(([key, value]) => ({\n key,\n value: { stringValue: String(value) },\n })),\n events: span.events.map(event => ({\n timeUnixNano: event.time[0] * 1e9 + event.time[1],\n name: event.name,\n attributes: Object.entries(event.attributes || {}).map(([key, value]) => ({\n key,\n value: { stringValue: String(value) },\n })),\n })),\n links: span.links.map(link => ({\n traceId: link.context.traceId,\n spanId: link.context.spanId,\n attributes: Object.entries(link.attributes || {}).map(([key, value]) => ({\n key,\n value: { stringValue: String(value) },\n })),\n })),\n status: {\n code: span.status.code,\n message: span.status.message || '',\n },\n }],\n }],\n })),\n }\n\n const encoder = new TextEncoder()\n return encoder.encode(JSON.stringify(request))\n },\n deserializeResponse: (arg: Uint8Array) => {\n const decoder = new TextDecoder()\n return JSON.parse(decoder.decode(arg))\n },\n },\n 'v1/traces',\n { 'Content-Type': 'application/json' },\n ),\n )\n\n this.config = config\n this.postMessageType = postMessageType\n this.postMessageTargetOrigin = postMessageTargetOrigin\n }\n\n override 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 traceId.startsWith(MULTIPLAYER_TRACE_DEBUG_PREFIX) ||\n traceId.startsWith(MULTIPLAYER_TRACE_CONTINUOUS_DEBUG_PREFIX)\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 super.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 override shutdown(): Promise<void> {\n return Promise.resolve()\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 private _serializeSpan(span: ReadableSpan): any {\n const spanContext = span.spanContext()\n return {\n _spanContext: spanContext,\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: spanContext.spanId, // Using spanId as parentSpanId is not available in newer versions\n droppedAttributesCount: span.droppedAttributesCount,\n droppedEventsCount: span.droppedEventsCount,\n droppedLinksCount: span.droppedLinksCount,\n resource: {\n attributes: span.resource.attributes,\n asyncAttributesPending: span.resource.asyncAttributesPending,\n },\n }\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'\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 timeoutMillis,\n keepAlive,\n concurrencyLimit,\n }\n this.postMessageType = postMessageType\n this.postMessageTargetOrigin = postMessageTargetOrigin\n\n this.exporter = this._createExporter()\n }\n\n export(\n spans: ReadableSpan[],\n resultCallback: (result: { code: number }) => void,\n ): 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 traceId.startsWith(MULTIPLAYER_TRACE_DEBUG_PREFIX) ||\n traceId.startsWith(MULTIPLAYER_TRACE_CONTINUOUS_DEBUG_PREFIX)\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 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 private _serializeSpan(span: ReadableSpan): any {\n const spanContext = span.spanContext()\n return {\n _spanContext: spanContext,\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 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/x-protobuf',\n 'User-Agent': '@multiplayer-app/session-recorder-common/1.0.0',\n '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\n this.exporter = this._createExporter()\n }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@multiplayer-app/session-recorder-common",
|
|
3
|
-
"version": "1.0.1-alpha.
|
|
3
|
+
"version": "1.0.1-alpha.2",
|
|
4
4
|
"description": "Multiplayer Fullstack Session Recorder - opentelemetry",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Multiplayer Software, Inc.",
|
|
@@ -37,13 +37,13 @@
|
|
|
37
37
|
"@opentelemetry/api": "^1.9.0"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@opentelemetry/core": "^
|
|
40
|
+
"@opentelemetry/core": "^2.0.1",
|
|
41
|
+
"@opentelemetry/otlp-transformer": "^0.203.0",
|
|
41
42
|
"@opentelemetry/exporter-logs-otlp-http": "^0.203.0",
|
|
42
43
|
"@opentelemetry/exporter-logs-otlp-proto": "^0.203.0",
|
|
43
44
|
"@opentelemetry/exporter-trace-otlp-http": "^0.203.0",
|
|
44
45
|
"@opentelemetry/exporter-trace-otlp-proto": "^0.203.0",
|
|
45
46
|
"@opentelemetry/otlp-exporter-base": "^0.203.0",
|
|
46
|
-
"@opentelemetry/otlp-transformer": "^0.203.0",
|
|
47
47
|
"@opentelemetry/resources": "^2.0.1",
|
|
48
48
|
"@opentelemetry/sdk-trace-base": "^2.0.1",
|
|
49
49
|
"@opentelemetry/semantic-conventions": "^1.36.0",
|
|
@@ -1,21 +1,24 @@
|
|
|
1
1
|
import { ReadableSpan, SpanExporter } from '@opentelemetry/sdk-trace-base'
|
|
2
|
-
import {
|
|
3
|
-
OTLPExporterConfigBase,
|
|
4
|
-
OTLPExporterBase,
|
|
5
|
-
} from '@opentelemetry/otlp-exporter-base'
|
|
6
|
-
import {
|
|
7
|
-
createLegacyOtlpBrowserExportDelegate,
|
|
8
|
-
} from '@opentelemetry/otlp-exporter-base/browser-http'
|
|
2
|
+
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'
|
|
9
3
|
import {
|
|
10
4
|
MULTIPLAYER_OTEL_DEFAULT_TRACES_EXPORTER_HTTP_URL,
|
|
11
5
|
MULTIPLAYER_TRACE_DEBUG_PREFIX,
|
|
12
6
|
MULTIPLAYER_TRACE_CONTINUOUS_DEBUG_PREFIX,
|
|
13
7
|
} from '../constants/constants.base'
|
|
14
8
|
|
|
15
|
-
export interface SessionRecorderBrowserTraceExporterConfig
|
|
16
|
-
|
|
9
|
+
export interface SessionRecorderBrowserTraceExporterConfig {
|
|
10
|
+
/** URL for the OTLP endpoint. Defaults to Multiplayer's default traces endpoint. */
|
|
11
|
+
url?: string
|
|
17
12
|
/** API key for authentication. Required. */
|
|
18
13
|
apiKey: string
|
|
14
|
+
/** Additional headers to include in requests */
|
|
15
|
+
headers?: Record<string, string>
|
|
16
|
+
/** Request timeout in milliseconds */
|
|
17
|
+
timeoutMillis?: number
|
|
18
|
+
/** Whether to use keep-alive connections */
|
|
19
|
+
keepAlive?: boolean
|
|
20
|
+
/** Maximum number of concurrent requests */
|
|
21
|
+
concurrencyLimit?: number
|
|
19
22
|
/** Whether to use postMessage fallback for cross-origin requests */
|
|
20
23
|
usePostMessageFallback?: boolean
|
|
21
24
|
/** PostMessage type identifier */
|
|
@@ -29,10 +32,8 @@ export interface SessionRecorderBrowserTraceExporterConfig
|
|
|
29
32
|
* Exports traces via HTTP to Multiplayer's OTLP endpoint with browser-specific optimizations
|
|
30
33
|
* Only exports spans with trace IDs starting with Multiplayer prefixes
|
|
31
34
|
*/
|
|
32
|
-
export class SessionRecorderBrowserTraceExporter
|
|
33
|
-
|
|
34
|
-
implements SpanExporter {
|
|
35
|
-
|
|
35
|
+
export class SessionRecorderBrowserTraceExporter implements SpanExporter {
|
|
36
|
+
private exporter: OTLPTraceExporter
|
|
36
37
|
private usePostMessage: boolean = false
|
|
37
38
|
private readonly postMessageType: string
|
|
38
39
|
private readonly postMessageTargetOrigin: string
|
|
@@ -43,93 +44,32 @@ export class SessionRecorderBrowserTraceExporter
|
|
|
43
44
|
url = MULTIPLAYER_OTEL_DEFAULT_TRACES_EXPORTER_HTTP_URL,
|
|
44
45
|
apiKey,
|
|
45
46
|
headers = {},
|
|
47
|
+
timeoutMillis = 30000,
|
|
48
|
+
keepAlive = true,
|
|
49
|
+
concurrencyLimit = 20,
|
|
46
50
|
postMessageType = 'MULTIPLAYER_SESSION_DEBUGGER_LIB',
|
|
47
51
|
postMessageTargetOrigin = '*',
|
|
48
|
-
...restConfig
|
|
49
52
|
} = config
|
|
50
53
|
|
|
51
|
-
|
|
52
|
-
...
|
|
54
|
+
this.config = {
|
|
55
|
+
...config,
|
|
53
56
|
url,
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
},
|
|
57
|
+
apiKey,
|
|
58
|
+
headers,
|
|
59
|
+
timeoutMillis,
|
|
60
|
+
keepAlive,
|
|
61
|
+
concurrencyLimit,
|
|
60
62
|
}
|
|
61
|
-
|
|
62
|
-
super(
|
|
63
|
-
createLegacyOtlpBrowserExportDelegate(
|
|
64
|
-
_config,
|
|
65
|
-
{
|
|
66
|
-
serializeRequest: (spans: ReadableSpan[]) => {
|
|
67
|
-
// Create a simple trace service request structure
|
|
68
|
-
const request = {
|
|
69
|
-
resourceSpans: spans.map(span => ({
|
|
70
|
-
resource: {
|
|
71
|
-
attributes: Object.entries(span.resource.attributes).map(([key, value]) => ({
|
|
72
|
-
key,
|
|
73
|
-
value: { stringValue: String(value) },
|
|
74
|
-
})),
|
|
75
|
-
},
|
|
76
|
-
scopeSpans: [{
|
|
77
|
-
spans: [{
|
|
78
|
-
traceId: span.spanContext().traceId,
|
|
79
|
-
spanId: span.spanContext().spanId,
|
|
80
|
-
parentSpanId: span.spanContext().spanId, // Using spanId as fallback
|
|
81
|
-
name: span.name,
|
|
82
|
-
kind: span.kind,
|
|
83
|
-
startTimeUnixNano: span.startTime[0] * 1e9 + span.startTime[1],
|
|
84
|
-
endTimeUnixNano: span.endTime ? span.endTime[0] * 1e9 + span.endTime[1] : undefined,
|
|
85
|
-
attributes: Object.entries(span.attributes).map(([key, value]) => ({
|
|
86
|
-
key,
|
|
87
|
-
value: { stringValue: String(value) },
|
|
88
|
-
})),
|
|
89
|
-
events: span.events.map(event => ({
|
|
90
|
-
timeUnixNano: event.time[0] * 1e9 + event.time[1],
|
|
91
|
-
name: event.name,
|
|
92
|
-
attributes: Object.entries(event.attributes || {}).map(([key, value]) => ({
|
|
93
|
-
key,
|
|
94
|
-
value: { stringValue: String(value) },
|
|
95
|
-
})),
|
|
96
|
-
})),
|
|
97
|
-
links: span.links.map(link => ({
|
|
98
|
-
traceId: link.context.traceId,
|
|
99
|
-
spanId: link.context.spanId,
|
|
100
|
-
attributes: Object.entries(link.attributes || {}).map(([key, value]) => ({
|
|
101
|
-
key,
|
|
102
|
-
value: { stringValue: String(value) },
|
|
103
|
-
})),
|
|
104
|
-
})),
|
|
105
|
-
status: {
|
|
106
|
-
code: span.status.code,
|
|
107
|
-
message: span.status.message || '',
|
|
108
|
-
},
|
|
109
|
-
}],
|
|
110
|
-
}],
|
|
111
|
-
})),
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
const encoder = new TextEncoder()
|
|
115
|
-
return encoder.encode(JSON.stringify(request))
|
|
116
|
-
},
|
|
117
|
-
deserializeResponse: (arg: Uint8Array) => {
|
|
118
|
-
const decoder = new TextDecoder()
|
|
119
|
-
return JSON.parse(decoder.decode(arg))
|
|
120
|
-
},
|
|
121
|
-
},
|
|
122
|
-
'v1/traces',
|
|
123
|
-
{ 'Content-Type': 'application/json' },
|
|
124
|
-
),
|
|
125
|
-
)
|
|
126
|
-
|
|
127
|
-
this.config = config
|
|
128
63
|
this.postMessageType = postMessageType
|
|
129
64
|
this.postMessageTargetOrigin = postMessageTargetOrigin
|
|
65
|
+
|
|
66
|
+
this.exporter = this._createExporter()
|
|
130
67
|
}
|
|
131
68
|
|
|
132
|
-
|
|
69
|
+
export(
|
|
70
|
+
spans: ReadableSpan[],
|
|
71
|
+
resultCallback: (result: { code: number }) => void,
|
|
72
|
+
): void {
|
|
133
73
|
// Filter spans to only include those with Multiplayer trace prefixes
|
|
134
74
|
const filteredSpans = spans.filter(span => {
|
|
135
75
|
const traceId = span.spanContext().traceId
|
|
@@ -148,7 +88,7 @@ export class SessionRecorderBrowserTraceExporter
|
|
|
148
88
|
return
|
|
149
89
|
}
|
|
150
90
|
|
|
151
|
-
|
|
91
|
+
this.exporter.export(filteredSpans, (result) => {
|
|
152
92
|
if (result.code === 0) {
|
|
153
93
|
resultCallback(result)
|
|
154
94
|
} else if (this.config.usePostMessageFallback) {
|
|
@@ -160,8 +100,8 @@ export class SessionRecorderBrowserTraceExporter
|
|
|
160
100
|
})
|
|
161
101
|
}
|
|
162
102
|
|
|
163
|
-
|
|
164
|
-
return
|
|
103
|
+
shutdown(): Promise<void> {
|
|
104
|
+
return this.exporter.shutdown()
|
|
165
105
|
}
|
|
166
106
|
|
|
167
107
|
private exportViaPostMessage(spans: ReadableSpan[], resultCallback: (result: { code: number }) => void): void {
|
|
@@ -199,7 +139,7 @@ export class SessionRecorderBrowserTraceExporter
|
|
|
199
139
|
startTime: span.startTime,
|
|
200
140
|
duration: span.duration,
|
|
201
141
|
attributes: span.attributes,
|
|
202
|
-
parentSpanId:
|
|
142
|
+
parentSpanId: span.parentSpanContext?.spanId,
|
|
203
143
|
droppedAttributesCount: span.droppedAttributesCount,
|
|
204
144
|
droppedEventsCount: span.droppedEventsCount,
|
|
205
145
|
droppedLinksCount: span.droppedLinksCount,
|
|
@@ -209,4 +149,25 @@ export class SessionRecorderBrowserTraceExporter
|
|
|
209
149
|
},
|
|
210
150
|
}
|
|
211
151
|
}
|
|
152
|
+
|
|
153
|
+
private _createExporter(): OTLPTraceExporter {
|
|
154
|
+
return new OTLPTraceExporter({
|
|
155
|
+
url: this.config.url,
|
|
156
|
+
headers: {
|
|
157
|
+
'Content-Type': 'application/x-protobuf',
|
|
158
|
+
'User-Agent': '@multiplayer-app/session-recorder-common/1.0.0',
|
|
159
|
+
'authorization': this.config.apiKey,
|
|
160
|
+
...(this.config.headers || {}),
|
|
161
|
+
},
|
|
162
|
+
timeoutMillis: this.config.timeoutMillis,
|
|
163
|
+
keepAlive: this.config.keepAlive,
|
|
164
|
+
concurrencyLimit: this.config.concurrencyLimit,
|
|
165
|
+
})
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
setApiKey(apiKey: string): void {
|
|
169
|
+
this.config.apiKey = apiKey
|
|
170
|
+
|
|
171
|
+
this.exporter = this._createExporter()
|
|
172
|
+
}
|
|
212
173
|
}
|