chinalife-otel-web-sdk 1.0.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.
@@ -0,0 +1,1425 @@
1
+ import { createContextKey, diag, trace, isSpanContextValid, TraceFlags, isValidTraceId, isValidSpanId, propagation, context } from '@opentelemetry/api';
2
+ import { BatchSpanProcessor, WebTracerProvider } from '@opentelemetry/sdk-trace-web';
3
+ import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
4
+ import { resourceFromAttributes } from '@opentelemetry/resources';
5
+ import { ATTR_SERVICE_VERSION, ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions';
6
+ import { registerInstrumentations } from '@opentelemetry/instrumentation';
7
+ import { FetchInstrumentation } from '@opentelemetry/instrumentation-fetch';
8
+ import { XMLHttpRequestInstrumentation } from '@opentelemetry/instrumentation-xml-http-request';
9
+ import { DocumentLoadInstrumentation } from '@opentelemetry/instrumentation-document-load';
10
+ import { UserInteractionInstrumentation } from '@opentelemetry/instrumentation-user-interaction';
11
+ import { LongTaskInstrumentation } from '@opentelemetry/instrumentation-long-task';
12
+
13
+ /**
14
+ * 默认配置
15
+ */
16
+ const DEFAULT_CONFIG = {
17
+ serviceName: 'unknown-service',
18
+ serviceVersion: '1.0.0',
19
+ environment: 'production',
20
+ datacenter: 'dc1',
21
+ collectorEndpoint: 'http://localhost:4318/v1/traces',
22
+ samplingRatio: 1.0,
23
+ enableAutoInstrumentation: true,
24
+ enableRouterTracing: true,
25
+ enableHttpTracing: true,
26
+ framework: 'h5',
27
+ batchConfig: {
28
+ maxQueueSize: 2048,
29
+ maxExportBatchSize: 512,
30
+ scheduledDelayMillis: 5000,
31
+ exportTimeoutMillis: 30000,
32
+ },
33
+ enableLocalStorage: false,
34
+ localStorageKey: 'otel_spans',
35
+ maxLocalStorageSize: 10240,
36
+ logLevel: 'info',
37
+ propagationFormat: 'w3c',
38
+ };
39
+ /**
40
+ * 从环境变量读取配置
41
+ */
42
+ function getEnvConfig() {
43
+ const config = {};
44
+ // 支持浏览器环境变量(通过 window 对象或 process.env)
45
+ // 注意:import.meta.env 需要在构建时由构建工具(如 Vite)注入
46
+ // 使用方应该通过配置选项或 window.__OTEL_CONFIG__ 传递环境变量
47
+ const getEnv = (key) => {
48
+ // 1. 尝试从 window.__OTEL_CONFIG__ 获取(推荐方式)
49
+ if (typeof window !== 'undefined') {
50
+ const win = window;
51
+ if (win.__OTEL_CONFIG__ && typeof win.__OTEL_CONFIG__ === 'object') {
52
+ const value = win.__OTEL_CONFIG__[key];
53
+ if (value !== undefined) {
54
+ return String(value);
55
+ }
56
+ }
57
+ }
58
+ // 2. 尝试从 process.env 获取(Node.js/Webpack 环境)
59
+ if (typeof process !== 'undefined' && process.env) {
60
+ const value = process.env[key];
61
+ if (value !== undefined) {
62
+ return value;
63
+ }
64
+ }
65
+ // 3. 尝试从全局对象获取(某些构建工具可能会注入)
66
+ if (typeof globalThis !== 'undefined') {
67
+ const global = globalThis;
68
+ if (global.process?.env?.[key]) {
69
+ return global.process.env[key];
70
+ }
71
+ }
72
+ return undefined;
73
+ };
74
+ // 获取环境变量(支持多种命名方式)
75
+ const serviceName = getEnv('OTEL_SERVICE_NAME') || getEnv('VITE_OTEL_SERVICE_NAME') || getEnv('REACT_APP_OTEL_SERVICE_NAME');
76
+ if (serviceName)
77
+ config.serviceName = serviceName;
78
+ const serviceVersion = getEnv('OTEL_SERVICE_VERSION') || getEnv('VITE_OTEL_SERVICE_VERSION') || getEnv('REACT_APP_OTEL_SERVICE_VERSION');
79
+ if (serviceVersion)
80
+ config.serviceVersion = serviceVersion;
81
+ const environment = getEnv('OTEL_DEPLOYMENT_ENVIRONMENT') || getEnv('VITE_OTEL_DEPLOYMENT_ENVIRONMENT') || getEnv('REACT_APP_OTEL_DEPLOYMENT_ENVIRONMENT');
82
+ if (environment)
83
+ config.environment = environment;
84
+ const datacenter = getEnv('OTEL_DATACENTER') || getEnv('VITE_OTEL_DATACENTER') || getEnv('REACT_APP_OTEL_DATACENTER');
85
+ if (datacenter)
86
+ config.datacenter = datacenter;
87
+ const collectorEndpoint = getEnv('OTEL_EXPORTER_OTLP_ENDPOINT') ||
88
+ getEnv('OTEL_EXPORTER_OTLP_TRACES_ENDPOINT') ||
89
+ getEnv('VITE_OTEL_COLLECTOR_ENDPOINT') ||
90
+ getEnv('VITE_OTEL_EXPORTER_OTLP_ENDPOINT') ||
91
+ getEnv('REACT_APP_OTEL_COLLECTOR_ENDPOINT');
92
+ if (collectorEndpoint)
93
+ config.collectorEndpoint = collectorEndpoint;
94
+ const samplingRatio = getEnv('OTEL_SAMPLING_RATIO') || getEnv('VITE_OTEL_SAMPLING_RATIO') || getEnv('REACT_APP_OTEL_SAMPLING_RATIO');
95
+ if (samplingRatio) {
96
+ const ratio = parseFloat(samplingRatio);
97
+ if (!isNaN(ratio))
98
+ config.samplingRatio = ratio;
99
+ }
100
+ const logLevel = getEnv('OTEL_LOG_LEVEL') || getEnv('VITE_OTEL_LOG_LEVEL') || getEnv('REACT_APP_OTEL_LOG_LEVEL');
101
+ if (logLevel) {
102
+ const level = logLevel.toLowerCase();
103
+ if (['debug', 'info', 'warn', 'error'].includes(level)) {
104
+ config.logLevel = level;
105
+ }
106
+ }
107
+ // 获取传播协议格式
108
+ const propagationFormat = getEnv('OTEL_PROPAGATION_FORMAT') ||
109
+ getEnv('OTEL_PROPAGATORS') ||
110
+ getEnv('VITE_OTEL_PROPAGATION_FORMAT') ||
111
+ getEnv('REACT_APP_OTEL_PROPAGATION_FORMAT');
112
+ if (propagationFormat) {
113
+ // 支持逗号分隔的多个格式
114
+ const formats = propagationFormat.split(',').map(f => f.trim()).filter(f => f);
115
+ if (formats.length > 0) {
116
+ // 类型断言:单个格式或格式数组
117
+ config.propagationFormat = (formats.length === 1 ? formats[0] : formats);
118
+ }
119
+ }
120
+ return config;
121
+ }
122
+ /**
123
+ * 合并配置
124
+ */
125
+ function mergeConfig(options) {
126
+ const envConfig = getEnvConfig();
127
+ const merged = {
128
+ ...DEFAULT_CONFIG,
129
+ ...envConfig,
130
+ ...options,
131
+ };
132
+ // 如果用户明确配置了 propagationFormat,确保不被默认值覆盖
133
+ if (options?.propagationFormat !== undefined) {
134
+ merged.propagationFormat = options.propagationFormat;
135
+ }
136
+ else if (envConfig.propagationFormat !== undefined) {
137
+ merged.propagationFormat = envConfig.propagationFormat;
138
+ }
139
+ // 合并批量配置
140
+ if (options?.batchConfig || envConfig.batchConfig) {
141
+ merged.batchConfig = {
142
+ ...DEFAULT_CONFIG.batchConfig,
143
+ ...envConfig.batchConfig,
144
+ ...options?.batchConfig,
145
+ };
146
+ }
147
+ // 根据数据中心选择 Collector 端点
148
+ if (merged.datacenter && !options?.collectorEndpoint && !envConfig.collectorEndpoint) {
149
+ const datacenterEndpoints = {
150
+ dc1: 'http://collector-dc1.otel.svc.cluster.local:4318/v1/traces',
151
+ dc2: 'http://collector-dc2.otel.svc.cluster.local:4318/v1/traces',
152
+ dc3: 'http://collector-dc3.otel.svc.cluster.local:4318/v1/traces',
153
+ };
154
+ if (datacenterEndpoints[merged.datacenter]) {
155
+ merged.collectorEndpoint = datacenterEndpoints[merged.datacenter];
156
+ }
157
+ }
158
+ return merged;
159
+ }
160
+ /**
161
+ * 日志工具
162
+ */
163
+ class Logger {
164
+ constructor(level = 'info') {
165
+ this.level = level;
166
+ }
167
+ setLevel(level) {
168
+ this.level = level;
169
+ }
170
+ shouldLog(level) {
171
+ const levels = ['debug', 'info', 'warn', 'error'];
172
+ return levels.indexOf(level) >= levels.indexOf(this.level);
173
+ }
174
+ debug(...args) {
175
+ if (this.shouldLog('debug')) {
176
+ console.debug('[OTel SDK]', ...args);
177
+ }
178
+ }
179
+ info(...args) {
180
+ if (this.shouldLog('info')) {
181
+ console.info('[OTel SDK]', ...args);
182
+ }
183
+ }
184
+ warn(...args) {
185
+ if (this.shouldLog('warn')) {
186
+ console.warn('[OTel SDK]', ...args);
187
+ }
188
+ }
189
+ error(...args) {
190
+ if (this.shouldLog('error')) {
191
+ console.error('[OTel SDK]', ...args);
192
+ }
193
+ }
194
+ }
195
+
196
+ /**
197
+ * 创建自动 instrumentation 配置
198
+ */
199
+ function createInstrumentations(options) {
200
+ const instrumentations = [];
201
+ if (!options.enableAutoInstrumentation) {
202
+ return instrumentations;
203
+ }
204
+ // HTTP 追踪
205
+ if (options.enableHttpTracing) {
206
+ // Fetch API
207
+ instrumentations.push(new FetchInstrumentation({
208
+ propagateTraceHeaderCorsUrls: [/^https?:\/\/.*/],
209
+ clearTimingResources: true,
210
+ }));
211
+ // XMLHttpRequest
212
+ instrumentations.push(new XMLHttpRequestInstrumentation({
213
+ propagateTraceHeaderCorsUrls: [/^https?:\/\/.*/],
214
+ clearTimingResources: true,
215
+ }));
216
+ }
217
+ // 文档加载追踪
218
+ instrumentations.push(new DocumentLoadInstrumentation());
219
+ // 用户交互追踪
220
+ instrumentations.push(new UserInteractionInstrumentation({
221
+ enabled: true,
222
+ }));
223
+ // 长任务追踪
224
+ instrumentations.push(new LongTaskInstrumentation({
225
+ enabled: true,
226
+ }));
227
+ return instrumentations;
228
+ }
229
+ /**
230
+ * Vue Router instrumentation(如果使用 Vue)
231
+ */
232
+ function setupVueRouterInstrumentation(router, tracer) {
233
+ if (!router || !tracer) {
234
+ return;
235
+ }
236
+ // 监听路由变化
237
+ router.beforeEach((to, from, next) => {
238
+ const span = tracer.startSpan('route_change', {
239
+ attributes: {
240
+ 'route.from': from.path,
241
+ 'route.to': to.path,
242
+ 'route.name': to.name || 'unknown',
243
+ },
244
+ });
245
+ // 将 span 存储到路由元信息中
246
+ to.__otel_span__ = span;
247
+ next();
248
+ });
249
+ router.afterEach((to) => {
250
+ const span = to.__otel_span__;
251
+ if (span) {
252
+ span.setStatus({ code: 1 }); // OK
253
+ span.end();
254
+ delete to.__otel_span__;
255
+ }
256
+ });
257
+ }
258
+
259
+ /*
260
+ * Copyright The OpenTelemetry Authors
261
+ *
262
+ * Licensed under the Apache License, Version 2.0 (the "License");
263
+ * you may not use this file except in compliance with the License.
264
+ * You may obtain a copy of the License at
265
+ *
266
+ * https://www.apache.org/licenses/LICENSE-2.0
267
+ *
268
+ * Unless required by applicable law or agreed to in writing, software
269
+ * distributed under the License is distributed on an "AS IS" BASIS,
270
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
271
+ * See the License for the specific language governing permissions and
272
+ * limitations under the License.
273
+ */
274
+ var SUPPRESS_TRACING_KEY = createContextKey('OpenTelemetry SDK Context Key SUPPRESS_TRACING');
275
+ function isTracingSuppressed(context) {
276
+ return context.getValue(SUPPRESS_TRACING_KEY) === true;
277
+ }
278
+
279
+ /*
280
+ * Copyright The OpenTelemetry Authors
281
+ *
282
+ * Licensed under the Apache License, Version 2.0 (the "License");
283
+ * you may not use this file except in compliance with the License.
284
+ * You may obtain a copy of the License at
285
+ *
286
+ * https://www.apache.org/licenses/LICENSE-2.0
287
+ *
288
+ * Unless required by applicable law or agreed to in writing, software
289
+ * distributed under the License is distributed on an "AS IS" BASIS,
290
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
291
+ * See the License for the specific language governing permissions and
292
+ * limitations under the License.
293
+ */
294
+ var __values = (undefined && undefined.__values) || function(o) {
295
+ var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
296
+ if (m) return m.call(o);
297
+ if (o && typeof o.length === "number") return {
298
+ next: function () {
299
+ if (o && i >= o.length) o = void 0;
300
+ return { value: o && o[i++], done: !o };
301
+ }
302
+ };
303
+ throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
304
+ };
305
+ /** Combines multiple propagators into a single propagator. */
306
+ var CompositePropagator = /** @class */ (function () {
307
+ /**
308
+ * Construct a composite propagator from a list of propagators.
309
+ *
310
+ * @param [config] Configuration object for composite propagator
311
+ */
312
+ function CompositePropagator(config) {
313
+ if (config === void 0) { config = {}; }
314
+ var _a;
315
+ this._propagators = (_a = config.propagators) !== null && _a !== void 0 ? _a : [];
316
+ this._fields = Array.from(new Set(this._propagators
317
+ // older propagators may not have fields function, null check to be sure
318
+ .map(function (p) { return (typeof p.fields === 'function' ? p.fields() : []); })
319
+ .reduce(function (x, y) { return x.concat(y); }, [])));
320
+ }
321
+ /**
322
+ * Run each of the configured propagators with the given context and carrier.
323
+ * Propagators are run in the order they are configured, so if multiple
324
+ * propagators write the same carrier key, the propagator later in the list
325
+ * will "win".
326
+ *
327
+ * @param context Context to inject
328
+ * @param carrier Carrier into which context will be injected
329
+ */
330
+ CompositePropagator.prototype.inject = function (context, carrier, setter) {
331
+ var e_1, _a;
332
+ try {
333
+ for (var _b = __values(this._propagators), _c = _b.next(); !_c.done; _c = _b.next()) {
334
+ var propagator = _c.value;
335
+ try {
336
+ propagator.inject(context, carrier, setter);
337
+ }
338
+ catch (err) {
339
+ diag.warn("Failed to inject with " + propagator.constructor.name + ". Err: " + err.message);
340
+ }
341
+ }
342
+ }
343
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
344
+ finally {
345
+ try {
346
+ if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
347
+ }
348
+ finally { if (e_1) throw e_1.error; }
349
+ }
350
+ };
351
+ /**
352
+ * Run each of the configured propagators with the given context and carrier.
353
+ * Propagators are run in the order they are configured, so if multiple
354
+ * propagators write the same context key, the propagator later in the list
355
+ * will "win".
356
+ *
357
+ * @param context Context to add values to
358
+ * @param carrier Carrier from which to extract context
359
+ */
360
+ CompositePropagator.prototype.extract = function (context, carrier, getter) {
361
+ return this._propagators.reduce(function (ctx, propagator) {
362
+ try {
363
+ return propagator.extract(ctx, carrier, getter);
364
+ }
365
+ catch (err) {
366
+ diag.warn("Failed to extract with " + propagator.constructor.name + ". Err: " + err.message);
367
+ }
368
+ return ctx;
369
+ }, context);
370
+ };
371
+ CompositePropagator.prototype.fields = function () {
372
+ // return a new array so our fields cannot be modified
373
+ return this._fields.slice();
374
+ };
375
+ return CompositePropagator;
376
+ }());
377
+
378
+ /*
379
+ * Copyright The OpenTelemetry Authors
380
+ *
381
+ * Licensed under the Apache License, Version 2.0 (the "License");
382
+ * you may not use this file except in compliance with the License.
383
+ * You may obtain a copy of the License at
384
+ *
385
+ * https://www.apache.org/licenses/LICENSE-2.0
386
+ *
387
+ * Unless required by applicable law or agreed to in writing, software
388
+ * distributed under the License is distributed on an "AS IS" BASIS,
389
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
390
+ * See the License for the specific language governing permissions and
391
+ * limitations under the License.
392
+ */
393
+ var VALID_KEY_CHAR_RANGE = '[_0-9a-z-*/]';
394
+ var VALID_KEY = "[a-z]" + VALID_KEY_CHAR_RANGE + "{0,255}";
395
+ var VALID_VENDOR_KEY = "[a-z0-9]" + VALID_KEY_CHAR_RANGE + "{0,240}@[a-z]" + VALID_KEY_CHAR_RANGE + "{0,13}";
396
+ var VALID_KEY_REGEX = new RegExp("^(?:" + VALID_KEY + "|" + VALID_VENDOR_KEY + ")$");
397
+ var VALID_VALUE_BASE_REGEX = /^[ -~]{0,255}[!-~]$/;
398
+ var INVALID_VALUE_COMMA_EQUAL_REGEX = /,|=/;
399
+ /**
400
+ * Key is opaque string up to 256 characters printable. It MUST begin with a
401
+ * lowercase letter, and can only contain lowercase letters a-z, digits 0-9,
402
+ * underscores _, dashes -, asterisks *, and forward slashes /.
403
+ * For multi-tenant vendor scenarios, an at sign (@) can be used to prefix the
404
+ * vendor name. Vendors SHOULD set the tenant ID at the beginning of the key.
405
+ * see https://www.w3.org/TR/trace-context/#key
406
+ */
407
+ function validateKey(key) {
408
+ return VALID_KEY_REGEX.test(key);
409
+ }
410
+ /**
411
+ * Value is opaque string up to 256 characters printable ASCII RFC0020
412
+ * characters (i.e., the range 0x20 to 0x7E) except comma , and =.
413
+ */
414
+ function validateValue(value) {
415
+ return (VALID_VALUE_BASE_REGEX.test(value) &&
416
+ !INVALID_VALUE_COMMA_EQUAL_REGEX.test(value));
417
+ }
418
+
419
+ /*
420
+ * Copyright The OpenTelemetry Authors
421
+ *
422
+ * Licensed under the Apache License, Version 2.0 (the "License");
423
+ * you may not use this file except in compliance with the License.
424
+ * You may obtain a copy of the License at
425
+ *
426
+ * https://www.apache.org/licenses/LICENSE-2.0
427
+ *
428
+ * Unless required by applicable law or agreed to in writing, software
429
+ * distributed under the License is distributed on an "AS IS" BASIS,
430
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
431
+ * See the License for the specific language governing permissions and
432
+ * limitations under the License.
433
+ */
434
+ var MAX_TRACE_STATE_ITEMS = 32;
435
+ var MAX_TRACE_STATE_LEN = 512;
436
+ var LIST_MEMBERS_SEPARATOR = ',';
437
+ var LIST_MEMBER_KEY_VALUE_SPLITTER = '=';
438
+ /**
439
+ * TraceState must be a class and not a simple object type because of the spec
440
+ * requirement (https://www.w3.org/TR/trace-context/#tracestate-field).
441
+ *
442
+ * Here is the list of allowed mutations:
443
+ * - New key-value pair should be added into the beginning of the list
444
+ * - The value of any key can be updated. Modified keys MUST be moved to the
445
+ * beginning of the list.
446
+ */
447
+ var TraceState = /** @class */ (function () {
448
+ function TraceState(rawTraceState) {
449
+ this._internalState = new Map();
450
+ if (rawTraceState)
451
+ this._parse(rawTraceState);
452
+ }
453
+ TraceState.prototype.set = function (key, value) {
454
+ // TODO: Benchmark the different approaches(map vs list) and
455
+ // use the faster one.
456
+ var traceState = this._clone();
457
+ if (traceState._internalState.has(key)) {
458
+ traceState._internalState.delete(key);
459
+ }
460
+ traceState._internalState.set(key, value);
461
+ return traceState;
462
+ };
463
+ TraceState.prototype.unset = function (key) {
464
+ var traceState = this._clone();
465
+ traceState._internalState.delete(key);
466
+ return traceState;
467
+ };
468
+ TraceState.prototype.get = function (key) {
469
+ return this._internalState.get(key);
470
+ };
471
+ TraceState.prototype.serialize = function () {
472
+ var _this = this;
473
+ return this._keys()
474
+ .reduce(function (agg, key) {
475
+ agg.push(key + LIST_MEMBER_KEY_VALUE_SPLITTER + _this.get(key));
476
+ return agg;
477
+ }, [])
478
+ .join(LIST_MEMBERS_SEPARATOR);
479
+ };
480
+ TraceState.prototype._parse = function (rawTraceState) {
481
+ if (rawTraceState.length > MAX_TRACE_STATE_LEN)
482
+ return;
483
+ this._internalState = rawTraceState
484
+ .split(LIST_MEMBERS_SEPARATOR)
485
+ .reverse() // Store in reverse so new keys (.set(...)) will be placed at the beginning
486
+ .reduce(function (agg, part) {
487
+ var listMember = part.trim(); // Optional Whitespace (OWS) handling
488
+ var i = listMember.indexOf(LIST_MEMBER_KEY_VALUE_SPLITTER);
489
+ if (i !== -1) {
490
+ var key = listMember.slice(0, i);
491
+ var value = listMember.slice(i + 1, part.length);
492
+ if (validateKey(key) && validateValue(value)) {
493
+ agg.set(key, value);
494
+ }
495
+ }
496
+ return agg;
497
+ }, new Map());
498
+ // Because of the reverse() requirement, trunc must be done after map is created
499
+ if (this._internalState.size > MAX_TRACE_STATE_ITEMS) {
500
+ this._internalState = new Map(Array.from(this._internalState.entries())
501
+ .reverse() // Use reverse same as original tracestate parse chain
502
+ .slice(0, MAX_TRACE_STATE_ITEMS));
503
+ }
504
+ };
505
+ TraceState.prototype._keys = function () {
506
+ return Array.from(this._internalState.keys()).reverse();
507
+ };
508
+ TraceState.prototype._clone = function () {
509
+ var traceState = new TraceState();
510
+ traceState._internalState = new Map(this._internalState);
511
+ return traceState;
512
+ };
513
+ return TraceState;
514
+ }());
515
+
516
+ /*
517
+ * Copyright The OpenTelemetry Authors
518
+ *
519
+ * Licensed under the Apache License, Version 2.0 (the "License");
520
+ * you may not use this file except in compliance with the License.
521
+ * You may obtain a copy of the License at
522
+ *
523
+ * https://www.apache.org/licenses/LICENSE-2.0
524
+ *
525
+ * Unless required by applicable law or agreed to in writing, software
526
+ * distributed under the License is distributed on an "AS IS" BASIS,
527
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
528
+ * See the License for the specific language governing permissions and
529
+ * limitations under the License.
530
+ */
531
+ var TRACE_PARENT_HEADER = 'traceparent';
532
+ var TRACE_STATE_HEADER = 'tracestate';
533
+ var VERSION = '00';
534
+ var VERSION_PART = '(?!ff)[\\da-f]{2}';
535
+ var TRACE_ID_PART = '(?![0]{32})[\\da-f]{32}';
536
+ var PARENT_ID_PART = '(?![0]{16})[\\da-f]{16}';
537
+ var FLAGS_PART = '[\\da-f]{2}';
538
+ var TRACE_PARENT_REGEX = new RegExp("^\\s?(" + VERSION_PART + ")-(" + TRACE_ID_PART + ")-(" + PARENT_ID_PART + ")-(" + FLAGS_PART + ")(-.*)?\\s?$");
539
+ /**
540
+ * Parses information from the [traceparent] span tag and converts it into {@link SpanContext}
541
+ * @param traceParent - A meta property that comes from server.
542
+ * It should be dynamically generated server side to have the server's request trace Id,
543
+ * a parent span Id that was set on the server's request span,
544
+ * and the trace flags to indicate the server's sampling decision
545
+ * (01 = sampled, 00 = not sampled).
546
+ * for example: '{version}-{traceId}-{spanId}-{sampleDecision}'
547
+ * For more information see {@link https://www.w3.org/TR/trace-context/}
548
+ */
549
+ function parseTraceParent(traceParent) {
550
+ var match = TRACE_PARENT_REGEX.exec(traceParent);
551
+ if (!match)
552
+ return null;
553
+ // According to the specification the implementation should be compatible
554
+ // with future versions. If there are more parts, we only reject it if it's using version 00
555
+ // See https://www.w3.org/TR/trace-context/#versioning-of-traceparent
556
+ if (match[1] === '00' && match[5])
557
+ return null;
558
+ return {
559
+ traceId: match[2],
560
+ spanId: match[3],
561
+ traceFlags: parseInt(match[4], 16),
562
+ };
563
+ }
564
+ /**
565
+ * Propagates {@link SpanContext} through Trace Context format propagation.
566
+ *
567
+ * Based on the Trace Context specification:
568
+ * https://www.w3.org/TR/trace-context/
569
+ */
570
+ var W3CTraceContextPropagator = /** @class */ (function () {
571
+ function W3CTraceContextPropagator() {
572
+ }
573
+ W3CTraceContextPropagator.prototype.inject = function (context, carrier, setter) {
574
+ var spanContext = trace.getSpanContext(context);
575
+ if (!spanContext ||
576
+ isTracingSuppressed(context) ||
577
+ !isSpanContextValid(spanContext))
578
+ return;
579
+ var traceParent = VERSION + "-" + spanContext.traceId + "-" + spanContext.spanId + "-0" + Number(spanContext.traceFlags || TraceFlags.NONE).toString(16);
580
+ setter.set(carrier, TRACE_PARENT_HEADER, traceParent);
581
+ if (spanContext.traceState) {
582
+ setter.set(carrier, TRACE_STATE_HEADER, spanContext.traceState.serialize());
583
+ }
584
+ };
585
+ W3CTraceContextPropagator.prototype.extract = function (context, carrier, getter) {
586
+ var traceParentHeader = getter.get(carrier, TRACE_PARENT_HEADER);
587
+ if (!traceParentHeader)
588
+ return context;
589
+ var traceParent = Array.isArray(traceParentHeader)
590
+ ? traceParentHeader[0]
591
+ : traceParentHeader;
592
+ if (typeof traceParent !== 'string')
593
+ return context;
594
+ var spanContext = parseTraceParent(traceParent);
595
+ if (!spanContext)
596
+ return context;
597
+ spanContext.isRemote = true;
598
+ var traceStateHeader = getter.get(carrier, TRACE_STATE_HEADER);
599
+ if (traceStateHeader) {
600
+ // If more than one `tracestate` header is found, we merge them into a
601
+ // single header.
602
+ var state = Array.isArray(traceStateHeader)
603
+ ? traceStateHeader.join(',')
604
+ : traceStateHeader;
605
+ spanContext.traceState = new TraceState(typeof state === 'string' ? state : undefined);
606
+ }
607
+ return trace.setSpanContext(context, spanContext);
608
+ };
609
+ W3CTraceContextPropagator.prototype.fields = function () {
610
+ return [TRACE_PARENT_HEADER, TRACE_STATE_HEADER];
611
+ };
612
+ return W3CTraceContextPropagator;
613
+ }());
614
+
615
+ /*
616
+ * Copyright The OpenTelemetry Authors
617
+ *
618
+ * Licensed under the Apache License, Version 2.0 (the "License");
619
+ * you may not use this file except in compliance with the License.
620
+ * You may obtain a copy of the License at
621
+ *
622
+ * https://www.apache.org/licenses/LICENSE-2.0
623
+ *
624
+ * Unless required by applicable law or agreed to in writing, software
625
+ * distributed under the License is distributed on an "AS IS" BASIS,
626
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
627
+ * See the License for the specific language governing permissions and
628
+ * limitations under the License.
629
+ */
630
+ /** shared context for storing an extracted b3 debug flag */
631
+ var B3_DEBUG_FLAG_KEY = createContextKey('OpenTelemetry Context Key B3 Debug Flag');
632
+
633
+ /*
634
+ * Copyright The OpenTelemetry Authors
635
+ *
636
+ * Licensed under the Apache License, Version 2.0 (the "License");
637
+ * you may not use this file except in compliance with the License.
638
+ * You may obtain a copy of the License at
639
+ *
640
+ * https://www.apache.org/licenses/LICENSE-2.0
641
+ *
642
+ * Unless required by applicable law or agreed to in writing, software
643
+ * distributed under the License is distributed on an "AS IS" BASIS,
644
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
645
+ * See the License for the specific language governing permissions and
646
+ * limitations under the License.
647
+ */
648
+ /** B3 single-header key */
649
+ var B3_CONTEXT_HEADER = 'b3';
650
+ /* b3 multi-header keys */
651
+ var X_B3_TRACE_ID = 'x-b3-traceid';
652
+ var X_B3_SPAN_ID = 'x-b3-spanid';
653
+ var X_B3_SAMPLED = 'x-b3-sampled';
654
+ var X_B3_PARENT_SPAN_ID = 'x-b3-parentspanid';
655
+ var X_B3_FLAGS = 'x-b3-flags';
656
+
657
+ /*
658
+ * Copyright The OpenTelemetry Authors
659
+ *
660
+ * Licensed under the Apache License, Version 2.0 (the "License");
661
+ * you may not use this file except in compliance with the License.
662
+ * You may obtain a copy of the License at
663
+ *
664
+ * https://www.apache.org/licenses/LICENSE-2.0
665
+ *
666
+ * Unless required by applicable law or agreed to in writing, software
667
+ * distributed under the License is distributed on an "AS IS" BASIS,
668
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
669
+ * See the License for the specific language governing permissions and
670
+ * limitations under the License.
671
+ */
672
+ var VALID_SAMPLED_VALUES = new Set([true, 'true', 'True', '1', 1]);
673
+ var VALID_UNSAMPLED_VALUES = new Set([false, 'false', 'False', '0', 0]);
674
+ function isValidSampledValue(sampled) {
675
+ return sampled === TraceFlags.SAMPLED || sampled === TraceFlags.NONE;
676
+ }
677
+ function parseHeader(header) {
678
+ return Array.isArray(header) ? header[0] : header;
679
+ }
680
+ function getHeaderValue(carrier, getter, key) {
681
+ var header = getter.get(carrier, key);
682
+ return parseHeader(header);
683
+ }
684
+ function getTraceId(carrier, getter) {
685
+ var traceId = getHeaderValue(carrier, getter, X_B3_TRACE_ID);
686
+ if (typeof traceId === 'string') {
687
+ return traceId.padStart(32, '0');
688
+ }
689
+ return '';
690
+ }
691
+ function getSpanId(carrier, getter) {
692
+ var spanId = getHeaderValue(carrier, getter, X_B3_SPAN_ID);
693
+ if (typeof spanId === 'string') {
694
+ return spanId;
695
+ }
696
+ return '';
697
+ }
698
+ function getDebug(carrier, getter) {
699
+ var debug = getHeaderValue(carrier, getter, X_B3_FLAGS);
700
+ return debug === '1' ? '1' : undefined;
701
+ }
702
+ function getTraceFlags(carrier, getter) {
703
+ var traceFlags = getHeaderValue(carrier, getter, X_B3_SAMPLED);
704
+ var debug = getDebug(carrier, getter);
705
+ if (debug === '1' || VALID_SAMPLED_VALUES.has(traceFlags)) {
706
+ return TraceFlags.SAMPLED;
707
+ }
708
+ if (traceFlags === undefined || VALID_UNSAMPLED_VALUES.has(traceFlags)) {
709
+ return TraceFlags.NONE;
710
+ }
711
+ // This indicates to isValidSampledValue that this is not valid
712
+ return;
713
+ }
714
+ /**
715
+ * Propagator for the B3 multiple-header HTTP format.
716
+ * Based on: https://github.com/openzipkin/b3-propagation
717
+ */
718
+ var B3MultiPropagator = /** @class */ (function () {
719
+ function B3MultiPropagator() {
720
+ }
721
+ B3MultiPropagator.prototype.inject = function (context, carrier, setter) {
722
+ var spanContext = trace.getSpanContext(context);
723
+ if (!spanContext ||
724
+ !isSpanContextValid(spanContext) ||
725
+ isTracingSuppressed(context))
726
+ return;
727
+ var debug = context.getValue(B3_DEBUG_FLAG_KEY);
728
+ setter.set(carrier, X_B3_TRACE_ID, spanContext.traceId);
729
+ setter.set(carrier, X_B3_SPAN_ID, spanContext.spanId);
730
+ // According to the B3 spec, if the debug flag is set,
731
+ // the sampled flag shouldn't be propagated as well.
732
+ if (debug === '1') {
733
+ setter.set(carrier, X_B3_FLAGS, debug);
734
+ }
735
+ else if (spanContext.traceFlags !== undefined) {
736
+ // We set the header only if there is an existing sampling decision.
737
+ // Otherwise we will omit it => Absent.
738
+ setter.set(carrier, X_B3_SAMPLED, (TraceFlags.SAMPLED & spanContext.traceFlags) === TraceFlags.SAMPLED
739
+ ? '1'
740
+ : '0');
741
+ }
742
+ };
743
+ B3MultiPropagator.prototype.extract = function (context, carrier, getter) {
744
+ var traceId = getTraceId(carrier, getter);
745
+ var spanId = getSpanId(carrier, getter);
746
+ var traceFlags = getTraceFlags(carrier, getter);
747
+ var debug = getDebug(carrier, getter);
748
+ if (isValidTraceId(traceId) &&
749
+ isValidSpanId(spanId) &&
750
+ isValidSampledValue(traceFlags)) {
751
+ context = context.setValue(B3_DEBUG_FLAG_KEY, debug);
752
+ return trace.setSpanContext(context, {
753
+ traceId: traceId,
754
+ spanId: spanId,
755
+ isRemote: true,
756
+ traceFlags: traceFlags,
757
+ });
758
+ }
759
+ return context;
760
+ };
761
+ B3MultiPropagator.prototype.fields = function () {
762
+ return [
763
+ X_B3_TRACE_ID,
764
+ X_B3_SPAN_ID,
765
+ X_B3_FLAGS,
766
+ X_B3_SAMPLED,
767
+ X_B3_PARENT_SPAN_ID,
768
+ ];
769
+ };
770
+ return B3MultiPropagator;
771
+ }());
772
+
773
+ /*
774
+ * Copyright The OpenTelemetry Authors
775
+ *
776
+ * Licensed under the Apache License, Version 2.0 (the "License");
777
+ * you may not use this file except in compliance with the License.
778
+ * You may obtain a copy of the License at
779
+ *
780
+ * https://www.apache.org/licenses/LICENSE-2.0
781
+ *
782
+ * Unless required by applicable law or agreed to in writing, software
783
+ * distributed under the License is distributed on an "AS IS" BASIS,
784
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
785
+ * See the License for the specific language governing permissions and
786
+ * limitations under the License.
787
+ */
788
+ var __read = (undefined && undefined.__read) || function (o, n) {
789
+ var m = typeof Symbol === "function" && o[Symbol.iterator];
790
+ if (!m) return o;
791
+ var i = m.call(o), r, ar = [], e;
792
+ try {
793
+ while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
794
+ }
795
+ catch (error) { e = { error: error }; }
796
+ finally {
797
+ try {
798
+ if (r && !r.done && (m = i["return"])) m.call(i);
799
+ }
800
+ finally { if (e) throw e.error; }
801
+ }
802
+ return ar;
803
+ };
804
+ var B3_CONTEXT_REGEX = /((?:[0-9a-f]{16}){1,2})-([0-9a-f]{16})(?:-([01d](?![0-9a-f])))?(?:-([0-9a-f]{16}))?/;
805
+ var PADDING = '0'.repeat(16);
806
+ var SAMPLED_VALUES = new Set(['d', '1']);
807
+ var DEBUG_STATE = 'd';
808
+ function convertToTraceId128(traceId) {
809
+ return traceId.length === 32 ? traceId : "" + PADDING + traceId;
810
+ }
811
+ function convertToTraceFlags(samplingState) {
812
+ if (samplingState && SAMPLED_VALUES.has(samplingState)) {
813
+ return TraceFlags.SAMPLED;
814
+ }
815
+ return TraceFlags.NONE;
816
+ }
817
+ /**
818
+ * Propagator for the B3 single-header HTTP format.
819
+ * Based on: https://github.com/openzipkin/b3-propagation
820
+ */
821
+ var B3SinglePropagator = /** @class */ (function () {
822
+ function B3SinglePropagator() {
823
+ }
824
+ B3SinglePropagator.prototype.inject = function (context, carrier, setter) {
825
+ var spanContext = trace.getSpanContext(context);
826
+ if (!spanContext ||
827
+ !isSpanContextValid(spanContext) ||
828
+ isTracingSuppressed(context))
829
+ return;
830
+ var samplingState = context.getValue(B3_DEBUG_FLAG_KEY) || spanContext.traceFlags & 0x1;
831
+ var value = spanContext.traceId + "-" + spanContext.spanId + "-" + samplingState;
832
+ setter.set(carrier, B3_CONTEXT_HEADER, value);
833
+ };
834
+ B3SinglePropagator.prototype.extract = function (context, carrier, getter) {
835
+ var header = getter.get(carrier, B3_CONTEXT_HEADER);
836
+ var b3Context = Array.isArray(header) ? header[0] : header;
837
+ if (typeof b3Context !== 'string')
838
+ return context;
839
+ var match = b3Context.match(B3_CONTEXT_REGEX);
840
+ if (!match)
841
+ return context;
842
+ var _a = __read(match, 4), extractedTraceId = _a[1], spanId = _a[2], samplingState = _a[3];
843
+ var traceId = convertToTraceId128(extractedTraceId);
844
+ if (!isValidTraceId(traceId) || !isValidSpanId(spanId))
845
+ return context;
846
+ var traceFlags = convertToTraceFlags(samplingState);
847
+ if (samplingState === DEBUG_STATE) {
848
+ context = context.setValue(B3_DEBUG_FLAG_KEY, samplingState);
849
+ }
850
+ return trace.setSpanContext(context, {
851
+ traceId: traceId,
852
+ spanId: spanId,
853
+ isRemote: true,
854
+ traceFlags: traceFlags,
855
+ });
856
+ };
857
+ B3SinglePropagator.prototype.fields = function () {
858
+ return [B3_CONTEXT_HEADER];
859
+ };
860
+ return B3SinglePropagator;
861
+ }());
862
+
863
+ /*
864
+ * Copyright The OpenTelemetry Authors
865
+ *
866
+ * Licensed under the Apache License, Version 2.0 (the "License");
867
+ * you may not use this file except in compliance with the License.
868
+ * You may obtain a copy of the License at
869
+ *
870
+ * https://www.apache.org/licenses/LICENSE-2.0
871
+ *
872
+ * Unless required by applicable law or agreed to in writing, software
873
+ * distributed under the License is distributed on an "AS IS" BASIS,
874
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
875
+ * See the License for the specific language governing permissions and
876
+ * limitations under the License.
877
+ */
878
+ /** Enumeration of B3 inject encodings */
879
+ var B3InjectEncoding;
880
+ (function (B3InjectEncoding) {
881
+ B3InjectEncoding[B3InjectEncoding["SINGLE_HEADER"] = 0] = "SINGLE_HEADER";
882
+ B3InjectEncoding[B3InjectEncoding["MULTI_HEADER"] = 1] = "MULTI_HEADER";
883
+ })(B3InjectEncoding || (B3InjectEncoding = {}));
884
+
885
+ /*
886
+ * Copyright The OpenTelemetry Authors
887
+ *
888
+ * Licensed under the Apache License, Version 2.0 (the "License");
889
+ * you may not use this file except in compliance with the License.
890
+ * You may obtain a copy of the License at
891
+ *
892
+ * https://www.apache.org/licenses/LICENSE-2.0
893
+ *
894
+ * Unless required by applicable law or agreed to in writing, software
895
+ * distributed under the License is distributed on an "AS IS" BASIS,
896
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
897
+ * See the License for the specific language governing permissions and
898
+ * limitations under the License.
899
+ */
900
+ /**
901
+ * Propagator that extracts B3 context in both single and multi-header variants,
902
+ * with configurable injection format defaulting to B3 single-header. Due to
903
+ * the asymmetry in injection and extraction formats this is not suitable to
904
+ * be implemented as a composite propagator.
905
+ * Based on: https://github.com/openzipkin/b3-propagation
906
+ */
907
+ var B3Propagator = /** @class */ (function () {
908
+ function B3Propagator(config) {
909
+ if (config === void 0) { config = {}; }
910
+ this._b3MultiPropagator = new B3MultiPropagator();
911
+ this._b3SinglePropagator = new B3SinglePropagator();
912
+ if (config.injectEncoding === B3InjectEncoding.MULTI_HEADER) {
913
+ this._inject = this._b3MultiPropagator.inject;
914
+ this._fields = this._b3MultiPropagator.fields();
915
+ }
916
+ else {
917
+ this._inject = this._b3SinglePropagator.inject;
918
+ this._fields = this._b3SinglePropagator.fields();
919
+ }
920
+ }
921
+ B3Propagator.prototype.inject = function (context, carrier, setter) {
922
+ if (isTracingSuppressed(context)) {
923
+ return;
924
+ }
925
+ this._inject(context, carrier, setter);
926
+ };
927
+ B3Propagator.prototype.extract = function (context, carrier, getter) {
928
+ var header = getter.get(carrier, B3_CONTEXT_HEADER);
929
+ var b3Context = Array.isArray(header) ? header[0] : header;
930
+ if (b3Context) {
931
+ return this._b3SinglePropagator.extract(context, carrier, getter);
932
+ }
933
+ else {
934
+ return this._b3MultiPropagator.extract(context, carrier, getter);
935
+ }
936
+ };
937
+ B3Propagator.prototype.fields = function () {
938
+ return this._fields;
939
+ };
940
+ return B3Propagator;
941
+ }());
942
+
943
+ /**
944
+ * Pinpoint 传播器实现
945
+ * Pinpoint TraceId 格式:serviceName^毫秒时间戳^序列号
946
+ * 例如:my-service^1704067200000^1
947
+ *
948
+ * Pinpoint 使用以下头部:
949
+ * - Pinpoint-TraceId (serviceName^毫秒时间戳^序列号)
950
+ * - Pinpoint-SpanId (8字节十六进制)
951
+ * - Pinpoint-ParentSpanId (8字节十六进制,可选)
952
+ * - Pinpoint-Sampled (1 或 0)
953
+ * - Pinpoint-Flags (通常为 0)
954
+ */
955
+ class PinpointPropagator {
956
+ constructor(serviceName = 'unknown-service') {
957
+ this.FIELDS = [
958
+ 'Pinpoint-TraceId',
959
+ 'Pinpoint-SpanId',
960
+ 'Pinpoint-ParentSpanId',
961
+ 'Pinpoint-Sampled',
962
+ 'Pinpoint-Flags',
963
+ ];
964
+ this.sequenceCounter = 0;
965
+ this.lastTimestamp = 0;
966
+ this.serviceName = serviceName;
967
+ }
968
+ fields() {
969
+ return [...this.FIELDS];
970
+ }
971
+ /**
972
+ * 生成 Pinpoint 格式的 TraceId: serviceName^毫秒时间戳^序列号
973
+ */
974
+ generatePinpointTraceId() {
975
+ const now = Date.now();
976
+ // 如果时间戳改变,重置序列号
977
+ if (now !== this.lastTimestamp) {
978
+ this.sequenceCounter = 0;
979
+ this.lastTimestamp = now;
980
+ }
981
+ // 序列号递增
982
+ this.sequenceCounter++;
983
+ // 格式:serviceName^毫秒时间戳^序列号
984
+ return `${this.serviceName}^${now}^${this.sequenceCounter}`;
985
+ }
986
+ /**
987
+ * 将字符串转换为十六进制(用于 extract 时转换)
988
+ */
989
+ stringToHex(str) {
990
+ let hex = '';
991
+ for (let i = 0; i < str.length; i++) {
992
+ const charCode = str.charCodeAt(i);
993
+ hex += charCode.toString(16).padStart(2, '0');
994
+ }
995
+ return hex;
996
+ }
997
+ inject(context, carrier, setter) {
998
+ // 从 context 中获取当前活动的 span
999
+ const span = trace.getSpan(context);
1000
+ if (!span) {
1001
+ return;
1002
+ }
1003
+ const spanContext = span.spanContext();
1004
+ if (!spanContext || spanContext.traceId === '00000000000000000000000000000000') {
1005
+ return;
1006
+ }
1007
+ const { spanId, traceFlags } = spanContext;
1008
+ // 生成 Pinpoint 格式的 TraceId: serviceName^毫秒时间戳^序列号
1009
+ const pinpointTraceId = this.generatePinpointTraceId();
1010
+ // 将 Pinpoint traceId 添加到 span 属性中,这样在导出时会包含在 traces 数据中
1011
+ span.setAttribute('pinpoint.trace_id', pinpointTraceId);
1012
+ setter.set(carrier, 'Pinpoint-TraceId', pinpointTraceId);
1013
+ setter.set(carrier, 'Pinpoint-SpanId', spanId);
1014
+ setter.set(carrier, 'Pinpoint-Sampled', traceFlags === TraceFlags.SAMPLED ? '1' : '0');
1015
+ setter.set(carrier, 'Pinpoint-Flags', '0');
1016
+ }
1017
+ extract(context, carrier, getter) {
1018
+ const traceId = getter.get(carrier, 'Pinpoint-TraceId');
1019
+ const spanId = getter.get(carrier, 'Pinpoint-SpanId');
1020
+ const sampled = getter.get(carrier, 'Pinpoint-Sampled');
1021
+ if (!traceId || !spanId) {
1022
+ return context;
1023
+ }
1024
+ // Pinpoint TraceId 格式:serviceName^毫秒时间戳^序列号
1025
+ // 需要转换为 OpenTelemetry 的 32 字符十六进制格式
1026
+ let otelTraceId;
1027
+ if (traceId.includes('^')) {
1028
+ // Pinpoint 格式,需要转换
1029
+ // 使用哈希方式:将字符串转换为十六进制
1030
+ const hash = this.stringToHex(traceId);
1031
+ // 确保是 32 字符(如果超过则截取,如果不足则补0)
1032
+ otelTraceId = hash.length >= 32
1033
+ ? hash.substring(0, 32)
1034
+ : hash.padEnd(32, '0');
1035
+ }
1036
+ else {
1037
+ // 可能是旧的十六进制格式,直接使用
1038
+ otelTraceId = traceId.length === 16
1039
+ ? '0000000000000000' + traceId
1040
+ : traceId.padStart(32, '0').substring(0, 32);
1041
+ }
1042
+ const spanContext = {
1043
+ traceId: otelTraceId,
1044
+ spanId: spanId,
1045
+ traceFlags: sampled === '1' ? TraceFlags.SAMPLED : TraceFlags.NONE,
1046
+ isRemote: true,
1047
+ };
1048
+ // 使用 trace API 设置 span context
1049
+ return trace.setSpanContext(context, spanContext);
1050
+ }
1051
+ }
1052
+ /**
1053
+ * 创建传播器
1054
+ */
1055
+ function createPropagators(options) {
1056
+ const propagators = [];
1057
+ // 注意:如果用户明确配置了 propagationFormat,不应该使用默认值
1058
+ const formats = options.propagationFormat !== undefined
1059
+ ? options.propagationFormat
1060
+ : 'w3c';
1061
+ // 处理单个格式或格式数组
1062
+ const formatList = Array.isArray(formats) ? formats : [formats];
1063
+ for (const format of formatList) {
1064
+ const formatLower = format.toLowerCase();
1065
+ switch (formatLower) {
1066
+ case 'w3c':
1067
+ propagators.push(new W3CTraceContextPropagator());
1068
+ break;
1069
+ case 'b3':
1070
+ case 'b3multi':
1071
+ // B3 多头部格式
1072
+ propagators.push(new B3Propagator({
1073
+ injectEncoding: B3InjectEncoding.MULTI_HEADER,
1074
+ }));
1075
+ break;
1076
+ case 'pinpoint':
1077
+ // 传递 serviceName 给 PinpointPropagator
1078
+ propagators.push(new PinpointPropagator(options.serviceName || 'unknown-service'));
1079
+ break;
1080
+ default:
1081
+ // 未知格式,使用 W3C 作为后备
1082
+ propagators.push(new W3CTraceContextPropagator());
1083
+ }
1084
+ }
1085
+ // 添加自定义传播器
1086
+ if (options.propagators?.customPropagators) {
1087
+ propagators.push(...options.propagators.customPropagators);
1088
+ }
1089
+ // 如果只有一个传播器,直接返回;否则使用组合传播器
1090
+ if (propagators.length === 0) {
1091
+ return new W3CTraceContextPropagator();
1092
+ }
1093
+ else if (propagators.length === 1) {
1094
+ return propagators[0];
1095
+ }
1096
+ else {
1097
+ // 使用 CompositePropagator 组合多个传播器
1098
+ return new CompositePropagator({
1099
+ propagators: propagators,
1100
+ });
1101
+ }
1102
+ }
1103
+
1104
+ /**
1105
+ * SDK 实例
1106
+ */
1107
+ class OTelSDK {
1108
+ constructor() {
1109
+ this.provider = null;
1110
+ this.tracer = null;
1111
+ this.config = null;
1112
+ this.initialized = false;
1113
+ this.logger = new Logger('info');
1114
+ }
1115
+ /**
1116
+ * 初始化 SDK
1117
+ */
1118
+ init(options) {
1119
+ if (this.initialized) {
1120
+ this.logger.warn('SDK already initialized, skipping...');
1121
+ return;
1122
+ }
1123
+ try {
1124
+ this.config = mergeConfig(options);
1125
+ this.logger.setLevel(this.config.logLevel || 'info');
1126
+ this.logger.debug('Initializing SDK with config:', this.config);
1127
+ // 1. 创建资源
1128
+ const resource = resourceFromAttributes({
1129
+ [ATTR_SERVICE_NAME]: this.config.serviceName || 'unknown-service',
1130
+ [ATTR_SERVICE_VERSION]: this.config.serviceVersion || '1.0.0',
1131
+ 'deployment.environment': this.config.environment || 'production',
1132
+ 'datacenter': this.config.datacenter || 'dc1',
1133
+ });
1134
+ // 2. 创建 OTLP exporter
1135
+ const exporter = new OTLPTraceExporter({
1136
+ url: this.config.collectorEndpoint,
1137
+ headers: {
1138
+ 'Content-Type': 'application/json',
1139
+ },
1140
+ });
1141
+ // 3. 创建 BatchSpanProcessor
1142
+ const batchConfig = this.config.batchConfig || {};
1143
+ const spanProcessor = new BatchSpanProcessor(exporter, {
1144
+ maxQueueSize: batchConfig.maxQueueSize || 2048,
1145
+ maxExportBatchSize: batchConfig.maxExportBatchSize || 512,
1146
+ scheduledDelayMillis: batchConfig.scheduledDelayMillis || 5000,
1147
+ exportTimeoutMillis: batchConfig.exportTimeoutMillis || 30000,
1148
+ });
1149
+ // 4. 创建传播器(在 provider 注册之前)
1150
+ const propagator = createPropagators(this.config);
1151
+ const propagationFormat = Array.isArray(this.config.propagationFormat)
1152
+ ? this.config.propagationFormat.join(',')
1153
+ : (this.config.propagationFormat || 'w3c');
1154
+ this.logger.debug(`Propagation format: ${propagationFormat}`);
1155
+ // 5. 先设置全局传播器(必须在 provider.register() 之前)
1156
+ // 这样可以确保 provider.register() 不会覆盖我们的传播器设置
1157
+ propagation.setGlobalPropagator(propagator);
1158
+ this.logger.debug(`Global propagator set with ${propagationFormat} format(s)`);
1159
+ // 6. 创建 TracerProvider
1160
+ this.provider = new WebTracerProvider({
1161
+ resource: resource,
1162
+ spanProcessors: [spanProcessor],
1163
+ });
1164
+ // 7. 注册 provider(此时全局传播器已经设置,不会被覆盖)
1165
+ this.provider.register();
1166
+ // 8. Provider 已注册,传播器已设置
1167
+ // 9. 获取 tracer
1168
+ this.tracer = this.provider.getTracer(this.config.serviceName || 'unknown-service', this.config.serviceVersion || '1.0.0');
1169
+ // 10. 注册自动 instrumentation(传播器已设置,instrumentation 会使用全局传播器)
1170
+ if (this.config.enableAutoInstrumentation) {
1171
+ const instrumentations = createInstrumentations(this.config);
1172
+ if (instrumentations.length > 0) {
1173
+ registerInstrumentations({
1174
+ instrumentations: instrumentations,
1175
+ });
1176
+ this.logger.debug(`Registered ${instrumentations.length} instrumentations`);
1177
+ }
1178
+ }
1179
+ // 11. 设置 Vue Router instrumentation(如果提供)
1180
+ if (this.config.framework === 'vue' && this.config.router && this.tracer) {
1181
+ setupVueRouterInstrumentation(this.config.router, this.tracer);
1182
+ this.logger.debug('Vue Router instrumentation enabled');
1183
+ }
1184
+ // 12. 设置全局错误捕获
1185
+ this.setupErrorHandling();
1186
+ // 13. 页面卸载时刷新
1187
+ if (typeof window !== 'undefined') {
1188
+ window.addEventListener('beforeunload', () => {
1189
+ this.forceFlush();
1190
+ });
1191
+ }
1192
+ this.initialized = true;
1193
+ this.logger.info('SDK initialized successfully');
1194
+ this.logger.info(`Service: ${this.config.serviceName}`);
1195
+ this.logger.info(`Collector: ${this.config.collectorEndpoint}`);
1196
+ }
1197
+ catch (error) {
1198
+ this.logger.error('Failed to initialize SDK:', error);
1199
+ throw error;
1200
+ }
1201
+ }
1202
+ /**
1203
+ * 设置全局错误处理
1204
+ */
1205
+ setupErrorHandling() {
1206
+ if (typeof window === 'undefined') {
1207
+ return;
1208
+ }
1209
+ // 捕获全局错误
1210
+ window.addEventListener('error', (event) => {
1211
+ const activeSpan = trace.getActiveSpan();
1212
+ if (activeSpan) {
1213
+ activeSpan.recordException(event.error || new Error(event.message));
1214
+ activeSpan.setStatus({ code: 2, message: event.message });
1215
+ }
1216
+ });
1217
+ // 捕获未处理的 Promise 拒绝
1218
+ window.addEventListener('unhandledrejection', (event) => {
1219
+ const activeSpan = trace.getActiveSpan();
1220
+ if (activeSpan) {
1221
+ activeSpan.recordException(event.reason);
1222
+ activeSpan.setStatus({ code: 2, message: String(event.reason) });
1223
+ }
1224
+ });
1225
+ }
1226
+ /**
1227
+ * 强制刷新所有 spans
1228
+ */
1229
+ forceFlush() {
1230
+ if (!this.provider) {
1231
+ return Promise.resolve();
1232
+ }
1233
+ return this.provider.forceFlush();
1234
+ }
1235
+ /**
1236
+ * 获取 tracer
1237
+ */
1238
+ getTracer(name, version) {
1239
+ if (!this.tracer) {
1240
+ throw new Error('SDK not initialized. Call init() first.');
1241
+ }
1242
+ return this.tracer;
1243
+ }
1244
+ /**
1245
+ * 获取当前配置
1246
+ */
1247
+ getConfig() {
1248
+ return this.config;
1249
+ }
1250
+ /**
1251
+ * 获取 logger
1252
+ */
1253
+ getLogger() {
1254
+ return this.logger;
1255
+ }
1256
+ /**
1257
+ * 检查是否已初始化
1258
+ */
1259
+ isInitialized() {
1260
+ return this.initialized;
1261
+ }
1262
+ }
1263
+ // 单例实例
1264
+ let sdkInstance = null;
1265
+ /**
1266
+ * 获取 SDK 实例
1267
+ */
1268
+ function getSDK() {
1269
+ if (!sdkInstance) {
1270
+ sdkInstance = new OTelSDK();
1271
+ }
1272
+ return sdkInstance;
1273
+ }
1274
+ /**
1275
+ * 初始化 SDK
1276
+ */
1277
+ function initOTel(options) {
1278
+ const sdk = getSDK();
1279
+ sdk.init(options);
1280
+ }
1281
+ /**
1282
+ * 获取 tracer
1283
+ */
1284
+ function getTracer(name, version) {
1285
+ const sdk = getSDK();
1286
+ return sdk.getTracer(name, version);
1287
+ }
1288
+ /**
1289
+ * 强制刷新
1290
+ */
1291
+ function forceFlush() {
1292
+ const sdk = getSDK();
1293
+ return sdk.forceFlush();
1294
+ }
1295
+
1296
+ /**
1297
+ * Trace API - 简化的追踪接口
1298
+ */
1299
+ const traceAPI = {
1300
+ /**
1301
+ * 获取 tracer
1302
+ */
1303
+ getTracer(name, version) {
1304
+ return getTracer(name, version);
1305
+ },
1306
+ /**
1307
+ * 创建并启动一个 span
1308
+ */
1309
+ startSpan(name, options) {
1310
+ const tracer = getTracer();
1311
+ const span = tracer.startSpan(name, {
1312
+ attributes: options?.attributes || {},
1313
+ kind: options?.kind === 'internal' ? 0 :
1314
+ options?.kind === 'server' ? 1 :
1315
+ options?.kind === 'client' ? 2 :
1316
+ options?.kind === 'producer' ? 3 :
1317
+ options?.kind === 'consumer' ? 4 : 0,
1318
+ startTime: options?.startTime,
1319
+ });
1320
+ return span;
1321
+ },
1322
+ /**
1323
+ * 在上下文中执行函数并创建 span
1324
+ */
1325
+ withSpan(name, fn, options) {
1326
+ const span = traceAPI.startSpan(name, options);
1327
+ return context.with(trace.setSpan(context.active(), span), () => {
1328
+ try {
1329
+ const result = fn(span);
1330
+ if (result instanceof Promise) {
1331
+ return result
1332
+ .then((value) => {
1333
+ span.setStatus({ code: 1 }); // OK
1334
+ span.end();
1335
+ return value;
1336
+ })
1337
+ .catch((error) => {
1338
+ span.setStatus({ code: 2, message: error.message }); // ERROR
1339
+ span.recordException(error);
1340
+ span.end();
1341
+ throw error;
1342
+ });
1343
+ }
1344
+ else {
1345
+ span.setStatus({ code: 1 }); // OK
1346
+ span.end();
1347
+ return result;
1348
+ }
1349
+ }
1350
+ catch (error) {
1351
+ span.setStatus({ code: 2, message: error?.message || 'Unknown error' }); // ERROR
1352
+ span.recordException(error);
1353
+ span.end();
1354
+ throw error;
1355
+ }
1356
+ });
1357
+ },
1358
+ /**
1359
+ * 异步执行函数并创建 span
1360
+ */
1361
+ async withSpanAsync(name, fn, options) {
1362
+ const span = traceAPI.startSpan(name, options);
1363
+ return context.with(trace.setSpan(context.active(), span), async () => {
1364
+ try {
1365
+ const result = await fn(span);
1366
+ span.setStatus({ code: 1 }); // OK
1367
+ span.end();
1368
+ return result;
1369
+ }
1370
+ catch (error) {
1371
+ span.setStatus({ code: 2, message: error?.message || 'Unknown error' }); // ERROR
1372
+ span.recordException(error);
1373
+ span.end();
1374
+ throw error;
1375
+ }
1376
+ });
1377
+ },
1378
+ /**
1379
+ * 获取当前活动的 span
1380
+ */
1381
+ getActiveSpan() {
1382
+ return trace.getActiveSpan();
1383
+ },
1384
+ /**
1385
+ * 获取当前 trace ID
1386
+ */
1387
+ getCurrentTraceId() {
1388
+ const span = trace.getActiveSpan();
1389
+ if (!span) {
1390
+ return undefined;
1391
+ }
1392
+ const spanContext = span.spanContext();
1393
+ return spanContext.traceId;
1394
+ },
1395
+ /**
1396
+ * 获取当前 span ID
1397
+ */
1398
+ getCurrentSpanId() {
1399
+ const span = trace.getActiveSpan();
1400
+ if (!span) {
1401
+ return undefined;
1402
+ }
1403
+ const spanContext = span.spanContext();
1404
+ return spanContext.spanId;
1405
+ },
1406
+ };
1407
+ // 默认导出
1408
+ var index = {
1409
+ init: initOTel,
1410
+ trace: traceAPI,
1411
+ getTracer,
1412
+ forceFlush,
1413
+ };
1414
+ // 为了兼容 CDN 方式,在浏览器环境中暴露到 window
1415
+ if (typeof window !== 'undefined') {
1416
+ window.OTelSDK = {
1417
+ init: initOTel,
1418
+ trace: traceAPI,
1419
+ getTracer,
1420
+ forceFlush,
1421
+ };
1422
+ }
1423
+
1424
+ export { index as default, forceFlush, getTracer, initOTel, traceAPI as trace, traceAPI };
1425
+ //# sourceMappingURL=index.esm.js.map