nestjs-pino
Advanced tools
Comparing version 0.2.1 to 0.3.0
@@ -1,2 +0,4 @@ | ||
import { LoggerService, DynamicModule, NestModule, MiddlewareConsumer } from "@nestjs/common"; | ||
import { LoggerService, DynamicModule } from "@nestjs/common"; | ||
import { ModuleMetadata } from "@nestjs/common/interfaces"; | ||
import pinoHttp from "pino-http"; | ||
import pino, { LoggerOptions, DestinationStream } from "pino"; | ||
@@ -6,26 +8,22 @@ declare type PassedLogger = { | ||
}; | ||
declare type Params = [] | [PassedLogger] | [LoggerOptions | DestinationStream] | [LoggerOptions, DestinationStream]; | ||
export declare type Params = null | pinoHttp.Options | DestinationStream | [pinoHttp.Options, DestinationStream]; | ||
export interface LoggerModuleAsyncOptions extends Pick<ModuleMetadata, "imports" | "providers"> { | ||
useFactory: (...args: any[]) => Params; | ||
inject?: any[]; | ||
} | ||
export declare class Logger implements LoggerService { | ||
verbose(msg: string, ...args: any[]): void; | ||
verbose(obj: object, msg?: string, ...args: any[]): void; | ||
debug(msg: string, ...args: any[]): void; | ||
debug(obj: object, msg?: string, ...args: any[]): void; | ||
log(msg: string, ...args: any[]): void; | ||
log(obj: object, msg?: string, ...args: any[]): void; | ||
warn(msg: string, ...args: any[]): void; | ||
warn(obj: object, msg?: string, ...args: any[]): void; | ||
error(msg: string, ...args: any[]): void; | ||
error(obj: object, msg?: string, ...args: any[]): void; | ||
private readonly outOfContext; | ||
constructor(options: Params); | ||
verbose(message: any, context?: string, ...args: any[]): void; | ||
debug(message: any, context?: string, ...args: any[]): void; | ||
log(message: any, context?: string, ...args: any[]): void; | ||
warn(message: any, context?: string, ...args: any[]): void; | ||
error(message: any, trace?: string, context?: string, ...args: any[]): void; | ||
private readonly logger; | ||
} | ||
export declare class LoggerModule implements NestModule { | ||
static forRoot(...params: Params): DynamicModule; | ||
configure(consumer: MiddlewareConsumer): void; | ||
export declare class LoggerModule { | ||
static forRoot(opts?: PassedLogger | LoggerOptions | DestinationStream): DynamicModule; | ||
static forRoot(opts: LoggerOptions, stream: DestinationStream): DynamicModule; | ||
static forRootAsync(options: LoggerModuleAsyncOptions): DynamicModule; | ||
} | ||
declare global { | ||
namespace Express { | ||
interface Request { | ||
log: pino.Logger; | ||
} | ||
} | ||
} | ||
export {}; |
@@ -8,77 +8,160 @@ "use strict"; | ||
}; | ||
var __metadata = (this && this.__metadata) || function (k, v) { | ||
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); | ||
}; | ||
var __param = (this && this.__param) || function (paramIndex, decorator) { | ||
return function (target, key) { decorator(target, key, paramIndex); } | ||
}; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
var LoggerModule_1; | ||
var LoggerModule_1, LoggerCoreModule_1; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
"use strict"; | ||
const common_1 = require("@nestjs/common"); | ||
const express_pino_logger_1 = __importDefault(require("express-pino-logger")); | ||
const pino_http_1 = __importDefault(require("pino-http")); | ||
const pino_1 = __importDefault(require("pino")); | ||
const express_ctx_1 = require("express-ctx"); | ||
let outOfContextLogger; | ||
const loggerKey = "logger"; | ||
let rootParams; | ||
const LOGGER_KEY = "logger"; | ||
const OPTIONS_PROVIDER_TOKEN = "pino-options"; | ||
let Logger = class Logger { | ||
verbose(...params) { | ||
return (express_ctx_1.getValue(loggerKey) || outOfContextLogger).trace(...params); | ||
constructor(options) { | ||
if (Array.isArray(options)) { | ||
this.outOfContext = pino_1.default(...options); | ||
} | ||
else if (isPassedLogger(options)) { | ||
this.outOfContext = options.logger; | ||
} | ||
else { | ||
this.outOfContext = pino_1.default(options || undefined); | ||
} | ||
} | ||
debug(...params) { | ||
return (express_ctx_1.getValue(loggerKey) || outOfContextLogger).debug(...params); | ||
verbose(message, context, ...args) { | ||
if (context) { | ||
this.logger.trace({ context }, message, ...args); | ||
} | ||
else { | ||
this.logger.trace(message); | ||
} | ||
} | ||
log(...params) { | ||
return (express_ctx_1.getValue(loggerKey) || outOfContextLogger).info(...params); | ||
debug(message, context, ...args) { | ||
if (context) { | ||
this.logger.debug({ context }, message, ...args); | ||
} | ||
else { | ||
this.logger.debug(message); | ||
} | ||
} | ||
warn(...params) { | ||
return (express_ctx_1.getValue(loggerKey) || outOfContextLogger).warn(...params); | ||
log(message, context, ...args) { | ||
if (context) { | ||
this.logger.info({ context }, message, ...args); | ||
} | ||
else { | ||
this.logger.info(message); | ||
} | ||
} | ||
error(...params) { | ||
return (express_ctx_1.getValue(loggerKey) || outOfContextLogger).error(...params); | ||
warn(message, context, ...args) { | ||
if (context) { | ||
this.logger.warn({ context }, message, ...args); | ||
} | ||
else { | ||
this.logger.warn(message); | ||
} | ||
} | ||
error(message, trace, context, ...args) { | ||
if (context) { | ||
this.logger.error({ context, trace }, message, ...args); | ||
} | ||
else if (trace) { | ||
this.logger.error({ trace }, message); | ||
} | ||
else { | ||
this.logger.error(message); | ||
} | ||
} | ||
get logger() { | ||
return express_ctx_1.getValue(LOGGER_KEY) || this.outOfContext; | ||
} | ||
}; | ||
Logger = __decorate([ | ||
common_1.Injectable() | ||
common_1.Injectable(), | ||
__param(0, common_1.Inject(OPTIONS_PROVIDER_TOKEN)), | ||
__metadata("design:paramtypes", [Object]) | ||
], Logger); | ||
exports.Logger = Logger; | ||
let LoggerModule = LoggerModule_1 = class LoggerModule { | ||
static forRoot(...params) { | ||
rootParams = params; | ||
if (hasLoggerParamsPassedLogger(rootParams)) { | ||
outOfContextLogger = rootParams[0].logger; | ||
} | ||
else { | ||
outOfContextLogger = pino_1.default(...rootParams); | ||
} | ||
static forRoot(opts, stream) { | ||
return { | ||
module: LoggerModule_1, | ||
providers: [Logger], | ||
imports: [ | ||
LoggerCoreModule.forRoot(stream ? [opts, stream] : opts) | ||
] | ||
}; | ||
} | ||
static forRootAsync(options) { | ||
return { | ||
module: LoggerModule_1, | ||
imports: [LoggerCoreModule.forRootAsync(options)] | ||
}; | ||
} | ||
}; | ||
LoggerModule = LoggerModule_1 = __decorate([ | ||
common_1.Module({}) | ||
], LoggerModule); | ||
exports.LoggerModule = LoggerModule; | ||
let LoggerCoreModule = LoggerCoreModule_1 = class LoggerCoreModule { | ||
constructor(options) { | ||
this.options = options; | ||
} | ||
static forRoot(options) { | ||
const optionsProvider = { | ||
provide: OPTIONS_PROVIDER_TOKEN, | ||
useValue: options || null | ||
}; | ||
return { | ||
module: LoggerCoreModule_1, | ||
providers: [Logger, optionsProvider], | ||
exports: [Logger] | ||
}; | ||
} | ||
static forRootAsync(options) { | ||
const optionsProvider = { | ||
provide: OPTIONS_PROVIDER_TOKEN, | ||
useFactory: options.useFactory, | ||
inject: options.inject | ||
}; | ||
return { | ||
module: LoggerCoreModule_1, | ||
imports: options.imports, | ||
providers: options.providers | ||
? [Logger, optionsProvider, ...options.providers] | ||
: [Logger, optionsProvider], | ||
exports: [Logger] | ||
}; | ||
} | ||
configure(consumer) { | ||
consumer | ||
.apply(...createLoggerMiddlewares()) | ||
.apply(...createLoggerMiddlewares(this.options)) | ||
.forRoutes({ path: "*", method: common_1.RequestMethod.ALL }); | ||
} | ||
}; | ||
LoggerModule = LoggerModule_1 = __decorate([ | ||
LoggerCoreModule = LoggerCoreModule_1 = __decorate([ | ||
common_1.Global(), | ||
common_1.Module({ | ||
providers: [Logger], | ||
exports: [Logger] | ||
}) | ||
], LoggerModule); | ||
exports.LoggerModule = LoggerModule; | ||
function createLoggerMiddlewares() { | ||
return [ | ||
express_ctx_1.middleware, | ||
express_pino_logger_1.default(...rootParams), | ||
(req, res, next) => { | ||
express_ctx_1.setValue(loggerKey, req.log); | ||
next(); | ||
} | ||
]; | ||
common_1.Module({ providers: [Logger], exports: [Logger] }), | ||
__param(0, common_1.Inject(OPTIONS_PROVIDER_TOKEN)), | ||
__metadata("design:paramtypes", [Object]) | ||
], LoggerCoreModule); | ||
function isPassedLogger(params) { | ||
return !!params && "logger" in params; | ||
} | ||
function hasLoggerParamsPassedLogger(params) { | ||
return params[0] && "logger" in params[0]; | ||
function createLoggerMiddlewares(params) { | ||
const middleware = Array.isArray(params) | ||
? pino_http_1.default(...params) | ||
: pino_http_1.default(params || undefined); | ||
return [express_ctx_1.middleware, middleware, bindLoggerMiddleware]; | ||
} | ||
function bindLoggerMiddleware(req, res, next) { | ||
express_ctx_1.setValue(LOGGER_KEY, req.log); | ||
next(); | ||
} | ||
//# sourceMappingURL=index.js.map |
{ | ||
"name": "nestjs-pino", | ||
"version": "0.2.1", | ||
"version": "0.3.0", | ||
"description": "Pino logger for NestJS", | ||
"main": "dist/index.js", | ||
"scripts": { | ||
"test": "jest", | ||
"build": "tsc --p tsconfig.build.json", | ||
"test": "jest --verbose", | ||
"build": "rm -rf ./dist && tsc --p tsconfig.build.json", | ||
"report": "cat ./coverage/lcov.info | coveralls", | ||
"example": "tsc && LOG_LEVEL=debug node dist/example/main" | ||
"example": "rm -rf ./dist && tsc && LOG_LEVEL=debug node dist/example/main" | ||
}, | ||
@@ -35,3 +35,3 @@ "engineStrict": false, | ||
"express-ctx": "^0.1.1", | ||
"express-pino-logger": "^4.0.0" | ||
"pino-http": "^4.3.0" | ||
}, | ||
@@ -45,6 +45,6 @@ "devDependencies": { | ||
"@types/express": "^4.17.1", | ||
"@types/express-pino-logger": "^4.0.1", | ||
"@types/jest": "^24.0.18", | ||
"@types/memorystream": "^0.3.0", | ||
"@types/node": "^12.7.4", | ||
"@types/pino-http": "^4.3.2", | ||
"@types/supertest": "^2.0.8", | ||
@@ -67,3 +67,2 @@ "coveralls": "^3.0.6", | ||
], | ||
"rootDir": "src", | ||
"testRegex": ".spec.ts$", | ||
@@ -75,4 +74,7 @@ "transform": { | ||
"coverageDirectory": "../coverage", | ||
"collectCoverageFrom": [ | ||
"src/**/*.ts" | ||
], | ||
"testEnvironment": "node" | ||
} | ||
} |
126
README.md
@@ -27,2 +27,15 @@ <p align="center"> | ||
Import module: | ||
```ts | ||
import { LoggerModule } from 'nestjs-pino'; | ||
@Module({ | ||
imports: [LoggerModule.forRoot()], | ||
controllers: [AppController], | ||
providers: [MyService] | ||
}) | ||
class MyModule {} | ||
``` | ||
In controller: | ||
@@ -42,3 +55,3 @@ | ||
getHello(): string { | ||
this.logger.log("calling AppController.getHello"); | ||
this.logger.log("getHello()", AppController.name); | ||
return `Hello ${this.myService.getWorld()}`; | ||
@@ -58,4 +71,4 @@ } | ||
getWorld() { | ||
this.logger.debug("calling MyService.getWorld"); | ||
getWorld(...params: any[]) { | ||
this.logger.log("getWorld(%o)", MyService.name, params); | ||
return "World!"; | ||
@@ -66,8 +79,17 @@ } | ||
Output (every log has request context): | ||
Output: | ||
```json | ||
{"level":30,"time":1568720266616,"pid":25566,"hostname":"my-host","req":{"id":1,"method":"GET","url":"/","headers":{...},"remoteAddress":"::1","remotePort":53753},"msg":"calling AppController.getHello","v":1} | ||
{"level":20,"time":1568720266616,"pid":25566,"hostname":"my-host","req":{"id":1,"method":"GET","url":"/","headers":{...},"remoteAddress":"::1","remotePort":53753},"msg":"calling MyService.getWorld","v":1} | ||
{"level":30,"time":1568720266623,"pid":25566,"hostname":"my-host","req":{"id":1,"method":"GET","url":"/","headers":{...},"remoteAddress":"::1","remotePort":53753},"res":{"statusCode":200,"headers":{...}},"responseTime":9,"msg":"request completed","v":1} | ||
// Logs by Nest itself, when set `app.useLogger(app.get(Logger))` | ||
{"level":30,"time":1570470154387,"pid":17383,"hostname":"my-host","context":"RoutesResolver","msg":"AppController {/}: true","v":1} | ||
{"level":30,"time":1570470154391,"pid":17383,"hostname":"my-host","context":"RouterExplorer","msg":"Mapped {/, GET} route true","v":1} | ||
{"level":30,"time":1570470154405,"pid":17383,"hostname":"my-host","context":"NestApplication","msg":"Nest application successfully started true","v":1} | ||
// Logs by injected Logger methods in Services/Controllers | ||
// Every log has it's request data | ||
{"level":30,"time":1570470161805,"pid":17383,"hostname":"my-host","req":{"id":1,"method":"GET","url":"/","headers":{...},"remoteAddress":"::1","remotePort":53957},"context":"AppController","msg":"getHello()","v":1} | ||
{"level":30,"time":1570470161805,"pid":17383,"hostname":"my-host","req":{"id":1,"method":"GET","url":"/","headers":{...},"remoteAddress":"::1","remotePort":53957},"context":"MyService","msg":"getWorld([])","v":1} | ||
// Automatic logs of every request/response | ||
{"level":30,"time":1570470161819,"pid":17383,"hostname":"my-host","req":{"id":1,"method":"GET","url":"/","headers":{...},"remoteAddress":"::1","remotePort":53957},"res":{"statusCode":304,"headers":{...}},"responseTime":15,"msg":"request completed","v":1} | ||
``` | ||
@@ -97,5 +119,5 @@ | ||
### Configure | ||
### Synchronous configuration | ||
Also, you can configure it. `forRoot` function has the same API as [express-pino-logger](https://github.com/pinojs/express-pino-logger#api) has (it's the same as [pino itself](https://github.com/pinojs/pino/blob/master/docs/api.md#options) and can take existing logger via `{ logger: pino(...) }`): | ||
`LoggerModule.forRoot` has the same API as [pino-http](https://github.com/pinojs/pino-http#pinohttpopts-stream): | ||
@@ -123,2 +145,64 @@ ```ts | ||
### Asynchronous configuration | ||
With `LoggerModule.forRootAsync` you can for example import your `ConfigModule` and inject `ConfigService` to use it in `useFactory` method. | ||
`useFactory` should return result typeof arguments of [pino-http](https://github.com/pinojs/pino-http#pinohttpopts-stream) or `null`, example: | ||
```ts | ||
import { LoggerModule } from 'nestjs-pino'; | ||
@Injectable() | ||
class ConfigService { | ||
public readonly level = "debug"; | ||
} | ||
@Module({ | ||
providers: [ConfigService], | ||
exports: [ConfigService] | ||
}) | ||
class ConfigModule {} | ||
@Module({ | ||
imports: [ | ||
LoggerModule.forRootAsync({ | ||
imports: [ConfigModule], | ||
inject: [ConfigService], | ||
useFactory: (config: ConfigService) => { | ||
return { level: config.level }; | ||
} | ||
}) | ||
], | ||
... | ||
}) | ||
class TestModule {} | ||
``` | ||
Or without `ConfigModule` you can just pass `ConfigService` to `providers`: | ||
```ts | ||
import { LoggerModule } from 'nestjs-pino'; | ||
@Injectable() | ||
class ConfigService { | ||
public readonly level = "debug"; | ||
public readonly stream = stream; | ||
} | ||
@Module({ | ||
imports: [ | ||
LoggerModule.forRootAsync({ | ||
providers: [ConfigService], | ||
inject: [ConfigService], | ||
useFactory: (config: ConfigService) => { | ||
return [{ level: config.level }, config.stream]; | ||
} | ||
}) | ||
], | ||
controllers: [TestController] | ||
}) | ||
class TestModule {} | ||
``` | ||
### Extreme mode | ||
@@ -158,4 +242,4 @@ | ||
getWorld() { | ||
this.logger.debug("calling MyService.getWorld"); | ||
getWorld(...params: any[]) { | ||
this.logger.log("getWorld(%o)", MyService.name, params); | ||
return "World!"; | ||
@@ -168,6 +252,9 @@ } | ||
According to [official docs](https://docs.nestjs.com/techniques/logger#dependency-injection), loggers with Dependency injection should be set via following construction: | ||
```ts | ||
import { Logger } from 'nestjs-pino'; | ||
const app = await NestFactory.create(MyModule, { logger: new Logger() }); | ||
const app = await NestFactory.create(MyModule, { logger: false }); | ||
app.useLogger(app.get(Logger)); | ||
``` | ||
@@ -179,3 +266,3 @@ | ||
__A__: It use [express-pino-logger](https://github.com/pinojs/express-pino-logger) under hood, so every request has it's own [child-logger](https://github.com/pinojs/pino/blob/master/docs/child-loggers.md), and with help of [async_hooks](https://nodejs.org/api/async_hooks.html) `Logger` can get it while calling own methods. So your logs can be groupped by `req.id`. | ||
__A__: It use [pino-http](https://github.com/pinojs/pino-http) under hood, so every request has it's own [child-logger](https://github.com/pinojs/pino/blob/master/docs/child-loggers.md), and with help of [async_hooks](https://nodejs.org/api/async_hooks.html) `Logger` can get it while calling own methods. So your logs can be groupped by `req.id`. | ||
@@ -193,6 +280,9 @@ __Q__: _Why use [async_hooks](https://nodejs.org/api/async_hooks.html) instead of [REQUEST scope](https://docs.nestjs.com/fundamentals/injection-scopes#per-request-injection)?_ | ||
__A__: Pino built in methods are not compatible to NestJS built in `LoggerService` methods, so decision is to map pino methods to `LoggerService` methods to save `Logger` API: | ||
- `trace`=`verbose` | ||
- `debug`=`debug` | ||
- `info`=`log` | ||
- `warn`=`warn` | ||
- `error`=`error` | ||
| pino | LoggerService | | ||
| ------- | ------------- | | ||
| `trace` | `verbose` | | ||
| `debug` | `debug` | | ||
| `info` | `log` | | ||
| `warn` | `warn` | | ||
| `error` | `error` | |
{ | ||
"extends": "./tsconfig.json", | ||
"exclude": ["node_modules", "dist", "**/*.spec.ts", "example"] | ||
"include": ["src"] | ||
} |
@@ -13,5 +13,6 @@ { | ||
"incremental": true, | ||
"esModuleInterop": true | ||
"esModuleInterop": true, | ||
"strict": true | ||
}, | ||
"exclude": ["node_modules", "dist"] | ||
"include": ["src", "example", "__tests__"] | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
346143
16
716
279
1
+ Addedpino-http@^4.3.0
- Removedexpress-pino-logger@^4.0.0
- Removedexpress-pino-logger@4.0.0(transitive)