Try   HackMD

Log setting - Winston and Morgan

Installation

  • winston
    • A logger for just about everything
  • morgan
    • HTTP request logger middleware for node.js

npm i morgan winston

npm i -D @types/morgan

Winston and morgan configuration

  • winston.ts
import morgan, { StreamOptions } from 'morgan'; import winston from 'winston'; import { currentENV } from '../utils'; // This parameter returns 'deveopment' or other NODE_ENV // export const currentENV = process.env.NODE_ENV || 'development'; const levels: { [key: string]: number } = { error: 0, warn: 1, info: 2, http: 3, debug: 4 }; // write only error log in development mode, write all logs in other mode const level = () => { const env = currentENV; const isDevelopment = env === 'development'; return isDevelopment ? 'error' : 'debug'; }; type IWinstonColors = 'red' | 'yellow' | 'green' | 'magenta' | 'white'; const colors: { [key: string]: IWinstonColors } = { error: 'red', warn: 'yellow', info: 'green', http: 'magenta', debug: 'white' }; winston.addColors(colors); const format = winston.format.combine( winston.format.timestamp({ format: 'DD-MM-YYYY HH:mm:ss' }), winston.format.printf( msg => `${[msg.timestamp]} ${msg.level}: ${msg.message}` ) ); // only add color in console // because if we add color in log file, the format will be unreadable const transports = [ new winston.transports.Console({ format: winston.format.combine(winston.format.colorize({ all: true })), level: 'debug' }), new winston.transports.File({ filename: 'error.log', level: 'error' }), new winston.transports.File({ filename: 'all.log' }) ]; export const logger = winston.createLogger({ level: level(), levels, format, transports }); const stream: StreamOptions = { write: message => logger.http(message) // morgan will log the http request // so we use 'http' level for logging }; export const morganMiddleware = morgan( ':method :url :status :res[content-length] - :response-time ms - :remote-addr', // Options: in this case, I overwrote the stream and the skip logic. // See the methods above. { stream } );

Use it in the entry file

  • index.ts
... import { logger, morganMiddleware } from './config/winston'; .... const app = express(); app.use(cors()); app.use(morganMiddleware); // use log middleware before other route settings // otherwise it will behave strangely // only routes which aren't defined will trigger the log // Body parser middleware app.use(express.json()); app.use(express.urlencoded({ extended: false })); app.use('/api', routes); app.use('/swagger', swaggerUi.serve, swaggerUi.setup(swaggerSpec)); app.enable('trust proxy'); const PORT = process.env.PORT || 5000; app.listen(PORT, () => { const message = `server running on port ${PORT}`; logger.info(message); // log info }); (async () => { try { const options = await getConnectionOptions(currentENV); await createConnection({ ...options, name: 'default', synchronize: false }); const message = 'database ok'; logger.info(message); // log info } catch (e) { console.log(e); const message = 'database connection failed!'; logger.error(message); // log error } })();

Can also log something in the common response file

  • customResponse.ts
import { response, Response } from 'express'; import { ICustomResponse, IResponseFormat } from '../../@types'; import { logger } from '../../config/winston'; response.customResponse = function ( httpStatusCode: ICustomResponse['httpStatusCode'], message: ICustomResponse['message'], data: ICustomResponse['data'] = null, errors: ICustomResponse['error'] = null ): Response { const res: IResponseFormat = { status: httpStatusCode, code: httpStatusCode === 200 ? 1 : 0, message }; const validData = data && (typeof data === 'object' || Array.isArray(data)); if (validData) { res.data = data; } if (errors) { res.error = errors; } if (res.code === 0) { let errorMessage = message; if (errors && typeof errors === 'object') { errorMessage = `${errorMessage} - ${JSON.stringify(errors)}`; } logger.error(errorMessage); // Use here to log the error message } return this.status(httpStatusCode).json(res); };