Skip to content
Browse files

0.1.0: Initial working version with tests

  • Loading branch information...
Download committed Sep 25, 2016
1 parent 6db5f57 commit 1b98513015a5241909222c3a8057aa68dd57491f
Showing with 1,501 additions and 37 deletions.
  1. +1 −36 .gitignore
  2. +11 −0 .travis.yml
  3. +161 −0
  4. +213 −1
  5. +14 −0 browser.js
  6. +10 −0 microscopical.spec.js
  7. +79 −0 node.js
  8. +62 −0 package.json
  9. +89 −0 test/browser-source-map-support.js
  10. +11 −0 test/index.html
  11. +82 −0 ulog.js
  12. +1 −0 ulog.min.js
  13. +1 −0
  14. BIN
  15. BIN ulog.png
  16. +580 −0 ulog.spec.js
  17. +165 −0 ulog.umd.js
  18. +1 −0
  19. +20 −0 webpack.config.js
@@ -1,37 +1,2 @@
# Logs

# Runtime data

# Directory for instrumented libs generated by jscoverage/JSCover

# Coverage directory used by tools like istanbul

# nyc test coverage

# Grunt intermediate storage (

# node-waf configuration

# Compiled binary addons (

# Dependency directories

# Optional npm cache directory

# Optional REPL history
@@ -0,0 +1,11 @@
language: node_js
- "node"
- "6"
- "5"
- "4"
- "4"
- "0.12"
- "0.11"
- "0.10"
- "iojs"

Large diffs are not rendered by default.

Oops, something went wrong.
@@ -1,2 +1,214 @@
# ulog
# ulog <sub><sup>v0.1.0</sup></sub>
Microscopically small universal logging library

![mind BLOWN](



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.

## Download
* [ulog.umd.js]( (~2kB, source)
* [ulog.min.js]( (~1.1kB, minified)

## Install
npm install --save ulog

## Include in your app

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

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

### AMD
define(['ulog'], function(ulog){
var log = ulog('my-module')

### Script tag
<script src=""></script>

## Logging methods
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');'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..

## Logging levels
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) {'This message will be logged');
log.level = log.WARN;'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.');

## Default log level
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
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
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.

## Changing the log level
Changing the log level can be done in two ways:
1. Programmatically, through the API
2. Via a startup parameter

### Changing the log level via the API
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

### Changing the log level via a startup paramter
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.

#### Environment variable
Set the environment variable `LOG` to the desired log level.

$ LOG=info && node ./myapp.js
or, in Windows:
$ set LOG=INFO && node ./myapp.js

#### Querystring parameter
Add the parameter `log` to the querystring of the page:


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

### Setting modules to DEBUG with a startup parameter
We can set some modules to DEBUG using a startup parameter. In this mode, they
will log any messages of level DEBUG or higher.

#### Environment variable
Set the environment variable `DEBUG` to the module names:

$ DEBUG=my-module && node ./myapp.js
or, in Windows:
$ set DEBUG=my-module && node ./myapp.js

#### Querystring parameter
Add the parameter `debug` to the querystring of the page:


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 `DEBUG=connect:bodyParser,connect:compress,connect:session`,
you may simply do `DEBUG=connect:*`.

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

## Using ulog as a polyfill
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;'Nashorn can do logging to!');

## Performance considerations
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();;

## Copyright
Copyright 2016 by [Stijn de Witt]( Some rights reserved.

## License
Licensed under the [Creative Commons Attribution 4.0 International (CC-BY-4.0)]( Open Source license.
@@ -0,0 +1,14 @@
var log = require('./ulog'),
qs =,
args = qs && qs.split('&'),
lvl, dbg, i, m

for (i=0; m=args && args[i] && args[i].split('='); i++) {
(m[0] == 'log') && (lvl = m[1])
(m[0] == 'debug') && (dbg = m[1])

log.con = function(){return window.console && console}
dbg && log.enable(dbg)
module.exports = log()
log.level = lvl || log.WARN
@@ -0,0 +1,10 @@
var expect = require('chai').expect
var fs = require('fs')
var node = typeof window != 'object'

describe('ulog', function(){
it('is microscopically small (less than 2000 bytes minified, about 1kB when gzipped)', function(){
var stats = fs.statSync("ulog.min.js")
79 node.js
@@ -0,0 +1,79 @@
var log = require('./ulog')

var level, debug

if (process.env.LOG ) {level = process.env.LOG}
if (process.env.DEBUG) {debug = process.env.DEBUG}

var fd = process.env.DEBUG_FD && parseInt(process.env.DEBUG_FD, 10),
c = console;
if (typeof fd == 'number') {
var stream = createWritableStdioStream(fd),
logger = function(){stream.write(util.format.apply(this, arguments) + '\n')}
c = {error:logger, warn:logger, info:logger, log:logger, trace:logger}

log.con = function(){return c}
if (debug) {log.enable(debug)}
module.exports = log()
log.level = level || log.INFO

// SEE
function createWritableStdioStream (fd) {
var stream;
var tty_wrap = process.binding('tty_wrap');

switch (tty_wrap.guessHandleType(fd)) {
case 'TTY':
stream = new tty.WriteStream(fd);
stream._type = 'tty';

// Hack to have stream not keep the event loop alive.
// See
if (stream._handle && stream._handle.unref) {

case 'FILE':
var fs = require('fs');
stream = new fs.SyncWriteStream(fd, { autoClose: false });
stream._type = 'fs';

case 'PIPE':
case 'TCP':
var net = require('net');
stream = new net.Socket({
fd: fd,
readable: false,
writable: true

// FIXME Should probably have an option in net.Socket to create a
// stream from an existing fd which is writable only. But for now
// we'll just add this hack and set the `readable` member to false.
// Test: ./node test/fixtures/echo.js < /etc/passwd
stream.readable = false; = null;
stream._type = 'pipe';

// FIXME Hack to have stream not keep the event loop alive.
// See
if (stream._handle && stream._handle.unref) {

// Probably an error on in uv_guess_handle()
throw new Error('Implement me. Unknown stream file type!');

// For supporting legacy API we put the FD here.
stream.fd = fd;

stream._isStdio = true;

return stream;
Oops, something went wrong.

0 comments on commit 1b98513

Please sign in to comment.
You can’t perform that action at this time.