@lark-apaas/nestjs-logger 0.1.0-alpha.1 → 0.1.0-alpha.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/dist/index.js CHANGED
@@ -14520,13 +14520,11 @@ var BasePinoLogger = class _BasePinoLogger {
14520
14520
  const traceId = requestState?.requestId ?? null;
14521
14521
  const payload = {
14522
14522
  trace_id: traceId,
14523
- requestId: traceId,
14524
14523
  path: requestState?.path,
14525
14524
  method: requestState?.method,
14526
14525
  user_id: requestState?.userId ?? null,
14527
14526
  app_id: requestState?.appId ?? null,
14528
14527
  tenant_id: requestState?.tenantId ?? null,
14529
- ip: requestState?.ip ?? null,
14530
14528
  pid: process.pid
14531
14529
  };
14532
14530
  if (context) {
@@ -14671,7 +14669,7 @@ function sanitizeValue(value) {
14671
14669
  __name(sanitizeValue, "sanitizeValue");
14672
14670
 
14673
14671
  // src/module.ts
14674
- var import_config2 = __toESM(require_config2(), 1);
14672
+ var import_config3 = __toESM(require_config2(), 1);
14675
14673
  import { Global, Module } from "@nestjs/common";
14676
14674
  import { APP_INTERCEPTOR } from "@nestjs/core";
14677
14675
 
@@ -14693,48 +14691,22 @@ function normalizeLevel(level) {
14693
14691
  }
14694
14692
  }
14695
14693
  __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
14694
  var logger_config_default = (0, import_config.registerAs)("logger", () => {
14729
14695
  const level = normalizeLevel(process.env.LOGGER_LEVEL || (process.env.NODE_ENV === "production" ? "info" : "debug"));
14730
- const destinations = parseDestinations(process.env.LOGGER_DESTINATIONS);
14696
+ const maxBodyLengthEnv = process.env.LOG_MAX_BODY_LENGTH;
14697
+ const maxBodyLength = maxBodyLengthEnv ? Number(maxBodyLengthEnv) : null;
14731
14698
  return {
14732
14699
  level,
14733
- destinations
14700
+ logDir: process.env.LOG_DIR || "logs",
14701
+ logRequestBody: process.env.LOG_REQUEST_BODY === "true",
14702
+ logResponseBody: process.env.LOG_RESPONSE_BODY === "true",
14703
+ maxBodyLength,
14704
+ sensitiveFields: (process.env.LOG_SENSITIVE_FIELDS || "password,token,secret,authorization,cookie,apiKey,accessToken,refreshToken").split(",").map((f) => f.trim())
14734
14705
  };
14735
14706
  });
14736
14707
 
14737
14708
  // src/interceptor/logging.interceptor.ts
14709
+ var import_config2 = __toESM(require_config2(), 1);
14738
14710
  var import_operators = __toESM(require_operators(), 1);
14739
14711
  import { Inject as Inject2, Injectable as Injectable3, LoggerService } from "@nestjs/common";
14740
14712
  function _ts_decorate3(decorators, target, key, desc) {
@@ -14760,9 +14732,11 @@ var LoggingInterceptor = class {
14760
14732
  }
14761
14733
  traceLogger;
14762
14734
  requestContext;
14763
- constructor(traceLogger, requestContext) {
14735
+ config;
14736
+ constructor(traceLogger, requestContext, config) {
14764
14737
  this.traceLogger = traceLogger;
14765
14738
  this.requestContext = requestContext;
14739
+ this.config = config;
14766
14740
  }
14767
14741
  intercept(context, next) {
14768
14742
  if (context.getType() !== "http") {
@@ -14781,7 +14755,6 @@ var LoggingInterceptor = class {
14781
14755
  const baseMeta = {
14782
14756
  method,
14783
14757
  path: url,
14784
- requestId: req.requestId ?? req.id,
14785
14758
  trace_id: req.requestId ?? req.id,
14786
14759
  user_id: req.userContext?.userId ?? null,
14787
14760
  tenant_id: req.userContext?.tenantId ?? null,
@@ -14789,15 +14762,32 @@ var LoggingInterceptor = class {
14789
14762
  ip: req.ip ?? null,
14790
14763
  pid: process.pid
14791
14764
  };
14792
- this.traceLogger.log("HTTP request started", baseMeta, "HTTP");
14793
- return next.handle().pipe((0, import_operators.tap)(() => {
14765
+ const requestMeta = {
14766
+ ...baseMeta
14767
+ };
14768
+ if (this.config.logRequestBody && req.body) {
14769
+ requestMeta["requestBody"] = this.sanitizeAndTruncate(req.body);
14770
+ }
14771
+ if (this.config.logRequestBody && Object.keys(req.query || {}).length > 0) {
14772
+ requestMeta["queryParams"] = this.sanitizeAndTruncate(req.query);
14773
+ }
14774
+ this.traceLogger.verbose?.("HTTP request started", requestMeta, "HTTP");
14775
+ return next.handle().pipe((0, import_operators.tap)((responseData) => {
14794
14776
  const durationMs = Date.now() - startedAt;
14795
14777
  const statusCode = res.statusCode;
14796
- this.traceLogger.log("HTTP request completed", {
14778
+ const responseMeta = {
14797
14779
  ...baseMeta,
14798
14780
  statusCode,
14799
14781
  durationMs
14800
- }, "HTTP");
14782
+ };
14783
+ if (this.config.logResponseBody && responseData !== void 0) {
14784
+ const contentType = res.getHeader("content-type");
14785
+ const isJsonResponse = this.isJsonContentType(contentType);
14786
+ if (isJsonResponse) {
14787
+ responseMeta["responseBody"] = this.sanitizeAndTruncate(responseData);
14788
+ }
14789
+ }
14790
+ this.traceLogger.verbose?.("HTTP request completed", responseMeta, "HTTP");
14801
14791
  }), (0, import_operators.catchError)((error) => {
14802
14792
  const durationMs = Date.now() - startedAt;
14803
14793
  const statusCode = res.statusCode >= 400 ? res.statusCode : 500;
@@ -14814,14 +14804,78 @@ var LoggingInterceptor = class {
14814
14804
  throw error;
14815
14805
  }));
14816
14806
  }
14807
+ /**
14808
+ * 对数据进行脱敏和截断处理
14809
+ */
14810
+ sanitizeAndTruncate(data) {
14811
+ try {
14812
+ const sanitized = this.maskSensitiveFields(data);
14813
+ if (this.config.maxBodyLength === null) {
14814
+ return sanitized;
14815
+ }
14816
+ const jsonStr = JSON.stringify(sanitized);
14817
+ if (jsonStr.length > this.config.maxBodyLength) {
14818
+ return {
14819
+ _truncated: true,
14820
+ _originalLength: jsonStr.length,
14821
+ _data: jsonStr.substring(0, this.config.maxBodyLength) + "..."
14822
+ };
14823
+ }
14824
+ return sanitized;
14825
+ } catch (error) {
14826
+ return {
14827
+ _error: "Failed to serialize data",
14828
+ _message: error instanceof Error ? error.message : String(error)
14829
+ };
14830
+ }
14831
+ }
14832
+ /**
14833
+ * 判断是否是 JSON 响应类型
14834
+ */
14835
+ isJsonContentType(contentType) {
14836
+ if (typeof contentType !== "string") {
14837
+ return false;
14838
+ }
14839
+ const contentTypeLower = contentType.toLowerCase();
14840
+ return contentTypeLower.includes("application/json") || contentTypeLower.includes("application/vnd.api+json") || contentTypeLower.includes("+json");
14841
+ }
14842
+ /**
14843
+ * 脱敏敏感字段
14844
+ */
14845
+ maskSensitiveFields(data) {
14846
+ if (data === null || data === void 0) {
14847
+ return data;
14848
+ }
14849
+ if (Array.isArray(data)) {
14850
+ return data.map((item) => this.maskSensitiveFields(item));
14851
+ }
14852
+ if (typeof data === "object") {
14853
+ const result = {};
14854
+ for (const [key, value] of Object.entries(data)) {
14855
+ const lowerKey = key.toLowerCase();
14856
+ const isSensitive = this.config.sensitiveFields.some((field) => lowerKey.includes(field.toLowerCase()));
14857
+ if (isSensitive) {
14858
+ result[key] = "***MASKED***";
14859
+ } else if (typeof value === "object" && value !== null) {
14860
+ result[key] = this.maskSensitiveFields(value);
14861
+ } else {
14862
+ result[key] = value;
14863
+ }
14864
+ }
14865
+ return result;
14866
+ }
14867
+ return data;
14868
+ }
14817
14869
  };
14818
14870
  LoggingInterceptor = _ts_decorate3([
14819
14871
  Injectable3(),
14820
14872
  _ts_param2(0, Inject2(TRACE_LOGGER)),
14873
+ _ts_param2(2, Inject2(logger_config_default.KEY)),
14821
14874
  _ts_metadata2("design:type", Function),
14822
14875
  _ts_metadata2("design:paramtypes", [
14823
14876
  typeof LoggerService === "undefined" ? Object : LoggerService,
14824
- typeof RequestContextService === "undefined" ? Object : RequestContextService
14877
+ typeof RequestContextService === "undefined" ? Object : RequestContextService,
14878
+ typeof import_config2.ConfigType === "undefined" ? Object : import_config2.ConfigType
14825
14879
  ])
14826
14880
  ], LoggingInterceptor);
14827
14881
 
@@ -14844,12 +14898,12 @@ function createPinoLogger(config) {
14844
14898
  }
14845
14899
  }
14846
14900
  };
14847
- const destinations = config.destinations.length > 0 ? config.destinations : [
14901
+ const streams = [
14848
14902
  {
14849
- type: "stdout"
14903
+ level: config.level,
14904
+ stream: createFileDestination(config.filePath)
14850
14905
  }
14851
14906
  ];
14852
- const streams = buildStreams(destinations, baseLevel);
14853
14907
  if (streams.length === 0) {
14854
14908
  return pino(options);
14855
14909
  }
@@ -14859,30 +14913,6 @@ function createPinoLogger(config) {
14859
14913
  return pino(options, pino.multistream(streams));
14860
14914
  }
14861
14915
  __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
14916
  function createFileDestination(pathname) {
14887
14917
  const target = pathname && pathname.length > 0 ? pathname : "logs/app.log";
14888
14918
  const resolved = isAbsolute(target) ? target : join(process.cwd(), target);
@@ -14963,32 +14993,32 @@ LoggerModule = _ts_decorate5([
14963
14993
  Global(),
14964
14994
  Module({
14965
14995
  imports: [
14966
- import_config2.ConfigModule.forFeature(logger_config_default)
14996
+ import_config3.ConfigModule.forFeature(logger_config_default)
14967
14997
  ],
14968
14998
  providers: [
14969
14999
  RequestContextService,
14970
15000
  LoggerContextMiddleware,
14971
15001
  {
14972
15002
  provide: PINO_ROOT_LOGGER,
14973
- useFactory: /* @__PURE__ */ __name((config) => createPinoLogger(config), "useFactory"),
15003
+ useFactory: /* @__PURE__ */ __name((config) => {
15004
+ return createPinoLogger({
15005
+ level: "trace",
15006
+ filePath: `${config.logDir}/app.log`
15007
+ });
15008
+ }, "useFactory"),
14974
15009
  inject: [
14975
15010
  logger_config_default.KEY
14976
15011
  ]
14977
15012
  },
14978
15013
  {
14979
15014
  provide: TRACE_LOGGER,
14980
- useFactory: /* @__PURE__ */ __name((requestContext) => new PinoLoggerService(createPinoLogger({
15015
+ useFactory: /* @__PURE__ */ __name((requestContext, config) => new PinoLoggerService(createPinoLogger({
14981
15016
  level: "trace",
14982
- destinations: [
14983
- {
14984
- type: "file",
14985
- path: "logs/trace.log",
14986
- level: "trace"
14987
- }
14988
- ]
15017
+ filePath: `${config.logDir}/trace.log`
14989
15018
  }), requestContext), "useFactory"),
14990
15019
  inject: [
14991
- RequestContextService
15020
+ RequestContextService,
15021
+ logger_config_default.KEY
14992
15022
  ]
14993
15023
  },
14994
15024
  AppLogger,