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.
- package/README.md +347 -0
- package/dist/config.d.ts +19 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/index.d.ts +76 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.esm.js +1425 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/index.js +1434 -0
- package/dist/index.js.map +1 -0
- package/dist/index.umd.js +1428 -0
- package/dist/index.umd.js.map +1 -0
- package/dist/instrumentations.d.ts +12 -0
- package/dist/instrumentations.d.ts.map +1 -0
- package/dist/propagators.d.ts +7 -0
- package/dist/propagators.d.ts.map +1 -0
- package/dist/sdk.d.ts +60 -0
- package/dist/sdk.d.ts.map +1 -0
- package/dist/types.d.ts +100 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +63 -0
|
@@ -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
|