cli-logger
v0.5.40
Published
Logger implementation for command line interfaces
Downloads
14,321
Readme
Logger
Logger implementation for command line interfaces.
This module is inspired by and borrows from bunyan it differs primarily in that it is designed for command line interfaces rather than long running services. For all intents and purposes it is a drop in replacement for bunyan, see credits for more information.
The library is considered feature complete and ready for use, however the major version has not been incremented to 1
to maintain compatibility with bunyan.
Install
npm install cli-logger
Test
npm test
Features
- Seamless integration with ttycolor
- JSON output compatible with bunyan
- Bitwise log level support
- 100% code coverage
Examples
Example programs are in the bin directory and there are many usage examples in the test suite.
Usage
Unless streams have been configured all log messages are written to process.stdout
.
Normal
Normal mode uses log levels that correspond to the bunyan log levels and behaviour (log messages are written if they are greater than or equal to the configured log level):
var logger = require('cli-logger');
var conf = {level: log.WARN};
log = logger(conf);
log.warn('mock %s message', 'warn');
// this will not be logged as the log level is warn
log.info('mock %s message', 'info');
Bitwise
Using bitwise log levels allows fine-grained control of how log messages are routed, to enable bitwise mode pass true
as the second argument when creating the logger:
var logger = require('cli-logger');
var conf = {level: log.BW_INFO|log.BW_FATAL};
log = logger(conf, true);
log.info('mock %s message', 'info');
If you just want to disable one or two log levels it is more convenient to use the XOR
operator:
var logger = require('cli-logger');
var conf = {level: log.BW_ALL^log.BW_TRACE};
log = logger(conf, true);
log.info('mock %s message', 'info');
Note that in normal mode you may use string log levels, such as 'trace'
, but in bitwise mode you may only use integer log levels.
Messages
Logging messages is consistent with bunyan:
log.info('info message') // log a simple message
log.warn('%s message', 'warn') // log a message with parameters
log.debug({foo: 'bar'}, '%s message', 'debug') // add field(s) to the log record
log.error(err) // log an Error instance
log.fatal(err, '%s error', 'fatal') // log an Error with custom message
Log Levels
The log levels correspond to a method on the logger, the methods and corresponding module constants are shown below:
log.trace() // TRACE: 10 | BW_TRACE: 1
log.debug() // DEBUG: 20 | BW_DEBUG: 2
log.info() // INFO: 30 | BW_INFO: 4
log.warn() // WARN: 40 | BW_WARN: 8
log.error() // ERROR: 50 | BW_ERROR: 16
log.fatal() // FATAL: 60 | BW_FATAL: 32
In normal mode the additional constant NONE
(70) may be used to disable logging. In bitwise mode you may also use BW_NONE
(0) and BW_ALL
(63), BW_ALL
is particularly useful for XOR
operations, see constants.
The API for getting and setting log levels is identical to bunyan:
log.info() // true if any stream is enabled for the info level
log.level() // get a level integer (lowest level of all streams)
log.level(log.INFO) // sets all streams to the INFO level
log.level('info') // sets all streams to the INFO level (normal)
log.levels() // gets an array of the levels of all streams
log.levels(0) // get level of stream at index zero
log.levels('foo') // get level of the stream named 'foo'
log.levels(0, log.INFO) // set level of stream at index zero to INFO
log.levels(0, 'info') // set level of stream at index zero to INFO (normal)
log.levels(0, log.INFO) // set level of stream at index zero to INFO
log.levels('foo', log.WARN) // set level of stream named 'foo' to WARN
Configuration
console
: A boolean indicating that console methods should be used when writing log records, this enables the ttycolor integration, default isfalse
, see console.hostname
: Allows overriding thehostname
field added to all log records, default isnull
but is automatically set toos.hostname()
if not specified, see record.json
: Print log records as newline delimited JSON, default isfalse
, see json.level
: A default log level to use when a stream does not explicitly specify a log level, default isINFO
, see level.name
: The name of the logger, default isbasename(process.argv[1])
, see name.pid
: Allows overriding thepid
field added to all log records, default isnull
but is automatically set toprocess.pid
if not specified, see record.prefix
: A function used to prepend a prefix to log messages, default isnull
, see prefix.serializers
: Map of log record property names to serialization functions, default isnull
, see serializers.src
: A boolean that indicates that file name, line number and function name (when available) should be included in the log record, default isfalse
, see source.stack
: A boolean used in conjunction withsrc
to also include an array of the stack trace caller information, default isfalse
, see source.stream
: Shortcut for specifying a stream for single stream loggers, default isnull
, see streams.streams
: An array or object that configures the streams that log records are written to, by default if this property is not present a single stream is configured forprocess.stdout
, see streams.writers
: Map of log level string names to console functions, default isnull
. Use this to customize the functions used whenconsole
istrue
, see writers.formatter
: A formatter function or string formatter name. Supported names arepedantic
,capitalize
andnormalize
. Signature isfunction(record, parameters, format)
;record
is the log record about to be written,parameters
is the message replacement parameters andformat
is the default formatter function bound to the logger instance. Formatter functions are invoked in the scope of the logger.
If you specify any unknown properties in the configuration then these are considered persistent fields and are added to every log record. This is a convenient way to add labels for sub-components to log records.
Console
Set the console
configuration property to redirect all log messages via console
methods, this enables integration with the ttycolor module.
The default mapping between log methods and console
methods is:
trace // => console.log
debug // => console.log
info // => console.info
warn // => console.warn
error // => console.error
fatal // => console.error
Run the color example program to see the default output.
Writers
You may customize the mapping between log methods and console
methods by either specifying the writers
configuration property as a console
function (to set all log methods to the same console
method):
var conf = {console: true, writers: console.error};
Or by mapping individual log method names, for example if you would prefer trace
and debug
messages to be printed to stderr
:
var conf = {
console: true,
writers: {
trace: console.error,
debug: console.error
}
}
If required you could define your own function that has the same signature as a console
method (function(message, ...)
) to create different output styles using the ttycolor module methods.
This configuration option should only be used in conjunction with the console
option.
JSON
This module is designed for command line interfaces so JSON output is not enabled by default, you may enable JSON output for all streams by specifying the json
configuration option or enable for specific streams by specifying the json
property on a stream.
Alternatively, you can use createLogger
to set the json
configuration property if it is not defined:
var logger = require('cli-logger');
log = logger.createLogger();
// ...
Level
The level
configuration option sets the default log level when a level is not specified on a stream, it may also be used in conjunction with stream
to configure a single stream, see streams for an example.
Name
All loggers must have a name
specified, typically this will be the name of the program so it is determined automatically, however you can override with a different name
if required. Note that when specified this option must be a non-zero length string or an error will be thrown.
Prefix
Sometimes it may be useful to prefix all log messages, possibly with the program name, log level or date. Specify a function as the prefix
configuration option to set a prefix for all log messages. The function has the signature:
function prefix(record)
It should return a string to prepend to all log messages.
The prefix
function is invoked in the scope of the Logger
instance so you may access this.name
, this.names()
etc. See the prefix example program.
Source
Set the src
configuration property when you want information about the file name, line number and function name that invoked a log method, you may also set the stack
property to include a full stack trace.
Caution: do not use this in production, retrieving call site information is slow.
var logger = require('cli-logger');
var conf = {src: true, stack: true};
var log = logger(conf);
log.on('write', function(record, stream, msg, parameters) {
console.log(JSON.stringify(record, undefined, 2));
})
function info() {
log.info('mock %s message', 'info');
}
info();
{
"time": "2014-02-17T16:28:02.573Z",
"pid": 2747,
"hostname": "pearl.local",
"name": "source",
"msg": "mock info message",
"level": 30,
"src": {
"file": "/Users/cyberfunk/git/cli/logger/bin/source",
"line": 16,
"func": "info",
"stack": [
"info (/Users/cyberfunk/git/cli/logger/bin/source:16:7)",
"Object.<anonymous> (/Users/cyberfunk/git/cli/logger/bin/source:18:1)",
"Module._compile (module.js:456:26)",
"Object.Module._extensions..js (module.js:474:10)",
"Module.load (module.js:356:32)",
"Function.Module._load (module.js:312:12)",
"Function.Module.runMain (module.js:497:10)"
]
},
"v": 0
}
See the source example program.
Serializers
Serializers behave the same as bunyan and the same standard serializers are provided via the serializers
module property (also aliased via stdSerializers
for bunyan compatibility).
req
: Serialize an HTTP request.res
: Serialize an HTTP response.err
: Serialize an Error instance.
var logger = require('cli-logger');
var conf = {serializers: {err: logger.serializers.err}};
var log = logger(conf);
log.on('write', function(record, stream, msg, parameters) {
console.log(JSON.stringify(record, undefined, 2));
})
log.info({err: new Error('Mock simple error')});
{
"time": "2014-02-17T15:54:52.830Z",
"err": {
"message": "Mock simple error",
"name": "Error",
"stack": "..."
},
"pid": 65543,
"hostname": "pearl.local",
"name": "write",
"msg": "",
"level": 30,
"v": 0
}
See the serialize example program.
Streams
The following types of streams are supported:
stream
: An existing stream such asprocess.stdout
,process.stderr
or some other writable stream.file
: Creates a writable stream from a string file system path.raw
: Forces the raw log record to always be passed to thewrite()
method of the stream.console
: A stream that writes log messages to aconsole
method, streams of this type are passed the raw log record which is decorated with the additional propertiesmessage
(the raw message string) andparameters
(message replacement parameters).
Stream
Configuring output streams is typically done using an array, but as a convenience an object may be specified to configure a single output stream:
var logger = require('cli-logger');
var conf = {streams: {stream: process.stderr, level: log.WARN}}
var log = logger(conf);
// ...
Or even more succinctly for single stream loggers you can specify stream
at the configuration level:
var logger = require('cli-logger');
var conf = {stream: process.stderr, level: log.FATAL}
var log = logger(conf);
// ...
File
You may configure a file stream by using the path
stream property. For example, to log the INFO
level and above to stdout
and ERROR
and above to a file:
var logger = require('cli-logger');
var conf = {streams: [
{
stream: process.stdout,
level: log.INFO
},
{
path: 'log/errors.log',
level: log.ERROR
}
]};
var log = logger(conf);
// ...
When creating file streams the default flags used is a
you may specify the flags
property to change this behaviour:
var logger = require('cli-logger');
var conf = {streams: [
{
path: 'log/errors.log',
flags: 'ax',
level: log.ERROR
}
]};
var log = logger(conf);
// ...
The encoding
and mode
options supported by fs.createWriteStream
are also respected if present.
Raw
The raw stream type is useful when you want to ensure that the stream implementation is passed the raw log record, this type is implied when using a ring buffer.
Console
Use the console
stream type when you need access to the raw message and message replacement parameters. This stream type is implied when the console
configuration property is set, alternatively you can use this to redirect some messages to console
methods and possibly others to a log file as JSON records, for example:
var logger = require('cli-logger');
var conf = {
streams: [
{
stream: new logger.ConsoleStream(),
level: log.BW_TRACE|log.BW_DEBUG|log.BW_INFO
},
{
path: 'log/json.log',
json: true,
level: log.BW_ALL^log.BW_TRACE^log.BW_DEBUG^log.BW_INFO
}
]
}
var log = logger(conf, true);
// print to stdout using `console.info`
log.info('mock %s message', 'info');
// print to log file as a json record
log.warn('mock %s message', 'warn');
See the json example program.
Child Loggers
The module supports child loggers to label subcomponents of your application and follows the same rules as bunyan:
- All streams are inherited from the parent logger, you may configure child loggers with additional streams but you cannot remove the streams inherited from the parent.
- The parent's serializers are inherited though may be overriden in the child.
- Changes to the log level of a child logger do not affect the parent log level.
See the child example program.
Ring Buffer
The RingBuffer
implementation does not write records to a stream it gathers them into an array (with a specified limit) and optionally can flush the records to a stream at a later time.
This is useful if you wish to keep track of all (or some) log messages and then write a debug log file when a particular event occurs (such as a fatal error).
See the ringbuffer example program.
Events
The Logger
implementation dispatches the following events:
error
Emitted if there is an error on an underlying stream, it is recommended you listen for this event.
function error(err, stream)
log
Emitted when a log record has been written to stream(s), if listeners exist for write
this event will not fire.
function log(record, level, msg, parameters)
Note that if a log record is written to multiple streams this event will only be emitted once for the log record.
write
If a listener exists for this event it is invoked with all the log record information and no data is written by the logger to the underlying stream(s).
function write(record, stream, msg, parameters)
Note that record.msg
contains the message with parameters substituted, whilst msg
and parameters
are the raw message and replacement parameters should you need them.
This event will fire once for each stream that is enabled for the log level associated with the record and effectively disables all the default logic for writing log records, if you listen for this event it is your responsibility to process the log record.
Constants
The exposed log level constants deserve some attention. The module exposes all normal mode levels as constants, eg INFO
and prefixes bitwise constants for example, BW_INFO
. However constants are also exposed on the instance regardless of mode, such that the following is possible:
var logger = require('cli-logger');
log = logger();
// set level of all streams to warn
log.level(log.WARN);
// ...
The equivalent for bitwise mode is:
var logger = require('cli-logger');
log = logger(null, true);
// set level of all streams to warn
log.level(log.WARN);
// ...
Note that no prefix is necessary when accessing constants on the instance.
The rule of thumb is that if you are setting log levels in the configuration then use the prefixed constants for bitwise mode. If you are setting log levels after instantiation then you can use the constants exposed by the Logger
instance without a prefix regardless of mode.
Record
Log record fields are consistent with bunyan, a log record in normal mode may look like:
{
"time": "2014-02-18T06:42:41.868Z",
"pid": 31641,
"hostname": "pearl.local",
"name": "record",
"msg": "mock info message",
"level": 30,
"v": 0
}
See the record example program.
Fields
v
: Required integer, cannot be overriden. Module major version extracted frompackage.json
.level
: Required integer, cannot be overriden. Determined automatically by the log method.name
: Required string, cannot be overriden. Provided at creation orbasename(process.argv[1])
.hostname
: Required string, provided at creation oros.hostname()
.pid
: Required integer, provided at creation orprocess.pid
.time
: Required string, can be overriden. Default isnew Date().toISOString()
.msg
: Required string. Must be supplied when calling a log method.src
: Optional object, contains log caller information. Automatically added if thesrc
configuration property has been set.
You may add your own fields to log records by specifying an object as the first argument to a log method, see messages.
API
Module
logger([conf], [bitwise])
Create a Logger
instance.
conf
: A configuration for the logger.bitwise
: A boolean indicating the logger uses bitwise log levels.
var logger = require('cli-logger');
var log = logger();
createLogger([conf], [bitwise])
Create a Logger
instance and enable JSON log records for all streams.
conf
: A configuration for the logger.bitwise
: A boolean indicating the logger uses bitwise log levels.
var logger = require('cli-logger');
var log = logger.createLogger();
bitwise
Map of all bitwise mode constants.
circular
Reference to the function used to safely stringify
objects that may contain circular references.
ConsoleStream
Reference to the ConsoleStream
class.
keys
Array of log method names.
levels
Map of all normal mode constants.
Logger
Reference to the Logger
class.
RingBuffer
Reference to the RingBuffer
class.
serializers
Map of standard serializer functions, aliased via stdSerializers
, see serializers.
types
Array of supported stream types.
Constants (miscellaneous)
CONSOLE
: String representing the console stream type.FILE
: String representing the file stream type.LOG_VERSION
: Integer indicating the module major version.RAW
: String representing the raw stream type.STREAM
: String representing the stream type.
Constants (normal)
TRACE
: Integer representing the trace log level.DEBUG
: Integer representing the debug log level.INFO
: Integer representing the info log level.WARN
: Integer representing the warn log level.ERROR
: Integer representing the error log level.FATAL
: Integer representing the fatal log level.NONE
: Integer representing the none log level.
Constants (bitwise)
BW_NONE
: Integer representing the none log level.BW_TRACE
: Integer representing the trace log level.BW_DEBUG
: Integer representing the debug log level.BW_INFO
: Integer representing the info log level.BW_WARN
: Integer representing the warn log level.BW_ERROR
: Integer representing the error log level.BW_FATAL
: Integer representing the fatal log level.BW_ALL
: Integer representing the all log level.
ConsoleStream
Class used to redirect log records to console
methods.
ConsoleStream([options])
Create a ConsoleStream
instance.
options
: Options for the console stream.
The options
may contain a writers
property to map log level string names to console
functions, see writers.
Logger
Class representing the logger implementation.
Logger([conf], [bitwise])
Create a Logger
instance.
conf
: A configuration for the logger.bitwise
: A boolean indicating the logger uses bitwise log levels.
child([conf], [bitwise])
Creates a child Logger
instance, see child loggers.
conf
: A configuration for the child logger.bitwise
: A boolean indicating the child logger uses bitwise log levels.
conf
Map of the logger configuration.
debug([object], message, ...)
Log a debug message.
object
: An object orError
instance.message
: The log message....
: Message replacement parameters.
Returns a boolean indicating whether the log record was written to a stream.
When invoked with zero arguments this method returns a boolean indicating if the debug
level is enabled for any stream.
error([object], message, ...)
Log an error message.
object
: An object orError
instance.message
: The log message....
: Message replacement parameters.
Returns a boolean indicating whether the log record was written to a stream.
When invoked with zero arguments this method returns a boolean indicating if the error
level is enabled for any stream.
fatal([object], message, ...)
Log a fatal error message.
object
: An object orError
instance.message
: The log message....
: Message replacement parameters.
Returns a boolean indicating whether the log record was written to a stream.
When invoked with zero arguments this method returns a boolean indicating if the fatal
level is enabled for any stream.
info([object], message, ...)
Log an info message.
Returns a boolean indicating whether the log record was written to a stream.
When invoked with zero arguments this method returns a boolean indicating if the info
level is enabled for any stream.
level([level])
Get or set the log level for all streams, see log levels.
level
: Integer level to set on all streams.
Returns the lowest level of all streams when invoked with no arguments.
levels([name], [level])
Get or set the log level for individual streams, see log levels.
name
: The name of the stream or a valid index into the streams array.level
: Integer level to set on a stream.
Returns an array of the levels of all streams when invoked with no arguments.
name
The string name of the logger.
names(level, [complex])
Retrieve the string name of a level from a log level integer.
level
: The log level integer.complex
: A boolean indicating that complex bitwise log levels should be resolved to an array of names.
Returns the string log level, or if complex
is specified an array of log level names.
If level
could not be resolved to a string name (unknown level) it is returned.
trace([object], message, ...)
Log a trace message.
object
: An object orError
instance.message
: The log message....
: Message replacement parameters.
Returns a boolean indicating whether the log record was written to a stream.
When invoked with zero arguments this method returns a boolean indicating if the trace
level is enabled for any stream.
warn([object], message, ...)
Log a warn message.
object
: An object orError
instance.message
: The log message....
: Message replacement parameters.
Returns a boolean indicating whether the log record was written to a stream.
When invoked with zero arguments this method returns a boolean indicating if the warn
level is enabled for any stream.
RingBuffer
Class used to buffer log records in an array.
RingBuffer([options])
Create a RingBuffer
instance.
options
: Options for the ring buffer.
The options
may contain a limit
integer which determines the maximum number of records to buffer and a json
boolean which indicates that the log records should be written as JSON when they are flushed.
flush(stream, [options])
Flush buffered log records to a stream, the buffered log records are cleared as they are written to the stream.
stream
: A string file system path or an existing writable stream.options
: Options passed tofs.createWriteStream
whenstream
is a string.
When stream
is a string the created writable stream is destroyed once all records have been written, otherwise if a writable stream is passed you should destroy the stream when necessary.
If stream
is a string and no options
are passed the w
mode is used with utf8
encoding.
Returns the writable stream.
Credits
This module is inspired by bunyan, by far the best logging library for node. Thanks to trentm for the elegant library.
The following functions have been borrowed more or less verbatim from bunyan:
circular
(safeCycles
)getCallerInfo
(getCaller3Info
)getFullErrorStack
serializers
(stdSerializers
)
Caveats
Bugs notwithstanding, the following list of caveats apply:
dtrace
: This library has nodtrace
support as it is far less likely to be required for command line interfaces. Furthermore, we have had intermittent compile errors while buildingdtrace
on Linux which at times has broken automated deployment.rotating-file
: The rotating file stream type has not been implemented as it is more applicable to long running services, if it is ever implemented it will be based on file size as opposed to daily rotation.
This library does not provide a command line tool to inspect JSON log records as it is designed to be compatible with bunyan(1)
which you can use to view and query JSON logs.