Skip to content

Commit c16febd

Browse files
authored
Make Logger obey log level settings (#703)
With this change, log messages can be suppressed if they are at a lower level than whatever the current log level setting is (i.e., if the setting is `'error'`, a call to `log.debug(...)` will output nothing). Currently the log level is set on on the console transport via an argument to `makeConsoleTransport()`. For backwards compatibility until we decide otherwise, if unspecified the level defaults to `'debug'`, i.e., always print all log messages. However, now that we can obey the level setting, we should probably consider changing the default. We probably should also consider adding an `'off'` level that suppresses all log output, but for weird complicated reasons having to do with conflation of the `console` API with the level name strings that's tricky. (I'd argue that the default level should either be `'error'` or `'off'`.) Eventually we will probably also want to read the setting from an environment variable, but that's an additional bundle of complication that deserves its own issue. Closes #679
1 parent fccb69f commit c16febd

File tree

17 files changed

+114
-78
lines changed

17 files changed

+114
-78
lines changed

packages/kernel-agents/test/e2e/agents.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import '@ocap/repo-tools/test-utils/mock-endoify';
22

3-
import { consoleTransport, Logger } from '@metamask/logger';
3+
import { makeConsoleTransport, Logger } from '@metamask/logger';
44
import { OllamaNodejsService } from '@ocap/kernel-language-model-service/ollama/nodejs';
55
import { fetchMock } from '@ocap/repo-tools/test-utils/fetch-mock';
66
import {
@@ -25,7 +25,7 @@ import { filterTransports, randomLetter } from '../utils.ts';
2525

2626
const logger = new Logger({
2727
tags: ['test'],
28-
transports: [filterTransports(consoleTransport)],
28+
transports: [filterTransports(makeConsoleTransport())],
2929
});
3030

3131
const makeJsonAgentWithMathCapabilities = (args: MakeAgentArgs) =>

packages/kernel-browser-runtime/test/build-tests.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { runTests } from '@ocap/repo-tools/build-utils/test';
22
import path from 'node:path';
33

4-
// eslint-disable-next-line n/no-unsupported-features/node-builtins
54
const outDir = path.resolve(import.meta.dirname, '../dist/static');
65

76
const untransformedFiles = [

packages/kernel-browser-runtime/vite.config.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,6 @@ const staticCopyTargets: readonly (string | Target)[] = [
2929
'../../kernel-shims/dist/endoify.js',
3030
];
3131

32-
// We will only run this where it's available, but ESLint doesn't know that
33-
// eslint-disable-next-line n/no-unsupported-features/node-builtins
3432
const sourceDir = path.resolve(import.meta.dirname, 'src');
3533
const buildDir = path.resolve(sourceDir, '../dist/static');
3634

packages/kernel-test/src/utils.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@
33

44
import type { KernelDatabase } from '@metamask/kernel-store';
55
import { stringify, waitUntilQuiescent } from '@metamask/kernel-utils';
6-
import { Logger, makeArrayTransport, consoleTransport } from '@metamask/logger';
6+
import {
7+
Logger,
8+
makeArrayTransport,
9+
makeConsoleTransport,
10+
} from '@metamask/logger';
711
import type { LogEntry } from '@metamask/logger';
812
import { Kernel, kunser } from '@metamask/ocap-kernel';
913
import type { ClusterConfig, PlatformServices } from '@metamask/ocap-kernel';
@@ -193,7 +197,7 @@ export function logDatabase(
193197
export const makeTestLogger = (): { logger: Logger; entries: LogEntry[] } => {
194198
const entries: LogEntry[] = [];
195199
const logger = new Logger({
196-
transports: [consoleTransport, makeArrayTransport(entries)],
200+
transports: [makeConsoleTransport(), makeArrayTransport(entries)],
197201
});
198202
return { logger, entries };
199203
};

packages/logger/src/constants.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,8 @@
1-
export const logLevels = ['debug', 'info', 'log', 'warn', 'error'] as const;
1+
export const logLevels = {
2+
debug: 1,
3+
info: 2,
4+
log: 3,
5+
warn: 4,
6+
error: 5,
7+
} as const;
28
export const TOKEN_UNDEFINED = 'e790f877-6987-4b06-9ce2-5fc889d5b9ac';

packages/logger/src/index.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ describe('index', () => {
77
expect(Object.keys(indexModule).sort()).toStrictEqual(
88
expect.arrayContaining([
99
'Logger',
10-
'consoleTransport',
1110
'makeArrayTransport',
11+
'makeConsoleTransport',
1212
'makeStreamTransport',
1313
'splitLoggerStream',
1414
]),

packages/logger/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
export { Logger } from './logger.ts';
22
export {
3-
consoleTransport,
3+
makeConsoleTransport,
44
makeArrayTransport,
55
makeStreamTransport,
66
} from './transports.ts';

packages/logger/src/logger.test.ts

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,24 @@ import type { DuplexStream } from '@metamask/streams';
22
import { delay } from '@ocap/repo-tools/test-utils';
33
import { describe, it, expect, vi } from 'vitest';
44

5+
import { logLevels } from './constants.ts';
56
import { Logger } from './logger.ts';
67
import { lser } from './stream.ts';
78
import type { LogMessage } from './stream.ts';
8-
import { consoleTransport } from './transports.ts';
9+
import { makeConsoleTransport } from './transports.ts';
910

1011
const consoleMethod = ['log', 'debug', 'info', 'warn', 'error'] as const;
11-
const transports = [consoleTransport];
12+
const transports = [makeConsoleTransport()];
1213

1314
describe('Logger', () => {
1415
it.each([
1516
['no arguments', undefined],
1617
['an empty object', {}],
1718
['a string tag', 'test'],
18-
['an options bag', { tags: ['test'], transports: [consoleTransport] }],
19+
[
20+
'an options bag',
21+
{ tags: ['test'], transports: [makeConsoleTransport()] },
22+
],
1923
])('can be constructed with $description', (_description, options) => {
2024
const logger = new Logger(options);
2125
expect(logger).toBeInstanceOf(Logger);
@@ -113,6 +117,27 @@ describe('Logger', () => {
113117
});
114118
});
115119

120+
describe('logging level limits output', () => {
121+
consoleMethod.forEach((level) => {
122+
it(`logging at level ${level}`, () => {
123+
const testLogger = new Logger({
124+
transports: [makeConsoleTransport(level)],
125+
});
126+
consoleMethod.forEach((method) => {
127+
const consoleSpy = vi.spyOn(console, method);
128+
testLogger[method](`foo ${method}`);
129+
if (logLevels[level] <= logLevels[method]) {
130+
// eslint-disable-next-line vitest/no-conditional-expect
131+
expect(consoleSpy).toHaveBeenCalledWith(`foo ${method}`);
132+
} else {
133+
// eslint-disable-next-line vitest/no-conditional-expect
134+
expect(consoleSpy).not.toHaveBeenCalled();
135+
}
136+
});
137+
});
138+
});
139+
});
140+
116141
describe('injectStream', () => {
117142
it('calls drain on the provided stream', () => {
118143
const logger = new Logger();

packages/logger/src/logger.ts

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,6 @@ export class Logger {
8989
* logger's tag.
9090
* @param options.transports - The transports, which deliver the log messages
9191
* to the appropriate destination.
92-
* @param options.level - The log level for the logger, used as a default
93-
* argument for the transports.
9492
* @param options.tags - The tags for the logger, which are accumulated by
9593
* sub-loggers and passed to the transports.
9694
*/
@@ -99,16 +97,14 @@ export class Logger {
9997

10098
// Create aliases for the log methods, allowing them to be used in a
10199
// manner similar to the console object.
102-
const bind = (level: LogLevel): LogMethod =>
103-
harden(
104-
this.#dispatch.bind(this, {
105-
...this.#options,
106-
level,
107-
}),
100+
const bind = (level: LogLevel): LogMethod => {
101+
return harden(
102+
this.#dispatch.bind(this, { ...this.#options }, level),
108103
) as LogMethod;
109-
this.log = bind('log');
104+
};
110105
this.debug = bind('debug');
111106
this.info = bind('info');
107+
this.log = bind('log');
112108
this.warn = bind('warn');
113109
this.error = bind('error');
114110
}
@@ -146,13 +142,13 @@ export class Logger {
146142
const { level, tags, message, data } = lunser(args);
147143
const logArgs: LogArgs =
148144
message === undefined ? [] : [message, ...(data ?? [])];
149-
this.#dispatch({ level, tags }, ...logArgs);
145+
this.#dispatch({ tags }, level, ...logArgs);
150146
})
151147
.catch((problem) => onError?.(problem));
152148
}
153149

154-
#dispatch(options: LoggerOptions, ...args: LogArgs): void {
155-
const { transports, level, tags } = mergeOptions(this.#options, options);
150+
#dispatch(options: LoggerOptions, level: LogLevel, ...args: LogArgs): void {
151+
const { transports, tags } = mergeOptions(this.#options, options);
156152
const [message, ...data] = args;
157153
const entry: LogEntry = harden({ level, tags, message, data });
158154
transports.forEach((transport) => transport(entry));

packages/logger/src/options.test.ts

Lines changed: 10 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,29 @@
11
import { describe, expect, it, vi } from 'vitest';
22

33
import { DEFAULT_OPTIONS, mergeOptions, parseOptions } from './options.ts';
4-
import type { LoggerOptions, LogLevel, Transport } from './types.ts';
4+
import type { Transport } from './types.ts';
55

66
const mocks = vi.hoisted(() => ({
7-
consoleTransport: vi.fn(),
7+
makeConsoleTransport: vi.fn(),
88
}));
99

1010
vi.mock('./transports.ts', () => ({
11-
consoleTransport: mocks.consoleTransport,
11+
makeConsoleTransport: mocks.makeConsoleTransport,
1212
}));
1313

1414
describe('parseOptions', () => {
1515
it('parses an undefined options bag', () => {
1616
const options = parseOptions(undefined);
17-
expect(options).toStrictEqual({ transports: [mocks.consoleTransport] });
17+
expect(options).toStrictEqual({
18+
transports: [mocks.makeConsoleTransport()],
19+
});
1820
});
1921

2022
it('parses an empty options bag', () => {
2123
const options = parseOptions({});
22-
expect(options).toStrictEqual({ transports: [mocks.consoleTransport] });
24+
expect(options).toStrictEqual({
25+
transports: [mocks.makeConsoleTransport()],
26+
});
2327
});
2428

2529
it('parses an options bag', () => {
@@ -38,7 +42,7 @@ describe('parseOptions', () => {
3842
const options = parseOptions('test');
3943
expect(options).toStrictEqual({
4044
tags: ['test'],
41-
transports: [mocks.consoleTransport],
45+
transports: [mocks.makeConsoleTransport()],
4246
});
4347
});
4448

@@ -100,19 +104,4 @@ describe('mergeOptions', () => {
100104
]);
101105
},
102106
);
103-
104-
it.each([
105-
{ left: { level: 'warn' }, right: { level: 'error' }, result: 'error' },
106-
{ left: { level: undefined }, right: { level: 'warn' }, result: 'warn' },
107-
{ left: { level: 'info' }, right: {}, result: 'info' },
108-
] as { left: LoggerOptions; right: LoggerOptions; result: LogLevel }[])(
109-
'merges levels as expected: $left and $right',
110-
({ left, right, result }) => {
111-
const options = mergeOptions(
112-
{ ...left, transports: [] },
113-
{ ...right, transports: [] },
114-
);
115-
expect(options.level).toBe(result);
116-
},
117-
);
118107
});

0 commit comments

Comments
 (0)