@travetto/log 3.0.0-rc.1 → 3.0.0-rc.12
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 +10 -91
- package/{index.ts → __index__.ts} +1 -1
- package/package.json +11 -11
- package/src/appender/console.ts +3 -12
- package/src/appender/file.ts +3 -4
- package/src/common.ts +54 -0
- package/src/formatter/line.ts +21 -20
- package/src/internal/types.ts +1 -0
- package/src/service.ts +24 -120
- package/src/types.ts +10 -42
- package/src/util.ts +0 -76
- package/support/phase.init.ts +0 -12
- package/support/transformer.logger.ts +0 -97
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/
|
|
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 "
|
|
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`
|
|
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
|
-
$
|
|
39
|
+
$ trv main doc/output-run.ts
|
|
123
40
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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
|
```
|
package/package.json
CHANGED
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/log",
|
|
3
|
-
"
|
|
4
|
-
"version": "3.0.0-rc.1",
|
|
3
|
+
"version": "3.0.0-rc.12",
|
|
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
|
-
"
|
|
21
|
-
"src"
|
|
22
|
-
"support"
|
|
17
|
+
"__index__.ts",
|
|
18
|
+
"src"
|
|
23
19
|
],
|
|
24
|
-
"main": "
|
|
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/
|
|
31
|
-
"@travetto/
|
|
26
|
+
"@travetto/config": "^3.0.0-rc.11",
|
|
27
|
+
"@travetto/di": "^3.0.0-rc.11",
|
|
28
|
+
"@travetto/terminal": "^3.0.0-rc.6"
|
|
29
|
+
},
|
|
30
|
+
"travetto": {
|
|
31
|
+
"displayName": "Logging"
|
|
32
32
|
},
|
|
33
33
|
"publishConfig": {
|
|
34
34
|
"access": "public"
|
package/src/appender/console.ts
CHANGED
|
@@ -1,19 +1,10 @@
|
|
|
1
|
-
import {
|
|
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
|
-
|
|
15
|
-
|
|
16
|
-
append(level: LogLevel, message: string): void {
|
|
17
|
-
console;
|
|
7
|
+
append(ev: LogEvent, formatted: string): void {
|
|
8
|
+
console;
|
|
18
9
|
}
|
|
19
10
|
}
|
package/src/appender/file.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { createWriteStream, WriteStream } from 'fs';
|
|
2
|
-
import {
|
|
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(
|
|
26
|
-
this.stream.write(`${
|
|
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,54 @@
|
|
|
1
|
+
import { GlobalEnv } from '@travetto/base';
|
|
2
|
+
import { Config, EnvVar } from '@travetto/config';
|
|
3
|
+
import { Inject, Injectable } from '@travetto/di';
|
|
4
|
+
|
|
5
|
+
import { ConsoleAppender } from './appender/console';
|
|
6
|
+
import { FileAppender } from './appender/file';
|
|
7
|
+
import { JsonFormatter } from './formatter/json';
|
|
8
|
+
import { LineFormatter } from './formatter/line';
|
|
9
|
+
import { Appender, Formatter, LogEvent } from './types';
|
|
10
|
+
|
|
11
|
+
@Config('log')
|
|
12
|
+
export class CommonLoggerConfig {
|
|
13
|
+
/** Should we enrich the console by default */
|
|
14
|
+
@EnvVar('TRV_LOG_FORMAT')
|
|
15
|
+
format: 'line' | 'json' = 'line';
|
|
16
|
+
|
|
17
|
+
/** Log file, if needed */
|
|
18
|
+
@EnvVar('TRV_LOG_FILE')
|
|
19
|
+
file?: string;
|
|
20
|
+
|
|
21
|
+
@EnvVar('TRV_LOG_PLAIN')
|
|
22
|
+
plain?: boolean;
|
|
23
|
+
|
|
24
|
+
@EnvVar('TRV_LOG_TIME')
|
|
25
|
+
timestamp: 's' | 'ms' | false = 'ms';
|
|
26
|
+
|
|
27
|
+
postConstruct(): void {
|
|
28
|
+
if (GlobalEnv.test) {
|
|
29
|
+
this.timestamp = false;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
@Injectable()
|
|
35
|
+
export class CommonLogger {
|
|
36
|
+
#appender: Appender;
|
|
37
|
+
#formatter: Formatter;
|
|
38
|
+
|
|
39
|
+
@Inject()
|
|
40
|
+
config: CommonLoggerConfig;
|
|
41
|
+
|
|
42
|
+
postConstruct(): void {
|
|
43
|
+
this.#formatter = this.config.format === 'line' ?
|
|
44
|
+
new LineFormatter(this.config) :
|
|
45
|
+
new JsonFormatter();
|
|
46
|
+
this.#appender = this.config.file ?
|
|
47
|
+
new FileAppender({ file: this.config.file }) :
|
|
48
|
+
new ConsoleAppender();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
onLog(ev: LogEvent): void {
|
|
52
|
+
this.#appender.append(ev, this.#formatter.format(ev));
|
|
53
|
+
}
|
|
54
|
+
}
|
package/src/formatter/line.ts
CHANGED
|
@@ -1,25 +1,26 @@
|
|
|
1
|
-
import
|
|
1
|
+
import util from 'util';
|
|
2
2
|
|
|
3
|
-
import {
|
|
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:
|
|
12
|
-
debug:
|
|
13
|
-
warn:
|
|
14
|
-
error:
|
|
15
|
-
timestamp:
|
|
16
|
-
location:
|
|
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 =
|
|
38
|
+
const notPlain = opts.plain !== true;
|
|
38
39
|
this.#opts = {
|
|
39
|
-
colorize: notPlain
|
|
40
|
-
timestamp: notPlain ?
|
|
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
|
|
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.
|
|
85
|
-
const ns = ev.
|
|
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 {
|
|
2
|
-
import {
|
|
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 {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
83
|
-
|
|
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
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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
|
-
|
|
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(
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
if (!
|
|
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
|
-
|
|
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 {
|
|
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(
|
|
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
|
-
}
|
package/support/phase.init.ts
DELETED
|
@@ -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
|
-
}
|