Disable winston logging when running unit tests?

2020-05-30 08:32发布

Can Winston logging be selectively disabled when executing unit tests of a node module?

Ideally, I'd like to have logging for informational and debug purposes when the application is running, but be suppressed as to not clutter the presentation unit test results when I run my tests.

My use of winston is internal to my module, something like this:

// MyModule.js
var logger = require('winston');
module.exports = function() {
  // does some stuff
  // and logs some stuff like so:
  logger.log('an informational message');
}

// MyModuleTest.js
describe('MyModule', fucntion() {
  it('should do some stuff', function() {
     var myModuleUnderTest = require('MyModule');
     // some tests
  }
}   

7条回答
来,给爷笑一个
2楼-- · 2020-05-30 09:07

Create a logger:

const logger = createLogger({
    level: "info",
    format: format.json(),
    transports: []
});

Silence all logging:

logger.transports.forEach((t) => (t.silent = true));
查看更多
迷人小祖宗
3楼-- · 2020-05-30 09:20

Winston transports have a silent property that you can set, which is probably a little nicer than removing the entire transport.

I add a name to the transports to make is a little easier like this:

var logger = new winston.Logger();

logger.add(winston.transports.Console, {
    name: 'console.info',
    colorize: true,
    showLevel: true,
    formatter: consoleFormatter,
})

Then in the test or set-up I can selectively turn logging on and off with:

logger.transports['console.info'].silent = true  // turns off
logger.transports['console.info'].silent = false // logging back on
查看更多
戒情不戒烟
4楼-- · 2020-05-30 09:21

Here's my setup:

const { createLogger, format, transports, config } = require("winston");

let level, silent;
switch (process.env.NODE_ENV) {
  case "production":
    level = "warning";
    silent = false;
    break;
  case "test":
    level = "emerg";
    silent = true;
    break;
  default:
    level = "debug";
    silent = false;
    break;
}

const options = {
  console: {
    level,
    silent,
    handleExceptions: true,
    format: format.combine(
      format.colorize(),
      format.splat(),
      format.printf(
        info => `${new Date().toISOString()} ${info.level}: ${info.message}`,
      ),
    ),
  },
};

const logger = createLogger({
  levels: config.syslog.levels,
  transports: [new transports.Console(options.console)],
  exitOnError: false,
});

module.exports = logger;
查看更多
疯言疯语
5楼-- · 2020-05-30 09:21

The set up stuff did not work for me, I am using winston v3.1.0, there is a new way to create loggers.

From the winston site: https://github.com/winstonjs/winston

The recommended way to use winston is to create your own logger. The simplest way to do this is using winston.createLogger:

const logger = winston.createLogger({
    level: 'info',
    format: winston.format.json(),
    transports: [
      //
      // - Write to all logs with level `info` and below to `combined.log` 
      // - Write all logs error (and below) to `error.log`.
      //
      new winston.transports.File({ filename: 'error.log', level: 'error' }),
      new winston.transports.File({ filename: 'combined.log' })
    ]
  });

  //
  // If we're not in production then log to the `console` with the format:
  // `${info.level}: ${info.message} JSON.stringify({ ...rest }) `
  // 
  if (process.env.NODE_ENV !== 'production') {
    logger.add(new winston.transports.Console({
    format: winston.format.simple()
  }));
}

So I do this in my logger.js

if (process.env.NODE_ENV === 'test') {
    return winston.createLogger({
      transports: [ new winston.transports.Console({ level: 'error'}) ]
    });
}

This stops all log messages unless you have an error, which I would like to see to help with debugging any issues.

Hope this helps.

查看更多
叛逆
6楼-- · 2020-05-30 09:25

I realise that this is quite late but I just wanted to share my solution with using jest, since I wasn't entirely satisfied with the solutions found here. I can't say my solution is very elegant and may just be hiding some code smell as I'm still learning TDD, but it works.

In my work I often want to log to a file specified via a winston.transports.File(filename: "<filename>") transport. Let's say my log file is info.log

Of course, during testing, I don't want

  1. logs to be written to this info.log
  2. info.log to be created if it doesn't exist.

This is so to avoid side-effects. The answers above along with mocking were enough for avoiding 1. but for some reason did not avoid 2. (explained why below) .

The way I set up my projects is usually as such

   src
    ├── app.js
    ├── services
    │   ├── logging
    │   │   ├── logger.js
    │   │   └── logger_utils.js
    │   ├── foo.js
    │   ├── bar.js
    │   └── etc.js
    ├── tests
    │   ├── foo.test.js
    │   ├── bar.test.js
    │   └── etc.test.js
    └── logs
        └── info.log

Focus mostly on the log-related files. logger.js is where I instantiate and subsequently export the winston Logger object. I then write helper functions in logger_utils.js for modularity and easier testing.

When my issue was appearing, logger.js consisted in

problematic_logger.js
const winston = require("winston");
const path = require("path");
// define the log file directory
const log_dir = path.resolve(__dirname, "./../../logs/info.log");
// create logger
const logger = winston.createLogger({
    transports: [
      new winston.transports.File({
        filename: log_dir
      })
    ]
  });
// export it
module.exports = logger;

I then required it in logger_utils.js which would in turn be required in any other modules scripts. So, in testing (apart from testing logger_utils.js), I only need to mock functions contained in logger_utils.js, with no need to worry about logger.js, since it is only called by logger_utils.js.

Now, I'm not entirely sure about this, but I think 2. defined above still failed despite the mocks and the silencing because winston.createLogger() was still being called, and I believe this will create a file even when a --silent flag is set. I don't know if this is true, but nevertheless the solutions above weren't working.

So, (inspired by this answer) what I decided to do is to simply not create any winston object when testing. I did this by changing my logger.js file to

fixed_logger.js
const winston = require("winston");
const path = require("path");
// define the log file directory
const log_dir = path.resolve(__dirname, "../../logs/info.log");
// if we are testing, don't create any winston object
if (process.env.NODE_ENV === "test") {
  // export
  module.exports = {};
} else {
  // behave normally otherwise
  // create winston logger
  const logger = winston.createLogger({
    transports: [
      new winston.transports.File({
        filename: log_dir
      })
    ]
  });
  // export it
  module.exports = logger;
}

(NODE_ENV is automatically set to "test" when running npm test or npm run test:watch etc.)

We still need to export something for logger_utils.js to not break when testing it, so we export an empty object. This is fine since it will be mocked.

Anyway, that's my first answer on stackoverflow out of the way. I hope it wasn't too disastrous, let me know if anyone wants further details.

查看更多
迷人小祖宗
7楼-- · 2020-05-30 09:28

If you are using Jest, you can disable it like so:

  1. Set set up files to be run before jest runs the test. In package.json:

    {
        "jest": {
            "setupFiles": ["<rootDir>/jest-set-up/index.js"]
        }
    }
    
  2. In jest-set-up/index.js:

    import winston from 'winston'
    winston.remove(winston.transports.Console)
    
查看更多
登录 后发表回答