refactor
This commit is contained in:
parent
f13a1d4c63
commit
c792dbdcd6
31
dist/README.md
vendored
31
dist/README.md
vendored
@ -1,10 +1,28 @@
|
|||||||
# NestLoggedDecorators
|
# NestLoggedDecorators
|
||||||
|
|
||||||
This package provides some decorations to make NestJS logging simpler.
|
This package provides some decorations to make NestJS logging simpler.
|
||||||
It only uses Logger provided by @nestjs/common package and some dependencies required for nestjs.
|
It only uses Logger provided by @nestjs/common package and some dependencies required for nestjs.
|
||||||
|
|
||||||
|
> TODO: Improve README, providing Quickstart section, add wiki to github
|
||||||
|
|
||||||
## How to use
|
## How to use
|
||||||
|
|
||||||
|
### Installation
|
||||||
|
|
||||||
|
npm:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm i nestlogged
|
||||||
|
```
|
||||||
|
|
||||||
|
yarn:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
yarn add nestlogged
|
||||||
|
```
|
||||||
|
|
||||||
### Route Logging
|
### Route Logging
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { Controller, Get } from "@nestjs/common";
|
import { Controller, Get } from "@nestjs/common";
|
||||||
import { LoggedRoute } from "nlogdec";
|
import { LoggedRoute } from "nlogdec";
|
||||||
@ -21,7 +39,7 @@ export class WhateverController {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
```
|
```md
|
||||||
[Nest] 000000 - 00/00/0000, 00:00:00 AM LOG [WhateverController] HIT HTTP WhateverController//you/like (whateverYouLikeImpl)
|
[Nest] 000000 - 00/00/0000, 00:00:00 AM LOG [WhateverController] HIT HTTP WhateverController//you/like (whateverYouLikeImpl)
|
||||||
[Nest] 000000 - 00/00/0000, 00:00:00 AM LOG [WhateverController] RETURNED RESPONSE WhateverController//you/like (whateverYouLikeImpl)
|
[Nest] 000000 - 00/00/0000, 00:00:00 AM LOG [WhateverController] RETURNED RESPONSE WhateverController//you/like (whateverYouLikeImpl)
|
||||||
```
|
```
|
||||||
@ -46,7 +64,7 @@ export class WhateverController {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
```
|
```md
|
||||||
[Nest] 000000 - 00/00/0000, 00:00:00 AM LOG [WhateverController] HIT HTTP WhateverController//you/like (whateverYouLikeImpl)
|
[Nest] 000000 - 00/00/0000, 00:00:00 AM LOG [WhateverController] HIT HTTP WhateverController//you/like (whateverYouLikeImpl)
|
||||||
[Nest] 000000 - 00/00/0000, 00:00:00 AM LOG [WhateverController] WHILE HTTP WhateverController//you/like (whateverYouLikeImpl) ERROR BadRequestException: I don't like this
|
[Nest] 000000 - 00/00/0000, 00:00:00 AM LOG [WhateverController] WHILE HTTP WhateverController//you/like (whateverYouLikeImpl) ERROR BadRequestException: I don't like this
|
||||||
```
|
```
|
||||||
@ -71,7 +89,7 @@ export class WhateverController {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
```
|
```md
|
||||||
[Nest] 000000 - 00/00/0000, 00:00:00 AM LOG [WhateverController] HIT HTTP WhateverController/you/like (whateverYouLikeImpl)
|
[Nest] 000000 - 00/00/0000, 00:00:00 AM LOG [WhateverController] HIT HTTP WhateverController/you/like (whateverYouLikeImpl)
|
||||||
[Nest] 000000 - 00/00/0000, 00:00:00 AM LOG [WhateverController] WHILE HTTP WhateverController/you/like (whateverYouLikeImpl) ERROR BadRequestException: I don't like this
|
[Nest] 000000 - 00/00/0000, 00:00:00 AM LOG [WhateverController] WHILE HTTP WhateverController/you/like (whateverYouLikeImpl) ERROR BadRequestException: I don't like this
|
||||||
```
|
```
|
||||||
@ -81,6 +99,7 @@ You feel the change?
|
|||||||
Logged path is slightly changed from `WhateverController//you/like` to `WhateverController/you/like`.
|
Logged path is slightly changed from `WhateverController//you/like` to `WhateverController/you/like`.
|
||||||
|
|
||||||
### Function Logging
|
### Function Logging
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { LoggedFunction } from "nlogdec";
|
import { LoggedFunction } from "nlogdec";
|
||||||
|
|
||||||
@ -92,7 +111,7 @@ export async function doILikeThis(stuff: "apple" | "banana"): "yes" | "no" {
|
|||||||
|
|
||||||
LoggedFunction decorator will log function calls and returns for you.
|
LoggedFunction decorator will log function calls and returns for you.
|
||||||
|
|
||||||
**Note: This decorator is expected to be used with a class method. You can't use this outside of class**
|
> Note: This decorator is expected to be used with a class method. You can't use this outside of class
|
||||||
|
|
||||||
Like `LoggedRoute` decorator, it will automatically catch all exceptions, log it, and throw it again.
|
Like `LoggedRoute` decorator, it will automatically catch all exceptions, log it, and throw it again.
|
||||||
|
|
||||||
@ -111,7 +130,8 @@ export async function doILikeThis(
|
|||||||
|
|
||||||
doILikeThis("apple")
|
doILikeThis("apple")
|
||||||
```
|
```
|
||||||
```
|
|
||||||
|
```md
|
||||||
HIT HTTP WhateverController//you/like (whateverYouLikeImpl) WITH stuff="apple"
|
HIT HTTP WhateverController//you/like (whateverYouLikeImpl) WITH stuff="apple"
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -121,6 +141,7 @@ The name of parameter is decided by the first parameter of LoggedParam decorator
|
|||||||
This decorator also can be used with `LoggedRoute`.
|
This decorator also can be used with `LoggedRoute`.
|
||||||
|
|
||||||
### Class Logging
|
### Class Logging
|
||||||
|
|
||||||
You can make all method in injectable classes to logged function.
|
You can make all method in injectable classes to logged function.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
|
4
dist/package.json
vendored
4
dist/package.json
vendored
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "nlogdec",
|
"name": "nestlogged",
|
||||||
"version": "1.2.3",
|
"version": "1.0.0",
|
||||||
"description": "A NestJS Logger Decorator Library",
|
"description": "A NestJS Logger Decorator Library",
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"repository": "https://github.com/worplo/nestlogged",
|
"repository": "https://github.com/worplo/nestlogged",
|
||||||
|
335
src/logged.ts
335
src/logged.ts
@ -6,7 +6,12 @@ import {
|
|||||||
ScopeOptions,
|
ScopeOptions,
|
||||||
} from "@nestjs/common";
|
} from "@nestjs/common";
|
||||||
import { ScopedLogger } from "./logger";
|
import { ScopedLogger } from "./logger";
|
||||||
import { LoggedParamReflectData } from "./reflected";
|
import {
|
||||||
|
LoggedParamReflectData,
|
||||||
|
ScopeKeyReflectData,
|
||||||
|
forceScopeKey,
|
||||||
|
scopeKey,
|
||||||
|
} from "./reflected";
|
||||||
import { loggedParam, scopedLogger } from "./reflected";
|
import { loggedParam, scopedLogger } from "./reflected";
|
||||||
import objectContainedLogged from "./functions";
|
import objectContainedLogged from "./functions";
|
||||||
import { RequestMethod } from "@nestjs/common";
|
import { RequestMethod } from "@nestjs/common";
|
||||||
@ -36,7 +41,9 @@ function loggerInit(_target: any) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function LoggedInjectable(options?: ScopeOptions) {
|
export function LoggedInjectable(
|
||||||
|
options?: ScopeOptions & { verbose?: boolean }
|
||||||
|
) {
|
||||||
return (target: any) => {
|
return (target: any) => {
|
||||||
loggerInit(target.prototype);
|
loggerInit(target.prototype);
|
||||||
|
|
||||||
@ -52,7 +59,8 @@ export function LoggedInjectable(options?: ScopeOptions) {
|
|||||||
const all = Reflect.getMetadataKeys(target.prototype[method]).map(
|
const all = Reflect.getMetadataKeys(target.prototype[method]).map(
|
||||||
(k) => [k, Reflect.getMetadata(k, target.prototype[method])]
|
(k) => [k, Reflect.getMetadata(k, target.prototype[method])]
|
||||||
);
|
);
|
||||||
logger.log(`LoggedFunction applied to ${method}`);
|
if (options && options.verbose)
|
||||||
|
logger.log(`LoggedFunction applied to ${method}`);
|
||||||
LoggedFunction(target.prototype, method, {
|
LoggedFunction(target.prototype, method, {
|
||||||
value: target.prototype[method],
|
value: target.prototype[method],
|
||||||
});
|
});
|
||||||
@ -71,7 +79,7 @@ export function LoggedController(
|
|||||||
prefix: string | string[]
|
prefix: string | string[]
|
||||||
): (target: any) => void;
|
): (target: any) => void;
|
||||||
export function LoggedController(
|
export function LoggedController(
|
||||||
options: ControllerOptions
|
options: ControllerOptions & { verbose?: boolean }
|
||||||
): (target: any) => void;
|
): (target: any) => void;
|
||||||
|
|
||||||
export function LoggedController(param?: any): (target: any) => void {
|
export function LoggedController(param?: any): (target: any) => void {
|
||||||
@ -82,6 +90,11 @@ export function LoggedController(param?: any): (target: any) => void {
|
|||||||
|
|
||||||
const methods = Object.getOwnPropertyNames(target.prototype);
|
const methods = Object.getOwnPropertyNames(target.prototype);
|
||||||
|
|
||||||
|
let verbose =
|
||||||
|
typeof param === "object" && Object.keys(param).includes("verbose")
|
||||||
|
? param.verbose
|
||||||
|
: false;
|
||||||
|
|
||||||
methods.forEach((method) => {
|
methods.forEach((method) => {
|
||||||
if (
|
if (
|
||||||
method !== "constructor" &&
|
method !== "constructor" &&
|
||||||
@ -95,9 +108,10 @@ export function LoggedController(param?: any): (target: any) => void {
|
|||||||
const all = Reflect.getMetadataKeys(target.prototype[method]).map(
|
const all = Reflect.getMetadataKeys(target.prototype[method]).map(
|
||||||
(k) => [k, Reflect.getMetadata(k, target.prototype[method])]
|
(k) => [k, Reflect.getMetadata(k, target.prototype[method])]
|
||||||
);
|
);
|
||||||
logger.log(
|
if (verbose)
|
||||||
`LoggedRoute applied to ${method} (${RevRequestMethod[httpMethod]} ${path})`
|
logger.log(
|
||||||
);
|
`LoggedRoute applied to ${method} (${RevRequestMethod[httpMethod]} ${path})`
|
||||||
|
);
|
||||||
LoggedRoute()(target.prototype, method, {
|
LoggedRoute()(target.prototype, method, {
|
||||||
value: target.prototype[method],
|
value: target.prototype[method],
|
||||||
});
|
});
|
||||||
@ -111,6 +125,126 @@ export function LoggedController(param?: any): (target: any) => void {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface FunctionMetadata {
|
||||||
|
scopedLoggerInjectableParam?: number;
|
||||||
|
scopeKeys?: ScopeKeyReflectData[];
|
||||||
|
shouldScoped?: boolean;
|
||||||
|
loggedParams?: LoggedParamReflectData[];
|
||||||
|
}
|
||||||
|
|
||||||
|
function overrideBuild<F extends Array<any>, R>(
|
||||||
|
originalFunction: (...args: F) => Promise<R>,
|
||||||
|
baseLogger: Logger,
|
||||||
|
metadatas: FunctionMetadata,
|
||||||
|
key: string,
|
||||||
|
route?: {
|
||||||
|
fullRoute: string;
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
return async function (...args: F) {
|
||||||
|
let injectedLogger: Logger = baseLogger;
|
||||||
|
if (typeof metadatas.scopedLoggerInjectableParam !== "undefined") {
|
||||||
|
if (
|
||||||
|
args.length <= metadatas.scopedLoggerInjectableParam ||
|
||||||
|
!(args[metadatas.scopedLoggerInjectableParam] instanceof ScopedLogger)
|
||||||
|
) {
|
||||||
|
args[metadatas.scopedLoggerInjectableParam] = new ScopedLogger(
|
||||||
|
baseLogger,
|
||||||
|
key
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
args[metadatas.scopedLoggerInjectableParam] = new ScopedLogger(
|
||||||
|
args[metadatas.scopedLoggerInjectableParam],
|
||||||
|
key
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
injectedLogger = args[metadatas.scopedLoggerInjectableParam];
|
||||||
|
|
||||||
|
if (Array.isArray(metadatas.scopeKeys)) {
|
||||||
|
const scopeKeyResults: { error: boolean; value: string }[] =
|
||||||
|
metadatas.scopeKeys.map((key) => {
|
||||||
|
const argsValue = args[key.index];
|
||||||
|
if (!key.path) {
|
||||||
|
if (!metadatas.shouldScoped || argsValue) {
|
||||||
|
return { error: false, value: `${key.name}=${argsValue}` };
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
error: true,
|
||||||
|
value: `ScopeKey in ShouldScope cannot be falsy value (${argsValue})`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const reduceResult = key.path.reduce((base, keyPath) => {
|
||||||
|
if (
|
||||||
|
typeof base !== "object" ||
|
||||||
|
!Object.keys(base).includes(keyPath)
|
||||||
|
)
|
||||||
|
throw new Error(
|
||||||
|
`Cannot find key ${keyPath} in ${
|
||||||
|
typeof base === "object" ? JSON.stringify(base) : base
|
||||||
|
}`
|
||||||
|
);
|
||||||
|
return base[keyPath];
|
||||||
|
}, argsValue);
|
||||||
|
return { error: false, value: `${key.name}=${reduceResult}` };
|
||||||
|
} catch (e) {
|
||||||
|
return { error: true, value: e.message };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const successResults = scopeKeyResults.filter((v) => v.error === false);
|
||||||
|
if (successResults.length === 0) {
|
||||||
|
if (metadatas.shouldScoped) {
|
||||||
|
scopeKeyResults.forEach((v) => injectedLogger.warn(v.value));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
(injectedLogger as ScopedLogger).addScope(successResults[0].value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
injectedLogger.log(
|
||||||
|
`${route ? "HIT HTTP" : "CALL"} ${
|
||||||
|
route ? `${route.fullRoute} (${key})` : key
|
||||||
|
} ${
|
||||||
|
metadatas.loggedParams && metadatas.loggedParams.length > 0
|
||||||
|
? "WITH " +
|
||||||
|
(
|
||||||
|
await Promise.all(
|
||||||
|
metadatas.loggedParams.map(
|
||||||
|
async ({ name, index, include, exclude }) =>
|
||||||
|
name +
|
||||||
|
"=" +
|
||||||
|
(await objectContainedLogged(args[index], {
|
||||||
|
include,
|
||||||
|
exclude,
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
).join(", ")
|
||||||
|
: ""
|
||||||
|
}`
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const r: R = await originalFunction.call(this, ...args);
|
||||||
|
injectedLogger.log(
|
||||||
|
route
|
||||||
|
? `RETURNED RESPONSE ${route.fullRoute} (${key})`
|
||||||
|
: `RETURNED ${key}`
|
||||||
|
);
|
||||||
|
return r;
|
||||||
|
} catch (e) {
|
||||||
|
injectedLogger.error(
|
||||||
|
`WHILE ${route ? `HTTP ${route.fullRoute} (${key})` : key} ERROR ${e}`
|
||||||
|
);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function LoggedFunction<F extends Array<any>, R>(
|
export function LoggedFunction<F extends Array<any>, R>(
|
||||||
_target: any,
|
_target: any,
|
||||||
key: string,
|
key: string,
|
||||||
@ -129,67 +263,40 @@ export function LoggedFunction<F extends Array<any>, R>(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_target[key] = async function (...args: F) {
|
const scopedLoggerInjectableParam: number = Reflect.getOwnMetadata(
|
||||||
const scopedLoggerInjectableParam: number = Reflect.getOwnMetadata(
|
scopedLogger,
|
||||||
scopedLogger,
|
_target,
|
||||||
_target,
|
key
|
||||||
key
|
);
|
||||||
);
|
|
||||||
|
|
||||||
if (
|
const loggedParams: LoggedParamReflectData[] = Reflect.getOwnMetadata(
|
||||||
typeof scopedLoggerInjectableParam !== "undefined" &&
|
loggedParam,
|
||||||
(args.length <= scopedLoggerInjectableParam ||
|
_target,
|
||||||
!(args[scopedLoggerInjectableParam] instanceof ScopedLogger))
|
key
|
||||||
) {
|
);
|
||||||
args[scopedLoggerInjectableParam] = new ScopedLogger(logger, key);
|
|
||||||
} else if (typeof scopedLoggerInjectableParam !== "undefined") {
|
|
||||||
args[scopedLoggerInjectableParam] = new ScopedLogger(
|
|
||||||
args[scopedLoggerInjectableParam],
|
|
||||||
key
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const injectedLogger =
|
const scopeKeys: ScopeKeyReflectData[] = Reflect.getOwnMetadata(
|
||||||
typeof scopedLoggerInjectableParam !== "undefined"
|
scopeKey,
|
||||||
? args[scopedLoggerInjectableParam]
|
_target,
|
||||||
: logger;
|
key
|
||||||
|
);
|
||||||
|
|
||||||
const loggedParams: LoggedParamReflectData[] = Reflect.getOwnMetadata(
|
const shouldScoped: boolean = Reflect.getOwnMetadata(forceScopeKey, fn);
|
||||||
loggedParam,
|
|
||||||
_target,
|
|
||||||
key
|
|
||||||
);
|
|
||||||
|
|
||||||
injectedLogger.log(
|
const overrideFunction = overrideBuild(
|
||||||
`CALL ${key} ${
|
fn,
|
||||||
loggedParams && loggedParams.length > 0
|
logger,
|
||||||
? "WITH " +
|
{
|
||||||
(
|
scopedLoggerInjectableParam,
|
||||||
await Promise.all(
|
loggedParams,
|
||||||
loggedParams.map(
|
scopeKeys,
|
||||||
async ({ name, index, include, exclude }) =>
|
shouldScoped,
|
||||||
name +
|
},
|
||||||
"=" +
|
key
|
||||||
(await objectContainedLogged(args[index], {
|
);
|
||||||
include,
|
|
||||||
exclude,
|
|
||||||
}))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
).join(", ")
|
|
||||||
: ""
|
|
||||||
}`
|
|
||||||
);
|
|
||||||
|
|
||||||
try {
|
_target[key] = overrideFunction;
|
||||||
const r: R = await fn.call(this, ...args);
|
descriptor.value = overrideFunction;
|
||||||
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) {
|
export function LoggedRoute<F extends Array<any>, R>(route?: string) {
|
||||||
@ -204,13 +311,6 @@ export function LoggedRoute<F extends Array<any>, R>(route?: string) {
|
|||||||
|
|
||||||
const fn = descriptor.value;
|
const fn = descriptor.value;
|
||||||
|
|
||||||
const httpPath: string = Reflect.getMetadata("path", fn);
|
|
||||||
const httpMethod: RequestMethod = Reflect.getMetadata("method", fn);
|
|
||||||
|
|
||||||
const fullRoute = `${_target.constructor.name}::${route ?? httpPath}[${
|
|
||||||
RevRequestMethod[httpMethod]
|
|
||||||
}]`;
|
|
||||||
|
|
||||||
if (!fn || typeof fn !== "function") {
|
if (!fn || typeof fn !== "function") {
|
||||||
logger.warn(
|
logger.warn(
|
||||||
`LoggedRoute decorator applied to non-function property: ${key}`
|
`LoggedRoute decorator applied to non-function property: ${key}`
|
||||||
@ -218,63 +318,46 @@ export function LoggedRoute<F extends Array<any>, R>(route?: string) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_target[key] = async function (...args: F) {
|
const httpPath: string = Reflect.getMetadata("path", fn);
|
||||||
const scopedLoggerInjectableParam: number = Reflect.getOwnMetadata(
|
const httpMethod: RequestMethod = Reflect.getMetadata("method", fn);
|
||||||
scopedLogger,
|
|
||||||
_target,
|
|
||||||
key
|
|
||||||
);
|
|
||||||
|
|
||||||
if (
|
const fullRoute = `${_target.constructor.name}::${route ?? httpPath}[${
|
||||||
typeof scopedLoggerInjectableParam !== "undefined" &&
|
RevRequestMethod[httpMethod]
|
||||||
(args.length <= scopedLoggerInjectableParam ||
|
}]`;
|
||||||
!(args[scopedLoggerInjectableParam] instanceof ScopedLogger))
|
|
||||||
) {
|
const scopedLoggerInjectableParam: number = Reflect.getOwnMetadata(
|
||||||
args[scopedLoggerInjectableParam] = new ScopedLogger(logger, fullRoute);
|
scopedLogger,
|
||||||
|
_target,
|
||||||
|
key
|
||||||
|
);
|
||||||
|
|
||||||
|
const loggedParams: LoggedParamReflectData[] = Reflect.getOwnMetadata(
|
||||||
|
loggedParam,
|
||||||
|
_target,
|
||||||
|
key
|
||||||
|
);
|
||||||
|
|
||||||
|
const scopeKeys: ScopeKeyReflectData[] = Reflect.getOwnMetadata(
|
||||||
|
scopeKey,
|
||||||
|
_target,
|
||||||
|
key
|
||||||
|
);
|
||||||
|
|
||||||
|
const shouldScoped: boolean = Reflect.getOwnMetadata(forceScopeKey, fn);
|
||||||
|
|
||||||
|
const overrideFunction = overrideBuild(
|
||||||
|
fn,
|
||||||
|
logger,
|
||||||
|
{
|
||||||
|
scopedLoggerInjectableParam,
|
||||||
|
loggedParams,
|
||||||
|
scopeKeys,
|
||||||
|
shouldScoped,
|
||||||
|
},
|
||||||
|
key,
|
||||||
|
{
|
||||||
|
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;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return [httpPath, httpMethod];
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,10 @@ export class ScopedLogger extends Logger {
|
|||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public addScope(scopeId: string) {
|
||||||
|
this.scopeId = scopeId;
|
||||||
|
}
|
||||||
|
|
||||||
private scopedLog(method: LogLevel) {
|
private scopedLog(method: LogLevel) {
|
||||||
return (message: string) => {
|
return (message: string) => {
|
||||||
this.logger[method](
|
this.logger[method](
|
||||||
|
@ -5,8 +5,17 @@ export interface LoggedParamReflectData {
|
|||||||
exclude?: string[];
|
exclude?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const scopedLogger = Symbol("scopedLogger");
|
export interface ScopeKeyReflectData {
|
||||||
export const loggedParam = Symbol("loggedParam");
|
name: string;
|
||||||
|
index: number;
|
||||||
|
path?: string[];
|
||||||
|
priority?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const scopedLogger = Symbol("nlogdec-scopedLogger");
|
||||||
|
export const loggedParam = Symbol("nlogdec-loggedParam");
|
||||||
|
export const scopeKey = Symbol("nlogdec-scopeKey");
|
||||||
|
export const forceScopeKey = Symbol("nlogdec-forceScopeKey");
|
||||||
|
|
||||||
export function InjectLogger(
|
export function InjectLogger(
|
||||||
target: any,
|
target: any,
|
||||||
@ -34,6 +43,7 @@ export function LoggedParam(
|
|||||||
existingLoggedParams.push({
|
existingLoggedParams.push({
|
||||||
name,
|
name,
|
||||||
index: parameterIndex,
|
index: parameterIndex,
|
||||||
|
// If path is provided in string[] type, convert it to string path because it is used in string type
|
||||||
include:
|
include:
|
||||||
options &&
|
options &&
|
||||||
options.includePath &&
|
options.includePath &&
|
||||||
@ -52,3 +62,38 @@ export function LoggedParam(
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function ScopeKey(
|
||||||
|
name: string,
|
||||||
|
options?: { path?: string | string[]; priority?: number }
|
||||||
|
) {
|
||||||
|
return (
|
||||||
|
target: any,
|
||||||
|
propertyKey: string | symbol,
|
||||||
|
parameterIndex: number
|
||||||
|
) => {
|
||||||
|
const existingScopeKeys: ScopeKeyReflectData[] =
|
||||||
|
Reflect.getOwnMetadata(scopeKey, target, propertyKey) || [];
|
||||||
|
|
||||||
|
existingScopeKeys.push({
|
||||||
|
name,
|
||||||
|
index: parameterIndex,
|
||||||
|
path: Array.isArray(options?.path)
|
||||||
|
? options.path
|
||||||
|
: options?.path?.split("."),
|
||||||
|
priority: options?.priority,
|
||||||
|
});
|
||||||
|
|
||||||
|
existingScopeKeys.sort((a, b) => (b.priority ?? 1) - (a.priority ?? 1));
|
||||||
|
|
||||||
|
Reflect.defineMetadata(scopeKey, existingScopeKeys, target, propertyKey);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ShouldScoped(
|
||||||
|
_target: any,
|
||||||
|
_key: string,
|
||||||
|
descriptor: TypedPropertyDescriptor<(...args: any[]) => Promise<any>>
|
||||||
|
) {
|
||||||
|
Reflect.defineMetadata(forceScopeKey, true, descriptor.value);
|
||||||
|
}
|
||||||
|
@ -0,0 +1,313 @@
|
|||||||
|
import { LoggedFunction, LoggedInjectable } from "../logged";
|
||||||
|
import { ScopedLogger } from "../logger";
|
||||||
|
import {
|
||||||
|
InjectLogger,
|
||||||
|
LoggedParam,
|
||||||
|
ScopeKey,
|
||||||
|
ShouldScoped,
|
||||||
|
} from "../reflected";
|
||||||
|
|
||||||
|
type TestObject = {
|
||||||
|
a: string;
|
||||||
|
b: { c: string; f: number };
|
||||||
|
d: [number, string];
|
||||||
|
e: { p: string; g: number };
|
||||||
|
};
|
||||||
|
|
||||||
|
const testObject: TestObject = {
|
||||||
|
a: "asdf",
|
||||||
|
b: { c: "zxcv", f: 1 },
|
||||||
|
d: [2, "qwer"],
|
||||||
|
e: { p: "uiop", g: 3 },
|
||||||
|
};
|
||||||
|
|
||||||
|
@LoggedInjectable()
|
||||||
|
class LoggedClass {
|
||||||
|
async testParameterLoggingWithoutInjection(@LoggedParam("key") key: number) {
|
||||||
|
console.log(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
async testMultiParameterLoggingWithoutInjection(
|
||||||
|
@LoggedParam("key") key: number,
|
||||||
|
@LoggedParam("key2") key2: string
|
||||||
|
) {
|
||||||
|
console.log(key, key2);
|
||||||
|
}
|
||||||
|
|
||||||
|
async testParameterLoggingWithInjection(
|
||||||
|
@LoggedParam("key") key: number,
|
||||||
|
@InjectLogger logger?: ScopedLogger
|
||||||
|
) {
|
||||||
|
logger.log(key.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
async testMultiParameterLoggingWithInjection(
|
||||||
|
@LoggedParam("key") key: number,
|
||||||
|
@LoggedParam("key2") key2: string,
|
||||||
|
@InjectLogger logger?: ScopedLogger
|
||||||
|
) {
|
||||||
|
logger.log(key.toString() + key2);
|
||||||
|
}
|
||||||
|
|
||||||
|
async testObjectParameterLogging(
|
||||||
|
@LoggedParam("key") key: TestObject,
|
||||||
|
@InjectLogger logger?: ScopedLogger
|
||||||
|
) {
|
||||||
|
logger.log(JSON.stringify(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
async testObjectParameterDotIncludeLogging(
|
||||||
|
@LoggedParam("key", { includePath: ["a", "b.c", "d.0", "e"] })
|
||||||
|
key: TestObject,
|
||||||
|
@InjectLogger logger?: ScopedLogger
|
||||||
|
) {
|
||||||
|
logger.log(JSON.stringify(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
async testObjectParameterArrayIncludeLogging(
|
||||||
|
@LoggedParam("key", { includePath: [["a"], ["b", "c"], ["d", "0"], ["e"]] })
|
||||||
|
key: TestObject,
|
||||||
|
@InjectLogger logger?: ScopedLogger
|
||||||
|
) {
|
||||||
|
logger.log(JSON.stringify(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
async testObjectParameterDotExcludeLogging(
|
||||||
|
@LoggedParam("key", { excludePath: ["a", "b.c", "d.0", "e"] })
|
||||||
|
key: TestObject,
|
||||||
|
@InjectLogger logger?: ScopedLogger
|
||||||
|
) {
|
||||||
|
logger.log(JSON.stringify(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
async testObjectParameterArrayExcludeLogging(
|
||||||
|
@LoggedParam("key", { excludePath: [["a"], ["b", "c"], ["d", "0"], ["e"]] })
|
||||||
|
key: TestObject,
|
||||||
|
@InjectLogger logger?: ScopedLogger
|
||||||
|
) {
|
||||||
|
logger.log(JSON.stringify(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
async testScopedLogging(
|
||||||
|
@LoggedParam("key") @ScopeKey("scopekey") key: string,
|
||||||
|
@LoggedParam("key2") key2: number,
|
||||||
|
@InjectLogger logger?: ScopedLogger
|
||||||
|
) {
|
||||||
|
logger.log(key + key2.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
async testPathScopedLogging(
|
||||||
|
@LoggedParam("key") @ScopeKey("scopekey", { path: "b.c" }) key: TestObject,
|
||||||
|
@InjectLogger logger?: ScopedLogger
|
||||||
|
) {
|
||||||
|
logger.log(JSON.stringify(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
async testOrScopedLogging(
|
||||||
|
@LoggedParam("key")
|
||||||
|
@ScopeKey("scopekey-a", { path: "a" })
|
||||||
|
@ScopeKey("scopekey-b", { path: "b" })
|
||||||
|
key: { a: string } | { b: string },
|
||||||
|
@InjectLogger logger?: ScopedLogger
|
||||||
|
) {
|
||||||
|
logger.log(JSON.stringify(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
async testPriorityScopedLogging(
|
||||||
|
@LoggedParam("key")
|
||||||
|
@ScopeKey("scopekey-a", { path: "a", priority: 0.5 })
|
||||||
|
@ScopeKey("scopekey-b", { path: "b" }) // default 1
|
||||||
|
key: { a?: string; b?: string },
|
||||||
|
// if both a and b are undefined, set scope to nothing
|
||||||
|
@InjectLogger logger?: ScopedLogger
|
||||||
|
) {
|
||||||
|
logger.log(JSON.stringify(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
async testOptionalScopedLogging(
|
||||||
|
@LoggedParam("key")
|
||||||
|
@ScopeKey("scopekey")
|
||||||
|
key?: string,
|
||||||
|
@InjectLogger logger?: ScopedLogger
|
||||||
|
) {
|
||||||
|
logger.log(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ShouldScoped // Warn if there is no valid scopekey
|
||||||
|
async testShouldScopedLogging(
|
||||||
|
@LoggedParam("key")
|
||||||
|
@ScopeKey("scopekey")
|
||||||
|
key?: string,
|
||||||
|
@InjectLogger logger?: ScopedLogger
|
||||||
|
) {
|
||||||
|
logger.log(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LoggedMethodsClass {
|
||||||
|
@LoggedFunction
|
||||||
|
async testParameterLoggingWithoutInjection(@LoggedParam("key") key: number) {
|
||||||
|
console.log(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@LoggedFunction
|
||||||
|
async testMultiParameterLoggingWithoutInjection(
|
||||||
|
@LoggedParam("key") key: number,
|
||||||
|
@LoggedParam("key2") key2: string
|
||||||
|
) {
|
||||||
|
console.log(key, key2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@LoggedFunction
|
||||||
|
async testParameterLoggingWithInjection(
|
||||||
|
@LoggedParam("key") key: number,
|
||||||
|
@InjectLogger logger?: ScopedLogger
|
||||||
|
) {
|
||||||
|
logger.log(key.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@LoggedFunction
|
||||||
|
async testMultiParameterLoggingWithInjection(
|
||||||
|
@LoggedParam("key") key: number,
|
||||||
|
@LoggedParam("key2") key2: string,
|
||||||
|
@InjectLogger logger?: ScopedLogger
|
||||||
|
) {
|
||||||
|
logger.log(key.toString() + key2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@LoggedFunction
|
||||||
|
async testObjectParameterLogging(
|
||||||
|
@LoggedParam("key") key: TestObject,
|
||||||
|
@InjectLogger logger?: ScopedLogger
|
||||||
|
) {
|
||||||
|
logger.log(JSON.stringify(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
@LoggedFunction
|
||||||
|
async testObjectParameterDotIncludeLogging(
|
||||||
|
@LoggedParam("key", { includePath: ["a", "b.c", "d.0", "e"] })
|
||||||
|
key: TestObject,
|
||||||
|
@InjectLogger logger?: ScopedLogger
|
||||||
|
) {
|
||||||
|
logger.log(JSON.stringify(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
@LoggedFunction
|
||||||
|
async testObjectParameterArrayIncludeLogging(
|
||||||
|
@LoggedParam("key", { includePath: [["a"], ["b", "c"], ["d", "0"], ["e"]] })
|
||||||
|
key: TestObject,
|
||||||
|
@InjectLogger logger?: ScopedLogger
|
||||||
|
) {
|
||||||
|
logger.log(JSON.stringify(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
@LoggedFunction
|
||||||
|
async testObjectParameterDotExcludeLogging(
|
||||||
|
@LoggedParam("key", { excludePath: ["a", "b.c", "d.0", "e"] })
|
||||||
|
key: TestObject,
|
||||||
|
@InjectLogger logger?: ScopedLogger
|
||||||
|
) {
|
||||||
|
logger.log(JSON.stringify(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
@LoggedFunction
|
||||||
|
async testObjectParameterArrayExcludeLogging(
|
||||||
|
@LoggedParam("key", { excludePath: [["a"], ["b", "c"], ["d", "0"], ["e"]] })
|
||||||
|
key: TestObject,
|
||||||
|
@InjectLogger logger?: ScopedLogger
|
||||||
|
) {
|
||||||
|
logger.log(JSON.stringify(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
@LoggedFunction
|
||||||
|
async testScopedLogging(
|
||||||
|
@LoggedParam("key") @ScopeKey("scopekey") key: string,
|
||||||
|
@LoggedParam("key2") key2: number,
|
||||||
|
@InjectLogger logger?: ScopedLogger
|
||||||
|
) {
|
||||||
|
logger.log(key + key2.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@LoggedFunction
|
||||||
|
async testPathScopedLogging(
|
||||||
|
@LoggedParam("key") @ScopeKey("scopekey", { path: "b.c" }) key: TestObject,
|
||||||
|
@InjectLogger logger?: ScopedLogger
|
||||||
|
) {
|
||||||
|
logger.log(JSON.stringify(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
@LoggedFunction
|
||||||
|
async testOrScopedLogging(
|
||||||
|
@LoggedParam("key")
|
||||||
|
@ScopeKey("scopekey-a", { path: "a" })
|
||||||
|
@ScopeKey("scopekey-b", { path: "b" })
|
||||||
|
key: { a: string } | { b: string },
|
||||||
|
@InjectLogger logger?: ScopedLogger
|
||||||
|
) {
|
||||||
|
logger.log(JSON.stringify(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
@LoggedFunction
|
||||||
|
async testPriorityScopedLogging(
|
||||||
|
@LoggedParam("key")
|
||||||
|
@ScopeKey("scopekey-a", { path: "a", priority: 0.5 })
|
||||||
|
@ScopeKey("scopekey-b", { path: "b" }) // default 1
|
||||||
|
key: { a?: string; b?: string },
|
||||||
|
// if both a and b are undefined, set scope to nothing
|
||||||
|
@InjectLogger logger?: ScopedLogger
|
||||||
|
) {
|
||||||
|
logger.log(JSON.stringify(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
@LoggedFunction
|
||||||
|
async testOptionalScopedLogging(
|
||||||
|
@LoggedParam("key")
|
||||||
|
@ScopeKey("scopekey")
|
||||||
|
key?: string,
|
||||||
|
@InjectLogger logger?: ScopedLogger
|
||||||
|
) {
|
||||||
|
logger.log(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@LoggedFunction
|
||||||
|
@ShouldScoped // Warn if there is no valid scopekey
|
||||||
|
async testShouldScopedLogging(
|
||||||
|
@LoggedParam("key")
|
||||||
|
@ScopeKey("scopekey")
|
||||||
|
key?: string,
|
||||||
|
@InjectLogger logger?: ScopedLogger
|
||||||
|
) {
|
||||||
|
logger.log(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Choose Class to Test
|
||||||
|
*/
|
||||||
|
// const tester = new LoggedClass();
|
||||||
|
const tester = new LoggedMethodsClass();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Choose Method to Test
|
||||||
|
*/
|
||||||
|
// tester.testParameterLoggingWithoutInjection(1);
|
||||||
|
// tester.testMultiParameterLoggingWithoutInjection(1, "asdf");
|
||||||
|
// tester.testParameterLoggingWithInjection(1);
|
||||||
|
// tester.testMultiParameterLoggingWithInjection(1, "asdf");
|
||||||
|
// tester.testObjectParameterLogging(testObject);
|
||||||
|
// tester.testObjectParameterDotIncludeLogging(testObject);
|
||||||
|
// tester.testObjectParameterArrayIncludeLogging(testObject);
|
||||||
|
// tester.testObjectParameterDotExcludeLogging(testObject);
|
||||||
|
// tester.testObjectParameterArrayExcludeLogging(testObject);
|
||||||
|
// tester.testScopedLogging("asdf", 2);
|
||||||
|
// tester.testPathScopedLogging(testObject);
|
||||||
|
// tester.testOrScopedLogging({ a: "asdf" });
|
||||||
|
// tester.testOrScopedLogging({ b: "qwer" });
|
||||||
|
// tester.testPriorityScopedLogging({ a: "asdf", b: "qwer" });
|
||||||
|
// tester.testPriorityScopedLogging({ a: "asdf" });
|
||||||
|
// tester.testPriorityScopedLogging({ b: "qwer" });
|
||||||
|
// tester.testPriorityScopedLogging({});
|
||||||
|
// tester.testOptionalScopedLogging("asdf");
|
||||||
|
// tester.testOptionalScopedLogging();
|
||||||
|
// tester.testShouldScopedLogging("asdf");
|
||||||
|
tester.testShouldScopedLogging();
|
Loading…
x
Reference in New Issue
Block a user