@travetto/log 3.3.1 → 3.3.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/README.md CHANGED
@@ -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#L11) can be extended by providing an implementation of either a [LogFormatter](https://github.com/travetto/travetto/tree/main/module/log/src/types.ts#L31) or [LogAppender](https://github.com/travetto/travetto/tree/main/module/log/src/types.ts#L23), 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#L11) 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Ⲑ`.
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#L31)/[LogAppender](https://github.com/travetto/travetto/tree/main/module/log/src/types.ts#L23) 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#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.
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#L38) 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#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.
52
52
 
53
53
  **Code: Logger Shape**
54
54
  ```typescript
@@ -113,6 +113,38 @@ export class CustomLogger implements Logger {
113
113
  }
114
114
  ```
115
115
 
116
+ ## 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.
118
+
119
+ **Code: Log Decorator Shape**
120
+ ```typescript
121
+ export interface LogDecorator {
122
+ decorate(ev: LogEvent): LogEvent;
123
+ }
124
+ ```
125
+
126
+ **Code: Custom Logger**
127
+ ```typescript
128
+ import os from 'os';
129
+
130
+ import { Injectable } from '@travetto/di';
131
+ import { LogDecorator, LogEvent } from '@travetto/log';
132
+
133
+ @Injectable()
134
+ export class CustomDecorator implements LogDecorator {
135
+ decorate(ev: LogEvent): LogEvent {
136
+
137
+ // Add memory usage, and hostname
138
+ Object.assign(ev.context ??= {}, {
139
+ memory: process.memoryUsage,
140
+ hostname: os.hostname()
141
+ });
142
+
143
+ return ev;
144
+ }
145
+ }
146
+ ```
147
+
116
148
  ## Logging to External Systems
117
149
  By default the logging functionality logs messages directly to the console, relying on the `util.inspect` method, as is the standard behavior. When building distributed systems, with multiple separate logs, it is useful to rely on structured logging for common consumption. The framework supports logging as [JSON](https://www.json.org), which is easily consumable by services like [elasticsearch](https://elastic.co) or [AWS Cloudwatch](https://aws.amazon.com/cloudwatch/) if running as a lambda or in a docker container.
118
150
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@travetto/log",
3
- "version": "3.3.1",
3
+ "version": "3.3.3",
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": "^3.3.1",
27
- "@travetto/di": "^3.3.1",
28
- "@travetto/terminal": "^3.3.0"
26
+ "@travetto/config": "^3.3.3",
27
+ "@travetto/di": "^3.3.3",
28
+ "@travetto/terminal": "^3.3.1"
29
29
  },
30
30
  "travetto": {
31
31
  "displayName": "Logging"
@@ -1,3 +1,4 @@
1
1
  export class LogAppenderTarget { }
2
2
  export class LogFormatterTarget { }
3
- export class LoggerTarget { }
3
+ export class LogDecoratorTarget { }
4
+ export class LoggerTarget { }
package/src/service.ts CHANGED
@@ -2,8 +2,8 @@ import { ObjectUtil, ConsoleListener, ConsoleManager, ConsoleEvent } from '@trav
2
2
  import { AutoCreate, DependencyRegistry, Injectable } from '@travetto/di';
3
3
  import { GlobalTerminal } from '@travetto/terminal';
4
4
 
5
- import { LogEvent, Logger } from './types';
6
- import { LoggerTarget } from './internal/types';
5
+ import { LogDecorator, LogEvent, Logger } from './types';
6
+ import { LogDecoratorTarget, LoggerTarget } from './internal/types';
7
7
  import { CommonLogger } from './common';
8
8
 
9
9
  /**
@@ -13,24 +13,25 @@ import { CommonLogger } from './common';
13
13
  export class LogService implements ConsoleListener, AutoCreate {
14
14
 
15
15
  /**
16
- * List of all listeners
16
+ * Log listeners
17
17
  */
18
18
  #listeners: Logger[] = [];
19
19
 
20
+ /**
21
+ * Log decorators
22
+ */
23
+ #decorators: LogDecorator[] = [];
24
+
20
25
  async postConstruct(): Promise<void> {
21
26
  await GlobalTerminal.init();
22
27
 
23
- const loggers = DependencyRegistry.getCandidateTypes(LoggerTarget).filter(c => c.class !== CommonLogger);
24
-
25
- // If the user specified logger(s) directly, load them all
26
- if (loggers.length) {
27
- const instances = await Promise.all(loggers.map(l => DependencyRegistry.getInstance<Logger>(l.class, l.qualifier)));
28
- for (const inst of instances) {
29
- this.#listeners.push(inst);
30
- }
31
- } else { // Otherwise fall back to the common logger
32
- this.#listeners.push(await DependencyRegistry.getInstance(CommonLogger));
28
+ this.#listeners = await DependencyRegistry.getCandidateInstances<Logger>(LoggerTarget, c => c.class !== CommonLogger);
29
+ if (!this.#listeners.length) {
30
+ this.#listeners = [await DependencyRegistry.getInstance(CommonLogger)];
33
31
  }
32
+
33
+ this.#decorators = await DependencyRegistry.getCandidateInstances<LogDecorator>(LogDecoratorTarget);
34
+
34
35
  // Take over
35
36
  ConsoleManager.set(this, true);
36
37
  }
@@ -40,27 +41,29 @@ export class LogService implements ConsoleListener, AutoCreate {
40
41
  */
41
42
  onLog(ev: ConsoleEvent): void {
42
43
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions, prefer-const
43
- let [message, context, ...args] = ev.args as [string, Record<string, unknown>, ...unknown[]];
44
+ let [message, context, ...args] = ev.args as [string | undefined, Record<string, unknown>, ...unknown[]];
44
45
  if (!ObjectUtil.isPlainObject(context)) {
45
- args.unshift(context);
46
+ if (context !== undefined) {
47
+ args.unshift(context);
48
+ }
46
49
  context = {};
47
50
  }
48
51
 
49
- if (typeof message !== 'string') {
52
+ if (typeof message !== 'string' && message !== undefined) {
50
53
  args.unshift(message);
51
- message = '';
54
+ message = undefined;
52
55
  }
53
56
 
54
57
  // Allow for controlled order of event properties
55
- const finalEvent: LogEvent = {
56
- ...ev,
57
- message: message !== '' ? message : undefined,
58
- context,
59
- args: args.filter(x => x !== undefined)
60
- };
58
+ let outEvent: LogEvent = { ...ev, message, context, args };
59
+
60
+ // Decorate event as needed
61
+ for (const d of this.#decorators) {
62
+ outEvent = d.decorate(outEvent);
63
+ }
61
64
 
62
65
  for (const l of this.#listeners) {
63
- l.onLog(finalEvent);
66
+ l.onLog(outEvent);
64
67
  }
65
68
  }
66
69
  }
package/src/types.ts CHANGED
@@ -16,6 +16,13 @@ export interface LogEvent extends ConsoleEvent {
16
16
  context?: Record<string, unknown>;
17
17
  }
18
18
 
19
+ /**
20
+ * @concrete ./internal/types:LogDecoratorTarget
21
+ */
22
+ export interface LogDecorator {
23
+ decorate(ev: LogEvent): LogEvent;
24
+ }
25
+
19
26
  /**
20
27
  * Output appender for the logger
21
28
  * @concrete ./internal/types:LogAppenderTarget