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
Create a logger:
const logger = createLogger({
level: "info",
format: format.json(),
transports: []
});
Silence all logging:
logger.transports.forEach((t) => (t.silent = true));
Sorry, I know this is a bit of an old question.
What I do is a bit ugly, but allows me to keep using Jest's --silent
option normally. I just set Winston's silent
to process.argv.indexOf("--silent") >= 0
. For example:
const logger = new winston.Logger({
…,
transports: [
new winston.transports.Console({
…,
silent: process.argv.indexOf("--silent") >= 0,
}),
],
});
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;
If you are using Jest, you can disable it like so:
Set set up files to be run before jest runs the test. In package.json
:
{
"jest": {
"setupFiles": ["<rootDir>/jest-set-up/index.js"]
}
}
In jest-set-up/index.js
:
import winston from 'winston'
winston.remove(winston.transports.Console)
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
info.log
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.
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