I'm trying to inject nestwinston
globally in my nestjs app and also to keep a beautiful format in my console/terminal and in Grafana, and most important to display custom objects where needed, but using a beautiful format, not simply JSON.
My problem is that the nestwinston
changes its behavior when is injected globally.
I followed https://github.com/gremo/nest-winston#replacing-the-nest-logger-also-for-bootstrapping, but when I'm running the next code sample, my error log's object is not displayed in log whatever format type I use (tried all of them and the only option was to format the log as simple JSON, which looks awful in Grafana):
import { Injectable, Logger } from '@nestjs/common';
...
constructor(
private readonly logger: Logger,
) {
this.logger.log("my message", {myValue: true});
}
result:
[NestWinston] Info [Bootstrapper] my message - {}
see that {myValue: true}
is not displayed in log... even it displays an empty object which I really don't know what it is...
I achived that by creating a custom LoggerService
in which I use the nestwinston
alongside with a custom logger formatter, then replacing the nestjs default logger with this service, so it will be used everywhere in my project.
LoggerService
:
import { LoggerService as LS } from '@nestjs/common';
import * as winston from 'winston';
const { combine, timestamp, printf } = winston.format;
import * as Transport from 'winston-transport';
import {
utilities as nestWinstonModuleUtilities,
WinstonModule,
} from 'nest-winston';
export class LoggerService implements LS {
private logger: LS;
constructor() {
this.logger = WinstonModule.createLogger({
transports: this.logTransports(),
});
}
log(message: any, fields?: any) {
this.logger.log(this.toPrettyJson(message, fields));
}
error(message: any, fields?: any) {
this.logger.error(this.toPrettyJson(message, fields));
}
warn(message: any, fields?: any) {
this.logger.warn(this.toPrettyJson(message, fields));
}
debug(message: any, fields?: any) {
this.logger.debug(this.toPrettyJson(message, fields));
}
verbose(message: any, fields?: any) {
this.logger.verbose(this.toPrettyJson(message, fields));
}
private toPrettyJson(message: any, fields?: any) {
let log = {};
if (typeof message === 'string') {
log['message'] = message;
} else if (typeof message === 'object') {
for (const [key, value] of Object.entries(message)) {
log[key] = value;
}
}
if (fields) {
if (typeof fields === 'object') {
for (const [key, value] of Object.entries(fields)) {
log[key] = value;
}
} else if (typeof fields === 'string') {
log['context'] = fields;
}
}
return log;
}
private logTransports = () => {
const format = combine(
timestamp(),
nestWinstonModuleUtilities.format.nestLike(),
);
const logTransports: Transport[] = [
new winston.transports.Console({
format: format,
}),
];
return logTransports;
};
}
main.ts
:
...
const app = await NestFactory.create(AppModule, {
logger: new LoggerService(),
});
...
then in any module you want to use the new Logger, add it to providers:
import { Logger } from '@nestjs/common';
...
providers: [Logger...]
...
and then to any module's service:
import { Injectable, Logger } from '@nestjs/common';
...
constructor(
private readonly logger: Logger,
) {
// method 1
this.logger.log("my message", {myValue: true});
// method 2
this.logger.log({message: "my message", myValue: true});
}
=>
[NestWinston] Info [Bootstrapper] my message - {"myValue": true}