Created
December 16, 2011 22:25
-
-
Save akidee/1488297 to your computer and use it in GitHub Desktop.
An example for a JS logger API, nearly beta
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
_ = require('underscorex') | |
step = require('stepc') | |
slice = Array::slice | |
### | |
Constructor for loggers. A logger is a log function with properties that make it configurable. Pass: | |
preTraits: one or several (Array) traits | |
traits: " | |
transports: one or several transport instances | |
### | |
Logger = exports.Logger = (options = {}) -> | |
_data = _send = undefined | |
logger = -> | |
__ = arguments[arguments.length - 1] | |
if typeof __ != 'function' | |
_send(_data.apply(null, arguments)) | |
# Does a callback exist? | |
else | |
args = slice.call(arguments, 0, arguments.length - 1) | |
_send(_data.apply(null, args), __) | |
(-> | |
_data = () => | |
traitData = [] | |
for trait in @preTraits | |
traitData.push(trait()) | |
for arg, i in arguments | |
trait = @traits[i] | |
data = if !trait | |
arg | |
else | |
trait(arg) | |
traitData.push(data) | |
traitData | |
_send = (data, __) => | |
if !__ | |
for transport in @transports | |
transport.send(data.reduce(transport.reduceCallback, transport.reduceGetInit())) | |
else | |
if [email protected] | |
return __() | |
_this = @ | |
step.async( | |
(e, next) -> | |
group = @group() | |
for transport in _this.transports | |
serializedData = data.reduce(transport.reduceCallback, transport.reduceGetInit()) | |
if transport.send.length <= 1 | |
# try catch? | |
transport.send(serializedData) | |
group()() | |
else | |
transport.send(serializedData, group()) | |
(e, results) -> | |
if e | |
return __(e) | |
__(null, results) | |
) | |
@traits = if !(options.traits instanceof Array) | |
traits = [] | |
if options.traits | |
traits.push(options.traits) | |
traits | |
else | |
options.traits | |
@preTraits = if !(options.preTraits instanceof Array) | |
preTraits = [] | |
if options.preTraits | |
preTraits.push(options.preTraits) | |
preTraits | |
else | |
options.preTraits | |
@transports = if !(options.transports instanceof Array) | |
transports = [] | |
if options.transports | |
transports.push(options.transports) | |
transports | |
else | |
options.transports | |
@ | |
).call(logger) | |
### | |
Create several logging levels. Every object key points to a logger | |
### | |
create = exports.create = (levels, defaultOptions = {}) -> | |
c = {} | |
for own level, _options of levels | |
options = _.extend({}, defaultOptions, _options) | |
c[level] = new Logger(options) | |
c | |
### | |
Traits are default aspects implemented as functions. They are either called without argument (as preTraits) or with exactly 1 argument (as traits which serve as a map function). | |
Every logger can have several preTraits and traits. preTraits are always called - without any argument. If there are traits, they are called, one by one, argument by argument, and can filter a large object to return only the properties that should be logged. Take a look at ./traits/node.coffee to see some map functions for node.js standard instances. | |
If there are no traits the arguments are taken as they are. All objects are pushed to a data object, whcih represents the unserialized essence of this log call. | |
### | |
traits = exports.traits = {} | |
_.extend( | |
traits | |
require('./traits/common') | |
require('./traits/node') | |
) | |
### | |
After traits have been applied, the data must be serialized. | |
Example: Serialize data in a readable way to a string and send it to stdout, or convert the data array into an object and insert it into a MongoDB collection. | |
### | |
transports = exports.transports = | |
MongoDB: require('./transports/mongoDB') | |
WritableStream: require('./transports/writableStream') | |
### | |
The default console object can be implemented by using traits and transports. | |
This helps to change the default behavior of a function as console.log. For example, you can add an additional transport to persist a log, simply add preTraits, like logging memory usage or time. | |
### | |
exports.createConsole = -> | |
c = | |
log: | |
transports: transports.WritableStream.stdout | |
warn: | |
transports: transports.WritableStream.stderr | |
dir: | |
transports: new transports.WritableStream(process.stdout, on) | |
c.info = c.log | |
c.error = c.warn | |
create(c) | |
exports.createPragmaticLoggers = (persistentTransport = null) -> | |
### | |
Only info, warn, error are rarely used or in an exceptional case, so they log to stdout/err | |
by default. | |
### | |
c = | |
log: # frequently called | |
preTraits: traits.t | |
info: # called regularly but not frequently, for basic info | |
preTraits: traits.t | |
transports: transports.WritableStream.stdout | |
warn: # not dangerous, but it should be go to both the console and persistence layer | |
preTraits: traits.t | |
transports: transports.WritableStream.stderr | |
error: # same here | |
preTraits: traits.t | |
traits: traits.error | |
transports: transports.WritableStream.stderr | |
serverRequest: # frequent | |
preTraits: traits.t | |
traits: traits.serverRequest | |
call: # frequent | |
preTraits: traits.t | |
traits: [ traits.call_name, traits.call_args ] | |
process: # regularly | |
preTraits: [ traits.t, traits.process ] | |
memoryUsage: # regularly | |
preTraits: [ traits.t, traits.memoryUsage ] | |
loadavg: # regularly | |
preTraits: [ traits.t, traits.loadavg ] | |
c = create(c) | |
# All functions, except for info, send to the given persistent transport | |
if persistentTransport | |
[ 'log', 'warn', 'error', 'serverRequest', 'call', 'process', 'memoryUsage', 'loadavg' ] | |
.forEach((level) -> | |
c[level].transports.push(persistentTransport) | |
) | |
c |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment