UNPKG

moleculer

Version:

Fast & powerful microservices framework for Node.JS

185 lines (157 loc) 4.24 kB
/* * moleculer * Copyright (c) 2019 MoleculerJS (https://github.com/moleculerjs/moleculer) * MIT Licensed */ "use strict"; const _ = require("lodash"); const kleur = require("kleur"); const fs = require("fs"); const path = require("path"); const { makeDirs, match, isObject, safetyObject } = require("../../utils"); module.exports = function ActionLoggerMiddleware(opts) { opts = _.defaultsDeep(opts, { logger: null, logLevel: "info", logParams: false, logResponse: false, logMeta: false, folder: null, extension: ".json", colors: { request: "yellow", response: "cyan", error: "red" }, whitelist: ["**"] }); let logger; let nodeID; let targetFolder; function saveToFile(filename, payload) { let data; if (payload === undefined) { data = "<undefined>"; } else { try { data = JSON.stringify( payload, payload instanceof Error ? Object.getOwnPropertyNames(payload) : null, 4 ); } catch { data = JSON.stringify( safetyObject(payload), payload instanceof Error ? Object.getOwnPropertyNames(payload) : null, 4 ); } } fs.writeFile(path.join(targetFolder, filename), data, () => { /* Silent error */ }); } function isWhiteListed(actionName) { return !!opts.whitelist.find(pattern => match(actionName, pattern)); } const coloringRequest = opts.colors && opts.colors.request ? opts.colors.request.split(".").reduce((a, b) => a[b] || a()[b], kleur) : s => s; const coloringResponse = opts.colors && opts.colors.response ? opts.colors.response.split(".").reduce((a, b) => a[b] || a()[b], kleur) : s => s; const coloringError = opts.colors && opts.colors.error ? opts.colors.error.split(".").reduce((a, b) => a[b] || a()[b], kleur) : s => s; let logFn; return { name: "ActionLogger", created(broker) { logger = opts.logger || broker.getLogger("debug"); nodeID = broker.nodeID; if (opts.folder) { targetFolder = path.join(opts.folder, nodeID); makeDirs(targetFolder); } logFn = opts.logLevel ? logger[opts.logLevel] : null; }, call(next) { return (actionName, params, callingOpts) => { // Whitelist filtering if ( !isWhiteListed( isObject(actionName) ? /** @type {Record<string, any>} */ (actionName).action.name : actionName ) ) { return next(actionName, params, callingOpts); } // Logging to logger if (logFn) { const msg = coloringRequest( `Calling '${actionName}'` + (opts.logParams ? " with params:" : ".") ); opts.logParams ? logFn(msg, params) : logFn(msg); if (opts.logMeta && callingOpts && callingOpts.meta) { logFn("Meta:", callingOpts.meta); } } // Logging to file if (targetFolder) { if (opts.logParams) { saveToFile( `${Date.now()}-call-${actionName}-request${opts.extension}`, params ); } if (opts.logMeta && callingOpts && callingOpts.meta) { saveToFile( `${Date.now()}-call-${actionName}-meta${opts.extension}`, callingOpts.meta ); } } // Call the original method const p = next(actionName, params, callingOpts); const p2 = p .then(response => { // Log response to logger if (logFn) { const msg = coloringResponse( `Response for '${actionName}' is received` + (opts.logResponse ? ":" : ".") ); opts.logResponse ? logFn(msg, response) : logFn(msg); } // Log response to file if (targetFolder && opts.logResponse) saveToFile( `${Date.now()}-call-${actionName}-response${opts.extension}`, response ); return response; }) .catch(err => { // Log error to logger if (logFn) { logFn(coloringError(`Error for '${actionName}' is received:`), err); } // Logger error to file if (targetFolder && opts.logResponse) saveToFile( `${Date.now()}-call-${actionName}-error${opts.extension}`, err ); throw err; }); // Context issue workaround: https://github.com/moleculerjs/moleculer/issues/413 p2.ctx = p.ctx; return p2; }; } }; };