@travetto/log 3.0.0-rc.2 → 3.0.0-rc.20

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
@@ -1,5 +1,5 @@
1
1
  <!-- This file was generated by @travetto/doc and should not be modified directly -->
2
- <!-- Please modify https://github.com/travetto/travetto/tree/main/module/log/doc.ts and execute "npx trv doc" to rebuild -->
2
+ <!-- Please modify https://github.com/travetto/travetto/tree/main/module/log/DOC.ts and execute "npx trv doc" to rebuild -->
3
3
  # Logging
4
4
  ## Logging framework that integrates at the console.log level.
5
5
 
@@ -8,95 +8,12 @@
8
8
  npm install @travetto/log
9
9
  ```
10
10
 
11
- 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 "Application phase management, environment config and common utilities for travetto applications.") module. This is all ultimately built upon [console](https://nodejs.org/api/console.html) operations.
12
-
13
- The supported operations are:
14
-
15
- * `console.error` which logs at the `ERROR` level
16
- * `console.warn` which logs at the `WARN` level
17
- * `console.info` which logs at the `INFO` level
18
- * `console.debug` which logs at the `DEBUG` level
19
- * `console.log` which logs at the `INFO` level
20
-
21
- **Note**: All other console methods are excluded, specifically `trace`, `inspect`, `dir`, `time`/`timeEnd`
22
-
23
- ## Filtering Debug
24
-
25
- The `debug` messages can be filtered using the patterns from the [debug](https://www.npmjs.com/package/debug). You can specify wild cards to only `DEBUG` specific modules, folders or files. You can specify multiple, and you can also add negations to exclude specific packages.
26
-
27
- **Terminal: Sample environment flags**
28
- ```bash
29
- # Debug
30
- $ DEBUG=@app:*,-@trv:model npx trv run app
31
- $ DEBUG=-@trv:registry npx trv run app
32
- $ DEBUG=@trv:rest npx trv run app
33
- $ DEBUG=@trv:*,-@trv:model npx trv run app
34
- ```
35
-
36
- **Note**: In production mode, all `console.debug` invocations are compiled away for performance/security reasons. This means that the code is actually removed, and will not execute.
37
-
38
- ## How Logging is Instrumented
39
-
40
- All of the logging instrumentation occurs at transpilation time. All `console.*` methods are replaced with a call to a globally defined variable that delegates to the [ConsoleManager](https://github.com/travetto/travetto/tree/main/module/base/src/console.ts). This module, hooks into the [ConsoleManager](https://github.com/travetto/travetto/tree/main/module/base/src/console.ts) and receives all logging events from all files compiled by the [Compiler](https://github.com/travetto/travetto/tree/main/module/compiler#readme "Node-integration of Typescript Compiler with advanced functionality for detecting changes in classes and methods.") module.
41
-
42
- A sample of the instrumentation would be:
43
-
44
- **Code: Sample logging at various levels**
45
- ```typescript
46
- export function work() {
47
- console.debug('Start Work');
48
-
49
- try {
50
- 1 / 0;
51
- } catch (err) {
52
- console.error('Divide by zero', { error: err });
53
- }
54
- console.debug('End Work');
55
- }
56
- ```
57
-
58
- **Code: Sample After Transpilation**
59
- ```javascript
60
- "use strict";
61
- Object.defineProperty(exports, "__esModule", { value: true });
62
- exports.work = void 0;
63
- function work() {
64
- ᚕlg("debug", { file: ᚕsrc(__filename), line: 2, scope: "work" }, 'Start Work');
65
- try {
66
- 1 / 0;
67
- }
68
- catch (err) {
69
- ᚕlg("error", { file: ᚕsrc(__filename), line: 7, scope: "work" }, 'Divide by zero', { error: err });
70
- }
71
- ᚕlg("debug", { file: ᚕsrc(__filename), line: 9, scope: "work" }, 'End Work');
72
- }
73
- exports.work = work;
74
- Object.defineProperty(exports, 'ᚕtrv', { configurable: true, value: true });
75
- ```
76
-
77
- And when in `prod` mode transforms into:
78
-
79
- **Code: Sample After Transpilation, in Prod**
80
- ```javascript
81
- "use strict";
82
- Object.defineProperty(exports, "__esModule", { value: true });
83
- exports.work = void 0;
84
- function work() {
85
- try {
86
- 1 / 0;
87
- }
88
- catch (err) {
89
- ᚕlg("error", { file: ᚕsrc(__filename), line: 5, scope: "work" }, 'Divide by Zero', { error: err });
90
- }
91
- }
92
- exports.work = work;
93
- Object.defineProperty(exports, 'ᚕtrv', { configurable: true, value: true });
94
- ```
11
+ 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.
95
12
 
96
13
  ## Logging to External Systems
97
14
  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.
98
15
 
99
- The main caveat that comes with this, is that not all objects can be converted to JSON (specifically circular dependencies, and unsupported types). That end, the framework recommends logging with the following format, `message: string` `context: Record<string, Primitive>`. Here context can be recursive, but the general idea is to only pass in known data structures that will not break the [JSON](https://www.json.org) production.
16
+ The main caveat that comes with this, is that not all objects can be converted to JSON (specifically circular dependencies, and unsupported types). That end, the framework recommends logging with the following format, `message: string` `context: Record<string, Primitive>`. Here context can be recursive, but the general idea is to only pass in known data structures that will not break the [JSON](https://www.json.org) production.
100
17
 
101
18
  ## Sample Output
102
19
 
@@ -119,10 +36,12 @@ The corresponding output would be
119
36
 
120
37
  **Terminal: Logging output**
121
38
  ```bash
