Create Logger class for debugging
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import Debug from "debug";
|
import { Logger } from "./utils/logger.js";
|
||||||
import { Engine } from "@xo-cash/engine";
|
import { Engine } from "@xo-cash/engine";
|
||||||
|
|
||||||
import { Config } from "./services/config.js";
|
import { Config } from "./services/config.js";
|
||||||
@@ -22,7 +22,7 @@ type VendingMachineDeps = {
|
|||||||
|
|
||||||
export class VendingMachine {
|
export class VendingMachine {
|
||||||
static async from(config: Config) {
|
static async from(config: Config) {
|
||||||
const debug = Debug("vending-machine");
|
const debug = new Logger("vending-machine");
|
||||||
|
|
||||||
debug("Config: %O", config);
|
debug("Config: %O", config);
|
||||||
|
|
||||||
@@ -65,7 +65,7 @@ export class VendingMachine {
|
|||||||
const httpService = new HTTPService({
|
const httpService = new HTTPService({
|
||||||
routes,
|
routes,
|
||||||
config: config.server,
|
config: config.server,
|
||||||
debug,
|
debug: new Logger("vending-machine"),
|
||||||
});
|
});
|
||||||
|
|
||||||
debug("Creating vending machine");
|
debug("Creating vending machine");
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { Debugger as Debug } from "debug";
|
import type { Logger } from '../utils/logger.js';
|
||||||
import type { RouteOptions, FastifyRequest, FastifyReply } from "fastify";
|
import type { RouteOptions, FastifyRequest, FastifyReply } from "fastify";
|
||||||
import type { Engine } from "@xo-cash/engine";
|
import type { Engine } from "@xo-cash/engine";
|
||||||
import type { Database } from "../services/database/database.js";
|
import type { Database } from "../services/database/database.js";
|
||||||
@@ -8,7 +8,7 @@ import { z } from "zod";
|
|||||||
export type ItemsRouteDeps = {
|
export type ItemsRouteDeps = {
|
||||||
database: Database;
|
database: Database;
|
||||||
engine: Engine;
|
engine: Engine;
|
||||||
debug: Debug;
|
debug: Logger;
|
||||||
};
|
};
|
||||||
|
|
||||||
export class ItemsRoute {
|
export class ItemsRoute {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { Debugger as Debug } from "debug";
|
import type { Logger } from '../utils/logger.js';
|
||||||
import type { RouteOptions, FastifyRequest, FastifyReply } from "fastify";
|
import type { RouteOptions, FastifyRequest, FastifyReply } from "fastify";
|
||||||
|
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
@@ -13,7 +13,7 @@ export type OrdersRouteDeps = {
|
|||||||
database: Database;
|
database: Database;
|
||||||
orderPaymentService: OrderPaymentService;
|
orderPaymentService: OrderPaymentService;
|
||||||
syncServerUrl: string;
|
syncServerUrl: string;
|
||||||
debug: Debug;
|
debug: Logger;
|
||||||
};
|
};
|
||||||
|
|
||||||
export class OrdersRoute {
|
export class OrdersRoute {
|
||||||
|
|||||||
@@ -1,18 +1,19 @@
|
|||||||
import { type Debugger } from "debug";
|
import { Logger } from '../../utils/logger.js';
|
||||||
|
|
||||||
import DatabaseConstructor from "better-sqlite3";
|
import DatabaseConstructor from "better-sqlite3";
|
||||||
import { Kysely, SqliteDialect } from "kysely";
|
import { Kysely, SqliteDialect } from "kysely";
|
||||||
import type { Database as DatabaseTables } from "./tables.js";
|
import type { Database as DatabaseTables } from "./tables.js";
|
||||||
|
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { debuggerSchema } from "../../types.js";
|
|
||||||
|
|
||||||
export const databaseOptionsSchema = z.object({
|
export const databaseOptionsSchema = z.object({
|
||||||
config: z.object({
|
config: z.object({
|
||||||
path: z.string(),
|
path: z.string(),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
debug: debuggerSchema,
|
debug: z.custom<Logger>(Logger.isLogger, {
|
||||||
|
message: "Expected a Logger instance",
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
type DatabaseOptionsInput = z.input<typeof databaseOptionsSchema>;
|
type DatabaseOptionsInput = z.input<typeof databaseOptionsSchema>;
|
||||||
@@ -26,7 +27,7 @@ type DatabaseOptionsInput = z.input<typeof databaseOptionsSchema>;
|
|||||||
*/
|
*/
|
||||||
export class Database {
|
export class Database {
|
||||||
// Debugger instance used for logging.
|
// Debugger instance used for logging.
|
||||||
private readonly debug: Debugger;
|
private readonly debug: Logger;
|
||||||
|
|
||||||
// SQLite connection instance.
|
// SQLite connection instance.
|
||||||
private readonly sqlite: DatabaseConstructor.Database;
|
private readonly sqlite: DatabaseConstructor.Database;
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
import Debug from "debug";
|
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import fastify, { type FastifyInstance, type RouteOptions } from "fastify";
|
import fastify, { type FastifyInstance, type RouteOptions } from "fastify";
|
||||||
import cors from "@fastify/cors";
|
import cors from "@fastify/cors";
|
||||||
|
|
||||||
import { debuggerSchema } from "../types.js";
|
import { Logger } from "../utils/logger.js";
|
||||||
|
|
||||||
// Interface to add to our route classes so that we can register them.
|
// Interface to add to our route classes so that we can register them.
|
||||||
// NOTE: I hate this pattern. But ExpressJS is odd in that it is structured as a singleton that still needs registration.
|
// NOTE: I hate this pattern. But ExpressJS is odd in that it is structured as a singleton that still needs registration.
|
||||||
@@ -33,9 +32,12 @@ export const serverConfigSchema = z.object({
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Zod schema for the server debug instance.
|
// Zod schema for the server debug instance.
|
||||||
export const serverDebugSchema = debuggerSchema
|
export const serverDebugSchema = z
|
||||||
|
.custom<Logger>(Logger.isLogger, {
|
||||||
|
message: "Expected a Logger instance",
|
||||||
|
})
|
||||||
.optional()
|
.optional()
|
||||||
.default(Debug("vending-machine"));
|
.default(new Logger("vending-machine"));
|
||||||
|
|
||||||
// Zod schema for the HTTP service options.
|
// Zod schema for the HTTP service options.
|
||||||
export const HTTPOptions = z.object({
|
export const HTTPOptions = z.object({
|
||||||
@@ -61,7 +63,7 @@ export class HTTPService {
|
|||||||
private server: FastifyInstance;
|
private server: FastifyInstance;
|
||||||
|
|
||||||
// Private properties.
|
// Private properties.
|
||||||
private debug: debug.Debugger;
|
private debug: Logger;
|
||||||
private config: HTTPServiceOptions["config"];
|
private config: HTTPServiceOptions["config"];
|
||||||
private routes: HTTPServiceOptions["routes"];
|
private routes: HTTPServiceOptions["routes"];
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { Debugger } from "debug";
|
import type { Logger } from '../utils/logger.js';
|
||||||
import type { Engine } from "@xo-cash/engine";
|
import type { Engine } from "@xo-cash/engine";
|
||||||
import type { XOInvitation, XOInvitationCommit } from "@xo-cash/types";
|
import type { XOInvitation, XOInvitationCommit } from "@xo-cash/types";
|
||||||
|
|
||||||
@@ -11,7 +11,7 @@ export type OrderInvitationTrackerDeps = {
|
|||||||
database: Database;
|
database: Database;
|
||||||
orderId: string;
|
orderId: string;
|
||||||
invitation: XOInvitation;
|
invitation: XOInvitation;
|
||||||
debug: Debugger;
|
debug: Logger;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
import type { Debugger } from "debug";
|
import type { Logger } from '../utils/logger.js';
|
||||||
import type { Engine } from "@xo-cash/engine";
|
import type { Engine } from "@xo-cash/engine";
|
||||||
import { serializeInvitation } from "@xo-cash/engine";
|
import { serializeInvitation } from "@xo-cash/engine";
|
||||||
import { vendingMachineTemplate } from "../templates/vending-machine.js";
|
import { vendingMachineTemplate } from "../templates/vending-machine.js";
|
||||||
|
|
||||||
import type { Config } from "./config.js";
|
import type { Config } from "./config.js";
|
||||||
import type { Database } from "./database/database.js";
|
import type { Database } from "./database/database.js";
|
||||||
import type { ItemsTable } from "./database/tables.js";
|
|
||||||
import { InvitationSyncClient } from "./invitation-sync-client.js";
|
import { InvitationSyncClient } from "./invitation-sync-client.js";
|
||||||
import { OrderInvitationTracker } from "./order-invitation-tracker.js";
|
import { OrderInvitationTracker } from "./order-invitation-tracker.js";
|
||||||
|
|
||||||
@@ -40,7 +39,7 @@ export type OrderPaymentServiceDeps = {
|
|||||||
database: Database;
|
database: Database;
|
||||||
config: Config;
|
config: Config;
|
||||||
syncClient: InvitationSyncClient;
|
syncClient: InvitationSyncClient;
|
||||||
debug: Debugger;
|
debug: Logger;
|
||||||
templateIdentifier: string;
|
templateIdentifier: string;
|
||||||
trackers: Map<string, OrderInvitationTracker>;
|
trackers: Map<string, OrderInvitationTracker>;
|
||||||
};
|
};
|
||||||
@@ -55,7 +54,7 @@ export class OrderPaymentService {
|
|||||||
engine: Engine;
|
engine: Engine;
|
||||||
database: Database;
|
database: Database;
|
||||||
config: Config;
|
config: Config;
|
||||||
debug: Debugger;
|
debug: Logger;
|
||||||
trackers: Map<string, OrderInvitationTracker>;
|
trackers: Map<string, OrderInvitationTracker>;
|
||||||
}): Promise<OrderPaymentService> {
|
}): Promise<OrderPaymentService> {
|
||||||
const { templateIdentifier } = await deps.engine.importTemplate(
|
const { templateIdentifier } = await deps.engine.importTemplate(
|
||||||
|
|||||||
18
src/types.ts
18
src/types.ts
@@ -1,18 +0,0 @@
|
|||||||
import { type Debugger } from "debug";
|
|
||||||
import { z } from "zod";
|
|
||||||
|
|
||||||
export const isDebugger = (value: unknown): value is Debugger => {
|
|
||||||
if (typeof value !== "function") return false;
|
|
||||||
|
|
||||||
const candidate = value as Partial<Debugger>;
|
|
||||||
|
|
||||||
return (
|
|
||||||
typeof candidate.namespace === "string" &&
|
|
||||||
typeof candidate.extend === "function" &&
|
|
||||||
typeof candidate.enabled === "boolean"
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const debuggerSchema = z.custom<Debugger>(isDebugger, {
|
|
||||||
message: "Expected a debug.Debugger instance",
|
|
||||||
});
|
|
||||||
57
src/utils/logger.ts
Normal file
57
src/utils/logger.ts
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import Debug, { type Debugger } from "debug";
|
||||||
|
|
||||||
|
type LogHandler = {
|
||||||
|
(...args: Parameters<Debugger>): void;
|
||||||
|
extend: (namespace: string) => LogHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Declares that Logger instances may also be invoked as functions.
|
||||||
|
*/
|
||||||
|
export interface Logger {
|
||||||
|
(...args: unknown[]): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Logger {
|
||||||
|
public readonly namespace!: string;
|
||||||
|
|
||||||
|
private readonly handler!: LogHandler;
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
namespace: string,
|
||||||
|
handler: LogHandler = Debug(namespace),
|
||||||
|
) {
|
||||||
|
const logger = ((...args: Parameters<Debugger>): void => {
|
||||||
|
handler(...args);
|
||||||
|
}) as Logger;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This makes `logger instanceof Logger` true and provides
|
||||||
|
* access to instance methods such as `extend`.
|
||||||
|
*/
|
||||||
|
Object.setPrototypeOf(logger, new.target.prototype);
|
||||||
|
|
||||||
|
Object.defineProperties(logger, {
|
||||||
|
namespace: {
|
||||||
|
value: namespace,
|
||||||
|
enumerable: true,
|
||||||
|
},
|
||||||
|
handler: {
|
||||||
|
value: handler,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public extend(childNamespace: string): Logger {
|
||||||
|
return new Logger(
|
||||||
|
childNamespace,
|
||||||
|
this.handler.extend(childNamespace),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static isLogger(value: unknown): value is Logger {
|
||||||
|
return value instanceof Logger;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user