@travetto/log 3.1.2 → 3.1.4

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
@@ -15,8 +15,40 @@ yarn add @travetto/log
15
15
 
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
+ ## 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#L11), 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#L11) 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#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#L10) depending on the value of `CommonLoggerConfig.output`.
20
+
21
+ **Code: Standard Logging Config**
22
+ ```typescript
23
+ export class CommonLoggerConfig {
24
+ @EnvVar('TRV_LOG_FORMAT')
25
+ format: 'line' | 'json' = 'line';
26
+
27
+ @EnvVar('TRV_LOG_OUTPUT')
28
+ output: 'console' | 'file' | string = 'console';
29
+ }
30
+ ```
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Ⲑ`.
33
+
34
+ **Code: Sample Common Formatter**
35
+ ```typescript
36
+ import { Injectable } from '@travetto/di';
37
+ import { LogFormatter, LogCommonⲐ, LogEvent } from '@travetto/log';
38
+
39
+ @Injectable(LogCommonⲐ)
40
+ export class SampleFormatter implements LogFormatter {
41
+
42
+ format(e: LogEvent): string {
43
+ return `${e.timestamp} [${e.level}]#[${e.scope ?? 'unknown'}] ${e.message ?? 'NO MESSAGE'} ${(e.args ?? []).join(' ')}`;
44
+ }
45
+ }
46
+ ```
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.
49
+
18
50
  ## Creating a Logger
19
- The default pattern for logging is to create a [Logger](https://github.com/travetto/travetto/tree/main/module/log/src/types.ts#L33) 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#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.
20
52
 
21
53
  **Code: Logger Shape**
22
54
  ```typescript
@@ -61,7 +93,7 @@ export type ConsoleEvent = {
61
93
  };
62
94
  ```
63
95
 
64
- The [LogEvent](https://github.com/travetto/travetto/tree/main/module/log/src/types.ts#L5) is an extension of the [ConsoleEvent](https://github.com/travetto/travetto/tree/main/module/base/src/types.ts#L12) with the addition of two fields:
96
+ The [LogEvent](https://github.com/travetto/travetto/tree/main/module/log/src/types.ts#L8) is an extension of the [ConsoleEvent](https://github.com/travetto/travetto/tree/main/module/base/src/types.ts#L12) with the addition of two fields:
65
97
  * `message` - This is the primary argument passed to the console statement, if it happens to be a string, otherwise the field is left empty
66
98
  * `context` - This is the final argument passed to the console statement, if it happens to be a simple object. This is useful for external loggers that allow for searching/querying by complex data
67
99
 
@@ -93,39 +125,16 @@ The main caveat that comes with this, is that not all objects can be converted t
93
125
  **Code: Standard Logging Config**
94
126
  ```typescript
95
127
  export class CommonLoggerConfig {
96
- @EnvVar('TRV_LOG_COMMON')
97
- commonActive?: boolean;
98
-
99
- /** Should we enrich the console by default */
100
128
  @EnvVar('TRV_LOG_FORMAT')
101
129
  format: 'line' | 'json' = 'line';
102
130
 
103
- /** Log file, if needed */
104
- @EnvVar('TRV_LOG_FILE')
105
- file?: string;
106
-
107
- @EnvVar('TRV_LOG_PLAIN')
108
- plain?: boolean;
109
-
110
- @EnvVar('TRV_LOG_TIME')
111
- time: 's' | 'ms' | string = 'ms';
112
-
113
- @Ignore()
114
- get timestamp(): 's' | 'ms' | false {
115
- return (this.time ?? 'ms') === 'ms' ? 'ms' : (this.time === 's' ? 's' : false);
116
- }
117
-
118
- postConstruct(): void {
119
- if (GlobalEnv.test) {
120
- this.time = '';
121
- }
122
- this.plain ??= GlobalTerminal.colorLevel === 0;
123
- }
131
+ @EnvVar('TRV_LOG_OUTPUT')
132
+ output: 'console' | 'file' | string = 'console';
124
133
  }
