Skip to content

Commit

Permalink
feat: migrate to @std/log
Browse files Browse the repository at this point in the history
  • Loading branch information
kitsonk committed Jul 2, 2024
1 parent 42a5043 commit 90c919a
Show file tree
Hide file tree
Showing 7 changed files with 168 additions and 108 deletions.
2 changes: 1 addition & 1 deletion _examples/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const keys = new KeyStack(["super secret"]);

const router = new Router({
keys,
logger: { console: { level: "debug" } },
logger: { console: { level: "DEBUG" } },
});

router.get("/", async (ctx) => {
Expand Down
14 changes: 8 additions & 6 deletions context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
* @module
*/

import { getLogger, type Logger } from "@logtape/logtape";
import { type KeyRing, SecureCookieMap } from "@oak/commons/cookie_map";
import { createHttpError } from "@oak/commons/http_errors";
import {
Expand All @@ -22,6 +21,7 @@ import { Status } from "@oak/commons/status";
import { UserAgent } from "@std/http/user-agent";
import type { InferOutput } from "@valibot/valibot";

import { getLogger, type Logger } from "./logger.ts";
import type { BodySchema, QueryStringSchema, Schema } from "./schema.ts";
import type {
Addr,
Expand Down Expand Up @@ -186,7 +186,7 @@ export class Context<
response: responseHeaders,
secure,
});
this.#logger = getLogger(["acorn", "context", requestEvent.id]);
this.#logger = getLogger("acorn.context");
}

/**
Expand All @@ -206,7 +206,7 @@ export class Context<
async body(): Promise<RequestBody | undefined> {
if (!this.#bodySet) {
this.#bodySet = true;
this.#logger.debug`validating body`;
this.#logger.debug(`${this.#requestEvent.id} validating body`);
const result = await this.#schema.validateBody(this.#requestEvent);
if (result.invalidResponse) {
this.#requestEvent.respond(result.invalidResponse);
Expand All @@ -226,7 +226,9 @@ export class Context<
*/
async queryParams(): Promise<QueryParams | undefined> {
if (!this.#queryParams) {
this.#logger.debug`validating query parameters`;
this.#logger.debug(
`${this.#requestEvent.id} validating query parameters`,
);
const result = await this.#schema.validateQueryString(this.#requestEvent);
if (result.invalidResponse) {
this.#requestEvent.respond(result.invalidResponse);
Expand All @@ -252,7 +254,7 @@ export class Context<
if (this.#requestEvent.responded) {
throw new Error("Cannot send the correct response, already responded.");
}
this.#logger.debug`starting server sent events`;
this.#logger.debug(`${this.#requestEvent.id} starting server sent events`);
const sse = new ServerSentEventStreamTarget(options);
const response = sse.asResponse(options);
this.#requestEvent.respond(appendHeaders(response, this.#responseHeaders));
Expand All @@ -279,7 +281,7 @@ export class Context<
if (this.#requestEvent.responded) {
throw new Error("Cannot upgrade, already responded.");
}
this.#logger.debug`upgrading to web socket`;
this.#logger.debug(`${this.#requestEvent.id} upgrading to web socket`);
return this.#requestEvent.upgrade(options);
}

Expand Down
6 changes: 3 additions & 3 deletions deno.json
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
{
"name": "@oak/acorn",
"version": "1.0.0-alpha.3",
"version": "1.0.0-alpha.4",
"exports": { ".": "./mod.ts" },
"tasks": {
"bench": "deno bench",
"bench": "deno bench --allow-write --allow-read",
"check": "deno check mod.ts",
"example": "deno run --allow-net --allow-env --allow-hrtime _examples/server.ts",
"test": "deno test --allow-net --allow-env --allow-hrtime"
},
"imports": {
"@logtape/logtape": "jsr:@kitsonk/[email protected]",
"@oak/commons": "jsr:@oak/commons@^0.12",
"@std/assert": "jsr:@std/assert@^0.226",
"@std/http": "jsr:@std/http@^0.224",
"@std/log": "jsr:@std/log@^0.224.3",
"@std/media-types": "jsr:@std/media-types@^1.0",
"@valibot/valibot": "jsr:@valibot/valibot@^0.35",
"hyperid": "npm:hyperid@^3.2",
Expand Down
114 changes: 74 additions & 40 deletions logger.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
// Copyright 2018-2024 the oak authors. All rights reserved.

import {
configure,
getConsoleSink,
getRotatingFileSink,
getStreamSink,
type BaseHandler,
ConsoleHandler,
type FormatterFunction,
getLogger as gl,
type LevelName,
type Logger,
type LoggerConfig,
type LogLevel,
type Sink,
} from "@logtape/logtape";
RotatingFileHandler,
setup,
} from "@std/log";

export { type Logger } from "@std/log";

/**
* Options which can be set when configuring file logging on the logger.
Expand All @@ -17,20 +21,20 @@ export interface FileLoggerOptions {
/**
* The log level to log at. The default is `"info"`.
*/
level?: LogLevel;
level?: LevelName;
/**
* The maximum number of log files to keep. The default is `5`.
*/
maxFiles?: number;
maxBackupCount?: number;
/**
* The maximum size of a log file before it is rotated in bytes. The default
* is 10MB.
*/
maxSize?: number;
maxBytes?: number;
/**
* The path to the log file.
*/
path: string;
filename: string;
}

/**
Expand All @@ -42,7 +46,7 @@ export interface LoggerOptions {
* Log events to the console. If `true`, log at the "info" level. If an
* object, the `level` can be specified.
*/
console?: boolean | { level: LogLevel };
console?: boolean | { level: LevelName };
/**
* Log events to a rotating log file. The value should be an object with the
* `path` to the log file and optionally the `level` to log at. If `level` is
Expand All @@ -54,44 +58,74 @@ export interface LoggerOptions {
* pipe the log events to and optionally the `level` to log at. If `level` is
* not specified, the default is `"info"`.
*/
stream?: { level?: LogLevel; stream: WritableStream };
stream?: { level?: LevelName; stream: WritableStream };
}

export const formatter: FormatterFunction = (
{ datetime, levelName, loggerName, msg },
) => `${datetime.toISOString()} [${levelName}] ${loggerName}: ${msg}`;

const mods = [
"acorn.context",
"acorn.request_event_cfw",
"acorn.request_server_bun",
"acorn.request_server_deno",
"acorn.request_server_node",
"acorn.route",
"acorn.router",
"acorn.schema",
"acorn.status_route",
] as const;

type Loggers = typeof mods[number];

export function getLogger(mod: Loggers): Logger {
return gl(mod);
}

export function configureLogger(options?: LoggerOptions): Promise<void> {
const sinks: Record<string, Sink> = {};
const loggers: LoggerConfig<"console" | "file" | "stream", string>[] = [];
sinks.console = getConsoleSink();
loggers.push({ category: "logtape", level: "warning", sinks: ["console"] });
export function configure(options?: LoggerOptions): void {
const config = {
handlers: {} as { [key: string]: BaseHandler },
loggers: {} as { [key: string]: LoggerConfig },
};
const handlers: string[] = [];
if (options) {
if (options.console) {
loggers.push({
category: ["acorn"],
sinks: ["console"],
level: options.console === true ? "info" : options.console.level,
});
config.handlers.console = new ConsoleHandler(
typeof options.console === "object" ? options.console.level : "INFO",
{ formatter },
);
handlers.push("console");
}
if (options.file) {
sinks.file = getRotatingFileSink(options.file.path, {
maxFiles: options.file.maxFiles ?? 5,
maxSize: options.file.maxSize ?? 1_024 * 1_024 * 10,
});
loggers.push({
category: ["acorn"],
sinks: ["file"],
level: options.file.level ?? "info",
const {
filename,
maxBackupCount = 5,
maxBytes = 1024 * 1024 * 10,
level = "INFO",
} = options.file;
config.handlers.file = new RotatingFileHandler(level, {
filename,
maxBackupCount,
maxBytes,
formatter,
});
handlers.push("file");
}
if (options.stream) {
sinks.stream = getStreamSink(options.stream.stream);
loggers.push({
category: ["acorn"],
sinks: ["stream"],
level: options.stream.level ?? "info",
});
// sinks.stream = getStreamSink(options.stream.stream);
// loggers.push({
// category: ["acorn"],
// sinks: ["stream"],
// level: options.stream.level ?? "info",
// });
}
} else {
sinks.console = getConsoleSink();
loggers.push({ category: "acorn", level: "warning", sinks: ["console"] });
config.handlers.console = new ConsoleHandler("WARN", { formatter });
handlers.push("console");
}
for (const mod of mods) {
config.loggers[mod] = { level: "DEBUG", handlers };
}
return configure({ sinks, filters: {}, loggers });
setup(config);
}
33 changes: 21 additions & 12 deletions route.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// Copyright 2018-2024 the oak authors. All rights reserved.

import { getLogger, type Logger } from "@logtape/logtape";
import type { KeyRing } from "@oak/commons/cookie_map";
import { createHttpError } from "@oak/commons/http_errors";
import type { HttpMethod } from "@oak/commons/method";
Expand All @@ -9,6 +8,7 @@ import { type Key, pathToRegexp } from "path-to-regexp";
import type { InferOutput } from "@valibot/valibot";

import { Context } from "./context.ts";
import { getLogger, type Logger } from "./logger.ts";
import type {
ParamsDictionary,
RequestEvent,
Expand Down Expand Up @@ -179,9 +179,9 @@ export class PathRoute<
this.#handler = handler;
this.#keys = keys;
this.#regex = pathToRegexp(path, this.#paramKeys, options);
this.#logger = getLogger(["acorn", "route", path]);
this.#logger = getLogger("acorn.route");
this.#logger
.debug`created route with path: ${path} and methods: ${methods}`;
.debug(`created route with path: ${path} and methods: ${methods}`);
}

/**
Expand All @@ -193,7 +193,7 @@ export class PathRoute<
responseHeaders: Headers,
secure: boolean,
): Promise<Response | undefined> {
this.#logger.debug`${requestEvent.id} route.handle()`;
this.#logger.debug(`[${this.#path}] ${requestEvent.id} route.handle()`);
if (!this.#params) {
throw createHttpError(
Status.InternalServerError,
Expand All @@ -216,27 +216,35 @@ export class PathRoute<
this.#schema,
this.#keys,
);
this.#logger.debug`${requestEvent.id} calling handler`;
this.#logger.debug(`[${this.#path}] ${requestEvent.id} calling handler`);
const result = await this.#handler(context);
this.#logger
.debug`${requestEvent.id} handler returned with value: ${!!result}`;
.debug(`${requestEvent.id} handler returned with value: ${!!result}`);
if (result instanceof Response) {
this.#logger.debug`${requestEvent.id} handler returned a Response object`;
this.#logger
.debug(
`[${this.#path}] ${requestEvent.id} handler returned a Response object`,
);
return appendHeaders(result, responseHeaders);
}
if (result) {
this.#logger
.debug`${requestEvent.id} handler returned a value, validating response`;
.debug(
`${requestEvent.id} handler returned a value, validating response`,
);
const maybeValid = await this.#schema.validateResponse(result);
if (maybeValid.output) {
this.#logger.debug`${requestEvent.id} response is valid`;
this.#logger
.debug(`[${this.#path}] ${requestEvent.id} response is valid`);
return Response.json(maybeValid.output, { headers: responseHeaders });
} else {
this.#logger.error`${requestEvent.id} response is invalid`;
this.#logger
.error(`[${this.#path}] ${requestEvent.id} response is invalid`);
return maybeValid.invalidResponse;
}
}
this.#logger.debug`${requestEvent.id} handler returned no value`;
this.#logger
.debug(`[${this.#path}] ${requestEvent.id} handler returned no value`);
return new Response(null, {
status: Status.NoContent,
statusText: STATUS_TEXT[Status.NoContent],
Expand All @@ -250,7 +258,8 @@ export class PathRoute<
if (this.#methods.includes(method)) {
const match = pathname.match(this.#regex);
if (match) {
this.#logger.debug`route matched: ${method} ${pathname}`;
this.#logger
.debug(`[${this.#path}] route matched: ${method} ${pathname}`);
const params = {} as Params;
const captures = match.slice(1);
for (let i = 0; i < captures.length; i++) {
Expand Down
Loading

0 comments on commit 90c919a

Please sign in to comment.