@ryanzeng/nest-observe 0.1.1 → 0.1.3

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 CHANGED
@@ -36,7 +36,7 @@ OBSERVE_ENVIRONMENT=production
36
36
 
37
37
  通用 endpoint 会自动派生 `/v1/traces`、`/v1/metrics` 和 `/v1/logs`。也可使用标准的 `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT`、`OTEL_EXPORTER_OTLP_METRICS_ENDPOINT`、`OTEL_EXPORTER_OTLP_LOGS_ENDPOINT` 分别覆盖。
38
38
 
39
- 初始化或 exporter 配置失败时,SDK 会降级为 no-op,不会阻止业务应用启动。
39
+ 初始化或 exporter 配置失败时,SDK 默认保持 fail-open,不会阻止业务应用启动;同时会把脱敏、限频后的诊断信息写入 `stderr`,避免观测链路静默失效。
40
40
 
41
41
  ## OpenObserve 完整示例
42
42
 
@@ -56,6 +56,8 @@ import { ObserveModule } from '@ryanzeng/nest-observe';
56
56
  export class AppModule {}
57
57
  ```
58
58
 
59
+ Module-only 模式还会注册一个全局 Nest interceptor,补采因 HTTP 模块已经加载而无法挂载底层 hook 的已匹配 Controller 请求指标。它与早期 HTTP instrumentation 共享请求状态,不会重复计数。
60
+
59
61
  ## 装饰器
60
62
 
61
63
  ```ts
@@ -95,6 +97,7 @@ Traces:
95
97
  Logs:
96
98
 
97
99
  - 默认 Nest `Logger` 的 `verbose/debug/log/warn/error/fatal`
100
+ - 对象和数组消息会在脱敏后保持结构化 body,由观测后端或查询侧决定如何展开、展示或序列化
98
101
  - OTLP severity、`nestjs.context`、`trace_id`、`span_id`
99
102
  - `service.name`、`service.version`、`deployment.environment.name`
100
103
  - `StructuredLogEmitter` 抽象可用于后续 Pino/Winston adapter
@@ -110,6 +113,8 @@ Metrics:
110
113
 
111
114
  HTTP 与 Nest method 指标只使用 route template、method、status、controller/provider/method 等有限维度,避免把 URL id 或请求数据变成高基数标签。
112
115
 
116
+ OpenObserve 会把 OTel metric 名称中的 `.` 转换为 `_`,例如 `process.uptime` 对应 `process_uptime` stream,`http.server.request.count` 对应 `http_server_request_count`。指标使用独立的 Metrics streams,不在 Logs 的 `default` stream 中;首次数据默认最多需要等待 `metricExportIntervalMillis`(默认 60 秒),也可以调用 `forceFlush()` 立即导出。应用关闭时 SDK 会先采集并 flush runtime observable metrics,再移除 callbacks。
117
+
113
118
  ## 程序化配置
114
119
 
