443 lines
20 KiB
JavaScript
443 lines
20 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.LoggedMiddleware = 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',
|
|
middleware: 'HIT MIDDLEWARE',
|
|
};
|
|
function createCallLogIdentifyMessage(type, key, route) {
|
|
if (type === 'route')
|
|
return `${callLogIdentifyMessageDictionary[type]} ${route} (${key})`;
|
|
if (type === 'guard' || type === 'interceptor' || type === 'middleware')
|
|
return `${callLogIdentifyMessageDictionary[type]} ${route}`;
|
|
if (type === 'function')
|
|
return `${callLogIdentifyMessageDictionary[type]} ${key}`;
|
|
return callLogIdentifyMessageDictionary[type];
|
|
}
|
|
const REQUEST_LOG_ID = '__nestlogged_request_log_id__';
|
|
function overrideBuild(type, originalFunction, baseLogger, metadatas, key, returnsData, logged, route) {
|
|
return function (...args) {
|
|
// Creating ScopedLogger
|
|
let injectedLogger = baseLogger;
|
|
if (typeof metadatas.scopedLoggerInjectableParam !== "undefined") {
|
|
if (type === 'function') {
|
|
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);
|
|
}
|
|
}
|
|
else {
|
|
// special, can access to request object
|
|
if (type === 'guard' || type === 'interceptor') {
|
|
// args[0] == ExecutionContext
|
|
const ctx = args[0];
|
|
if (ctx.getType() !== 'http') {
|
|
injectedLogger.error('Cannot inject logger: Request type is not http');
|
|
}
|
|
else {
|
|
let req = ctx.switchToHttp().getRequest();
|
|
if (req[REQUEST_LOG_ID] === undefined) {
|
|
req[REQUEST_LOG_ID] = logger_1.ScopedLogger.createScopeId();
|
|
}
|
|
args[metadatas.scopedLoggerInjectableParam] = logger_1.ScopedLogger.fromRoot(baseLogger, key, req[REQUEST_LOG_ID]);
|
|
}
|
|
}
|
|
else if (type === 'middleware') {
|
|
let req = args[0];
|
|
if (req[REQUEST_LOG_ID] === undefined) {
|
|
req[REQUEST_LOG_ID] = logger_1.ScopedLogger.createScopeId();
|
|
}
|
|
args[metadatas.scopedLoggerInjectableParam] = logger_1.ScopedLogger.fromRoot(baseLogger, key, req[REQUEST_LOG_ID]);
|
|
}
|
|
else if (type === 'route') {
|
|
// args[metadatas.scopedLoggerInjectableParam] is now Request object, thanks to code in @LoggedRoute!!!!
|
|
let req = args[metadatas.scopedLoggerInjectableParam];
|
|
if (req[REQUEST_LOG_ID] === undefined) {
|
|
req[REQUEST_LOG_ID] = logger_1.ScopedLogger.createScopeId();
|
|
}
|
|
args[metadatas.scopedLoggerInjectableParam] = logger_1.ScopedLogger.fromRoot(baseLogger, key, req[REQUEST_LOG_ID]);
|
|
}
|
|
}
|
|
injectedLogger = args[metadatas.scopedLoggerInjectableParam];
|
|
}
|
|
// If this is ExecutionContext based function (e.g. Guard, Interceptor) get Request from Context
|
|
if (type === 'guard' || type === 'interceptor') {
|
|
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 === 'middleware' || type === 'guard' || type === 'interceptor'
|
|
? createCallLogIdentifyMessage(type, route)
|
|
: type === 'route'
|
|
? 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);
|
|
// if @InjectLogger exists, fake nestjs as it is @Req()
|
|
if (scopedLoggerInjectableParam !== undefined) {
|
|
(0, reflected_1.createRouteParamDecorator)(0)()(_target, key, scopedLoggerInjectableParam);
|
|
}
|
|
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: [],
|
|
}, _target.constructor.name, 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: [],
|
|
}, _target.constructor.name, 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;
|
|
function LoggedMiddleware(options) {
|
|
return (_target, key, descriptor) => {
|
|
loggerInit(_target);
|
|
const logger = _target.logger;
|
|
const fn = descriptor.value;
|
|
if (!fn || typeof fn !== "function") {
|
|
logger.warn(`LoggedMiddleware 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('middleware', fn, logger, {
|
|
scopedLoggerInjectableParam,
|
|
loggedParams: [],
|
|
}, _target.constructor.name, 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.LoggedMiddleware = LoggedMiddleware;
|