I am building a Node.JS project (P) and a library (L). P depends on L. L is complex enough to warrant different categories of logging.
I’ve never used a JavaScript logging library before, but I’m experienced with Java loggers (e.g., log4j). Java loggers are built to be configured externally, that is, I can set L’s log levels from P.
But I’ve looked into three Node.JS logging libraries so far, and even though I like winston‘s API enough to give it a try, none of them provided documentation on configuring the logger externally. All the examples require changing L’s source code to change its log levels.
So here’s my question: how do I configure L’s winston log levels from P’s code/configuration?
If I had to come up with my own solution, I probably use See my answer below.process.env.level.<containerName>
in each L module like this:
But given the rest of the winston API, this seems really low level and it feels like there should be an easier way. More importantly, it’s unlikely other libraries would come up with the same implementation, so their logging would not be externally configurable.
Since I needed a solution, I came up with this typescript implementation:
import { Container, Logger, format, transports } from 'winston';
const container = new Container();
export type Category = string; // My real implementation uses a union of string literals.
export const getLogger = (categoryName: Category = 'DEFAULT'): Logger => {
if (getEnvLevelValue('DEFAULT') === undefined) {
throw new Error(
`Logger Configuration Error: Environment Variable '${getEnvLevelKey("DEFAULT")}' does not exist.`
);
}
if (container.has(categoryName)) {
return container.get(categoryName);
} else {
const configuredLevel = getEnvLevelValue(categoryName);
if (configuredLevel === undefined) {
getLogger('DEFAULT').warn(
`Environment Variable '${getEnvLevelKey(categoryName)}' does not exist. Defaulting to 'debug'.`
);
}
return container.add(categoryName, {
level: configuredLevel ?? 'debug',
format: format.simple(),
transports: new transports.Console(),
exceptionHandlers: new transports.Console(),
rejectionHandlers: new transports.Console(),
exitOnError: false,
});
}
};
const getEnvLevelKey = (categoryName: Category): string => {
return `logger.${categoryName}.level`;
};
const getEnvLevelValue = (categoryName: Category): string | undefined => {
return process.env[getEnvLevelKey(categoryName)];
};
It’s used like this: const logger = getLogger("<your category>");
I won’t mark this as accepted because this is only useful if a library uses similar code. I’m looking for a solution that exists for all libraries that use winston, not just mine.
I found an open github issue that might be related.