115
120
  ```ts
@@ -134,6 +139,27 @@ await telemetry.shutdown();
134
139
 
135
140
  环境变量方式仍是推荐方式。有合理默认值的配置无需填写。
136
141
 
142
+ ### 故障诊断
143
+
144
+ SDK 会区分本地 pipeline 已启动和远端导出是否健康。`status` 可能为 `inactive`、`starting`、`active`、`degraded` 或 `stopped`;例如 OTLP endpoint 返回 401 时,业务继续运行,但状态会变为 `degraded`:
145
+
146
+ ```ts
147
+ const telemetry = observe({
148
+ serviceName: 'mall-app',
149
+ endpoint: 'https://observe.example.com',
150
+ onError(event) {
151
+ // event.error 已脱敏,不包含 Authorization、token 等凭据。
152
+ console.error(event.signal, event.stage, event.error.message);
153
+ },
154
+ });
155
+
156
+ console.log(telemetry.status, telemetry.lastError);
157
+ ```
158
+
159
+ 默认诊断日志直接写入原始 `stderr`,不会经过 Nest Logger,因而不会形成“导出失败日志再次导出”的递归。相同错误在恢复前只报告一次。可以设置 `diagnosticLogging: false` 关闭默认输出,并继续通过 `onError` 接收事件。
160
+
161
+ 同步初始化失败默认返回 `started: false`、`status: 'inactive'` 的 handle。测试或严格部署场景可以设置 `failFast: true`,让同步初始化错误直接阻止 Nest 启动;异步 OTLP 导出错误仍采用 fail-open,并通过 `degraded`、`lastError` 和 `onError` 暴露。
162
+
137
163
  ### Sampling
138
164
 
139
165
  支持显式 `sampling`(0–1)以及标准环境变量:
package/dist/index.d.mts CHANGED
@@ -6,9 +6,10 @@ import { Logger } from '@opentelemetry/api-logs';
6
6
  import { InstrumentationConfig, InstrumentationNodeModuleDefinition } from '@opentelemetry/instrumentation';
7
7
  import { NestInstrumentation } from '@opentelemetry/instrumentation-nestjs-core';
8
8
  import { IncomingMessage, ServerResponse } from 'node:http';
9
- import { DynamicModule } from '@nestjs/common';
10
- import { Resource } from '@opentelemetry/resources';
9
+ import { NestInterceptor, ExecutionContext, CallHandler, DynamicModule } from '@nestjs/common';
10
+ import { Observable } from 'rxjs';
11
11
  import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node';
12
+ import { Resource } from '@opentelemetry/resources';
12
13
 
13
14
  interface ObserveExporters {
14
15
  /** Primarily useful for custom backends and tests. OTLP is used by default. */
@@ -16,6 +17,15 @@ interface ObserveExporters {
16
17
  log?: LogRecordExporter;
17
18
  metricReader?: MetricReader;
18
19
  }
20
+ type ObserveSignal = 'sdk' | 'traces' | 'metrics' | 'logs';
21
+ type ObserveErrorStage = 'initialization' | 'export' | 'forceFlush' | 'shutdown';
22
+ type ObserveStatus = 'inactive' | 'starting' | 'active' | 'degraded' | 'stopped';
23
+ interface ObserveErrorEvent {
24
+ signal: ObserveSignal;
25
+ stage: ObserveErrorStage;
26
+ error: Error;
27
+ timestamp: number;
28
+ }
19
29
  interface ObserveOptions {
20
30
  enabled?: boolean;
21
31
  serviceName?: string;
@@ -34,6 +44,12 @@ interface ObserveOptions {
34
44
  exportTimeoutMillis?: number;
35
45
  metricExportIntervalMillis?: number;
36
46
  resourceAttributes?: Record<string, string | number | boolean>;
47
+ /** Writes sanitized, rate-limited SDK/export failures to stderr. Defaults to true. */
48
+ diagnosticLogging?: boolean;
49
+ /** Receives sanitized pipeline failure notifications without affecting the application. */
50
+ onError?: (event: ObserveErrorEvent) => void;
51
+ /** Throws synchronous initialization failures instead of degrading to an inactive handle. */
52
+ failFast?: boolean;
37
53
  exporters?: ObserveExporters;
38
54
  }
39
55
  interface ResolvedObserveConfig {
@@ -58,10 +74,14 @@ interface ResolvedObserveConfig {
58
74
  exportTimeoutMillis: number;
59
75
  metricExportIntervalMillis: number;
60
76
  resourceAttributes: Record<string, string | number | boolean>;
77
+ diagnosticLogging: boolean;
78
+ failFast: boolean;
61
79
  exporters?: ObserveExporters;
62
80
  }
63
81
  interface ObserveHandle {
64
82
  readonly started: boolean;
83
+ readonly status: ObserveStatus;
84
+ readonly lastError: ObserveErrorEvent | undefined;
65
85
  readonly config: Readonly<ResolvedObserveConfig>;
66
86
  forceFlush(): Promise<void>;
67
87
  shutdown(): Promise<void>;
@@ -132,8 +152,8 @@ declare class HttpRequestMetrics {
132
152
  private readonly errorCount;
133
153
  private readonly startedAt;
134
154
  constructor(meter: Meter, serviceName: string);
135
- start(request: IncomingMessage): void;
136
- record(request: IncomingMessage, response: ServerResponse): void;
155
+ start(request: IncomingMessage): boolean;
156
+ record(request: IncomingMessage, response: ServerResponse): boolean;
137
157
  }
138
158
 
139
159
  declare class RuntimeMetrics {
@@ -157,19 +177,34 @@ declare class NestMethodInstrumenter {
157
177
  private readonly errors;
158
178
  private readonly instrumented;
159
179
  constructor(tracer: Tracer, meter: Meter);
160
- instrumentInstance(instance: object, kind: NestComponentKind, componentName?: string): void;
161
- instrumentPrototype(type: Function, kind: NestComponentKind, componentName?: string): void;
180
+ instrumentInstance(instance: object, kind: NestComponentKind, componentName?: unknown): void;
181
+ instrumentPrototype(type: Function, kind: NestComponentKind, componentName?: unknown): void;
162
182
  private instrumentTarget;
163
183
  }
164
184
 
185
+ interface ObserveRuntime extends ObserveHandle {
186
+ readonly tracerProvider: NodeTracerProvider | undefined;
187
+ readonly meterProvider: MeterProvider | undefined;
188
+ readonly loggerProvider: LoggerProvider | undefined;
189
+ readonly httpRequestMetrics: HttpRequestMetrics | undefined;
190
+ }
191
+ declare function observe(options?: ObserveOptions): ObserveRuntime;
192
+ declare function getObserveRuntime(): ObserveRuntime | undefined;
193
+
165
194
  declare const OBSERVE_OPTIONS: unique symbol;
166
195
  declare const OBSERVE_HANDLE: unique symbol;
196
+ /** Provides inbound HTTP metrics when module-load instrumentation was registered too late. */
197
+ declare class NestHttpMetricsInterceptor implements NestInterceptor {
198
+ private readonly handle;
199
+ constructor(handle: ObserveRuntime);
200
+ intercept(context: ExecutionContext, next: CallHandler): Observable<unknown>;
201
+ }
167
202
  declare class ObserveModule {
168
203
  static forRoot(options?: ObserveOptions): DynamicModule;
169
204
  }
170
205
 
171
- declare const SDK_NAME = "@ryanzeng/nest-observe";
172
- declare const SDK_VERSION = "0.1.0";
206
+ declare const SDK_NAME: string;
207
+ declare const SDK_VERSION: string;
173
208
  declare function createObserveResource(config: ResolvedObserveConfig): Resource;
174
209
 
175
210
  declare const REDACTED = "[REDACTED]";
@@ -186,12 +221,4 @@ declare class SpanRedactionProcessor implements SpanProcessor {
186
221
  shutdown(): Promise<void>;
187
222
  }
188
223
 
189
- interface ObserveRuntime extends ObserveHandle {
190
- readonly tracerProvider: NodeTracerProvider | undefined;
191
- readonly meterProvider: MeterProvider | undefined;
192
- readonly loggerProvider: LoggerProvider | undefined;
193
- }
194
- declare function observe(options?: ObserveOptions): ObserveRuntime;
195
- declare function getObserveRuntime(): ObserveRuntime | undefined;
196
-
197
- export { CompatibleNestInstrumentation, type CompatibleNestInstrumentationConfig, HttpRequestMetrics, IgnoreTrace, type NestComponentKind, NestLoggerInstrumentation, NestMethodInstrumenter, OBSERVE_HANDLE, OBSERVE_OPTIONS, type ObserveExporters, type ObserveHandle, ObserveModule, type ObserveOptions, type ObserveRuntime, OpenTelemetryLogEmitter, REDACTED, type ResolvedObserveConfig, RuntimeMetrics, SDK_NAME, SDK_VERSION, SpanRedactionProcessor, type StructuredLogEmitter, type StructuredLogRecord, Trace, type TraceOptions, createObserveResource, getObserveRuntime, isSensitiveKey, isTraceDecorated, isTraceIgnored, markTraceDecorated, markTraceIgnored, observe, parseKeyValueList, redact, redactText, resolveObserveConfig, sanitizeHeaders };
224
+ export { CompatibleNestInstrumentation, type CompatibleNestInstrumentationConfig, HttpRequestMetrics, IgnoreTrace, type NestComponentKind, NestHttpMetricsInterceptor, NestLoggerInstrumentation, NestMethodInstrumenter, OBSERVE_HANDLE, OBSERVE_OPTIONS, type ObserveErrorEvent, type ObserveErrorStage, type ObserveExporters, type ObserveHandle, ObserveModule, type ObserveOptions, type ObserveRuntime, type ObserveSignal, type ObserveStatus, OpenTelemetryLogEmitter, REDACTED, type ResolvedObserveConfig, RuntimeMetrics, SDK_NAME, SDK_VERSION, SpanRedactionProcessor, type StructuredLogEmitter, type StructuredLogRecord, Trace, type TraceOptions, createObserveResource, getObserveRuntime, isSensitiveKey, isTraceDecorated, isTraceIgnored, markTraceDecorated, markTraceIgnored, observe, parseKeyValueList, redact, redactText, resolveObserveConfig, sanitizeHeaders };
package/dist/index.d.ts CHANGED
@@ -6,9 +6,10 @@ import { Logger } from '@opentelemetry/api-logs';
6
6
  import { InstrumentationConfig, InstrumentationNodeModuleDefinition } from '@opentelemetry/instrumentation';
7
7
  import { NestInstrumentation } from '@opentelemetry/instrumentation-nestjs-core';
8
8
  import { IncomingMessage, ServerResponse } from 'node:http';
9
- import { DynamicModule } from '@nestjs/common';
10
- import { Resource } from '@opentelemetry/resources';
9
+ import { NestInterceptor, ExecutionContext, CallHandler, DynamicModule } from '@nestjs/common';
10
+ import { Observable } from 'rxjs';
11
11
  import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node';
12
+ import { Resource } from '@opentelemetry/resources';
12
13
 
13
14
  interface ObserveExporters {
14
15
  /** Primarily useful for custom backends and tests. OTLP is used by default. */
@@ -16,6 +17,15 @@ interface ObserveExporters {
16
17
  log?: LogRecordExporter;
17
18
  metricReader?: MetricReader;
18
19
  }
20
+ type ObserveSignal = 'sdk' | 'traces' | 'metrics' | 'logs';
21
+ type ObserveErrorStage = 'initialization' | 'export' | 'forceFlush' | 'shutdown';
22
+ type ObserveStatus = 'inactive' | 'starting' | 'active' | 'degraded' | 'stopped';
23
+ interface ObserveErrorEvent {
24
+ signal: ObserveSignal;
25
+ stage: ObserveErrorStage;
26
+ error: Error;
27
+ timestamp: number;
28
+ }
19
29
  interface ObserveOptions {
20
30
  enabled?: boolean;
21
31
  serviceName?: string;
@@ -34,6 +44,12 @@ interface ObserveOptions {
34
44
  exportTimeoutMillis?: number;
35
45
  metricExportIntervalMillis?: number;
36
46
  resourceAttributes?: Record<string, string | number | boolean>;
47
+ /** Writes sanitized, rate-limited SDK/export failures to stderr. Defaults to true. */
48
+ diagnosticLogging?: boolean;
49
+ /** Receives sanitized pipeline failure notifications without affecting the application. */
50
+ onError?: (event: ObserveErrorEvent) => void;
51
+ /** Throws synchronous initialization failures instead of degrading to an inactive handle. */
52
+ failFast?: boolean;
37
53
  exporters?: ObserveExporters;
38
54
  }
39
55
  interface ResolvedObserveConfig {
@@ -58,10 +74,14 @@ interface ResolvedObserveConfig {
58
74
  exportTimeoutMillis: number;
59
75
  metricExportIntervalMillis: number;
60
76
  resourceAttributes: Record<string, string | number | boolean>;
77
+ diagnosticLogging: boolean;
78
+ failFast: boolean;
61
79
  exporters?: ObserveExporters;
62
80
  }
63
81
  interface ObserveHandle {
64
82
  readonly started: boolean;
83
+ readonly status: ObserveStatus;
84
+ readonly lastError: ObserveErrorEvent | undefined;
65
85
  readonly config: Readonly<ResolvedObserveConfig>;
66
86
  forceFlush(): Promise<void>;
67
87
  shutdown(): Promise<void>;
@@ -132,8 +152,8 @@ declare class HttpRequestMetrics {
132
152
  private readonly errorCount;
133
153
  private readonly startedAt;
134
154
  constructor(meter: Meter, serviceName: string);
135
- start(request: IncomingMessage): void;
136
- record(request: IncomingMessage, response: ServerResponse): void;
155
+ start(request: IncomingMessage): boolean;
156
+ record(request: IncomingMessage, response: ServerResponse): boolean;
137
157
  }
138
158
 
139
159
  declare class RuntimeMetrics {
@@ -157,19 +177,34 @@ declare class NestMethodInstrumenter {
157
177
  private readonly errors;
158
178
  private readonly instrumented;
159
179
  constructor(tracer: Tracer, meter: Meter);
160
- instrumentInstance(instance: object, kind: NestComponentKind, componentName?: string): void;
161
- instrumentPrototype(type: Function, kind: NestComponentKind, componentName?: string): void;
180
+ instrumentInstance(instance: object, kind: NestComponentKind, componentName?: unknown): void;
181
+ instrumentPrototype(type: Function, kind: NestComponentKind, componentName?: unknown): void;
162
182
  private instrumentTarget;
163
183
  }
164
184
 
185
+ interface ObserveRuntime extends ObserveHandle {
186
+ readonly tracerProvider: NodeTracerProvider | undefined;
187
+ readonly meterProvider: MeterProvider | undefined;
188
+ readonly loggerProvider: LoggerProvider | undefined;
189
+ readonly httpRequestMetrics: HttpRequestMetrics | undefined;
190
+ }
191
+ declare function observe(options?: ObserveOptions): ObserveRuntime;
192
+ declare function getObserveRuntime(): ObserveRuntime | undefined;
193
+
165
194
  declare const OBSERVE_OPTIONS: unique symbol;
166
195
  declare const OBSERVE_HANDLE: unique symbol;
196
+ /** Provides inbound HTTP metrics when module-load instrumentation was registered too late. */
197
+ declare class NestHttpMetricsInterceptor implements NestInterceptor {
198
+ private readonly handle;
199
+ constructor(handle: ObserveRuntime);
200
+ intercept(context: ExecutionContext, next: CallHandler): Observable<unknown>;
201
+ }
167
202
  declare class ObserveModule {
168
203
  static forRoot(options?: ObserveOptions): DynamicModule;
169
204
  }
170
205
 
171
- declare const SDK_NAME = "@ryanzeng/nest-observe";
172
- declare const SDK_VERSION = "0.1.0";
206
+ declare const SDK_NAME: string;
207
+ declare const SDK_VERSION: string;
173
208
  declare function createObserveResource(config: ResolvedObserveConfig): Resource;
174
209
 
175
210
  declare const REDACTED = "[REDACTED]";
@@ -186,12 +221,4 @@ declare class SpanRedactionProcessor implements SpanProcessor {
186
221
  shutdown(): Promise<void>;
187
222
  }
188
223
 
189
- interface ObserveRuntime extends ObserveHandle {
190
- readonly tracerProvider: NodeTracerProvider | undefined;
191
- readonly meterProvider: MeterProvider | undefined;
192
- readonly loggerProvider: LoggerProvider | undefined;
193
- }
194
- declare function observe(options?: ObserveOptions): ObserveRuntime;
195
- declare function getObserveRuntime(): ObserveRuntime | undefined;
196
-
197
- export { CompatibleNestInstrumentation, type CompatibleNestInstrumentationConfig, HttpRequestMetrics, IgnoreTrace, type NestComponentKind, NestLoggerInstrumentation, NestMethodInstrumenter, OBSERVE_HANDLE, OBSERVE_OPTIONS, type ObserveExporters, type ObserveHandle, ObserveModule, type ObserveOptions, type ObserveRuntime, OpenTelemetryLogEmitter, REDACTED, type ResolvedObserveConfig, RuntimeMetrics, SDK_NAME, SDK_VERSION, SpanRedactionProcessor, type StructuredLogEmitter, type StructuredLogRecord, Trace, type TraceOptions, createObserveResource, getObserveRuntime, isSensitiveKey, isTraceDecorated, isTraceIgnored, markTraceDecorated, markTraceIgnored, observe, parseKeyValueList, redact, redactText, resolveObserveConfig, sanitizeHeaders };
224
+ export { CompatibleNestInstrumentation, type CompatibleNestInstrumentationConfig, HttpRequestMetrics, IgnoreTrace, type NestComponentKind, NestHttpMetricsInterceptor, NestLoggerInstrumentation, NestMethodInstrumenter, OBSERVE_HANDLE, OBSERVE_OPTIONS, type ObserveErrorEvent, type ObserveErrorStage, type ObserveExporters, type ObserveHandle, ObserveModule, type ObserveOptions, type ObserveRuntime, type ObserveSignal, type ObserveStatus, OpenTelemetryLogEmitter, REDACTED, type ResolvedObserveConfig, RuntimeMetrics, SDK_NAME, SDK_VERSION, SpanRedactionProcessor, type StructuredLogEmitter, type StructuredLogRecord, Trace, type TraceOptions, createObserveResource, getObserveRuntime, isSensitiveKey, isTraceDecorated, isTraceIgnored, markTraceDecorated, markTraceIgnored, observe, parseKeyValueList, redact, redactText, resolveObserveConfig, sanitizeHeaders };