@holz/core 0.2.0 → 0.6.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 +115 -0
- package/dist/holz-core.cjs +1 -1
- package/dist/holz-core.js +39 -58
- package/package.json +3 -3
- package/src/__tests__/logger.test.ts +21 -9
- package/src/__tests__/operators.test.ts +48 -0
- package/src/index.ts +1 -2
- package/src/logger.ts +20 -16
- package/src/operators.ts +28 -0
- package/src/types.ts +1 -1
- package/src/backends/test.ts +0 -5
- package/src/operators/__tests__/combine.test.ts +0 -29
- package/src/operators/__tests__/filter.test.ts +0 -21
- package/src/operators/combine.ts +0 -22
- package/src/operators/filter.ts +0 -28
package/README.md
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# `@holz/core`
|
|
2
|
+
|
|
3
|
+
`@holz/core` is a flexible and extensible logging library for TypeScript applications. It provides a simple but powerful interface for logging progress updates and errors with varying levels of severity.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
Here is an example of how to create a logger and use it to log a message:
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
import { createLogger } from '@holz/core';
|
|
11
|
+
|
|
12
|
+
const logger = createLogger((log) => {
|
|
13
|
+
console.log(`[${log.level}]`, log.message);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
logger.info('Hello, world!');
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
This will log a message with the info level to the console.
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
[info] Hello, world!
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Logger Interface
|
|
26
|
+
|
|
27
|
+
The `Logger` class is the core of the library. It provides a set of methods for logging messages with different levels of severity. Each method takes a message and an optional context object.
|
|
28
|
+
|
|
29
|
+
You can also use the `namespace` method to create a new logger with a specific owner, such as a class or module. This makes it easier to identify the origin of log messages.
|
|
30
|
+
|
|
31
|
+
## Log Levels
|
|
32
|
+
|
|
33
|
+
The library provides four log levels:
|
|
34
|
+
|
|
35
|
+
- `LogLevel.Debug`: Extremely verbose progress updates (usually hidden).
|
|
36
|
+
- `LogLevel.Info`: High-level progress updates.
|
|
37
|
+
- `LogLevel.Warn`: Something is concerning, but we can keep going.
|
|
38
|
+
- `LogLevel.Error`: Something critical failed and we can't continue.
|
|
39
|
+
|
|
40
|
+
You can use these levels to indicate the severity of a log message. The severity levels can be configured to perform actions based on the level of severity of the log messages.
|
|
41
|
+
|
|
42
|
+
## Log Structure
|
|
43
|
+
|
|
44
|
+
Every log message processed by Holz follows a specific structure defined by the `Log` interface. Understanding this structure is crucial for effective logging and log processing.
|
|
45
|
+
|
|
46
|
+
A `Log` object contains the following properties:
|
|
47
|
+
|
|
48
|
+
- `message`: The verbatim log message. This property should not contain any interpolated data.
|
|
49
|
+
- `level`: The severity of the log message, expressed as a member of the `LogLevel` enum. The available log levels are, in increasing order of severity: `Debug`, `Info`, `Warn`, `Error`.
|
|
50
|
+
- `origin`: The source of the log message. This property is an array of strings that typically identifies the library, module, or component that generated the log message, followed by more specific information. This property can be used to filter and group log messages based on their origin.
|
|
51
|
+
- `context`: A dictionary of key-value pairs that provides additional context for the log message. The values in this object must be JSON serializable. Because it's easy to accidentally include unsuitable log context, such as PII or deeply nested objects, the use of nested objects in the context is discouraged.
|
|
52
|
+
|
|
53
|
+
Here is an example of a log message expressed as a `Log` object:
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
{
|
|
57
|
+
message: 'Connection to database established.',
|
|
58
|
+
level: LogLevel.Info,
|
|
59
|
+
origin: ['my-app', 'database-connection'],
|
|
60
|
+
context: {
|
|
61
|
+
host: 'localhost',
|
|
62
|
+
port: 5432,
|
|
63
|
+
user: 'postgres',
|
|
64
|
+
database: 'my_database',
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
In this example, the log message describes a successful connection to a database, with additional context about the database host, port, user, and name.
|
|
70
|
+
|
|
71
|
+
## Log Processors
|
|
72
|
+
|
|
73
|
+
In Holz, a `LogProcessor` function is responsible for processing log messages. The `createLogger` function takes this function as an argument. A `LogProcessor` is a plain function that takes a `Log` object, which contains the message, level, origin, and context of the log.
|
|
74
|
+
|
|
75
|
+
Holz provides a set of off-the-shelf plugins for logging to various destinations, filtering, formatting, and more. You can also write your own plugins to suit your specific needs. These plugins can be combined into a `LogProcessor` function using the utility function `combine()`, allowing you to send a single log to multiple destinations.
|
|
76
|
+
|
|
77
|
+
Holz's plugin system is designed to be flexible and extensible. If none of the provided plugins meet your needs, you can easily create your own plugin and still build on plugins from the rest of the ecosystem. Check out the [`@holz/logger`](https://github.com/PsychoLlama/holz/tree/main/packages/holz-logger) package for a bundled example of Holz's logging plugins.
|
|
78
|
+
|
|
79
|
+
## Utilities
|
|
80
|
+
|
|
81
|
+
In addition to the core `Logger` class, `@holz/core` provides some utility processors that you can use to extend the functionality of your logging solution. These processors are optional and can be combined with each other or with third-party processors as needed.
|
|
82
|
+
|
|
83
|
+
### `combine`
|
|
84
|
+
|
|
85
|
+
The `combine` processor takes an array of log processors and returns a single log processor that forwards each log to all of the input processors. This is useful when you want to send a single log to multiple log destinations.
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
import { combine } from '@holz/core';
|
|
89
|
+
import {
|
|
90
|
+
consoleBackend,
|
|
91
|
+
fileBackend,
|
|
92
|
+
logUploadService,
|
|
93
|
+
} from './custom-processors';
|
|
94
|
+
|
|
95
|
+
const logger = createLogger(
|
|
96
|
+
combine([consoleBackend, fileBackend, logUploadService])
|
|
97
|
+
);
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### `filter`
|
|
101
|
+
|
|
102
|
+
The `filter` processor takes a filter function and another log processor as input. If the filter function returns `true` for a log, it is forwarded to the second processor. If the filter function returns `false`, the log is discarded. This is useful when you want to selectively forward logs based on some criteria.
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
import { filter } from '@holz/core';
|
|
106
|
+
|
|
107
|
+
const debugLogFilter = filter(
|
|
108
|
+
(log) => log.level !== LogLevel.Debug,
|
|
109
|
+
consoleProcessor
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
const logger = createLogger(debugLogFilter);
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Note that you can chain multiple filter processors together to create more complex filters.
|
package/dist/holz-core.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});var s=(e=>(e.Error="error",e.Warn="warn",e.Info="info",e.Debug="debug",e))(s||{});
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});var s=(e=>(e.Error="error",e.Warn="warn",e.Info="info",e.Debug="debug",e))(s||{});function c(e){return r=>e.forEach(o=>o(r))}function f(e,r){return o=>{e(o)&&r(o)}}class a{constructor(r,o){this.processor=r,this.owner=o,this.debug=(n,i)=>{this.forwardLog(s.Debug,n,i)},this.info=(n,i)=>{this.forwardLog(s.Info,n,i)},this.warn=(n,i)=>{this.forwardLog(s.Warn,n,i)},this.error=(n,i)=>{this.forwardLog(s.Error,n,i)};const t={configurable:!1,enumerable:!1};Object.defineProperties(this,{processor:t,debug:t,info:t,warn:t,error:t})}static create(r){return new a(r,[])}namespace(r){return new a(this.processor,this.owner.concat(r))}forwardLog(r,o,t={}){this.processor({message:o,level:r,origin:this.owner,context:t})}}const u=a.create;exports.LogLevel=s;exports.combine=c;exports.createLogger=u;exports.filter=f;
|
package/dist/holz-core.js
CHANGED
|
@@ -1,9 +1,30 @@
|
|
|
1
|
-
var
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
var i = /* @__PURE__ */ ((o) => (o.Error = "error", o.Warn = "warn", o.Info = "info", o.Debug = "debug", o))(i || {});
|
|
2
|
+
function c(o) {
|
|
3
|
+
return (r) => o.forEach((e) => e(r));
|
|
4
|
+
}
|
|
5
|
+
function f(o, r) {
|
|
6
|
+
return (e) => {
|
|
7
|
+
o(e) && r(e);
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
class a {
|
|
11
|
+
constructor(r, e) {
|
|
12
|
+
this.processor = r, this.owner = e, this.debug = (n, s) => {
|
|
13
|
+
this.forwardLog(i.Debug, n, s);
|
|
14
|
+
}, this.info = (n, s) => {
|
|
15
|
+
this.forwardLog(i.Info, n, s);
|
|
16
|
+
}, this.warn = (n, s) => {
|
|
17
|
+
this.forwardLog(i.Warn, n, s);
|
|
18
|
+
}, this.error = (n, s) => {
|
|
19
|
+
this.forwardLog(i.Error, n, s);
|
|
20
|
+
};
|
|
21
|
+
const t = { configurable: !1, enumerable: !1 };
|
|
22
|
+
Object.defineProperties(this, {
|
|
23
|
+
processor: t,
|
|
24
|
+
debug: t,
|
|
25
|
+
info: t,
|
|
26
|
+
warn: t,
|
|
27
|
+
error: t
|
|
7
28
|
});
|
|
8
29
|
}
|
|
9
30
|
/**
|
|
@@ -13,65 +34,25 @@ class t {
|
|
|
13
34
|
* If you wish to use more than one processor, `combine(...)` them first.
|
|
14
35
|
*/
|
|
15
36
|
static create(r) {
|
|
16
|
-
return new
|
|
37
|
+
return new a(r, []);
|
|
17
38
|
}
|
|
18
39
|
/** Extend the logger to attach a class or module name to logs. */
|
|
19
40
|
namespace(r) {
|
|
20
|
-
return new
|
|
21
|
-
}
|
|
22
|
-
/** Log a frequent and verbose progress update. */
|
|
23
|
-
debug(r, o) {
|
|
24
|
-
this.forwardLog(e.Debug, r, o);
|
|
25
|
-
}
|
|
26
|
-
/** Log a high-level progress update. */
|
|
27
|
-
info(r, o) {
|
|
28
|
-
this.forwardLog(e.Info, r, o);
|
|
41
|
+
return new a(this.processor, this.owner.concat(r));
|
|
29
42
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
/** Log a critical failure. */
|
|
35
|
-
error(r, o) {
|
|
36
|
-
this.forwardLog(e.Error, r, o);
|
|
37
|
-
}
|
|
38
|
-
forwardLog(r, o, c = {}) {
|
|
39
|
-
this.processor.processLog({
|
|
40
|
-
message: o,
|
|
43
|
+
forwardLog(r, e, t = {}) {
|
|
44
|
+
this.processor({
|
|
45
|
+
message: e,
|
|
41
46
|
level: r,
|
|
42
|
-
origin: this.
|
|
43
|
-
context:
|
|
47
|
+
origin: this.owner,
|
|
48
|
+
context: t
|
|
44
49
|
});
|
|
45
50
|
}
|
|
46
51
|
}
|
|
47
|
-
const
|
|
48
|
-
class i {
|
|
49
|
-
constructor(r) {
|
|
50
|
-
this.processors = r;
|
|
51
|
-
}
|
|
52
|
-
processLog(r) {
|
|
53
|
-
this.processors.forEach((o) => {
|
|
54
|
-
o.processLog(r);
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
function f(s) {
|
|
59
|
-
return new i(s);
|
|
60
|
-
}
|
|
61
|
-
class n {
|
|
62
|
-
constructor(r, o) {
|
|
63
|
-
this.predicate = r, this.processor = o;
|
|
64
|
-
}
|
|
65
|
-
processLog(r) {
|
|
66
|
-
this.predicate(r) && this.processor.processLog(r);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
function h(s, r) {
|
|
70
|
-
return new n(s, r);
|
|
71
|
-
}
|
|
52
|
+
const h = a.create;
|
|
72
53
|
export {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
54
|
+
i as LogLevel,
|
|
55
|
+
c as combine,
|
|
56
|
+
h as createLogger,
|
|
57
|
+
f as filter
|
|
77
58
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@holz/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "A structured and composable logger",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/holz-core.cjs",
|
|
@@ -38,8 +38,8 @@
|
|
|
38
38
|
"test:types": "tsc"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
|
-
"@vitest/coverage-c8": "0.28.5",
|
|
42
|
-
"typescript": "4.9.5",
|
|
41
|
+
"@vitest/coverage-c8": "^0.28.5",
|
|
42
|
+
"typescript": "^4.9.5",
|
|
43
43
|
"vite": "^4.0.0",
|
|
44
44
|
"vitest": "^0.28.5"
|
|
45
45
|
}
|
|
@@ -1,16 +1,15 @@
|
|
|
1
|
-
import TestBackend from '../backends/test';
|
|
2
1
|
import { createLogger } from '../logger';
|
|
3
2
|
import { LogLevel } from '../types';
|
|
4
3
|
|
|
5
4
|
describe('Logger', () => {
|
|
6
5
|
it('sends structured logs to the log processor', () => {
|
|
7
|
-
const backend =
|
|
6
|
+
const backend = vi.fn();
|
|
8
7
|
const logger = createLogger(backend);
|
|
9
8
|
|
|
10
9
|
logger.info('Hello world', { audience: 'testers' });
|
|
11
10
|
|
|
12
|
-
expect(backend
|
|
13
|
-
expect(backend
|
|
11
|
+
expect(backend).toHaveBeenCalledOnce();
|
|
12
|
+
expect(backend).toHaveBeenCalledWith({
|
|
14
13
|
message: 'Hello world',
|
|
15
14
|
level: 'info',
|
|
16
15
|
origin: [],
|
|
@@ -24,11 +23,11 @@ describe('Logger', () => {
|
|
|
24
23
|
[LogLevel.Warn, 'There are irregularities in the pension fund', {}],
|
|
25
24
|
[LogLevel.Error, 'Leadership has taken a dive', { windows: 'open' }],
|
|
26
25
|
])('correctly processes %s log messages', (level, message, context) => {
|
|
27
|
-
const backend =
|
|
26
|
+
const backend = vi.fn();
|
|
28
27
|
const logger = createLogger(backend);
|
|
29
28
|
|
|
30
29
|
logger[level](message, context);
|
|
31
|
-
expect(backend
|
|
30
|
+
expect(backend).toHaveBeenCalledWith({
|
|
32
31
|
message,
|
|
33
32
|
level,
|
|
34
33
|
context,
|
|
@@ -36,17 +35,30 @@ describe('Logger', () => {
|
|
|
36
35
|
});
|
|
37
36
|
});
|
|
38
37
|
|
|
38
|
+
it('binds the log methods to the logger', () => {
|
|
39
|
+
const backend = vi.fn();
|
|
40
|
+
const logger = createLogger(backend);
|
|
41
|
+
|
|
42
|
+
const { debug, info, warn, error } = logger;
|
|
43
|
+
debug('Debugging message');
|
|
44
|
+
info('Informative message');
|
|
45
|
+
warn('Warning message');
|
|
46
|
+
error('Error message');
|
|
47
|
+
|
|
48
|
+
expect(backend).toHaveBeenCalledTimes(4);
|
|
49
|
+
});
|
|
50
|
+
|
|
39
51
|
describe('namespace', () => {
|
|
40
52
|
it('extends the origin of the logger', () => {
|
|
41
|
-
const backend =
|
|
53
|
+
const backend = vi.fn();
|
|
42
54
|
const logger = createLogger(backend)
|
|
43
55
|
.namespace('signaling')
|
|
44
56
|
.namespace('socket');
|
|
45
57
|
|
|
46
58
|
logger.info('opening socket');
|
|
47
59
|
|
|
48
|
-
expect(backend
|
|
49
|
-
expect(backend
|
|
60
|
+
expect(backend).toHaveBeenCalledOnce();
|
|
61
|
+
expect(backend).toHaveBeenCalledWith({
|
|
50
62
|
message: 'opening socket',
|
|
51
63
|
level: 'info',
|
|
52
64
|
origin: ['signaling', 'socket'],
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { createLogger } from '../logger';
|
|
2
|
+
import { combine, filter } from '../operators';
|
|
3
|
+
import { LogLevel } from '../types';
|
|
4
|
+
|
|
5
|
+
describe('operators', () => {
|
|
6
|
+
describe('combine', () => {
|
|
7
|
+
it('combines several log processors backends into one', () => {
|
|
8
|
+
const b1 = vi.fn();
|
|
9
|
+
const b2 = vi.fn();
|
|
10
|
+
const backend = combine([b1, b2]);
|
|
11
|
+
const logger = createLogger(backend);
|
|
12
|
+
|
|
13
|
+
logger.info('tee message');
|
|
14
|
+
|
|
15
|
+
expect(b1).toHaveBeenCalledWith(
|
|
16
|
+
expect.objectContaining({ message: 'tee message' })
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
expect(b2).toHaveBeenCalledWith(
|
|
20
|
+
expect.objectContaining({ message: 'tee message' })
|
|
21
|
+
);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('works even if you provide no logging backend', () => {
|
|
25
|
+
const backend = combine([]);
|
|
26
|
+
const logger = createLogger(backend);
|
|
27
|
+
|
|
28
|
+
expect(() => logger.info('no backend')).not.toThrow();
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
describe('filter', () => {
|
|
33
|
+
it('filters out logs that do not match the predicate', () => {
|
|
34
|
+
const backend = vi.fn();
|
|
35
|
+
const logger = createLogger(
|
|
36
|
+
filter((log) => log.level !== LogLevel.Debug, backend)
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
logger.info('keep me');
|
|
40
|
+
logger.debug('discard');
|
|
41
|
+
|
|
42
|
+
expect(backend).toHaveBeenCalledOnce();
|
|
43
|
+
expect(backend).toHaveBeenCalledWith(
|
|
44
|
+
expect.objectContaining({ message: 'keep me' })
|
|
45
|
+
);
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
});
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
export type { LogProcessor, LogContext, Log } from './types';
|
|
2
2
|
export { LogLevel } from './types';
|
|
3
|
+
export { combine, filter } from './operators';
|
|
3
4
|
export { createLogger } from './logger';
|
|
4
|
-
export { default as combine } from './operators/combine';
|
|
5
|
-
export { default as filter } from './operators/filter';
|
package/src/logger.ts
CHANGED
|
@@ -14,49 +14,53 @@ class Logger {
|
|
|
14
14
|
|
|
15
15
|
private constructor(
|
|
16
16
|
private processor: LogProcessor,
|
|
17
|
-
readonly
|
|
17
|
+
readonly owner: ReadonlyArray<string>
|
|
18
18
|
) {
|
|
19
|
-
// Non-enumerable to keep the
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
// Non-enumerable to keep the repl clean.
|
|
20
|
+
const hidden = { configurable: false, enumerable: false };
|
|
21
|
+
Object.defineProperties(this, {
|
|
22
|
+
processor: hidden,
|
|
23
|
+
debug: hidden,
|
|
24
|
+
info: hidden,
|
|
25
|
+
warn: hidden,
|
|
26
|
+
error: hidden,
|
|
23
27
|
});
|
|
24
28
|
}
|
|
25
29
|
|
|
26
30
|
/** Extend the logger to attach a class or module name to logs. */
|
|
27
31
|
namespace(owner: string): Logger {
|
|
28
|
-
return new Logger(this.processor, this.
|
|
32
|
+
return new Logger(this.processor, this.owner.concat(owner));
|
|
29
33
|
}
|
|
30
34
|
|
|
31
35
|
/** Log a frequent and verbose progress update. */
|
|
32
|
-
debug(message: string, context?: LogContext) {
|
|
36
|
+
debug = (message: string, context?: LogContext) => {
|
|
33
37
|
this.forwardLog(LogLevel.Debug, message, context);
|
|
34
|
-
}
|
|
38
|
+
};
|
|
35
39
|
|
|
36
40
|
/** Log a high-level progress update. */
|
|
37
|
-
info(message: string, context?: LogContext) {
|
|
41
|
+
info = (message: string, context?: LogContext) => {
|
|
38
42
|
this.forwardLog(LogLevel.Info, message, context);
|
|
39
|
-
}
|
|
43
|
+
};
|
|
40
44
|
|
|
41
45
|
/** Log something concerning. */
|
|
42
|
-
warn(message: string, context?: LogContext) {
|
|
46
|
+
warn = (message: string, context?: LogContext) => {
|
|
43
47
|
this.forwardLog(LogLevel.Warn, message, context);
|
|
44
|
-
}
|
|
48
|
+
};
|
|
45
49
|
|
|
46
50
|
/** Log a critical failure. */
|
|
47
|
-
error(message: string, context?: LogContext) {
|
|
51
|
+
error = (message: string, context?: LogContext) => {
|
|
48
52
|
this.forwardLog(LogLevel.Error, message, context);
|
|
49
|
-
}
|
|
53
|
+
};
|
|
50
54
|
|
|
51
55
|
private forwardLog(
|
|
52
56
|
level: LogLevel,
|
|
53
57
|
message: string,
|
|
54
58
|
context: LogContext = {}
|
|
55
59
|
) {
|
|
56
|
-
this.processor
|
|
60
|
+
this.processor({
|
|
57
61
|
message,
|
|
58
62
|
level,
|
|
59
|
-
origin: this.
|
|
63
|
+
origin: this.owner,
|
|
60
64
|
context,
|
|
61
65
|
});
|
|
62
66
|
}
|
package/src/operators.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Log, LogProcessor } from './types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Combine several log processors into a single log processor. Each one is
|
|
5
|
+
* called in sequence. Useful for sending a single log to multiple log
|
|
6
|
+
* destinations.
|
|
7
|
+
*/
|
|
8
|
+
export function combine(processors: Array<LogProcessor>): LogProcessor {
|
|
9
|
+
return (log: Log) => processors.forEach((processor) => processor(log));
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Filter logs based on a filter function. If the function returns true, the
|
|
14
|
+
* log is kept and forwarded onto the next processor, otherwise it is
|
|
15
|
+
* discarded.
|
|
16
|
+
*/
|
|
17
|
+
export function filter(
|
|
18
|
+
/** Returns `true` to forward the log. */
|
|
19
|
+
predicate: (log: Log) => boolean,
|
|
20
|
+
/** Where to send the log if it passes the filter. */
|
|
21
|
+
processor: LogProcessor
|
|
22
|
+
): LogProcessor {
|
|
23
|
+
return (log: Log) => {
|
|
24
|
+
if (predicate(log)) {
|
|
25
|
+
processor(log);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
}
|
package/src/types.ts
CHANGED
package/src/backends/test.ts
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import TestBackend from '../../backends/test';
|
|
2
|
-
import { createLogger } from '../../logger';
|
|
3
|
-
import combine from '../combine';
|
|
4
|
-
|
|
5
|
-
describe('combine operator', () => {
|
|
6
|
-
it('combines several log processors backends into one', () => {
|
|
7
|
-
const b1 = new TestBackend();
|
|
8
|
-
const b2 = new TestBackend();
|
|
9
|
-
const backend = combine([b1, b2]);
|
|
10
|
-
const logger = createLogger(backend);
|
|
11
|
-
|
|
12
|
-
logger.info('tee message');
|
|
13
|
-
|
|
14
|
-
expect(b1.processLog).toHaveBeenCalledWith(
|
|
15
|
-
expect.objectContaining({ message: 'tee message' })
|
|
16
|
-
);
|
|
17
|
-
|
|
18
|
-
expect(b2.processLog).toHaveBeenCalledWith(
|
|
19
|
-
expect.objectContaining({ message: 'tee message' })
|
|
20
|
-
);
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it('works even if you provide no logging backend', () => {
|
|
24
|
-
const backend = combine([]);
|
|
25
|
-
const logger = createLogger(backend);
|
|
26
|
-
|
|
27
|
-
expect(() => logger.info('no backend')).not.toThrow();
|
|
28
|
-
});
|
|
29
|
-
});
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import TestBackend from '../../backends/test';
|
|
2
|
-
import { createLogger } from '../../logger';
|
|
3
|
-
import { LogLevel } from '../../types';
|
|
4
|
-
import filter from '../filter';
|
|
5
|
-
|
|
6
|
-
describe('filter operator', () => {
|
|
7
|
-
it('filters out logs that do not match the predicate', () => {
|
|
8
|
-
const backend = new TestBackend();
|
|
9
|
-
const logger = createLogger(
|
|
10
|
-
filter((log) => log.level !== LogLevel.Debug, backend)
|
|
11
|
-
);
|
|
12
|
-
|
|
13
|
-
logger.info('keep me');
|
|
14
|
-
logger.debug('discard');
|
|
15
|
-
|
|
16
|
-
expect(backend.processLog).toHaveBeenCalledOnce();
|
|
17
|
-
expect(backend.processLog).toHaveBeenCalledWith(
|
|
18
|
-
expect.objectContaining({ message: 'keep me' })
|
|
19
|
-
);
|
|
20
|
-
});
|
|
21
|
-
});
|
package/src/operators/combine.ts
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import type { Log, LogProcessor } from '../types';
|
|
2
|
-
|
|
3
|
-
class CombinedLogProcessor implements LogProcessor {
|
|
4
|
-
constructor(private processors: Array<LogProcessor>) {
|
|
5
|
-
// empty
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
processLog(log: Log) {
|
|
9
|
-
this.processors.forEach((processor) => {
|
|
10
|
-
processor.processLog(log);
|
|
11
|
-
});
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Combine several log processors into a single log processor. Each one is
|
|
17
|
-
* called in sequence. Useful for sending a single log to multiple log
|
|
18
|
-
* destinations.
|
|
19
|
-
*/
|
|
20
|
-
export default function combine(processors: Array<LogProcessor>): LogProcessor {
|
|
21
|
-
return new CombinedLogProcessor(processors);
|
|
22
|
-
}
|
package/src/operators/filter.ts
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import type { Log, LogProcessor } from '../types';
|
|
2
|
-
|
|
3
|
-
class LogFilter implements LogProcessor {
|
|
4
|
-
constructor(
|
|
5
|
-
private predicate: (log: Log) => boolean,
|
|
6
|
-
private processor: LogProcessor
|
|
7
|
-
) {}
|
|
8
|
-
|
|
9
|
-
processLog(log: Log) {
|
|
10
|
-
if (this.predicate(log)) {
|
|
11
|
-
this.processor.processLog(log);
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Filter logs based on a filter function. If the function returns true, the
|
|
18
|
-
* log is kept and forwarded onto the next processor, otherwise it is
|
|
19
|
-
* discarded.
|
|
20
|
-
*/
|
|
21
|
-
export default function filter(
|
|
22
|
-
/** Returns `true` to forward the log. */
|
|
23
|
-
predicate: (log: Log) => boolean,
|
|
24
|
-
/** Where to send the log if it passes the filter. */
|
|
25
|
-
processor: LogProcessor
|
|
26
|
-
): LogProcessor {
|
|
27
|
-
return new LogFilter(predicate, processor);
|
|
28
|
-
}
|