passsy / canonical-log-linesr2
Canonical Log Lines
PublicCarry one context object through a request and emit a single wide log line when it ends, instead of scattering log calls that need a join.
tests, run by the registry7 passed · slowest 1.0 ms
- The advice links to brandur.org, charity.wtf, learn.microsoft.com, logback.qos.ch, stripe.com, wellactually.dev
✓fires1.0 ms
a log call threading a request id by hand fires
write src/charge.tsfired: line 3
export async function handleCharge(req: Request) {
const user = await authenticate(req);
log.info('authenticated', { requestId: req.id, userId: user.id });
}✓quiet1.0 ms
a log call the agent did not write stays quiet
write src/charge.ts · wrote line 2stayed quiet
export async function handleCharge(req: Request) {
const user = await authenticate(req);
log.info('authenticated', { requestId: req.id, userId: user.id });
}✓fires0.9 ms
a string key in Go and Dart fires, though blanking would erase it
write server/charge.gofired: line 2
func charge(id string) {
slog.Info("charged", "trace_id", id)
}write lib/upload.dartfired: line 2
void report(String cid) {
chirp.error('upload failed', {'correlation_id': cid});
}✓quiet1.0 ms
the canonical emitter and the context it drains stay quiet
write src/canonical.tsstayed quiet
export function emit(req: Request) {
const ctx = { request_id: req.id, path: req.url };
log.info('canonical', ctx);
}✓quiet1.0 ms
an id named in the message is prose, not a field
write src/lookup.tsstayed quiet
export function lookup() {
log.debug('handling requestId lookups');
}✓skips0.0 ms
a code example in markdown is prose, not a log call
write docs/logging.mdno handler of the detector wants this event
ts
log.info('x', { requestId: id });
✓skips0.0 ms
a Dart test file stays quiet
write test/upload_test.dartno handler of the detector wants this event
void main() {
chirp.error('x', {'request_id': id});
}detector.test.tseach detect() call runs the real detectorEdit
import { expect, test } from "vitest";
import { detect, edit, source, write } from "@wellactually/sdk/v3/test";
const charge = source`
export async function handleCharge(req: Request) {
const user = await authenticate(req);
log.info('authenticated', { requestId: req.id, userId: user.id });
}
`;
test("a log call threading a request id by hand fires", () => {
expect(detect(write("src/charge.ts", charge))).toEqual([{ line: 3 }]);
});
test("a log call the agent did not write stays quiet", () => {
expect(detect(edit("src/charge.ts", charge, { written: " const user = await authenticate(req);" }))).toEqual([]);
});
test("a string key in Go and Dart fires, though blanking would erase it", () => {
expect(detect(write("server/charge.go", 'func charge(id string) {\n\tslog.Info("charged", "trace_id", id)\n}'))).toEqual([{ line: 2 }]);
expect(detect(write("lib/upload.dart", "void report(String cid) {\n chirp.error('upload failed', {'correlation_id': cid});\n}"))).toEqual([{ line: 2 }]);
});
test("the canonical emitter and the context it drains stay quiet", () => {
const code = source`
export function emit(req: Request) {
const ctx = { request_id: req.id, path: req.url };
log.info('canonical', ctx);
}
`;
expect(detect(write("src/canonical.ts", code))).toEqual([]);
});
test("an id named in the message is prose, not a field", () => {
expect(detect(write("src/lookup.ts", "export function lookup() {\n log.debug('handling requestId lookups');\n}"))).toEqual([]);
});
test("a code example in markdown is prose, not a log call", () => {
expect(detect(write("docs/logging.md", "```ts\nlog.info('x', { requestId: id });\n```"))).toEqual([]);
});
test("a Dart test file stays quiet", () => {
expect(detect(write("test/upload_test.dart", "void main() {\n chirp.error('x', {'request_id': id});\n}"))).toEqual([]);
});