122
- $ node @travetto/base/bin/main ./doc/output.ts
39
+ $ trv main doc/output-run.ts
123
40
 
124
- 2022-03-14T04:00:00.618Z info [@trv:log/doc/output:2] Hello World
125
- 2022-03-14T04:00:00.837Z info [@trv:log/doc/output:4] Woah! {
126
- 2022-03-14T04:00:01.510Z info [@trv:log/doc/output:6] Woah!
127
- 2022-03-14T04:00:02.450Z debug [@trv:log/doc/output:8] Test
41
+ 2029-03-14T04:00:00.618Z info [@travetto/log:doc/output.ts:2] Hello World
42
+ 2029-03-14T04:00:00.837Z info [@travetto/log:doc/output.ts:4] Woah! {
43
+ a: { b: { c: { d: 10 } } }
44
+ }
45
+ 2029-03-14T04:00:01.510Z info [@travetto/log:doc/output.ts:6] Woah!
46
+ 2029-03-14T04:00:02.450Z debug [@travetto/log:doc/output.ts:8] Test
128
47
  ```
@@ -4,4 +4,4 @@ export * from './src/formatter/line';
4
4
  export * from './src/appender/console';
5
5
  export * from './src/appender/file';
6
6
  export * from './src/types';
7
- export * from './src/util';
7
+ export * from './src/common';
package/package.json CHANGED
@@ -1,14 +1,11 @@
1
1
  {
2
2
  "name": "@travetto/log",
3
- "displayName": "Logging",
4
- "version": "3.0.0-rc.2",
3
+ "version": "3.0.0-rc.20",
5
4
  "description": "Logging framework that integrates at the console.log level.",
6
5
  "keywords": [
7
6
  "typescript",
8
7
  "travetto",
9
- "logging",
10
- "ast-transformation",
11
- "console-log"
8
+ "logging"
12
9
  ],
13
10
  "homepage": "https://travetto.io",
14
11
  "license": "MIT",
@@ -17,18 +14,21 @@
17
14
  "name": "Travetto Framework"
18
15
  },
19
16
  "files": [
20
- "index.ts",
21
- "src",
22
- "support"
17
+ "__index__.ts",
18
+ "src"
23
19
  ],
24
- "main": "index.ts",
20
+ "main": "__index__.ts",
25
21
  "repository": {
26
22
  "url": "https://github.com/travetto/travetto.git",
27
23
  "directory": "module/log"
28
24
  },
29
25
  "dependencies": {
30
- "@travetto/base": "^3.0.0-rc.0",
31
- "@travetto/transformer": "^3.0.0-rc.2"
26
+ "@travetto/config": "^3.0.0-rc.19",
27
+ "@travetto/di": "^3.0.0-rc.18",
28
+ "@travetto/terminal": "^3.0.0-rc.7"
29
+ },
30
+ "travetto": {
31
+ "displayName": "Logging"
32
32
  },
33
33
  "publishConfig": {
34
34
  "access": "public"
@@ -1,19 +1,10 @@
1
- import { LogLevel } from '@travetto/base';
2
- import { Appender } from '../types';
3
-
4
- /**
5
- * Console logging config
6
- */
7
- export interface ConsoleAppenderOpts {
8
- }
1
+ import { Appender, LogEvent } from '../types';
9
2
 
10
3
  /**
11
4
  * Console.output
12
5
  */
13
6
  export class ConsoleAppender implements Appender {
14
- constructor(opts: ConsoleAppenderOpts = {}) { }
15
-
16
- append(level: LogLevel, message: string): void {
17
- console![level](message);
7
+ append(ev: LogEvent, formatted: string): void {
8
+ console![ev.level](formatted);
18
9
  }
19
10
  }
@@ -1,6 +1,5 @@
1
1
  import { createWriteStream, WriteStream } from 'fs';
2
- import { LogLevel } from '@travetto/base';
3
- import { Appender } from '../types';
2
+ import { Appender, LogEvent } from '../types';
4
3
 
5
4
  /**
6
5
  * File appender options
@@ -22,7 +21,7 @@ export class FileAppender implements Appender {
22
21
  });
23
22
  }
24
23
 
25
- append(level: LogLevel, message: string): void {
26
- this.stream.write(`${message}\n`);
24
+ append(ev: LogEvent, formatted: string): void {
25
+ this.stream.write(`${formatted}\n`);
27
26
  }
28
27
  }
package/src/common.ts ADDED
@@ -0,0 +1,62 @@
1
+ import { GlobalEnv } from '@travetto/base';
2
+ import { Config, EnvVar } from '@travetto/config';
3
+ import { Inject, Injectable } from '@travetto/di';
4
+ import { Ignore } from '@travetto/schema';
5
+ import { GlobalTerminal } from '@travetto/terminal';
6
+
7
+ import { ConsoleAppender } from './appender/console';
8
+ import { FileAppender } from './appender/file';
9
+ import { JsonFormatter } from './formatter/json';
10
+ import { LineFormatter } from './formatter/line';
11
+ import { Appender, Formatter, LogEvent } from './types';
12
+
13
+ @Config('log')
14
+ export class CommonLoggerConfig {
15
+ /** Should we enrich the console by default */
16
+ @EnvVar('TRV_LOG_FORMAT')
17
+ format: 'line' | 'json' = 'line';
18
+
19
+ /** Log file, if needed */
20
+ @EnvVar('TRV_LOG_FILE')
21
+ file?: string;
22
+
23
+ @EnvVar('TRV_LOG_PLAIN')
24
+ plain?: boolean;
25
+
26
+ @EnvVar('TRV_LOG_TIME')
27
+ time: 's' | 'ms' | string = 'ms';
28
+
29
+ @Ignore()
30
+ get timestamp(): 's' | 'ms' | false {
31
+ return (this.time ?? 'ms') === 'ms' ? 'ms' : (this.time === 's' ? 's' : false);
32
+ }
33
+
34
+ postConstruct(): void {
35
+ if (GlobalEnv.test) {
36
+ this.time = '';
37
+ }
38
+ this.plain ??= GlobalTerminal.colorLevel === 0;
39
+ }
40
+ }
41
+
42
+ @Injectable()
43
+ export class CommonLogger {
44
+ #appender: Appender;
45
+ #formatter: Formatter;
46
+
47
+ @Inject()
48
+ config: CommonLoggerConfig;
49
+
50
+ postConstruct(): void {
51
+ this.#formatter = this.config.format === 'line' ?
52
+ new LineFormatter(this.config) :
53
+ new JsonFormatter();
54
+ this.#appender = this.config.file ?
55
+ new FileAppender({ file: this.config.file }) :
56
+ new ConsoleAppender();
57
+ }
58
+
59
+ onLog(ev: LogEvent): void {
60
+ this.#appender.append(ev, this.#formatter.format(ev));
61
+ }
62
+ }
@@ -1,25 +1,26 @@
1
- import * as util from 'util';
1
+ import util from 'util';
2
2
 
3
- import { ColorUtil, EnvUtil } from '@travetto/boot';
3
+ import { GlobalTerminal } from '@travetto/terminal';
4
4
 
5
5
  import { LogEvent, Formatter } from '../types';
6
6
 
7
7
  /**
8
8
  * Level coloring
9
9
  */
10
- export const STYLES = {
11
- info: ColorUtil.makeColorer('white'),
12
- debug: ColorUtil.makeColorer('yellow'),
13
- warn: ColorUtil.makeColorer('magenta'),
14
- error: ColorUtil.makeColorer('cyan', 'inverse'),
15
- timestamp: ColorUtil.makeColorer('white', 'bold'),
16
- location: ColorUtil.makeColorer('blue')
17
- };
10
+ export const STYLES = GlobalTerminal.palette({
11
+ info: ['yellow', 'goldenrod'],
12
+ debug: ['lightGray', '#555555'],
13
+ warn: ['darkOrange', 'brightMagenta'],
14
+ error: ['darkRed', { text: 'brightCyan', inverse: true }],
15
+ timestamp: ['white', 'black'],
16
+ location: ['lightBlue', 'purple']
17
+ });
18
18
 
19
19
  /**
20
20
  * Line formatting options
21
21
  */
22
22
  export interface LineFormatterOpts {
23
+ plain?: boolean;
23
24
  timestamp?: 'ms' | 's' | false;
24
25
  colorize?: boolean;
25
26
  align?: boolean;
@@ -34,10 +35,10 @@ export class LineFormatter implements Formatter {
34
35
  #opts: LineFormatterOpts;
35
36
 
36
37
  constructor(opts: LineFormatterOpts = {}) {
37
- const notPlain = !EnvUtil.isTrue('TRV_LOG_PLAIN');
38
+ const notPlain = opts.plain !== true;
38
39
  this.#opts = {
39
- colorize: notPlain && ColorUtil.colorize,
40
- timestamp: notPlain ? EnvUtil.isValueOrFalse('TRV_LOG_TIME', ['s', 'ms'] as const, 'ms') : undefined,
40
+ colorize: notPlain,
41
+ timestamp: notPlain ? opts.timestamp : undefined,
41
42
  align: true, level: notPlain, location: notPlain,
42
43
  ...opts
43
44
  };
@@ -60,7 +61,7 @@ export class LineFormatter implements Formatter {
60
61
  const out = [];
61
62
 
62
63
  if (opts.timestamp) {
63
- let timestamp = ev.timestamp;
64
+ let timestamp = ev.timestamp.toISOString();
64
65
  if (opts.timestamp === 's') {
65
66
  timestamp = timestamp.replace(/[.]\d{3}/, '');
66
67
  }
@@ -72,17 +73,17 @@ export class LineFormatter implements Formatter {
72
73
 
73
74
  if (opts.level) {
74
75
  let level: string = ev.level;
75
- if (opts.colorize) {
76
- level = STYLES[ev.level](ev.level);
77
- }
78
76
  if (opts.align) {
79
- level += ' '.repeat(5 - ev.level.length);
77
+ level = level.padEnd(5, ' ');
78
+ }
79
+ if (opts.colorize) {
80
+ level = STYLES[ev.level](level);
80
81
  }
81
82
  out.push(level);
82
83
  }
83
84
 
84
- if (ev.file && opts.location) {
85
- const ns = ev.category;
85
+ if (ev.source && opts.location) {
86
+ const ns = `${ev.module}:${ev.modulePath}`;
86
87
  let loc = ev.line ? `${ns}:${ev.line}` : ns;
87
88
  if (opts.colorize) {
88
89
  loc = STYLES.location(loc);
@@ -0,0 +1 @@
1
+ export class LoggerTarget { }
package/src/service.ts CHANGED
@@ -1,125 +1,42 @@
1
- import { EnvUtil } from '@travetto/boot';
2
- import { ConsoleManager, LogLevel, AppManifest, Util } from '@travetto/base';
3
- import { ModuleUtil } from '@travetto/boot/src/internal/module-util';
1
+ import { ObjectUtil, ConsoleListener, ConsoleManager, ConsoleEvent } from '@travetto/base';
2
+ import { AutoCreate, DependencyRegistry, Injectable } from '@travetto/di';
4
3
 
5
- import { Appender, Formatter, LogEvent, LogLevels } from './types';
6
- import { LineFormatter } from './formatter/line';
7
- import { JsonFormatter } from './formatter/json';
8
- import { ConsoleAppender } from './appender/console';
9
- import { FileAppender } from './appender/file';
10
- import { LogUtil } from './util';
11
-
12
- const DefaultLoggerⲐ = Symbol.for('@trv:log/default');
13
-
14
- type LineContext = { file: string, line: number, scope?: string };
4
+ import { LogEvent, Logger } from './types';
5
+ import { LoggerTarget } from './internal/types';
6
+ import { CommonLogger } from './common';
15
7
 
16
8
  /**
17
9
  * Logger service
18
10
  */
19
- class $Logger {
20
-
21
- /**
22
- * Should we enrich the console by default
23
- */
24
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
25
- readonly #logFormat: 'line' | 'json' = EnvUtil.get('TRV_LOG_FORMAT', 'line') as 'line';
26
-
27
- readonly #logFile?: string = EnvUtil.get('TRV_LOG_FILE');
11
+ @Injectable()
12
+ export class LogService implements ConsoleListener, AutoCreate {
28
13
 
29
- /**
30
- * Listeners for logging events
31
- */
32
- #listenerMap = new Map<string | symbol, (ev: LogEvent) => void>();
33
14
  /**
34
15
  * List of all listeners
35
16
  */
36
- #listeners: ((ev: LogEvent) => void)[] = [];
37
-
38
- /**
39
- * List of logging filters
40
- */
41
- #filters: Partial<Record<LogLevel, (x: string) => boolean>> = {};
42
- /**
43
- * Which log levels to exclude
44
- */
45
- #exclude: Partial<Record<LogLevel, boolean>> = { debug: true };
46
-
47
- /**
48
- * Initialize
49
- */
50
- init(): void {
51
- if (AppManifest.env.debug.status !== false) {
52
- delete this.#exclude.debug;
53
- const filter = LogUtil.buildFilter(AppManifest.env.debug.value ?? '@app');
54
- if (filter) {
55
- this.#filters.debug = filter;
56
- }
57
- }
58
-
59
- if (!this.#listenerMap.get(DefaultLoggerⲐ)) {
60
- // Build default formatter
61
- let formatter: Formatter;
62
- switch (this.#logFormat) {
63
- case 'line': formatter = new LineFormatter(); break;
64
- case 'json': formatter = new JsonFormatter(); break;
65
- }
66
- this.listenDefault(formatter, this.#logFile ? new FileAppender({ file: this.#logFile }) : undefined);
67
- }
68
-
69
- ConsoleManager.set(this, true); // Make default
70
- }
71
-
72
- /**
73
- * Add log event listener
74
- */
75
- listen(key: string | symbol, handler: (ev: LogEvent) => void): void {
76
- this.removeListener(key);
77
- this.#listenerMap.set(key, handler);
78
- this.#listeners.push(handler);
79
- }
17
+ #listeners: Logger[] = [];
80
18
 
81
- /**
82
- * Set default listener
83
- * @param formatter
84
- * @param appender Defaults to console appender unless specified
85
- */
86
- listenDefault(formatter: Formatter, appender?: Appender): void {
87
- this.listen(DefaultLoggerⲐ, LogUtil.buildListener(formatter, appender ?? new ConsoleAppender()));
88
- }
89
-
90
- /**
91
- * Clear all listeners
92
- */
93
- removeAll(): void {
94
- this.#listenerMap.clear();
95
- this.#listeners = [];
96
- }
19
+ async postConstruct(): Promise<void> {
20
+ const def = await DependencyRegistry.getInstance(CommonLogger);
21
+ this.#listeners.push(def);
97
22
 
98
- /**
99
- * Remove specific listener
100
- */
101
- removeListener(key: string | symbol): void {
102
- const handler = this.#listenerMap.get(key);
103
- if (handler) {
104
- this.#listenerMap.delete(key);
105
- this.#listeners.splice(this.#listeners.indexOf(handler), 1);
23
+ const loggers = DependencyRegistry.getCandidateTypes(LoggerTarget);
24
+ const instances = await Promise.all(loggers.map(l => DependencyRegistry.getInstance<Logger>(l.class, l.qualifier)));
25
+ for (const inst of instances) {
26
+ this.#listeners.push(inst);
106
27
  }
107
- }
108
28
 
109
- /**
110
- * See if log level is enabled
111
- */
112
- enabled(level: LogLevel): boolean {
113
- return !(level in this.#exclude);
29
+ // Take over
30
+ ConsoleManager.set(this, true);
114
31
  }
115
32
 
116
33
  /**
117
34
  * Endpoint for listening, endpoint registered with ConsoleManager
118
35
  */
119
- onLog(level: LogLevel, { file, line, scope }: LineContext, [message, context, ...args]: [string, Record<string, unknown>, ...unknown[]]): void {
120
- level = (level in LogLevels) ? level : 'info';
121
-
122
- if (!Util.isPlainObject(context)) {
36
+ onLog(ev: ConsoleEvent): void {
37
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions, prefer-const
38
+ let [message, context, ...args] = ev.args as [string, Record<string, unknown>, ...unknown[]];
39
+ if (!ObjectUtil.isPlainObject(context)) {
123
40
  args.unshift(context);
124
41
  context = {};
125
42
  }
@@ -129,29 +46,16 @@ class $Logger {
129
46
  message = '';
130
47
  }
131
48
 
132
- const category = ModuleUtil.getId(file);
133
-
134
- if ((level in this.#exclude) || (category && level in this.#filters && !this.#filters[level]!(category))) {
135
- return;
136
- }
137
-
138
49
  // Allow for controlled order of event properties
139
50
  const finalEvent: LogEvent = {
140
- timestamp: new Date().toISOString(),
141
- level,
142
- file,
143
- line,
144
- category,
145
- scope,
51
+ ...ev,
146
52
  message: message !== '' ? message : undefined,
147
53
  context,
148
54
  args: args.filter(x => x !== undefined)
149
55
  };
150
56
 
151
57
  for (const l of this.#listeners) {
152
- l(finalEvent);
58
+ l.onLog(finalEvent);
153
59
  }
154
60
  }
155
- }
156
-
157
- export const Logger = new $Logger();
61
+ }
package/src/types.ts CHANGED
@@ -1,43 +1,8 @@
1
- import { LogLevel } from '@travetto/base';
2
-
3
- /**
4
- * Log levels, numerically
5
- */
6
- export const LogLevels = {
7
- debug: 1,
8
- info: 2,
9
- warn: 3,
10
- error: 4
11
- };
12
-
1
+ import { ConsoleEvent } from '@travetto/base';
13
2
  /**
14
3
  * Logging event
15
4
  */
16
- export interface LogEvent {
17
- /**
18
- * Time of event, ISO 8601 timestamp
19
- */
20
- timestamp: string;
21
- /**
22
- * Log level
23
- */
24
- level: LogLevel;
25
- /**
26
- * File
27
- */
28
- file: string;
29
- /**
30
- * Line number
31
- */
32
- line: number;
33
- /**
34
- * Categorization of file into a readable name
35
- */
36
- category: string;
37
- /**
38
- * The scope of identifiers to the location of the log statement
39
- */
40
- scope?: string;
5
+ export interface LogEvent extends ConsoleEvent {
41
6
  /**
42
7
  * Log message
43
8
  */
@@ -46,17 +11,13 @@ export interface LogEvent {
46
11
  * Log Message context
47
12
  */
48
13
  context?: Record<string, unknown>;
49
- /**
50
- * Log arguments
51
- */
52
- args?: unknown[];
53
14
  }
54
15
 
55
16
  /**
56
17
  * Output appender for the logger
57
18
  */
58
19
  export interface Appender {
59
- append(level: LogLevel, msg: string): void;
20
+ append(ev: LogEvent, formatted: string): void;
60
21
  }
61
22
 
62
23
  /**
@@ -64,4 +25,11 @@ export interface Appender {
64
25
  */
65
26
  export interface Formatter {
66
27
  format(e: LogEvent): string;
28
+ }
29
+
30
+ /**
31
+ * @concrete ./internal/types:LoggerTarget
32
+ */
33
+ export interface Logger {
34
+ onLog(ev: LogEvent): void;
67
35
  }
package/src/util.ts DELETED
@@ -1,76 +0,0 @@
1
- import { AppManifest } from '@travetto/base';
2
- import { LogEvent, Formatter, Appender } from './types';
3
-
4
- /**
5
- * Logging utilities
6
- */
7
- export class LogUtil {
8
-
9
- /**
10
- * Produce an event listener
11
- */
12
- static buildListener(formatter: Formatter, appender: Appender, filter?: (ev: LogEvent) => boolean): (ev: LogEvent) => void {
13
- if (filter) {
14
- return (ev: LogEvent) => {
15
- if (filter(ev)) {
16
- appender.append(ev.level, formatter.format(ev));
17
- }
18
- };
19
- } else {
20
- return (ev: LogEvent) => appender.append(ev.level, formatter.format(ev));
21
- }
22
- }
23
-
24
- /**
25
- * Build a filter element
26
- */
27
- static buildFilterPart(p: string): { key: 'exc' | 'inc', filter: string[] } {
28
- const [, neg, prop] = p.match(/(-|[+])?(.*)/)!;
29
- const cleaned = (/^.*:[^\/]*[^*]$/.test(prop) ? `${prop}/*` : prop).replace(/([\/.])/g, a => `\\${a}`);
30
- const key: 'exc' | 'inc' = neg ? 'exc' : 'inc';
31
- const filter: string[] = [];
32
-
33
- // Auto wildcard for modules
34
- if (cleaned.startsWith('@app')) {
35
- const [, sfx] = cleaned.match(/^@app(?::(.*)?)?$/)!;
36
- const sub = [...AppManifest.source.common, ...AppManifest.source.local]
37
- .map(f => ['.', f, sfx ?? ''].filter(x => !!x).join('/'));
38
- filter.push(...sub);
39
- } else {
40
- filter.push(cleaned);
41
- }
42
-
43
- return { key, filter };
44
- }
45
-
46
- /**
47
- * Convert filter into test function for filtering
48
- */
49
- static buildFilter(v: string): ((file: string) => boolean) | undefined {
50
- const config: { inc: string[], exc: string[] } = { inc: [], exc: [] };
51
- const { inc, exc } = config;
52
-
53
- for (const p of v.split(/\s*,\s*/)) {
54
- const { key, filter } = this.buildFilterPart(p);
55
- config[key].push(...filter);
56
- }
57
-
58
- if (inc.includes('*')) {
59
- inc.splice(0, inc.length);
60
- } else if (inc.length === 0 && exc.length) { // If excluding and nothing included
61
- const { key, filter } = this.buildFilterPart('@app');
62
- config[key].push(...filter); // Listen to src by default if not explicit
63
- }
64
-
65
- const incRe = new RegExp(`^(${inc.join('|').replace(/[.]/g, '[.]').replace(/[*]/g, '.*')})`);
66
- const excRe = new RegExp(`^(${exc.join('|').replace(/[.]/g, '[.]').replace(/[*]/g, '.*')})`);
67
-
68
- if (inc.length && exc.length) {
69
- return (x: string) => incRe.test(x) && !excRe.test(x);
70
- } else if (inc.length) {
71
- return (x: string) => incRe.test(x);
72
- } else if (exc.length) {
73
- return (x: string) => !excRe.test(x);
74
- }
75
- }
76
- }
@@ -1,12 +0,0 @@
1
- /**
2
- * Initializes the logger to take over from base
3
- */
4
- export const init = {
5
- key: '@trv:log/init',
6
- after: ['@trv:base/init'],
7
- before: ['@trv:config/init'],
8
- action: async (): Promise<void> => {
9
- const { Logger } = await import('../src/service');
10
- Logger.init();
11
- }
12
- };
@@ -1,97 +0,0 @@
1
- import * as ts from 'typescript';
2
-
3
- import { ConsoleManager, AppManifest } from '@travetto/base';
4
- import {
5
- TransformerId, TransformerState, OnCall, CoreUtil, LiteralUtil,
6
- OnClass, AfterClass, OnMethod, AfterMethod, AfterFunction, OnFunction
7
- } from '@travetto/transformer';
8
-
9
- type CustomState = TransformerState & {
10
- scope: { type: 'method' | 'class' | 'function', name: string }[];
11
- };
12
-
13
- /**
14
- * Allows for removal of debug log messages depending on whether app is running
15
- * in prod mode.
16
- */
17
- export class LoggerTransformer {
18
-
19
- static [TransformerId] = '@trv:log';
20
-
21
- static initState(state: CustomState): void {
22
- state.scope = state.scope ?? [];
23
- }
24
-
25
- @OnClass()
26
- static onClass(state: CustomState, node: ts.ClassDeclaration): typeof node {
27
- this.initState(state);
28
- state.scope.push({ type: 'class', name: node.name?.text ?? 'unknown' });
29
- return node;
30
- }
31
-
32
- @AfterClass()
33
- static afterClass(state: CustomState, node: ts.ClassDeclaration): typeof node {
34
- state.scope.pop();
35
- return node;
36
- }
37
-
38
- @OnMethod()
39
- static onMethod(state: CustomState, node: ts.MethodDeclaration): typeof node {
40
- this.initState(state);
41
- let name = 'unknown';
42
- if (ts.isIdentifier(node.name)) {
43
- name = node.name?.text ?? name;
44
- }
45
- state.scope.push({ type: 'method', name });
46
- return node;
47
- }
48
-
49
- @AfterMethod()
50
- static afterMethod(state: CustomState, node: ts.MethodDeclaration): typeof node {
51
- state.scope.pop();
52
- return node;
53
- }
54
-
55
- @OnFunction()
56
- static onFunction(state: CustomState, node: ts.FunctionDeclaration | ts.FunctionExpression): typeof node {
57
- this.initState(state);
58
- state.scope.push({ type: 'function', name: node.name?.text ?? 'unknown' });
59
- return node;
60
- }
61
-
62
- @AfterFunction()
63
- static afterFunction(state: CustomState, node: ts.FunctionDeclaration | ts.FunctionExpression): typeof node {
64
- state.scope.pop();
65
- return node;
66
- }
67
-
68
- @OnCall()
69
- static onDebugCall(state: CustomState, node: ts.CallExpression): typeof node | ts.Identifier {
70
- if (!ts.isIdentifier(node.expression) || node.expression.text !== ConsoleManager.key) {
71
- return node;
72
- }
73
- const arg = CoreUtil.getArgument(node);
74
- if (arg) {
75
- // Okay since we create the object ourselves in ConsoleManager
76
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
77
- if (!ts.isStringLiteral(arg)) {
78
- return node;
79
- }
80
- const level = LiteralUtil.toLiteral(arg, false);
81
- if (AppManifest.prod && level === 'debug') {
82
- return state.createIdentifier('undefined'); // Lose debug logging if in prod
83
- } else {
84
- return state.factory.updateCallExpression(node, node.expression, node.typeArguments, [
85
- state.factory.createStringLiteral(level),
86
- LiteralUtil.fromLiteral(state.factory, {
87
- file: state.getFilenameAsSrc(),
88
- line: state.source.getLineAndCharacterOfPosition(node.getStart(state.source)).line + 1,
89
- scope: state.scope?.map(x => x.name).join(':'),
90
- }),
91
- ...node.arguments.slice(2) // Drop log level, and previous context from base/console support
92
- ]);
93
- }
94
- }
95
- return node;
96
- }
97
- }