bithavoc / express-winston

express.js middleware for winstonjs
https://www.npmjs.com/package/express-winston
MIT License
797 stars 186 forks source link

How to do logging within routes using the same logger? #237

Closed kopax closed 4 years ago

kopax commented 4 years ago

Hi and thanks for sharing and reading this,

I have configured these middleware for express:

import winston from 'winston';
import { logger, errorLogger } from 'express-winston';

export const errorLoggerMiddleware = errorLogger({
  transports: [
    new winston.transports.Console(),
  ],
  format: winston.format.combine(
    winston.format.colorize(),
    winston.format.json(),
  ),
});

export default logger({
  transports: [
    new winston.transports.Console(),
  ],
  format: winston.format.combine(
    winston.format.colorize(),
    winston.format.json(),
  ),
  meta: true, // optional: control whether you want to log the meta data about the request (default to true)
  msg: 'HTTP {{req.method}} {{req.url}}', // optional: customize the default logging message. E.g. "{{res.statusCode}} {{req.method}} {{res.responseTime}}ms {{req.url}}"
  expressFormat: true, // Use the default Express/morgan request formatting. Enabling this will override any msg if true. Will only output colors with colorize set to true
  colorize: false, // Color the text and status code, using the Express/morgan color palette (text: gray, status: default green, 3XX cyan, 4XX yellow, 5XX red).
  // ignoreRoute: (/* req, res */) => false, // optional: allows to skip some log messages based on request and/or response
});

I want within this route to add a simple log:

router.get('/search', async (req, res, next) => {
  try {
    logger.log(req.originalUrl);
    const result = await fetch(`${config.addok.url}${req.originalUrl}`);
    const json = await result.json();
    res.json(json);
  } catch (error) {
    if (error.code === 'ENOTFOUND') {
      next(new ErrorHandler(SERVICE_UNAVAILABLE));
    } else {
      next(new ErrorHandler(INTERNAL_SERVER_ERROR, error.message));
    }
  }
});

However, logger.log is not a function, how can I add extra logging within routes using the same express logger?

Thanks for helping,

Dimitri

yinzara commented 4 years ago

"express-winston" is not a logger instance, it's an express middleware to log all requests (or just errors).

You would "router.use" the logger instance to have express log all requests. If you want to do what you're saying, there is no reason to use express-winston. Just use "winston" itself.

If you do want to use the middleware based logging and want to add additional logging, instead of passing "transports" to the logger function, create logger from the winston library itself and pass it as the "winstonInstance" of the logger options (instead of transports).

import winston from 'winston';
import * as expressWinston from 'express-winston';

const logger = winston.createLogger({
   transports: [
    new winston.transports.Console(),
  ],
  format: winston.format.combine(
    winston.format.colorize(),
    winston.format.json(),
  )
});

export const loggerMiddleware = expressWinston.logger({
  winstonInstance: logger,
  meta: true, // optional: control whether you want to log the meta data about the request (default to true)
  msg: 'HTTP {{req.method}} {{req.url}}', // optional: customize the default logging message. E.g. "{{res.statusCode}} {{req.method}} {{res.responseTime}}ms {{req.url}}"
  expressFormat: true, // Use the default Express/morgan request formatting. Enabling this will override any msg if true. Will only output colors with colorize set to true
  colorize: false, // Color the text and status code, using the Express/morgan color palette (text: gray, status: default green, 3XX cyan, 4XX yellow, 5XX red).
  // ignoreRoute: (/* req, res */) => false, // optional: allows to skip some log messages based on request and/or response
});

export const errorLoggerMiddleware = expressWinston.errorLogger({
   winstonInstance: logger
});

export default logger;
import logger, { loggerMiddleware, errorLoggerMiddleware} from "./mylogger"

router.use(loggerMiddleware);
router.get('/search', async (req, res, next) => {
  try {
    logger.log(req.originalUrl);
    const result = await fetch(`${config.addok.url}${req.originalUrl}`);
    const json = await result.json();
    res.json(json);
  } catch (error) {
    if (error.code === 'ENOTFOUND') {
      next(new ErrorHandler(SERVICE_UNAVAILABLE));
    } else {
      next(new ErrorHandler(INTERNAL_SERVER_ERROR, error.message));
    }
  }
});
router.use(errorLoggerMiddleware);
kopax commented 4 years ago

Hi and thanks for your detailed reply.

Using winstonInstance I am still able to use the express middleware and express error middleware, however, when I try to use logger.log, I have:

Cannot create property 'Symbol(level)' on string 'Application is listening on port 3001'.

This is logger.js:

import winston from 'winston';

const logger = winston.createLogger({
  transports: [
    new winston.transports.Console(),
  ],
  format: winston.format.combine(
    winston.format.colorize(),
    winston.format.json(),
  )
});

export default logger;

and this is middleware/logger.js:

import {
  logger as expressLogger,
  errorLogger as expressErrorLogger,
} from 'express-winston';
import logger from '../logger';

export const errorLoggerMiddleware = expressErrorLogger({
  winstonInstance: logger,
});

