Skip to content

Commit

Permalink
LogRecord.rawMessage property
Browse files Browse the repository at this point in the history
Close #17
  • Loading branch information
dahlia committed Sep 24, 2024
1 parent e650a1e commit b8b960d
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 7 deletions.
3 changes: 3 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ To be released.
However, if a property with exactly the same name exists, it will be
prioritized over space-trimmed properties. [[#16]]

- Added `LogRecord.rawMessage` property. [[#17]]

- Built-in text formatters now can be customized with a `TextFormatterOptions`
object. [[#13]]

Expand All @@ -31,6 +33,7 @@ To be released.
[#13]: https://github.com/dahlia/logtape/issues/13
[#15]: https://github.com/dahlia/logtape/issues/15
[#16]: https://github.com/dahlia/logtape/issues/16
[#17]: https://github.com/dahlia/logtape/issues/17


Version 0.5.2
Expand Down
4 changes: 4 additions & 0 deletions logtape/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ Deno.test("configure()", async (t) => {
level: "warning",
category: ["my-app", "foo"],
message: ["logged"],
rawMessage: "logged",
properties: {},
timestamp: bLogs[0].timestamp,
},
Expand All @@ -90,6 +91,7 @@ Deno.test("configure()", async (t) => {
level: "info",
category: ["my-app", "bar"],
message: ["logged"],
rawMessage: "logged",
properties: {},
timestamp: cLogs[0].timestamp,
},
Expand All @@ -99,6 +101,7 @@ Deno.test("configure()", async (t) => {
level: "warning",
category: ["my-app", "foo"],
message: ["logged"],
rawMessage: "logged",
properties: {},
timestamp: bLogs[0].timestamp,
},
Expand All @@ -108,6 +111,7 @@ Deno.test("configure()", async (t) => {
level: "info",
category: ["my-app", "bar"],
message: ["logged"],
rawMessage: "logged",
properties: {},
timestamp: cLogs[0].timestamp,
},
Expand Down
1 change: 1 addition & 0 deletions logtape/fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export const info: LogRecord = {
level: "info",
category: ["my-app", "junk"],
message: ["Hello, ", 123, " & ", 456, "!"],
rawMessage: "Hello, {a} & {b}!",
timestamp: 1700000000000,
properties: {},
};
Expand Down
22 changes: 22 additions & 0 deletions logtape/logger.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ import {
import type { LogRecord } from "./record.ts";
import type { Sink } from "./sink.ts";

function templateLiteral(tpl: TemplateStringsArray, ..._: unknown[]) {
return tpl;
}

Deno.test("getLogger()", () => {
assertEquals(getLogger().category, []);
assertStrictEquals(getLogger(), getLogger());
Expand Down Expand Up @@ -215,6 +219,7 @@ Deno.test("LoggerImpl.log()", async (t) => {
category: ["foo"],
level: "info",
message: ["Hello, ", 123, "!"],
rawMessage: "Hello, {foo}!",
timestamp: logs[0].timestamp,
properties: { foo: 123 },
},
Expand All @@ -241,6 +246,7 @@ Deno.test("LoggerImpl.log()", async (t) => {
category: ["foo"],
level: "error",
message: ["Hello, ", 123, "!"],
rawMessage: "Hello, {foo}!",
timestamp: logs[0].timestamp,
properties: { foo: 123 },
},
Expand Down Expand Up @@ -278,6 +284,7 @@ Deno.test("LoggerImpl.logLazily()", async (t) => {
category: ["foo"],
level: "error",
message: ["Hello, ", 123, "!"],
rawMessage: templateLiteral`Hello, ${null}!`,
timestamp: logs[0].timestamp,
properties: {},
},
Expand Down Expand Up @@ -310,6 +317,7 @@ Deno.test("LoggerImpl.logTemplate()", async (t) => {
category: ["foo"],
level: "info",
message: ["Hello, ", 123, "!"],
rawMessage: templateLiteral`Hello, ${null}!`,
timestamp: logs[0].timestamp,
properties: {},
},
Expand Down Expand Up @@ -338,6 +346,7 @@ Deno.test("LoggerCtx.log()", async (t) => {
category: ["foo"],
level: "info",
message: ["Hello, ", 1, " ", 2, " ", 3, "!"],
rawMessage: "Hello, {a} {b} {c}!",
timestamp: logs[0].timestamp,
properties: { a: 1, b: 2, c: 3 },
},
Expand All @@ -364,6 +373,7 @@ Deno.test("LoggerCtx.log()", async (t) => {
category: ["foo"],
level: "error",
message: ["Hello, ", 1, " ", 2, " ", 3, "!"],
rawMessage: "Hello, {a} {b} {c}!",
timestamp: logs[0].timestamp,
properties: { a: 1, b: 2, c: 3 },
},
Expand Down Expand Up @@ -402,6 +412,7 @@ Deno.test("LoggerCtx.logLazily()", async (t) => {
category: ["foo"],
level: "error",
message: ["Hello, ", 123, "!"],
rawMessage: templateLiteral`Hello, ${null}!`,
timestamp: logs[0].timestamp,
properties: { a: 1, b: 2 },
},
Expand Down Expand Up @@ -435,6 +446,7 @@ Deno.test("LoggerCtx.logTemplate()", async (t) => {
category: ["foo"],
level: "info",
message: ["Hello, ", 123, "!"],
rawMessage: templateLiteral`Hello, ${null}!`,
timestamp: logs[0].timestamp,
properties: { a: 1, b: 2 },
},
Expand Down Expand Up @@ -469,6 +481,7 @@ for (const method of methods) {
category: ["foo"],
level: method === "warn" ? "warning" : method,
message: ["Hello, ", 123, "!"],
rawMessage: templateLiteral`Hello, ${null}!`,
timestamp: logs[0].timestamp,
properties: {},
},
Expand All @@ -489,6 +502,7 @@ for (const method of methods) {
category: ["foo"],
level: method === "warn" ? "warning" : method,
message: ["Hello, ", 123, "!"],
rawMessage: templateLiteral`Hello, ${null}!`,
timestamp: logs[0].timestamp,
properties: { a: 1, b: 2 },
},
Expand All @@ -512,6 +526,7 @@ for (const method of methods) {
category: ["foo"],
level: method === "warn" ? "warning" : method,
message: ["Hello, ", 123, "!"],
rawMessage: templateLiteral`Hello, ${null}!`,
timestamp: logs[0].timestamp,
properties: {},
},
Expand All @@ -528,6 +543,7 @@ for (const method of methods) {
category: ["foo"],
level: method === "warn" ? "warning" : method,
message: ["Hello, ", 123, "!"],
rawMessage: templateLiteral`Hello, ${null}!`,
timestamp: logs[0].timestamp,
properties: { a: 1, b: 2 },
},
Expand All @@ -551,6 +567,7 @@ for (const method of methods) {
category: ["foo"],
level: method === "warn" ? "warning" : method,
message: ["Hello, ", 123, "!"],
rawMessage: "Hello, {foo}!",
timestamp: logs[0].timestamp,
properties: { foo: 123 },
},
Expand All @@ -565,6 +582,7 @@ for (const method of methods) {
category: ["foo"],
level: method === "warn" ? "warning" : method,
message: ["Hello, world!"],
rawMessage: "Hello, world!",
timestamp: logs[0].timestamp,
properties: {},
},
Expand All @@ -579,6 +597,7 @@ for (const method of methods) {
category: ["foo"],
level: method === "warn" ? "warning" : method,
message: ["Hello, ", 1, " ", 2, " ", 3, "!"],
rawMessage: "Hello, {a} {b} {c}!",
timestamp: logs[0].timestamp,
properties: { a: 1, b: 2, c: 3 },
},
Expand All @@ -591,6 +610,7 @@ for (const method of methods) {
category: ["foo"],
level: method === "warn" ? "warning" : method,
message: ["Hello, world!"],
rawMessage: "Hello, world!",
timestamp: logs[0].timestamp,
properties: { a: 1, b: 2 },
},
Expand All @@ -614,6 +634,7 @@ for (const method of methods) {
category: ["foo"],
level: method === "warn" ? "warning" : method,
message: ["Hello, ", 123, "!"],
rawMessage: "Hello, {foo}!",
timestamp: logs[0].timestamp,
properties: { foo: 123 },
},
Expand All @@ -632,6 +653,7 @@ for (const method of methods) {
category: ["foo"],
level: method === "warn" ? "warning" : method,
message: ["Hello, ", 1, " ", 2, " ", 3, "!"],
rawMessage: "Hello, {a} {b} {c}!",
timestamp: logs[0].timestamp,
properties: { a: 1, b: 2, c: 3 },
},
Expand Down
33 changes: 26 additions & 7 deletions logtape/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ export interface Logger {
* ```
*
* @param callback A callback that returns the message template prefix.
* @throws {TypeError} If no log record was made inside the callback.
*/
debug(callback: LogCallback): void;

Expand Down Expand Up @@ -182,6 +183,7 @@ export interface Logger {
* ```
*
* @param callback A callback that returns the message template prefix.
* @throws {TypeError} If no log record was made inside the callback.
*/
info(callback: LogCallback): void;

Expand Down Expand Up @@ -236,6 +238,7 @@ export interface Logger {
* ```
*
* @param callback A callback that returns the message template prefix.
* @throws {TypeError} If no log record was made inside the callback.
*/
warn(callback: LogCallback): void;

Expand Down Expand Up @@ -290,6 +293,7 @@ export interface Logger {
* ```
*
* @param callback A callback that returns the message template prefix.
* @throws {TypeError} If no log record was made inside the callback.
*/
error(callback: LogCallback): void;

Expand Down Expand Up @@ -344,6 +348,7 @@ export interface Logger {
* ```
*
* @param callback A callback that returns the message template prefix.
* @throws {TypeError} If no log record was made inside the callback.
*/
fatal(callback: LogCallback): void;
}
Expand Down Expand Up @@ -517,7 +522,7 @@ export class LoggerImpl implements Logger {

log(
level: LogLevel,
message: string,
rawMessage: string,
properties: Record<string, unknown> | (() => Record<string, unknown>),
bypassSinks?: Set<Sink>,
): void {
Expand All @@ -528,8 +533,9 @@ export class LoggerImpl implements Logger {
level,
timestamp: Date.now(),
get message() {
return parseMessageTemplate(message, this.properties);
return parseMessageTemplate(rawMessage, this.properties);
},
rawMessage,
get properties() {
if (cachedProps == null) cachedProps = properties();
return cachedProps;
Expand All @@ -539,7 +545,8 @@ export class LoggerImpl implements Logger {
category: this.category,
level,
timestamp: Date.now(),
message: parseMessageTemplate(message, properties),
message: parseMessageTemplate(rawMessage, properties),
rawMessage,
properties,
};
this.emit(record, bypassSinks);
Expand All @@ -550,15 +557,26 @@ export class LoggerImpl implements Logger {
callback: LogCallback,
properties: Record<string, unknown> = {},
): void {
let rawMessage: TemplateStringsArray | undefined = undefined;
let msg: unknown[] | undefined = undefined;
function realizeMessage(): [unknown[], TemplateStringsArray] {
if (msg == null || rawMessage == null) {
msg = callback((tpl, ...values) => {
rawMessage = tpl;
return renderMessage(tpl, values);
});
if (rawMessage == null) throw new TypeError("No log record was made.");
}
return [msg, rawMessage];
}
this.emit({
category: this.category,
level,
get message() {
if (msg == null) {
msg = callback((tpl, ...values) => renderMessage(tpl, values));
}
return msg;
return realizeMessage()[0];
},
get rawMessage() {
return realizeMessage()[1];
},
timestamp: Date.now(),
properties,
Expand All @@ -575,6 +593,7 @@ export class LoggerImpl implements Logger {
category: this.category,
level,
message: renderMessage(messageTemplate, values),
rawMessage: messageTemplate,
timestamp: Date.now(),
properties,
});
Expand Down
15 changes: 15 additions & 0 deletions logtape/record.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,21 @@ export interface LogRecord {
*/
readonly message: readonly unknown[];

/**
* The raw log message. This is the original message template without any
* further processing. It can be either:
*
* - A string without any substitutions if the log record was created with
* a method call syntax, e.g., "Hello, {name}!" for
* `logger.info("Hello, {name}!", { name })`.
* - A template string array if the log record was created with a tagged
* template literal syntax, e.g., `["Hello, ", "!"]` for
* ``logger.info`Hello, ${name}!```.
*
* @since 0.6.0
*/
readonly rawMessage: string | TemplateStringsArray;

/**
* The timestamp of the log record in milliseconds since the Unix epoch.
*/
Expand Down

0 comments on commit b8b960d

Please sign in to comment.