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