@dxos/log 0.8.4-main.ae835ea → 0.8.4-main.bbf232bc24
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/dist/lib/browser/chunk-IEP6GGEX.mjs +23 -0
- package/dist/lib/browser/chunk-IEP6GGEX.mjs.map +7 -0
- package/dist/lib/browser/chunk-V7FYKT4H.mjs +311 -0
- package/dist/lib/browser/chunk-V7FYKT4H.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +346 -199
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/platform/browser/index.mjs +26 -0
- package/dist/lib/browser/platform/browser/index.mjs.map +7 -0
- package/dist/lib/browser/platform/node/index.mjs +21 -0
- package/dist/lib/browser/platform/node/index.mjs.map +7 -0
- package/dist/lib/browser/processors/console-processor.mjs +102 -0
- package/dist/lib/browser/processors/console-processor.mjs.map +7 -0
- package/dist/lib/browser/processors/console-stub.mjs +9 -0
- package/dist/lib/browser/processors/console-stub.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-2SZHAWBN.mjs +24 -0
- package/dist/lib/node-esm/chunk-2SZHAWBN.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-5TBDXMQF.mjs +313 -0
- package/dist/lib/node-esm/chunk-5TBDXMQF.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +348 -288
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/platform/browser/index.mjs +27 -0
- package/dist/lib/node-esm/platform/browser/index.mjs.map +7 -0
- package/dist/lib/node-esm/platform/node/index.mjs +22 -0
- package/dist/lib/node-esm/platform/node/index.mjs.map +7 -0
- package/dist/lib/node-esm/processors/console-processor.mjs +103 -0
- package/dist/lib/node-esm/processors/console-processor.mjs.map +7 -0
- package/dist/lib/node-esm/processors/console-stub.mjs +10 -0
- package/dist/lib/node-esm/processors/console-stub.mjs.map +7 -0
- package/dist/types/src/config.d.ts.map +1 -1
- package/dist/types/src/context.d.ts +78 -2
- package/dist/types/src/context.d.ts.map +1 -1
- package/dist/types/src/dbg.d.ts +23 -0
- package/dist/types/src/dbg.d.ts.map +1 -0
- package/dist/types/src/decorators.d.ts +1 -1
- package/dist/types/src/decorators.d.ts.map +1 -1
- package/dist/types/src/environment.d.ts +24 -0
- package/dist/types/src/environment.d.ts.map +1 -0
- package/dist/types/src/environment.test.d.ts +2 -0
- package/dist/types/src/environment.test.d.ts.map +1 -0
- package/dist/types/src/experimental/ownership.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +7 -3
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/jsonl.d.ts +53 -0
- package/dist/types/src/jsonl.d.ts.map +1 -0
- package/dist/types/src/jsonl.test.d.ts +2 -0
- package/dist/types/src/jsonl.test.d.ts.map +1 -0
- package/dist/types/src/log-buffer.d.ts +20 -0
- package/dist/types/src/log-buffer.d.ts.map +1 -0
- package/dist/types/src/log-buffer.test.d.ts +2 -0
- package/dist/types/src/log-buffer.test.d.ts.map +1 -0
- package/dist/types/src/log.d.ts +55 -18
- package/dist/types/src/log.d.ts.map +1 -1
- package/dist/types/src/meta.d.ts +20 -1
- package/dist/types/src/meta.d.ts.map +1 -1
- package/dist/types/src/options.d.ts +1 -6
- package/dist/types/src/options.d.ts.map +1 -1
- package/dist/types/src/platform/browser/index.d.ts.map +1 -1
- package/dist/types/src/platform/index.d.ts +1 -1
- package/dist/types/src/platform/index.d.ts.map +1 -1
- package/dist/types/src/platform/node/index.d.ts.map +1 -1
- package/dist/types/src/processors/browser-processor.d.ts.map +1 -1
- package/dist/types/src/processors/common.d.ts.map +1 -1
- package/dist/types/src/processors/console-processor.d.ts.map +1 -1
- package/dist/types/src/processors/file-processor.d.ts.map +1 -1
- package/dist/types/src/processors/index.d.ts +3 -3
- package/dist/types/src/processors/index.d.ts.map +1 -1
- package/dist/types/src/scope.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +30 -15
- package/src/config.ts +1 -0
- package/src/context.ts +278 -7
- package/src/dbg.ts +34 -0
- package/src/decorators.ts +4 -5
- package/src/environment.test.ts +222 -0
- package/src/environment.ts +129 -0
- package/src/experimental/classes.test.ts +0 -1
- package/src/index.ts +7 -4
- package/src/jsonl.test.ts +121 -0
- package/src/jsonl.ts +104 -0
- package/src/log-buffer.test.ts +158 -0
- package/src/log-buffer.ts +89 -0
- package/src/log.test.ts +48 -19
- package/src/log.ts +146 -58
- package/src/meta.ts +29 -1
- package/src/options.ts +26 -10
- package/src/platform/index.ts +1 -1
- package/src/platform/node/index.ts +1 -2
- package/src/processors/browser-processor.ts +27 -28
- package/src/processors/console-processor.ts +5 -13
- package/src/processors/file-processor.ts +9 -9
- package/src/processors/index.ts +3 -3
package/src/log.test.ts
CHANGED
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import path from 'node:path';
|
|
6
|
-
|
|
7
|
-
import { describe, test } from 'vitest';
|
|
6
|
+
import { beforeEach, describe, test } from 'vitest';
|
|
8
7
|
|
|
9
8
|
import { LogLevel } from './config';
|
|
10
|
-
import {
|
|
9
|
+
import { shouldLog } from './context';
|
|
10
|
+
import { type Log, createLog } from './log';
|
|
11
11
|
|
|
12
12
|
class LogError extends Error {
|
|
13
13
|
constructor(
|
|
@@ -25,13 +25,52 @@ class LogError extends Error {
|
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
log
|
|
29
|
-
|
|
30
|
-
});
|
|
28
|
+
describe('log', () => {
|
|
29
|
+
let log!: Log;
|
|
31
30
|
|
|
32
|
-
|
|
31
|
+
beforeEach(() => {
|
|
32
|
+
log = createLog();
|
|
33
|
+
log.config({
|
|
34
|
+
filter: LogLevel.DEBUG,
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test('filters', ({ expect }) => {
|
|
39
|
+
const tests = [
|
|
40
|
+
{ expected: 0, filter: 'ERROR' },
|
|
41
|
+
{ expected: 2, filter: 'INFO' },
|
|
42
|
+
{ expected: 1, filter: 'foo:INFO' },
|
|
43
|
+
{ expected: 4, filter: 'DEBUG' },
|
|
44
|
+
{ expected: 2, filter: 'DEBUG,-foo:*' },
|
|
45
|
+
{ expected: 1, filter: 'INFO,-foo:*' },
|
|
46
|
+
{ expected: 3, filter: 'DEBUG,-foo:INFO' },
|
|
47
|
+
{ expected: 3, filter: 'foo:DEBUG,bar:INFO' },
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
for (const test of tests) {
|
|
51
|
+
let count = 0;
|
|
52
|
+
const log = createLog();
|
|
53
|
+
const remove = log.addProcessor((config, entry) => {
|
|
54
|
+
if (shouldLog(entry, config.filters)) {
|
|
55
|
+
count++;
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
log.config({
|
|
59
|
+
filter: test.filter,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
console.group(`Filter: "${test.filter}"`);
|
|
63
|
+
log.debug('line 1', {}, { F: 'foo.ts', L: 1, S: undefined });
|
|
64
|
+
log.info('line 2', {}, { F: 'foo.ts', L: 2, S: undefined });
|
|
65
|
+
log.debug('line 3', {}, { F: 'bar.ts', L: 3, S: undefined });
|
|
66
|
+
log.info('line 4', {}, { F: 'bar.ts', L: 4, S: undefined });
|
|
67
|
+
console.groupEnd();
|
|
68
|
+
|
|
69
|
+
expect(count, `Filter: "${test.filter}"`).toBe(test.expected);
|
|
70
|
+
remove();
|
|
71
|
+
}
|
|
72
|
+
});
|
|
33
73
|
|
|
34
|
-
describe('log', () => {
|
|
35
74
|
test('throws an error', () => {
|
|
36
75
|
try {
|
|
37
76
|
throw new LogError('Test failed', { value: 1 });
|
|
@@ -56,16 +95,6 @@ describe('log', () => {
|
|
|
56
95
|
}
|
|
57
96
|
});
|
|
58
97
|
|
|
59
|
-
test('config', () => {
|
|
60
|
-
log.config({
|
|
61
|
-
filter: LogLevel.INFO,
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
log.debug('Debug level log message');
|
|
65
|
-
log.info('Info level log message');
|
|
66
|
-
log.warn('Warn level log message');
|
|
67
|
-
});
|
|
68
|
-
|
|
69
98
|
test('config file', () => {
|
|
70
99
|
log.config({
|
|
71
100
|
file: path.join('packages/common/log/test-config.yml'),
|
|
@@ -91,7 +120,7 @@ describe('log', () => {
|
|
|
91
120
|
});
|
|
92
121
|
});
|
|
93
122
|
|
|
94
|
-
test('error',
|
|
123
|
+
test('error', () => {
|
|
95
124
|
const myError = new Error('Test error', { cause: new Error('Cause') });
|
|
96
125
|
log.catch(myError);
|
|
97
126
|
});
|
package/src/log.ts
CHANGED
|
@@ -3,10 +3,19 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { type LogConfig, LogLevel, type LogOptions } from './config';
|
|
6
|
-
import { type LogContext, type LogProcessor } from './context';
|
|
6
|
+
import { type LogContext, LogEntry, type LogProcessor } from './context';
|
|
7
7
|
import { createFunctionLogDecorator, createMethodLogDecorator } from './decorators';
|
|
8
8
|
import { type CallMetadata } from './meta';
|
|
9
|
-
import {
|
|
9
|
+
import { createConfig } from './options';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Accessible from browser console.
|
|
13
|
+
* Example: `DX_LOG.config({ filter: 'ERROR' })`
|
|
14
|
+
* NOTE: File level filtering isn't supported in storybooks.
|
|
15
|
+
*/
|
|
16
|
+
declare global {
|
|
17
|
+
const DX_LOG: Log;
|
|
18
|
+
}
|
|
10
19
|
|
|
11
20
|
/**
|
|
12
21
|
* Logging function.
|
|
@@ -17,77 +26,115 @@ type LogFunction = (message: string, context?: LogContext, meta?: CallMetadata)
|
|
|
17
26
|
* Logging methods.
|
|
18
27
|
*/
|
|
19
28
|
export interface LogMethods {
|
|
29
|
+
config: (options?: LogOptions) => Log;
|
|
30
|
+
addProcessor: (processor: LogProcessor, addDefault?: boolean) => () => void;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Log at `trace` level.
|
|
34
|
+
*
|
|
35
|
+
* Generally not surfaced to the developer and not captured in a log file.
|
|
36
|
+
*/
|
|
20
37
|
trace: LogFunction;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Log at `debug` level.
|
|
41
|
+
* Generally not surfaced to the developer and captured in a log file.
|
|
42
|
+
*/
|
|
21
43
|
debug: LogFunction;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Log at `verbose` level.
|
|
47
|
+
* Generally not surfaced to the developer and not captured in a log file.
|
|
48
|
+
*/
|
|
22
49
|
verbose: LogFunction;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Log at `info` level.
|
|
53
|
+
* Generally surfaced to the developer and captured in a log file.
|
|
54
|
+
*/
|
|
23
55
|
info: LogFunction;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Log at `warn` level.
|
|
59
|
+
* Generally surfaced to the developer and captured in a log file.
|
|
60
|
+
*/
|
|
24
61
|
warn: LogFunction;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Log at `error` level.
|
|
65
|
+
* Generally surfaced to the developer and captured in a log file.
|
|
66
|
+
*/
|
|
25
67
|
error: LogFunction;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Log an error and its stack trace at an `error` level.
|
|
71
|
+
* Generally surfaced to the developer and captured in a log file.
|
|
72
|
+
*/
|
|
26
73
|
catch: (error: Error | any, context?: LogContext, meta?: CallMetadata) => void;
|
|
27
|
-
|
|
28
|
-
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Decorator to log method parameters and return value at the `info` level.
|
|
77
|
+
*/
|
|
29
78
|
method: (arg0?: never, arg1?: never, meta?: CallMetadata) => MethodDecorator;
|
|
30
|
-
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Wrapper to log function parameters and return value at the `info` level.
|
|
82
|
+
*/
|
|
83
|
+
function: <F extends (...args: any[]) => any>(
|
|
31
84
|
name: string,
|
|
32
85
|
fn: F,
|
|
33
|
-
opts?: {
|
|
86
|
+
opts?: {
|
|
87
|
+
transformOutput?: (result: ReturnType<F>) => Promise<any> | any;
|
|
88
|
+
},
|
|
34
89
|
) => F;
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Log a horizontal rule at the `info` level.
|
|
93
|
+
*/
|
|
94
|
+
break: () => void;
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Log a stack trace at the `info` level.
|
|
98
|
+
*/
|
|
99
|
+
stack: (message?: string, context?: never, meta?: CallMetadata) => void;
|
|
35
100
|
}
|
|
36
101
|
|
|
37
102
|
/**
|
|
38
103
|
* Properties accessible on the logging function.
|
|
104
|
+
* @internal
|
|
39
105
|
*/
|
|
40
|
-
interface Log extends
|
|
41
|
-
|
|
42
|
-
addProcessor: (processor: LogProcessor) => void;
|
|
43
|
-
runtimeConfig: LogConfig;
|
|
106
|
+
export interface Log extends LogFunction, LogMethods {
|
|
107
|
+
readonly runtimeConfig: LogConfig;
|
|
44
108
|
}
|
|
45
109
|
|
|
110
|
+
/**
|
|
111
|
+
* @internal
|
|
112
|
+
*/
|
|
46
113
|
interface LogImp extends Log {
|
|
114
|
+
_id: string;
|
|
47
115
|
_config: LogConfig;
|
|
48
116
|
}
|
|
49
117
|
|
|
50
|
-
|
|
51
|
-
const log: LogImp = ((...params) => processLog(LogLevel.DEBUG, ...params)) as LogImp;
|
|
52
|
-
|
|
53
|
-
log._config = getConfig();
|
|
54
|
-
Object.defineProperty(log, 'runtimeConfig', { get: () => log._config });
|
|
55
|
-
|
|
56
|
-
log.addProcessor = (processor: LogProcessor) => {
|
|
57
|
-
if (DEFAULT_PROCESSORS.filter((p) => p === processor).length === 0) {
|
|
58
|
-
DEFAULT_PROCESSORS.push(processor);
|
|
59
|
-
}
|
|
60
|
-
if (log._config.processors.filter((p) => p === processor).length === 0) {
|
|
61
|
-
log._config.processors.push(processor);
|
|
62
|
-
}
|
|
63
|
-
};
|
|
118
|
+
let logCount = 0;
|
|
64
119
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
//
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
log.trace = (...params) => processLog(LogLevel.TRACE, ...params);
|
|
74
|
-
log.debug = (...params) => processLog(LogLevel.DEBUG, ...params);
|
|
75
|
-
log.verbose = (...params) => processLog(LogLevel.VERBOSE, ...params);
|
|
76
|
-
log.info = (...params) => processLog(LogLevel.INFO, ...params);
|
|
77
|
-
log.warn = (...params) => processLog(LogLevel.WARN, ...params);
|
|
78
|
-
log.error = (...params) => processLog(LogLevel.ERROR, ...params);
|
|
79
|
-
|
|
80
|
-
// Catch only shows error message, not stacktrace.
|
|
81
|
-
log.catch = (error: Error | any, context, meta) => processLog(LogLevel.ERROR, undefined, context, meta, error);
|
|
82
|
-
|
|
83
|
-
// Show break.
|
|
84
|
-
log.break = () => log.info('——————————————————————————————————————————————————');
|
|
120
|
+
/**
|
|
121
|
+
* Create a logging function with properties.
|
|
122
|
+
* @internal
|
|
123
|
+
*/
|
|
124
|
+
export const createLog = (): LogImp => {
|
|
125
|
+
// Default function.
|
|
126
|
+
const log: LogImp = ((...params) => processLog(LogLevel.DEBUG, ...params)) as LogImp;
|
|
85
127
|
|
|
86
|
-
|
|
87
|
-
|
|
128
|
+
// Add private properties.
|
|
129
|
+
Object.assign<LogImp, Partial<LogImp>>(log, {
|
|
130
|
+
_id: `log-${++logCount}`,
|
|
131
|
+
_config: createConfig(),
|
|
132
|
+
});
|
|
88
133
|
|
|
89
|
-
|
|
90
|
-
log
|
|
134
|
+
// TODO(burdon): Document.
|
|
135
|
+
Object.defineProperty(log, 'runtimeConfig', {
|
|
136
|
+
get: () => log._config,
|
|
137
|
+
});
|
|
91
138
|
|
|
92
139
|
/**
|
|
93
140
|
* Process the current log call.
|
|
@@ -99,16 +146,65 @@ const createLog = (): LogImp => {
|
|
|
99
146
|
meta?: CallMetadata,
|
|
100
147
|
error?: Error,
|
|
101
148
|
) => {
|
|
102
|
-
|
|
149
|
+
// TODO(burdon): Do the filter matching upstream (here) rather than in each processor?
|
|
150
|
+
const entry = new LogEntry({ level, message, context, meta, error });
|
|
151
|
+
log._config.processors.forEach((processor) => processor(log._config, entry));
|
|
103
152
|
};
|
|
104
153
|
|
|
154
|
+
/**
|
|
155
|
+
* API.
|
|
156
|
+
*/
|
|
157
|
+
Object.assign<Log, LogMethods>(log, {
|
|
158
|
+
/**
|
|
159
|
+
* Update config.
|
|
160
|
+
* NOTE: Preserves any processors that were already added to this logger instance
|
|
161
|
+
* unless an explicit processor option is provided.
|
|
162
|
+
*/
|
|
163
|
+
config: ({ processor, ...options } = {}) => {
|
|
164
|
+
const config = createConfig(options);
|
|
165
|
+
// TODO(burdon): This could be buggy since the behavior is not reentrant.
|
|
166
|
+
const processors = processor ? config.processors : log._config.processors;
|
|
167
|
+
log._config = { ...config, processors };
|
|
168
|
+
return log;
|
|
169
|
+
},
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Adds a processor to the logger.
|
|
173
|
+
*/
|
|
174
|
+
addProcessor: (processor) => {
|
|
175
|
+
if (log._config.processors.filter((p) => p === processor).length === 0) {
|
|
176
|
+
log._config.processors.push(processor);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return () => {
|
|
180
|
+
log._config.processors = log._config.processors.filter((p) => p !== processor);
|
|
181
|
+
};
|
|
182
|
+
},
|
|
183
|
+
|
|
184
|
+
trace: (...params) => processLog(LogLevel.TRACE, ...params),
|
|
185
|
+
debug: (...params) => processLog(LogLevel.DEBUG, ...params),
|
|
186
|
+
verbose: (...params) => processLog(LogLevel.VERBOSE, ...params),
|
|
187
|
+
info: (...params) => processLog(LogLevel.INFO, ...params),
|
|
188
|
+
warn: (...params) => processLog(LogLevel.WARN, ...params),
|
|
189
|
+
error: (...params) => processLog(LogLevel.ERROR, ...params),
|
|
190
|
+
catch: (error, context, meta) => processLog(LogLevel.ERROR, undefined, context, meta, error),
|
|
191
|
+
|
|
192
|
+
method: createMethodLogDecorator(log),
|
|
193
|
+
function: createFunctionLogDecorator(log),
|
|
194
|
+
|
|
195
|
+
break: () => log.info('-'.repeat(80)),
|
|
196
|
+
stack: (message, context, meta) => {
|
|
197
|
+
return processLog(LogLevel.INFO, `${message ?? 'Stack Dump'}\n${getFormattedStackTrace()}`, context, meta);
|
|
198
|
+
},
|
|
199
|
+
});
|
|
200
|
+
|
|
105
201
|
return log;
|
|
106
202
|
};
|
|
107
203
|
|
|
108
204
|
/**
|
|
109
205
|
* Global logging function.
|
|
110
206
|
*/
|
|
111
|
-
export const log: Log = ((globalThis as any).
|
|
207
|
+
export const log: Log = ((globalThis as any).DX_LOG ??= createLog());
|
|
112
208
|
|
|
113
209
|
const start = Date.now();
|
|
114
210
|
let last = start;
|
|
@@ -128,12 +224,4 @@ export const debug = (label?: any, args?: any) => {
|
|
|
128
224
|
last = Date.now();
|
|
129
225
|
};
|
|
130
226
|
|
|
131
|
-
/**
|
|
132
|
-
* Accessible from browser console.
|
|
133
|
-
*/
|
|
134
|
-
declare global {
|
|
135
|
-
// eslint-disable-next-line camelcase
|
|
136
|
-
const dx_log: Log;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
227
|
const getFormattedStackTrace = () => new Error().stack!.split('\n').slice(3).join('\n');
|
package/src/meta.ts
CHANGED
|
@@ -3,11 +3,27 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
6
|
+
* Marker key + value injected on every {@link CallMetadata} object by the log transform plugin.
|
|
7
|
+
* Used by {@link isLogMeta} to detect a log-meta argument at runtime (e.g. for variadic
|
|
8
|
+
* `param_index: 'last'` callees that don't have a fixed meta slot).
|
|
9
|
+
*/
|
|
10
|
+
export const LOG_META_MARKER = '~LogMeta';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Metadata injected by the log transform plugin.
|
|
7
14
|
*
|
|
8
15
|
* Field names are intentionally short to reduce the size of the generated code.
|
|
9
16
|
*/
|
|
10
17
|
export interface CallMetadata {
|
|
18
|
+
/**
|
|
19
|
+
* Marker tag — when present, equal to {@link LOG_META_MARKER} ({@link `'~LogMeta'`}).
|
|
20
|
+
* Injected by the log transform plugin on every emitted meta object so that {@link isLogMeta}
|
|
21
|
+
* can distinguish a meta argument from a regular user-supplied value at runtime.
|
|
22
|
+
* Optional because hand-written `CallMetadata` literals (decorators, RPC mappers, tests)
|
|
23
|
+
* don't need the marker — they are recognized by position in the call signature.
|
|
24
|
+
*/
|
|
25
|
+
'~LogMeta'?: typeof LOG_META_MARKER;
|
|
26
|
+
|
|
11
27
|
/**
|
|
12
28
|
* File name.
|
|
13
29
|
*/
|
|
@@ -35,3 +51,15 @@ export interface CallMetadata {
|
|
|
35
51
|
*/
|
|
36
52
|
A?: string[];
|
|
37
53
|
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Type guard: `true` when `value` is a {@link CallMetadata} object emitted by the log transform plugin.
|
|
57
|
+
* Detection is based on the presence of the {@link LOG_META_MARKER} marker key/value.
|
|
58
|
+
*/
|
|
59
|
+
export const isLogMeta = (value: unknown): value is CallMetadata => {
|
|
60
|
+
return (
|
|
61
|
+
value != null &&
|
|
62
|
+
typeof value === 'object' &&
|
|
63
|
+
(value as Record<string, unknown>)[LOG_META_MARKER] === LOG_META_MARKER
|
|
64
|
+
);
|
|
65
|
+
};
|
package/src/options.ts
CHANGED
|
@@ -12,32 +12,48 @@ import { BROWSER_PROCESSOR, CONSOLE_PROCESSOR, DEBUG_PROCESSOR } from './process
|
|
|
12
12
|
/**
|
|
13
13
|
* Processor variants.
|
|
14
14
|
*/
|
|
15
|
-
export const processors:
|
|
15
|
+
export const processors: Record<string, LogProcessor> = {
|
|
16
16
|
[LogProcessorType.CONSOLE]: CONSOLE_PROCESSOR,
|
|
17
17
|
[LogProcessorType.BROWSER]: BROWSER_PROCESSOR,
|
|
18
18
|
[LogProcessorType.DEBUG]: DEBUG_PROCESSOR,
|
|
19
19
|
};
|
|
20
20
|
|
|
21
|
-
const
|
|
21
|
+
const browser =
|
|
22
|
+
(typeof window !== 'undefined' || typeof navigator !== 'undefined') &&
|
|
23
|
+
!(typeof process !== 'undefined' && process?.env?.VITEST);
|
|
22
24
|
|
|
23
|
-
export const DEFAULT_PROCESSORS = [
|
|
25
|
+
export const DEFAULT_PROCESSORS = [browser ? BROWSER_PROCESSOR : CONSOLE_PROCESSOR];
|
|
24
26
|
|
|
27
|
+
const parseLogLevel = (level: string, defValue = LogLevel.WARN) => levels[level.toLowerCase()] ?? defValue;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @internal
|
|
31
|
+
*/
|
|
25
32
|
export const parseFilter = (filter: string | string[] | LogLevel): LogFilter[] => {
|
|
26
33
|
if (typeof filter === 'number') {
|
|
27
34
|
return [{ level: filter }];
|
|
28
35
|
}
|
|
29
36
|
|
|
30
|
-
const parseLogLevel = (level: string, defValue = LogLevel.WARN) => levels[level.toLowerCase()] ?? defValue;
|
|
31
|
-
|
|
32
37
|
const lines = typeof filter === 'string' ? filter.split(/,\s*/) : filter;
|
|
33
38
|
return lines.map((filter) => {
|
|
34
39
|
const [pattern, level] = filter.split(':');
|
|
35
|
-
return level
|
|
40
|
+
return level
|
|
41
|
+
? {
|
|
42
|
+
level: parseLogLevel(level),
|
|
43
|
+
pattern,
|
|
44
|
+
}
|
|
45
|
+
: {
|
|
46
|
+
level: parseLogLevel(pattern),
|
|
47
|
+
};
|
|
36
48
|
});
|
|
37
49
|
};
|
|
38
50
|
|
|
39
|
-
|
|
40
|
-
|
|
51
|
+
/**
|
|
52
|
+
* @internal
|
|
53
|
+
*/
|
|
54
|
+
export const createConfig = (options?: LogOptions): LogConfig => {
|
|
55
|
+
// Node only.
|
|
56
|
+
const envOptions: LogOptions | undefined =
|
|
41
57
|
'process' in globalThis
|
|
42
58
|
? {
|
|
43
59
|
file: process!.env.LOG_CONFIG,
|
|
@@ -46,12 +62,12 @@ export const getConfig = (options?: LogOptions): LogConfig => {
|
|
|
46
62
|
}
|
|
47
63
|
: undefined;
|
|
48
64
|
|
|
49
|
-
const mergedOptions: LogOptions = defaultsDeep({}, loadOptions(
|
|
65
|
+
const mergedOptions: LogOptions = defaultsDeep({}, loadOptions(envOptions?.file), envOptions, options);
|
|
50
66
|
return {
|
|
51
67
|
options: mergedOptions,
|
|
52
68
|
filters: parseFilter(mergedOptions.filter ?? LogLevel.INFO),
|
|
53
69
|
captureFilters: parseFilter(mergedOptions.captureFilter ?? LogLevel.WARN),
|
|
54
|
-
processors: mergedOptions.processor ? [processors[mergedOptions.processor]] : DEFAULT_PROCESSORS,
|
|
70
|
+
processors: mergedOptions.processor ? [processors[mergedOptions.processor]] : [...DEFAULT_PROCESSORS],
|
|
55
71
|
prefix: mergedOptions.prefix,
|
|
56
72
|
};
|
|
57
73
|
};
|
package/src/platform/index.ts
CHANGED
|
@@ -2,23 +2,11 @@
|
|
|
2
2
|
// Copyright 2022 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { safariCheck } from '@dxos/util';
|
|
6
6
|
|
|
7
7
|
import { LogLevel } from '../config';
|
|
8
8
|
import { type LogProcessor, getContextFromEntry, shouldLog } from '../context';
|
|
9
9
|
|
|
10
|
-
const getRelativeFilename = (filename: string) => {
|
|
11
|
-
// TODO(burdon): Hack uses "packages" as an anchor (pre-parse NX?)
|
|
12
|
-
// Including `packages/` part of the path so that excluded paths (e.g. from dist) are clickable in vscode.
|
|
13
|
-
const match = filename.match(/.+\/(packages\/.+\/.+)/);
|
|
14
|
-
if (match) {
|
|
15
|
-
const [, filePath] = match;
|
|
16
|
-
return filePath;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
return filename;
|
|
20
|
-
};
|
|
21
|
-
|
|
22
10
|
type Config = {
|
|
23
11
|
useTestProcessor: boolean;
|
|
24
12
|
printFileLinks: boolean;
|
|
@@ -47,23 +35,26 @@ const APP_BROWSER_PROCESSOR: LogProcessor = (config, entry) => {
|
|
|
47
35
|
// const LOG_BROWSER_CSS = ['color:gray; font-size:10px; padding-bottom: 4px', 'color:#B97852; font-size:14px;'];
|
|
48
36
|
const LOG_BROWSER_CSS: string[] = [];
|
|
49
37
|
|
|
38
|
+
const { filename, line: lineNumber, context: scopeDebugName } = entry.computedMeta;
|
|
39
|
+
|
|
50
40
|
let link = '';
|
|
51
|
-
if (
|
|
52
|
-
const filename = getRelativeFilename(entry.meta.F);
|
|
41
|
+
if (filename !== undefined && lineNumber !== undefined) {
|
|
53
42
|
const filepath = `${LOG_BROWSER_PREFIX.replace(/\/$/, '')}/${filename}`;
|
|
54
43
|
// TODO(burdon): Line numbers not working for app link, even with colons.
|
|
55
44
|
// https://stackoverflow.com/a/54459820/2804332
|
|
56
|
-
link = `${filepath}#L${
|
|
45
|
+
link = `${filepath}#L${lineNumber}`;
|
|
57
46
|
}
|
|
58
47
|
|
|
59
48
|
let args = [];
|
|
60
49
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
const scopeName = scope.name ||
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
50
|
+
const scope = entry.meta?.S;
|
|
51
|
+
if (scope) {
|
|
52
|
+
const scopeName = scope.name || scopeDebugName;
|
|
53
|
+
if (scopeName) {
|
|
54
|
+
const processPrefix = scope.hostSessionId ? '[worker] ' : '';
|
|
55
|
+
// TODO(dmaretskyi): Those can be made clickable with a custom formatter.
|
|
56
|
+
args.push(`%c${processPrefix}${scopeName}`, 'color:#C026D3;font-weight:bold');
|
|
57
|
+
}
|
|
67
58
|
}
|
|
68
59
|
|
|
69
60
|
if (entry.message) {
|
|
@@ -73,9 +64,9 @@ const APP_BROWSER_PROCESSOR: LogProcessor = (config, entry) => {
|
|
|
73
64
|
const context = getContextFromEntry(entry);
|
|
74
65
|
if (context) {
|
|
75
66
|
if (Object.keys(context).length === 1 && 'error' in context) {
|
|
76
|
-
args.push(context.error);
|
|
67
|
+
args.push(unwrapEffectError(context.error));
|
|
77
68
|
} else if (Object.keys(context).length === 1 && 'err' in context) {
|
|
78
|
-
args.push(context.err);
|
|
69
|
+
args.push(unwrapEffectError(context.err));
|
|
79
70
|
} else {
|
|
80
71
|
args.push(context);
|
|
81
72
|
}
|
|
@@ -114,10 +105,8 @@ const TEST_BROWSER_PROCESSOR: LogProcessor = (config, entry) => {
|
|
|
114
105
|
return;
|
|
115
106
|
}
|
|
116
107
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
path = `${getRelativeFilename(entry.meta.F)}:${entry.meta.L}`;
|
|
120
|
-
}
|
|
108
|
+
const { filename, line: lineNumber } = entry.computedMeta;
|
|
109
|
+
const path = filename !== undefined && lineNumber !== undefined ? `${filename}:${lineNumber}` : '';
|
|
121
110
|
|
|
122
111
|
let args = [];
|
|
123
112
|
|
|
@@ -148,3 +137,13 @@ const TEST_BROWSER_PROCESSOR: LogProcessor = (config, entry) => {
|
|
|
148
137
|
};
|
|
149
138
|
|
|
150
139
|
export const BROWSER_PROCESSOR: LogProcessor = CONFIG.useTestProcessor ? TEST_BROWSER_PROCESSOR : APP_BROWSER_PROCESSOR;
|
|
140
|
+
|
|
141
|
+
// effect-specific
|
|
142
|
+
const originalSymbol = Symbol.for('effect/OriginalAnnotation');
|
|
143
|
+
|
|
144
|
+
const unwrapEffectError = (error: any) => {
|
|
145
|
+
if (typeof error === 'object' && error !== null && originalSymbol in error) {
|
|
146
|
+
return error[originalSymbol];
|
|
147
|
+
}
|
|
148
|
+
return error;
|
|
149
|
+
};
|
|
@@ -2,17 +2,14 @@
|
|
|
2
2
|
// Copyright 2022 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { inspect } from 'node:util';
|
|
6
|
-
|
|
7
5
|
import chalk from 'chalk';
|
|
6
|
+
import { inspect } from 'node:util';
|
|
8
7
|
|
|
9
8
|
import { getPrototypeSpecificInstanceId, pickBy } from '@dxos/util';
|
|
10
9
|
|
|
11
10
|
import { type LogConfig, LogLevel, shortLevelName } from '../config';
|
|
12
11
|
import { type LogProcessor, getContextFromEntry, shouldLog } from '../context';
|
|
13
12
|
|
|
14
|
-
import { getRelativeFilename } from './common';
|
|
15
|
-
|
|
16
13
|
const LEVEL_COLORS: Record<LogLevel, typeof chalk.ForegroundColor> = {
|
|
17
14
|
[LogLevel.TRACE]: 'gray',
|
|
18
15
|
[LogLevel.DEBUG]: 'gray',
|
|
@@ -95,22 +92,17 @@ export const CONSOLE_PROCESSOR: LogProcessor = (config, entry) => {
|
|
|
95
92
|
return;
|
|
96
93
|
}
|
|
97
94
|
|
|
95
|
+
const { filename, line: lineNumber } = entry.computedMeta;
|
|
98
96
|
const parts: FormatParts = {
|
|
99
97
|
level,
|
|
100
98
|
message,
|
|
101
99
|
error,
|
|
102
|
-
path:
|
|
103
|
-
line:
|
|
104
|
-
scope:
|
|
100
|
+
path: filename,
|
|
101
|
+
line: lineNumber,
|
|
102
|
+
scope: meta?.S,
|
|
105
103
|
context: undefined,
|
|
106
104
|
};
|
|
107
105
|
|
|
108
|
-
if (meta) {
|
|
109
|
-
parts.path = getRelativeFilename(meta.F);
|
|
110
|
-
parts.line = meta.L;
|
|
111
|
-
parts.scope = meta.S;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
106
|
const context = getContextFromEntry(entry);
|
|
115
107
|
if (context) {
|
|
116
108
|
// Remove undefined fields.
|
|
@@ -5,15 +5,12 @@
|
|
|
5
5
|
import { appendFileSync, mkdirSync, openSync } from 'node:fs';
|
|
6
6
|
import { dirname } from 'node:path';
|
|
7
7
|
|
|
8
|
-
import { jsonlogify } from '@dxos/util';
|
|
9
|
-
|
|
10
8
|
import { type LogFilter, LogLevel } from '../config';
|
|
11
|
-
import { type LogProcessor,
|
|
12
|
-
|
|
13
|
-
import { getRelativeFilename } from './common';
|
|
9
|
+
import { type LogProcessor, shouldLog } from '../context';
|
|
14
10
|
|
|
15
11
|
// Amount of time to retry writing after encountering EAGAIN before giving up.
|
|
16
12
|
const EAGAIN_MAX_DURATION = 1000;
|
|
13
|
+
|
|
17
14
|
/**
|
|
18
15
|
* Create a file processor.
|
|
19
16
|
* @param path - Path to log file to create or append to, or existing open file descriptor e.g. stdout.
|
|
@@ -38,6 +35,7 @@ export const createFileProcessor = ({
|
|
|
38
35
|
if (!shouldLog(entry, filters)) {
|
|
39
36
|
return;
|
|
40
37
|
}
|
|
38
|
+
|
|
41
39
|
if (typeof pathOrFd === 'number') {
|
|
42
40
|
fd = pathOrFd;
|
|
43
41
|
} else {
|
|
@@ -48,10 +46,12 @@ export const createFileProcessor = ({
|
|
|
48
46
|
}
|
|
49
47
|
|
|
50
48
|
const record = {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
49
|
+
level: entry.level,
|
|
50
|
+
message: entry.message,
|
|
51
|
+
timestamp: entry.timestamp,
|
|
52
|
+
meta: entry.computedMeta,
|
|
53
|
+
context: entry.computedContext,
|
|
54
|
+
error: entry.computedError,
|
|
55
55
|
};
|
|
56
56
|
let retryTS: number = 0;
|
|
57
57
|
|