@rollercoaster-dev/rd-logger
Version:
A neurodivergent-friendly logger for Rollercoaster.dev projects
115 lines (114 loc) • 4.89 kB
JavaScript
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);
};
};