node package manager

ulog

Microscopically small universal logging library

ulog v1.0.0

Microscopically small universal logging library

.

Ulog builds on the experience gained building and using Picolog, possibly the smallest universal logging library on NPM that supports levels, and adds some features from debug that I missed. Even with these extra features, ulog is still very small, weighing in just over 1 kB minified and gzipped.

npm install --save ulog

my-module.js

var ulog = require('ulog')
var log = ulog('my-module')
// or, shorthand 
var log = require('ulog')('my-module')

my-module.js

import ulog from 'ulog'
const log = ulog('my-module')

my-module.js

define(['ulog'], function(ulog){
    var log = ulog('my-module')
});
<script src="https://cdn.rawgit.com/download/ulog/1.0.0/ulog.min.js"></script>

ulog defines 6 logging methods, which correspond with available log levels:

log.error('This logs an ERROR message')
log.warn('This logs a WARN message')
log.info('This logs an INFO message')
log.log('This logs a LOG message')
log.debug('This logs a DEBUG message')
log.trace('This logs a TRACE message')

ulog does not mess with your stacktrace or line numbers. Line numbers shown in the console will be from your code, not from some wrapper function..

ulog defines 6 logging levels, which correspond with the available logging methods:

log.ERROR // 1 
log.WARN  // 2 
log.INFO  // 3 
log.LOG   // 4 
log.DEBUG // 5 
log.TRACE // 6 

In addition, there is a 7th level that completely disables all logging:

log.NONE  // 0 

To get or set the log level, we use the log.level property:

if (log.level >= log.INFO) {
    log.info('This message will be logged')
}
log.level = log.WARN
log.info('This info message will NOT be logged.')
log.warn('This warning message WILL be logged.')
log.level = log.NONE
log.error('Logging is completely disabled.')

I've found that it makes sense to have different default log levels in the browser and in Node. In Node, logging is often the only UI we have available and we (the devs) are the only ones that will see that logging. In the browser, we have an alternative UI (the webpage itself), so logging will be less useful for normal users.

In Node, the log level defaults to log.INFO. This allows you to use INFO, WARN and ERROR when informing the user of stuff that happened. With Picolog I found I had to resort to logging informational messages at WARN because I wanted them to be visible with the default settings and this did not feel right.

In the browser the log level defaults to log.WARN. This means INFO messages will be excluded, but for most users these messages won't be relevant anyway and we can easily change the log level in the browser using a query parameter in the URL (see next section).

Changing the log level can be done in two ways:

  1. Programmatically, through the API
  2. Via a startup parameter

We can set the global log level directly on the ulog function:

var ulog = require('ulog')
// ... 
ulog.level = ulog.DEBUG

We can set the level of a specific module in much the same way:

var log = ulog('my-module')
// ... 
log.level = ulog.DEBUG

We can set the initial global log level with a startup parameter. In Node we use an environment variable, whereas in the browser we use a querystring parameter in the url.

Set the environment variable LOG to the desired log level.

$ LOG=info && node ./myapp.js

or, in Windows:

set LOG=INFO && node ./myapp.js

Add the parameter log to the querystring of the page:

http://www.example.com/?log=debug

Both the uppercase and lowercase names of the log levels work, as well as their numerical value.

In addition to setting the global log level and setting the log levels of individual loggers, you can also enable debug mode for a group of loggers. When in debug mode, the logger's individual log level will only be used if it is set to TRACE. Otherwise it will be ignored and the module will behave as if its level was set to DEBUG.

The ulog function has 3 methods that allow us to control debug mode:

  • ulog.enable(str) - Enables debug mode for the loggers listed in str
  • ulog.enabled(name) - Tests whether the logger is currently in debug mode
  • ulog.disable() - Disables debug mode for all loggers

The * character may be used as a wildcard. Suppose for example your module has loggers named "connect:bodyParser", "connect:compress" and "connect:session". Instead of listing all three with connect:bodyParser,connect:compress,connect:session, you may simply use connect:*.

You can also exclude specific loggers by prefixing them with a "-" character. For example, *,-connect:* would include all debuggers except those starting with "connect:".

// given modules app, lib, connect:bodyParser, connect:compress and connect:session 
ulog.enable('app,connect:*')
ulog.enabled('app') // true 
ulog.enabled('lib') // false 
ulog.enabled('connect:compress') // true 
ulog.enable('app,connect:*,-connect:compress') // negation symbol means 'except' 
ulog.enabled('app') // true 
ulog.enabled('lib') // false 
ulog.enabled('connect:compress') // false 
ulog.disable()
ulog.enabled('app') // false 

We can enable debug mode for some loggers using a startup parameter. On Node we use environment variables and on the browser we use querystring parameters.

Set the environment variable DEBUG to the string with logger names:

$ DEBUG=my-module && node ./myapp.js

or, in Windows:

set DEBUG=my-module && node ./myapp.js

Add the parameter debug to the querystring of the page:

http://www.example.com/?debug=my-module

ulog supports all functions in the NodeJS Console API, so you should be able to use it as a polyfill in environments where there is no console available (e.g. Nashorn):

// assuming you already made sure there is a `global` object 
global.console = log;
console.info('Nashorn can do logging to!');

The logging methods on the log object that correspond to a log level which is higher than the currently set level, are replaced by no-op methods. As such, you generally don't have to worry about the performance overhead of leaving the log statements in the production code. There is one exception to this rule though. If preparing the message itself is a costly operation, you may want to surround the log code with an if (log.level >= myLevel) statement:

if (log.level >= log.INFO) {
    var message = doLotsOfWorkToGenerateLogMessage();
    log.info(message);
}

Add an issue in this project's issue tracker to let me know of any problems you find, or questions you may have.

Credits go to:

  • Felix Geisendörfer from debuggable.com for kindly giving up the ulog namespace on NPM. Thanks Felix!
  • TJ Holowaychuk for creating debug, which was a great inspiration for ulog.

Copyright 2016 by Stijn de Witt. Some rights reserved.

Licensed under the Creative Commons Attribution 4.0 International (CC-BY-4.0) Open Source license.