@hg-ts/logger 0.7.27 → 0.8.1

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.
Files changed (42) hide show
  1. package/dist/index.d.ts +1 -1
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js.map +1 -1
  4. package/dist/logger/base.formatter.d.ts +4 -1
  5. package/dist/logger/base.formatter.d.ts.map +1 -1
  6. package/dist/logger/base.formatter.js +29 -20
  7. package/dist/logger/base.formatter.js.map +1 -1
  8. package/dist/logger/base.logger.d.ts +20 -9
  9. package/dist/logger/base.logger.d.ts.map +1 -1
  10. package/dist/logger/base.logger.js +114 -22
  11. package/dist/logger/base.logger.js.map +1 -1
  12. package/dist/logger/console.logger.d.ts.map +1 -1
  13. package/dist/logger/console.logger.js +8 -1
  14. package/dist/logger/console.logger.js.map +1 -1
  15. package/dist/logger/console.logger.test.d.ts +9 -0
  16. package/dist/logger/console.logger.test.d.ts.map +1 -0
  17. package/dist/logger/console.logger.test.js +42 -0
  18. package/dist/logger/console.logger.test.js.map +1 -0
  19. package/dist/logger/index.d.ts +1 -1
  20. package/dist/logger/index.d.ts.map +1 -1
  21. package/dist/logger/index.js +1 -1
  22. package/dist/logger/index.js.map +1 -1
  23. package/dist/logger/json.logger.test.d.ts +8 -0
  24. package/dist/logger/json.logger.test.d.ts.map +1 -0
  25. package/dist/logger/json.logger.test.js +45 -0
  26. package/dist/logger/json.logger.test.js.map +1 -0
  27. package/dist/logger/logger.d.ts +18 -8
  28. package/dist/logger/logger.d.ts.map +1 -1
  29. package/dist/logger/logger.js.map +1 -1
  30. package/dist/nest-wrapped.logger.d.ts.map +1 -1
  31. package/dist/nest-wrapped.logger.js +15 -6
  32. package/dist/nest-wrapped.logger.js.map +1 -1
  33. package/package.json +15 -8
  34. package/src/index.ts +3 -0
  35. package/src/logger/base.formatter.ts +43 -24
  36. package/src/logger/base.logger.ts +198 -23
  37. package/src/logger/console.logger.test.ts +56 -0
  38. package/src/logger/console.logger.ts +10 -1
  39. package/src/logger/index.ts +6 -1
  40. package/src/logger/json.logger.test.ts +59 -0
  41. package/src/logger/logger.ts +26 -8
  42. package/src/nest-wrapped.logger.ts +17 -6
@@ -1 +1 @@
1
- {"version":3,"file":"nest-wrapped.logger.d.ts","sourceRoot":"","sources":["../src/nest-wrapped.logger.ts"],"names":[],"mappings":"AAAA,OAAO,EAGN,aAAa,EACb,YAAY,EACZ,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAG3C,qBAAa,iBAAkB,YAAW,aAAa,EAAE,YAAY;IAEpE,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAE3B,KAAK,CAAC,OAAO,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,GAAG;IAK1C,KAAK,CAAC,OAAO,EAAE,GAAG,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,GAAG;IAK1D,GAAG,CAAC,OAAO,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,GAAG;IAKxC,OAAO,CAAC,OAAO,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,GAAG;IAK5C,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,GAAG;IAMnC,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ1C,OAAO,CAAC,kBAAkB;CAG1B"}
1
+ {"version":3,"file":"nest-wrapped.logger.d.ts","sourceRoot":"","sources":["../src/nest-wrapped.logger.ts"],"names":[],"mappings":"AACA,OAAO,EAGN,aAAa,EACb,YAAY,EACZ,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAG3C,qBAAa,iBAAkB,YAAW,aAAa,EAAE,YAAY;IAEpE,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAE3B,KAAK,CAAC,OAAO,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,GAAG;IAK1C,KAAK,CAAC,OAAO,EAAE,GAAG,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,GAAG;IAe1D,GAAG,CAAC,OAAO,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,GAAG;IAKxC,OAAO,CAAC,OAAO,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,GAAG;IAK5C,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,GAAG;IAMnC,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ1C,OAAO,CAAC,kBAAkB;CAG1B"}
@@ -1,27 +1,36 @@
1
1
  import { __decorate, __metadata } from "tslib";
