Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow category deep nesting #22

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all 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
14 changes: 14 additions & 0 deletions logtape/category.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export type CategoryList = readonly string[];

export type Category = string | readonly Category[];

export function getCategoryList(category: Category): CategoryList {
return deepFlatten(category);
}

function deepFlatten(arr: Category): string[] {
if (typeof arr === "string") return [arr];
return arr.flatMap((item) => (
typeof item === "string" ? [item] : deepFlatten(item)
));
}
3 changes: 2 additions & 1 deletion logtape/config.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { Category } from "./category.ts";
import { type FilterLike, toFilter } from "./filter.ts";
import type { LogLevel } from "./level.ts";
import { LoggerImpl } from "./logger.ts";
Expand Down Expand Up @@ -40,7 +41,7 @@ export interface LoggerConfig<
* The category of the logger. If a string, it is equivalent to an array
* with one element.
*/
category: string | string[];
category: Category;

/**
* The sink identifiers to use.
Expand Down
3 changes: 2 additions & 1 deletion logtape/formatter.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { CategoryList } from "./category.ts";
import type { LogLevel } from "./level.ts";
import util from "./nodeUtil.ts";
import type { LogRecord } from "./record.ts";
Expand Down Expand Up @@ -154,7 +155,7 @@ export interface TextFormatterOptions {
* If this is a function, it will be called with the category array and
* should return a string, which will be used for rendering the category.
*/
category?: string | ((category: readonly string[]) => string);
category?: string | ((category: CategoryList) => string);

/**
* The format of the embedded values.
Expand Down
4 changes: 4 additions & 0 deletions logtape/logger.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ Deno.test("getLogger()", () => {
getLogger(["foo", "bar"]),
getLogger().getChild("foo").getChild("bar"),
);
assertStrictEquals(
getLogger(["foo", ["bar", ["baz", ["qux"]]]]),
getLogger().getChild("foo").getChild("bar").getChild("baz").getChild("qux"),
);
});

Deno.test("Logger.getChild()", () => {
Expand Down
40 changes: 19 additions & 21 deletions logtape/logger.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
import {
type Category,
type CategoryList,
getCategoryList,
} from "./category.ts";
import type { Filter } from "./filter.ts";
import type { LogLevel } from "./level.ts";
import type { LogRecord } from "./record.ts";
Expand All @@ -20,7 +25,7 @@ export interface Logger {
/**
* The category of the logger. It is an array of strings.
*/
readonly category: readonly string[];
readonly category: CategoryList;

/**
* The logger with the supercategory of the current logger. If the current
Expand All @@ -46,9 +51,7 @@ export interface Logger {
* @param subcategory The subcategory.
* @returns The child logger.
*/
getChild(
subcategory: string | readonly [string] | readonly [string, ...string[]],
): Logger;
getChild(subcategory: Category): Logger;

/**
* Get a logger with contextual properties. This is useful for
Expand Down Expand Up @@ -385,7 +388,7 @@ export type LogTemplatePrefix = (
* with a single element.
* @returns The logger.
*/
export function getLogger(category: string | readonly string[] = []): Logger {
export function getLogger(category: Category = []): Logger {
return LoggerImpl.getLogger(category);
}

Expand All @@ -408,12 +411,12 @@ interface GlobalRootLoggerRegistry {
export class LoggerImpl implements Logger {
readonly parent: LoggerImpl | null;
readonly children: Record<string, LoggerImpl | WeakRef<LoggerImpl>>;
readonly category: readonly string[];
readonly category: CategoryList;
readonly sinks: Sink[];
parentSinks: "inherit" | "override" = "inherit";
readonly filters: Filter[];

static getLogger(category: string | readonly string[] = []): LoggerImpl {
static getLogger(category: Category = []): LoggerImpl {
let rootLogger: LoggerImpl | null = globalRootLoggerSymbol in globalThis
? ((globalThis as GlobalRootLoggerRegistry)[globalRootLoggerSymbol] ??
null)
Expand All @@ -423,12 +426,11 @@ export class LoggerImpl implements Logger {
(globalThis as GlobalRootLoggerRegistry)[globalRootLoggerSymbol] =
rootLogger;
}
if (typeof category === "string") return rootLogger.getChild(category);
if (category.length === 0) return rootLogger;
return rootLogger.getChild(category as readonly [string, ...string[]]);
return rootLogger.getChild(category);
}

private constructor(parent: LoggerImpl | null, category: readonly string[]) {
constructor(parent: LoggerImpl | null, category: CategoryList) {
this.parent = parent;
this.children = {};
this.category = category;
Expand All @@ -437,12 +439,10 @@ export class LoggerImpl implements Logger {
}

getChild(
subcategory:
| string
| readonly [string]
| readonly [string, ...(readonly string[])],
subcategory: Category,
): LoggerImpl {
const name = typeof subcategory === "string" ? subcategory : subcategory[0];
const subcategoryList = getCategoryList(subcategory);
const name = subcategoryList[0];
const childRef = this.children[name];
let child: LoggerImpl | undefined = childRef instanceof LoggerImpl
? childRef
Expand All @@ -453,12 +453,10 @@ export class LoggerImpl implements Logger {
? new WeakRef(child)
: child;
}
if (typeof subcategory === "string" || subcategory.length === 1) {
if (subcategoryList.length === 1) {
return child;
}
return child.getChild(
subcategory.slice(1) as [string, ...(readonly string[])],
);
return child.getChild(subcategoryList.slice(1));
}

/**
Expand Down Expand Up @@ -683,7 +681,7 @@ export class LoggerCtx implements Logger {
this.properties = properties;
}

get category(): readonly string[] {
get category(): CategoryList {
return this.logger.category;
}

Expand All @@ -692,7 +690,7 @@ export class LoggerCtx implements Logger {
}

getChild(
subcategory: string | readonly [string] | readonly [string, ...string[]],
subcategory: Category,
): Logger {
return this.logger.getChild(subcategory).with(this.properties);
}
Expand Down
1 change: 1 addition & 0 deletions logtape/mod.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { type Category, type CategoryList } from "./category.ts";
export {
type Config,
ConfigError,
Expand Down
3 changes: 2 additions & 1 deletion logtape/record.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { CategoryList } from "./category.ts";
import type { LogLevel } from "./level.ts";

/**
Expand All @@ -7,7 +8,7 @@ export interface LogRecord {
/**
* The category of the logger that produced the log record.
*/
readonly category: readonly string[];
readonly category: CategoryList;

/**
* The log level.
Expand Down