@krainovsd/fastify-logger
Version:
Krainov fastify logger
200 lines (192 loc) • 7.2 kB
JavaScript
;
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