2
+ import { BaseException } from '@hg-ts/exception';
2
3
  import { Inject, Logger as NestLogger, } from '@nestjs/common';
3
4
  import { Logger } from './logger/index.js';
4
5
  export class NestWrappedLogger {
5
6
  logger;
6
7
  debug(message, context) {
7
8
  this.setOptionalContext(context);
8
- this.logger.debug(message);
9
+ this.logger.debug(String(message));
9
10
  }
10
11
  error(message, trace, context) {
11
- this.setOptionalContext(context);
12
- this.logger.error(message, trace);
12
+ if (message instanceof Error) {
13
+ this.setOptionalContext(context);
14
+ const exception = message instanceof BaseException
15
+ ? message
16
+ : BaseException.fromError(message);
17
+ this.logger.error(exception, trace ?? message.message);
18
+ return;
19
+ }
20
+ this.setOptionalContext(trace);
21
+ this.logger.error(String(message));
13
22
  }
14
23
  log(message, context) {
15
24
  this.setOptionalContext(context);
16
- this.logger.info(message);
25
+ this.logger.info(String(message));
17
26
  }
18
27
  verbose(message, context) {
19
28
  this.setOptionalContext(context);
20
- this.logger.notice(message);
29
+ this.logger.notice(String(message));
21
30
  }
22
31
  warn(message, context) {
23
32
  this.setOptionalContext(context);
24
- this.logger.warning(message);
33
+ this.logger.warning(String(message));
25
34
  }
26
35
  async onModuleInit() {
27
36
  this.logger.setContext(null);
@@ -1 +1 @@
1
- {"version":3,"file":"nest-wrapped.logger.js","sourceRoot":"","sources":["../src/nest-wrapped.logger.ts"],"names":[],"mappings":";AAAA,OAAO,EACN,MAAM,EACN,MAAM,IAAI,UAAU,GAGpB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAG3C,MAAM,OAAO,iBAAiB;IAEV,MAAM,CAAS;IAE3B,KAAK,CAAC,OAAY,EAAE,OAAgB;QAC1C,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC;QACjC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC5B,CAAC;IAEM,KAAK,CAAC,OAAY,EAAE,KAAc,EAAE,OAAgB;QAC1D,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC;QACjC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACnC,CAAC;IAEM,GAAG,CAAC,OAAY,EAAE,OAAgB;QACxC,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC;QACjC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC3B,CAAC;IAEM,OAAO,CAAC,OAAY,EAAE,OAAgB;QAC5C,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC;QACjC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC7B,CAAC;IAEM,IAAI,CAAC,OAAY,EAAE,OAAgB;QACzC,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC;QACjC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9B,CAAC;IAGM,KAAK,CAAC,YAAY;QACxB,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QAC7B,UAAU,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;QAChC,UAAU,CAAC,cAAc,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;QACxE,UAAU,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;QAChC,UAAU,CAAC,KAAK,EAAE,CAAC;IACpB,CAAC;IAEO,kBAAkB,CAAC,UAA4B,IAAI;QAC1D,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;IACjC,CAAC;CACD;AAvCmB;IADlB,MAAM,EAAE;8BACkB,MAAM;iDAAC"}
1
+ {"version":3,"file":"nest-wrapped.logger.js","sourceRoot":"","sources":["../src/nest-wrapped.logger.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EACN,MAAM,EACN,MAAM,IAAI,UAAU,GAGpB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAG3C,MAAM,OAAO,iBAAiB;IAEV,MAAM,CAAS;IAE3B,KAAK,CAAC,OAAY,EAAE,OAAgB;QAC1C,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC;QACjC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;IACpC,CAAC;IAEM,KAAK,CAAC,OAAY,EAAE,KAAc,EAAE,OAAgB;QAC1D,IAAI,OAAO,YAAY,KAAK,EAAE,CAAC;YAC9B,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC;YACjC,MAAM,SAAS,GAAG,OAAO,YAAY,aAAa;gBACjD,CAAC,CAAC,OAAO;gBACT,CAAC,CAAC,aAAa,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;YAEpC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,EAAE,KAAK,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;YACvD,OAAO;QACR,CAAC;QAED,IAAI,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC;QAC/B,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;IACpC,CAAC;IAEM,GAAG,CAAC,OAAY,EAAE,OAAgB;QACxC,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC;QACjC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;IACnC,CAAC;IAEM,OAAO,CAAC,OAAY,EAAE,OAAgB;QAC5C,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC;QACjC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;IACrC,CAAC;IAEM,IAAI,CAAC,OAAY,EAAE,OAAgB;QACzC,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC;QACjC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;IACtC,CAAC;IAGM,KAAK,CAAC,YAAY;QACxB,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QAC7B,UAAU,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;QAChC,UAAU,CAAC,cAAc,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;QACxE,UAAU,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;QAChC,UAAU,CAAC,KAAK,EAAE,CAAC;IACpB,CAAC;IAEO,kBAAkB,CAAC,UAA4B,IAAI;QAC1D,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;IACjC,CAAC;CACD;AAjDmB;IADlB,MAAM,EAAE;8BACkB,MAAM;iDAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hg-ts/logger",
3
- "version": "0.7.27",
3
+ "version": "0.8.1",
4
4
  "main": "dist/index.js",
5
5
  "type": "module",
6
6
  "exports": {
@@ -13,27 +13,33 @@
13
13
  "build": "tsc",
14
14
  "build:dev": "tsc-watch",
15
15
  "lint:ts": "lint-ts",
16
- "lint:ts:fix": "lint-ts --fix"
16
+ "lint:ts:fix": "lint-ts --fix",
17
+ "test": "vitest run",
18
+ "test:dev": "vitest watch"
17
19
  },
18
20
  "devDependencies": {
19
- "@hg-ts-config/typescript": "0.7.27",
20
- "@hg-ts/async-context": "0.7.27",
21
- "@hg-ts/execution-mode": "0.7.27",
22
- "@hg-ts/linter": "0.7.27",
23
- "@hg-ts/types": "0.7.27",
21
+ "@hg-ts-config/typescript": "0.8.1",
22
+ "@hg-ts/async-context": "0.8.1",
23
+ "@hg-ts/execution-mode": "0.8.1",
24
+ "@hg-ts/linter": "0.8.1",
25
+ "@hg-ts/tests": "0.8.1",
26
+ "@hg-ts/types": "0.8.1",
24
27
  "@nestjs/common": "11.1.0",
25
28
  "@nestjs/core": "11.1.0",
26
29
  "@types/node": "22.19.1",
27
30
  "@types/triple-beam": "1.3.5",
31
+ "@vitest/coverage-v8": "4.0.14",
28
32
  "eslint": "9.18.0",
29
33
  "reflect-metadata": "0.2.2",
30
34
  "rxjs": "7.8.1",
31
35
  "tsc-watch": "6.3.0",
32
36
  "tslib": "2.8.1",
33
- "typescript": "5.7.3"
37
+ "typescript": "5.7.3",
38
+ "vitest": "4.0.14"
34
39
  },
35
40
  "peerDependencies": {
36
41
  "@hg-ts/async-context": "*",
42
+ "@hg-ts/exception": "*",
37
43
  "@hg-ts/execution-mode": "*",
38
44
  "@hg-ts/types": "*",
39
45
  "@nestjs/common": "*",
@@ -43,6 +49,7 @@
43
49
  "tslib": "*"
44
50
  },
45
51
  "dependencies": {
52
+ "@hg-ts/exception": "0.8.1",
46
53
  "triple-beam": "1.4.1",
47
54
  "winston": "3.17.0",
48
55
  "winston-syslog": "2.7.1"
package/src/index.ts CHANGED
@@ -3,6 +3,9 @@ export {
3
3
  ConsoleLogger,
4
4
  JSONLogger,
5
5
  MockLogger,
6
+ type InlineMeta,
7
+ type InlineMetaValue,
8
+ type LogMeta,
6
9
  } from './logger/index.js';
7
10
  export * from './logger.module.js';
8
11
  export * from './trace.context.js';
@@ -1,4 +1,8 @@
1
1
  import * as assert from 'node:assert';
2
+ import {
3
+ BaseException,
4
+ type JSONBaseException,
5
+ } from '@hg-ts/exception';
2
6
  import winston from 'winston';
3
7
  import {
4
8
  LogLevel,
@@ -9,21 +13,43 @@ export type LogMessage = winston.Logform.TransformableInfo & {
9
13
  context: Nullable<string>;
10
14
  message: string;
11
15
  level: LogLevel;
16
+ meta?: Record<string, unknown>;
17
+ error?: JSONBaseException;
18
+ traceId?: string;
12
19
  process: {
13
20
  pid: number;
14
21
  title: Nullable<string>;
15
22
  };
16
23
  timestamp: Date;
17
- items: unknown[];
18
24
  };
19
25
 
26
+ function serializeMeta(meta: unknown): Nullable<Record<string, unknown>> {
27
+ if (!meta || typeof meta !== 'object' || Array.isArray(meta)) {
28
+ return null;
29
+ }
30
+
31
+ return meta as Record<string, unknown>;
32
+ }
33
+
34
+ function serializeError(error: unknown): Nullable<JSONBaseException> {
35
+ if (!(error instanceof BaseException)) {
36
+ return null;
37
+ }
38
+
39
+ return error.toJSON();
40
+ }
41
+
20
42
  function getLogData(info: winston.Logform.TransformableInfo): LogMessage {
21
43
  const level = syslogToLoggerMap.get(info.level) ?? info.level as LogLevel;
22
44
  const context = info['context'] ?? null;
45
+ const meta = serializeMeta(info['meta']);
46
+ const error = serializeError(info['error']);
47
+ const { traceId } = info;
23
48
 
24
49
  assert.ok(typeof context === 'string' || context === null);
50
+ assert.ok(typeof traceId === 'string' || typeof traceId === 'undefined');
25
51
 
26
- return {
52
+ const logData: LogMessage = {
27
53
  level,
28
54
  context,
29
55
  process: {
@@ -31,32 +57,25 @@ function getLogData(info: winston.Logform.TransformableInfo): LogMessage {
31
57
  title: process.title.endsWith('node') ? null : process.title,
32
58
  },
33
59
  message: info.message as string,
34
- items: Array.isArray(info['items']) ? info['items'] : [info['items']],
35
60
  timestamp: new Date(),
36
61
  };
37
- }
38
62
 
39
- export const baseFormatter = winston.format(info => {
40
- const logData = getLogData(info);
41
-
42
- logData['items'] = logData.items
43
- .map(item => {
44
- if (typeof item === 'bigint') {
45
- return `${item}n`;
46
- }
47
-
48
- if (item instanceof Error && !('toJSON' in item)) {
49
- return {
50
- name: item.name,
51
- message: item.message,
52
- stack: item.stack,
53
- };
54
- }
55
-
56
- return item;
57
- });
63
+ if (meta) {
64
+ logData.meta = meta;
65
+ }
66
+
67
+ if (error) {
68
+ logData.error = error;
69
+ }
70
+
71
+ if (traceId) {
72
+ logData.traceId = traceId;
73
+ }
74
+
58
75
  return logData;
59
- });
76
+ }
77
+
78
+ export const baseFormatter = winston.format(info => getLogData(info));
60
79
 
61
80
  export function createFormatter(formatter: (info: LogMessage) => LogMessage): winston.Logform.FormatWrap {
62
81
  return winston.format(formatter as any);
@@ -1,10 +1,27 @@
1
- import { format } from 'node:util';
1
+ import { BaseException } from '@hg-ts/exception';
2
2
  import winston from 'winston';
3
3
  import { TraceContext } from '../trace.context.js';
4
4
 
5
- import { Logger } from './logger.js';
5
+ import {
6
+ InlineMeta,
7
+ InlineMetaPrimitive,
8
+ Logger,
9
+ LogMeta,
10
+ } from './logger.js';
6
11
 
7
12
  type Transport = winston.Logger['add'] extends (transport: infer R) => void ? R : never;
13
+ type LogOptions = {
14
+ error?: BaseException;
15
+ meta: Optional<LogMeta>;
16
+ inlineMeta: Optional<InlineMeta>;
17
+ };
18
+ type ErrorLogOptions = {
19
+ errorOrMessage: BaseException | string;
20
+ messageOrMeta: Optional<string | LogMeta>;
21
+ metaOrInlineMeta: Optional<LogMeta | InlineMeta>;
22
+ inlineMeta: Optional<InlineMeta>;
23
+ };
24
+ type NormalizedLogPayload = LogOptions & { message: string };
8
25
 
9
26
  export type LogLevel = keyof Omit<Logger, 'setContext'>;
10
27
 
@@ -36,29 +53,103 @@ export abstract class BaseLogger implements Logger {
36
53
  });
37
54
  }
38
55
 
39
- public debug(...messages: unknown[]): void {
40
- this.log('debug', messages);
56
+ public debug(message: string, meta?: LogMeta, inlineMeta?: InlineMeta): void {
57
+ this.log('debug', message, {
58
+ meta,
59
+ inlineMeta,
60
+ });
41
61
  }
42
- public info(...messages: unknown[]): void {
43
- this.log('info', messages);
62
+ public info(message: string, meta?: LogMeta, inlineMeta?: InlineMeta): void {
63
+ this.log('info', message, {
64
+ meta,
65
+ inlineMeta,
66
+ });
44
67
  }
45
- public notice(...messages: unknown[]): void {
46
- this.log('notice', messages);
68
+ public notice(message: string, meta?: LogMeta, inlineMeta?: InlineMeta): void {
69
+ this.log('notice', message, {
70
+ meta,
71
+ inlineMeta,
72
+ });
47
73
  }
48
- public warning(...messages: unknown[]): void {
49
- this.log('warning', messages);
74
+ public warning(error: BaseException, message: string, meta?: LogMeta, inlineMeta?: InlineMeta): void;
75
+ public warning(message: string, meta?: LogMeta, inlineMeta?: InlineMeta): void;
76
+ // eslint-disable-next-line max-params
77
+ public warning(
78
+ errorOrMessage: BaseException | string,
79
+ messageOrMeta?: string | LogMeta,
80
+ metaOrInlineMeta?: LogMeta | InlineMeta,
81
+ inlineMeta?: InlineMeta,
82
+ ): void {
83
+ this.logErrorLevel('warning', {
84
+ errorOrMessage,
85
+ messageOrMeta,
86
+ metaOrInlineMeta,
87
+ inlineMeta,
88
+ });
50
89
  }
51
- public error(...messages: unknown[]): void {
52
- this.log('error', messages);
90
+ public error(error: BaseException, message: string, meta?: LogMeta, inlineMeta?: InlineMeta): void;
91
+ public error(message: string, meta?: LogMeta, inlineMeta?: InlineMeta): void;
92
+ // eslint-disable-next-line max-params
93
+ public error(
94
+ errorOrMessage: BaseException | string,
95
+ messageOrMeta?: string | LogMeta,
96
+ metaOrInlineMeta?: LogMeta | InlineMeta,
97
+ inlineMeta?: InlineMeta,
98
+ ): void {
99
+ this.logErrorLevel('error', {
100
+ errorOrMessage,
101
+ messageOrMeta,
102
+ metaOrInlineMeta,
103
+ inlineMeta,
104
+ });
53
105
  }
54
- public critical(...messages: unknown[]): void {
55
- this.log('critical', messages);
106
+ public critical(error: BaseException, message: string, meta?: LogMeta, inlineMeta?: InlineMeta): void;
107
+ public critical(message: string, meta?: LogMeta, inlineMeta?: InlineMeta): void;
108
+ // eslint-disable-next-line max-params
109
+ public critical(
110
+ errorOrMessage: BaseException | string,
111
+ messageOrMeta?: string | LogMeta,
112
+ metaOrInlineMeta?: LogMeta | InlineMeta,
113
+ inlineMeta?: InlineMeta,
114
+ ): void {
115
+ this.logErrorLevel('critical', {
116
+ errorOrMessage,
117
+ messageOrMeta,
118
+ metaOrInlineMeta,
119
+ inlineMeta,
120
+ });
56
121
  }
57
- public alert(...messages: unknown[]): void {
58
- this.log('alert', messages);
122
+ public alert(error: BaseException, message: string, meta?: LogMeta, inlineMeta?: InlineMeta): void;
123
+ public alert(message: string, meta?: LogMeta, inlineMeta?: InlineMeta): void;
124
+ // eslint-disable-next-line max-params
125
+ public alert(
126
+ errorOrMessage: BaseException | string,
127
+ messageOrMeta?: string | LogMeta,
128
+ metaOrInlineMeta?: LogMeta | InlineMeta,
129
+ inlineMeta?: InlineMeta,
130
+ ): void {
131
+ this.logErrorLevel('alert', {
132
+ errorOrMessage,
133
+ messageOrMeta,
134
+ metaOrInlineMeta,
135
+ inlineMeta,
136
+ });
59
137
  }
60
- public emergency(...messages: unknown[]): void {
61
- this.log('emergency', messages);
138
+ public emergency(error: BaseException, message: string, meta?: LogMeta, inlineMeta?: InlineMeta): void;
139
+ public emergency(message: string, meta?: LogMeta, inlineMeta?: InlineMeta): void;
140
+ // eslint-disable-next-line max-params
141
+ public emergency(
142
+ errorOrMessage: BaseException | string,
143
+ messageOrMeta?: string | LogMeta,
144
+ metaOrInlineMeta?: LogMeta | InlineMeta,
145
+ inlineMeta?: InlineMeta,
146
+ ): void {
147
+ this.logErrorLevel('emergency', {
148
+ errorOrMessage,
149
+ messageOrMeta,
150
+ metaOrInlineMeta,
151
+ inlineMeta,
152
+ });
62
153
  }
63
154
 
64
155
  public setContext(context: Nullable<string>): void {
@@ -69,15 +160,99 @@ export abstract class BaseLogger implements Logger {
69
160
  this.logger.add(transport);
70
161
  }
71
162
 
72
- private log(level: LogLevel, messages: unknown[]): void {
163
+ private logErrorLevel(level: LogLevel, args: ErrorLogOptions): void {
164
+ const payload = this.normalizeErrorLogPayload(args);
165
+ const options: LogOptions = {
166
+ meta: payload.meta,
167
+ inlineMeta: payload.inlineMeta,
168
+ };
169
+
170
+ if (payload.error) {
171
+ options.error = payload.error;
172
+ }
173
+
174
+ this.log(level, payload.message, options);
175
+ }
176
+
177
+ private log(level: LogLevel, message: string, options: LogOptions): void {
178
+ const {
179
+ error,
180
+ meta,
181
+ inlineMeta,
182
+ } = options;
73
183
  const mappedLevel = loggerToSyslogMap.get(level) ?? level;
74
184
 
75
- this.logger.log({
185
+ const payload: winston.LogEntry = {
76
186
  level: mappedLevel,
77
- message: format(...messages),
78
- items: messages,
187
+ message: `${message}${this.formatInlineMeta(inlineMeta)}`,
79
188
  context: this.context,
80
189
  traceId: this.traceContext.get()?.traceId,
81
- });
190
+ };
191
+
192
+ if (meta) {
193
+ payload['meta'] = meta;
194
+ }
195
+
196
+ if (error) {
197
+ payload['error'] = error;
198
+ }
199
+
200
+ this.logger.log(payload);
201
+ }
202
+
203
+ private normalizeErrorLogPayload(args: ErrorLogOptions): NormalizedLogPayload {
204
+ const {
205
+ errorOrMessage,
206
+ messageOrMeta,
207
+ metaOrInlineMeta,
208
+ inlineMeta,
209
+ } = args;
210
+
211
+ if (errorOrMessage instanceof BaseException) {
212
+ return {
213
+ error: errorOrMessage,
214
+ message: messageOrMeta as string,
215
+ meta: metaOrInlineMeta as LogMeta,
216
+ inlineMeta,
217
+ };
218
+ }
219
+
220
+ return {
221
+ message: errorOrMessage,
222
+ meta: messageOrMeta as LogMeta,
223
+ inlineMeta: metaOrInlineMeta as InlineMeta,
224
+ };
225
+ }
226
+
227
+ private formatInlineMeta(inlineMeta: Optional<InlineMeta>): string {
228
+ if (!inlineMeta || Object.keys(inlineMeta).length === 0) {
229
+ return '';
230
+ }
231
+
232
+ const items = Object
233
+ .entries(inlineMeta)
234
+ .map(([key, value]) => `${key}=${this.formatInlineMetaValue(value)}`);
235
+
236
+ return `: ${items.join(', ')}`;
237
+ }
238
+
239
+ private formatInlineMetaValue(value: InlineMeta[keyof InlineMeta]): string {
240
+ if (Array.isArray(value)) {
241
+ return `[${value.map(item => this.formatInlineMetaPrimitive(item)).join(',')}]`;
242
+ }
243
+
244
+ return this.formatInlineMetaPrimitive(value);
245
+ }
246
+
247
+ private formatInlineMetaPrimitive(value: InlineMetaPrimitive): string {
248
+ if (value instanceof Date) {
249
+ return value.toISOString();
250
+ }
251
+
252
+ if (Buffer.isBuffer(value)) {
253
+ return value.toString('base64');
254
+ }
255
+
256
+ return String(value);
82
257
  }
83
258
  }
@@ -0,0 +1,56 @@
1
+ import {
2
+ Describe,
3
+ expect,
4
+ Suite,
5
+ Test,
6
+ } from '@hg-ts/tests';
7
+ import { vi } from 'vitest';
8
+
9
+ type ConsoleStdout = {
10
+ _stdout: {
11
+ write: typeof process.stdout.write;
12
+ };
13
+ };
14
+
15
+ @Describe()
16
+ export class ConsoleLoggerTest extends Suite {
17
+ private output: string[] = [];
18
+
19
+ @Test()
20
+ public async writesMessageInlineMetaAndMetaToStdout(): Promise<void> {
21
+ const { TraceContext } = await import('../trace.context.js');
22
+ const { ConsoleLogger } = await import('./console.logger.js');
23
+ const logger = new ConsoleLogger(new TraceContext());
24
+
25
+ logger.info(
26
+ 'message',
27
+ { metaKey: 'metaValue' },
28
+ {
29
+ inlineMetaKey: 123,
30
+ anotherKey: true,
31
+ },
32
+ );
33
+
34
+ expect(this.getOutput()).toContain('message: inlineMetaKey=123, anotherKey=true {"metaKey":"metaValue"}');
35
+ }
36
+
37
+ public override async beforeEach(): Promise<void> {
38
+ this.output = [];
39
+ vi.resetModules();
40
+ const stdout = (console as unknown as ConsoleStdout)['_stdout'];
41
+
42
+ vi.spyOn(stdout, 'write').mockImplementation(((chunk: string | Uint8Array): boolean => {
43
+ this.output.push(chunk.toString());
44
+
45
+ return true;
46
+ }) as typeof process.stdout.write);
47
+ }
48
+
49
+ public override async afterEach(): Promise<void> {
50
+ vi.restoreAllMocks();
51
+ }
52
+
53
+ private getOutput(): string {
54
+ return this.output.join('');
55
+ }
56
+ }
@@ -60,12 +60,21 @@ function formatTime(time: Date): string {
60
60
  return time.toLocaleString('ru-RU');
61
61
  }
62
62
 
63
+ function formatMeta(meta?: Record<string, unknown>): string {
64
+ if (!meta || Object.keys(meta).length === 0) {
65
+ return '';
66
+ }
67
+
68
+ return ` ${JSON.stringify(meta)}`;
69
+ }
70
+
63
71
  const customFormat = createFormatter(info => {
64
72
  const process = formatProcess(info.process);
65
73
  const timestamp = formatTime(info.timestamp);
66
74
  const context = colorizeContext(info.context);
67
75
  const level = colorizeLevel(info.level);
68
- const message = colorizeMessage(info.level, info.message);
76
+ const rawMessage = `${info.message}${formatMeta(info.meta)}`;
77
+ const message = colorizeMessage(info.level, rawMessage);
69
78
  const traceId = info['traceId'] ? formatTraceId(info['traceId'] as string) : null;
70
79
 
71
80
  const metaInfoItems: string[] = [timestamp, context, level, process];
@@ -1,4 +1,9 @@
1
- export { Logger } from './logger.js';
1
+ export {
2
+ Logger,
3
+ type InlineMeta,
4
+ type InlineMetaValue,
5
+ type LogMeta,
6
+ } from './logger.js';
2
7
  export { MockLogger } from './mock.logger.js';
3
8
  export { ConsoleLogger } from './console.logger.js';
4
9
  export { JSONLogger } from './json.logger.js';
@@ -0,0 +1,59 @@
1
+ import {
2
+ Describe,
3
+ expect,
4
+ Suite,
5
+ Test,
6
+ } from '@hg-ts/tests';
7
+ import { vi } from 'vitest';
8
+
9
+ type ConsoleStdout = {
10
+ _stdout: {
11
+ write: typeof process.stdout.write;
12
+ };
13
+ };
14
+
15
+ @Describe()
16
+ export class JSONLoggerTest extends Suite {
17
+ private output: string[] = [];
18
+
19
+ @Test()
20
+ public async writesMessageInlineMetaAndMetaToStdout(): Promise<void> {
21
+ const { TraceContext } = await import('../trace.context.js');
22
+ const { JSONLogger } = await import('./json.logger.js');
23
+ const traceContext = new TraceContext();
24
+ const logger = new JSONLogger(traceContext);
25
+ const traceId = 'test-trace-id';
26
+
27
+ traceContext.run({ traceId }, () => logger.info(
28
+ 'message',
29
+ { metaKey: 'metaValue' },
30
+ {
31
+ inlineMetaKey: 123,
32
+ anotherKey: true,
33
+ },
34
+ ));
35
+
36
+ const log = JSON.parse(this.output.join('')) as Record<string, unknown>;
37
+
38
+ expect(log['message']).toBe('message: inlineMetaKey=123, anotherKey=true');
39
+ expect(log['meta']).toEqual({ metaKey: 'metaValue' });
40
+ expect(log['inlineMeta']).toBeUndefined();
41
+ expect(log['traceId']).toBe(traceId);
42
+ }
43
+
44
+ public override async beforeEach(): Promise<void> {
45
+ this.output = [];
46
+ vi.resetModules();
47
+ const stdout = (console as unknown as ConsoleStdout)['_stdout'];
48
+
49
+ vi.spyOn(stdout, 'write').mockImplementation(((chunk: string | Uint8Array): boolean => {
50
+ this.output.push(chunk.toString());
51
+
52
+ return true;
53
+ }) as typeof process.stdout.write);
54
+ }
55
+
56
+ public override async afterEach(): Promise<void> {
57
+ vi.restoreAllMocks();
58
+ }
59
+ }
@@ -1,12 +1,30 @@
1
+ import { type BaseException } from '@hg-ts/exception';
2
+
3
+ export type InlineMetaPrimitive = string | number | boolean | Date | Buffer;
4
+
5
+ export type InlineMetaValue = InlineMetaPrimitive | InlineMetaPrimitive[];
6
+ export type InlineMeta = Record<string, InlineMetaValue>;
7
+ export type LogMeta = Record<string, unknown>;
8
+
1
9
  export abstract class Logger {
2
- public abstract debug(...messages: unknown[]): void;
3
- public abstract info(...messages: unknown[]): void;
4
- public abstract notice(...messages: unknown[]): void;
5
- public abstract warning(...messages: unknown[]): void;
6
- public abstract error(...messages: unknown[]): void;
7
- public abstract critical(...messages: unknown[]): void;
8
- public abstract alert(...messages: unknown[]): void;
9
- public abstract emergency(...messages: unknown[]): void;
10
+ public abstract debug(message: string, meta?: LogMeta, inlineMeta?: InlineMeta): void;
11
+ public abstract info(message: string, meta?: LogMeta, inlineMeta?: InlineMeta): void;
12
+ public abstract notice(message: string, meta?: LogMeta, inlineMeta?: InlineMeta): void;
13
+
14
+ public abstract warning(error: BaseException, message: string, meta?: LogMeta, inlineMeta?: InlineMeta): void;
15
+ public abstract warning(message: string, meta?: LogMeta, inlineMeta?: InlineMeta): void;
16
+
17
+ public abstract error(error: BaseException, message: string, meta?: LogMeta, inlineMeta?: InlineMeta): void;
18
+ public abstract error(message: string, meta?: LogMeta, inlineMeta?: InlineMeta): void;
19
+
20
+ public abstract critical(error: BaseException, message: string, meta?: LogMeta, inlineMeta?: InlineMeta): void;
21
+ public abstract critical(message: string, meta?: LogMeta, inlineMeta?: InlineMeta): void;
22
+
23
+ public abstract alert(error: BaseException, message: string, meta?: LogMeta, inlineMeta?: InlineMeta): void;
24
+ public abstract alert(message: string, meta?: LogMeta, inlineMeta?: InlineMeta): void;
25
+
26
+ public abstract emergency(error: BaseException, message: string, meta?: LogMeta, inlineMeta?: InlineMeta): void;
27
+ public abstract emergency(message: string, meta?: LogMeta, inlineMeta?: InlineMeta): void;
10
28
 
11
29
  public abstract setContext(context: Nullable<string>): void;
12
30
  }