@0xchain/telemetry 1.1.0-beta.17 → 1.1.0-beta.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,395 +1,609 @@
1
- import { NodeSDK as F } from "@opentelemetry/sdk-node";
2
- import { OTLPTraceExporter as Y } from "@opentelemetry/exporter-trace-otlp-http";
3
- import { resourceFromAttributes as U } from "@opentelemetry/resources";
4
- import { ATTR_SERVICE_VERSION as G, ATTR_SERVICE_NAME as q } from "@opentelemetry/semantic-conventions";
5
- import { SamplingDecision as d, TraceIdRatioBasedSampler as L, AlwaysOnSampler as B, BatchSpanProcessor as k } from "@opentelemetry/sdk-trace-base";
6
- import { trace as z, TraceFlags as M, propagation as V, context as x, SpanStatusCode as $ } from "@opentelemetry/api";
7
- import { readFile as j } from "node:fs/promises";
8
- import { HttpInstrumentation as Q } from "@opentelemetry/instrumentation-http";
9
- import { UndiciInstrumentation as X } from "@opentelemetry/instrumentation-undici";
10
- import { c as W, m as J, a as K, d as Z } from "./ignore-CMi2r3b2.js";
11
- const tt = "deployment.environment.name", T = (t, e) => {
12
- if (!t) return;
13
- const r = t[e.toLowerCase()];
14
- if (Array.isArray(r)) {
15
- const n = r[0];
16
- return typeof n == "string" ? n : void 0;
17
- }
18
- return typeof r == "string" ? r : void 0;
19
- }, R = (t) => {
20
- if (!t) return "";
1
+ import { NodeSDK } from "@opentelemetry/sdk-node";
2
+ import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
3
+ import { resourceFromAttributes } from "@opentelemetry/resources";
4
+ import { ATTR_SERVICE_VERSION, ATTR_SERVICE_NAME } from "@opentelemetry/semantic-conventions";
5
+ import { ATTR_DEPLOYMENT_ENVIRONMENT_NAME } from "@opentelemetry/semantic-conventions/incubating";
6
+ import { SamplingDecision, TraceIdRatioBasedSampler, AlwaysOnSampler, BatchSpanProcessor } from "@opentelemetry/sdk-trace-base";
7
+ import { trace, TraceFlags, propagation, context, SpanStatusCode } from "@opentelemetry/api";
8
+ import { readFile } from "node:fs/promises";
9
+ import { HttpInstrumentation } from "@opentelemetry/instrumentation-http";
10
+ import { UndiciInstrumentation } from "@opentelemetry/instrumentation-undici";
11
+ import { c as createIgnoreMatcher, m as matchesPathPattern, a as createSpanNameMatcher, d as defaultIgnoreConfig } from "./ignore-BvHZTNsF.js";
12
+ const getHeaderValue = (headers, name) => {
13
+ if (!headers) return void 0;
14
+ const value = headers[name.toLowerCase()];
15
+ if (Array.isArray(value)) {
16
+ const first = value[0];
17
+ return typeof first === "string" ? first : void 0;
18
+ }
19
+ return typeof value === "string" ? value : void 0;
20
+ };
21
+ const getPathname = (url) => {
22
+ if (!url) return "";
21
23
  try {
22
- return t.startsWith("http://") || t.startsWith("https://") ? new URL(t).pathname : new URL(t, "http://localhost").pathname;
24
+ if (url.startsWith("http://") || url.startsWith("https://")) {
25
+ return new URL(url).pathname;
26
+ }
27
+ return new URL(url, "http://localhost").pathname;
23
28
  } catch {
24
- return t.split("?")[0] || t;
29
+ return url.split("?")[0] || url;
25
30
  }
26
- }, O = /* @__PURE__ */ new WeakMap();
27
- function et(t) {
28
- const e = W(t.ignore), r = (t.inspectionHeader ?? "x-inspection").toLowerCase(), n = (t.userTypeHeader ?? "x-telemetry-user-type").toLowerCase(), s = (t.requestStartHeader ?? "x-telemetry-start").toLowerCase();
31
+ };
32
+ const spanStartTime = /* @__PURE__ */ new WeakMap();
33
+ function getInstrumentations(config) {
34
+ const shouldIgnore = createIgnoreMatcher(config.ignore);
35
+ const inspectionHeader = (config.inspectionHeader ?? "x-inspection").toLowerCase();
36
+ const userTypeHeader = (config.userTypeHeader ?? "x-telemetry-user-type").toLowerCase();
37
+ const requestStartHeader = (config.requestStartHeader ?? "x-telemetry-start").toLowerCase();
29
38
  return [
30
- new Q({
31
- startIncomingSpanHook: (o) => {
32
- const i = "headers" in o ? o.headers : void 0, a = T(i, r), c = T(i, n), p = T(i, s), u = "method" in o ? o.method : void 0, m = R("url" in o ? o.url : void 0), f = Number(p || Date.now()), E = {
33
- "telemetry.request.start_time_ms": Number.isFinite(f) ? f : Date.now()
39
+ new HttpInstrumentation({
40
+ startIncomingSpanHook: (request) => {
41
+ const headers = "headers" in request ? request.headers : void 0;
42
+ const inspectionValue = getHeaderValue(headers, inspectionHeader);
43
+ const userTypeValue = getHeaderValue(headers, userTypeHeader);
44
+ const startTimeValue = getHeaderValue(headers, requestStartHeader);
45
+ const method = "method" in request ? request.method : void 0;
46
+ const pathname = getPathname("url" in request ? request.url : void 0);
47
+ const startTime = Number(startTimeValue || Date.now());
48
+ const attributes = {
49
+ "telemetry.request.start_time_ms": Number.isFinite(startTime) ? startTime : Date.now()
34
50
  };
35
- return m && (E["url.path"] = m), u && (E["http.request.method"] = u), a && (E["telemetry.request.inspection"] = !0, E["http.request.header.x-inspection"] = a), c && (E["enduser.type"] = c, E["telemetry.request.user_type"] = c), E;
51
+ if (pathname) {
52
+ attributes["url.path"] = pathname;
53
+ }
54
+ if (method) {
55
+ attributes["http.request.method"] = method;
56
+ }
57
+ if (inspectionValue) {
58
+ attributes["telemetry.request.inspection"] = true;
59
+ attributes["http.request.header.x-inspection"] = inspectionValue;
60
+ }
61
+ if (userTypeValue) {
62
+ attributes["enduser.type"] = userTypeValue;
63
+ attributes["telemetry.request.user_type"] = userTypeValue;
64
+ }
65
+ return attributes;
36
66
  },
37
- requestHook: (o, i) => {
38
- const a = "headers" in i ? i.headers : void 0, c = T(a, s), p = Number(c || Date.now());
39
- O.set(o, Number.isFinite(p) ? p : Date.now());
40
- const u = "method" in i && i.method || "GET", m = R("url" in i ? i.url : void 0);
41
- m && o.updateName(`${u} ${m}`);
67
+ requestHook: (span, request) => {
68
+ const headers = "headers" in request ? request.headers : void 0;
69
+ const startTimeValue = getHeaderValue(headers, requestStartHeader);
70
+ const startTime = Number(startTimeValue || Date.now());
71
+ spanStartTime.set(span, Number.isFinite(startTime) ? startTime : Date.now());
72
+ const method = "method" in request ? request.method || "GET" : "GET";
73
+ const pathname = getPathname("url" in request ? request.url : void 0);
74
+ if (pathname) {
75
+ span.updateName(`${method} ${pathname}`);
76
+ }
42
77
  },
43
- responseHook: (o, i) => {
44
- const a = "statusCode" in i ? i.statusCode : void 0;
45
- typeof a == "number" && (o.setAttribute("http.response.status_code", a), o.setAttribute("telemetry.response.status_code", a), a >= 500 && o.setAttribute("telemetry.request.error", !0));
46
- const c = O.get(o);
47
- typeof c == "number" && o.setAttribute("telemetry.request.duration_ms", Date.now() - c);
78
+ responseHook: (span, response) => {
79
+ const statusCode = "statusCode" in response ? response.statusCode : void 0;
80
+ if (typeof statusCode === "number") {
81
+ span.setAttribute("http.response.status_code", statusCode);
82
+ span.setAttribute("telemetry.response.status_code", statusCode);
83
+ if (statusCode >= 500) {
84
+ span.setAttribute("telemetry.request.error", true);
85
+ }
86
+ }
87
+ const startTime = spanStartTime.get(span);
88
+ if (typeof startTime === "number") {
89
+ span.setAttribute("telemetry.request.duration_ms", Date.now() - startTime);
90
+ }
48
91
  },
49
- ignoreIncomingRequestHook: (o) => {
50
- const i = "url" in o ? o.url : void 0, a = R(i), c = "method" in o ? o.method : void 0;
51
- return e(a, c);
92
+ ignoreIncomingRequestHook: (request) => {
93
+ const url = "url" in request ? request.url : void 0;
94
+ const pathname = getPathname(url);
95
+ const method = "method" in request ? request.method : void 0;
96
+ return shouldIgnore(pathname, method);
52
97
  },
53
- ignoreOutgoingRequestHook: (o) => {
54
- const i = typeof o == "string" ? o : o.path || "";
55
- return e(R(i));
98
+ ignoreOutgoingRequestHook: (request) => {
99
+ const url = typeof request === "string" ? request : request.path || "";
100
+ return shouldIgnore(getPathname(url));
56
101
  }
57
102
  }),
58
- new X()
103
+ new UndiciInstrumentation()
59
104
  ];
60
105
  }
61
- const S = (t) => {
62
- if (typeof t == "string") return t;
63
- if (Array.isArray(t)) {
64
- const e = t[0];
65
- return typeof e == "string" ? e : void 0;
66
- }
67
- }, H = (t) => {
68
- if (typeof t == "number") return t;
69
- if (typeof t == "string") {
70
- const e = Number(t);
71
- return Number.isFinite(e) ? e : void 0;
72
- }
73
- if (Array.isArray(t)) {
74
- const e = t[0];
75
- return H(e);
76
- }
77
- }, l = (t, e) => {
78
- for (const r of e)
79
- if (r in t)
80
- return t[r];
81
- }, rt = (t) => {
82
- const e = l(t, ["url.path", "http.target", "http.route"]), r = l(t, ["http.request.method", "http.method"]), n = l(t, ["enduser.type", "telemetry.request.user_type"]), s = l(t, [
106
+ const toStringValue = (value) => {
107
+ if (typeof value === "string") return value;
108
+ if (Array.isArray(value)) {
109
+ const first = value[0];
110
+ return typeof first === "string" ? first : void 0;
111
+ }
112
+ return void 0;
113
+ };
114
+ const toNumberValue = (value) => {
115
+ if (typeof value === "number") return value;
116
+ if (typeof value === "string") {
117
+ const parsed = Number(value);
118
+ return Number.isFinite(parsed) ? parsed : void 0;
119
+ }
120
+ if (Array.isArray(value)) {
121
+ const first = value[0];
122
+ return toNumberValue(first);
123
+ }
124
+ return void 0;
125
+ };
126
+ const pickAttribute = (attributes, keys) => {
127
+ for (const key of keys) {
128
+ if (key in attributes) {
129
+ return attributes[key];
130
+ }
131
+ }
132
+ return void 0;
133
+ };
134
+ const buildSamplingContextFromAttributes = (attributes) => {
135
+ const pathValue = pickAttribute(attributes, ["url.path", "http.target", "http.route"]);
136
+ const methodValue = pickAttribute(attributes, ["http.request.method", "http.method"]);
137
+ const userTypeValue = pickAttribute(attributes, ["enduser.type", "telemetry.request.user_type"]);
138
+ const statusValue = pickAttribute(attributes, [
83
139
  "http.response.status_code",
84
140
  "http.status_code",
85
141
  "telemetry.response.status_code"
86
- ]), o = l(t, [
142
+ ]);
143
+ const inspectionValue = pickAttribute(attributes, [
87
144
  "telemetry.request.inspection",
88
145
  "http.request.header.x-inspection",
89
146
  "x-inspection"
90
- ]), i = o === !0 || o === "true" || o === "1" || o === 1;
147
+ ]);
148
+ const inspection = inspectionValue === true || inspectionValue === "true" || inspectionValue === "1" || inspectionValue === 1;
91
149
  return {
92
- path: S(e),
93
- method: S(r),
94
- userType: S(n),
95
- statusCode: H(s),
96
- inspection: i
150
+ path: toStringValue(pathValue),
151
+ method: toStringValue(methodValue),
152
+ userType: toStringValue(userTypeValue),
153
+ statusCode: toNumberValue(statusValue),
154
+ inspection
97
155
  };
98
- }, A = (t, e) => !t || !(e != null && e.length) ? !1 : e.some((r) => r.toLowerCase() === t.toLowerCase()), nt = (t, e) => !t || !(e != null && e.length) ? !1 : e.some((r) => J(r, t)), ot = (t, e) => {
99
- const r = t.when;
100
- if (!r) return !0;
101
- if (r.inspection !== void 0 && r.inspection !== e.inspection || r.path && !nt(e.path, r.path) || r.method && !A(e.method, r.method) || r.userType && !A(e.userType, r.userType))
102
- return !1;
103
- if (r.statusCode && typeof e.statusCode == "number") {
104
- if (!r.statusCode.includes(e.statusCode))
105
- return !1;
106
- } else if (r.statusCode && r.statusCode.length)
107
- return !1;
108
- return !(typeof r.minStatusCode == "number" && (typeof e.statusCode != "number" || e.statusCode < r.minStatusCode) || typeof r.maxStatusCode == "number" && (typeof e.statusCode != "number" || e.statusCode > r.maxStatusCode));
109
- }, y = (t) => Math.max(0, Math.min(1, t)), st = (t, e, r) => {
110
- if (!(t != null && t.length)) return y(r);
111
- for (const n of t)
112
- if (ot(n, e))
113
- return y(n.ratio);
114
- return y(r);
115
- }, w = /* @__PURE__ */ new Map(), it = (t) => {
116
- if (t <= 0)
117
- return new L(0);
118
- if (t >= 1)
119
- return new B();
120
- const e = Number(t.toFixed(6)), r = w.get(e);
121
- if (r) return r;
122
- const n = new L(e);
123
- return w.set(e, n), n;
124
- }, at = (t) => {
125
- let e, r;
156
+ };
157
+ const matchStringList = (value, list) => {
158
+ if (!value || !list?.length) return false;
159
+ return list.some((item) => item.toLowerCase() === value.toLowerCase());
160
+ };
161
+ const matchPathList = (path, list) => {
162
+ if (!path || !list?.length) return false;
163
+ return list.some((pattern) => matchesPathPattern(pattern, path));
164
+ };
165
+ const matchSamplingRule = (rule, context2) => {
166
+ const condition = rule.when;
167
+ if (!condition) return true;
168
+ if (condition.inspection !== void 0 && condition.inspection !== context2.inspection) {
169
+ return false;
170
+ }
171
+ if (condition.path && !matchPathList(context2.path, condition.path)) {
172
+ return false;
173
+ }
174
+ if (condition.method && !matchStringList(context2.method, condition.method)) {
175
+ return false;
176
+ }
177
+ if (condition.userType && !matchStringList(context2.userType, condition.userType)) {
178
+ return false;
179
+ }
180
+ if (condition.statusCode && typeof context2.statusCode === "number") {
181
+ if (!condition.statusCode.includes(context2.statusCode)) {
182
+ return false;
183
+ }
184
+ } else if (condition.statusCode && condition.statusCode.length) {
185
+ return false;
186
+ }
187
+ if (typeof condition.minStatusCode === "number") {
188
+ if (typeof context2.statusCode !== "number" || context2.statusCode < condition.minStatusCode) {
189
+ return false;
190
+ }
191
+ }
192
+ if (typeof condition.maxStatusCode === "number") {
193
+ if (typeof context2.statusCode !== "number" || context2.statusCode > condition.maxStatusCode) {
194
+ return false;
195
+ }
196
+ }
197
+ return true;
198
+ };
199
+ const clampRatio = (ratio) => Math.max(0, Math.min(1, ratio));
200
+ const resolveSamplingRatio = (rules, context2, defaultRatio) => {
201
+ if (!rules?.length) return clampRatio(defaultRatio);
202
+ for (const rule of rules) {
203
+ if (matchSamplingRule(rule, context2)) {
204
+ return clampRatio(rule.ratio);
205
+ }
206
+ }
207
+ return clampRatio(defaultRatio);
208
+ };
209
+ const samplerCache = /* @__PURE__ */ new Map();
210
+ const getSamplerForRatio = (ratio) => {
211
+ if (ratio <= 0) {
212
+ return new TraceIdRatioBasedSampler(0);
213
+ }
214
+ if (ratio >= 1) {
215
+ return new AlwaysOnSampler();
216
+ }
217
+ const normalized = Number(ratio.toFixed(6));
218
+ const cached = samplerCache.get(normalized);
219
+ if (cached) return cached;
220
+ const sampler = new TraceIdRatioBasedSampler(normalized);
221
+ samplerCache.set(normalized, sampler);
222
+ return sampler;
223
+ };
224
+ const getSpanNameMatcher = (config, cache) => {
225
+ if (config !== cache.ignore) {
226
+ cache.ignore = config;
227
+ cache.matcher = createSpanNameMatcher(config);
228
+ }
229
+ return cache.matcher;
230
+ };
231
+ const createRuleBasedSampler = (runtime2) => {
232
+ const matcherCache = {};
126
233
  return {
127
- shouldSample(n, s, o, i, a, c) {
128
- const p = z.getSpanContext(n);
129
- if (p)
130
- return (p.traceFlags & M.SAMPLED) === M.SAMPLED ? { decision: d.RECORD_AND_SAMPLED } : { decision: d.NOT_RECORD };
131
- const u = t.getConfig();
132
- if (u.ignore !== e && (e = u.ignore, r = K(u.ignore)), r != null && r(o))
133
- return { decision: d.NOT_RECORD };
134
- const m = rt(a);
135
- if (m.inspection)
136
- return { decision: d.RECORD_AND_SAMPLED };
137
- const f = st(
138
- u.samplingRules,
139
- m,
140
- u.defaultSamplingRatio ?? 0.01
234
+ shouldSample(context2, traceId, spanName, spanKind, attributes, links) {
235
+ const parentContext = trace.getSpanContext(context2);
236
+ if (parentContext) {
237
+ if (parentContext.isRemote) {
238
+ const config2 = runtime2.getConfig();
239
+ const matcher2 = getSpanNameMatcher(config2.ignore, matcherCache);
240
+ if (matcher2?.(spanName)) {
241
+ return { decision: SamplingDecision.NOT_RECORD };
242
+ }
243
+ }
244
+ if ((parentContext.traceFlags & TraceFlags.SAMPLED) === TraceFlags.SAMPLED) {
245
+ return { decision: SamplingDecision.RECORD_AND_SAMPLED };
246
+ }
247
+ return { decision: SamplingDecision.NOT_RECORD };
248
+ }
249
+ const config = runtime2.getConfig();
250
+ const matcher = getSpanNameMatcher(config.ignore, matcherCache);
251
+ if (matcher?.(spanName)) {
252
+ return { decision: SamplingDecision.NOT_RECORD };
253
+ }
254
+ const samplingContext = buildSamplingContextFromAttributes(attributes);
255
+ if (samplingContext.inspection) {
256
+ return { decision: SamplingDecision.RECORD_AND_SAMPLED };
257
+ }
258
+ const ratio = resolveSamplingRatio(
259
+ config.samplingRules,
260
+ samplingContext,
261
+ config.defaultSamplingRatio ?? 0.01
141
262
  );
142
- return it(f).shouldSample(n, s, o, i, a, c);
263
+ return getSamplerForRatio(ratio).shouldSample(context2, traceId, spanName, spanKind, attributes, links);
143
264
  },
144
265
  toString: () => "RuleBasedSampler"
145
266
  };
146
- }, ct = (t) => t;
147
- let _, h;
148
- const g = (t) => Array.from(new Set(t)), N = (t) => (t == null ? void 0 : t.split(",").map((e) => e.trim()).filter(Boolean)) ?? [], ut = (t) => {
149
- if (!t) return;
150
- const e = {};
151
- for (const r of t.split(",")) {
152
- const [n, ...s] = r.split("="), o = n == null ? void 0 : n.trim();
153
- o && (e[o] = s.join("=").trim());
154
- }
155
- return e;
156
- }, C = (t) => {
157
- if (t)
158
- try {
159
- return JSON.parse(t);
160
- } catch {
161
- return;
162
- }
163
- }, mt = async (t) => {
164
- if (t)
165
- try {
166
- const e = await j(t, "utf8");
167
- return C(e);
168
- } catch {
169
- return;
170
- }
171
- }, D = (t, e) => {
172
- const r = g([...(t == null ? void 0 : t.paths) ?? [], ...(e == null ? void 0 : e.paths) ?? []]), n = g([
173
- ...(t == null ? void 0 : t.extensions) ?? [],
174
- ...(e == null ? void 0 : e.extensions) ?? []
175
- ]), s = g([...(t == null ? void 0 : t.methods) ?? [], ...(e == null ? void 0 : e.methods) ?? []]), o = g([...(t == null ? void 0 : t.spanNames) ?? [], ...(e == null ? void 0 : e.spanNames) ?? []]);
267
+ };
268
+ const createFilteringTraceExporter = (exporter) => exporter;
269
+ let sdk;
270
+ let runtime;
271
+ const uniqueList = (items) => Array.from(new Set(items));
272
+ const parseList = (value) => value?.split(",").map((item) => item.trim()).filter(Boolean) ?? [];
273
+ const parseHeaders = (value) => {
274
+ if (!value) return void 0;
275
+ const headers = {};
276
+ for (const pair of value.split(",")) {
277
+ const [key, ...rest] = pair.split("=");
278
+ const normalizedKey = key?.trim();
279
+ if (!normalizedKey) continue;
280
+ headers[normalizedKey] = rest.join("=").trim();
281
+ }
282
+ return headers;
283
+ };
284
+ const parseJson = (value) => {
285
+ if (!value) return void 0;
286
+ try {
287
+ return JSON.parse(value);
288
+ } catch {
289
+ return void 0;
290
+ }
291
+ };
292
+ const readConfigFile = async (path) => {
293
+ if (!path) return void 0;
294
+ try {
295
+ const raw = await readFile(path, "utf8");
296
+ return parseJson(raw);
297
+ } catch {
298
+ return void 0;
299
+ }
300
+ };
301
+ const mergeIgnoreConfig = (base, overrides) => {
302
+ const mergedPaths = uniqueList([...base?.paths ?? [], ...overrides?.paths ?? []]);
303
+ const mergedExtensions = uniqueList([
304
+ ...base?.extensions ?? [],
305
+ ...overrides?.extensions ?? []
306
+ ]);
307
+ const mergedMethods = uniqueList([...base?.methods ?? [], ...overrides?.methods ?? []]);
308
+ const mergedSpanNames = uniqueList([...base?.spanNames ?? [], ...overrides?.spanNames ?? []]);
176
309
  return {
177
- paths: r.length ? r : void 0,
178
- extensions: n.length ? n : void 0,
179
- methods: s.length ? s : void 0,
180
- spanNames: o.length ? o : void 0
310
+ paths: mergedPaths.length ? mergedPaths : void 0,
311
+ extensions: mergedExtensions.length ? mergedExtensions : void 0,
312
+ methods: mergedMethods.length ? mergedMethods : void 0,
313
+ spanNames: mergedSpanNames.length ? mergedSpanNames : void 0
181
314
  };
182
- }, pt = (t) => {
183
- if (t != null && t.length)
184
- return t.map((e) => ({
185
- ...e,
186
- ratio: Number.isFinite(e.ratio) ? Math.max(0, Math.min(1, e.ratio)) : 0
187
- })).filter((e) => e.ratio >= 0);
188
- }, Et = async (t = process.env) => {
189
- const e = await mt(t.TELEMETRY_CONFIG_FILE), r = N(t.TELEMETRY_IGNORE_PATHS), n = N(t.TELEMETRY_IGNORE_EXTENSIONS), s = N(t.TELEMETRY_IGNORE_METHODS), o = C(t.TELEMETRY_SAMPLING_RULES), i = typeof (e == null ? void 0 : e.samplingRules) == "string" ? C(e.samplingRules) : e == null ? void 0 : e.samplingRules, a = {
190
- serviceName: t.OTEL_SERVICE_NAME || t.TELEMETRY_SERVICE_NAME,
191
- serviceVersion: t.OTEL_SERVICE_VERSION || t.TELEMETRY_SERVICE_VERSION,
192
- environment: t.OTEL_RESOURCE_ATTRIBUTES || t.TELEMETRY_ENVIRONMENT || (t.VERCEL ? t.VERCEL_ENV : void 0),
193
- defaultSamplingRatio: t.TELEMETRY_SAMPLING_RATIO ? Number(t.TELEMETRY_SAMPLING_RATIO) : void 0,
194
- url: t.OTEL_EXPORTER_OTLP_ENDPOINT || t.TELEMETRY_OTLP_ENDPOINT,
195
- headers: ut(t.OTEL_EXPORTER_OTLP_HEADERS || t.TELEMETRY_OTLP_HEADERS),
196
- inspectionHeader: t.TELEMETRY_INSPECTION_HEADER,
197
- userTypeHeader: t.TELEMETRY_USER_TYPE_HEADER,
198
- requestStartHeader: t.TELEMETRY_REQUEST_START_HEADER,
199
- samplingRules: o || i,
200
- ignore: r.length || n.length || s.length ? {
201
- paths: r.length ? r : void 0,
202
- extensions: n.length ? n : void 0,
203
- methods: s.length ? s : void 0
315
+ };
316
+ const normalizeSamplingRules = (rules) => {
317
+ if (!rules?.length) return void 0;
318
+ return rules.map((rule) => ({
319
+ ...rule,
320
+ ratio: Number.isFinite(rule.ratio) ? Math.max(0, Math.min(1, rule.ratio)) : 0
321
+ })).filter((rule) => rule.ratio >= 0);
322
+ };
323
+ const loadTelemetryConfigFromEnv = async (env = process.env) => {
324
+ const fileConfig = await readConfigFile(env.TELEMETRY_CONFIG_FILE);
325
+ const ignorePaths = parseList(env.TELEMETRY_IGNORE_PATHS);
326
+ const ignoreExtensions = parseList(env.TELEMETRY_IGNORE_EXTENSIONS);
327
+ const ignoreMethods = parseList(env.TELEMETRY_IGNORE_METHODS);
328
+ const samplingRules = parseJson(env.TELEMETRY_SAMPLING_RULES);
329
+ const fileSamplingRules = typeof fileConfig?.samplingRules === "string" ? parseJson(fileConfig.samplingRules) : fileConfig?.samplingRules;
330
+ const envConfig = {
331
+ serviceName: env.OTEL_SERVICE_NAME || env.TELEMETRY_SERVICE_NAME,
332
+ serviceVersion: env.OTEL_SERVICE_VERSION || env.TELEMETRY_SERVICE_VERSION,
333
+ environment: env.OTEL_RESOURCE_ATTRIBUTES || env.TELEMETRY_ENVIRONMENT || (env.VERCEL ? env.VERCEL_ENV : void 0),
334
+ defaultSamplingRatio: env.TELEMETRY_SAMPLING_RATIO ? Number(env.TELEMETRY_SAMPLING_RATIO) : void 0,
335
+ url: env.OTEL_EXPORTER_OTLP_ENDPOINT || env.TELEMETRY_OTLP_ENDPOINT,
336
+ headers: parseHeaders(env.OTEL_EXPORTER_OTLP_HEADERS || env.TELEMETRY_OTLP_HEADERS),
337
+ inspectionHeader: env.TELEMETRY_INSPECTION_HEADER,
338
+ userTypeHeader: env.TELEMETRY_USER_TYPE_HEADER,
339
+ requestStartHeader: env.TELEMETRY_REQUEST_START_HEADER,
340
+ samplingRules: samplingRules || fileSamplingRules,
341
+ ignore: ignorePaths.length || ignoreExtensions.length || ignoreMethods.length ? {
342
+ paths: ignorePaths.length ? ignorePaths : void 0,
343
+ extensions: ignoreExtensions.length ? ignoreExtensions : void 0,
344
+ methods: ignoreMethods.length ? ignoreMethods : void 0
204
345
  } : void 0
205
346
  };
206
- return e && typeof e == "object" ? { ...e, ...a } : a;
207
- }, ht = (t) => {
208
- if (!t) return process.env.NODE_ENV || "development";
209
- if (t.includes("deployment.environment")) {
210
- const r = t.split(",").find((s) => s.includes("deployment.environment"));
211
- if (!r) return t;
212
- const [, n] = r.split("=");
213
- return (n == null ? void 0 : n.trim()) || t;
214
- }
215
- return t;
216
- }, lt = (t, e) => {
217
- var n, s;
218
- const r = [
219
- { name: "inspection", ratio: 1, when: { inspection: !0 } },
347
+ if (fileConfig && typeof fileConfig === "object") {
348
+ return { ...fileConfig, ...envConfig };
349
+ }
350
+ return envConfig;
351
+ };
352
+ const resolveEnvironmentName = (environment) => {
353
+ if (!environment) return process.env.NODE_ENV || "development";
354
+ if (environment.includes("deployment.environment")) {
355
+ const entries = environment.split(",");
356
+ const match = entries.find((entry) => entry.includes("deployment.environment"));
357
+ if (!match) return environment;
358
+ const [, value] = match.split("=");
359
+ return value?.trim() || environment;
360
+ }
361
+ return environment;
362
+ };
363
+ const buildLayeredSamplingRules = (layered, defaultRatio) => {
364
+ const rules = [
365
+ { name: "inspection", ratio: 1, when: { inspection: true } },
220
366
  { name: "error", ratio: 1, when: { minStatusCode: 500 } }
221
367
  ];
222
- if ((n = t == null ? void 0 : t.criticalPaths) != null && n.length && r.push({
223
- name: "critical-path",
224
- ratio: 1,
225
- when: { path: t.criticalPaths }
226
- }), (s = t == null ? void 0 : t.criticalUserTypes) != null && s.length && r.push({
227
- name: "critical-user",
228
- ratio: 1,
229
- when: { userType: t.criticalUserTypes }
230
- }), t != null && t.pathSampling)
231
- for (const [o, i] of Object.entries(t.pathSampling))
232
- r.push({
233
- name: `path:${o}`,
234
- ratio: i,
235
- when: { path: [o] }
368
+ if (layered?.criticalPaths?.length) {
369
+ rules.push({
370
+ name: "critical-path",
371
+ ratio: 1,
372
+ when: { path: layered.criticalPaths }
373
+ });
374
+ }
375
+ if (layered?.criticalUserTypes?.length) {
376
+ rules.push({
377
+ name: "critical-user",
378
+ ratio: 1,
379
+ when: { userType: layered.criticalUserTypes }
380
+ });
381
+ }
382
+ if (layered?.pathSampling) {
383
+ for (const [pattern, ratio] of Object.entries(layered.pathSampling)) {
384
+ rules.push({
385
+ name: `path:${pattern}`,
386
+ ratio,
387
+ when: { path: [pattern] }
236
388
  });
237
- if (t != null && t.userTypeSampling)
238
- for (const [o, i] of Object.entries(t.userTypeSampling))
239
- r.push({
240
- name: `user:${o}`,
241
- ratio: i,
242
- when: { userType: [o] }
389
+ }
390
+ }
391
+ if (layered?.userTypeSampling) {
392
+ for (const [userType, ratio] of Object.entries(layered.userTypeSampling)) {
393
+ rules.push({
394
+ name: `user:${userType}`,
395
+ ratio,
396
+ when: { userType: [userType] }
243
397
  });
244
- return r.push({ name: "default", ratio: e }), r;
245
- }, b = (t) => {
246
- const e = ht(t.environment), r = D(Z, t.ignore), n = typeof t.defaultSamplingRatio == "number" ? Math.max(0, Math.min(1, t.defaultSamplingRatio)) : 0.01, s = pt(t.samplingRules) ?? lt(t.layeredSampling, n);
398
+ }
399
+ }
400
+ rules.push({ name: "default", ratio: defaultRatio });
401
+ return rules;
402
+ };
403
+ const normalizeResolvedConfig = (merged) => {
404
+ const environment = resolveEnvironmentName(merged.environment);
405
+ const ignore = mergeIgnoreConfig(defaultIgnoreConfig, merged.ignore);
406
+ const defaultSamplingRatio = typeof merged.defaultSamplingRatio === "number" ? Math.max(0, Math.min(1, merged.defaultSamplingRatio)) : 0.01;
407
+ const samplingRules = normalizeSamplingRules(merged.samplingRules) ?? buildLayeredSamplingRules(merged.layeredSampling, defaultSamplingRatio);
247
408
  return {
248
- ...t,
249
- serviceName: t.serviceName || "unknown-service",
250
- serviceVersion: t.serviceVersion,
251
- environment: e,
252
- defaultSamplingRatio: n,
253
- ignore: r,
254
- samplingRules: s,
255
- inspectionHeader: t.inspectionHeader ?? "x-inspection",
256
- userTypeHeader: t.userTypeHeader ?? "x-telemetry-user-type",
257
- requestStartHeader: t.requestStartHeader ?? "x-telemetry-start",
258
- scheduledDelayMillis: t.scheduledDelayMillis ?? 1e3,
259
- maxQueueSize: t.maxQueueSize ?? 2048,
260
- maxExportBatchSize: t.maxExportBatchSize ?? 512,
261
- exporterTimeoutMillis: t.exporterTimeoutMillis ?? 3e4
409
+ ...merged,
410
+ serviceName: merged.serviceName || "unknown-service",
411
+ serviceVersion: merged.serviceVersion,
412
+ environment,
413
+ defaultSamplingRatio,
414
+ ignore,
415
+ samplingRules,
416
+ inspectionHeader: merged.inspectionHeader ?? "x-inspection",
417
+ userTypeHeader: merged.userTypeHeader ?? "x-telemetry-user-type",
418
+ requestStartHeader: merged.requestStartHeader ?? "x-telemetry-start",
419
+ scheduledDelayMillis: merged.scheduledDelayMillis ?? 1e3,
420
+ maxQueueSize: merged.maxQueueSize ?? 2048,
421
+ maxExportBatchSize: merged.maxExportBatchSize ?? 512,
422
+ exporterTimeoutMillis: merged.exporterTimeoutMillis ?? 3e4
262
423
  };
263
- }, v = async (t = {}, e = process.env) => {
264
- const r = await Et(e);
265
- return b({ ...r, ...t });
266
- }, ft = async (t) => {
267
- let e = await v(t);
424
+ };
425
+ const resolveTelemetryConfig = async (options = {}, env = process.env) => {
426
+ const envConfig = await loadTelemetryConfigFromEnv(env);
427
+ return normalizeResolvedConfig({ ...envConfig, ...options });
428
+ };
429
+ const createTelemetryRuntime = async (initialConfig) => {
430
+ let current = await resolveTelemetryConfig(initialConfig);
268
431
  return {
269
- getConfig: () => e,
270
- updateConfig: (r) => {
271
- e = b({
272
- ...e,
273
- ...r,
274
- ignore: D(e.ignore, r.ignore)
432
+ getConfig: () => current,
433
+ updateConfig: (partial) => {
434
+ current = normalizeResolvedConfig({
435
+ ...current,
436
+ ...partial,
437
+ ignore: mergeIgnoreConfig(current.ignore, partial.ignore)
275
438
  });
276
439
  }
277
440
  };
278
- }, dt = (t) => t[0] * 1e3 + t[1] / 1e6, Tt = (t, e) => {
279
- for (const r of e) {
280
- const n = t.attributes[r];
281
- if (typeof n == "number") return n;
282
- }
283
- }, Rt = (t) => {
284
- const e = t.events.find((n) => n.name === "exception");
285
- if (!(e != null && e.attributes)) return;
286
- const r = e.attributes["exception.message"];
287
- return typeof r == "string" ? r : void 0;
288
- }, I = /^(GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS)$/, P = (t) => {
289
- for (const r of ["url.path", "http.target", "http.route", "next.route"]) {
290
- const n = t[r];
291
- if (n && typeof n == "string") return n;
292
- }
293
- const e = t["http.url"] ?? t["url.full"];
294
- if (e && typeof e == "string")
441
+ };
442
+ const hrTimeToMilliseconds = (time) => time[0] * 1e3 + time[1] / 1e6;
443
+ const getSpanAttributeNumber = (span, keys) => {
444
+ for (const key of keys) {
445
+ const value = span.attributes[key];
446
+ if (typeof value === "number") return value;
447
+ }
448
+ return void 0;
449
+ };
450
+ const extractExceptionMessage = (span) => {
451
+ const exceptionEvent = span.events.find((event) => event.name === "exception");
452
+ if (!exceptionEvent?.attributes) return void 0;
453
+ const message = exceptionEvent.attributes["exception.message"];
454
+ return typeof message === "string" ? message : void 0;
455
+ };
456
+ const BARE_HTTP_METHOD = /^(GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS)$/;
457
+ const extractPathFromSpanAttributes = (attrs) => {
458
+ for (const key of ["url.path", "http.target", "http.route", "next.route"]) {
459
+ const val = attrs[key];
460
+ if (val && typeof val === "string") return val;
461
+ }
462
+ const url = attrs["http.url"] ?? attrs["url.full"];
463
+ if (url && typeof url === "string") {
295
464
  try {
296
- return new URL(e).pathname;
465
+ return new URL(url).pathname;
297
466
  } catch {
298
- return e.split("?")[0];
299
- }
300
- }, gt = () => ({
301
- onStart: (t) => {
302
- const e = t, r = e.name;
303
- if (!r || !I.test(r)) return;
304
- const n = P(e.attributes);
305
- n && t.updateName(`${r} ${n}`);
306
- },
307
- onEnd: (t) => {
308
- const e = t.name;
309
- if (!e || !I.test(e)) return;
310
- const r = P(t.attributes);
311
- if (!r) return;
312
- const n = t.attributes["http.request.method"] ?? t.attributes["http.method"], s = typeof n == "string" ? n : e;
313
- t.name = `${s} ${r}`;
314
- },
315
- shutdown: () => Promise.resolve(),
316
- forceFlush: () => Promise.resolve()
317
- }), _t = () => ({
318
- onStart: () => {
319
- },
320
- onEnd: (t) => {
321
- const e = dt(t.duration);
322
- t.attributes["telemetry.request.duration_ms"] = Math.round(e), typeof t.attributes["telemetry.request.start_time_ms"] != "number" && (t.attributes["telemetry.request.start_time_ms"] = Date.now() - Math.round(e));
323
- const r = Tt(t, [
324
- "http.response.status_code",
325
- "http.status_code",
326
- "telemetry.response.status_code"
327
- ]);
328
- if (typeof r == "number" && (t.attributes["telemetry.response.status_code"] = r), t.status.code === $.ERROR || typeof r == "number" && r >= 500) {
329
- t.attributes["telemetry.request.error"] = !0;
330
- const s = t.status.message || Rt(t);
331
- s && (t.attributes["telemetry.request.error_message"] = s);
467
+ return url.split("?")[0];
332
468
  }
333
- },
334
- shutdown: () => Promise.resolve(),
335
- forceFlush: () => Promise.resolve()
336
- }), Pt = async (t) => {
337
- const e = await v(t);
338
- h = await ft(e);
339
- const r = process.env, n = {
340
- [q]: e.serviceName,
341
- [G]: e.serviceVersion || "1.0.0",
342
- [tt]: e.environment || r.VERCEL_ENV || r.NODE_ENV || "development"
469
+ }
470
+ return void 0;
471
+ };
472
+ const createSpanNamingProcessor = () => {
473
+ return {
474
+ onStart: (span) => {
475
+ const sdkSpan = span;
476
+ const name = sdkSpan.name;
477
+ if (!name || !BARE_HTTP_METHOD.test(name)) return;
478
+ const path = extractPathFromSpanAttributes(sdkSpan.attributes);
479
+ if (path) {
480
+ span.updateName(`${name} ${path}`);
481
+ }
482
+ },
483
+ onEnd: (span) => {
484
+ const name = span.name;
485
+ if (!name || !BARE_HTTP_METHOD.test(name)) return;
486
+ const path = extractPathFromSpanAttributes(span.attributes);
487
+ if (!path) return;
488
+ const method = span.attributes["http.request.method"] ?? span.attributes["http.method"];
489
+ const resolvedMethod = typeof method === "string" ? method : name;
490
+ span.name = `${resolvedMethod} ${path}`;
491
+ },
492
+ shutdown: () => Promise.resolve(),
493
+ forceFlush: () => Promise.resolve()
494
+ };
495
+ };
496
+ const createRequestSpanProcessor = () => {
497
+ return {
498
+ onStart: () => void 0,
499
+ onEnd: (span) => {
500
+ const durationMs = hrTimeToMilliseconds(span.duration);
501
+ span.attributes["telemetry.request.duration_ms"] = Math.round(durationMs);
502
+ if (typeof span.attributes["telemetry.request.start_time_ms"] !== "number") {
503
+ span.attributes["telemetry.request.start_time_ms"] = Date.now() - Math.round(durationMs);
504
+ }
505
+ const statusCode = getSpanAttributeNumber(span, [
506
+ "http.response.status_code",
507
+ "http.status_code",
508
+ "telemetry.response.status_code"
509
+ ]);
510
+ if (typeof statusCode === "number") {
511
+ span.attributes["telemetry.response.status_code"] = statusCode;
512
+ }
513
+ const isError = span.status.code === SpanStatusCode.ERROR || typeof statusCode === "number" && statusCode >= 500;
514
+ if (isError) {
515
+ span.attributes["telemetry.request.error"] = true;
516
+ const message = span.status.message || extractExceptionMessage(span);
517
+ if (message) {
518
+ span.attributes["telemetry.request.error_message"] = message;
519
+ }
520
+ }
521
+ },
522
+ shutdown: () => Promise.resolve(),
523
+ forceFlush: () => Promise.resolve()
343
524
  };
344
- r.VERCEL && (r.VERCEL_REGION && (n["vercel.region"] = r.VERCEL_REGION), r.NEXT_RUNTIME && (n["vercel.runtime"] = r.NEXT_RUNTIME), r.VERCEL_GIT_COMMIT_SHA && (n["vercel.sha"] = r.VERCEL_GIT_COMMIT_SHA), r.VERCEL_URL && (n["vercel.host"] = r.VERCEL_URL), r.VERCEL_BRANCH_URL && (n["vercel.branch_host"] = r.VERCEL_BRANCH_URL), r.VERCEL_DEPLOYMENT_ID && (n["vercel.deployment_id"] = r.VERCEL_DEPLOYMENT_ID));
345
- const s = U(n), o = new Y(e), i = ct(o), a = [
346
- gt(),
347
- _t(),
348
- new k(i, {
349
- scheduledDelayMillis: e.scheduledDelayMillis,
350
- maxQueueSize: e.maxQueueSize,
351
- maxExportBatchSize: e.maxExportBatchSize,
352
- exportTimeoutMillis: e.exporterTimeoutMillis
525
+ };
526
+ const startTelemetry = async (options) => {
527
+ const config = await resolveTelemetryConfig(options);
528
+ runtime = await createTelemetryRuntime(config);
529
+ const env = process.env;
530
+ const resourceAttrs = {
531
+ [ATTR_SERVICE_NAME]: config.serviceName,
532
+ [ATTR_SERVICE_VERSION]: config.serviceVersion || "1.0.0",
533
+ [ATTR_DEPLOYMENT_ENVIRONMENT_NAME]: config.environment || env.VERCEL_ENV || env.NODE_ENV || "development"
534
+ };
535
+ if (env.VERCEL) {
536
+ if (env.VERCEL_REGION) resourceAttrs["vercel.region"] = env.VERCEL_REGION;
537
+ if (env.NEXT_RUNTIME) resourceAttrs["vercel.runtime"] = env.NEXT_RUNTIME;
538
+ if (env.VERCEL_GIT_COMMIT_SHA) resourceAttrs["vercel.sha"] = env.VERCEL_GIT_COMMIT_SHA;
539
+ if (env.VERCEL_URL) resourceAttrs["vercel.host"] = env.VERCEL_URL;
540
+ if (env.VERCEL_BRANCH_URL) resourceAttrs["vercel.branch_host"] = env.VERCEL_BRANCH_URL;
541
+ if (env.VERCEL_DEPLOYMENT_ID) resourceAttrs["vercel.deployment_id"] = env.VERCEL_DEPLOYMENT_ID;
542
+ }
543
+ const resource = resourceFromAttributes(resourceAttrs);
544
+ const traceExporter = new OTLPTraceExporter(config);
545
+ const filteringExporter = createFilteringTraceExporter(traceExporter);
546
+ const spanProcessors = [
547
+ createSpanNamingProcessor(),
548
+ createRequestSpanProcessor(),
549
+ new BatchSpanProcessor(filteringExporter, {
550
+ scheduledDelayMillis: config.scheduledDelayMillis,
551
+ maxQueueSize: config.maxQueueSize,
552
+ maxExportBatchSize: config.maxExportBatchSize,
553
+ exportTimeoutMillis: config.exporterTimeoutMillis
353
554
  })
354
555
  ];
355
- _ = new F({
356
- serviceName: e.serviceName,
357
- resource: s,
358
- spanProcessors: a,
359
- sampler: at(h),
360
- instrumentations: et(e)
556
+ sdk = new NodeSDK({
557
+ serviceName: config.serviceName,
558
+ resource,
559
+ spanProcessors,
560
+ sampler: createRuleBasedSampler(runtime),
561
+ instrumentations: getInstrumentations(config)
361
562
  });
362
563
  try {
363
- await _.start();
364
- } catch (c) {
365
- console.warn("Telemetry start failed:", c);
366
- }
367
- return h;
368
- }, Vt = (t) => {
369
- h == null || h.updateConfig(t);
370
- }, xt = (t) => (V.inject(x.active(), t), t), Ht = (t = fetch) => async (e, r) => {
371
- const n = (r == null ? void 0 : r.headers) ?? (e instanceof Request ? e.headers : void 0), s = new Headers(n);
372
- return V.inject(x.active(), s, {
373
- set: (o, i, a) => {
374
- o.set(i, a);
375
- }
376
- }), t(e, { ...r, headers: s });
377
- }, Dt = async () => {
378
- _ && await _.shutdown();
564
+ await sdk.start();
565
+ } catch (error) {
566
+ console.warn("Telemetry start failed:", error);
567
+ }
568
+ return runtime;
569
+ };
570
+ const updateTelemetryConfig = (partial) => {
571
+ runtime?.updateConfig(partial);
572
+ };
573
+ const injectTraceHeaders = (headers) => {
574
+ propagation.inject(context.active(), headers);
575
+ return headers;
576
+ };
577
+ const createPropagatingFetch = (fetchImpl = fetch) => {
578
+ return async (input, init) => {
579
+ const baseHeaders = init?.headers ?? (input instanceof Request ? input.headers : void 0);
580
+ const headers = new Headers(baseHeaders);
581
+ propagation.inject(context.active(), headers, {
582
+ set: (carrier, key, value) => {
583
+ carrier.set(key, value);
584
+ }
585
+ });
586
+ return fetchImpl(input, { ...init, headers });
587
+ };
588
+ };
589
+ const shutdownTelemetry = async () => {
590
+ if (sdk) {
591
+ await sdk.shutdown();
592
+ }
379
593
  };
380
594
  export {
381
- rt as buildSamplingContextFromAttributes,
382
- W as createIgnoreMatcher,
383
- Ht as createPropagatingFetch,
384
- K as createSpanNameMatcher,
385
- ft as createTelemetryRuntime,
386
- Z as defaultIgnoreConfig,
387
- xt as injectTraceHeaders,
388
- Et as loadTelemetryConfigFromEnv,
389
- ot as matchSamplingRule,
390
- st as resolveSamplingRatio,
391
- v as resolveTelemetryConfig,
392
- Dt as shutdownTelemetry,
393
- Pt as startTelemetry,
394
- Vt as updateTelemetryConfig
595
+ buildSamplingContextFromAttributes,
596
+ createIgnoreMatcher,
597
+ createPropagatingFetch,
598
+ createSpanNameMatcher,
599
+ createTelemetryRuntime,
600
+ defaultIgnoreConfig,
601
+ injectTraceHeaders,
602
+ loadTelemetryConfigFromEnv,
603
+ matchSamplingRule,
604
+ resolveSamplingRatio,
605
+ resolveTelemetryConfig,
606
+ shutdownTelemetry,
607
+ startTelemetry,
608
+ updateTelemetryConfig
395
609
  };