@ryanzeng/nest-observe 0.1.4 → 0.1.5
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 +4 -2
- package/dist/index.d.mts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +84 -11
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +84 -11
- package/dist/index.mjs.map +1 -1
- package/dist/register.js +84 -11
- package/dist/register.js.map +1 -1
- package/dist/register.mjs +84 -11
- package/dist/register.mjs.map +1 -1
- package/package.json +9 -10
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+

|
|
2
2
|
|
|
3
3
|
面向 NestJS 的零配置、Vendor Neutral Observability SDK。基于 OpenTelemetry,通过标准 OTLP 同时发送 Traces、Metrics 和 Logs,可连接 OpenObserve、Grafana、Jaeger、Datadog、Elastic 等兼容后端。
|
|
4
4
|
|
|
@@ -166,8 +166,10 @@ Traces:
|
|
|
166
166
|
Logs:
|
|
167
167
|
|
|
168
168
|
- 默认 Nest `Logger` 的 `verbose/debug/log/warn/error/fatal`
|
|
169
|
+
- 遵循 Nest `ConsoleLogger` 的重载解析语义,保留 `error(message, stack?, context?)`、附加消息和结构化参数
|
|
170
|
+
- `Error` 或显式 stack 会映射为 OTLP `exception.type`、`exception.message`、`exception.stacktrace` 属性
|
|
169
171
|
- 尊重 Nest 11+ 的实例级日志级别过滤(`logLevels` / `setLogLevels` / `Logger.overrideLogger`),上传内容与控制台输出一致
|
|
170
|
-
- 对象和数组消息会在脱敏后保持结构化 body
|
|
172
|
+
- 对象和数组消息会在脱敏后保持结构化 body;附加的结构化参数会展开为 OTLP attributes,同时保持首个 message 为 body
|
|
171
173
|
- OTLP severity、`nestjs.context`、`trace_id`、`span_id`
|
|
172
174
|
- `service.name`、`service.version`、`deployment.environment.name`
|
|
173
175
|
|
package/dist/index.d.mts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { LogRecordExporter, LoggerProvider } from '@opentelemetry/sdk-logs';
|
|
2
2
|
import { MetricReader, MeterProvider } from '@opentelemetry/sdk-metrics';
|
|
3
3
|
import { SpanExporter, SpanProcessor, ReadableSpan } from '@opentelemetry/sdk-trace-base';
|
|
4
|
-
import {
|
|
5
|
-
import { Logger } from '@opentelemetry/api-logs';
|
|
4
|
+
import { LogAttributes, Logger } from '@opentelemetry/api-logs';
|
|
6
5
|
import { InstrumentationConfig, InstrumentationNodeModuleDefinition } from '@opentelemetry/instrumentation';
|
|
7
6
|
import { NestInstrumentation } from '@opentelemetry/instrumentation-nestjs-core';
|
|
7
|
+
import { Meter, Tracer, Span, Context } from '@opentelemetry/api';
|
|
8
8
|
import { IncomingMessage, ServerResponse } from 'node:http';
|
|
9
9
|
import { NestInterceptor, ExecutionContext, CallHandler, DynamicModule } from '@nestjs/common';
|
|
10
10
|
import { Observable } from 'rxjs';
|
|
@@ -109,7 +109,7 @@ declare function Trace(nameOrOptions?: string | TraceOptions): TraceDecorator;
|
|
|
109
109
|
interface StructuredLogRecord {
|
|
110
110
|
body: unknown;
|
|
111
111
|
severityText: 'TRACE' | 'DEBUG' | 'INFO' | 'WARN' | 'ERROR' | 'FATAL';
|
|
112
|
-
attributes:
|
|
112
|
+
attributes: LogAttributes;
|
|
113
113
|
timestamp?: number;
|
|
114
114
|
}
|
|
115
115
|
interface StructuredLogEmitter {
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { LogRecordExporter, LoggerProvider } from '@opentelemetry/sdk-logs';
|
|
2
2
|
import { MetricReader, MeterProvider } from '@opentelemetry/sdk-metrics';
|
|
3
3
|
import { SpanExporter, SpanProcessor, ReadableSpan } from '@opentelemetry/sdk-trace-base';
|
|
4
|
-
import {
|
|
5
|
-
import { Logger } from '@opentelemetry/api-logs';
|
|
4
|
+
import { LogAttributes, Logger } from '@opentelemetry/api-logs';
|
|
6
5
|
import { InstrumentationConfig, InstrumentationNodeModuleDefinition } from '@opentelemetry/instrumentation';
|
|
7
6
|
import { NestInstrumentation } from '@opentelemetry/instrumentation-nestjs-core';
|
|
7
|
+
import { Meter, Tracer, Span, Context } from '@opentelemetry/api';
|
|
8
8
|
import { IncomingMessage, ServerResponse } from 'node:http';
|
|
9
9
|
import { NestInterceptor, ExecutionContext, CallHandler, DynamicModule } from '@nestjs/common';
|
|
10
10
|
import { Observable } from 'rxjs';
|
|
@@ -109,7 +109,7 @@ declare function Trace(nameOrOptions?: string | TraceOptions): TraceDecorator;
|
|
|
109
109
|
interface StructuredLogRecord {
|
|
110
110
|
body: unknown;
|
|
111
111
|
severityText: 'TRACE' | 'DEBUG' | 'INFO' | 'WARN' | 'ERROR' | 'FATAL';
|
|
112
|
-
attributes:
|
|
112
|
+
attributes: LogAttributes;
|
|
113
113
|
timestamp?: number;
|
|
114
114
|
}
|
|
115
115
|
interface StructuredLogEmitter {
|
package/dist/index.js
CHANGED
|
@@ -347,6 +347,71 @@ function bodyValue(message) {
|
|
|
347
347
|
return redactText(message.stack ?? `${message.name}: ${message.message}`);
|
|
348
348
|
return redact(message);
|
|
349
349
|
}
|
|
350
|
+
function isPlainObject(value) {
|
|
351
|
+
if (value === null || typeof value !== "object") return false;
|
|
352
|
+
const prototype = Object.getPrototypeOf(value);
|
|
353
|
+
if (prototype === null) return true;
|
|
354
|
+
const constructor = Object.prototype.hasOwnProperty.call(prototype, "constructor") ? prototype.constructor : void 0;
|
|
355
|
+
return typeof constructor === "function" && constructor instanceof constructor && Function.prototype.toString.call(constructor) === Function.prototype.toString.call(Object);
|
|
356
|
+
}
|
|
357
|
+
function isStackFormat(value) {
|
|
358
|
+
return typeof value === "string" && /^(.)+\n\s+at .+:\d+:\d+/.test(value);
|
|
359
|
+
}
|
|
360
|
+
function fallbackMessages(logger, values) {
|
|
361
|
+
if (values.length <= 1) return { messages: values, context: logger.context };
|
|
362
|
+
const lastValue = values.at(-1);
|
|
363
|
+
const contextName = typeof lastValue === "string" ? lastValue : logger.context;
|
|
364
|
+
const remaining = typeof lastValue === "string" ? values.slice(0, -1) : values;
|
|
365
|
+
if (logger.options?.structuredParams === false) {
|
|
366
|
+
return { messages: remaining, context: contextName };
|
|
367
|
+
}
|
|
368
|
+
const messages = [remaining[0]];
|
|
369
|
+
const structuredParams = [];
|
|
370
|
+
for (const value of remaining.slice(1)) {
|
|
371
|
+
if (isPlainObject(value)) structuredParams.push(value);
|
|
372
|
+
else messages.push(value);
|
|
373
|
+
}
|
|
374
|
+
const params = structuredParams.length > 0 ? Object.assign({}, ...structuredParams) : void 0;
|
|
375
|
+
return { messages, context: contextName, ...params ? { params } : {} };
|
|
376
|
+
}
|
|
377
|
+
function fallbackError(logger, values) {
|
|
378
|
+
if (values.length === 2) {
|
|
379
|
+
if (isStackFormat(values[1])) {
|
|
380
|
+
return { messages: [values[0]], stack: values[1], context: logger.context };
|
|
381
|
+
}
|
|
382
|
+
return fallbackMessages(logger, values);
|
|
383
|
+
}
|
|
384
|
+
const trailingValue = values.at(-1);
|
|
385
|
+
if (isStackFormat(trailingValue)) {
|
|
386
|
+
return { ...fallbackMessages(logger, values.slice(0, -1)), stack: trailingValue };
|
|
387
|
+
}
|
|
388
|
+
const parsed = fallbackMessages(logger, values);
|
|
389
|
+
if (parsed.messages.length <= 1) return parsed;
|
|
390
|
+
const possibleStack = parsed.messages.at(-1);
|
|
391
|
+
if (typeof possibleStack !== "string" && possibleStack !== void 0) return parsed;
|
|
392
|
+
return {
|
|
393
|
+
...parsed,
|
|
394
|
+
messages: parsed.messages.slice(0, -1),
|
|
395
|
+
...possibleStack === void 0 ? {} : { stack: possibleStack }
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
function parseLogCall(logger, method, message, args) {
|
|
399
|
+
const values = [message, ...args];
|
|
400
|
+
const nestParser = method === "error" ? logger.getContextAndStackAndMessagesToPrint : logger.getContextAndMessagesToPrint;
|
|
401
|
+
if (typeof nestParser === "function") return nestParser.call(logger, values);
|
|
402
|
+
return method === "error" ? fallbackError(logger, values) : fallbackMessages(logger, values);
|
|
403
|
+
}
|
|
404
|
+
function exceptionAttributes(message, stack) {
|
|
405
|
+
const error = message instanceof Error ? message : void 0;
|
|
406
|
+
const stacktrace = stack ?? error?.stack;
|
|
407
|
+
return {
|
|
408
|
+
...error ? {
|
|
409
|
+
"exception.type": error.name,
|
|
410
|
+
"exception.message": redactText(error.message)
|
|
411
|
+
} : {},
|
|
412
|
+
...stacktrace ? { "exception.stacktrace": redactText(stacktrace) } : {}
|
|
413
|
+
};
|
|
414
|
+
}
|
|
350
415
|
var NestLoggerInstrumentation = class {
|
|
351
416
|
constructor(emitter, resourceAttributes = {}) {
|
|
352
417
|
this.emitter = emitter;
|
|
@@ -373,17 +438,25 @@ var NestLoggerInstrumentation = class {
|
|
|
373
438
|
return original.call(this, message, ...args);
|
|
374
439
|
}
|
|
375
440
|
const spanContext = import_api3.trace.getActiveSpan()?.spanContext();
|
|
376
|
-
const
|
|
377
|
-
const
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
441
|
+
const parsed = parseLogCall(this, method, message, args);
|
|
442
|
+
const timestamp = Date.now();
|
|
443
|
+
for (const parsedMessage of parsed.messages) {
|
|
444
|
+
const params = parsed.params ? redact(parsed.params) : {};
|
|
445
|
+
const attributes = {
|
|
446
|
+
...params,
|
|
447
|
+
...resourceAttributes,
|
|
448
|
+
...exceptionAttributes(parsedMessage, parsed.stack)
|
|
449
|
+
};
|
|
450
|
+
if (parsed.context) attributes["nestjs.context"] = parsed.context;
|
|
451
|
+
if (spanContext?.traceId) attributes.trace_id = spanContext.traceId;
|
|
452
|
+
if (spanContext?.spanId) attributes.span_id = spanContext.spanId;
|
|
453
|
+
emitter.emit({
|
|
454
|
+
body: bodyValue(parsedMessage),
|
|
455
|
+
severityText: SEVERITY[method],
|
|
456
|
+
attributes,
|
|
457
|
+
timestamp
|
|
458
|
+
});
|
|
459
|
+
}
|
|
387
460
|
} catch {
|
|
388
461
|
}
|
|
389
462
|
return original.call(this, message, ...args);
|