UNPKG

@krainovsd/fastify-logger

Version:
200 lines (192 loc) 7.2 kB
'use strict'; Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); const jsHelpers = require('@krainovsd/js-helpers'); const api = require('@opentelemetry/api'); const uuid = require('uuid'); const path = require('path'); function getTraceId() { return api.trace?.getActiveSpan?.()?.spanContext?.()?.traceId ?? undefined; } function getErrorInfo(err, stack = true) { const error = jsHelpers.getByPath(err, "message") ?? undefined; const errorStatus = jsHelpers.getByPath(err, "status") ?? undefined; const errorDescription = jsHelpers.getByPath(err, "description") ?? undefined; const errorStack = stack ? (err instanceof Error ? err.stack : undefined) : undefined; const traceID = getTraceId(); return { error, errorStatus, errorDescription, errorStack, traceID }; } function getRequestInfo(req) { const host = req.ip; const url = req.url; const method = req.method; const traceID = req.traceId ?? getTraceId(); const operationId = req.operationId ?? uuid.v4(); return { host, url, method, traceID, operationId }; } function getResponseInfo(res) { const status = res?.statusCode ? String(res.statusCode) : null; return { status }; } function isHasSpace(str) { return typeof str === "string" && str.includes(" "); } function getCorrectLog(obj, deniedProperties, format = "logfmt") { const correctObj = Object.fromEntries(Object.entries(obj).filter(([key, value]) => !(deniedProperties?.includes?.(key.toLowerCase()) || typeof value === "undefined"))); switch (format) { case "logfmt": { let log = ""; Object.entries(correctObj).forEach(([key, value]) => { if (isHasSpace(value)) { value = `"${value}"`; } log += `${key}=${value} `; }); // eslint-disable-next-line no-console console.log(log.trim()); break; } case "json": { // eslint-disable-next-line no-console console.log(correctObj); break; } } } class Logger { logger; constructor({ logger }) { this.logger = logger; } async loggerLayer({ action, processData, loggerExecute = { error: true, start: false, stop: false }, loggerMessage, loggerInfo, }) { try { if (this.isHasLoggerAction(loggerExecute, "start")) this.debug({ info: loggerInfo, message: `start ${loggerMessage}` }); const data = await action(); const processingData = processData ? await processData(data) : data; if (this.isHasLoggerAction(loggerExecute, "stop")) this.debug({ info: loggerInfo, message: `stop ${loggerMessage}` }); return processingData; } catch (error) { if (this.isHasLoggerAction(loggerExecute, "error")) this.warn({ info: loggerInfo, error, message: `error ${loggerMessage}` }); throw error; } } async controllerLayer(action, logger = true) { try { const result = await action(); return { data: result, status: 200, success: true, }; } catch (error) { if (logger) this.error({ error }); if (error instanceof jsHelpers.ResponseError) { return { data: { message: error.message }, status: error.status, success: false }; } throw error; } } debug({ info = {}, message = "debug" }) { this.logger.debug(info, message); } info({ info = {}, message = "info" }) { this.logger.info(info, message); } warn({ info = {}, message = "warn", error }) { const errorInfo = getErrorInfo(error, false); this.logger.warn({ ...errorInfo, ...info }, message); } error({ error, info = {}, message = "error" }) { const errorInfo = getErrorInfo(error); this.logger.error({ ...errorInfo, ...info }, message); } isHasLoggerAction(options, action) { if (jsHelpers.isBoolean(options)) return options; if (jsHelpers.isObject(options)) return Boolean(options[action]); return false; } } function defineTransport(settings = {}) { const { ext = ".cjs", ...rest } = settings; return { target: getTransportPath(ext), options: { ...rest, }, }; } function getTransportPath(ext) { return path.join(__dirname, `./transport.${ext.replace(/^./, "")}`); } function defineMiddlewares(fastify, settings = {}) { const logger = new Logger({ logger: fastify.log }); fastify.setErrorHandler(function onError(error, request, reply) { const status = error.statusCode ?? 500; if (status === 500 && (settings.errorLogFilter == undefined || settings.errorLogFilter(request))) { const requestInfo = getRequestInfo(request); logger.error({ error, info: requestInfo, message: "error" }); } settings.onError?.(error, request, reply); reply .status(status) .header("traceId", request.traceId) .header("operationId", request.operationId) .send({ traceId: request.traceId, operationId: request.operationId, message: error.message, }); }); fastify.addHook("onRequest", function onRequest(request, reply, done) { if (settings.accessLogFilter != undefined && !settings.accessLogFilter(request)) { done(); return; } const requestInfo = getRequestInfo(request); request.traceId = requestInfo.traceID; request.operationId = requestInfo.operationId; logger.info({ info: requestInfo, message: "receive request" }); settings.onRequest?.(request, reply); done(); }); fastify.addHook("onSend", async function onSend(request, reply, payload) { reply.header("traceId", request.traceId); reply.header("operationId", request.operationId); await settings.onSend?.(request, reply, payload); return payload; }); fastify.addHook("onResponse", function onResponse(request, reply, done) { if (settings.accessLogFilter != undefined && !settings.accessLogFilter(request)) { done(); return; } const requestInfo = getRequestInfo(request); const responseInfo = getResponseInfo(reply); settings.onResponse?.(request, reply); logger.info({ info: { ...requestInfo, ...responseInfo, }, message: "send response", }); done(); }); } exports.Logger = Logger; exports.defineMiddlewares = defineMiddlewares; exports.defineTransport = defineTransport; exports.getCorrectLog = getCorrectLog; exports.getErrorInfo = getErrorInfo; exports.getRequestInfo = getRequestInfo; exports.getResponseInfo = getResponseInfo; exports.getTraceId = getTraceId; //# sourceMappingURL=index.cjs.map