@ryanzeng/nest-observe 0.1.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/LICENSE +21 -0
- package/README.md +177 -0
- package/dist/index.d.mts +197 -0
- package/dist/index.d.ts +197 -0
- package/dist/index.js +1121 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1073 -0
- package/dist/index.mjs.map +1 -0
- package/dist/register.d.mts +2 -0
- package/dist/register.d.ts +2 -0
- package/dist/register.js +891 -0
- package/dist/register.js.map +1 -0
- package/dist/register.mjs +892 -0
- package/dist/register.mjs.map +1 -0
- package/package.json +96 -0
package/dist/register.js
ADDED
|
@@ -0,0 +1,891 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// src/sdk.ts
|
|
4
|
+
var import_api_logs2 = require("@opentelemetry/api-logs");
|
|
5
|
+
var import_api5 = require("@opentelemetry/api");
|
|
6
|
+
var import_exporter_logs_otlp_proto = require("@opentelemetry/exporter-logs-otlp-proto");
|
|
7
|
+
var import_exporter_metrics_otlp_proto = require("@opentelemetry/exporter-metrics-otlp-proto");
|
|
8
|
+
var import_exporter_trace_otlp_proto = require("@opentelemetry/exporter-trace-otlp-proto");
|
|
9
|
+
var import_instrumentation2 = require("@opentelemetry/instrumentation");
|
|
10
|
+
var import_instrumentation_http = require("@opentelemetry/instrumentation-http");
|
|
11
|
+
var import_instrumentation3 = require("@prisma/instrumentation");
|
|
12
|
+
var import_sdk_logs = require("@opentelemetry/sdk-logs");
|
|
13
|
+
var import_sdk_metrics = require("@opentelemetry/sdk-metrics");
|
|
14
|
+
var import_sdk_trace_base = require("@opentelemetry/sdk-trace-base");
|
|
15
|
+
var import_sdk_trace_node = require("@opentelemetry/sdk-trace-node");
|
|
16
|
+
|
|
17
|
+
// src/config.ts
|
|
18
|
+
var import_node_crypto = require("crypto");
|
|
19
|
+
var DEFAULT_ALLOWED_HEADERS = ["accept", "content-type", "user-agent", "x-request-id", "traceparent"];
|
|
20
|
+
function booleanValue(value, fallback) {
|
|
21
|
+
if (typeof value === "boolean") return value;
|
|
22
|
+
if (value?.toLowerCase() === "true") return true;
|
|
23
|
+
if (value?.toLowerCase() === "false") return false;
|
|
24
|
+
return fallback;
|
|
25
|
+
}
|
|
26
|
+
function positiveInteger(value, fallback) {
|
|
27
|
+
const number = Number(value);
|
|
28
|
+
return Number.isFinite(number) && number > 0 ? Math.floor(number) : fallback;
|
|
29
|
+
}
|
|
30
|
+
function samplingValue(value) {
|
|
31
|
+
const number = Number(value);
|
|
32
|
+
if (!Number.isFinite(number)) return 1;
|
|
33
|
+
return Math.min(1, Math.max(0, number));
|
|
34
|
+
}
|
|
35
|
+
function environmentSampling(env) {
|
|
36
|
+
const sampler = env.OTEL_TRACES_SAMPLER?.toLowerCase();
|
|
37
|
+
if (sampler === "always_off" || sampler === "parentbased_always_off") return 0;
|
|
38
|
+
if (sampler === "always_on" || sampler === "parentbased_always_on") return 1;
|
|
39
|
+
return samplingValue(env.OTEL_TRACES_SAMPLER_ARG);
|
|
40
|
+
}
|
|
41
|
+
function decode(value) {
|
|
42
|
+
try {
|
|
43
|
+
return decodeURIComponent(value.trim());
|
|
44
|
+
} catch {
|
|
45
|
+
return value.trim();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function parseKeyValueList(value) {
|
|
49
|
+
if (!value) return {};
|
|
50
|
+
return Object.fromEntries(value.split(",").flatMap((entry) => {
|
|
51
|
+
const separator = entry.indexOf("=");
|
|
52
|
+
if (separator <= 0) return [];
|
|
53
|
+
const key = decode(entry.slice(0, separator));
|
|
54
|
+
const itemValue = decode(entry.slice(separator + 1));
|
|
55
|
+
return key ? [[key, itemValue]] : [];
|
|
56
|
+
}));
|
|
57
|
+
}
|
|
58
|
+
function appendSignalPath(endpoint, signal) {
|
|
59
|
+
if (!endpoint) return void 0;
|
|
60
|
+
return `${endpoint.replace(/\/+$/, "")}/v1/${signal}`;
|
|
61
|
+
}
|
|
62
|
+
function optionalString(value) {
|
|
63
|
+
const result = value?.trim();
|
|
64
|
+
return result ? result : void 0;
|
|
65
|
+
}
|
|
66
|
+
function resolveObserveConfig(options = {}, env = process.env) {
|
|
67
|
+
const otelResources = parseKeyValueList(env.OTEL_RESOURCE_ATTRIBUTES);
|
|
68
|
+
const resourceAttributes = { ...otelResources, ...options.resourceAttributes };
|
|
69
|
+
const genericEndpoint = optionalString(options.endpoint ?? env.OTEL_EXPORTER_OTLP_ENDPOINT);
|
|
70
|
+
const sampling = options.sampling === void 0 ? environmentSampling(env) : samplingValue(options.sampling);
|
|
71
|
+
const allowedHeaders = Array.from(new Set([
|
|
72
|
+
...DEFAULT_ALLOWED_HEADERS,
|
|
73
|
+
...options.allowedHeaders ?? []
|
|
74
|
+
].map((header) => header.toLowerCase().trim()).filter(Boolean)));
|
|
75
|
+
const result = {
|
|
76
|
+
enabled: booleanValue(options.enabled ?? env.OBSERVE_ENABLED, true),
|
|
77
|
+
serviceName: options.serviceName ?? env.OTEL_SERVICE_NAME ?? otelResources["service.name"] ?? "nest-application",
|
|
78
|
+
serviceVersion: options.serviceVersion ?? env.OTEL_SERVICE_VERSION ?? otelResources["service.version"] ?? "unknown",
|
|
79
|
+
environment: options.environment ?? env.OBSERVE_ENVIRONMENT ?? otelResources["deployment.environment.name"] ?? env.NODE_ENV ?? "development",
|
|
80
|
+
instanceId: options.instanceId ?? otelResources["service.instance.id"] ?? env.OTEL_SERVICE_INSTANCE_ID ?? env.HOSTNAME ?? (0, import_node_crypto.randomUUID)(),
|
|
81
|
+
endpoints: {
|
|
82
|
+
traces: optionalString(env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT) ?? appendSignalPath(genericEndpoint, "traces"),
|
|
83
|
+
metrics: optionalString(env.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT) ?? appendSignalPath(genericEndpoint, "metrics"),
|
|
84
|
+
logs: optionalString(env.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT) ?? appendSignalPath(genericEndpoint, "logs")
|
|
85
|
+
},
|
|
86
|
+
headers: { ...parseKeyValueList(env.OTEL_EXPORTER_OTLP_HEADERS), ...options.headers },
|
|
87
|
+
traces: booleanValue(options.traces, true),
|
|
88
|
+
logs: booleanValue(options.logs, true),
|
|
89
|
+
metrics: booleanValue(options.metrics, true),
|
|
90
|
+
providerTracing: booleanValue(options.providerTracing, true),
|
|
91
|
+
controllerTracing: booleanValue(options.controllerTracing, true),
|
|
92
|
+
sampling,
|
|
93
|
+
allowedHeaders,
|
|
94
|
+
exportTimeoutMillis: positiveInteger(options.exportTimeoutMillis ?? env.OTEL_EXPORTER_OTLP_TIMEOUT, 1e4),
|
|
95
|
+
metricExportIntervalMillis: positiveInteger(options.metricExportIntervalMillis ?? env.OTEL_METRIC_EXPORT_INTERVAL, 6e4),
|
|
96
|
+
resourceAttributes
|
|
97
|
+
};
|
|
98
|
+
if (options.exporters) result.exporters = options.exporters;
|
|
99
|
+
return result;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// src/exceptions/process-exception-capture.ts
|
|
103
|
+
var import_api = require("@opentelemetry/api");
|
|
104
|
+
|
|
105
|
+
// src/security/redaction.ts
|
|
106
|
+
var REDACTED = "[REDACTED]";
|
|
107
|
+
var SENSITIVE_KEY = /(?:authorization|proxy-authorization|cookie|set-cookie|pass(?:word|wd)?|secret|token|api[-_]?key|phone|mobile)/i;
|
|
108
|
+
function isSensitiveKey(name) {
|
|
109
|
+
return SENSITIVE_KEY.test(name);
|
|
110
|
+
}
|
|
111
|
+
function redactText(value) {
|
|
112
|
+
return value.replace(/(?<!\d)1[3-9]\d{9}(?!\d)/g, REDACTED).replace(/\b(authorization|proxy-authorization)\s*[:=]\s*(?:bearer\s+|basic\s+)?[^\s,;]+/gi, `$1=${REDACTED}`).replace(/\b(password|passwd|secret|token|access_token|api[-_]?key|cookie)\s*[:=]\s*[^\s,;]+/gi, `$1=${REDACTED}`).replace(/\bbearer\s+[A-Za-z0-9._~+/=-]+/gi, `Bearer ${REDACTED}`);
|
|
113
|
+
}
|
|
114
|
+
function errorValue(error) {
|
|
115
|
+
return {
|
|
116
|
+
name: error.name,
|
|
117
|
+
message: redactText(error.message),
|
|
118
|
+
...error.stack ? { stack: redactText(error.stack) } : {}
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
function visit(value, seen) {
|
|
122
|
+
if (typeof value === "string") return redactText(value);
|
|
123
|
+
if (value === null || typeof value !== "object") return value;
|
|
124
|
+
if (value instanceof Error) return errorValue(value);
|
|
125
|
+
if (seen.has(value)) return "[Circular]";
|
|
126
|
+
seen.add(value);
|
|
127
|
+
if (Array.isArray(value)) return value.map((item) => visit(item, seen));
|
|
128
|
+
const output = {};
|
|
129
|
+
for (const [key, item] of Object.entries(value)) {
|
|
130
|
+
output[key] = SENSITIVE_KEY.test(key) ? REDACTED : visit(item, seen);
|
|
131
|
+
}
|
|
132
|
+
return output;
|
|
133
|
+
}
|
|
134
|
+
function redact(value) {
|
|
135
|
+
return visit(value, /* @__PURE__ */ new WeakSet());
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// src/exceptions/process-exception-capture.ts
|
|
139
|
+
function asError(value) {
|
|
140
|
+
return value instanceof Error ? value : new Error(String(value));
|
|
141
|
+
}
|
|
142
|
+
var ProcessExceptionCapture = class {
|
|
143
|
+
constructor(emitter) {
|
|
144
|
+
this.emitter = emitter;
|
|
145
|
+
}
|
|
146
|
+
emitter;
|
|
147
|
+
listening = false;
|
|
148
|
+
onUncaughtException = (value) => this.capture(value, "uncaughtException");
|
|
149
|
+
start() {
|
|
150
|
+
if (this.listening) return;
|
|
151
|
+
this.listening = true;
|
|
152
|
+
process.on("uncaughtExceptionMonitor", this.onUncaughtException);
|
|
153
|
+
}
|
|
154
|
+
stop() {
|
|
155
|
+
if (!this.listening) return;
|
|
156
|
+
process.off("uncaughtExceptionMonitor", this.onUncaughtException);
|
|
157
|
+
this.listening = false;
|
|
158
|
+
}
|
|
159
|
+
capture(value, source = "nestjs") {
|
|
160
|
+
try {
|
|
161
|
+
const error = asError(value);
|
|
162
|
+
const span = import_api.trace.getActiveSpan();
|
|
163
|
+
span?.recordException(error);
|
|
164
|
+
span?.setStatus({ code: import_api.SpanStatusCode.ERROR, message: error.message });
|
|
165
|
+
const spanContext = span?.spanContext();
|
|
166
|
+
this.emitter?.emit({
|
|
167
|
+
body: redactText(error.stack ?? error.message),
|
|
168
|
+
severityText: "ERROR",
|
|
169
|
+
attributes: {
|
|
170
|
+
"exception.type": error.name,
|
|
171
|
+
"exception.message": redactText(error.message),
|
|
172
|
+
"exception.stacktrace": redactText(error.stack ?? ""),
|
|
173
|
+
"exception.source": source,
|
|
174
|
+
...spanContext ? { trace_id: spanContext.traceId, span_id: spanContext.spanId } : {}
|
|
175
|
+
},
|
|
176
|
+
timestamp: Date.now()
|
|
177
|
+
});
|
|
178
|
+
} catch {
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
// src/instrumentation/nest-compatible-instrumentation.ts
|
|
184
|
+
var import_instrumentation = require("@opentelemetry/instrumentation");
|
|
185
|
+
var import_instrumentation_nestjs_core = require("@opentelemetry/instrumentation-nestjs-core");
|
|
186
|
+
|
|
187
|
+
// src/decorators/invoke.ts
|
|
188
|
+
var import_api2 = require("@opentelemetry/api");
|
|
189
|
+
var import_rxjs = require("rxjs");
|
|
190
|
+
var import_operators = require("rxjs/operators");
|
|
191
|
+
function fail(span, error) {
|
|
192
|
+
span.recordException(error instanceof Error ? error : new Error(String(error)));
|
|
193
|
+
span.setStatus({ code: import_api2.SpanStatusCode.ERROR, message: error instanceof Error ? error.message : String(error) });
|
|
194
|
+
}
|
|
195
|
+
function invokeWithSpan(tracer, spanName, attributes, invoke, onFinish) {
|
|
196
|
+
return tracer.startActiveSpan(spanName, { attributes }, (span) => {
|
|
197
|
+
const started = process.hrtime.bigint();
|
|
198
|
+
const finish = (error) => {
|
|
199
|
+
if (error !== void 0) fail(span, error);
|
|
200
|
+
else span.setStatus({ code: import_api2.SpanStatusCode.OK });
|
|
201
|
+
onFinish?.(Number(process.hrtime.bigint() - started) / 1e9, error);
|
|
202
|
+
span.end();
|
|
203
|
+
};
|
|
204
|
+
try {
|
|
205
|
+
const result = invoke();
|
|
206
|
+
if (result instanceof Promise) {
|
|
207
|
+
return result.then(
|
|
208
|
+
(value) => {
|
|
209
|
+
finish();
|
|
210
|
+
return value;
|
|
211
|
+
},
|
|
212
|
+
(error) => {
|
|
213
|
+
finish(error);
|
|
214
|
+
throw error;
|
|
215
|
+
}
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
if ((0, import_rxjs.isObservable)(result)) {
|
|
219
|
+
const spanContext = import_api2.context.active();
|
|
220
|
+
const source = result;
|
|
221
|
+
return new import_rxjs.Observable((subscriber) => import_api2.context.with(spanContext, () => {
|
|
222
|
+
let streamError;
|
|
223
|
+
return source.pipe(
|
|
224
|
+
(0, import_operators.tap)({ error: (error) => {
|
|
225
|
+
streamError = error;
|
|
226
|
+
} }),
|
|
227
|
+
(0, import_operators.finalize)(() => finish(streamError))
|
|
228
|
+
).subscribe(subscriber);
|
|
229
|
+
}));
|
|
230
|
+
}
|
|
231
|
+
finish();
|
|
232
|
+
return result;
|
|
233
|
+
} catch (error) {
|
|
234
|
+
finish(error);
|
|
235
|
+
throw error;
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// src/decorators/metadata.ts
|
|
241
|
+
var ignoredTargets = /* @__PURE__ */ new WeakSet();
|
|
242
|
+
var tracedTargets = /* @__PURE__ */ new WeakSet();
|
|
243
|
+
function markTraceDecorated(target) {
|
|
244
|
+
tracedTargets.add(target);
|
|
245
|
+
}
|
|
246
|
+
function isTraceIgnored(method, type) {
|
|
247
|
+
return Boolean(method && ignoredTargets.has(method) || type && ignoredTargets.has(type));
|
|
248
|
+
}
|
|
249
|
+
function isTraceDecorated(method) {
|
|
250
|
+
return Boolean(method && tracedTargets.has(method));
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// src/nest/method-instrumenter.ts
|
|
254
|
+
var NestMethodInstrumenter = class {
|
|
255
|
+
constructor(tracer, meter) {
|
|
256
|
+
this.tracer = tracer;
|
|
257
|
+
this.calls = meter.createCounter("nestjs.method.calls", { unit: "{call}" });
|
|
258
|
+
this.duration = meter.createHistogram("nestjs.method.duration", {
|
|
259
|
+
unit: "s",
|
|
260
|
+
advice: { explicitBucketBoundaries: [1e-3, 5e-3, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5] }
|
|
261
|
+
});
|
|
262
|
+
this.errors = meter.createCounter("nestjs.method.errors", { unit: "{error}" });
|
|
263
|
+
}
|
|
264
|
+
tracer;
|
|
265
|
+
calls;
|
|
266
|
+
duration;
|
|
267
|
+
errors;
|
|
268
|
+
instrumented = /* @__PURE__ */ new WeakMap();
|
|
269
|
+
instrumentInstance(instance, kind, componentName) {
|
|
270
|
+
const type = instance.constructor;
|
|
271
|
+
const prototype = Object.getPrototypeOf(instance);
|
|
272
|
+
if (!prototype) return;
|
|
273
|
+
this.instrumentTarget(instance, prototype, type, kind, componentName);
|
|
274
|
+
}
|
|
275
|
+
instrumentPrototype(type, kind, componentName) {
|
|
276
|
+
const prototype = type.prototype;
|
|
277
|
+
if (!prototype) return;
|
|
278
|
+
this.instrumentTarget(prototype, prototype, type, kind, componentName);
|
|
279
|
+
}
|
|
280
|
+
instrumentTarget(target, prototype, type, kind, componentName) {
|
|
281
|
+
if (isTraceIgnored(type)) return;
|
|
282
|
+
const completed = this.instrumented.get(target) ?? /* @__PURE__ */ new Set();
|
|
283
|
+
this.instrumented.set(target, completed);
|
|
284
|
+
for (const methodName of Object.getOwnPropertyNames(prototype)) {
|
|
285
|
+
if (methodName === "constructor" || completed.has(methodName) || methodName.startsWith("onModule")) continue;
|
|
286
|
+
const descriptor = Object.getOwnPropertyDescriptor(prototype, methodName);
|
|
287
|
+
const original = descriptor?.value;
|
|
288
|
+
if (!descriptor || typeof original !== "function" || isTraceIgnored(original, type) || isTraceDecorated(original)) continue;
|
|
289
|
+
const existing = Reflect.get(target, methodName);
|
|
290
|
+
if (typeof existing === "function" && existing !== original && isTraceDecorated(existing)) continue;
|
|
291
|
+
const name = componentName || type.name || "Anonymous";
|
|
292
|
+
const attributes = {
|
|
293
|
+
[`nestjs.${kind}`]: name,
|
|
294
|
+
"nestjs.method": methodName
|
|
295
|
+
};
|
|
296
|
+
const instrumenter = this;
|
|
297
|
+
const wrapped = function(...args) {
|
|
298
|
+
instrumenter.calls.add(1, attributes);
|
|
299
|
+
return invokeWithSpan(
|
|
300
|
+
instrumenter.tracer,
|
|
301
|
+
`${name}.${methodName}`,
|
|
302
|
+
attributes,
|
|
303
|
+
() => original.apply(this, args),
|
|
304
|
+
(seconds, error) => {
|
|
305
|
+
instrumenter.duration.record(seconds, attributes);
|
|
306
|
+
if (error !== void 0) instrumenter.errors.add(1, attributes);
|
|
307
|
+
}
|
|
308
|
+
);
|
|
309
|
+
};
|
|
310
|
+
markTraceDecorated(wrapped);
|
|
311
|
+
try {
|
|
312
|
+
Object.defineProperty(target, methodName, { ...descriptor, value: wrapped });
|
|
313
|
+
completed.add(methodName);
|
|
314
|
+
} catch {
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
// src/instrumentation/nest-compatible-instrumentation.ts
|
|
321
|
+
var SUPPORTED_NEST_VERSIONS = [">=4.0.0 <13"];
|
|
322
|
+
var CompatibleNestInstrumentation = class extends import_instrumentation_nestjs_core.NestInstrumentation {
|
|
323
|
+
constructor(observeConfig = {}) {
|
|
324
|
+
super(observeConfig);
|
|
325
|
+
this.observeConfig = observeConfig;
|
|
326
|
+
}
|
|
327
|
+
observeConfig;
|
|
328
|
+
methodInstrumenter;
|
|
329
|
+
init() {
|
|
330
|
+
const definition = new import_instrumentation.InstrumentationNodeModuleDefinition("@nestjs/core", SUPPORTED_NEST_VERSIONS);
|
|
331
|
+
definition.files.push(
|
|
332
|
+
this.getNestFactoryFileInstrumentation(SUPPORTED_NEST_VERSIONS),
|
|
333
|
+
this.getRouterExecutionContextFileInstrumentation(SUPPORTED_NEST_VERSIONS),
|
|
334
|
+
new import_instrumentation.InstrumentationNodeModuleFile(
|
|
335
|
+
"@nestjs/core/injector/injector.js",
|
|
336
|
+
SUPPORTED_NEST_VERSIONS,
|
|
337
|
+
(moduleExports) => {
|
|
338
|
+
const instrumentation = this;
|
|
339
|
+
this._wrap(moduleExports.Injector.prototype, "instantiateClass", (original) => {
|
|
340
|
+
return async function(...args) {
|
|
341
|
+
const instance = await original.apply(this, args);
|
|
342
|
+
try {
|
|
343
|
+
const wrapper = args[1];
|
|
344
|
+
if (!instance || typeof instance !== "object" || !wrapper?.metatype) return instance;
|
|
345
|
+
if (["InternalCoreModule", "ObserveModule", "DiscoveryModule"].includes(wrapper.host?.name ?? "")) {
|
|
346
|
+
return instance;
|
|
347
|
+
}
|
|
348
|
+
const isController = wrapper.host?.controllers?.get(wrapper.token) === wrapper;
|
|
349
|
+
if (isController && instrumentation.observeConfig.controllerTracing === false || !isController && instrumentation.observeConfig.providerTracing === false) {
|
|
350
|
+
return instance;
|
|
351
|
+
}
|
|
352
|
+
instrumentation.getMethodInstrumenter().instrumentInstance(
|
|
353
|
+
instance,
|
|
354
|
+
isController ? "controller" : "provider",
|
|
355
|
+
wrapper.name ?? wrapper.metatype.name
|
|
356
|
+
);
|
|
357
|
+
} catch {
|
|
358
|
+
}
|
|
359
|
+
return instance;
|
|
360
|
+
};
|
|
361
|
+
});
|
|
362
|
+
return moduleExports;
|
|
363
|
+
},
|
|
364
|
+
(moduleExports) => {
|
|
365
|
+
this._unwrap(moduleExports.Injector.prototype, "instantiateClass");
|
|
366
|
+
}
|
|
367
|
+
)
|
|
368
|
+
);
|
|
369
|
+
return definition;
|
|
370
|
+
}
|
|
371
|
+
getMethodInstrumenter() {
|
|
372
|
+
return this.methodInstrumenter ??= new NestMethodInstrumenter(this.tracer, this.meter);
|
|
373
|
+
}
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
// src/logs/nest-logger-instrumentation.ts
|
|
377
|
+
var import_common = require("@nestjs/common");
|
|
378
|
+
var import_api3 = require("@opentelemetry/api");
|
|
379
|
+
var SEVERITY = {
|
|
380
|
+
verbose: "TRACE",
|
|
381
|
+
debug: "DEBUG",
|
|
382
|
+
log: "INFO",
|
|
383
|
+
warn: "WARN",
|
|
384
|
+
error: "ERROR",
|
|
385
|
+
fatal: "FATAL"
|
|
386
|
+
};
|
|
387
|
+
function bodyValue(message) {
|
|
388
|
+
if (message instanceof Error) return redactText(message.stack ?? `${message.name}: ${message.message}`);
|
|
389
|
+
return redact(message);
|
|
390
|
+
}
|
|
391
|
+
var NestLoggerInstrumentation = class {
|
|
392
|
+
constructor(emitter, resourceAttributes = {}) {
|
|
393
|
+
this.emitter = emitter;
|
|
394
|
+
this.resourceAttributes = resourceAttributes;
|
|
395
|
+
}
|
|
396
|
+
emitter;
|
|
397
|
+
resourceAttributes;
|
|
398
|
+
originals = /* @__PURE__ */ new Map();
|
|
399
|
+
enabled = false;
|
|
400
|
+
enable() {
|
|
401
|
+
if (this.enabled) return;
|
|
402
|
+
this.enabled = true;
|
|
403
|
+
const prototype = import_common.ConsoleLogger.prototype;
|
|
404
|
+
for (const method of Object.keys(SEVERITY)) {
|
|
405
|
+
const original = prototype[method];
|
|
406
|
+
if (typeof original !== "function") continue;
|
|
407
|
+
this.originals.set(method, original);
|
|
408
|
+
const emitter = this.emitter;
|
|
409
|
+
const resourceAttributes = this.resourceAttributes;
|
|
410
|
+
prototype[method] = function(message, ...args) {
|
|
411
|
+
try {
|
|
412
|
+
const spanContext = import_api3.trace.getActiveSpan()?.spanContext();
|
|
413
|
+
const contextName = this.context ?? (typeof args.at(-1) === "string" ? String(args.at(-1)) : void 0);
|
|
414
|
+
const attributes = { ...resourceAttributes };
|
|
415
|
+
if (contextName) attributes["nestjs.context"] = contextName;
|
|
416
|
+
if (spanContext?.traceId) attributes.trace_id = spanContext.traceId;
|
|
417
|
+
if (spanContext?.spanId) attributes.span_id = spanContext.spanId;
|
|
418
|
+
emitter.emit({
|
|
419
|
+
body: bodyValue(message),
|
|
420
|
+
severityText: SEVERITY[method],
|
|
421
|
+
attributes,
|
|
422
|
+
timestamp: Date.now()
|
|
423
|
+
});
|
|
424
|
+
} catch {
|
|
425
|
+
}
|
|
426
|
+
return original.call(this, message, ...args);
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
disable() {
|
|
431
|
+
if (!this.enabled) return;
|
|
432
|
+
const prototype = import_common.ConsoleLogger.prototype;
|
|
433
|
+
for (const [method, original] of this.originals) prototype[method] = original;
|
|
434
|
+
this.originals.clear();
|
|
435
|
+
this.enabled = false;
|
|
436
|
+
}
|
|
437
|
+
};
|
|
438
|
+
|
|
439
|
+
// src/logs/otel-log-emitter.ts
|
|
440
|
+
var import_api4 = require("@opentelemetry/api");
|
|
441
|
+
var import_api_logs = require("@opentelemetry/api-logs");
|
|
442
|
+
var SEVERITY_NUMBER = {
|
|
443
|
+
TRACE: import_api_logs.SeverityNumber.TRACE,
|
|
444
|
+
DEBUG: import_api_logs.SeverityNumber.DEBUG,
|
|
445
|
+
INFO: import_api_logs.SeverityNumber.INFO,
|
|
446
|
+
WARN: import_api_logs.SeverityNumber.WARN,
|
|
447
|
+
ERROR: import_api_logs.SeverityNumber.ERROR,
|
|
448
|
+
FATAL: import_api_logs.SeverityNumber.FATAL
|
|
449
|
+
};
|
|
450
|
+
var OpenTelemetryLogEmitter = class {
|
|
451
|
+
logger;
|
|
452
|
+
constructor(name = "@ryanzen9/nest-observe", version, logger) {
|
|
453
|
+
this.logger = logger ?? import_api_logs.logs.getLogger(name, version);
|
|
454
|
+
}
|
|
455
|
+
emit(record) {
|
|
456
|
+
this.logger.emit({
|
|
457
|
+
body: record.body,
|
|
458
|
+
severityText: record.severityText,
|
|
459
|
+
severityNumber: SEVERITY_NUMBER[record.severityText],
|
|
460
|
+
attributes: record.attributes,
|
|
461
|
+
...record.timestamp === void 0 ? {} : { timestamp: record.timestamp },
|
|
462
|
+
context: import_api4.context.active()
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
};
|
|
466
|
+
|
|
467
|
+
// src/metrics/http-request-metrics.ts
|
|
468
|
+
function routeFor(request) {
|
|
469
|
+
const route = request.route?.path ?? request.routerPath ?? request.routeOptions?.url;
|
|
470
|
+
if (!route) return "<unmatched>";
|
|
471
|
+
return `${request.baseUrl ?? ""}${route}` || "<unmatched>";
|
|
472
|
+
}
|
|
473
|
+
var HttpRequestMetrics = class {
|
|
474
|
+
constructor(meter, serviceName) {
|
|
475
|
+
this.serviceName = serviceName;
|
|
476
|
+
this.requestCount = meter.createCounter("http.server.request.count", {
|
|
477
|
+
description: "Number of completed inbound HTTP requests",
|
|
478
|
+
unit: "{request}"
|
|
479
|
+
});
|
|
480
|
+
this.requestDuration = meter.createHistogram("http.server.request.duration", {
|
|
481
|
+
description: "Inbound HTTP request duration",
|
|
482
|
+
unit: "s",
|
|
483
|
+
advice: { explicitBucketBoundaries: [5e-3, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10] }
|
|
484
|
+
});
|
|
485
|
+
this.errorCount = meter.createCounter("http.server.error.count", {
|
|
486
|
+
description: "Number of failed inbound HTTP requests",
|
|
487
|
+
unit: "{error}"
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
serviceName;
|
|
491
|
+
requestCount;
|
|
492
|
+
requestDuration;
|
|
493
|
+
errorCount;
|
|
494
|
+
startedAt = /* @__PURE__ */ new WeakMap();
|
|
495
|
+
start(request) {
|
|
496
|
+
this.startedAt.set(request, process.hrtime.bigint());
|
|
497
|
+
}
|
|
498
|
+
record(request, response) {
|
|
499
|
+
const statusCode = Number.isFinite(response.statusCode) ? response.statusCode : 0;
|
|
500
|
+
const attributes = {
|
|
501
|
+
"service.name": this.serviceName,
|
|
502
|
+
"http.route": routeFor(request),
|
|
503
|
+
"http.request.method": request.method ?? "UNKNOWN",
|
|
504
|
+
"http.response.status_code": statusCode
|
|
505
|
+
};
|
|
506
|
+
this.requestCount.add(1, attributes);
|
|
507
|
+
const started = this.startedAt.get(request);
|
|
508
|
+
if (started !== void 0) {
|
|
509
|
+
this.requestDuration.record(Number(process.hrtime.bigint() - started) / 1e9, attributes);
|
|
510
|
+
this.startedAt.delete(request);
|
|
511
|
+
}
|
|
512
|
+
if (statusCode >= 500) this.errorCount.add(1, attributes);
|
|
513
|
+
}
|
|
514
|
+
};
|
|
515
|
+
|
|
516
|
+
// src/metrics/runtime-metrics.ts
|
|
517
|
+
var import_node_os = require("os");
|
|
518
|
+
var import_node_perf_hooks = require("perf_hooks");
|
|
519
|
+
var RuntimeMetrics = class {
|
|
520
|
+
constructor(meter) {
|
|
521
|
+
this.meter = meter;
|
|
522
|
+
}
|
|
523
|
+
meter;
|
|
524
|
+
callbacks = [];
|
|
525
|
+
eventLoopDelay;
|
|
526
|
+
gcObserver;
|
|
527
|
+
started = false;
|
|
528
|
+
previousElu = import_node_perf_hooks.performance.eventLoopUtilization();
|
|
529
|
+
observe(instrument, callback) {
|
|
530
|
+
instrument.addCallback(callback);
|
|
531
|
+
this.callbacks.push({ instrument, callback });
|
|
532
|
+
}
|
|
533
|
+
start() {
|
|
534
|
+
if (this.started) return;
|
|
535
|
+
this.started = true;
|
|
536
|
+
const systemCpu = this.meter.createObservableGauge("system.cpu.utilization", { unit: "1" });
|
|
537
|
+
this.observe(systemCpu, (result) => {
|
|
538
|
+
const cores = (0, import_node_os.cpus)();
|
|
539
|
+
let idle = 0;
|
|
540
|
+
let total = 0;
|
|
541
|
+
for (const core of cores) {
|
|
542
|
+
idle += core.times.idle;
|
|
543
|
+
total += Object.values(core.times).reduce((sum, value) => sum + value, 0);
|
|
544
|
+
}
|
|
545
|
+
result.observe(total ? (total - idle) / total : 0);
|
|
546
|
+
});
|
|
547
|
+
const processCpu = this.meter.createObservableCounter("process.cpu.time", { unit: "s" });
|
|
548
|
+
this.observe(processCpu, (result) => {
|
|
549
|
+
const cpu = process.cpuUsage();
|
|
550
|
+
result.observe((cpu.user + cpu.system) / 1e6);
|
|
551
|
+
});
|
|
552
|
+
const rss = this.meter.createObservableGauge("process.memory.rss", { unit: "By" });
|
|
553
|
+
const heapUsed = this.meter.createObservableGauge("nodejs.memory.heap.used", { unit: "By" });
|
|
554
|
+
const heapTotal = this.meter.createObservableGauge("nodejs.memory.heap.total", { unit: "By" });
|
|
555
|
+
this.observe(rss, (result) => result.observe(process.memoryUsage().rss));
|
|
556
|
+
this.observe(heapUsed, (result) => result.observe(process.memoryUsage().heapUsed));
|
|
557
|
+
this.observe(heapTotal, (result) => result.observe(process.memoryUsage().heapTotal));
|
|
558
|
+
this.eventLoopDelay = (0, import_node_perf_hooks.monitorEventLoopDelay)({ resolution: 20 });
|
|
559
|
+
this.eventLoopDelay.enable();
|
|
560
|
+
const delay = this.meter.createObservableGauge("nodejs.eventloop.delay", { unit: "s" });
|
|
561
|
+
this.observe(delay, (result) => {
|
|
562
|
+
const mean = this.eventLoopDelay?.mean ?? 0;
|
|
563
|
+
result.observe(Number.isFinite(mean) ? mean / 1e9 : 0);
|
|
564
|
+
this.eventLoopDelay?.reset();
|
|
565
|
+
});
|
|
566
|
+
const utilization = this.meter.createObservableGauge("nodejs.eventloop.utilization", { unit: "1" });
|
|
567
|
+
this.observe(utilization, (result) => {
|
|
568
|
+
const current = import_node_perf_hooks.performance.eventLoopUtilization(this.previousElu);
|
|
569
|
+
this.previousElu = import_node_perf_hooks.performance.eventLoopUtilization();
|
|
570
|
+
result.observe(current.utilization);
|
|
571
|
+
});
|
|
572
|
+
const gc = this.meter.createHistogram("nodejs.gc.duration", { unit: "s" });
|
|
573
|
+
this.gcObserver = new import_node_perf_hooks.PerformanceObserver((list) => {
|
|
574
|
+
for (const entry of list.getEntries()) {
|
|
575
|
+
const detail = entry.detail;
|
|
576
|
+
gc.record(entry.duration / 1e3, { "nodejs.gc.type": String(detail?.kind ?? "unknown") });
|
|
577
|
+
}
|
|
578
|
+
});
|
|
579
|
+
try {
|
|
580
|
+
this.gcObserver.observe({ entryTypes: ["gc"] });
|
|
581
|
+
} catch {
|
|
582
|
+
}
|
|
583
|
+
const uptime = this.meter.createObservableGauge("process.uptime", { unit: "s" });
|
|
584
|
+
this.observe(uptime, (result) => result.observe(process.uptime()));
|
|
585
|
+
}
|
|
586
|
+
stop() {
|
|
587
|
+
for (const { instrument, callback } of this.callbacks) instrument.removeCallback(callback);
|
|
588
|
+
this.callbacks.length = 0;
|
|
589
|
+
this.eventLoopDelay?.disable();
|
|
590
|
+
this.eventLoopDelay = void 0;
|
|
591
|
+
this.gcObserver?.disconnect();
|
|
592
|
+
this.gcObserver = void 0;
|
|
593
|
+
this.started = false;
|
|
594
|
+
}
|
|
595
|
+
};
|
|
596
|
+
|
|
597
|
+
// src/resource.ts
|
|
598
|
+
var import_node_os2 = require("os");
|
|
599
|
+
var import_resources = require("@opentelemetry/resources");
|
|
600
|
+
var SDK_NAME = "@ryanzen9/nest-observe";
|
|
601
|
+
var SDK_VERSION = "0.1.0";
|
|
602
|
+
function createObserveResource(config) {
|
|
603
|
+
return (0, import_resources.defaultResource)().merge((0, import_resources.resourceFromAttributes)({
|
|
604
|
+
...config.resourceAttributes,
|
|
605
|
+
"service.name": config.serviceName,
|
|
606
|
+
"service.version": config.serviceVersion,
|
|
607
|
+
"deployment.environment.name": config.environment,
|
|
608
|
+
"service.instance.id": config.instanceId,
|
|
609
|
+
"telemetry.sdk.name": SDK_NAME,
|
|
610
|
+
"telemetry.sdk.version": SDK_VERSION,
|
|
611
|
+
"host.name": config.resourceAttributes["host.name"] ?? (0, import_node_os2.hostname)()
|
|
612
|
+
}));
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
// src/security/span-redaction-processor.ts
|
|
616
|
+
var DROP_VALUE = /(?:request\.body|db\.(?:query\.)?parameters?|db\.statement\.parameters?|redis\.(?:args|value))/i;
|
|
617
|
+
function cleanValue(value) {
|
|
618
|
+
if (typeof value === "string") return redactText(value);
|
|
619
|
+
if (Array.isArray(value)) {
|
|
620
|
+
return value.map((item) => typeof item === "string" ? redactText(item) : item);
|
|
621
|
+
}
|
|
622
|
+
return value;
|
|
623
|
+
}
|
|
624
|
+
function cleanAttributes(attributes) {
|
|
625
|
+
if (!attributes) return;
|
|
626
|
+
const mutable = attributes;
|
|
627
|
+
for (const [key, value] of Object.entries(mutable)) {
|
|
628
|
+
if (value === void 0) continue;
|
|
629
|
+
if (DROP_VALUE.test(key) || isSensitiveKey(key)) mutable[key] = REDACTED;
|
|
630
|
+
else mutable[key] = cleanValue(value);
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
var SpanRedactionProcessor = class {
|
|
634
|
+
onStart(_span, _parentContext) {
|
|
635
|
+
}
|
|
636
|
+
onEnd(span) {
|
|
637
|
+
const mutable = span;
|
|
638
|
+
try {
|
|
639
|
+
mutable.name = redactText(mutable.name);
|
|
640
|
+
cleanAttributes(mutable.attributes);
|
|
641
|
+
if (mutable.status.message) mutable.status.message = redactText(mutable.status.message);
|
|
642
|
+
for (const event of mutable.events) cleanAttributes(event.attributes);
|
|
643
|
+
} catch {
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
forceFlush() {
|
|
647
|
+
return Promise.resolve();
|
|
648
|
+
}
|
|
649
|
+
shutdown() {
|
|
650
|
+
return Promise.resolve();
|
|
651
|
+
}
|
|
652
|
+
};
|
|
653
|
+
|
|
654
|
+
// src/sdk.ts
|
|
655
|
+
var InactiveObserveHandle = class {
|
|
656
|
+
constructor(config) {
|
|
657
|
+
this.config = config;
|
|
658
|
+
}
|
|
659
|
+
config;
|
|
660
|
+
started = false;
|
|
661
|
+
tracerProvider = void 0;
|
|
662
|
+
meterProvider = void 0;
|
|
663
|
+
loggerProvider = void 0;
|
|
664
|
+
forceFlush() {
|
|
665
|
+
return Promise.resolve();
|
|
666
|
+
}
|
|
667
|
+
shutdown() {
|
|
668
|
+
return Promise.resolve();
|
|
669
|
+
}
|
|
670
|
+
};
|
|
671
|
+
var ActiveObserveHandle = class {
|
|
672
|
+
constructor(config, tracerProvider, meterProvider, loggerProvider, runtimeMetrics, loggerInstrumentation, exceptionCapture, instrumentations) {
|
|
673
|
+
this.config = config;
|
|
674
|
+
this.tracerProvider = tracerProvider;
|
|
675
|
+
this.meterProvider = meterProvider;
|
|
676
|
+
this.loggerProvider = loggerProvider;
|
|
677
|
+
this.runtimeMetrics = runtimeMetrics;
|
|
678
|
+
this.loggerInstrumentation = loggerInstrumentation;
|
|
679
|
+
this.exceptionCapture = exceptionCapture;
|
|
680
|
+
this.instrumentations = instrumentations;
|
|
681
|
+
}
|
|
682
|
+
config;
|
|
683
|
+
tracerProvider;
|
|
684
|
+
meterProvider;
|
|
685
|
+
loggerProvider;
|
|
686
|
+
runtimeMetrics;
|
|
687
|
+
loggerInstrumentation;
|
|
688
|
+
exceptionCapture;
|
|
689
|
+
instrumentations;
|
|
690
|
+
started = true;
|
|
691
|
+
stopped = false;
|
|
692
|
+
async forceFlush() {
|
|
693
|
+
await Promise.allSettled([
|
|
694
|
+
this.tracerProvider?.forceFlush(),
|
|
695
|
+
this.meterProvider?.forceFlush(),
|
|
696
|
+
this.loggerProvider?.forceFlush({ timeoutMillis: this.config.exportTimeoutMillis })
|
|
697
|
+
].filter((item) => Boolean(item)));
|
|
698
|
+
}
|
|
699
|
+
async shutdown() {
|
|
700
|
+
if (this.stopped) return;
|
|
701
|
+
this.stopped = true;
|
|
702
|
+
this.loggerInstrumentation?.disable();
|
|
703
|
+
this.runtimeMetrics?.stop();
|
|
704
|
+
this.exceptionCapture.stop();
|
|
705
|
+
for (const instrumentation of this.instrumentations) {
|
|
706
|
+
try {
|
|
707
|
+
instrumentation.disable();
|
|
708
|
+
} catch {
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
await this.forceFlush();
|
|
712
|
+
await Promise.allSettled([
|
|
713
|
+
this.tracerProvider?.shutdown(),
|
|
714
|
+
this.meterProvider?.shutdown(),
|
|
715
|
+
this.loggerProvider?.shutdown()
|
|
716
|
+
].filter((item) => Boolean(item)));
|
|
717
|
+
if (activeRuntime === this) activeRuntime = void 0;
|
|
718
|
+
}
|
|
719
|
+
};
|
|
720
|
+
var activeRuntime;
|
|
721
|
+
function exporterConfig(url, config) {
|
|
722
|
+
return {
|
|
723
|
+
...url ? { url } : {},
|
|
724
|
+
headers: config.headers,
|
|
725
|
+
timeoutMillis: config.exportTimeoutMillis
|
|
726
|
+
};
|
|
727
|
+
}
|
|
728
|
+
function createTraceProvider(config, resource) {
|
|
729
|
+
if (!config.traces && !config.metrics) return void 0;
|
|
730
|
+
const spanProcessors = [new SpanRedactionProcessor()];
|
|
731
|
+
if (config.traces) {
|
|
732
|
+
const exporter = config.exporters?.span ?? new import_exporter_trace_otlp_proto.OTLPTraceExporter(exporterConfig(config.endpoints.traces, config));
|
|
733
|
+
spanProcessors.push(new import_sdk_trace_base.BatchSpanProcessor(exporter, {
|
|
734
|
+
exportTimeoutMillis: config.exportTimeoutMillis,
|
|
735
|
+
maxQueueSize: 2048,
|
|
736
|
+
maxExportBatchSize: 512
|
|
737
|
+
}));
|
|
738
|
+
}
|
|
739
|
+
const provider = new import_sdk_trace_node.NodeTracerProvider({
|
|
740
|
+
resource,
|
|
741
|
+
sampler: config.traces ? new import_sdk_trace_base.ParentBasedSampler({ root: new import_sdk_trace_base.TraceIdRatioBasedSampler(config.sampling) }) : new import_sdk_trace_base.AlwaysOffSampler(),
|
|
742
|
+
spanProcessors,
|
|
743
|
+
spanLimits: { attributeCountLimit: 64, eventCountLimit: 64, linkCountLimit: 16 }
|
|
744
|
+
});
|
|
745
|
+
provider.register();
|
|
746
|
+
return provider;
|
|
747
|
+
}
|
|
748
|
+
function createMeterProvider(config, resource) {
|
|
749
|
+
if (!config.metrics) return void 0;
|
|
750
|
+
let reader;
|
|
751
|
+
if (config.exporters?.metricReader) {
|
|
752
|
+
reader = config.exporters.metricReader;
|
|
753
|
+
} else {
|
|
754
|
+
reader = new import_sdk_metrics.PeriodicExportingMetricReader({
|
|
755
|
+
exporter: new import_exporter_metrics_otlp_proto.OTLPMetricExporter(exporterConfig(config.endpoints.metrics, config)),
|
|
756
|
+
exportIntervalMillis: config.metricExportIntervalMillis,
|
|
757
|
+
exportTimeoutMillis: Math.min(config.exportTimeoutMillis, config.metricExportIntervalMillis),
|
|
758
|
+
cardinalityLimits: { default: 2e3, histogram: 2e3 }
|
|
759
|
+
});
|
|
760
|
+
}
|
|
761
|
+
const provider = new import_sdk_metrics.MeterProvider({
|
|
762
|
+
resource,
|
|
763
|
+
readers: [reader],
|
|
764
|
+
views: [{
|
|
765
|
+
instrumentName: "http.server.request.duration",
|
|
766
|
+
meterName: "@opentelemetry/instrumentation-http",
|
|
767
|
+
aggregation: { type: import_sdk_metrics.AggregationType.DROP }
|
|
768
|
+
}]
|
|
769
|
+
});
|
|
770
|
+
import_api5.metrics.setGlobalMeterProvider(provider);
|
|
771
|
+
return provider;
|
|
772
|
+
}
|
|
773
|
+
function createLoggerProvider(config, resource) {
|
|
774
|
+
if (!config.logs) return void 0;
|
|
775
|
+
const exporter = config.exporters?.log ?? new import_exporter_logs_otlp_proto.OTLPLogExporter(exporterConfig(config.endpoints.logs, config));
|
|
776
|
+
const provider = new import_sdk_logs.LoggerProvider({
|
|
777
|
+
resource,
|
|
778
|
+
processors: [new import_sdk_logs.BatchLogRecordProcessor({
|
|
779
|
+
exporter,
|
|
780
|
+
exportTimeoutMillis: config.exportTimeoutMillis,
|
|
781
|
+
maxQueueSize: 2048,
|
|
782
|
+
maxExportBatchSize: 512
|
|
783
|
+
})]
|
|
784
|
+
});
|
|
785
|
+
import_api_logs2.logs.setGlobalLoggerProvider(provider);
|
|
786
|
+
return provider;
|
|
787
|
+
}
|
|
788
|
+
function observe(options = {}) {
|
|
789
|
+
if (activeRuntime?.started) return activeRuntime;
|
|
790
|
+
const config = resolveObserveConfig(options);
|
|
791
|
+
if (!config.enabled) return new InactiveObserveHandle(config);
|
|
792
|
+
let tracerProvider;
|
|
793
|
+
let meterProvider;
|
|
794
|
+
let loggerProvider;
|
|
795
|
+
let runtimeMetrics;
|
|
796
|
+
let loggerInstrumentation;
|
|
797
|
+
let exceptionCapture;
|
|
798
|
+
const instrumentations = [];
|
|
799
|
+
try {
|
|
800
|
+
const resource = createObserveResource(config);
|
|
801
|
+
meterProvider = createMeterProvider(config, resource);
|
|
802
|
+
const meter = meterProvider?.getMeter(SDK_NAME, SDK_VERSION);
|
|
803
|
+
tracerProvider = createTraceProvider(config, resource);
|
|
804
|
+
loggerProvider = createLoggerProvider(config, resource);
|
|
805
|
+
runtimeMetrics = meter ? new RuntimeMetrics(meter) : void 0;
|
|
806
|
+
runtimeMetrics?.start();
|
|
807
|
+
const otelLogger = loggerProvider?.getLogger(SDK_NAME, SDK_VERSION);
|
|
808
|
+
const logEmitter = config.logs ? new OpenTelemetryLogEmitter(SDK_NAME, SDK_VERSION, otelLogger) : void 0;
|
|
809
|
+
loggerInstrumentation = logEmitter ? new NestLoggerInstrumentation(logEmitter, {
|
|
810
|
+
"service.name": config.serviceName,
|
|
811
|
+
"service.version": config.serviceVersion,
|
|
812
|
+
"deployment.environment.name": config.environment
|
|
813
|
+
}) : void 0;
|
|
814
|
+
loggerInstrumentation?.enable();
|
|
815
|
+
exceptionCapture = new ProcessExceptionCapture(logEmitter);
|
|
816
|
+
exceptionCapture.start();
|
|
817
|
+
if (config.traces || config.metrics) {
|
|
818
|
+
const safeHeaders = config.allowedHeaders.filter((header) => !isSensitiveKey(header));
|
|
819
|
+
const httpRequestMetrics = meter ? new HttpRequestMetrics(meter, config.serviceName) : void 0;
|
|
820
|
+
instrumentations.push(new import_instrumentation_http.HttpInstrumentation({
|
|
821
|
+
requireParentforOutgoingSpans: false,
|
|
822
|
+
headersToSpanAttributes: {
|
|
823
|
+
client: { requestHeaders: safeHeaders, responseHeaders: safeHeaders },
|
|
824
|
+
server: { requestHeaders: safeHeaders, responseHeaders: safeHeaders }
|
|
825
|
+
},
|
|
826
|
+
redactedQueryParams: [
|
|
827
|
+
"sig",
|
|
828
|
+
"Signature",
|
|
829
|
+
"AWSAccessKeyId",
|
|
830
|
+
"X-Goog-Signature",
|
|
831
|
+
"password",
|
|
832
|
+
"passwd",
|
|
833
|
+
"token",
|
|
834
|
+
"access_token",
|
|
835
|
+
"api_key",
|
|
836
|
+
"secret"
|
|
837
|
+
],
|
|
838
|
+
...httpRequestMetrics ? {
|
|
839
|
+
requestHook: (_span, request) => {
|
|
840
|
+
if (!("getHeader" in request)) httpRequestMetrics.start(request);
|
|
841
|
+
},
|
|
842
|
+
applyCustomAttributesOnSpan: (_span, request, response) => {
|
|
843
|
+
if ("setHeader" in response) httpRequestMetrics.record(request, response);
|
|
844
|
+
}
|
|
845
|
+
} : {}
|
|
846
|
+
}));
|
|
847
|
+
instrumentations.push(new CompatibleNestInstrumentation({
|
|
848
|
+
providerTracing: config.providerTracing,
|
|
849
|
+
controllerTracing: config.controllerTracing
|
|
850
|
+
}));
|
|
851
|
+
if (config.traces) instrumentations.push(new import_instrumentation3.PrismaInstrumentation());
|
|
852
|
+
(0, import_instrumentation2.registerInstrumentations)({
|
|
853
|
+
instrumentations,
|
|
854
|
+
...tracerProvider ? { tracerProvider } : {},
|
|
855
|
+
...meterProvider ? { meterProvider } : {}
|
|
856
|
+
});
|
|
857
|
+
}
|
|
858
|
+
const runtime = new ActiveObserveHandle(
|
|
859
|
+
config,
|
|
860
|
+
tracerProvider,
|
|
861
|
+
meterProvider,
|
|
862
|
+
loggerProvider,
|
|
863
|
+
runtimeMetrics,
|
|
864
|
+
loggerInstrumentation,
|
|
865
|
+
exceptionCapture,
|
|
866
|
+
instrumentations
|
|
867
|
+
);
|
|
868
|
+
activeRuntime = runtime;
|
|
869
|
+
return runtime;
|
|
870
|
+
} catch {
|
|
871
|
+
loggerInstrumentation?.disable();
|
|
872
|
+
runtimeMetrics?.stop();
|
|
873
|
+
exceptionCapture?.stop();
|
|
874
|
+
for (const instrumentation of instrumentations) {
|
|
875
|
+
try {
|
|
876
|
+
instrumentation.disable();
|
|
877
|
+
} catch {
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
void Promise.allSettled([
|
|
881
|
+
tracerProvider?.shutdown(),
|
|
882
|
+
meterProvider?.shutdown(),
|
|
883
|
+
loggerProvider?.shutdown()
|
|
884
|
+
].filter((item) => Boolean(item)));
|
|
885
|
+
return new InactiveObserveHandle(config);
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
// src/register.ts
|
|
890
|
+
observe();
|
|
891
|
+
//# sourceMappingURL=register.js.map
|