@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.cjs CHANGED
@@ -14375,6 +14375,7 @@ __export(index_exports, {
14375
14375
  AppLogger: () => AppLogger,
14376
14376
  LoggerContextMiddleware: () => LoggerContextMiddleware,
14377
14377
  LoggerModule: () => LoggerModule,
14378
+ PinoLoggerService: () => PinoLoggerService,
14378
14379
  TRACE_LOGGER: () => TRACE_LOGGER
14379
14380
  });
14380
14381
  module.exports = __toCommonJS(index_exports);
@@ -14449,13 +14450,7 @@ var LogLevelState = class LogLevelState2 {
14449
14450
  static {
14450
14451
  __name(this, "LogLevelState");
14451
14452
  }
14452
- levels = /* @__PURE__ */ new Set([
14453
- "log",
14454
- "error",
14455
- "warn",
14456
- "debug",
14457
- "verbose"
14458
- ]);
14453
+ levels = /* @__PURE__ */ new Set();
14459
14454
  set(levels) {
14460
14455
  this.levels = new Set(levels);
14461
14456
  }
@@ -14486,6 +14481,61 @@ var LogLevelState = class LogLevelState2 {
14486
14481
  }
14487
14482
  return "fatal";
14488
14483
  }
14484
+ /**
14485
+ * 根据 Pino 级别转换为 NestJS LogLevel 数组
14486
+ */
14487
+ static fromPinoLevel(pinoLevel) {
14488
+ switch (pinoLevel) {
14489
+ case "trace":
14490
+ return [
14491
+ "verbose",
14492
+ "debug",
14493
+ "log",
14494
+ "warn",
14495
+ "error",
14496
+ "fatal"
14497
+ ];
14498
+ case "debug":
14499
+ return [
14500
+ "debug",
14501
+ "log",
14502
+ "warn",
14503
+ "error",
14504
+ "fatal"
14505
+ ];
14506
+ case "info":
14507
+ return [
14508
+ "log",
14509
+ "warn",
14510
+ "error",
14511
+ "fatal"
14512
+ ];
14513
+ case "warn":
14514
+ return [
14515
+ "warn",
14516
+ "error",
14517
+ "fatal"
14518
+ ];
14519
+ case "error":
14520
+ return [
14521
+ "error",
14522
+ "fatal"
14523
+ ];
14524
+ case "fatal":
14525
+ return [
14526
+ "fatal"
14527
+ ];
14528
+ case "silent":
14529
+ return [];
14530
+ default:
14531
+ return [
14532
+ "log",
14533
+ "warn",
14534
+ "error",
14535
+ "fatal"
14536
+ ];
14537
+ }
14538
+ }
14489
14539
  };
