UNPKG

@rollercoaster-dev/rd-logger

Version:

A neurodivergent-friendly logger for Rollercoaster.dev projects

115 lines (114 loc) 4.89 kB
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; import { Logger } from '../core/logger.service'; import { runWithRequestContext, getRequestStore, } from '../core/request-context'; /** * Hono middleware for neuro-friendly logging and request context management. * * @param options Configuration options for the logger middleware. * @returns Hono MiddlewareHandler. */ export const honoLogger = (options = {}) => { const logger = options.loggerInstance || new Logger(options.loggerOptions); return (c, next) => __awaiter(void 0, void 0, void 0, function* () { const existingRequestId = c.req.header('x-request-id'); yield runWithRequestContext(() => __awaiter(void 0, void 0, void 0, function* () { var _a; const store = getRequestStore(); const requestId = store.requestId; const startTime = store.requestStartTime; // Set requestId in Hono context for access in handlers c.set('requestId', requestId); // Set response header c.res.headers.set('x-request-id', requestId); // Check if logging should be skipped if ((_a = options.skip) === null || _a === void 0 ? void 0 : _a.call(options, c)) { yield next(); return; } // Log incoming request const method = c.req.method; const path = new URL(c.req.url).pathname; const requestMsg = options.requestMessage ? options.requestMessage(c, store) : `▶ Incoming request`; logger.info(requestMsg, { method, path, requestId, }); // Proceed with the request handling try { yield next(); } catch (error) { // Log uncaught errors that might bypass Hono's default error handler // Note: It's generally better to rely on Hono's built-in .onError if (error instanceof Error) { logger.error(`Middleware error: ${error.message}`, { error: error, // Pass the raw error object requestId, }); } // Re-throw the error to let Hono handle the response throw error; } // Log outgoing response const duration = Date.now() - startTime; const status = c.res.status; const responseMsg = options.responseMessage ? options.responseMessage(c, store, duration, status) : `◀ Request completed`; const logLevel = status >= 500 ? 'error' : status >= 400 ? 'warn' : 'info'; logger[logLevel](responseMsg, { method, path, status, duration: `${duration}ms`, requestId, }); }), existingRequestId); }); }; /** * Hono error handler that logs errors using the logger and request context. * * Usage: * ```typescript * import { Hono } from 'hono'; * import { honoErrorHandler } from './path/to/honoErrorHandler'; * import { Logger } from './path/to/logger'; * * const app = new Hono(); * const logger = new Logger(); * * app.onError(honoErrorHandler(logger)); * // ... other middleware and routes * ``` * * @param loggerInstance An instance of the Logger. * @returns Hono ErrorHandler. */ export const honoErrorHandler = (loggerInstance) => { return (err, c) => { const store = getRequestStore(); // Get context established by honoLogger middleware const requestId = (store === null || store === void 0 ? void 0 : store.requestId) || c.req.header('x-request-id') || 'unknown'; loggerInstance.error(`Unhandled Hono error: ${err.message}`, { error: err, requestId, method: c.req.method, path: new URL(c.req.url).pathname, }); // Default Hono error response console.error(err); // Log the raw error for visibility const message = 'Internal Server Error'; return c.json({ ok: false, message }, 500); }; };