Skip to content

Latest commit

 

History

History

logref

@jfhbrook/logref

an implementation of the process.logging pseudo-specification

huh?

in late 2011, @mikeal posted a thread on the node.js mailing list where he said:

I've spent some time rethinking logging.

Logging is strange problem. It needs to be instrumented, to some degree, in all the modules we use, not just in our application code.

For debugging and increased visibility in to production you just want to be notified of the internal workings.

The traditional log4j approach is kind of ridiculous to instrument as a module developer, you don't really have a need for log levels for instance.

Logging is also one of the very few cases that we need a truly process global module that is not in core.

So, i started to think about what the most minimal API for such a logger might bem.

I created an implementation and a draft spec. The idea here is that we define a minimal logger that different people can implement so long as they obey this minimal API contract. Then in my own modules, like request, I'm going to check for process.logging and if it's there I'll get myself a log function and use it accordingly.

I'm open to suggestions about the API but keep in mind that the last of features is it's greatest feature. Anything that can be built on top of this API without any breaking changes should not be in the spec portion.

https://github.com/mikeal/logging

I'm sure there are typos and misspellings, it's quite late and i'm beat. I'll be using this in request starting tomorrow.

-Mikeal

(note that the github repo got moved to mikeal/logref - the link has been updated.)

as far as I know this never took off and the only module I've seen implement it outside my own is the now-deprecated request library. but the core insight here is a really good and important one.

as a module author, I want to expose simple logging for debug use cases. however, as a module author it's also difficult to bring opinions to the table and nobody wants to ship a whole logger with their module.

on the flip side: as an app writer, I want to be able to log stuff that's happening inside the modules I'm using, but I want control over what happens to those logs. in my case, I want to send them to winston, which is ironically exactly the kind of "traditional log4j approach" mikeal railed against then.

what you really want, then, is a standard - and very lightweight - way to send logging events from your module to a small logger, and for your app to declare how these sorts of logs should be handled app-wide. a spicy take, perhaps, bit mikeal in 2011 was right that this calls for a global.

this post has haunted me in the meantime. just about every project I used that had request in it was using this hook in one way or another, and it was one of the few special features I supported when I wrote my own logger around the same time.

today, I don't really care what other people are using, but I know that I have this problem in my apps today, and this fits the bill. I'll be adding process.logging support to my libraries over time and using this to intercept them in my apps.

api

mikeal's spec is a little fuzzy. some things are specified that don't quite have to be and some things aren't specified when the whole thing won't really work without them. having worked with the original logref off and on for a while, this is where I've arrived when it comes to de facto use.

api for module authors

inside your module, check to see if process.logging is defined, and if it's defined use it to create a logger for your module:

var log = process.logging ? process.logging('my-cool-module') : function () {};

from here, you can call the logging function like so:

log('hello %user', {user: 'world'});

process.logging specifies this particular templating language, which I haven't seen anywhere other than the logref reference implementation. I think this is a big part of why process.logging failed to catch on with developers - but you have to start somewhere, and there is a reference implementation.

api for application authors

on the very first line of your app, define process.logging by importing your implementation:

process.logging = require('@jfhbrook/logref');

the logging framework beyond the API described for module authors is not part of the logref spec; however certain aspects of that design are tough to replace given the requirements, and so the structure and events are very similar.

one major difference, however, is that instead of calling .stdout() and similar to get the logger output, we have logging.observe which can be used to add hooks on every process.logging logger like so:

process.logging.observe(function (logger) {
  // a baby logref logger!
  logger.on('log', function (msg, ctx) {
    console.log(process.logging.format(msg, ctx));
  });
});

loggers are simple EventEmitter instances with the additional property of logger.name - as in mikeal's implementation - and similarly use the 'log' event to emit logs. however, mikeal's implementation also applies formatting to those messages and re-emits them on a 'msg' event - we don't do that here.

api for testing

there's a secret method process.logging.reset() which will drop all registered loggers on the floor. this is mostly only useful if you're doing tests which involve process.logging state.

formatting, linting, tests, oh my!

I'm using my custom prettier config to do formatting, which you can run with npm run format. I don't have linting working yet. tests are written in tap and may be run with npm test. the tests are simple, but so is the module.

license

mikeal didn't put a license on the original logref, which I leaned on heavily, but for what I did here I gave it an MIT license and called it a day.