@dxos/log 0.8.4-main.f9ba587 → 0.8.4-main.fffef41

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.
Files changed (38) hide show
  1. package/dist/lib/browser/index.mjs +95 -48
  2. package/dist/lib/browser/index.mjs.map +3 -3
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/node-esm/index.mjs +101 -52
  5. package/dist/lib/node-esm/index.mjs.map +3 -3
  6. package/dist/lib/node-esm/meta.json +1 -1
  7. package/dist/types/src/config.d.ts +2 -3
  8. package/dist/types/src/config.d.ts.map +1 -1
  9. package/dist/types/src/context.d.ts +1 -1
  10. package/dist/types/src/context.d.ts.map +1 -1
  11. package/dist/types/src/decorators.d.ts +1 -1
  12. package/dist/types/src/decorators.d.ts.map +1 -1
  13. package/dist/types/src/index.d.ts +1 -1
  14. package/dist/types/src/index.d.ts.map +1 -1
  15. package/dist/types/src/log.d.ts +12 -18
  16. package/dist/types/src/log.d.ts.map +1 -1
  17. package/dist/types/src/options.d.ts +1 -6
  18. package/dist/types/src/options.d.ts.map +1 -1
  19. package/dist/types/src/platform/node/index.d.ts.map +1 -1
  20. package/dist/types/src/processors/browser-processor.d.ts.map +1 -1
  21. package/dist/types/src/processors/console-processor.d.ts +1 -1
  22. package/dist/types/src/processors/console-processor.d.ts.map +1 -1
  23. package/dist/types/src/processors/file-processor.d.ts.map +1 -1
  24. package/dist/types/tsconfig.tsbuildinfo +1 -1
  25. package/package.json +5 -5
  26. package/src/config.ts +3 -2
  27. package/src/context.ts +38 -8
  28. package/src/decorators.ts +5 -4
  29. package/src/experimental/classes.test.ts +2 -1
  30. package/src/index.ts +1 -1
  31. package/src/log.test.ts +59 -23
  32. package/src/log.ts +102 -59
  33. package/src/options.ts +25 -11
  34. package/src/platform/node/index.ts +2 -1
  35. package/src/processors/browser-processor.ts +6 -2
  36. package/src/processors/console-processor.ts +11 -7
  37. package/src/processors/file-processor.ts +4 -1
  38. package/src/scope.ts +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/log",
3
- "version": "0.8.4-main.f9ba587",
3
+ "version": "0.8.4-main.fffef41",
4
4
  "description": "Logger",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -10,12 +10,12 @@
10
10
  "type": "module",
11
11
  "exports": {
12
12
  ".": {
13
+ "types": "./dist/types/src/index.d.ts",
13
14
  "browser": "./dist/lib/browser/index.mjs",
14
15
  "node": {
15
16
  "require": "./dist/lib/node/index.cjs",
16
17
  "default": "./dist/lib/node-esm/index.mjs"
17
- },
18
- "types": "./dist/types/src/index.d.ts"
18
+ }
19
19
  }
20
20
  },
