@lark-apaas/nestjs-logger 0.1.0-alpha.1 → 0.1.0-alpha.10

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.d.cts CHANGED
@@ -32,6 +32,14 @@ declare abstract class BasePinoLogger implements LoggerService {
32
32
  debug?(message: unknown, ...optionalParams: unknown[]): void;
33
33
  verbose?(message: unknown, ...optionalParams: unknown[]): void;
34
34
  fatal?(message: unknown, ...optionalParams: unknown[]): void;
35
+ /**
36
+ * 记录结构化日志,将 meta 对象合并到日志字段中
37
+ * @param level 日志级别
38
+ * @param message 消息文本
39
+ * @param meta 要合并的元数据对象
40
+ * @param context 上下文标识
41
+ */
42
+ logStructured(level: LogLevel, message: string, meta: Record<string, unknown>, context?: string): void;
35
43
  protected write(level: LogLevel, message: unknown, optionalParams: unknown[], treatStack?: boolean): void;
36
44
  private extractOptionalParams;
37
45
  private buildMessagePayload;
@@ -40,6 +48,8 @@ declare abstract class BasePinoLogger implements LoggerService {
40
48
  declare class AppLogger extends BasePinoLogger {
41
49
  constructor(logger: Logger, requestContext: RequestContextService);
42
50
  }
51
+ declare class PinoLoggerService extends BasePinoLogger {
52
+ }
43
53
 
44
54
  declare class LoggerModule {
45
55
  }
@@ -59,4 +69,4 @@ declare class LoggerContextMiddleware implements NestMiddleware {
59
69
 
60
70
  declare const TRACE_LOGGER: unique symbol;
61
71
 
62
- export { AppLogger, LoggerContextMiddleware, LoggerModule, TRACE_LOGGER };
72
+ export { AppLogger, LoggerContextMiddleware, LoggerModule, PinoLoggerService, TRACE_LOGGER };
package/dist/index.d.ts CHANGED
@@ -32,6 +32,14 @@ declare abstract class BasePinoLogger implements LoggerService {
32
32
  debug?(message: unknown, ...optionalParams: unknown[]): void;
33
33
  verbose?(message: unknown, ...optionalParams: unknown[]): void;
34
34
  fatal?(message: unknown, ...optionalParams: unknown[]): void;
35
+ /**
36
+ * 记录结构化日志,将 meta 对象合并到日志字段中
37
+ * @param level 日志级别
38
+ * @param message 消息文本
39
+ * @param meta 要合并的元数据对象
40
+ * @param context 上下文标识
41
+ */
42
+ logStructured(level: LogLevel, message: string, meta: Record<string, unknown>, context?: string): void;
35
43
  protected write(level: LogLevel, message: unknown, optionalParams: unknown[], treatStack?: boolean): void;
36
44
  private extractOptionalParams;
37
45
  private buildMessagePayload;
@@ -40,6 +48,8 @@ declare abstract class BasePinoLogger implements LoggerService {
40
48
  declare class AppLogger extends BasePinoLogger {
41
49
  constructor(logger: Logger, requestContext: RequestContextService);
42
50
  }
51
+ declare class PinoLoggerService extends BasePinoLogger {
52
+ }
43
53
 
44
54
  declare class LoggerModule {
45
55
  }
@@ -59,4 +69,4 @@ declare class LoggerContextMiddleware implements NestMiddleware {
59
69
 
60
70
  declare const TRACE_LOGGER: unique symbol;
61
71
 
62
- export { AppLogger, LoggerContextMiddleware, LoggerModule, TRACE_LOGGER };
72
+ export { AppLogger, LoggerContextMiddleware, LoggerModule, PinoLoggerService, TRACE_LOGGER };
package/dist/index.js CHANGED
@@ -14439,13 +14439,7 @@ var LogLevelState = class LogLevelState2 {
14439
14439
  static {
14440
14440
  __name(this, "LogLevelState");
14441
14441
  }
14442
- levels = /* @__PURE__ */ new Set([
14443
- "log",
14444
- "error",
14445
- "warn",
14446
- "debug",
14447
- "verbose"
14448
- ]);
14442
+ levels = /* @__PURE__ */ new Set();
14449
14443
  set(levels) {
14450
14444
  this.levels = new Set(levels);
14451
14445
  }
@@ -14476,6 +14470,61 @@ var LogLevelState = class LogLevelState2 {
14476
14470
  }
14477
14471
  return "fatal";
14478
14472
  }
14473
+ /**
14474
+ * 根据 Pino 级别转换为 NestJS LogLevel 数组
14475
+ */
14476
+ static fromPinoLevel(pinoLevel) {
14477
+ switch (pinoLevel) {
14478
+ case "trace":
14479
+ return [
14480
+ "verbose",
14481
+ "debug",
14482
+ "log",
14483
+ "warn",
14484
+ "error",
14485
+ "fatal"
14486
+ ];
14487
+ case "debug":
14488
+ return [
14489
+ "debug",
14490
+ "log",
14491
+ "warn",
14492
+ "error",
14493
+ "fatal"
14494
+ ];
14495
+ case "info":
14496
+ return [
14497
+ "log",
14498
+ "warn",
14499
+ "error",
14500
+ "fatal"
14501
+ ];
14502
+ case "warn":
14503
+ return [
14504
+ "warn",
14505
+ "error",
14506
+ "fatal"
14507
+ ];
14508
+ case "error":
14509
+ return [
14510
+ "error",
14511
+ "fatal"
14512
+ ];
14513
+ case "fatal":
14514
+ return [
14515
+ "fatal"
14516
+ ];
14517
+ case "silent":
14518
+ return [];
14519
+ default:
14520
+ return [
14521
+ "log",
14522
+ "warn",
14523
+ "error",
14524
+ "fatal"
14525
+ ];
14526
+ }
14527
+ }
14479
14528
  };
14480
14529
  var BasePinoLogger = class _BasePinoLogger {
14481
14530
  static {
@@ -14487,7 +14536,9 @@ var BasePinoLogger = class _BasePinoLogger {
14487
14536
  constructor(logger, contextStore) {
14488
14537
  this.logger = logger;
14489
14538
  this.contextStore = contextStore;
14490
- this.logger.level = this.levelState.getMinPinoLevel();
14539
+ const pinoLevel = this.logger.level;
14540
+ const nestLevels = LogLevelState.fromPinoLevel(pinoLevel);
14541
+ this.levelState.set(nestLevels);
14491
14542
  }
14492
14543
  setLogLevels(levels) {
14493
14544
  this.levelState.set(levels);
@@ -14511,6 +14562,36 @@ var BasePinoLogger = class _BasePinoLogger {
14511
14562
  fatal(message, ...optionalParams) {
14512
14563
  this.write("fatal", message, optionalParams, true);
14513
14564
  }
14565
+ /**
14566
+ * 记录结构化日志,将 meta 对象合并到日志字段中
14567
+ * @param level 日志级别
14568
+ * @param message 消息文本
14569
+ * @param meta 要合并的元数据对象
14570
+ * @param context 上下文标识
14571
+ */
14572
+ logStructured(level, message, meta, context) {
14573
+ if (!this.levelState.isEnabled(level)) {
14574
+ return;
14575
+ }
14576
+ const requestState = this.contextStore.getContext();
14577
+ const traceId = requestState?.requestId ?? null;
14578
+ const payload = {
14579
+ trace_id: traceId,
14580
+ path: requestState?.path,
14581
+ method: requestState?.method,
14582
+ user_id: requestState?.userId ?? null,
14583
+ app_id: requestState?.appId ?? null,
14584
+ tenant_id: requestState?.tenantId ?? null,
14585
+ pid: process.pid,
14586
+ ...sanitizeValue(meta)
14587
+ };
14588
+ if (context) {
14589
+ payload.context = context;
14590
+ }
14591
+ const pinoLevel = mapLogLevelToPino(level);
14592
+ const sanitizedPayload = sanitizeValue(payload);
14593
+ this.logger[pinoLevel](sanitizedPayload, message);
14594
+ }
14514
14595
  write(level, message, optionalParams, treatStack = false) {
14515
14596
  if (!this.levelState.isEnabled(level)) {
14516
14597
  return;
@@ -14520,13 +14601,11 @@ var BasePinoLogger = class _BasePinoLogger {
14520
14601
  const traceId = requestState?.requestId ?? null;
14521
14602
  const payload = {
14522
14603
  trace_id: traceId,
14523
- requestId: traceId,
14524
14604
  path: requestState?.path,
14525
14605
  method: requestState?.method,
14526
14606
  user_id: requestState?.userId ?? null,
14527
14607
  app_id: requestState?.appId ?? null,
14528
14608
  tenant_id: requestState?.tenantId ?? null,
14529
- ip: requestState?.ip ?? null,
14530
14609
  pid: process.pid
14531
14610
  };
14532
14611
  if (context) {
@@ -14671,7 +14750,7 @@ function sanitizeValue(value) {
14671
14750
  __name(sanitizeValue, "sanitizeValue");
14672
14751
 
14673
14752
  // src/module.ts
14674
- var import_config2 = __toESM(require_config2(), 1);
14753
+ var import_config3 = __toESM(require_config2(), 1);
14675
14754
  import { Global, Module } from "@nestjs/common";
14676
14755
  import { APP_INTERCEPTOR } from "@nestjs/core";
14677
14756
 
@@ -14693,50 +14772,23 @@ function normalizeLevel(level) {
14693
14772
  }
14694
14773
  }
14695
14774
  __name(normalizeLevel, "normalizeLevel");
14696
- function parseDestinations(raw) {
14697
- if (!raw) {
14698
- return [
14699
- {
14700
- type: "stdout"
14701
- }
14702
- ];
14703
- }
14704
- return raw.split(",").map((token) => token.trim()).filter(Boolean).map((token) => {
14705
- const [typeAndPath, level] = token.split("@");
14706
- const [type, path] = typeAndPath.split(":");
14707
- const normalizedType = (type ?? "stdout").toLowerCase();
14708
- if (normalizedType === "file") {
14709
- return {
14710
- type: "file",
14711
- path: path || "logs/app.log",
14712
- level
14713
- };
14714
- }
14715
- if (normalizedType === "stderr") {
14716
- return {
14717
- type: "stderr",
14718
- level
14719
- };
14720
- }
14721
- return {
14722
- type: "stdout",
14723
- level
14724
- };
14725
- });
14726
- }
14727
- __name(parseDestinations, "parseDestinations");
14728
14775
  var logger_config_default = (0, import_config.registerAs)("logger", () => {
14729
- const level = normalizeLevel(process.env.LOGGER_LEVEL || (process.env.NODE_ENV === "production" ? "info" : "debug"));
14730
- const destinations = parseDestinations(process.env.LOGGER_DESTINATIONS);
14776
+ const level = normalizeLevel(process.env.LOGGER_LEVEL || (process.env.NODE_ENV === "production" ? "info" : "trace"));
14777
+ const maxBodyLengthEnv = process.env.LOG_MAX_BODY_LENGTH;
14778
+ const maxBodyLength = maxBodyLengthEnv ? Number(maxBodyLengthEnv) : null;
14731
14779
  return {
14732
14780
  level,
14733
- destinations
14781
+ logDir: process.env.LOG_DIR || "logs",
14782
+ logRequestBody: process.env.LOG_REQUEST_BODY === "true",
14783
+ logResponseBody: process.env.LOG_RESPONSE_BODY === "true",
14784
+ maxBodyLength
14734
14785
  };
14735
14786
  });
14736
14787
 
14737
14788
  // src/interceptor/logging.interceptor.ts
14789
+ var import_config2 = __toESM(require_config2(), 1);
14738
14790
  var import_operators = __toESM(require_operators(), 1);
14739
- import { Inject as Inject2, Injectable as Injectable3, LoggerService } from "@nestjs/common";
14791
+ import { Inject as Inject2, Injectable as Injectable3 } from "@nestjs/common";
14740
14792
  function _ts_decorate3(decorators, target, key, desc) {
14741
14793
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
14742
14794
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -14760,9 +14812,13 @@ var LoggingInterceptor = class {
14760
14812
  }
14761
14813
  traceLogger;
14762
14814
  requestContext;
14763
- constructor(traceLogger, requestContext) {
14815
+ appLogger;
14816
+ config;
14817
+ constructor(traceLogger, requestContext, appLogger, config) {
14764
14818
  this.traceLogger = traceLogger;
14765
14819
  this.requestContext = requestContext;
14820
+ this.appLogger = appLogger;
14821
+ this.config = config;
14766
14822
  }
14767
14823
  intercept(context, next) {
14768
14824
  if (context.getType() !== "http") {
@@ -14772,7 +14828,7 @@ var LoggingInterceptor = class {
14772
14828
  const req = httpContext.getRequest();
14773
14829
  const res = httpContext.getResponse();
14774
14830
  const method = req.method;
14775
- const url = req.originalUrl ?? req.url;
14831
+ const path = req.path;
14776
14832
  this.requestContext.setContext({
14777
14833
  method,
14778
14834
  path: url
@@ -14780,8 +14836,7 @@ var LoggingInterceptor = class {
14780
14836
  const startedAt = Date.now();
14781
14837
  const baseMeta = {
14782
14838
  method,
14783
- path: url,
14784
- requestId: req.requestId ?? req.id,
14839
+ path,
14785
14840
  trace_id: req.requestId ?? req.id,
14786
14841
  user_id: req.userContext?.userId ?? null,
14787
14842
  tenant_id: req.userContext?.tenantId ?? null,
@@ -14789,39 +14844,101 @@ var LoggingInterceptor = class {
14789
14844
  ip: req.ip ?? null,
14790
14845
  pid: process.pid
14791
14846
  };
14792
- this.traceLogger.log("HTTP request started", baseMeta, "HTTP");
14793
- return next.handle().pipe((0, import_operators.tap)(() => {
14847
+ this.appLogger.logStructured("log", "HTTP request started", baseMeta, "HTTPTraceInterceptor");
14848
+ const requestMeta = {
14849
+ ...baseMeta
14850
+ };
14851
+ if (this.config.logRequestBody && req.body) {
14852
+ requestMeta["request_body"] = this.sanitizeAndTruncate(req.body);
14853
+ }
14854
+ if (this.config.logRequestBody && Object.keys(req.query || {}).length > 0) {
14855
+ requestMeta["query_params"] = this.sanitizeAndTruncate(req.query);
14856
+ }
14857
+ this.traceLogger.logStructured("verbose", "HTTP request started", requestMeta, "HTTPTraceInterceptor");
14858
+ return next.handle().pipe((0, import_operators.tap)((responseData) => {
14794
14859
  const durationMs = Date.now() - startedAt;
14795
14860
  const statusCode = res.statusCode;
14796
- this.traceLogger.log("HTTP request completed", {
14861
+ this.appLogger.logStructured("log", "HTTP request completed", {
14862
+ ...baseMeta,
14863
+ status_code: statusCode,
14864
+ duration_ms: durationMs
14865
+ }, "HTTPTraceInterceptor");
14866
+ const responseMeta = {
14797
14867
  ...baseMeta,
14798
- statusCode,
14799
- durationMs
14800
- }, "HTTP");
14868
+ status_code: statusCode,
14869
+ duration_ms: durationMs
14870
+ };
14871
+ if (this.config.logResponseBody && responseData !== void 0) {
14872
+ responseMeta["response_body"] = this.sanitizeAndTruncate(responseData);
14873
+ }
14874
+ const traceLevel = res.statusCode >= 400 ? "error" : "verbose";
14875
+ this.traceLogger.logStructured(traceLevel, "HTTP request completed", responseMeta, "HTTPTraceInterceptor");
14801
14876
  }), (0, import_operators.catchError)((error) => {
14802
14877
  const durationMs = Date.now() - startedAt;
14803
14878
  const statusCode = res.statusCode >= 400 ? res.statusCode : 500;
14879
+ const linkMeta = {
14880
+ ...baseMeta,
14881
+ status_code: statusCode,
14882
+ duration_ms: durationMs
14883
+ };
14884
+ if (error instanceof Error) {
14885
+ linkMeta["error_message"] = error.message;
14886
+ }
14887
+ this.appLogger.logStructured("log", "HTTP request failed", linkMeta, "HTTPTraceInterceptor");
14804
14888
  const meta = {
14805
14889
  ...baseMeta,
14806
- statusCode,
14807
- durationMs
14890
+ status_code: statusCode,
14891
+ duration_ms: durationMs
14808
14892
  };
14809
14893
  if (error instanceof Error) {
14810
- this.traceLogger.error(error, meta, "HTTP");
14894
+ meta["error"] = {
14895
+ message: error.message,
14896
+ stack: error.stack
14897
+ };
14811
14898
  } else {
14812
- this.traceLogger.error("Unknown error thrown", meta, "HTTP");
14899
+ meta["error"] = String(error);
14813
14900
  }
14901
+ this.traceLogger.logStructured("verbose", "HTTP request failed", meta, "HTTPTraceInterceptor");
14814
14902
  throw error;
14815
14903
  }));
14816
14904
  }
14905
+ /**
14906
+ * 对数据进行截断处理
14907
+ */
14908
+ sanitizeAndTruncate(data) {
14909
+ try {
14910
+ if (this.config.maxBodyLength === null) {
14911
+ return data;
14912
+ }
14913
+ const jsonStr = JSON.stringify(data);
14914
+ if (jsonStr.length > this.config.maxBodyLength) {
14915
+ return {
14916
+ _truncated: true,
14917
+ _originalLength: jsonStr.length,
14918
+ _data: jsonStr.substring(0, this.config.maxBodyLength) + "..."
14919
+ };
14920
+ }
14921
+ return data;
14922
+ } catch (error) {
14923
+ return {
14924
+ _error: "Failed to serialize data",
14925
+ _type: typeof data,
14926
+ _constructor: data?.constructor?.name
14927
+ };
14928
+ }
14929
+ }
14817
14930
  };
14818
14931
  LoggingInterceptor = _ts_decorate3([
14819
14932
  Injectable3(),
14820
14933
  _ts_param2(0, Inject2(TRACE_LOGGER)),
14934
+ _ts_param2(2, Inject2(AppLogger)),
14935
+ _ts_param2(3, Inject2(logger_config_default.KEY)),
14821
14936
  _ts_metadata2("design:type", Function),
14822
14937
  _ts_metadata2("design:paramtypes", [
14823
- typeof LoggerService === "undefined" ? Object : LoggerService,
14824
- typeof RequestContextService === "undefined" ? Object : RequestContextService
14938
+ typeof PinoLoggerService === "undefined" ? Object : PinoLoggerService,
14939
+ typeof RequestContextService === "undefined" ? Object : RequestContextService,
14940
+ typeof AppLogger === "undefined" ? Object : AppLogger,
14941
+ typeof import_config2.ConfigType === "undefined" ? Object : import_config2.ConfigType
14825
14942
  ])
14826
14943
  ], LoggingInterceptor);
14827
14944
 
@@ -14844,12 +14961,12 @@ function createPinoLogger(config) {
14844
14961
  }
14845
14962
  }
14846
14963
  };
14847
- const destinations = config.destinations.length > 0 ? config.destinations : [
14964
+ const streams = [
14848
14965
  {
14849
- type: "stdout"
14966
+ level: config.level,
14967
+ stream: createFileDestination(config.filePath)
14850
14968
  }
14851
14969
  ];
14852
- const streams = buildStreams(destinations, baseLevel);
14853
14970
  if (streams.length === 0) {
14854
14971
  return pino(options);
14855
14972
  }
@@ -14859,30 +14976,6 @@ function createPinoLogger(config) {
14859
14976
  return pino(options, pino.multistream(streams));
14860
14977
  }
14861
14978
  __name(createPinoLogger, "createPinoLogger");
14862
- function buildStreams(destinations, fallbackLevel) {
14863
- return destinations.map((destination) => {
14864
- const level = normalizeLevel(destination.level ?? fallbackLevel);
14865
- switch (destination.type) {
14866
- case "stderr":
14867
- return {
14868
- level,
14869
- stream: process.stderr
14870
- };
14871
- case "file":
14872
- return {
14873
- level,
14874
- stream: createFileDestination(destination.path)
14875
- };
14876
- case "stdout":
14877
- default:
14878
- return {
14879
- level,
14880
- stream: process.stdout
14881
- };
14882
- }
14883
- });
14884
- }
14885
- __name(buildStreams, "buildStreams");
14886
14979
  function createFileDestination(pathname) {
14887
14980
  const target = pathname && pathname.length > 0 ? pathname : "logs/app.log";
14888
14981
  const resolved = isAbsolute(target) ? target : join(process.cwd(), target);
@@ -14963,32 +15056,32 @@ LoggerModule = _ts_decorate5([
14963
15056
  Global(),
14964
15057
  Module({
14965
15058
  imports: [
14966
- import_config2.ConfigModule.forFeature(logger_config_default)
15059
+ import_config3.ConfigModule.forFeature(logger_config_default)
14967
15060
  ],
14968
15061
  providers: [
14969
15062
  RequestContextService,
14970
15063
  LoggerContextMiddleware,
14971
15064
  {
14972
15065
  provide: PINO_ROOT_LOGGER,
14973
- useFactory: /* @__PURE__ */ __name((config) => createPinoLogger(config), "useFactory"),
15066
+ useFactory: /* @__PURE__ */ __name((config) => {
15067
+ return createPinoLogger({
15068
+ level: config.level,
15069
+ filePath: `${config.logDir}/app.log`
15070
+ });
15071
+ }, "useFactory"),
14974
15072
  inject: [
14975
15073
  logger_config_default.KEY
14976
15074
  ]
14977
15075
  },
14978
15076
  {
14979
15077
  provide: TRACE_LOGGER,
14980
- useFactory: /* @__PURE__ */ __name((requestContext) => new PinoLoggerService(createPinoLogger({
14981
- level: "trace",
14982
- destinations: [
14983
- {
14984
- type: "file",
14985
- path: "logs/trace.log",
14986
- level: "trace"
14987
- }
14988
- ]
15078
+ useFactory: /* @__PURE__ */ __name((requestContext, config) => new PinoLoggerService(createPinoLogger({
15079
+ level: config.level,
15080
+ filePath: `${config.logDir}/trace.log`
14989
15081
  }), requestContext), "useFactory"),
14990
15082
  inject: [
14991
- RequestContextService
15083
+ RequestContextService,
15084
+ logger_config_default.KEY
14992
15085
  ]
14993
15086
  },
14994
15087
  AppLogger,
@@ -15010,6 +15103,7 @@ export {
15010
15103
  AppLogger,
15011
15104
  LoggerContextMiddleware,
15012
15105
  LoggerModule,
15106
+ PinoLoggerService,
15013
15107
  TRACE_LOGGER
15014
15108
  };
15015
15109
  //# sourceMappingURL=index.js.map