238 lines
6.2 KiB
TypeScript
238 lines
6.2 KiB
TypeScript
import {
|
|
Logger,
|
|
Injectable,
|
|
Controller,
|
|
ControllerOptions,
|
|
ScopeOptions,
|
|
} from "@nestjs/common";
|
|
import { ScopedLogger } from "./logger";
|
|
import { LoggedParamReflectData } from "./reflected";
|
|
import { loggedParam, scopedLogger } from "./reflected";
|
|
import objectContainedLogged from "./functions";
|
|
|
|
function loggerInit(_target: any) {
|
|
if (!Object.getOwnPropertyNames(_target).includes("logger")) {
|
|
const newTargetLogger = new Logger(_target.constructor.name);
|
|
newTargetLogger.log("Logger Initialized.");
|
|
Object.defineProperty(_target, "logger", {
|
|
writable: false,
|
|
enumerable: false,
|
|
configurable: false,
|
|
value: newTargetLogger,
|
|
});
|
|
}
|
|
}
|
|
|
|
export function LoggedInjectable(options?: ScopeOptions) {
|
|
return (target: any) => {
|
|
target = Injectable(options)(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"
|
|
) {
|
|
logger.log(`LoggedFunction applied to ${method}`);
|
|
LoggedFunction(target.prototype, method, {
|
|
value: target.prototype[method],
|
|
});
|
|
}
|
|
});
|
|
};
|
|
}
|
|
|
|
export function LoggedController(): (target: any) => void;
|
|
export function LoggedController(
|
|
prefix: string | string[]
|
|
): (target: any) => void;
|
|
export function LoggedController(
|
|
options: ControllerOptions
|
|
): (target: any) => void;
|
|
|
|
export function LoggedController(param?: any): (target: any) => void {
|
|
return (target: any) => {
|
|
target = Controller(param)(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"
|
|
) {
|
|
logger.log(`LoggedRoute applied to ${method}`);
|
|
LoggedRoute()(target.prototype, method, {
|
|
value: target.prototype[method],
|
|
});
|
|
}
|
|
});
|
|
};
|
|
}
|
|
|
|
export function LoggedFunction<F extends Array<any>, R>(
|
|
_target: any,
|
|
key: string,
|
|
descriptor: TypedPropertyDescriptor<(...args: F) => Promise<R>>
|
|
) {
|
|
loggerInit(_target);
|
|
|
|
const logger: Logger = _target.logger;
|
|
|
|
const fn = descriptor.value;
|
|
|
|
if (!fn || typeof fn !== "function") {
|
|
logger.warn(
|
|
`LoggedFunction decorator applied to non-function property: ${key}`
|
|
);
|
|
return;
|
|
}
|
|
|
|
_target[key] = async function (...args: F) {
|
|
const scopedLoggerInjectableParam: number = Reflect.getOwnMetadata(
|
|
scopedLogger,
|
|
_target,
|
|
key
|
|
);
|
|
|
|
if (
|
|
typeof scopedLoggerInjectableParam !== "undefined" &&
|
|
(args.length <= scopedLoggerInjectableParam ||
|
|
!(args[scopedLoggerInjectableParam] instanceof ScopedLogger))
|
|
) {
|
|
args[scopedLoggerInjectableParam] = new ScopedLogger(logger, key);
|
|
} else if (typeof scopedLoggerInjectableParam !== "undefined") {
|
|
args[scopedLoggerInjectableParam] = new ScopedLogger(
|
|
args[scopedLoggerInjectableParam],
|
|
key
|
|
);
|
|
}
|
|
|
|
const injectedLogger =
|
|
typeof scopedLoggerInjectableParam !== "undefined"
|
|
? args[scopedLoggerInjectableParam]
|
|
: logger;
|
|
|
|
const loggedParams: LoggedParamReflectData[] = Reflect.getOwnMetadata(
|
|
loggedParam,
|
|
_target,
|
|
key
|
|
);
|
|
|
|
injectedLogger.log(
|
|
`CALL ${key} ${
|
|
loggedParams && loggedParams.length > 0
|
|
? "WITH " +
|
|
(
|
|
await Promise.all(
|
|
loggedParams.map(
|
|
async ({ name, index, include, exclude }) =>
|
|
name +
|
|
"=" +
|
|
(await objectContainedLogged(args[index], {
|
|
include,
|
|
exclude,
|
|
}))
|
|
)
|
|
)
|
|
).join(", ")
|
|
: ""
|
|
}`
|
|
);
|
|
|
|
try {
|
|
const r: R = await fn.call(this, ...args);
|
|
injectedLogger.log(`RETURNED ${key}`);
|
|
return r;
|
|
} catch (e) {
|
|
injectedLogger.error(`WHILE ${key} ERROR ${e}`);
|
|
throw e;
|
|
}
|
|
};
|
|
}
|
|
|
|
export function LoggedRoute<F extends Array<any>, R>(route?: string) {
|
|
return (
|
|
_target: any,
|
|
key: string,
|
|
descriptor: TypedPropertyDescriptor<(...args: F) => Promise<R>>
|
|
) => {
|
|
loggerInit(_target);
|
|
|
|
const logger = _target.logger;
|
|
|
|
let fullRoute = `${_target.constructor.name}/`;
|
|
const fn = descriptor.value;
|
|
|
|
if (!fn) return;
|
|
|
|
descriptor.value = async function (...args: F) {
|
|
const scopedLoggerInjectableParam: number = Reflect.getOwnMetadata(
|
|
scopedLogger,
|
|
_target,
|
|
key
|
|
);
|
|
|
|
fullRoute += route || Reflect.getMetadata("path", fn);
|
|
|
|
if (
|
|
typeof scopedLoggerInjectableParam !== "undefined" &&
|
|
(args.length <= scopedLoggerInjectableParam ||
|
|
!(args[scopedLoggerInjectableParam] instanceof ScopedLogger))
|
|
) {
|
|
args[scopedLoggerInjectableParam] = new ScopedLogger(logger, fullRoute);
|
|
}
|
|
|
|
const injectedLogger =
|
|
typeof scopedLoggerInjectableParam !== "undefined"
|
|
? args[scopedLoggerInjectableParam]
|
|
: logger;
|
|
|
|
const loggedParams: LoggedParamReflectData[] = Reflect.getOwnMetadata(
|
|
loggedParam,
|
|
_target,
|
|
key
|
|
);
|
|
|
|
injectedLogger.log(
|
|
`HIT HTTP ${fullRoute} (${key}) ${
|
|
loggedParams && loggedParams.length > 0
|
|
? "WITH " +
|
|
(
|
|
await Promise.all(
|
|
loggedParams.map(
|
|
async ({ name, index, include, exclude }) =>
|
|
name +
|
|
"=" +
|
|
(await objectContainedLogged(args[index], {
|
|
include,
|
|
exclude,
|
|
}))
|
|
)
|
|
)
|
|
).join(", ")
|
|
: ""
|
|
}`
|
|
);
|
|
|
|
try {
|
|
const r: R = await fn.call(this, ...args);
|
|
injectedLogger.log(`RETURNED RESPONSE ${fullRoute} (${key})`);
|
|
return r;
|
|
} catch (e) {
|
|
injectedLogger.error(`WHILE HTTP ${fullRoute} (${key}) ERROR ${e}`);
|
|
throw e;
|
|
}
|
|
};
|
|
};
|
|
}
|