14490
14540
  var BasePinoLogger = class _BasePinoLogger {
14491
14541
  static {
@@ -14497,7 +14547,9 @@ var BasePinoLogger = class _BasePinoLogger {
14497
14547
  constructor(logger, contextStore) {
14498
14548
  this.logger = logger;
14499
14549
  this.contextStore = contextStore;
14500
- this.logger.level = this.levelState.getMinPinoLevel();
14550
+ const pinoLevel = this.logger.level;
14551
+ const nestLevels = LogLevelState.fromPinoLevel(pinoLevel);
14552
+ this.levelState.set(nestLevels);
14501
14553
  }
14502
14554
  setLogLevels(levels) {
14503
14555
  this.levelState.set(levels);
@@ -14521,6 +14573,36 @@ var BasePinoLogger = class _BasePinoLogger {
14521
14573
  fatal(message, ...optionalParams) {
14522
14574
  this.write("fatal", message, optionalParams, true);
14523
14575
  }
14576
+ /**
14577
+ * 记录结构化日志,将 meta 对象合并到日志字段中
14578
+ * @param level 日志级别
14579
+ * @param message 消息文本
14580
+ * @param meta 要合并的元数据对象
14581
+ * @param context 上下文标识
14582
+ */
14583
+ logStructured(level, message, meta, context) {
14584
+ if (!this.levelState.isEnabled(level)) {
14585
+ return;
14586
+ }
14587
+ const requestState = this.contextStore.getContext();
14588
+ const traceId = requestState?.requestId ?? null;
14589
+ const payload = {
14590
+ trace_id: traceId,
14591
+ path: requestState?.path,
14592
+ method: requestState?.method,
14593
+ user_id: requestState?.userId ?? null,
14594
+ app_id: requestState?.appId ?? null,
14595
+ tenant_id: requestState?.tenantId ?? null,
14596
+ pid: process.pid,
14597
+ ...sanitizeValue(meta)
14598
+ };
14599
+ if (context) {
14600
+ payload.context = context;
14601
+ }
14602
+ const pinoLevel = mapLogLevelToPino(level);
14603
+ const sanitizedPayload = sanitizeValue(payload);
14604
+ this.logger[pinoLevel](sanitizedPayload, message);
14605
+ }
14524
14606
  write(level, message, optionalParams, treatStack = false) {
14525
14607
  if (!this.levelState.isEnabled(level)) {
14526
14608
  return;
@@ -14530,13 +14612,11 @@ var BasePinoLogger = class _BasePinoLogger {
14530
14612
  const traceId = requestState?.requestId ?? null;
14531
14613
  const payload = {
14532
14614
  trace_id: traceId,
14533
- requestId: traceId,
14534
14615
  path: requestState?.path,
14535
14616
  method: requestState?.method,
14536
14617
  user_id: requestState?.userId ?? null,
14537
14618
  app_id: requestState?.appId ?? null,
14538
14619
  tenant_id: requestState?.tenantId ?? null,
14539
- ip: requestState?.ip ?? null,
14540
14620
  pid: process.pid
14541
14621
  };
14542
14622
  if (context) {
@@ -14683,7 +14763,7 @@ __name(sanitizeValue, "sanitizeValue");
14683
14763
  // src/module.ts
14684
14764
  var import_common5 = require("@nestjs/common");
14685
14765
  var import_core = require("@nestjs/core");
14686
- var import_config2 = __toESM(require_config2(), 1);
14766
+ var import_config3 = __toESM(require_config2(), 1);
14687
14767
 
14688
14768
  // src/config/logger.config.ts
14689
14769
  var import_config = __toESM(require_config2(), 1);
@@ -14703,49 +14783,22 @@ function normalizeLevel(level) {
14703
14783
  }
14704
14784
  }
14705
14785
  __name(normalizeLevel, "normalizeLevel");
14706
- function parseDestinations(raw) {
14707
- if (!raw) {
14708
- return [
14709
- {
14710
- type: "stdout"
14711
- }
14712
- ];
14713
- }
14714
- return raw.split(",").map((token) => token.trim()).filter(Boolean).map((token) => {
14715
- const [typeAndPath, level] = token.split("@");
14716
- const [type, path] = typeAndPath.split(":");
14717
- const normalizedType = (type ?? "stdout").toLowerCase();
14718
- if (normalizedType === "file") {
14719
- return {
14720
- type: "file",
14721
- path: path || "logs/app.log",
14722
- level
14723
- };
14724
- }
14725
- if (normalizedType === "stderr") {
14726
- return {
14727
- type: "stderr",
14728
- level
14729
- };
14730
- }
14731
- return {
14732
- type: "stdout",
14733
- level
14734
- };
14735
- });
14736
- }
14737
- __name(parseDestinations, "parseDestinations");
14738
14786
  var logger_config_default = (0, import_config.registerAs)("logger", () => {
14739
- const level = normalizeLevel(process.env.LOGGER_LEVEL || (process.env.NODE_ENV === "production" ? "info" : "debug"));
14740
- const destinations = parseDestinations(process.env.LOGGER_DESTINATIONS);
14787
+ const level = normalizeLevel(process.env.LOGGER_LEVEL || (process.env.NODE_ENV === "production" ? "info" : "trace"));
14788
+ const maxBodyLengthEnv = process.env.LOG_MAX_BODY_LENGTH;
14789
+ const maxBodyLength = maxBodyLengthEnv ? Number(maxBodyLengthEnv) : null;
14741
14790
  return {
14742
14791
  level,
14743
- destinations
14792
+ logDir: process.env.LOG_DIR || "logs",
14793
+ logRequestBody: process.env.LOG_REQUEST_BODY === "true",
14794
+ logResponseBody: process.env.LOG_RESPONSE_BODY === "true",
14795
+ maxBodyLength
14744
14796
  };
14745
14797
  });
14746
14798
 
14747
14799
  // src/interceptor/logging.interceptor.ts
14748
14800
  var import_common3 = require("@nestjs/common");
14801
+ var import_config2 = __toESM(require_config2(), 1);
14749
14802
  var import_operators = __toESM(require_operators(), 1);
14750
14803
  function _ts_decorate3(decorators, target, key, desc) {
14751
14804
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
@@ -14770,9 +14823,13 @@ var LoggingInterceptor = class {
14770
14823
  }
14771
14824
  traceLogger;
14772
14825
  requestContext;
14773
- constructor(traceLogger, requestContext) {
14826
+ appLogger;
14827
+ config;
14828
+ constructor(traceLogger, requestContext, appLogger, config) {
14774
14829
  this.traceLogger = traceLogger;
14775
14830
  this.requestContext = requestContext;
14831
+ this.appLogger = appLogger;
14832
+ this.config = config;
14776
14833
  }
14777
14834
  intercept(context, next) {
14778
14835
  if (context.getType() !== "http") {
@@ -14782,7 +14839,7 @@ var LoggingInterceptor = class {
14782
14839
  const req = httpContext.getRequest();
14783
14840
  const res = httpContext.getResponse();
14784
14841
  const method = req.method;
14785
- const url = req.originalUrl ?? req.url;
14842
+ const path = req.path;
14786
14843
  this.requestContext.setContext({
14787
14844
  method,
14788
14845
  path: url
@@ -14790,8 +14847,7 @@ var LoggingInterceptor = class {
14790
14847
  const startedAt = Date.now();
14791
14848
  const baseMeta = {
14792
14849
  method,
14793
- path: url,
14794
- requestId: req.requestId ?? req.id,
14850
+ path,
14795
14851
  trace_id: req.requestId ?? req.id,
14796
14852
  user_id: req.userContext?.userId ?? null,
14797
14853
  tenant_id: req.userContext?.tenantId ?? null,
@@ -14799,39 +14855,101 @@ var LoggingInterceptor = class {
14799
14855
  ip: req.ip ?? null,
14800
14856
  pid: process.pid
14801
14857
  };
14802
- this.traceLogger.log("HTTP request started", baseMeta, "HTTP");
14803
- return next.handle().pipe((0, import_operators.tap)(() => {
14858
+ this.appLogger.logStructured("log", "HTTP request started", baseMeta, "HTTPTraceInterceptor");
14859
+ const requestMeta = {
14860
+ ...baseMeta
14861
+ };
14862
+ if (this.config.logRequestBody && req.body) {
14863
+ requestMeta["request_body"] = this.sanitizeAndTruncate(req.body);
14864
+ }
14865
+ if (this.config.logRequestBody && Object.keys(req.query || {}).length > 0) {
14866
+ requestMeta["query_params"] = this.sanitizeAndTruncate(req.query);
14867
+ }
14868
+ this.traceLogger.logStructured("verbose", "HTTP request started", requestMeta, "HTTPTraceInterceptor");
14869
+ return next.handle().pipe((0, import_operators.tap)((responseData) => {
14804
14870
  const durationMs = Date.now() - startedAt;
14805
14871
  const statusCode = res.statusCode;
14806
- this.traceLogger.log("HTTP request completed", {
14872
+ this.appLogger.logStructured("log", "HTTP request completed", {
14873
+ ...baseMeta,
14874
+ status_code: statusCode,
14875
+ duration_ms: durationMs
14876
+ }, "HTTPTraceInterceptor");
14877
+ const responseMeta = {
14807
14878
  ...baseMeta,
14808
- statusCode,
14809
- durationMs
14810
- }, "HTTP");
14879
+ status_code: statusCode,
14880
+ duration_ms: durationMs
14881
+ };
14882
+ if (this.config.logResponseBody && responseData !== void 0) {
14883
+ responseMeta["response_body"] = this.sanitizeAndTruncate(responseData);
14884
+ }
14885
+ const traceLevel = res.statusCode >= 400 ? "error" : "verbose";
14886
+ this.traceLogger.logStructured(traceLevel, "HTTP request completed", responseMeta, "HTTPTraceInterceptor");
14811
14887
  }), (0, import_operators.catchError)((error) => {
14812
14888
  const durationMs = Date.now() - startedAt;
14813
14889
  const statusCode = res.statusCode >= 400 ? res.statusCode : 500;
14890
+ const linkMeta = {
14891
+ ...baseMeta,
14892
+ status_code: statusCode,
14893
+ duration_ms: durationMs
14894
+ };
14895
+ if (error instanceof Error) {
14896
+ linkMeta["error_message"] = error.message;
14897
+ }
14898
+ this.appLogger.logStructured("log", "HTTP request failed", linkMeta, "HTTPTraceInterceptor");
14814
14899
  const meta = {
14815
14900
  ...baseMeta,
14816
- statusCode,
14817
- durationMs
14901
+ status_code: statusCode,
14902
+ duration_ms: durationMs
14818
14903
  };
14819
14904
  if (error instanceof Error) {
14820
- this.traceLogger.error(error, meta, "HTTP");
14905
+ meta["error"] = {
14906
+ message: error.message,
14907
+ stack: error.stack
14908
+ };
14821
14909
  } else {
14822
- this.traceLogger.error("Unknown error thrown", meta, "HTTP");
14910
+ meta["error"] = String(error);
14823
14911
  }
14912
+ this.traceLogger.logStructured("verbose", "HTTP request failed", meta, "HTTPTraceInterceptor");
14824
14913
  throw error;
14825
14914
  }));
14826
14915
  }
14916
+ /**
14917
+ * 对数据进行截断处理
14918
+ */
14919
+ sanitizeAndTruncate(data) {
14920
+ try {
14921
+ if (this.config.maxBodyLength === null) {
14922
+ return data;
14923
+ }
14924
+ const jsonStr = JSON.stringify(data);
14925
+ if (jsonStr.length > this.config.maxBodyLength) {
14926
+ return {
14927
+ _truncated: true,
14928
+ _originalLength: jsonStr.length,
14929
+ _data: jsonStr.substring(0, this.config.maxBodyLength) + "..."
14930
+ };
14931
+ }
14932
+ return data;
14933
+ } catch (error) {
14934
+ return {
14935
+ _error: "Failed to serialize data",
14936
+ _type: typeof data,
14937
+ _constructor: data?.constructor?.name
14938
+ };
14939
+ }
14940
+ }
14827
14941
  };
14828
14942
  LoggingInterceptor = _ts_decorate3([
14829
14943
  (0, import_common3.Injectable)(),
14830
14944
  _ts_param2(0, (0, import_common3.Inject)(TRACE_LOGGER)),
14945
+ _ts_param2(2, (0, import_common3.Inject)(AppLogger)),
14946
+ _ts_param2(3, (0, import_common3.Inject)(logger_config_default.KEY)),
14831
14947
  _ts_metadata2("design:type", Function),
14832
14948
  _ts_metadata2("design:paramtypes", [
14833
- typeof import_common3.LoggerService === "undefined" ? Object : import_common3.LoggerService,
14834
- typeof RequestContextService === "undefined" ? Object : RequestContextService
14949
+ typeof PinoLoggerService === "undefined" ? Object : PinoLoggerService,
14950
+ typeof RequestContextService === "undefined" ? Object : RequestContextService,
14951
+ typeof AppLogger === "undefined" ? Object : AppLogger,
14952
+ typeof import_config2.ConfigType === "undefined" ? Object : import_config2.ConfigType
14835
14953
  ])
14836
14954
  ], LoggingInterceptor);
14837
14955
 
@@ -14854,12 +14972,12 @@ function createPinoLogger(config) {
14854
14972
  }
14855
14973
  }
14856
14974
  };
14857
- const destinations = config.destinations.length > 0 ? config.destinations : [
14975
+ const streams = [
14858
14976
  {
14859
- type: "stdout"
14977
+ level: config.level,
14978
+ stream: createFileDestination(config.filePath)
14860
14979
  }
14861
14980
  ];
14862
- const streams = buildStreams(destinations, baseLevel);
14863
14981
  if (streams.length === 0) {
14864
14982
  return (0, import_pino.default)(options);
14865
14983
  }
@@ -14869,30 +14987,6 @@ function createPinoLogger(config) {
14869
14987
  return (0, import_pino.default)(options, import_pino.default.multistream(streams));
14870
14988
  }
14871
14989
  __name(createPinoLogger, "createPinoLogger");
14872
- function buildStreams(destinations, fallbackLevel) {
14873
- return destinations.map((destination) => {
14874
- const level = normalizeLevel(destination.level ?? fallbackLevel);
14875
- switch (destination.type) {
14876
- case "stderr":
14877
- return {
14878
- level,
14879
- stream: process.stderr
14880
- };
14881
- case "file":
14882
- return {
14883
- level,
14884
- stream: createFileDestination(destination.path)
14885
- };
14886
- case "stdout":
14887
- default:
14888
- return {
14889
- level,
14890
- stream: process.stdout
14891
- };
14892
- }
14893
- });
14894
- }
14895
- __name(buildStreams, "buildStreams");
14896
14990
  function createFileDestination(pathname) {
14897
14991
  const target = pathname && pathname.length > 0 ? pathname : "logs/app.log";
14898
14992
  const resolved = (0, import_path.isAbsolute)(target) ? target : (0, import_path.join)(process.cwd(), target);
@@ -14973,32 +15067,32 @@ LoggerModule = _ts_decorate5([
14973
15067
  (0, import_common5.Global)(),
14974
15068
  (0, import_common5.Module)({
14975
15069
  imports: [
14976
- import_config2.ConfigModule.forFeature(logger_config_default)
15070
+ import_config3.ConfigModule.forFeature(logger_config_default)
14977
15071
  ],
14978
15072
  providers: [
14979
15073
  RequestContextService,
14980
15074
  LoggerContextMiddleware,
14981
15075
  {
14982
15076
  provide: PINO_ROOT_LOGGER,
14983
- useFactory: /* @__PURE__ */ __name((config) => createPinoLogger(config), "useFactory"),
15077
+ useFactory: /* @__PURE__ */ __name((config) => {
15078
+ return createPinoLogger({
15079
+ level: config.level,
15080
+ filePath: `${config.logDir}/app.log`
15081
+ });
15082
+ }, "useFactory"),
14984
15083
  inject: [
14985
15084
  logger_config_default.KEY
14986
15085
  ]
14987
15086
  },
14988
15087
  {
14989
15088
  provide: TRACE_LOGGER,
14990
- useFactory: /* @__PURE__ */ __name((requestContext) => new PinoLoggerService(createPinoLogger({
14991
- level: "trace",
14992
- destinations: [
14993
- {
14994
- type: "file",
14995
- path: "logs/trace.log",
14996
- level: "trace"
14997
- }
14998
- ]
15089
+ useFactory: /* @__PURE__ */ __name((requestContext, config) => new PinoLoggerService(createPinoLogger({
15090
+ level: config.level,
15091
+ filePath: `${config.logDir}/trace.log`
14999
15092
  }), requestContext), "useFactory"),
15000
15093
  inject: [
15001
- RequestContextService
15094
+ RequestContextService,
15095
+ logger_config_default.KEY
15002
15096
  ]
15003
15097
  },
15004
15098
  AppLogger,
@@ -15021,6 +15115,7 @@ LoggerModule = _ts_decorate5([
15021
15115
  AppLogger,
15022
15116
  LoggerContextMiddleware,
15023
15117
  LoggerModule,
15118
+ PinoLoggerService,
15024
15119
  TRACE_LOGGER
15025
15120
  });
15026
15121
  //# sourceMappingURL=index.cjs.map