21
21
  "browser": {
@@ -35,8 +35,8 @@
35
35
  "js-yaml": "^4.1.0",
36
36
  "lodash.defaultsdeep": "^4.6.1",
37
37
  "lodash.omit": "^4.5.0",
38
- "@dxos/node-std": "0.8.4-main.f9ba587",
39
- "@dxos/util": "0.8.4-main.f9ba587"
38
+ "@dxos/node-std": "0.8.4-main.fffef41",
39
+ "@dxos/util": "0.8.4-main.fffef41"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@types/js-yaml": "^4.0.5",
package/src/config.ts CHANGED
@@ -6,8 +6,8 @@ import { type LogProcessor } from './context';
6
6
 
7
7
  /**
8
8
  * Standard levels.
9
+ * NOTE: Keep aligned with LogLevel in @dxos/protocols.
9
10
  */
10
- // NOTE: Keep aligned with LogLevel in @dxos/protocols.
11
11
  // TODO(burdon): Update numbers?
12
12
  export enum LogLevel {
13
13
  TRACE = 5,
@@ -18,7 +18,8 @@ export enum LogLevel {
18
18
  ERROR = 14,
19
19
  }
20
20
 
21
- export const levels: { [index: string]: LogLevel } = {
21
+ export const levels: Record<string, LogLevel> = {
22
+ '*': LogLevel.TRACE,
22
23
  trace: LogLevel.TRACE,
23
24
  debug: LogLevel.DEBUG,
24
25
  verbose: LogLevel.VERBOSE,
package/src/context.ts CHANGED
@@ -16,7 +16,7 @@ export type LogContext = Record<string, any> | Error | any;
16
16
  */
17
17
  export interface LogEntry {
18
18
  level: LogLevel;
19
- message: string;
19
+ message?: string;
20
20
  context?: LogContext;
21
21
  meta?: CallMetadata;
22
22
  error?: Error;
@@ -27,8 +27,33 @@ export interface LogEntry {
27
27
  */
28
28
  export type LogProcessor = (config: LogConfig, entry: LogEntry) => void;
29
29
 
30
- const matchFilter = (filter: LogFilter, level: LogLevel, path: string) => {
31
- return level >= filter.level && (!filter.pattern || path.includes(filter.pattern));
30
+ /**
31
+ * Returns:
32
+ * true if the log entry matches the filter,
33
+ * false if should be excluded, or
34
+ * undefined if it the filter doesn't match the level.
35
+ */
36
+ const matchFilter = (filter: LogFilter, level: LogLevel, path?: string): boolean | undefined => {
37
+ // TODO(burdon): Support regexp.
38
+ if (filter.pattern?.startsWith('-')) {
39
+ // Exclude.
40
+ if (path?.includes(filter.pattern.slice(1))) {
41
+ if (level >= filter.level) {
42
+ return false;
43
+ }
44
+ }
45
+ } else {
46
+ // Include.
47
+ if (filter.pattern?.length) {
48
+ if (path?.includes(filter.pattern)) {
49
+ return level >= filter.level;
50
+ }
51
+ } else {
52
+ if (level >= filter.level) {
53
+ return true;
54
+ }
55
+ }
56
+ }
32
57
  };
33
58
 
34
59
  /**
@@ -36,10 +61,16 @@ const matchFilter = (filter: LogFilter, level: LogLevel, path: string) => {
36
61
  */
37
62
  export const shouldLog = (entry: LogEntry, filters?: LogFilter[]): boolean => {
38
63
  if (filters === undefined) {
39
- return true;
40
- } else {
41
- return filters.some((filter) => matchFilter(filter, entry.level, entry.meta?.F ?? ''));
64
+ return false;
42
65
  }
66
+
67
+ const results = filters
68
+ .map((filter) => matchFilter(filter, entry.level, entry.meta?.F))
69
+ .filter((result): result is boolean => result !== undefined);
70
+
71
+ // Skip if any are explicitely false.
72
+ // console.log({ level: entry.level, path: entry.meta?.F }, filters, results, results.length);
73
+ return results.length > 0 && !results.some((results) => results === false);
43
74
  };
44
75
 
45
76
  export const getContextFromEntry = (entry: LogEntry): Record<string, any> | undefined => {
@@ -64,8 +95,7 @@ export const getContextFromEntry = (entry: LogEntry): Record<string, any> | unde
64
95
  }
65
96
 
66
97
  if (entry.error) {
67
- const errorContext = (entry.error as any).context;
68
- context = Object.assign(context ?? {}, { error: entry.error, ...errorContext });
98
+ context = Object.assign(context ?? {}, { error: entry.error });
69
99
  }
70
100
 
71
101
  return context && Object.keys(context).length > 0 ? context : undefined;
package/src/decorators.ts CHANGED
@@ -2,10 +2,11 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import chalk from 'chalk';
6
5
  import { inspect } from 'node:util';
7
6
 
8
- import type { LogMethods } from './log';
7
+ import chalk from 'chalk';
8
+
9
+ import { type LogMethods } from './log';
9
10
  import { type CallMetadata } from './meta';
10
11
 
11
12
  let nextPromiseId = 0;
@@ -190,13 +191,13 @@ const logAsyncRejected = (
190
191
  );
191
192
  };
192
193
 
194
+ const COLOR_FUNCTION = [220, 220, 170] as const;
195
+
193
196
  // https://github.com/dxos/dxos/issues/7286
194
197
  const greenCheck = typeof chalk.green === 'function' ? chalk.green('✔') : '✔';
195
198
 
196
199
  const formatTimeElapsed = (startTime: number) => chalk.gray(`${(performance.now() - startTime).toFixed(0)}ms`);
197
200
 
198
- const COLOR_FUNCTION = [220, 220, 170] as const;
199
-
200
201
  const formatFunction = (name: string) => chalk.bold(chalk.rgb(...COLOR_FUNCTION)(name));
201
202
 
202
203
  const formatPromise = (id: number) => chalk.blue(`Promise#${id}`);
@@ -4,9 +4,10 @@
4
4
 
5
5
  import { describe, test } from 'vitest';
6
6
 
7
- import { debugInfo, ownershipClass } from './ownership';
8
7
  import { log } from '../log';
9
8
 
9
+ import { debugInfo, ownershipClass } from './ownership';
10
+
10
11
  describe('classes', function () {
11
12
  test('field instance', function () {
12
13
  @ownershipClass
package/src/index.ts CHANGED
@@ -14,6 +14,6 @@ export * from './log';
14
14
  export { parseFilter } from './options';
15
15
  export * from './processors';
16
16
  export * from './scope';
17
- export * from './meta';
17
+ export type * from './meta';
18
18
 
19
19
  export { getCurrentOwnershipScope } from './experimental/ownership';
package/src/log.test.ts CHANGED
@@ -3,10 +3,12 @@
3
3
  //
4
4
 
5
5
  import path from 'node:path';
6
- import { describe, test } from 'vitest';
6
+
7
+ import { beforeEach, describe, test } from 'vitest';
7
8
 
8
9
  import { LogLevel } from './config';
9
- import { log } from './log';
10
+ import { shouldLog } from './context';
11
+ import { type Log, createLog } from './log';
10
12
 
11
13
  class LogError extends Error {
12
14
  constructor(
@@ -24,14 +26,53 @@ class LogError extends Error {
24
26
  }
25
27
  }
26
28
 
27
- log.config({
28
- filter: LogLevel.DEBUG,
29
- });
29
+ describe('log', () => {
30
+ let log!: Log;
31
+
32
+ beforeEach(() => {
33
+ log = createLog();
34
+ log.config({
35
+ filter: LogLevel.DEBUG,
36
+ });
37
+ });
30
38
 
31
- /* eslint-disable @stayradiated/prefer-arrow-functions/prefer-arrow-functions */
39
+ test('filters', ({ expect }) => {
40
+ const tests = [
41
+ { expected: 0, filter: 'ERROR' },
42
+ { expected: 2, filter: 'INFO' },
43
+ { expected: 1, filter: 'foo:INFO' },
44
+ { expected: 4, filter: 'DEBUG' },
45
+ { expected: 2, filter: 'DEBUG,-foo:*' },
46
+ { expected: 1, filter: 'INFO,-foo:*' },
47
+ { expected: 3, filter: 'DEBUG,-foo:INFO' },
48
+ { expected: 3, filter: 'foo:DEBUG,bar:INFO' },
49
+ ];
50
+
51
+ for (const test of tests) {
52
+ let count = 0;
53
+ const log = createLog();
54
+ const remove = log.addProcessor((config, entry) => {
55
+ if (shouldLog(entry, config.filters)) {
56
+ count++;
57
+ }
58
+ });
59
+ log.config({
60
+ filter: test.filter,
61
+ });
62
+
63
+ console.group(`Filter: "${test.filter}"`);
64
+ log.debug('line 1', {}, { F: 'foo.ts', L: 1, S: undefined });
65
+ log.info('line 2', {}, { F: 'foo.ts', L: 2, S: undefined });
66
+ log.debug('line 3', {}, { F: 'bar.ts', L: 3, S: undefined });
67
+ log.info('line 4', {}, { F: 'bar.ts', L: 4, S: undefined });
68
+ console.groupEnd();
69
+
70
+ expect(count, `Filter: "${test.filter}"`).toBe(test.expected);
71
+ remove();
72
+ }
73
+ });
32
74
 
33
- describe('log', function () {
34
- test('throws an error', function () {
75
+ test('throws an error', () => {
35
76
  try {
36
77
  throw new LogError('Test failed', { value: 1 });
37
78
  } catch (err: any) {
@@ -39,7 +80,7 @@ describe('log', function () {
39
80
  }
40
81
  });
41
82
 
42
- test('throws an error showing stacktrace', function () {
83
+ test('throws an error showing stacktrace', () => {
43
84
  try {
44
85
  throw new LogError('Test failed', { value: 2 });
45
86
  } catch (err: any) {
@@ -47,7 +88,7 @@ describe('log', function () {
47
88
  }
48
89
  });
49
90
 
50
- test('catches an error', function () {
91
+ test('catches an error', () => {
51
92
  try {
52
93
  throw new LogError('ERROR ON LINE 21', { value: 3 });
53
94
  } catch (err: any) {
@@ -55,17 +96,7 @@ describe('log', function () {
55
96
  }
56
97
  });
57
98
 
58
- test('config', function () {
59
- log.config({
60
- filter: LogLevel.INFO,
61
- });
62
-
63
- log.debug('Debug level log message');
64
- log.info('Info level log message');
65
- log.warn('Warn level log message');
66
- });
67
-
68
- test('config file', function () {
99
+ test('config file', () => {
69
100
  log.config({
70
101
  file: path.join('packages/common/log/test-config.yml'),
71
102
  });
@@ -75,7 +106,7 @@ describe('log', function () {
75
106
  log.warn('Warn level log message');
76
107
  });
77
108
 
78
- test('levels', function () {
109
+ test('levels', () => {
79
110
  log('Default level log message');
80
111
  log.debug('Debug level log message');
81
112
  log.info('Info level log message');
@@ -83,10 +114,15 @@ describe('log', function () {
83
114
  log.error('Error level log message');
84
115
  });
85
116
 
86
- test('context', function () {
117
+ test('context', () => {
87
118
  log.info('Message with context', {
88
119
  title: 'test',
89
120
  context: 123,
90
121
  });
91
122
  });
123
+
124
+ test('error', () => {
125
+ const myError = new Error('Test error', { cause: new Error('Cause') });
126
+ log.catch(myError);
127
+ });
92
128
  });
package/src/log.ts CHANGED
@@ -6,7 +6,14 @@ import { type LogConfig, LogLevel, type LogOptions } from './config';
6
6
  import { type LogContext, type LogProcessor } from './context';
7
7
  import { createFunctionLogDecorator, createMethodLogDecorator } from './decorators';
8
8
  import { type CallMetadata } from './meta';
9
- import { getConfig, DEFAULT_PROCESSORS } from './options';
9
+ import { createConfig } from './options';
10
+
11
+ /**
12
+ * Accessible from browser console.
13
+ */
14
+ declare global {
15
+ const DX_LOG: Log;
16
+ }
10
17
 
11
18
  /**
12
19
  * Logging function.
@@ -17,6 +24,9 @@ type LogFunction = (message: string, context?: LogContext, meta?: CallMetadata)
17
24
  * Logging methods.
18
25
  */
19
26
  export interface LogMethods {
27
+ config: (options: LogOptions) => Log;
28
+ addProcessor: (processor: LogProcessor, addDefault?: boolean) => () => void;
29
+
20
30
  trace: LogFunction;
21
31
  debug: LogFunction;
22
32
  verbose: LogFunction;
@@ -24,92 +34,133 @@ export interface LogMethods {
24
34
  warn: LogFunction;
25
35
  error: LogFunction;
26
36
  catch: (error: Error | any, context?: LogContext, meta?: CallMetadata) => void;
27
- break: () => void;
28
- stack: (message?: string, context?: never, meta?: CallMetadata) => void;
37
+
29
38
  method: (arg0?: never, arg1?: never, meta?: CallMetadata) => MethodDecorator;
30
- func: <F extends (...args: any[]) => any>(
39
+ function: <F extends (...args: any[]) => any>(
31
40
  name: string,
32
41
  fn: F,
33
- opts?: { transformOutput?: (result: ReturnType<F>) => Promise<any> | any },
42
+ opts?: {
43
+ transformOutput?: (result: ReturnType<F>) => Promise<any> | any;
44
+ },
34
45
  ) => F;
46
+
47
+ break: () => void;
48
+ stack: (message?: string, context?: never, meta?: CallMetadata) => void;
35
49
  }
36
50
 
37
51
  /**
38
52
  * Properties accessible on the logging function.
53
+ * @internal
39
54
  */
40
- interface Log extends LogMethods, LogFunction {
41
- config: (options: LogOptions) => void;
42
- addProcessor: (processor: LogProcessor) => void;
43
- runtimeConfig: LogConfig;
55
+ export interface Log extends LogFunction, LogMethods {
56
+ readonly runtimeConfig: LogConfig;
44
57
  }
45
58
 
59
+ /**
60
+ * @internal
61
+ */
46
62
  interface LogImp extends Log {
63
+ _id: string;
47
64
  _config: LogConfig;
48
65
  }
49
66
 
50
- const createLog = (): LogImp => {
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
- };
64
-
65
- // Set config.
66
- log.config = (options: LogOptions) => {
67
- log._config = getConfig(options);
68
- };
69
-
70
- // TODO(burdon): API to set context and separate error object.
71
- // E.g., log.warn('failed', { key: 123 }, err);
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);
67
+ let logCount = 0;
79
68
 
80
- // Catch only shows error message, not stacktrace.
81
- log.catch = (error: Error | any, context, meta) =>
82
- processLog(LogLevel.ERROR, error?.message ?? String(error), context, meta, error);
83
-
84
- // Show break.
85
- log.break = () => log.info('——————————————————————————————————————————————————');
69
+ /**
70
+ * Create a logging function with properties.
71
+ * @internal
72
+ */
73
+ export const createLog = (): LogImp => {
74
+ // Default function.
75
+ const log: LogImp = ((...params) => processLog(LogLevel.DEBUG, ...params)) as LogImp;
86
76
 
87
- log.stack = (message, context, meta) =>
88
- processLog(LogLevel.INFO, `${message ?? 'Stack Dump'}\n${getFormattedStackTrace()}`, context, meta);
77
+ // Add private properties.
78
+ Object.assign<LogImp, Partial<LogImp>>(log, {
79
+ _id: `log-${++logCount}`,
80
+ _config: createConfig(),
81
+ });
89
82
 
90
- log.method = createMethodLogDecorator(log);
91
- log.func = createFunctionLogDecorator(log);
83
+ // TODO(burdon): Document.
84
+ Object.defineProperty(log, 'runtimeConfig', {
85
+ get: () => log._config,
86
+ });
92
87
 
93
88
  /**
94
89
  * Process the current log call.
95
90
  */
96
91
  const processLog = (
97
92
  level: LogLevel,
98
- message: string,
93
+ message: string | undefined,
99
94
  context: LogContext = {},
100
95
  meta?: CallMetadata,
101
96
  error?: Error,
102
97
  ) => {
103
- log._config.processors.forEach((processor) => processor(log._config, { level, message, context, meta, error }));
98
+ // TODO(burdon): Do the filter matching upstream (here) rather than in each processor?
99
+ log._config.processors.forEach((processor) =>
100
+ processor(log._config, {
101
+ level,
102
+ message,
103
+ context,
104
+ meta,
105
+ error,
106
+ }),
107
+ );
104
108
  };
105
109
 
110
+ /**
111
+ * API.
112
+ */
113
+ Object.assign<Log, LogMethods>(log, {
114
+ /**
115
+ * Update config.
116
+ * NOTE: Preserves any processors that were already added to this logger instance
117
+ * unless an explicit processor option is provided.
118
+ */
119
+ config: ({ processor, ...options }) => {
120
+ const config = createConfig(options);
121
+ // TODO(burdon): This could be buggy since the behavior is not reentrant.
122
+ const processors = processor ? config.processors : log._config.processors;
123
+ log._config = { ...config, processors };
124
+ return log;
125
+ },
126
+
127
+ /**
128
+ * Adds a processor to the logger.
129
+ */
130
+ addProcessor: (processor) => {
131
+ if (log._config.processors.filter((p) => p === processor).length === 0) {
132
+ log._config.processors.push(processor);
133
+ }
134
+
135
+ return () => {
136
+ log._config.processors = log._config.processors.filter((p) => p !== processor);
137
+ };
138
+ },
139
+
140
+ trace: (...params) => processLog(LogLevel.TRACE, ...params),
141
+ debug: (...params) => processLog(LogLevel.DEBUG, ...params),
142
+ verbose: (...params) => processLog(LogLevel.VERBOSE, ...params),
143
+ info: (...params) => processLog(LogLevel.INFO, ...params),
144
+ warn: (...params) => processLog(LogLevel.WARN, ...params),
145
+ error: (...params) => processLog(LogLevel.ERROR, ...params),
146
+ catch: (error, context, meta) => processLog(LogLevel.ERROR, undefined, context, meta, error),
147
+
148
+ method: createMethodLogDecorator(log),
149
+ function: createFunctionLogDecorator(log),
150
+
151
+ break: () => log.info('-'.repeat(80)),
152
+ stack: (message, context, meta) => {
153
+ return processLog(LogLevel.INFO, `${message ?? 'Stack Dump'}\n${getFormattedStackTrace()}`, context, meta);
154
+ },
155
+ });
156
+
106
157
  return log;
107
158
  };
108
159
 
109
160
  /**
110
161
  * Global logging function.
111
162
  */
112
- export const log: Log = ((globalThis as any).dx_log ??= createLog());
163
+ export const log: Log = ((globalThis as any).DX_LOG ??= createLog());
113
164
 
114
165
  const start = Date.now();
115
166
  let last = start;
@@ -129,12 +180,4 @@ export const debug = (label?: any, args?: any) => {
129
180
  last = Date.now();
130
181
  };
131
182
 
132
- /**
133
- * Accessible from browser console.
134
- */
135
- declare global {
136
- // eslint-disable-next-line camelcase
137
- const dx_log: Log;
138
- }
139
-
140
183
  const getFormattedStackTrace = () => new Error().stack!.split('\n').slice(3).join('\n');
package/src/options.ts CHANGED
@@ -7,37 +7,51 @@ import defaultsDeep from 'lodash.defaultsdeep';
7
7
  import { type LogConfig, type LogFilter, LogLevel, type LogOptions, LogProcessorType, levels } from './config';
8
8
  import { type LogProcessor } from './context';
9
9
  import { loadOptions } from './platform';
10
- import { CONSOLE_PROCESSOR, DEBUG_PROCESSOR, BROWSER_PROCESSOR } from './processors';
10
+ import { BROWSER_PROCESSOR, CONSOLE_PROCESSOR, DEBUG_PROCESSOR } from './processors';
11
11
 
12
12
  /**
13
13
  * Processor variants.
14
14
  */
15
- export const processors: { [index: string]: LogProcessor } = {
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 IS_BROWSER = typeof window !== 'undefined' || typeof navigator !== 'undefined';
21
+ const browser = typeof window !== 'undefined' || typeof navigator !== 'undefined';
22
22
 
23
- export const DEFAULT_PROCESSORS = [IS_BROWSER ? BROWSER_PROCESSOR : CONSOLE_PROCESSOR];
23
+ export const DEFAULT_PROCESSORS = [browser ? BROWSER_PROCESSOR : CONSOLE_PROCESSOR];
24
24
 
25
+ const parseLogLevel = (level: string, defValue = LogLevel.WARN) => levels[level.toLowerCase()] ?? defValue;
26
+
27
+ /**
28
+ * @internal
29
+ */
25
30
  export const parseFilter = (filter: string | string[] | LogLevel): LogFilter[] => {
26
31
  if (typeof filter === 'number') {
27
32
  return [{ level: filter }];
28
33
  }
29
34
 
30
- const parseLogLevel = (level: string, defValue = LogLevel.WARN) => levels[level.toLowerCase()] ?? defValue;
31
-
32
35
  const lines = typeof filter === 'string' ? filter.split(/,\s*/) : filter;
33
36
  return lines.map((filter) => {
34
37
  const [pattern, level] = filter.split(':');
35
- return level ? { level: parseLogLevel(level), pattern } : { level: parseLogLevel(pattern) };
38
+ return level
39
+ ? {
40
+ level: parseLogLevel(level),
41
+ pattern,
42
+ }
43
+ : {
44
+ level: parseLogLevel(pattern),
45
+ };
36
46
  });
37
47
  };
38
48
 
39
- export const getConfig = (options?: LogOptions): LogConfig => {
40
- const nodeOptions: LogOptions | undefined =
49
+ /**
50
+ * @internal
51
+ */
52
+ export const createConfig = (options?: LogOptions): LogConfig => {
53
+ // Node only.
54
+ const envOptions: LogOptions | undefined =
41
55
  'process' in globalThis
42
56
  ? {
43
57
  file: process!.env.LOG_CONFIG,
@@ -46,12 +60,12 @@ export const getConfig = (options?: LogOptions): LogConfig => {
46
60
  }
47
61
  : undefined;
48
62
 
49
- const mergedOptions: LogOptions = defaultsDeep({}, loadOptions(nodeOptions?.file), nodeOptions, options);
63
+ const mergedOptions: LogOptions = defaultsDeep({}, loadOptions(envOptions?.file), envOptions, options);
50
64
  return {
51
65
  options: mergedOptions,
52
66
  filters: parseFilter(mergedOptions.filter ?? LogLevel.INFO),
53
67
  captureFilters: parseFilter(mergedOptions.captureFilter ?? LogLevel.WARN),
54
- processors: mergedOptions.processor ? [processors[mergedOptions.processor]] : DEFAULT_PROCESSORS,
68
+ processors: mergedOptions.processor ? [processors[mergedOptions.processor]] : [...DEFAULT_PROCESSORS],
55
69
  prefix: mergedOptions.prefix,
56
70
  };
57
71
  };
@@ -2,9 +2,10 @@
2
2
  // Copyright 2022 DXOS.org
3
3
  //
4
4
 
5
- import yaml from 'js-yaml';
6
5
  import fs from 'node:fs';
7
6
 
7
+ import yaml from 'js-yaml';
8
+
8
9
  import { type LogOptions } from '../../config';
9
10
 
10
11
  /**
@@ -5,7 +5,7 @@
5
5
  import { getDebugName, safariCheck } from '@dxos/util';
6
6
 
7
7
  import { LogLevel } from '../config';
8
- import { getContextFromEntry, type LogProcessor, shouldLog } from '../context';
8
+ import { type LogProcessor, getContextFromEntry, shouldLog } from '../context';
9
9
 
10
10
  const getRelativeFilename = (filename: string) => {
11
11
  // TODO(burdon): Hack uses "packages" as an anchor (pre-parse NX?)
@@ -66,12 +66,16 @@ const APP_BROWSER_PROCESSOR: LogProcessor = (config, entry) => {
66
66
  args.push(`%c${processPrefix}${scopeName}`, 'color:#C026D3;font-weight:bold');
67
67
  }
68
68
 
69
- args.push(entry.message);
69
+ if (entry.message) {
70
+ args.push(entry.message);
71
+ }
70
72
 
71
73
  const context = getContextFromEntry(entry);
72
74
  if (context) {
73
75
  if (Object.keys(context).length === 1 && 'error' in context) {
74
76
  args.push(context.error);
77
+ } else if (Object.keys(context).length === 1 && 'err' in context) {
78
+ args.push(context.err);
75
79
  } else {
76
80
  args.push(context);
77
81
  }