9

I wrote this module for nodejs that can be used to dispatch events from anywhere via sockjs to the clients.

Now I'd like to include some configurable logging mechanisms.

At the moment, I'm adding winston as a dependency, require it as a logger in each class and use logger.error, logger.warn, ...

#logger.js
var logger = require('winston');
module.exports=logger;

and

#myClass
var logger = require("./logger");
logger.error("Something went wrong")

Now, how can I make the logger be replaceable by a custom logger or have the user configure the log level from OUTSIDE my module?

They should of course not have to touch the module code to change the logger but be able to create the logger with loglevel and my module should use it, if available.

Any receommendations on how to do that?

Or is a hard dependency on "winston" ok and have the log level be configurable via npm-config?

Xosofox
  • 840
  • 1
  • 7
  • 21

2 Answers2

7

Well I suggest to not include Winston at all. Winston is kinda huge and contains a lot of nice features that your users might not use, and I believe that your module should be small enough that the user will always use all it's features. If there's a case that they don't wanna use the built-in logger, well it's not small enough I guess. With that said, a module logging stuff when needed for debugging is helpful.

If you wanna have way for the developer using your module to debug your module with logs, then I suggest you to have a debug mode that can be turned on/off.

You could use something like this to help you: https://www.npmjs.org/package/debug

And if the user wants to integrate their own logging system, I suggest you to have a way that they could pass your module a logging function. Something like this(let's call your module rock-the-sock:

var rockTheSock = require('rock-the-sock');
var rockTheSock.logger = function (level, message, metadata) {
    console.log('level :', level, message, metadata); // TODO: Integrate winston
};
rockTheSock.startRocking();

And then in your module you would have a default logger, which can overriden as shown above.

socket.io is a nice example of how it's done(though you don't really need the configure method, that's just bells and whistles):

Here's how you would override socket.io default logger: https://github.com/LearnBoost/Socket.IO/wiki/Configuring-Socket.IO

Where as the logger is implemented this way: https://github.com/LearnBoost/Socket.IO/blob/0.9.14/lib/logger.js

And if ever something goes wrong, try to throw the error, or emit an 'error' event or call the callback with the error(do whichever applicable). Silently logging it, is not a good idea. If you are reporting it, then let the user code handle it. Maybe the error was expected(you have no idea in what crazy ways people use your modules!).

Farid Nouri Neshat
  • 29,438
  • 6
  • 74
  • 115
  • Hi, this sounds like a good approach, I'm convinced I should get rid of Winston as a dependency. I'm just wondering how to access this logger from within all sub modules? How can I get hold of the "logger" instance that was defined on the main module? – Xosofox May 20 '14 at 11:30
  • If the submodules are in the same codebase(not different published node modules), then make a file like `'./logger.js'` which returns the configured logger, or set it as a property of the main module/instance and let others use that. For different node modules, let the main module configure them and they will use it. `rock-the-sock` module in above, when it wants to log, it just do somehting like `exports.logger('debug', 'logging stuff');`. In socket.io code base it's actually a [property of the manager object](https://github.com/LearnBoost/socket.io/blob/0.9.14/lib/manager.js#L173-181) – Farid Nouri Neshat May 20 '14 at 11:50
  • Looks like I'm too n00bish to understand how to acces the properties. I checked the socket.io code and got scared by the __defineGetter__ approach. Maybe also my whole code is glued together in the wrong way? It's on https://github.com/TURTED/TURTED_node. Maybe you can have a look at examples/server.js? – Xosofox May 20 '14 at 12:16
  • `defineGetter` approach is actually somehow configuring the logger everytime it's accessed through `manager.log`. In your user(example code using your module) code, you can have something like `turted.setLogger(function (level, data) { console .log(level, data)});` which will basically is equal to `exports.setLogger = function (logger) { require('./Logger').baseLogger = logger`, which `Logger` modules calls baseLogger whenever it wants to log something. – Farid Nouri Neshat May 20 '14 at 12:31
  • Also your code is fine. Whenever you need you need to log, just use `./Logger` module. I've would have gone with this approach too. You just need to make it configurable. As I said in the above comment. – Farid Nouri Neshat May 20 '14 at 12:33
  • Sorry, still a little bit lost - is there an example or gist or jsfiddle you could point me to? As my modules don't seem to know about "turted", I'm not sure how they can access this container – Xosofox May 20 '14 at 12:58
1

Although winston is the number one logger used, there also is bunyan which is also widely adopted. So yes, you should leave the choice to the user and let him/her inject the logger into your module. The Elasticsearch client module solves this nicely. You could do it the same way.

analog-nico
  • 2,750
  • 17
  • 25