@travetto/log 4.0.0-rc.8 → 4.0.0

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/README.md CHANGED
@@ -16,7 +16,7 @@ yarn add @travetto/log
16
16
  This module provides logging functionality, building upon [ConsoleManager](https://github.com/travetto/travetto/tree/main/module/base/src/console.ts) in the [Base](https://github.com/travetto/travetto/tree/main/module/base#readme "Environment config and common utilities for travetto applications.") module. This is all ultimately built upon [console](https://nodejs.org/api/console.html) operations. The logging infrastructure is built upon the [Dependency Injection](https://github.com/travetto/travetto/tree/main/module/di#readme "Dependency registration/management and injection support.") system, and so new loggers can be created that rely upon dependency injected services and sources.
17
17
 
18
18
  ## Extending the Common Logger
19
- By default, the system ships with the [CommonLogger](https://github.com/travetto/travetto/tree/main/module/log/src/common.ts#L12), and by default will leverage the [LineLogFormatter](https://github.com/travetto/travetto/tree/main/module/log/src/formatter/line.ts#L36) and the [ConsoleLogAppender](https://github.com/travetto/travetto/tree/main/module/log/src/appender/console.ts#L7). The configuration [CommonLoggerConfig](https://github.com/travetto/travetto/tree/main/module/log/src/common.ts#L12) provides two configuration variables that allows for switching out [LineLogFormatter](https://github.com/travetto/travetto/tree/main/module/log/src/formatter/line.ts#L36) for the [JsonLogFormatter](https://github.com/travetto/travetto/tree/main/module/log/src/formatter/json.ts#L15), depending on the value of `CommonLoggerConfig.format`. Additionally the [ConsoleLogAppender](https://github.com/travetto/travetto/tree/main/module/log/src/appender/console.ts#L7) can be swapped out for the [FileLogAppender](https://github.com/travetto/travetto/tree/main/module/log/src/appender/file.ts#L11) depending on the value of `CommonLoggerConfig.output`.
19
+ By default, the system ships with the [CommonLogger](https://github.com/travetto/travetto/tree/main/module/log/src/common.ts#L12), and by default will leverage the [LineLogFormatter](https://github.com/travetto/travetto/tree/main/module/log/src/formatter/line.ts#L37) and the [ConsoleLogAppender](https://github.com/travetto/travetto/tree/main/module/log/src/appender/console.ts#L7). The configuration [CommonLoggerConfig](https://github.com/travetto/travetto/tree/main/module/log/src/common.ts#L12) provides two configuration variables that allows for switching out [LineLogFormatter](https://github.com/travetto/travetto/tree/main/module/log/src/formatter/line.ts#L37) for the [JsonLogFormatter](https://github.com/travetto/travetto/tree/main/module/log/src/formatter/json.ts#L16), depending on the value of `CommonLoggerConfig.format`. Additionally the [ConsoleLogAppender](https://github.com/travetto/travetto/tree/main/module/log/src/appender/console.ts#L7) can be swapped out for the [FileLogAppender](https://github.com/travetto/travetto/tree/main/module/log/src/appender/file.ts#L11) depending on the value of `CommonLoggerConfig.output`.
20
20
 
21
21
  **Code: Standard Logging Config**
22
22
  ```typescript
@@ -29,7 +29,7 @@ export class CommonLoggerConfig {
29
29
  }
30
30
  ```
31
31
 
32
- In addition to these simple overrides, the [CommonLogger](https://github.com/travetto/travetto/tree/main/module/log/src/common.ts#L12) can be extended by providing an implementation of either a [LogFormatter](https://github.com/travetto/travetto/tree/main/module/log/src/types.ts#L38) or [LogAppender](https://github.com/travetto/travetto/tree/main/module/log/src/types.ts#L30), with the declared symbol of `LogCommonⲐ`.
32
+ In addition to these simple overrides, the [CommonLogger](https://github.com/travetto/travetto/tree/main/module/log/src/common.ts#L12) can be extended by providing an implementation of either a [LogFormatter](https://github.com/travetto/travetto/tree/main/module/log/src/types.ts#L34) or [LogAppender](https://github.com/travetto/travetto/tree/main/module/log/src/types.ts#L26), with the declared symbol of `LogCommonⲐ`.
33
33
 
34
34
  **Code: Sample Common Formatter**
35
35
  ```typescript
@@ -45,10 +45,10 @@ export class SampleFormatter implements LogFormatter {
45
45
  }
46
46
  ```
47
47
 
48
- As you can see, implementing [LogFormatter](https://github.com/travetto/travetto/tree/main/module/log/src/types.ts#L38)/[LogAppender](https://github.com/travetto/travetto/tree/main/module/log/src/types.ts#L30) with the appropriate symbol is all that is necessary to customize the general logging functionality.
48
+ As you can see, implementing [LogFormatter](https://github.com/travetto/travetto/tree/main/module/log/src/types.ts#L34)/[LogAppender](https://github.com/travetto/travetto/tree/main/module/log/src/types.ts#L26) with the appropriate symbol is all that is necessary to customize the general logging functionality.
49
49
 
50
50
  ## Creating a Logger
51
- The default pattern for logging is to create a [Logger](https://github.com/travetto/travetto/tree/main/module/log/src/types.ts#L45) which simply consumes a logging event. The method is not asynchronous as ensuring the ordering of append calls will be the responsibility of the logger. The default logger uses `console.log` and that is synchronous by default.
51
+ The default pattern for logging is to create a [Logger](https://github.com/travetto/travetto/tree/main/module/log/src/types.ts#L41) which simply consumes a logging event. The method is not asynchronous as ensuring the ordering of append calls will be the responsibility of the logger. The default logger uses `console.log` and that is synchronous by default.
52
52
 
53
53
  **Code: Logger Shape**
54
54
  ```typescript
@@ -64,10 +64,6 @@ export interface LogEvent extends ConsoleEvent {
64
64
  * Log message
65
65
  */
66
66
  message?: string;
67
- /**
68
- * Log Message context
69
- */
70
- context?: Record<string, unknown>;
71
67
  }
72
68
  ```
73
69
 
@@ -114,7 +110,7 @@ export class CustomLogger implements Logger {
114
110
  ```
115
111
 
116
112
  ## Creating a Decorator
117
- In addition to being able to control the entire logging experience, there are also scenarios in which the caller may want to only add information to the log event, without affecting control of the formatting or appending. The [Logger](https://github.com/travetto/travetto/tree/main/module/log/src/types.ts#L22) is an interface that provides a contract that allows transforming the [LogEvent](https://github.com/travetto/travetto/tree/main/module/log/src/types.ts#L8) data. A common scenario for this would be to add additional metadata data (e.g. server name, ip, code revision, CPU usage, memory usage, etc) into the log messages.
113
+ In addition to being able to control the entire logging experience, there are also scenarios in which the caller may want to only add information to the log event, without affecting control of the formatting or appending. The [Logger](https://github.com/travetto/travetto/tree/main/module/log/src/types.ts#L18) is an interface that provides a contract that allows transforming the [LogEvent](https://github.com/travetto/travetto/tree/main/module/log/src/types.ts#L8) data. A common scenario for this would be to add additional metadata data (e.g. server name, ip, code revision, CPU usage, memory usage, etc) into the log messages.
118
114
 
119
115
  **Code: Log Decorator Shape**
120
116
  ```typescript
package/__index__.ts CHANGED
@@ -3,6 +3,7 @@ export * from './src/service';
3
3
  export * from './src/formatter/json';
4
4
  export * from './src/formatter/line';
5
5
  export * from './src/formatter/google';
6
+ export * from './src/formatter/util';
6
7
  export * from './src/appender/console';
7
8
  export * from './src/appender/file';
8
9
  export * from './src/types';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@travetto/log",
3
- "version": "4.0.0-rc.8",
3
+ "version": "4.0.0",
4
4
  "description": "Logging framework that integrates at the console.log level.",
5
5
  "keywords": [
6
6
  "typescript",
@@ -23,9 +23,9 @@
23
23
  "directory": "module/log"
24
24
  },
25
25
  "dependencies": {
26
- "@travetto/config": "^4.0.0-rc.8",
27
- "@travetto/di": "^4.0.0-rc.8",
28
- "@travetto/terminal": "^4.0.0-rc.8"
26
+ "@travetto/config": "^4.0.0",
27
+ "@travetto/di": "^4.0.0",
28
+ "@travetto/terminal": "^4.0.0"
29
29
  },
30
30
  "travetto": {
31
31
  "displayName": "Logging"
@@ -1,8 +1,7 @@
1
- import util from 'node:util';
2
-
3
1
  import { Injectable } from '@travetto/di';
4
2
 
5
3
  import { LogFormatter, LogEvent } from '../types';
4
+ import { LogFormatUtil } from './util';
6
5
 
7
6
  /**
8
7
  * Google Logging Formatter
@@ -11,33 +10,30 @@ import { LogFormatter, LogEvent } from '../types';
11
10
  */
12
11
  @Injectable()
13
12
  export class GoogleLogFormatter implements LogFormatter {
14
- #inspectOptions = { colors: false, showHidden: false, depth: 5, breakLength: 200 };
13
+ format(ev: LogEvent): string {
14
+ const context = LogFormatUtil.getContext(ev);
15
15
 
16
- format({
17
- source: file, line, scope, level, message, timestamp, module, args,
18
- context: { method, path, statusCode, ...context } = {},
19
- }: LogEvent): string {
20
- const final: unknown[] = [...args];
21
- if (message) {
22
- args.unshift(message);
23
- }
24
- if (Object.keys(context).length) {
25
- args.push(context);
16
+ const extra: Record<string, unknown> = {};
17
+ // Http request specific
18
+ if (context && ('method' in context && 'path' in context && 'statusCode' in context)) {
19
+ extra.httpRequest = {
20
+ requestMethod: context.method,
21
+ requestUrl: context.path,
22
+ status: context.statusCode
23
+ };
24
+ delete context.method;
25
+ delete context.path;
26
+ delete context.statusCode;
26
27
  }
28
+
27
29
  return JSON.stringify({
28
30
  context,
29
- 'logging.googleapis.com/sourceLocation': { file, line },
30
- 'logging.googleapis.com/labels': { module, scope },
31
- severity: level,
32
- message: util.formatWithOptions(this.#inspectOptions, final),
33
- timestamp,
34
- ...(method ? {
35
- httpRequest: {
36
- requestMethod: method,
37
- requestUrl: path,
38
- status: statusCode
39
- }
40
- } : {})
31
+ 'logging.googleapis.com/sourceLocation': { file: ev.source, line: ev.line },
32
+ 'logging.googleapis.com/labels': { module: ev.module, scope: ev.scope },
33
+ severity: ev.level,
34
+ message: LogFormatUtil.getLogMessage(ev),
35
+ timestamp: ev.timestamp,
36
+ ...extra,
41
37
  });
42
38
  }
43
39
  }
@@ -2,6 +2,7 @@ import { Injectable } from '@travetto/di';
2
2
  import { Config } from '@travetto/config';
3
3
 
4
4
  import { LogEvent, LogFormatter } from '../types';
5
+ import { LogFormatUtil } from './util';
5
6
 
6
7
  @Config('log')
7
8
  export class JSONLogFormatterConfig {
@@ -20,6 +21,13 @@ export class JsonLogFormatter implements LogFormatter {
20
21
  }
21
22
 
22
23
  format(ev: LogEvent): string {
23
- return JSON.stringify(ev, null, this.opts.jsonIndent);
24
+ const { message: _m, args: _a, ...rest } = ev;
25
+ const message = LogFormatUtil.getLogMessage(ev);
26
+ const context = LogFormatUtil.getContext(ev);
27
+ return JSON.stringify({
28
+ ...rest,
29
+ ...(message ? { message } : {}),
30
+ ...(context ? { context } : {}),
31
+ }, null, this.opts.jsonIndent);
24
32
  }
25
33
  }
@@ -7,6 +7,7 @@ import { Ignore } from '@travetto/schema';
7
7
  import { StyleUtil } from '@travetto/terminal';
8
8
 
9
9
  import { LogEvent, LogFormatter } from '../types';
10
+ import { LogFormatUtil } from './util';
10
11
 
11
12
  /**
12
13
  * Level coloring
@@ -85,13 +86,6 @@ export class LineLogFormatter implements LogFormatter {
85
86
  this.opts = opts;
86
87
  }
87
88
 
88
- pretty(ev: LogEvent, o: unknown): string {
89
- return util.inspect(o, {
90
- ...this.opts.inspectOptions,
91
- showHidden: ev.level === 'debug',
92
- });
93
- }
94
-
95
89
  /**
96
90
  * Format an event into a single line
97
91
  */
@@ -129,17 +123,7 @@ export class LineLogFormatter implements LogFormatter {
129
123
  out.push(`[${loc}]`);
130
124
  }
131
125
 
132
- if (ev.message) {
133
- out.push(ev.message);
134
- }
135
-
136
- if (ev.args && ev.args.length) {
137
- out.push(...ev.args.map(a => this.pretty(ev, a)));
138
- }
139
-
140
- if (ev.context && Object.keys(ev.context).length) {
141
- out.push(this.pretty(ev, ev.context));
142
- }
126
+ out.push(LogFormatUtil.getLogMessage(ev, this.opts.inspectOptions));
143
127
 
144
128
  return out.join(' ');
145
129
  }
@@ -0,0 +1,24 @@
1
+ import { inspect, type InspectOptions } from 'node:util';
2
+ import { ObjectUtil } from '@travetto/base';
3
+ import { LogEvent } from '../types';
4
+
5
+ export class LogFormatUtil {
6
+ static #inspectOptions = { colors: false, showHidden: false, depth: 5, breakLength: 200 };
7
+
8
+ /** Generate log context */
9
+ static getContext(ev: LogEvent): Record<string, unknown> | undefined {
10
+ const out: Record<string, unknown> = {};
11
+ for (const o of ev.args ?? []) {
12
+ if (ObjectUtil.isPlainObject(o)) {
13
+ Object.assign(out, o);
14
+ }
15
+ }
16
+ return out && Object.keys(out).length > 0 ? out : undefined;
17
+ }
18
+
19
+ /** Get log message */
20
+ static getLogMessage(ev: LogEvent, options: InspectOptions = this.#inspectOptions): string {
21
+ const formatted = ev.args?.map(x => inspect(x, options)) ?? [];
22
+ return (ev.message ? [ev.message, ...formatted] : formatted).join(' ');
23
+ }
24
+ }
package/src/service.ts CHANGED
@@ -38,25 +38,14 @@ export class LogService implements ConsoleListener, AutoCreate {
38
38
  */
39
39
  onLog(ev: ConsoleEvent): void {
40
40
  const args = [...ev.args];
41
- let context: Record<string, unknown> | undefined;
42
41
  let message: string | undefined;
43
42
  if (typeof args[0] === 'string') {
44
43
  message = args[0];
45
44
  args.shift(); // First arg is now the message
46
45
  }
47
46
 
48
- // More flexible on context
49
- const last = args[args.length - 1];
50
- if (last !== null && last !== undefined && typeof last === 'object') {
51
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
52
- context = Object.fromEntries(
53
- Object.entries(last).filter(x => typeof x[1] !== 'function')
54
- );
55
- args.pop();
56
- }
57
-
58
47
  // Allow for controlled order of event properties
59
- let outEvent: LogEvent = { ...ev, message, context, args };
48
+ let outEvent: LogEvent = { ...ev, message, args };
60
49
 
61
50
  // Decorate event as needed
62
51
  for (const d of this.#decorators) {
package/src/types.ts CHANGED
@@ -10,10 +10,6 @@ export interface LogEvent extends ConsoleEvent {
10
10
  * Log message
11
11
  */
12
12
  message?: string;
13
- /**
14
- * Log Message context
15
- */
16
- context?: Record<string, unknown>;
17
13
  }
18
14
 
19
15
  /**