Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion packages/store/src/sqlite/nodejs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ async function initDB(
const dbPath = await getDBFilename(dbFilename);
logger.debug('dbPath:', dbPath);
return new Sqlite(dbPath, {
verbose: verbose ? (...args) => logger.info(...args) : undefined,
verbose: (verbose ? logger.info.bind(logger) : undefined) as
| ((...args: unknown[]) => void)
| undefined,
});
}

Expand Down
2 changes: 1 addition & 1 deletion packages/store/src/sqlite/wasm.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ describe('makeSQLKernelDatabase', () => {
const consoleSpy = vi.spyOn(console, 'log');
await makeSQLKernelDatabase({ verbose: true });
expect(consoleSpy).toHaveBeenCalledWith(
'[sqlite]',
['[sqlite]'],
'Initializing kernel store',
);
});
Expand Down
1 change: 1 addition & 0 deletions packages/utils/src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ describe('index', () => {
'isPrimitive',
'isTypedArray',
'isTypedObject',
'Logger',
'makeCounter',
'makeLogger',
'stringify',
Expand Down
3 changes: 1 addition & 2 deletions packages/utils/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
export type { Logger } from './logger.ts';
export { makeLogger } from './logger.ts';
export { Logger, makeLogger } from './logger.ts';
export { delay, makeCounter } from './misc.ts';
export { stringify } from './stringify.ts';
export type { ExtractGuardType, PromiseCallbacks, TypeGuard } from './types.ts';
Expand Down
181 changes: 165 additions & 16 deletions packages/utils/src/logger.test.ts
Original file line number Diff line number Diff line change
@@ -1,51 +1,200 @@
import { describe, it, expect, vi } from 'vitest';

import { makeLogger } from './logger.ts';
import {
consoleTransport,
DEFAULT_OPTIONS,
Logger,
makeLogger,
mergeOptions,
} from './logger.ts';
import type { LoggerOptions, LogLevel } from './logger.ts';

describe('makeLogger', () => {
const consoleMethod = ['log', 'debug', 'info', 'warn', 'error'] as const;
const consoleMethod = ['log', 'debug', 'info', 'warn', 'error'] as const;

describe('Logger', () => {
it.each(consoleMethod)('has method %j', (method) => {
const testLogger = makeLogger('test');
const testLogger = new Logger({ tags: ['test'] });
expect(testLogger).toHaveProperty(method);
expect(testLogger[method]).toBeTypeOf('function');
});

it.each(consoleMethod)(
'calls %j with the provided label followed by a single argument',
'calls %j with the provided tags followed by a single argument',
(method) => {
const methodSpy = vi.spyOn(console, method);
const testLogger = makeLogger('test');
const tags = ['test'];
const testLogger = new Logger({ tags });
testLogger[method]('foo');
expect(methodSpy).toHaveBeenCalledWith('test', 'foo');
expect(methodSpy).toHaveBeenCalledWith(tags, 'foo');
},
);

it.each(consoleMethod)(
'calls %j with the provided label followed by multiple arguments',
'calls %j with the provided tags followed by multiple arguments',
(method) => {
const methodSpy = vi.spyOn(console, method);
const testLogger = makeLogger('test');
const tags = ['test'];
const testLogger = new Logger({ tags });
testLogger[method]('foo', { bar: 'bar' });
expect(methodSpy).toHaveBeenCalledWith('test', 'foo', { bar: 'bar' });
expect(methodSpy).toHaveBeenCalledWith(tags, 'foo', { bar: 'bar' });
},
);

it.each(consoleMethod)(
'calls %j with the provided label when given no argument',
'calls %j with the provided tags when given no argument',
(method) => {
const methodSpy = vi.spyOn(console, method);
const testLogger = makeLogger('test');
const tags = ['test'];
const testLogger = new Logger({ tags });
testLogger[method]();
expect(methodSpy).toHaveBeenCalledWith('test');
expect(methodSpy).toHaveBeenCalledWith(tags);
},
);

it('can be nested', () => {
const consoleSpy = vi.spyOn(console, 'log');
const vatLogger = makeLogger('[vat 0x01]');
const subLogger = makeLogger('(process)', vatLogger);
const vatLogger = new Logger({ tags: ['vat 0x01'] });
const subLogger = vatLogger.subLogger({ tags: ['(process)'] });
subLogger.log('foo');
expect(consoleSpy).toHaveBeenCalledWith('[vat 0x01]', '(process)', 'foo');
expect(consoleSpy).toHaveBeenCalledWith(['vat 0x01', '(process)'], 'foo');
});

it('omits tagline when no tags are provided', () => {
const consoleSpy = vi.spyOn(console, 'log');
const logger = new Logger();
logger.log('foo');
expect(consoleSpy).toHaveBeenCalledWith('foo');
});

it('passes objects directly in the data field', () => {
const consoleSpy = vi.spyOn(console, 'log');
const logger = new Logger({ tags: ['test'] });
const message = 'foo';
const data = { bar: 'bar' };
logger.log(message, data);
expect(consoleSpy).toHaveBeenCalledWith(['test'], message, data);
});

describe('subLogger', () => {
it('creates a new logger with the merged options', () => {
const consoleSpy = vi.spyOn(console, 'log');
const logger = new Logger({ tags: ['test'] });
const subLogger = logger.subLogger({ tags: ['sub'] });
expect(subLogger).toBeInstanceOf(Logger);
subLogger.log('foo');
expect(consoleSpy).toHaveBeenCalledWith(['test', 'sub'], 'foo');
});

it('works with no options', () => {
const consoleSpy = vi.spyOn(console, 'log');
const logger = new Logger({ tags: ['test'] });
const subLogger = logger.subLogger();
expect(subLogger).toBeInstanceOf(Logger);
subLogger.log('foo');
expect(consoleSpy).toHaveBeenCalledWith(['test'], 'foo');
});
});
});

describe('consoleTransport', () => {
it.each(consoleMethod)('logs to the console with method %j', (method) => {
const consoleSpy = vi.spyOn(console, method);
const logger = new Logger({ tags: ['test'] });
logger[method]('foo');
expect(consoleSpy).toHaveBeenCalledWith(['test'], 'foo');
});

it.each(consoleMethod)('default data is an empty array for %j', (level) => {
const consoleSpy = vi.spyOn(console, level);
const entry = { tags: ['test'], level, message: 'foo' };
consoleTransport(entry);
expect(consoleSpy).toHaveBeenCalledWith(['test'], 'foo');
});

it('does not log when the level is silent', () => {
const consoleMethodSpies = consoleMethod.map((method) =>
vi.spyOn(console, method),
);
consoleTransport({ tags: ['test'], level: 'silent', message: 'foo' });
consoleMethodSpies.forEach((spy) => expect(spy).not.toHaveBeenCalled());
});
});

describe('mergeOptions', () => {
it.each([
{ left: ['test'], right: ['sub'], result: ['test', 'sub'] },
{ left: ['test', 'test'], right: ['sub'], result: ['test', 'sub'] },
{
left: ['test', 'fizz'],
right: ['test', 'buzz'],
result: ['test', 'fizz', 'buzz'],
},
])('merges tags as expected: $left and $right', ({ left, right, result }) => {
const options = mergeOptions({ tags: left }, { tags: right });
expect(options.tags).toStrictEqual(result);
});

it('defaults to the default options', () => {
const options = mergeOptions();
expect(options).toStrictEqual(DEFAULT_OPTIONS);
});

const transportA = vi.fn();
const transportB = vi.fn();

it.each([
{ left: { transports: [] }, right: { transports: [] }, result: [] },
{
left: { transports: [transportA] },
right: { transports: [] },
result: [transportA],
},
{
left: { transports: [transportA] },
right: { transports: [transportA] },
result: [transportA],
},
{
left: { transports: [transportA] },
right: { transports: [transportB] },
result: [transportA, transportB],
},
])(
'merges transports as expected: $left and $right',
({ left, right, result }) => {
const options = mergeOptions(left, right);
expect(options.transports).toStrictEqual(result);
},
);

it.each([
{ left: { level: 'warn' }, right: { level: 'error' }, result: 'error' },
{ left: { level: undefined }, right: { level: 'warn' }, result: 'warn' },
{ left: { level: 'info' }, right: {}, result: 'info' },
] as { left: LoggerOptions; right: LoggerOptions; result: LogLevel }[])(
'merges levels as expected: $left and $right',
({ left, right, result }) => {
const options = mergeOptions(
{ ...left, transports: [] },
{ ...right, transports: [] },
);
expect(options.level).toBe(result);
},
);
});

describe('makeLogger', () => {
it('creates a new logger from a label and a parent logger', () => {
const logger = new Logger({ tags: ['test'] });
const subLogger = makeLogger('sub', logger);
expect(subLogger).toBeInstanceOf(Logger);
});

it('creates a new logger from a label', () => {
const logSpy = vi.spyOn(console, 'log');
const logger = makeLogger('test');
expect(logger).toBeInstanceOf(Logger);
logger.log('foo');
expect(logSpy).toHaveBeenCalledWith(['test'], 'foo');
});
});
Loading
Loading