@lark-apaas/nestjs-observable 0.0.4-alpha.4 → 0.0.4-alpha.6

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.cjs CHANGED
@@ -66,7 +66,8 @@ var Observable = class _Observable {
66
66
  const contextAttributes = {
67
67
  "user_id": context.userId,
68
68
  "tenant_id": context.tenantId,
69
- "app_id": context.appId
69
+ "app_id": context.appId,
70
+ "__point_kill_info": context.pointKillConfig
70
71
  };
71
72
  return contextAttributes;
72
73
  }
@@ -196,9 +197,14 @@ var ObservableTraceMiddleware = class {
196
197
  }
197
198
  use(req, res, next) {
198
199
  const spanName = `${req.method} ${req.baseUrl}`;
200
+ const pointKillConfig = req.headers["x-force-observability-pointkill"];
199
201
  import_observable3.appSdk.startContext(req.headers, spanName, (span) => {
200
202
  this.requestContext.setContext({
201
- requestRootSpan: span
203
+ requestRootSpan: span,
204
+ pointKillConfig
205
+ });
206
+ span.setAttributes({
207
+ "__point_kill_info": pointKillConfig
202
208
  });
203
209
  const userContext = req.userContext ?? {};
204
210
  res.on("finish", () => {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/module.ts","../src/service.ts","../src/middleware/observable.middleware.ts","../../../../node_modules/@opentelemetry/api/src/trace/status.ts","../src/interceptor/auto-trace.interceptor.ts"],"sourcesContent":["export { NestjsObservableModule } from './module';\nexport { Observable } from './service';\nexport { ObservableTraceMiddleware } from './middleware/observable.middleware';\nexport { TraceInterceptor } from './interceptor/auto-trace.interceptor';\nexport { Span } from '@lark-apaas/observable';","import { Global, Module, OnModuleInit } from '@nestjs/common';\nimport { appSdk } from '@lark-apaas/observable';\nimport { CommonModule } from '@lark-apaas/nestjs-common';\nimport { Observable } from './service';\n\n@Global()\n@Module({\n imports: [CommonModule],\n providers: [Observable],\n exports: [Observable],\n})\nexport class NestjsObservableModule implements OnModuleInit {\n constructor() { }\n\n onModuleInit() {\n appSdk.start();\n }\n}\n","import { Injectable } from '@nestjs/common';\nimport { Observable as RxjsObservable } from 'rxjs';\n\nimport { AppOTelSDK, appSdk, Span } from '@lark-apaas/observable';\nimport { ObservableService, RequestContextService, RequestContextState } from '@lark-apaas/nestjs-common';\n\n/**\n * 可注入的可观测服务(单例模式)\n * - 保持 NestJS DI 能力\n */\n@Injectable()\nexport class Observable implements ObservableService {\n private static defaultPlatformAttrs = {\n source_type: \"user\",\n module: \"app_server\",\n }\n\n\n\n constructor(private readonly reqCtx: RequestContextService) { }\n private getContextAttributes() {\n const context = (this.reqCtx.getContext() || {}) as RequestContextState;\n const contextAttributes = {\n \"user_id\": context.userId,\n \"tenant_id\": context.tenantId,\n \"app_id\": context.appId,\n }\n return contextAttributes;\n }\n /** 记录日志(自动合并上下文) */\n log(level: string, message: string, attributes: Record<string, unknown> = {}, parentSpan?: {\n traceId: string;\n spanId: string;\n }) {\n if (level === \"debug\") {\n // debug 日志不记录\n return;\n }\n const contextAttributes = this.getContextAttributes();\n appSdk.log(level, message, { ...Observable.defaultPlatformAttrs, ...attributes, ...contextAttributes, }, parentSpan);\n }\n /** 记录链路 wrapper */\n trace<T>(name: string, fn: (span: Span) => T | Promise<T> | RxjsObservable<T>): T | Promise<T> | RxjsObservable<T> {\n if (process.env.NODE_ENV !== 'production') {\n // 非线上环境不上报日志链路\n return fn(AppOTelSDK.emptySpan);\n }\n const contextAttributes = this.getContextAttributes();\n return appSdk.trace(name, (span) => {\n span.setAttributes({ ...Observable.defaultPlatformAttrs, ...contextAttributes });\n return fn(span);\n });\n }\n\n /** 记录链路 bypass */\n startTrace(name: string, parent: Span): Span {\n const span = appSdk.startTrace(name, parent)\n const contextAttributes = this.getContextAttributes();\n span.setAttributes({ ...Observable.defaultPlatformAttrs, ...contextAttributes });\n return span\n }\n\n dropTrace(span: Span): void {\n AppOTelSDK.dropTraceSpan(span)\n }\n\n /** 手动刷新(FaaS/请求结束时可调用) */\n async flush() {\n await appSdk.flush();\n }\n}\n","import { Injectable, NestMiddleware } from '@nestjs/common';\nimport { Request, Response, NextFunction } from 'express';\nimport { SpanStatusCode } from '@opentelemetry/api';\nimport { appSdk } from '@lark-apaas/observable';\nimport { RequestContextService } from '@lark-apaas/nestjs-common';\n\ntype RequestWithUserContext = Request & {\n userContext?: {\n userId?: string;\n tenantId?: string;\n appId?: string;\n };\n};\n\n@Injectable()\nexport class ObservableTraceMiddleware implements NestMiddleware {\n constructor(\n private readonly requestContext: RequestContextService,\n ) { }\n use(req: RequestWithUserContext, res: Response, next: NextFunction) {\n const spanName = `${req.method} ${req.baseUrl}`;\n // 2. 开启 Root Span (Context Scope)\n // 将 next() 放入回调函数\n appSdk.startContext(req.headers as Record<string, string>, spanName, (span) => {\n // 缓存当前根节点 span\n this.requestContext.setContext({\n requestRootSpan: span\n })\n const userContext = req.userContext ?? {};\n // ---监听响应结束 (Hook Response Finish) ---\n // 无论是正常返回、异常过滤器处理、还是拦截器修改,\n // NestJS 最终都会调用底层 Express 的 res.end(),触发 'finish' 事件\n res.on('finish', () => {\n // 1. 记录 HTTP 状态码\n const status = res.statusCode;\n const attributes = {\n 'http_status_code': status,\n 'user_id': userContext.userId || '',\n 'tenant_id': userContext.tenantId || '',\n 'app_id': userContext.appId || '',\n \"source_type\": \"platform\",\n \"module\": \"app_server\",\n };\n span.setAttributes(attributes);\n // 2. 设置 Span 状态\n if (status >= 500) {\n span.setStatus({ code: SpanStatusCode.ERROR, message: `Server Error ${status}` });\n } else if (status >= 400) {\n // 4xx 通常不算后端 Trace 的 ERROR\n span.setStatus({ code: SpanStatusCode.UNSET });\n } else {\n span.setStatus({ code: SpanStatusCode.OK });\n }\n\n // 3. 结束 Span\n span.end();\n\n // 4. [FaaS 关键] 隐式 Flush\n // 使用 fire-and-forget 模式,但实际上在 FaaS 这种单次调用的场景下\n // 需要捕获异常,防止日志系统崩溃导致请求看起来像挂了\n appSdk.flush().catch((err) => {\n // 这里尽量不要用 appSdk.log,因为可能死循环,直接 console\n console.error('[OtelMiddleware] Flush failed:', err);\n });\n });\n\n // --- 进入 NestJS 业务逻辑 ---\n // 这一步执行时,AsyncLocalStorage 已经存储了当前的 Root Span\n next();\n });\n }\n}","/*\n * Copyright The OpenTelemetry Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nexport interface SpanStatus {\n /** The status code of this message. */\n code: SpanStatusCode;\n /** A developer-facing error message. */\n message?: string;\n}\n\n/**\n * An enumeration of status codes.\n */\nexport enum SpanStatusCode {\n /**\n * The default status.\n */\n UNSET = 0,\n /**\n * The operation has been validated by an Application developer or\n * Operator to have completed successfully.\n */\n OK = 1,\n /**\n * The operation contains an error.\n */\n ERROR = 2,\n}\n","import { Injectable, NestInterceptor, ExecutionContext, CallHandler, Inject } from '@nestjs/common';\nimport { Reflector } from '@nestjs/core';\nimport { Observable } from 'rxjs';\n\nimport { AUTO_TRACE, OBSERVABLE_SERVICE, ObservableService } from '@lark-apaas/nestjs-common';\n\n@Injectable()\nexport class TraceInterceptor implements NestInterceptor {\n constructor(\n private readonly reflector: Reflector,\n @Inject(OBSERVABLE_SERVICE) private readonly observableService: ObservableService,\n ) {}\n\n intercept(context: ExecutionContext, next: CallHandler): Observable<unknown> {\n const traceName = this.reflector.get<string>(AUTO_TRACE, context.getHandler());\n\n if (!traceName && traceName !== '') {\n return next.handle();\n }\n\n const finalName = traceName || context.getHandler().name;\n\n \n return this.observableService.trace(finalName, () => {\n return next.handle();\n });\n }\n}"],"mappings":"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","names":["import_common","import_observable","import_nestjs_common","Observable","defaultPlatformAttrs","source_type","module","reqCtx","getContextAttributes","context","getContext","contextAttributes","userId","tenantId","appId","log","level","message","attributes","parentSpan","appSdk","trace","name","fn","process","env","NODE_ENV","AppOTelSDK","emptySpan","span","setAttributes","startTrace","parent","dropTrace","dropTraceSpan","flush","NestjsObservableModule","onModuleInit","appSdk","start","imports","CommonModule","providers","Observable","exports","import_common","SpanStatusCode","import_observable","import_nestjs_common","ObservableTraceMiddleware","requestContext","use","req","res","next","spanName","method","baseUrl","appSdk","startContext","headers","span","setContext","requestRootSpan","userContext","on","status","statusCode","attributes","userId","tenantId","appId","setAttributes","setStatus","code","SpanStatusCode","ERROR","message","UNSET","OK","end","flush","catch","err","console","error","import_common","import_nestjs_common","TraceInterceptor","reflector","observableService","intercept","context","next","traceName","get","AUTO_TRACE","getHandler","handle","finalName","name","trace","import_observable"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/module.ts","../src/service.ts","../src/middleware/observable.middleware.ts","../../../../node_modules/@opentelemetry/api/src/trace/status.ts","../src/interceptor/auto-trace.interceptor.ts"],"sourcesContent":["export { NestjsObservableModule } from './module';\nexport { Observable } from './service';\nexport { ObservableTraceMiddleware } from './middleware/observable.middleware';\nexport { TraceInterceptor } from './interceptor/auto-trace.interceptor';\nexport { Span } from '@lark-apaas/observable';","import { Global, Module, OnModuleInit } from '@nestjs/common';\nimport { appSdk } from '@lark-apaas/observable';\nimport { CommonModule } from '@lark-apaas/nestjs-common';\nimport { Observable } from './service';\n\n@Global()\n@Module({\n imports: [CommonModule],\n providers: [Observable],\n exports: [Observable],\n})\nexport class NestjsObservableModule implements OnModuleInit {\n constructor() { }\n\n onModuleInit() {\n appSdk.start();\n }\n}\n","import { Injectable } from '@nestjs/common';\nimport { Observable as RxjsObservable } from 'rxjs';\n\nimport { AppOTelSDK, appSdk, Span } from '@lark-apaas/observable';\nimport { ObservableService, RequestContextService, RequestContextState } from '@lark-apaas/nestjs-common';\n\n/**\n * 可注入的可观测服务(单例模式)\n * - 保持 NestJS DI 能力\n */\n@Injectable()\nexport class Observable implements ObservableService {\n private static defaultPlatformAttrs = {\n source_type: \"user\",\n module: \"app_server\",\n }\n\n\n\n constructor(private readonly reqCtx: RequestContextService) { }\n private getContextAttributes() {\n const context = (this.reqCtx.getContext() || {}) as RequestContextState;\n const contextAttributes = {\n \"user_id\": context.userId,\n \"tenant_id\": context.tenantId,\n \"app_id\": context.appId,\n \"__point_kill_info\": context.pointKillConfig as string,\n }\n return contextAttributes;\n }\n /** 记录日志(自动合并上下文) */\n log(level: string, message: string, attributes: Record<string, unknown> = {}, parentSpan?: {\n traceId: string;\n spanId: string;\n }) {\n if (level === \"debug\") {\n // debug 日志不记录\n return;\n }\n const contextAttributes = this.getContextAttributes();\n appSdk.log(level, message, { ...Observable.defaultPlatformAttrs, ...attributes, ...contextAttributes, }, parentSpan);\n }\n /** 记录链路 wrapper */\n trace<T>(name: string, fn: (span: Span) => T | Promise<T> | RxjsObservable<T>): T | Promise<T> | RxjsObservable<T> {\n if (process.env.NODE_ENV !== 'production') {\n // 非线上环境不上报日志链路\n return fn(AppOTelSDK.emptySpan);\n }\n const contextAttributes = this.getContextAttributes();\n return appSdk.trace(name, (span) => {\n span.setAttributes({ ...Observable.defaultPlatformAttrs, ...contextAttributes });\n return fn(span);\n });\n }\n\n /** 记录链路 bypass */\n startTrace(name: string, parent: Span): Span {\n const span = appSdk.startTrace(name, parent)\n const contextAttributes = this.getContextAttributes();\n span.setAttributes({ ...Observable.defaultPlatformAttrs, ...contextAttributes });\n return span\n }\n\n dropTrace(span: Span): void {\n AppOTelSDK.dropTraceSpan(span)\n }\n\n /** 手动刷新(FaaS/请求结束时可调用) */\n async flush() {\n await appSdk.flush();\n }\n}\n","import { Injectable, NestMiddleware } from '@nestjs/common';\nimport { Request, Response, NextFunction } from 'express';\nimport { SpanStatusCode } from '@opentelemetry/api';\nimport { appSdk } from '@lark-apaas/observable';\nimport { RequestContextService } from '@lark-apaas/nestjs-common';\n\ntype RequestWithUserContext = Request & {\n userContext?: {\n userId?: string;\n tenantId?: string;\n appId?: string;\n };\n};\n\n@Injectable()\nexport class ObservableTraceMiddleware implements NestMiddleware {\n constructor(\n private readonly requestContext: RequestContextService,\n ) { }\n use(req: RequestWithUserContext, res: Response, next: NextFunction) {\n const spanName = `${req.method} ${req.baseUrl}`;\n const pointKillConfig = req.headers[\"x-force-observability-pointkill\"];\n // 2. 开启 Root Span (Context Scope)\n // 将 next() 放入回调函数\n appSdk.startContext(req.headers as Record<string, string>, spanName, (span) => {\n // 缓存当前根节点 span 到 RequestContextService\n this.requestContext.setContext({\n requestRootSpan: span,\n pointKillConfig\n })\n span.setAttributes({\n \"__point_kill_info\": pointKillConfig,\n })\n const userContext = req.userContext ?? {};\n // ---监听响应结束 (Hook Response Finish) ---\n // 无论是正常返回、异常过滤器处理、还是拦截器修改,\n // NestJS 最终都会调用底层 Express 的 res.end(),触发 'finish' 事件\n res.on('finish', () => {\n // 1. 记录 HTTP 状态码\n const status = res.statusCode;\n const attributes = {\n 'http_status_code': status,\n 'user_id': userContext.userId || '',\n 'tenant_id': userContext.tenantId || '',\n 'app_id': userContext.appId || '',\n \"source_type\": \"platform\",\n \"module\": \"app_server\",\n };\n span.setAttributes(attributes);\n // 2. 设置 Span 状态\n if (status >= 500) {\n span.setStatus({ code: SpanStatusCode.ERROR, message: `Server Error ${status}` });\n } else if (status >= 400) {\n // 4xx 通常不算后端 Trace 的 ERROR\n span.setStatus({ code: SpanStatusCode.UNSET });\n } else {\n span.setStatus({ code: SpanStatusCode.OK });\n }\n\n // 3. 结束 Span\n span.end();\n\n // 4. [FaaS 关键] 隐式 Flush\n // 使用 fire-and-forget 模式,但实际上在 FaaS 这种单次调用的场景下\n // 需要捕获异常,防止日志系统崩溃导致请求看起来像挂了\n appSdk.flush().catch((err) => {\n // 这里尽量不要用 appSdk.log,因为可能死循环,直接 console\n console.error('[OtelMiddleware] Flush failed:', err);\n });\n });\n\n // --- 进入 NestJS 业务逻辑 ---\n // 这一步执行时,AsyncLocalStorage 已经存储了当前的 Root Span\n next();\n });\n }\n}","/*\n * Copyright The OpenTelemetry Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nexport interface SpanStatus {\n /** The status code of this message. */\n code: SpanStatusCode;\n /** A developer-facing error message. */\n message?: string;\n}\n\n/**\n * An enumeration of status codes.\n */\nexport enum SpanStatusCode {\n /**\n * The default status.\n */\n UNSET = 0,\n /**\n * The operation has been validated by an Application developer or\n * Operator to have completed successfully.\n */\n OK = 1,\n /**\n * The operation contains an error.\n */\n ERROR = 2,\n}\n","import { Injectable, NestInterceptor, ExecutionContext, CallHandler, Inject } from '@nestjs/common';\nimport { Reflector } from '@nestjs/core';\nimport { Observable } from 'rxjs';\n\nimport { AUTO_TRACE, OBSERVABLE_SERVICE, ObservableService } from '@lark-apaas/nestjs-common';\n\n@Injectable()\nexport class TraceInterceptor implements NestInterceptor {\n constructor(\n private readonly reflector: Reflector,\n @Inject(OBSERVABLE_SERVICE) private readonly observableService: ObservableService,\n ) {}\n\n intercept(context: ExecutionContext, next: CallHandler): Observable<unknown> {\n const traceName = this.reflector.get<string>(AUTO_TRACE, context.getHandler());\n\n if (!traceName && traceName !== '') {\n return next.handle();\n }\n\n const finalName = traceName || context.getHandler().name;\n\n \n return this.observableService.trace(finalName, () => {\n return next.handle();\n });\n }\n}"],"mappings":"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","names":["import_common","import_observable","import_nestjs_common","Observable","defaultPlatformAttrs","source_type","module","reqCtx","getContextAttributes","context","getContext","contextAttributes","userId","tenantId","appId","pointKillConfig","log","level","message","attributes","parentSpan","appSdk","trace","name","fn","process","env","NODE_ENV","AppOTelSDK","emptySpan","span","setAttributes","startTrace","parent","dropTrace","dropTraceSpan","flush","NestjsObservableModule","onModuleInit","appSdk","start","imports","CommonModule","providers","Observable","exports","import_common","SpanStatusCode","import_observable","import_nestjs_common","ObservableTraceMiddleware","requestContext","use","req","res","next","spanName","method","baseUrl","pointKillConfig","headers","appSdk","startContext","span","setContext","requestRootSpan","setAttributes","userContext","on","status","statusCode","attributes","userId","tenantId","appId","setStatus","code","SpanStatusCode","ERROR","message","UNSET","OK","end","flush","catch","err","console","error","import_common","import_nestjs_common","TraceInterceptor","reflector","observableService","intercept","context","next","traceName","get","AUTO_TRACE","getHandler","handle","finalName","name","trace","import_observable"]}
package/dist/index.js CHANGED
@@ -38,7 +38,8 @@ var Observable = class _Observable {
38
38
  const contextAttributes = {
39
39
  "user_id": context.userId,
40
40
  "tenant_id": context.tenantId,
41
- "app_id": context.appId
41
+ "app_id": context.appId,
42
+ "__point_kill_info": context.pointKillConfig
42
43
  };
43
44
  return contextAttributes;
44
45
  }
@@ -168,9 +169,14 @@ var ObservableTraceMiddleware = class {
168
169
  }
169
170
  use(req, res, next) {
170
171
  const spanName = `${req.method} ${req.baseUrl}`;
172
+ const pointKillConfig = req.headers["x-force-observability-pointkill"];
171
173
  appSdk3.startContext(req.headers, spanName, (span) => {
172
174
  this.requestContext.setContext({
173
- requestRootSpan: span
175
+ requestRootSpan: span,
176
+ pointKillConfig
177
+ });
178
+ span.setAttributes({
179
+ "__point_kill_info": pointKillConfig
174
180
  });
175
181
  const userContext = req.userContext ?? {};
176
182
  res.on("finish", () => {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/module.ts","../src/service.ts","../src/middleware/observable.middleware.ts","../../../../node_modules/@opentelemetry/api/src/trace/status.ts","../src/interceptor/auto-trace.interceptor.ts","../src/index.ts"],"sourcesContent":["import { Global, Module, OnModuleInit } from '@nestjs/common';\nimport { appSdk } from '@lark-apaas/observable';\nimport { CommonModule } from '@lark-apaas/nestjs-common';\nimport { Observable } from './service';\n\n@Global()\n@Module({\n imports: [CommonModule],\n providers: [Observable],\n exports: [Observable],\n})\nexport class NestjsObservableModule implements OnModuleInit {\n constructor() { }\n\n onModuleInit() {\n appSdk.start();\n }\n}\n","import { Injectable } from '@nestjs/common';\nimport { Observable as RxjsObservable } from 'rxjs';\n\nimport { AppOTelSDK, appSdk, Span } from '@lark-apaas/observable';\nimport { ObservableService, RequestContextService, RequestContextState } from '@lark-apaas/nestjs-common';\n\n/**\n * 可注入的可观测服务(单例模式)\n * - 保持 NestJS DI 能力\n */\n@Injectable()\nexport class Observable implements ObservableService {\n private static defaultPlatformAttrs = {\n source_type: \"user\",\n module: \"app_server\",\n }\n\n\n\n constructor(private readonly reqCtx: RequestContextService) { }\n private getContextAttributes() {\n const context = (this.reqCtx.getContext() || {}) as RequestContextState;\n const contextAttributes = {\n \"user_id\": context.userId,\n \"tenant_id\": context.tenantId,\n \"app_id\": context.appId,\n }\n return contextAttributes;\n }\n /** 记录日志(自动合并上下文) */\n log(level: string, message: string, attributes: Record<string, unknown> = {}, parentSpan?: {\n traceId: string;\n spanId: string;\n }) {\n if (level === \"debug\") {\n // debug 日志不记录\n return;\n }\n const contextAttributes = this.getContextAttributes();\n appSdk.log(level, message, { ...Observable.defaultPlatformAttrs, ...attributes, ...contextAttributes, }, parentSpan);\n }\n /** 记录链路 wrapper */\n trace<T>(name: string, fn: (span: Span) => T | Promise<T> | RxjsObservable<T>): T | Promise<T> | RxjsObservable<T> {\n if (process.env.NODE_ENV !== 'production') {\n // 非线上环境不上报日志链路\n return fn(AppOTelSDK.emptySpan);\n }\n const contextAttributes = this.getContextAttributes();\n return appSdk.trace(name, (span) => {\n span.setAttributes({ ...Observable.defaultPlatformAttrs, ...contextAttributes });\n return fn(span);\n });\n }\n\n /** 记录链路 bypass */\n startTrace(name: string, parent: Span): Span {\n const span = appSdk.startTrace(name, parent)\n const contextAttributes = this.getContextAttributes();\n span.setAttributes({ ...Observable.defaultPlatformAttrs, ...contextAttributes });\n return span\n }\n\n dropTrace(span: Span): void {\n AppOTelSDK.dropTraceSpan(span)\n }\n\n /** 手动刷新(FaaS/请求结束时可调用) */\n async flush() {\n await appSdk.flush();\n }\n}\n","import { Injectable, NestMiddleware } from '@nestjs/common';\nimport { Request, Response, NextFunction } from 'express';\nimport { SpanStatusCode } from '@opentelemetry/api';\nimport { appSdk } from '@lark-apaas/observable';\nimport { RequestContextService } from '@lark-apaas/nestjs-common';\n\ntype RequestWithUserContext = Request & {\n userContext?: {\n userId?: string;\n tenantId?: string;\n appId?: string;\n };\n};\n\n@Injectable()\nexport class ObservableTraceMiddleware implements NestMiddleware {\n constructor(\n private readonly requestContext: RequestContextService,\n ) { }\n use(req: RequestWithUserContext, res: Response, next: NextFunction) {\n const spanName = `${req.method} ${req.baseUrl}`;\n // 2. 开启 Root Span (Context Scope)\n // 将 next() 放入回调函数\n appSdk.startContext(req.headers as Record<string, string>, spanName, (span) => {\n // 缓存当前根节点 span\n this.requestContext.setContext({\n requestRootSpan: span\n })\n const userContext = req.userContext ?? {};\n // ---监听响应结束 (Hook Response Finish) ---\n // 无论是正常返回、异常过滤器处理、还是拦截器修改,\n // NestJS 最终都会调用底层 Express 的 res.end(),触发 'finish' 事件\n res.on('finish', () => {\n // 1. 记录 HTTP 状态码\n const status = res.statusCode;\n const attributes = {\n 'http_status_code': status,\n 'user_id': userContext.userId || '',\n 'tenant_id': userContext.tenantId || '',\n 'app_id': userContext.appId || '',\n \"source_type\": \"platform\",\n \"module\": \"app_server\",\n };\n span.setAttributes(attributes);\n // 2. 设置 Span 状态\n if (status >= 500) {\n span.setStatus({ code: SpanStatusCode.ERROR, message: `Server Error ${status}` });\n } else if (status >= 400) {\n // 4xx 通常不算后端 Trace 的 ERROR\n span.setStatus({ code: SpanStatusCode.UNSET });\n } else {\n span.setStatus({ code: SpanStatusCode.OK });\n }\n\n // 3. 结束 Span\n span.end();\n\n // 4. [FaaS 关键] 隐式 Flush\n // 使用 fire-and-forget 模式,但实际上在 FaaS 这种单次调用的场景下\n // 需要捕获异常,防止日志系统崩溃导致请求看起来像挂了\n appSdk.flush().catch((err) => {\n // 这里尽量不要用 appSdk.log,因为可能死循环,直接 console\n console.error('[OtelMiddleware] Flush failed:', err);\n });\n });\n\n // --- 进入 NestJS 业务逻辑 ---\n // 这一步执行时,AsyncLocalStorage 已经存储了当前的 Root Span\n next();\n });\n }\n}","/*\n * Copyright The OpenTelemetry Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nexport interface SpanStatus {\n /** The status code of this message. */\n code: SpanStatusCode;\n /** A developer-facing error message. */\n message?: string;\n}\n\n/**\n * An enumeration of status codes.\n */\nexport enum SpanStatusCode {\n /**\n * The default status.\n */\n UNSET = 0,\n /**\n * The operation has been validated by an Application developer or\n * Operator to have completed successfully.\n */\n OK = 1,\n /**\n * The operation contains an error.\n */\n ERROR = 2,\n}\n","import { Injectable, NestInterceptor, ExecutionContext, CallHandler, Inject } from '@nestjs/common';\nimport { Reflector } from '@nestjs/core';\nimport { Observable } from 'rxjs';\n\nimport { AUTO_TRACE, OBSERVABLE_SERVICE, ObservableService } from '@lark-apaas/nestjs-common';\n\n@Injectable()\nexport class TraceInterceptor implements NestInterceptor {\n constructor(\n private readonly reflector: Reflector,\n @Inject(OBSERVABLE_SERVICE) private readonly observableService: ObservableService,\n ) {}\n\n intercept(context: ExecutionContext, next: CallHandler): Observable<unknown> {\n const traceName = this.reflector.get<string>(AUTO_TRACE, context.getHandler());\n\n if (!traceName && traceName !== '') {\n return next.handle();\n }\n\n const finalName = traceName || context.getHandler().name;\n\n \n return this.observableService.trace(finalName, () => {\n return next.handle();\n });\n }\n}","export { NestjsObservableModule } from './module';\nexport { Observable } from './service';\nexport { ObservableTraceMiddleware } from './middleware/observable.middleware';\nexport { TraceInterceptor } from './interceptor/auto-trace.interceptor';\nexport { Span } from '@lark-apaas/observable';"],"mappings":"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","names":["Global","Module","appSdk","CommonModule","Injectable","AppOTelSDK","appSdk","RequestContextService","Observable","defaultPlatformAttrs","source_type","module","reqCtx","getContextAttributes","context","getContext","contextAttributes","userId","tenantId","appId","log","level","message","attributes","parentSpan","appSdk","trace","name","fn","process","env","NODE_ENV","AppOTelSDK","emptySpan","span","setAttributes","startTrace","parent","dropTrace","dropTraceSpan","flush","NestjsObservableModule","onModuleInit","appSdk","start","imports","CommonModule","providers","Observable","exports","Injectable","SpanStatusCode","appSdk","RequestContextService","ObservableTraceMiddleware","requestContext","use","req","res","next","spanName","method","baseUrl","appSdk","startContext","headers","span","setContext","requestRootSpan","userContext","on","status","statusCode","attributes","userId","tenantId","appId","setAttributes","setStatus","code","SpanStatusCode","ERROR","message","UNSET","OK","end","flush","catch","err","console","error","Injectable","Inject","Reflector","AUTO_TRACE","OBSERVABLE_SERVICE","ObservableService","TraceInterceptor","reflector","observableService","intercept","context","next","traceName","get","AUTO_TRACE","getHandler","handle","finalName","name","trace","Span"]}
1
+ {"version":3,"sources":["../src/module.ts","../src/service.ts","../src/middleware/observable.middleware.ts","../../../../node_modules/@opentelemetry/api/src/trace/status.ts","../src/interceptor/auto-trace.interceptor.ts","../src/index.ts"],"sourcesContent":["import { Global, Module, OnModuleInit } from '@nestjs/common';\nimport { appSdk } from '@lark-apaas/observable';\nimport { CommonModule } from '@lark-apaas/nestjs-common';\nimport { Observable } from './service';\n\n@Global()\n@Module({\n imports: [CommonModule],\n providers: [Observable],\n exports: [Observable],\n})\nexport class NestjsObservableModule implements OnModuleInit {\n constructor() { }\n\n onModuleInit() {\n appSdk.start();\n }\n}\n","import { Injectable } from '@nestjs/common';\nimport { Observable as RxjsObservable } from 'rxjs';\n\nimport { AppOTelSDK, appSdk, Span } from '@lark-apaas/observable';\nimport { ObservableService, RequestContextService, RequestContextState } from '@lark-apaas/nestjs-common';\n\n/**\n * 可注入的可观测服务(单例模式)\n * - 保持 NestJS DI 能力\n */\n@Injectable()\nexport class Observable implements ObservableService {\n private static defaultPlatformAttrs = {\n source_type: \"user\",\n module: \"app_server\",\n }\n\n\n\n constructor(private readonly reqCtx: RequestContextService) { }\n private getContextAttributes() {\n const context = (this.reqCtx.getContext() || {}) as RequestContextState;\n const contextAttributes = {\n \"user_id\": context.userId,\n \"tenant_id\": context.tenantId,\n \"app_id\": context.appId,\n \"__point_kill_info\": context.pointKillConfig as string,\n }\n return contextAttributes;\n }\n /** 记录日志(自动合并上下文) */\n log(level: string, message: string, attributes: Record<string, unknown> = {}, parentSpan?: {\n traceId: string;\n spanId: string;\n }) {\n if (level === \"debug\") {\n // debug 日志不记录\n return;\n }\n const contextAttributes = this.getContextAttributes();\n appSdk.log(level, message, { ...Observable.defaultPlatformAttrs, ...attributes, ...contextAttributes, }, parentSpan);\n }\n /** 记录链路 wrapper */\n trace<T>(name: string, fn: (span: Span) => T | Promise<T> | RxjsObservable<T>): T | Promise<T> | RxjsObservable<T> {\n if (process.env.NODE_ENV !== 'production') {\n // 非线上环境不上报日志链路\n return fn(AppOTelSDK.emptySpan);\n }\n const contextAttributes = this.getContextAttributes();\n return appSdk.trace(name, (span) => {\n span.setAttributes({ ...Observable.defaultPlatformAttrs, ...contextAttributes });\n return fn(span);\n });\n }\n\n /** 记录链路 bypass */\n startTrace(name: string, parent: Span): Span {\n const span = appSdk.startTrace(name, parent)\n const contextAttributes = this.getContextAttributes();\n span.setAttributes({ ...Observable.defaultPlatformAttrs, ...contextAttributes });\n return span\n }\n\n dropTrace(span: Span): void {\n AppOTelSDK.dropTraceSpan(span)\n }\n\n /** 手动刷新(FaaS/请求结束时可调用) */\n async flush() {\n await appSdk.flush();\n }\n}\n","import { Injectable, NestMiddleware } from '@nestjs/common';\nimport { Request, Response, NextFunction } from 'express';\nimport { SpanStatusCode } from '@opentelemetry/api';\nimport { appSdk } from '@lark-apaas/observable';\nimport { RequestContextService } from '@lark-apaas/nestjs-common';\n\ntype RequestWithUserContext = Request & {\n userContext?: {\n userId?: string;\n tenantId?: string;\n appId?: string;\n };\n};\n\n@Injectable()\nexport class ObservableTraceMiddleware implements NestMiddleware {\n constructor(\n private readonly requestContext: RequestContextService,\n ) { }\n use(req: RequestWithUserContext, res: Response, next: NextFunction) {\n const spanName = `${req.method} ${req.baseUrl}`;\n const pointKillConfig = req.headers[\"x-force-observability-pointkill\"];\n // 2. 开启 Root Span (Context Scope)\n // 将 next() 放入回调函数\n appSdk.startContext(req.headers as Record<string, string>, spanName, (span) => {\n // 缓存当前根节点 span 到 RequestContextService\n this.requestContext.setContext({\n requestRootSpan: span,\n pointKillConfig\n })\n span.setAttributes({\n \"__point_kill_info\": pointKillConfig,\n })\n const userContext = req.userContext ?? {};\n // ---监听响应结束 (Hook Response Finish) ---\n // 无论是正常返回、异常过滤器处理、还是拦截器修改,\n // NestJS 最终都会调用底层 Express 的 res.end(),触发 'finish' 事件\n res.on('finish', () => {\n // 1. 记录 HTTP 状态码\n const status = res.statusCode;\n const attributes = {\n 'http_status_code': status,\n 'user_id': userContext.userId || '',\n 'tenant_id': userContext.tenantId || '',\n 'app_id': userContext.appId || '',\n \"source_type\": \"platform\",\n \"module\": \"app_server\",\n };\n span.setAttributes(attributes);\n // 2. 设置 Span 状态\n if (status >= 500) {\n span.setStatus({ code: SpanStatusCode.ERROR, message: `Server Error ${status}` });\n } else if (status >= 400) {\n // 4xx 通常不算后端 Trace 的 ERROR\n span.setStatus({ code: SpanStatusCode.UNSET });\n } else {\n span.setStatus({ code: SpanStatusCode.OK });\n }\n\n // 3. 结束 Span\n span.end();\n\n // 4. [FaaS 关键] 隐式 Flush\n // 使用 fire-and-forget 模式,但实际上在 FaaS 这种单次调用的场景下\n // 需要捕获异常,防止日志系统崩溃导致请求看起来像挂了\n appSdk.flush().catch((err) => {\n // 这里尽量不要用 appSdk.log,因为可能死循环,直接 console\n console.error('[OtelMiddleware] Flush failed:', err);\n });\n });\n\n // --- 进入 NestJS 业务逻辑 ---\n // 这一步执行时,AsyncLocalStorage 已经存储了当前的 Root Span\n next();\n });\n }\n}","/*\n * Copyright The OpenTelemetry Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nexport interface SpanStatus {\n /** The status code of this message. */\n code: SpanStatusCode;\n /** A developer-facing error message. */\n message?: string;\n}\n\n/**\n * An enumeration of status codes.\n */\nexport enum SpanStatusCode {\n /**\n * The default status.\n */\n UNSET = 0,\n /**\n * The operation has been validated by an Application developer or\n * Operator to have completed successfully.\n */\n OK = 1,\n /**\n * The operation contains an error.\n */\n ERROR = 2,\n}\n","import { Injectable, NestInterceptor, ExecutionContext, CallHandler, Inject } from '@nestjs/common';\nimport { Reflector } from '@nestjs/core';\nimport { Observable } from 'rxjs';\n\nimport { AUTO_TRACE, OBSERVABLE_SERVICE, ObservableService } from '@lark-apaas/nestjs-common';\n\n@Injectable()\nexport class TraceInterceptor implements NestInterceptor {\n constructor(\n private readonly reflector: Reflector,\n @Inject(OBSERVABLE_SERVICE) private readonly observableService: ObservableService,\n ) {}\n\n intercept(context: ExecutionContext, next: CallHandler): Observable<unknown> {\n const traceName = this.reflector.get<string>(AUTO_TRACE, context.getHandler());\n\n if (!traceName && traceName !== '') {\n return next.handle();\n }\n\n const finalName = traceName || context.getHandler().name;\n\n \n return this.observableService.trace(finalName, () => {\n return next.handle();\n });\n }\n}","export { NestjsObservableModule } from './module';\nexport { Observable } from './service';\nexport { ObservableTraceMiddleware } from './middleware/observable.middleware';\nexport { TraceInterceptor } from './interceptor/auto-trace.interceptor';\nexport { Span } from '@lark-apaas/observable';"],"mappings":"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","names":["Global","Module","appSdk","CommonModule","Injectable","AppOTelSDK","appSdk","RequestContextService","Observable","defaultPlatformAttrs","source_type","module","reqCtx","getContextAttributes","context","getContext","contextAttributes","userId","tenantId","appId","pointKillConfig","log","level","message","attributes","parentSpan","appSdk","trace","name","fn","process","env","NODE_ENV","AppOTelSDK","emptySpan","span","setAttributes","startTrace","parent","dropTrace","dropTraceSpan","flush","NestjsObservableModule","onModuleInit","appSdk","start","imports","CommonModule","providers","Observable","exports","Injectable","SpanStatusCode","appSdk","RequestContextService","ObservableTraceMiddleware","requestContext","use","req","res","next","spanName","method","baseUrl","pointKillConfig","headers","appSdk","startContext","span","setContext","requestRootSpan","setAttributes","userContext","on","status","statusCode","attributes","userId","tenantId","appId","setStatus","code","SpanStatusCode","ERROR","message","UNSET","OK","end","flush","catch","err","console","error","Injectable","Inject","Reflector","AUTO_TRACE","OBSERVABLE_SERVICE","ObservableService","TraceInterceptor","reflector","observableService","intercept","context","next","traceName","get","AUTO_TRACE","getHandler","handle","finalName","name","trace","Span"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lark-apaas/nestjs-observable",
3
- "version": "0.0.4-alpha.4",
3
+ "version": "0.0.4-alpha.6",
4
4
  "description": "NestJS integration for Observable SDK",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -35,7 +35,7 @@
35
35
  },
36
36
  "dependencies": {
37
37
  "@lark-apaas/nestjs-common": "^0.1.2",
38
- "@lark-apaas/observable": "^1.0.4-alpha.4"
38
+ "@lark-apaas/observable": "^1.0.4-alpha.6"
39
39
  },
40
40
  "devDependencies": {
41
41
  "tsup": "^8.0.0",