@holz/stream-backend 0.9.1-rc.9 → 0.9.2-rc.0.sha-87f4046
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/package.json +10 -7
- package/src/index.ts +1 -0
- package/src/stream-backend.ts +70 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@holz/stream-backend",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.2-rc.0.sha-87f4046",
|
|
4
4
|
"description": "Print logs to stdout or a file.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
},
|
|
11
11
|
"exports": {
|
|
12
12
|
".": {
|
|
13
|
+
"source": "./src/index.ts",
|
|
13
14
|
"types": "./dist/holz-stream-backend.d.ts",
|
|
14
15
|
"require": "./dist/holz-stream-backend.cjs",
|
|
15
16
|
"import": "./dist/holz-stream-backend.js"
|
|
@@ -22,7 +23,9 @@
|
|
|
22
23
|
"license": "MIT",
|
|
23
24
|
"sideEffects": false,
|
|
24
25
|
"files": [
|
|
25
|
-
"dist"
|
|
26
|
+
"dist",
|
|
27
|
+
"src",
|
|
28
|
+
"!src/**/__tests__"
|
|
26
29
|
],
|
|
27
30
|
"keywords": [
|
|
28
31
|
"holz-backend",
|
|
@@ -37,18 +40,18 @@
|
|
|
37
40
|
"test:types": "tsc"
|
|
38
41
|
},
|
|
39
42
|
"peerDependencies": {
|
|
40
|
-
"@holz/core": "^0.8.0"
|
|
43
|
+
"@holz/core": "^0.8.0 || ^0.9.0"
|
|
41
44
|
},
|
|
42
45
|
"devDependencies": {
|
|
43
|
-
"@holz/core": "^0.9.
|
|
46
|
+
"@holz/core": "^0.9.2-rc.0.sha-87f4046",
|
|
44
47
|
"@microsoft/api-extractor": "^7.58.8",
|
|
45
48
|
"@types/node": "^24.0.0",
|
|
46
|
-
"@vitest/coverage-v8": "^
|
|
49
|
+
"@vitest/coverage-v8": "^5.0.0",
|
|
47
50
|
"typescript": "^6.0.0",
|
|
48
51
|
"vite": "^8.0.0",
|
|
49
52
|
"vite-plugin-dts": "^5.0.0",
|
|
50
53
|
"vite-tsconfig-paths": "^6.0.0",
|
|
51
|
-
"vitest": "^
|
|
54
|
+
"vitest": "^5.0.0"
|
|
52
55
|
},
|
|
53
|
-
"gitHead": "
|
|
56
|
+
"gitHead": "87f40467fe9aa798fa894c04a208751403c45e8c"
|
|
54
57
|
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { createStreamBackend } from './stream-backend';
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { Writable } from 'node:stream';
|
|
2
|
+
import { EOL } from 'node:os';
|
|
3
|
+
import { inspect } from 'node:util';
|
|
4
|
+
import {
|
|
5
|
+
level,
|
|
6
|
+
type LogLevel,
|
|
7
|
+
type Log,
|
|
8
|
+
type LogContext,
|
|
9
|
+
type LogProcessor,
|
|
10
|
+
} from '@holz/core';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Prints logs to a writable stream in plaintext. Optimized for log files.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* createStreamBackend({
|
|
17
|
+
* stream: fs.createWriteStream('my-app.log', { flags: 'a' }),
|
|
18
|
+
* })
|
|
19
|
+
*/
|
|
20
|
+
export const createStreamBackend = ({ stream }: Config): LogProcessor => {
|
|
21
|
+
return (log: Log) => {
|
|
22
|
+
const { error, ...plainContext } = log.context;
|
|
23
|
+
const time = new Date(log.timestamp).toISOString();
|
|
24
|
+
const level = LOG_LEVELS[log.level];
|
|
25
|
+
const context = stringifyContext(plainContext);
|
|
26
|
+
const namespace = log.origin.length ? `[${log.origin.join(':')}] ` : '';
|
|
27
|
+
|
|
28
|
+
const header = `${time} ${level} ${namespace}`;
|
|
29
|
+
const message = multilineIndent(header.length, log.message);
|
|
30
|
+
const output = `${header}${message}${context ? ' ' + context : ''}`;
|
|
31
|
+
const errorMessage = error ? inspect(error, { colors: false }) + EOL : '';
|
|
32
|
+
|
|
33
|
+
// NOTE: If the stream applies backpressure, we will lose logs. I believe
|
|
34
|
+
// this is the right tradeoff. We can't prevent the app from generating
|
|
35
|
+
// more logs, and if we buffered it would risk running out of memory and
|
|
36
|
+
// crashing the process.
|
|
37
|
+
//
|
|
38
|
+
// It is unlikely that a file or tty will apply backpressure in practice.
|
|
39
|
+
stream.write(`${output}${EOL}${errorMessage}`);
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Some messages will ruin your output without proper indentation. Stack
|
|
45
|
+
* traces are a good example of this.
|
|
46
|
+
*
|
|
47
|
+
* Supports Unix + DOS line endings.
|
|
48
|
+
*/
|
|
49
|
+
const multilineIndent = (offset: number, message: string) =>
|
|
50
|
+
message.replace(/(\r?\n)/g, (newline) => newline + ' '.repeat(offset));
|
|
51
|
+
|
|
52
|
+
// { id: 123, type: 'article' } -> 'id=123 type="article"'
|
|
53
|
+
const stringifyContext = (context: LogContext) =>
|
|
54
|
+
Object.entries(context)
|
|
55
|
+
.map(([key, value]) => `${key}=${JSON.stringify(value)}`)
|
|
56
|
+
.join(' ');
|
|
57
|
+
|
|
58
|
+
const LOG_LEVELS: Record<LogLevel, string> = {
|
|
59
|
+
[level.trace]: 'TRACE',
|
|
60
|
+
[level.debug]: 'DEBUG',
|
|
61
|
+
[level.info]: 'INFO ',
|
|
62
|
+
[level.warn]: 'WARN ',
|
|
63
|
+
[level.error]: 'ERROR',
|
|
64
|
+
[level.fatal]: 'FATAL',
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
interface Config {
|
|
68
|
+
/** Where to print logs. Normally `process.stderr`. */
|
|
69
|
+
stream: Writable;
|
|
70
|
+
}
|