125
134
  ```
126
135
 
127
136
  The following environment variables have control over the default logging config:
128
137
  * `TRV_LOG_FORMAT` - This determines whether or not the output is standard text lines, or is it output as a single line of [JSON](https://www.json.org)
129
- * `TRV_LOG_FILE` - This determines whether or not the logging goes to the console or if it is written to a file
138
+ * `TRV_LOG_OUTPUT` - This determines whether or not the logging goes to the console or if it is written to a file
130
139
  * `TRV_LOG_PLAIN` - Allows for an override of whether or not to log colored output, this defaults to values provided by the [Terminal](https://github.com/travetto/travetto/tree/main/module/terminal#readme "General terminal support") in response to `FORCE_COLOR` and `NO_COLOR`
131
140
  * `TRV_LOG_TIME` - This represents what level of time logging is desired, the default is `ms` which is millisecond output. A value of `s` allows for second level logging, and `false` will disable the output. When ingesting the content into another logging, its generally desirable to suppress the initial time output as most other loggers will append as needed.
package/__index__.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export * from './src/service';
2
2
  export * from './src/formatter/json';
3
3
  export * from './src/formatter/line';
4
+ export * from './src/formatter/google';
4
5
  export * from './src/appender/console';
5
6
  export * from './src/appender/file';
6
7
  export * from './src/types';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@travetto/log",
3
- "version": "3.1.2",
3
+ "version": "3.1.4",
4
4
  "description": "Logging framework that integrates at the console.log level.",
5
5
  "keywords": [
6
6
  "typescript",
@@ -1,10 +1,26 @@
1
- import { Appender, LogEvent } from '../types';
1
+ import { Injectable } from '@travetto/di';
2
+ import { Config } from '@travetto/config';
3
+
4
+ import { LogAppender, LogEvent } from '../types';
5
+
6
+ @Config('log')
7
+ export class ConsoleLogAppenderConfig {
8
+ logToLevel = true;
9
+ }
2
10
 
3
11
  /**
4
- * Console.output
12
+ * Console Logging Appender
5
13
  */
6
- export class ConsoleAppender implements Appender {
14
+ @Injectable()
15
+ export class ConsoleLogAppender implements LogAppender {
16
+
17
+ config: ConsoleLogAppenderConfig;
18
+
19
+ constructor(config: ConsoleLogAppenderConfig) {
20
+ this.config = config;
21
+ }
22
+
7
23
  append(ev: LogEvent, formatted: string): void {
8
- console![ev.level](formatted);
24
+ console![this.config.logToLevel ? ev.level : 'log'](formatted);
9
25
  }
10
26
  }
@@ -1,24 +1,33 @@
1
- import { createWriteStream, WriteStream } from 'fs';
2
- import { Appender, LogEvent } from '../types';
1
+ import { createWriteStream, WriteStream, mkdirSync } from 'fs';
3
2
 
4
- /**
5
- * File appender options
6
- */
7
- export interface FileAppenderOpts {
8
- file: string;
3
+ import { Injectable } from '@travetto/di';
4
+ import { Config, EnvVar } from '@travetto/config';
5
+ import { path, RootIndex } from '@travetto/manifest';
6
+
7
+ import { LogAppender, LogEvent } from '../types';
8
+
9
+ @Config('log')
10
+ export class FileLogAppenderConfig {
11
+ @EnvVar('TRV_LOG_OUTPUT')
12
+ output?: 'file' | string;
13
+
14
+ postConstruct(): void {
15
+ if (!this.output || this.output === 'file' || this.output === 'console') {
16
+ this.output = path.resolve(RootIndex.manifest.toolFolder, 'logs', `${RootIndex.manifest.mainModule}.log`);
17
+ }
18
+ }
9
19
  }
10
20
 
11
21
  /**
12
- * File appender logger
22
+ * File Logging Appender
13
23
  */
14
- export class FileAppender implements Appender {
24
+ @Injectable()
25
+ export class FileLogAppender implements LogAppender {
15
26
  stream: WriteStream;
16
27
 
17
- constructor(opts: FileAppenderOpts) {
18
- this.stream = createWriteStream(opts.file, {
19
- autoClose: true,
20
- flags: 'a'
21
- });
28
+ constructor(opts: FileLogAppenderConfig) {
29
+ mkdirSync(path.dirname(opts.output!), { recursive: true });
30
+ this.stream = createWriteStream(opts.output!, { autoClose: true, flags: 'a' });
22
31
  }
23
32
 
24
33
  append(ev: LogEvent, formatted: string): void {
package/src/common.ts CHANGED
@@ -1,69 +1,39 @@
1
- import { GlobalEnv } from '@travetto/base';
2
1
  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';
2
+ import { DependencyRegistry, Inject, Injectable } from '@travetto/di';
6
3
 
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';
4
+ import { ConsoleLogAppender } from './appender/console';
5
+ import { FileLogAppender } from './appender/file';
6
+ import { JsonLogFormatter } from './formatter/json';
7
+ import { LineLogFormatter } from './formatter/line';
8
+ import { LogAppender, LogFormatter, LogEvent, LogCommonⲐ, Logger } from './types';
12
9
 
13
10
  @Config('log')
14
11
  export class CommonLoggerConfig {
15
- @EnvVar('TRV_LOG_COMMON')
16
- commonActive?: boolean;
17
-
18
- /** Should we enrich the console by default */
19
12
  @EnvVar('TRV_LOG_FORMAT')
20
13
  format: 'line' | 'json' = 'line';
21
14
 
22
- /** Log file, if needed */
23
- @EnvVar('TRV_LOG_FILE')
24
- file?: string;
25
-
26
- @EnvVar('TRV_LOG_PLAIN')
27
- plain?: boolean;
28
-
29
- @EnvVar('TRV_LOG_TIME')
30
- time: 's' | 'ms' | string = 'ms';
31
-
32
- @Ignore()
33
- get timestamp(): 's' | 'ms' | false {
34
- return (this.time ?? 'ms') === 'ms' ? 'ms' : (this.time === 's' ? 's' : false);
35
- }
36
-
37
- postConstruct(): void {
38
- if (GlobalEnv.test) {
39
- this.time = '';
40
- }
41
- this.plain ??= GlobalTerminal.colorLevel === 0;
42
- }
15
+ @EnvVar('TRV_LOG_OUTPUT')
16
+ output: 'console' | 'file' | string = 'console';
43
17
  }
44
18
 
45
19
  @Injectable()
46
- export class CommonLogger {
47
- #appender: Appender;
48
- #formatter: Formatter;
20
+ export class CommonLogger implements Logger {
49
21
 
50
22
  @Inject()
51
23
  config: CommonLoggerConfig;
52
24
 
53
- get active(): boolean {
54
- return this.config.commonActive !== false;
55
- }
25
+ @Inject(LogCommonⲐ, { optional: true })
26
+ formatter: LogFormatter;
27
+
28
+ @Inject(LogCommonⲐ, { optional: true })
29
+ appender: LogAppender;
56
30
 
57
- postConstruct(): void {
58
- this.#formatter = this.config.format === 'line' ?
59
- new LineFormatter(this.config) :
60
- new JsonFormatter();
61
- this.#appender = this.config.file ?
62
- new FileAppender({ file: this.config.file }) :
63
- new ConsoleAppender();
31
+ async postConstruct(): Promise<void> {
32
+ this.formatter ??= await DependencyRegistry.getInstance(this.config.format === 'line' ? LineLogFormatter : JsonLogFormatter);
33
+ this.appender ??= await DependencyRegistry.getInstance(this.config.output !== 'console' ? FileLogAppender : ConsoleLogAppender);
64
34
  }
65
35
 
66
36
  onLog(ev: LogEvent): void {
67
- this.#appender.append(ev, this.#formatter.format(ev));
37
+ this.appender.append(ev, this.formatter.format(ev));
68
38
  }
69
39
  }
@@ -0,0 +1,34 @@
1
+ import util from 'util';
2
+
3
+ import { Injectable } from '@travetto/di';
4
+
5
+ import { LogFormatter, LogEvent } from '../types';
6
+
7
+ /**
8
+ * Google Logging Formatter
9
+ *
10
+ * Provides a standard google logging provider that adapts the content for google's logging structure
11
+ */
12
+ @Injectable()
13
+ export class GoogleLogFormatter implements LogFormatter {
14
+ format({
15
+ source: file, line, scope, level, message, timestamp, module, args,
16
+ context: { method, path, statusCode, ...context } = {},
17
+ }: LogEvent): string {
18
+ return JSON.stringify({
19
+ context,
20
+ 'logging.googleapis.com/sourceLocation': { file, line },
21
+ 'logging.googleapis.com/labels': { module, scope },
22
+ severity: level,
23
+ message: util.format(message, ...args, ...Object.entries(context).map(([k, v]) => util.format('%s=%s', k, v))),
24
+ timestamp,
25
+ ...(method ? {
26
+ httpRequest: {
27
+ requestMethod: method,
28
+ requestUrl: path,
29
+ status: statusCode
30
+ }
31
+ } : {})
32
+ });
33
+ }
34
+ }
@@ -1,23 +1,26 @@
1
- import { LogEvent, Formatter } from '../types';
1
+ import { Injectable } from '@travetto/di';
2
+ import { Config, EnvVar } from '@travetto/config';
2
3
 
3
- /**
4
- * JSON Options
5
- */
6
- export interface JSONFormatterOpts {
7
- depth?: number;
4
+ import { LogEvent, LogFormatter } from '../types';
5
+
6
+ @Config('log')
7
+ export class JSONLogFormatterConfig {
8
+ @EnvVar('TRV_LOG_JSON_INDENT')
9
+ jsonIndent?: number;
8
10
  }
9
11
 
10
12
  /**
11
- * JSON Formatter
13
+ * JSON Logging Formatter
12
14
  */
13
- export class JsonFormatter implements Formatter {
14
- #opts: JSONFormatterOpts;
15
+ @Injectable()
16
+ export class JsonLogFormatter implements LogFormatter {
17
+ opts: JSONLogFormatterConfig;
15
18
 
16
- constructor(opts: JSONFormatterOpts = {}) {
17
- this.#opts = opts;
19
+ constructor(opts: JSONLogFormatterConfig) {
20
+ this.opts = opts;
18
21
  }
19
22
 
20
23
  format(ev: LogEvent): string {
21
- return JSON.stringify(ev, null, this.#opts.depth);
24
+ return JSON.stringify(ev, null, this.opts.jsonIndent);
22
25
  }
23
26
  }
@@ -1,8 +1,12 @@
1
1
  import util from 'util';
2
2
 
3
3
  import { GlobalTerminal } from '@travetto/terminal';
4
+ import { Injectable } from '@travetto/di';
5
+ import { Config, EnvVar } from '@travetto/config';
6
+ import { GlobalEnv } from '@travetto/base';
7
+ import { Ignore } from '@travetto/schema';
4
8
 
5
- import { LogEvent, Formatter } from '../types';
9
+ import { LogEvent, LogFormatter } from '../types';
6
10
 
7
11
  /**
8
12
  * Level coloring
@@ -28,27 +32,56 @@ export interface LineFormatterOpts {
28
32
  location?: boolean;
29
33
  }
30
34
 
35
+ @Config('log')
36
+ export class LineLogFormatterConfig {
37
+ @EnvVar('TRV_LOG_PLAIN')
38
+ plain?: boolean;
39
+
40
+ @EnvVar('TRV_LOG_TIME')
41
+ time?: 's' | 'ms' | string;
42
+
43
+ colorize?: boolean;
44
+ align?: boolean;
45
+ level?: boolean;
46
+ location?: boolean;
47
+
48
+ @Ignore()
49
+ timestamp?: 's' | 'ms';
50
+
51
+ postConstruct(): void {
52
+ if (GlobalEnv.test) {
53
+ this.plain = true;
54
+ this.time = undefined;
55
+ }
56
+ this.time ??= (!this.plain ? 'ms' : undefined);
57
+ this.plain ??= GlobalTerminal.colorLevel === 0;
58
+ this.colorize ??= !this.plain;
59
+ this.location ??= !this.plain;
60
+ this.level ??= !this.plain;
61
+ this.align ??= !this.plain;
62
+ if (this.time !== undefined && this.time === 'ms' || this.time === 's') {
63
+ this.timestamp = this.time;
64
+ }
65
+ }
66
+ }
67
+
31
68
  /**
32
- * Line formatter
69
+ * Line Logging Formatter
33
70
  */
34
- export class LineFormatter implements Formatter {
35
- #opts: LineFormatterOpts;
36
-
37
- constructor(opts: LineFormatterOpts = {}) {
38
- const notPlain = opts.plain !== true;
39
- this.#opts = {
40
- colorize: notPlain,
41
- timestamp: notPlain ? opts.timestamp : undefined,
42
- align: true, level: notPlain, location: notPlain,
43
- ...opts
44
- };
71
+ @Injectable()
72
+ export class LineLogFormatter implements LogFormatter {
73
+
74
+ opts: LineLogFormatterConfig;
75
+
76
+ constructor(opts: LineLogFormatterConfig) {
77
+ this.opts = opts;
45
78
  }
46
79
 
47
80
  pretty(ev: LogEvent, o: unknown): string {
48
81
  return util.inspect(o, {
49
82
  showHidden: ev.level === 'debug',
50
83
  depth: 4,
51
- colors: this.#opts.colorize !== false,
84
+ colors: this.opts.colorize !== false,
52
85
  breakLength: 100
53
86
  });
54
87
  }
@@ -57,35 +90,34 @@ export class LineFormatter implements Formatter {
57
90
  * Format an event into a single line
58
91
  */
59
92
  format(ev: LogEvent): string {
60
- const opts = this.#opts;
61
93
  const out = [];
62
94
 
63
- if (opts.timestamp) {
95
+ if (this.opts.timestamp) {
64
96
  let timestamp = ev.timestamp.toISOString();
65
- if (opts.timestamp === 's') {
97
+ if (this.opts.timestamp === 's') {
66
98
  timestamp = timestamp.replace(/[.]\d{3}/, '');
67
99
  }
68
- if (opts.colorize) {
100
+ if (this.opts.colorize) {
69
101
  timestamp = STYLES.timestamp(timestamp);
70
102
  }
71
103
  out.push(timestamp);
72
104
  }
73
105
 
74
- if (opts.level) {
106
+ if (this.opts.level) {
75
107
  let level: string = ev.level;
76
- if (opts.align) {
108
+ if (this.opts.align) {
77
109
  level = level.padEnd(5, ' ');
78
110
  }
79
- if (opts.colorize) {
111
+ if (this.opts.colorize) {
80
112
  level = STYLES[ev.level](level);
81
113
  }
82
114
  out.push(level);
83
115
  }
84
116
 
85
- if (ev.source && opts.location) {
117
+ if (ev.source && this.opts.location) {
86
118
  const ns = `${ev.module}:${ev.modulePath}`;
87
119
  let loc = ev.line ? `${ns}:${ev.line}` : ns;
88
- if (opts.colorize) {
120
+ if (this.opts.colorize) {
89
121
  loc = STYLES.location(loc);
90
122
  }
91
123
  out.push(`[${loc}]`);
@@ -1 +1,3 @@
1
+ export class LogAppenderTarget { }
2
+ export class LogFormatterTarget { }
1
3
  export class LoggerTarget { }
package/src/service.ts CHANGED
@@ -20,17 +20,17 @@ export class LogService implements ConsoleListener, AutoCreate {
20
20
  async postConstruct(): Promise<void> {
21
21
  await GlobalTerminal.init();
22
22
 
23
- const def = await DependencyRegistry.getInstance(CommonLogger);
24
- if (def.active) {
25
- this.#listeners.push(def);
26
- }
23
+ const loggers = DependencyRegistry.getCandidateTypes(LoggerTarget).filter(c => c.class !== CommonLogger);
27
24
 
28
- const loggers = DependencyRegistry.getCandidateTypes(LoggerTarget);
29
- const instances = await Promise.all(loggers.map(l => DependencyRegistry.getInstance<Logger>(l.class, l.qualifier)));
30
- for (const inst of instances) {
31
- this.#listeners.push(inst);
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));
32
33
  }
33
-
34
34
  // Take over
35
35
  ConsoleManager.set(this, true);
36
36
  }
package/src/types.ts CHANGED
@@ -1,4 +1,7 @@
1
1
  import { ConsoleEvent } from '@travetto/base';
2
+
3
+ export const LogCommonⲐ = Symbol.for('@travetto/log:common');
4
+
2
5
  /**
3
6
  * Logging event
4
7
  */
@@ -15,15 +18,17 @@ export interface LogEvent extends ConsoleEvent {
15
18
 
16
19
  /**
17
20
  * Output appender for the logger
21
+ * @concrete ./internal/types:LogAppenderTarget
18
22
  */
19
- export interface Appender {
23
+ export interface LogAppender {
20
24
  append(ev: LogEvent, formatted: string): void;
21
25
  }
22
26
 
23
27
  /**
24
28
  * Output formatter
29
+ * @concrete ./internal/types:LogFormatterTarget
25
30
  */
26
- export interface Formatter {
31
+ export interface LogFormatter {
27
32
  format(e: LogEvent): string;
28
33
  }
29
34