export default expressLogger({
  winstonInstance: logger,
  meta: true, // optional: control whether you want to log the meta data about the request (default to true)
  msg: 'HTTP {{req.method}} {{req.url}}', // optional: customize the default logging message. E.g. "{{res.statusCode}} {{req.method}} {{res.responseTime}}ms {{req.url}}"
  expressFormat: true, // Use the default Express/morgan request formatting. Enabling this will override any msg if true. Will only output colors with colorize set to true
  colorize: false, // Color the text and status code, using the Express/morgan color palette (text: gray, status: default green, 3XX cyan, 4XX yellow, 5XX red).
  // ignoreRoute: (/* req, res */) => false, // optional: allows to skip some log messages based on request and/or response
});

This is roughly how I try to use logger.log:

import logger from './logger';

export function start() {
  const app = initExpressApp();
  const port = process.env.NODE_PORT || 3001;
  const server = app.listen(port);
  logger.log(`Application is listening on port ${port}`); // eslint-disable-line no-console
  return server;
}

I have done the followings logs:

console.log(logger):

<ref *1> DerivedLogger {
  _readableState: ReadableState {
    objectMode: true,
    highWaterMark: 16,
    buffer: BufferList { head: null, tail: null, length: 0 },
    length: 0,
    pipes: Console {
      _writableState: [WritableState],
      writable: true,
      _events: [Object: null prototype],
      _eventsCount: 6,
      _maxListeners: 30,
      format: undefined,
      level: undefined,
      handleExceptions: undefined,
      silent: undefined,
      name: 'console',
      stderrLevels: {},
      consoleWarnLevels: {},
      eol: '\n',
      __winstonerror: [Function: bound transportEvent],
      __winstonwarn: [Function: bound transportEvent],
      levels: [Object],
      parent: [Circular *1],
      [Symbol(kCapture)]: false
    },
    pipesCount: 1,
    flowing: true,
    ended: false,
    endEmitted: false,
    reading: false,
    sync: false,
    needReadable: true,
    emittedReadable: false,
    readableListening: false,
    resumeScheduled: true,
    paused: false,
    emitClose: true,
    autoDestroy: false,
    destroyed: false,
    defaultEncoding: 'utf8',
    awaitDrain: 0,
    readingMore: false,
    decoder: null,
    encoding: null
  },
  readable: true,
  _events: [Object: null prototype] {
    prefinish: [Function: prefinish],
    end: [Function: bound onceWrapper] { listener: [Function: onend] },
    data: [Function: ondata]
  },
  _eventsCount: 3,
  _maxListeners: undefined,
  _writableState: WritableState {
    objectMode: true,
    highWaterMark: 16,
    finalCalled: false,
    needDrain: false,
    ending: false,
    ended: false,
    finished: false,
    destroyed: false,
    decodeStrings: true,
    defaultEncoding: 'utf8',
    length: 0,
    writing: false,
    corked: 0,
    sync: true,
    bufferProcessing: false,
    onwrite: [Function (anonymous)],
    writecb: null,
    writelen: 0,
    bufferedRequest: null,
    lastBufferedRequest: null,
    pendingcb: 0,
    prefinished: false,
    errorEmitted: false,
    emitClose: true,
    autoDestroy: false,
    bufferedRequestCount: 0,
    corkedRequestsFree: CorkedRequest {
      next: null,
      entry: null,
      finish: [Function (anonymous)]
    }
  },
  writable: true,
  allowHalfOpen: true,
  _transformState: {
    afterTransform: [Function: bound afterTransform],
    needTransform: false,
    transforming: false,
    writecb: null,
    writechunk: null,
    writeencoding: null
  },
  silent: undefined,
  format: Format { options: {}, Format: [Function: Format] },
  defaultMeta: null,
  levels: {
    error: 0,
    warn: 1,
    info: 2,
    http: 3,
    verbose: 4,
    debug: 5,
    silly: 6
  },
  level: 'info',
  exceptions: ExceptionHandler { logger: [Circular *1], handlers: Map(0) {} },
  rejections: RejectionHandler { logger: [Circular *1], handlers: Map(0) {} },
  profilers: {},
  exitOnError: true,
  [Symbol(kCapture)]: false
}

And console.log(Object.keys(logger));:

[
  '_readableState',  'readable',
  '_events',         '_eventsCount',
  '_maxListeners',   '_writableState',
  'writable',        'allowHalfOpen',
  '_transformState', 'silent',
  'format',          'defaultMeta',
  'levels',          'level',
  'exceptions',      'rejections',
  'profilers',       'exitOnError'
]

What am I doing wrong? Thanks once more for your time and help.

Dimitri

kopax commented 4 years ago

Sorry, it is all explained in winston documentation.

The solution is to use :

logger.error('This is error');, 
logger.warn('This is warning');
logger.info('This is info');
logger.verbose('This is verbosity');
logger.debug('This is debug');
logger.silly('This is silly');

// or

logger.log({
  level: 'info',
  message: 'Hello distributed log files!'
});