Skip to content

Commit

Permalink
🚧 WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
maraisr committed May 22, 2024
1 parent cf427ae commit ecb8f6d
Show file tree
Hide file tree
Showing 8 changed files with 45 additions and 62 deletions.
7 changes: 2 additions & 5 deletions example.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class User {
function example(oe: OnEmitFn) {
let user = new User();

let scope = diary('example', oe);
let scope = diary(oe);

scope('log', 'this is a log message');
scope('info', 'this is an info message');
Expand All @@ -31,9 +31,6 @@ function example(oe: OnEmitFn) {

scope('info', 'this {user} exists', { user });
scope('info', 'we call that user {name} with {id}', user);

scope = diary(oe);
scope('debug', 'sometimes we do not want a scope name');
}

console.log('============ PRETTY ============\n');
Expand All @@ -47,7 +44,7 @@ console.log('NOTE: we omit the level, as that is presented in the DevTools.\n');
example(browser);

console.log('\n\n============ CLEF (custom) ============\n');
example((name, level, message, props) => {
example((level, message, props) => {
let event = {
'@t': new Date().toISOString(),
'@l': level,
Expand Down
33 changes: 12 additions & 21 deletions src/mod.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Deno.test('api', () => {
assertInstanceOf(lib.diary, Function);

let emit = spy<lib.OnEmitFn>();
let log = lib.diary('test', emit);
let log = lib.diary(emit);
assertInstanceOf(log, Function);
log('info', 'hello {name}', { name: 'world' });
// @ts-expect-error - wrong type "name" should be "foo"
Expand All @@ -17,59 +17,50 @@ Deno.test('api', () => {

Deno.test('calls onEmit', () => {
let emit = spy();
let log = lib.diary('test', emit);
let log = lib.diary(emit);
log('info', 'hello', { name: 'world' });
assertSpyCall(emit, 0, {
args: ['test', 'info', 'hello', { name: 'world' }],
args: ['info', 'hello', { name: 'world' }],
});
});

Deno.test('calls onEmit for every log', () => {
let emit = spy();
let log = lib.diary('test', emit);
let log = lib.diary(emit);
log('info', 'hello', { name: 'world' });
log('debug', 'hello {phrase}', { phrase: 'world' });
assertSpyCall(emit, 0, {
args: ['test', 'info', 'hello', { name: 'world' }],
args: ['info', 'hello', { name: 'world' }],
});
assertSpyCall(emit, 1, {
args: ['test', 'debug', 'hello {phrase}', { phrase: 'world' }],
args: ['debug', 'hello {phrase}', { phrase: 'world' }],
});
});

Deno.test('calls with correct level', () => {
let emit = spy();
let log = lib.diary('test', emit);
let log = lib.diary(emit);

let i = 0;
for (let level of ['log', 'debug', 'info', 'warn', 'error', 'fatal']) {
log(level as Level, 'hello', { name: 'world' });
assertSpyCall(emit, i++, {
args: ['test', level, 'hello', { name: 'world' }],
args: [level, 'hello', { name: 'world' }],
});
}
});

Deno.test('onEmit retains its `this` context', () => {
let emit = spy();
let log = lib.diary('test', emit);
log('info', 'hello', { name: 'world' });
assertSpyCall(emit, 0, {
self: emit,
});
});

Deno.test('should allow anything as prop value', () => {
class Test {}

let t = new Test();

let emit = spy();
let log = lib.diary('test', emit);
let log = lib.diary(emit);
log('info', 'hello {phrase}', { phrase: t });

assertSpyCall(emit, 0, {
args: ['test', 'info', 'hello {phrase}', { phrase: t }],
args: ['info', 'hello {phrase}', { phrase: t }],
});
});

Expand All @@ -81,7 +72,7 @@ Deno.test('should allow anything as prop', () => {
let t = new Test();

let emit = spy();
let log = lib.diary('test', emit);
let log = lib.diary(emit);
log('info', 'hello {phrase}', t);

log(
Expand All @@ -92,6 +83,6 @@ Deno.test('should allow anything as prop', () => {
);

assertSpyCall(emit, 0, {
args: ['test', 'info', 'hello {phrase}', t],
args: ['info', 'hello {phrase}', t],
});
});
18 changes: 5 additions & 13 deletions src/mod.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Pretty, Props, Dict } from './util.ts';
import type { Dict, Pretty, Props } from './util.ts';

export type Level = 'log' | 'debug' | 'info' | 'warn' | 'error' | 'fatal';

Expand All @@ -8,24 +8,16 @@ export interface OnEmitFn {
(level: Level, message: string, props?: object | undefined): any;
}

export interface LogFn<Ctx extends Dict> {
<const T extends string, Params extends Pretty<Omit<Props<T>, keyof Ctx>>> (
export interface LogFn<Ctx extends object> {
<const T extends string, Params extends Pretty<Omit<Props<T>, keyof Ctx>>>(
level: Level,
template: T,
...args: keyof Params extends never ? [] : Params extends object ? [properties: Params] : []
): void;
}

export function diary<Ctx extends Dict>(onEmit: OnEmitFn, ctx?: Ctx): LogFn<Ctx> {
export function diary<Ctx extends object>(onEmit: OnEmitFn, ctx?: Ctx): LogFn<Ctx> {
return function log(level, message, props) {
onEmit(level, message, {...ctx, ...props});
onEmit(level, message, ctx, props);
} as LogFn<Ctx>;
}

let s = diary(() => {}, { foo: 'bar'});
s('info', 'hello');
s('info', 'hello {foo}');
s('info', 'hello {foo} {phrase}', {
phrase: 'world',
});

13 changes: 5 additions & 8 deletions src/output.console.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ const LEVELS = {
fatal: '✗ fatal ' as const,
} as const;

function log(out: string, name: string | undefined, level: Level, message: string, props?: object) {
if (name) out += `[${name}] `;
function log(out: string, level: Level, message: string, props?: object) {
//if (name) out += `[${name}] `;

let args: unknown[] = [];
out += interpolate(message, props, (value) => {
Expand All @@ -25,25 +25,22 @@ function log(out: string, name: string | undefined, level: Level, message: strin
}

export function browser(
name: string | undefined,
level: Level,
message: string,
props?: object | undefined,
) {
log('', name, level, message, props);
log('', level, message, props);
}

export function plain(
name: string | undefined,
level: Level,
message: string,
props?: object | undefined,
) {
log(LEVELS[level], name, level, message, props);
log(LEVELS[level], level, message, props);
}

export function pretty(
name: string | undefined,
level: Level,
message: string,
props?: object | undefined,
Expand All @@ -55,5 +52,5 @@ export function pretty(
if (level === 'error') l = red(l);
if (level === 'fatal') l = bold(red(l));

log(l, name, level, message, props);
log(l, level, message, props);
}
6 changes: 3 additions & 3 deletions src/stream.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Deno.test('api', () => {

Deno.test('streams', async () => {
let events: lib.LogEvent[] = [];
let log = stream.diary('test', async (stream) => {
let log = stream.diary(async (stream) => {
assertInstanceOf(stream, ReadableStream);
for await (let event of stream) events.push(event);
});
Expand All @@ -20,7 +20,7 @@ Deno.test('streams', async () => {
await delay(1);

assertEquals(events, [
['test', 'info', 'hello', { name: 'world' }],
['test', 'debug', 'hello', { name: 'world' }],
['info', 'hello', { name: 'world' }],
['debug', 'hello', { name: 'world' }],
]);
});
7 changes: 5 additions & 2 deletions src/stream.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import * as lib from './mod.ts';

export function diary(name: string, cb: (r: ReadableStream<lib.LogEvent>) => any): lib.LogFn {
export function diary<Ctx extends object>(
cb: (r: ReadableStream<lib.LogEvent>) => any,
ctx?: Ctx,
): lib.LogFn<Ctx> {
let stream = new TransformStream<lib.LogEvent, lib.LogEvent>();
let writer = stream.writable.getWriter();
cb(stream.readable);
return lib.diary(
name,
function () {
writer.write(Array.from(arguments) as lib.LogEvent);
},
ctx,
);
}
12 changes: 6 additions & 6 deletions src/using.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,16 @@ Deno.test('calls onEmit', () => {
let emit = spy();

{
using log = lib.diary('test', emit);
using log = lib.diary(emit);
log('info', 'hello {name}', { name: 'world' });
log('debug', 'hello {name}', { name: 'world' });
}

assertSpyCall(emit, 0, {
args: [
[
['test', 'info', 'hello {name}', { name: 'world' }],
['test', 'debug', 'hello {name}', { name: 'world' }],
['info', 'hello {name}', { name: 'world' }],
['debug', 'hello {name}', { name: 'world' }],
],
],
});
Expand All @@ -30,16 +30,16 @@ Deno.test('allows async disposal', async () => {
let emit = spy(() => delay(1));

{
await using log = lib.diary('test', emit);
await using log = lib.diary(emit);
log('info', 'hello {name}', { name: 'world' });
log('debug', 'hello {name}', { name: 'world' });
}

assertSpyCall(emit, 0, {
args: [
[
['test', 'info', 'hello {name}', { name: 'world' }],
['test', 'debug', 'hello {name}', { name: 'world' }],
['info', 'hello {name}', { name: 'world' }],
['debug', 'hello {name}', { name: 'world' }],
],
],
});
Expand Down
11 changes: 7 additions & 4 deletions src/using.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
import * as lib from './mod.ts';

export interface LogFn extends lib.LogFn {
export interface LogFn<Ctx extends object> extends lib.LogFn<Ctx> {
[Symbol.dispose](): void;
}

export function diary(name: string, flush: (events: lib.LogEvent[]) => any): LogFn {
export function diary<Ctx extends object>(
flush: (events: lib.LogEvent[]) => any,
ctx?: Ctx,
): LogFn<Ctx> {
const events: lib.LogEvent[] = [];

let log = lib.diary(
name,
function () {
events.push(Array.from(arguments) as lib.LogEvent);
},
) as LogFn;
ctx,
) as LogFn<Ctx>;
log[Symbol.dispose] = function () {
return flush(events);
};
Expand Down

0 comments on commit ecb8f6d

Please sign in to comment.