"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.LoggedInterceptor = exports.LoggedGuard = exports.LoggedRoute = exports.LoggedFunction = exports.LoggedController = exports.LoggedInjectable = void 0; const common_1 = require("@nestjs/common"); const logger_1 = require("./logger"); const reflected_1 = require("./reflected"); const functions_1 = require("./functions"); const RevRequestMethod = [ "GET", "POST", "PUT", "DELETE", "PATCH", "ALL", "OPTIONS", "HEAD", "SEARCH", ]; function loggerInit(_target) { if (!Object.getOwnPropertyNames(_target).includes("logger")) { const newTargetLogger = new common_1.Logger(_target.constructor.name); newTargetLogger.log("Logger Initialized."); Object.defineProperty(_target, "logger", { writable: false, enumerable: false, configurable: false, value: newTargetLogger, }); } } function LoggedInjectable(options) { return (target) => { loggerInit(target.prototype); const logger = target.prototype.logger; const methods = Object.getOwnPropertyNames(target.prototype); methods.forEach((method) => { if (method !== "constructor" && typeof target.prototype[method] === "function") { if (options && options.verbose) logger.log(`LoggedFunction applied to ${method}`); LoggedFunction()(target.prototype, method, { value: target.prototype[method], }); } }); (0, common_1.Injectable)(options)(target); }; } exports.LoggedInjectable = LoggedInjectable; function LoggedController(param) { return (target) => { loggerInit(target.prototype); const logger = target.prototype.logger; const methods = Object.getOwnPropertyNames(target.prototype); let verbose = typeof param === "object" && Object.keys(param).includes("verbose") ? param.verbose : false; methods.forEach((method) => { if (method !== "constructor" && typeof target.prototype[method] === "function") { if (verbose) { const path = Reflect.getMetadata("path", target.prototype[method]); const httpMethod = Reflect.getMetadata("method", target.prototype[method]); logger.log(`LoggedRoute applied to ${method} (${RevRequestMethod[httpMethod]} ${path})`); } LoggedRoute()(target.prototype, method, { value: target.prototype[method], }); } }); (0, common_1.Controller)(param)(target); }; } exports.LoggedController = LoggedController; const defaultOverrideBuildOptions = { callLogLevel: 'log', returnLogLevel: 'log', errorLogLevel: 'error', skipCallLog: false, skipReturnLog: false, skipErrorLog: false, }; class LoggedMetadata { constructor(options) { this.options = { ...defaultOverrideBuildOptions, ...(options ?? {}), }; } updateOption(options) { this.options = { ...this.options, ...options }; } } const callLogIdentifyMessageDictionary = { route: 'HIT HTTP', function: 'CALL', guard: 'HIT GUARD', interceptor: 'HIT INTERCEPTOR' }; function createCallLogIdentifyMessage(type, key, route) { return `${callLogIdentifyMessageDictionary[type]} ${type === 'route' ? `${route} (${key})` : type === 'guard' ? `${route}` : key}`; } function overrideBuild(type, originalFunction, baseLogger, metadatas, key, returnsData, logged, route) { return function (...args) { // Creating ScopedLogger let injectedLogger = baseLogger; if (typeof metadatas.scopedLoggerInjectableParam !== "undefined") { if (args.length <= metadatas.scopedLoggerInjectableParam || !(args[metadatas.scopedLoggerInjectableParam] instanceof logger_1.ScopedLogger)) { args[metadatas.scopedLoggerInjectableParam] = logger_1.ScopedLogger.fromRoot(baseLogger, key); } else { args[metadatas.scopedLoggerInjectableParam] = logger_1.ScopedLogger.fromSuper(baseLogger, args[metadatas.scopedLoggerInjectableParam], key); } injectedLogger = args[metadatas.scopedLoggerInjectableParam]; } // If this is ExecutionContext based function (e.g. Guard, Interceptor) get Request from Context if (type === 'guard') { const context = args[0]; if (context.getType() === 'http') { const req = context.switchToHttp().getRequest(); route = new URL(( /* supporting FastifyRequest */req.raw ? req.raw.url : req.url)).pathname; } } // Start Log if (logged.options.callLogLevel !== 'skip') { const callLogIdentifyMessage = type === 'route' || type === 'guard' || type === 'interceptor' ? createCallLogIdentifyMessage(type, key, route) : createCallLogIdentifyMessage(type, key); injectedLogger[logged.options.callLogLevel](`${callLogIdentifyMessage} ${metadatas.loggedParams && metadatas.loggedParams.length > 0 ? "WITH " + metadatas.loggedParams.map(({ name, index, include, exclude }) => name + "=" + (0, functions_1.imObjectContainedLogSync)(args[index], { include, exclude, })).join(", ") : ""}`); } try { const r = originalFunction.call(this, ...args); // Try to call original function // Return Log if (logged.options.returnLogLevel !== 'skip') { if (originalFunction.constructor.name === 'AsyncFunction' || (r && typeof r === 'object' && typeof r['then'] === 'function')) { return r['then']((r) => { const resultLogged = Array.isArray(returnsData) ? typeof r === "object" && r !== null ? "WITH " + returnsData.map(({ name, path }) => { const value = (0, functions_1.getItemByPathSync)(r, path); return value !== undefined ? `${name}=${value}` : ""; }) .filter((v) => v.length > 0) .join(", ") : "" : typeof returnsData === 'string' ? "WITH " + returnsData + "=" + typeof r === "object" ? JSON.stringify(r) : r : returnsData ? typeof r === "object" ? "WITH " + JSON.stringify(r) : "WITH " + r : ""; injectedLogger[logged.options.returnLogLevel](route ? `RETURNED HTTP ${route} (${key}) ${resultLogged}` : `RETURNED ${key} ${resultLogged}`); return r; }); } else { const resultLogged = Array.isArray(returnsData) ? typeof r === "object" && r !== null ? "WITH " + returnsData.map(({ name, path }) => { const value = (0, functions_1.getItemByPathSync)(r, path); return value !== undefined ? `${name}=${value}` : ""; }) .filter((v) => v.length > 0) .join(", ") : "" : typeof returnsData === 'string' ? "WITH " + returnsData + "=" + typeof r === "object" ? JSON.stringify(r) : r : returnsData ? typeof r === "object" ? "WITH " + JSON.stringify(r) : "WITH " + r : ""; injectedLogger[logged.options.returnLogLevel](route ? `RETURNED HTTP ${route} (${key}) ${resultLogged}` : `RETURNED ${key} ${resultLogged}`); return r; } } else { return r; } } catch (e) { // Error Log if (logged.options.errorLogLevel !== 'skip') { injectedLogger[logged.options.errorLogLevel](`WHILE ${route ? `HTTP ${route} (${key})` : key} ERROR ${e}`); } throw e; } }; } function LoggedFunction(options) { return (_target, key, descriptor) => { loggerInit(_target); const logger = _target.logger; const fn = descriptor.value; if (!fn || typeof fn !== "function") { logger.warn(`LoggedFunction decorator applied to non-function property: ${key}`); return; } const logMetadata = Reflect.getOwnMetadata(reflected_1.nestLoggedMetadata, _target, key); if (logMetadata) { // already applied, override instead logMetadata.updateOption(options); return; } const newMetadata = new LoggedMetadata(options); const all = Reflect.getMetadataKeys(fn).map((k) => [ k, Reflect.getMetadata(k, fn), ]); const scopedLoggerInjectableParam = Reflect.getOwnMetadata(reflected_1.scopedLogger, _target, key); const loggedParams = Reflect.getOwnMetadata(reflected_1.loggedParam, _target, key); const returnsData = Reflect.getOwnMetadata(reflected_1.returns, fn); const overrideFunction = overrideBuild('function', fn, logger, { scopedLoggerInjectableParam, loggedParams, }, key, returnsData, newMetadata); _target[key] = overrideFunction; descriptor.value = overrideFunction; Reflect.defineMetadata(reflected_1.nestLoggedMetadata, newMetadata, _target, key); all.forEach(([k, v]) => { Reflect.defineMetadata(k, v, _target[key]); Reflect.defineMetadata(k, v, descriptor.value); }); }; } exports.LoggedFunction = LoggedFunction; function LoggedRoute(route, options) { return (_target, key, descriptor) => { loggerInit(_target); const logger = _target.logger; const fn = descriptor.value; if (!fn || typeof fn !== "function") { logger.warn(`LoggedRoute decorator applied to non-function property: ${key}`); return; } const logMetadata = Reflect.getOwnMetadata(reflected_1.nestLoggedMetadata, _target, key); if (logMetadata) { // already applied, override instead logMetadata.updateOption(options); return; } const newMetadata = new LoggedMetadata(options); const all = Reflect.getMetadataKeys(fn).map((k) => [ k, Reflect.getMetadata(k, fn), ]); const httpPath = Reflect.getMetadata("path", fn); const httpMethod = Reflect.getMetadata("method", fn); const fullRoute = `${_target.constructor.name}::${route ?? httpPath}[${RevRequestMethod[httpMethod]}]`; const scopedLoggerInjectableParam = Reflect.getOwnMetadata(reflected_1.scopedLogger, _target, key); const loggedParams = Reflect.getOwnMetadata(reflected_1.loggedParam, _target, key); const returnsData = Reflect.getOwnMetadata(reflected_1.returns, fn); const overrideFunction = overrideBuild('route', fn, logger, { scopedLoggerInjectableParam, loggedParams, }, key, returnsData, newMetadata, fullRoute); _target[key] = overrideFunction; descriptor.value = overrideFunction; Reflect.defineMetadata(reflected_1.nestLoggedMetadata, newMetadata, _target, key); all.forEach(([k, v]) => { Reflect.defineMetadata(k, v, _target[key]); Reflect.defineMetadata(k, v, descriptor.value); }); }; } exports.LoggedRoute = LoggedRoute; function LoggedGuard(options) { return (_target, key, descriptor) => { loggerInit(_target); const logger = _target.logger; const fn = descriptor.value; if (!fn || typeof fn !== "function") { logger.warn(`LoggedGuard decorator applied to non-function property: ${key}`); return; } const logMetadata = Reflect.getOwnMetadata(reflected_1.nestLoggedMetadata, _target, key); if (logMetadata) { // already applied, override instead logMetadata.updateOption(options); return; } const newMetadata = new LoggedMetadata(options); const all = Reflect.getMetadataKeys(fn).map((k) => [ k, Reflect.getMetadata(k, fn), ]); const scopedLoggerInjectableParam = Reflect.getOwnMetadata(reflected_1.scopedLogger, _target, key); const returnsData = Reflect.getOwnMetadata(reflected_1.returns, fn); const overrideFunction = overrideBuild('guard', fn, logger, { scopedLoggerInjectableParam, loggedParams: [], }, key, returnsData, newMetadata); _target[key] = overrideFunction; descriptor.value = overrideFunction; Reflect.defineMetadata(reflected_1.nestLoggedMetadata, newMetadata, _target, key); all.forEach(([k, v]) => { Reflect.defineMetadata(k, v, _target[key]); Reflect.defineMetadata(k, v, descriptor.value); }); }; } exports.LoggedGuard = LoggedGuard; function LoggedInterceptor(options) { return (_target, key, descriptor) => { loggerInit(_target); const logger = _target.logger; const fn = descriptor.value; if (!fn || typeof fn !== "function") { logger.warn(`LoggedInterceptor decorator applied to non-function property: ${key}`); return; } const logMetadata = Reflect.getOwnMetadata(reflected_1.nestLoggedMetadata, _target, key); if (logMetadata) { // already applied, override instead logMetadata.updateOption(options); return; } const newMetadata = new LoggedMetadata(options); const all = Reflect.getMetadataKeys(fn).map((k) => [ k, Reflect.getMetadata(k, fn), ]); const scopedLoggerInjectableParam = Reflect.getOwnMetadata(reflected_1.scopedLogger, _target, key); const returnsData = Reflect.getOwnMetadata(reflected_1.returns, fn); const overrideFunction = overrideBuild('interceptor', fn, logger, { scopedLoggerInjectableParam, loggedParams: [], }, key, returnsData, newMetadata); _target[key] = overrideFunction; descriptor.value = overrideFunction; Reflect.defineMetadata(reflected_1.nestLoggedMetadata, newMetadata, _target, key); all.forEach(([k, v]) => { Reflect.defineMetadata(k, v, _target[key]); Reflect.defineMetadata(k, v, descriptor.value); }); }; } exports.LoggedInterceptor = LoggedInterceptor;