minimal browser implementation
This commit is contained in:
Родитель
edc2ebb8fe
Коммит
b914c53d0d
|
@ -0,0 +1,4 @@
|
|||
name: pino
|
||||
ui: tape
|
||||
browserify:
|
||||
- transform: fresh-require/transform
|
|
@ -19,6 +19,7 @@ It also includes a shell utility to pretty-print its log files.
|
|||
* [How do I rotate log files?](#rotate)
|
||||
* [How do I redact sensitive information?](#redact)
|
||||
* [How to use Transports with Pino](#transports)
|
||||
* [Pino in the browser](#browser)
|
||||
* [Caveats](#caveats)
|
||||
* [Team](#team)
|
||||
* [Acknowledgements](#acknowledgements)
|
||||
|
@ -940,6 +941,13 @@ https://github.com/deviantony/docker-elk to setup an ELK stack.
|
|||
|
||||
[pino-socket]: https://www.npmjs.com/package/pino-socket
|
||||
|
||||
<a name="browser"></a>
|
||||
## Pino in the browser
|
||||
|
||||
Pino is compatible with [`browserify`](http://npm.im) for browser side usage. This can be useful with isomorphic/universal JavaScript code.
|
||||
|
||||
In the browser, `pino` uses corresponding [Log4j](https://en.wikipedia.org/wiki/Log4j) `console` methods (`console.error`, `console.warn`, `console.info`, `console.debug`, `console.trace`) and uses `console.error` for any `fatal` level logs.
|
||||
|
||||
<a name="caveats"></a>
|
||||
## Caveats
|
||||
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
'use strict'
|
||||
|
||||
module.exports = pino
|
||||
|
||||
var _console = global.console || {}
|
||||
function Pino () {}
|
||||
Pino.prototype = _console
|
||||
|
||||
function pino (opts) {
|
||||
opts = opts || {}
|
||||
var level = opts.level || 'info'
|
||||
var val = pino.levels.values[level]
|
||||
var logger = new Pino()
|
||||
if (!logger.log) logger.log = noop
|
||||
|
||||
set(logger, val, 'error', 'log') // <-- must stay first
|
||||
set(logger, val, 'fatal', 'error')
|
||||
set(logger, val, 'warn', 'error')
|
||||
set(logger, val, 'info', 'log')
|
||||
set(logger, val, 'debug', 'log')
|
||||
set(logger, val, 'trace', 'log')
|
||||
|
||||
logger.setMaxListeners = logger.getMaxListeners =
|
||||
logger.emit = logger.addListener = logger.on =
|
||||
logger.prependListener = logger.once =
|
||||
logger.prependOnceListener = logger.removeListener =
|
||||
logger.removeAllListeners = logger.listeners =
|
||||
logger.listenerCount = logger.eventNames =
|
||||
logger.write = logger.flush = noop
|
||||
|
||||
logger.child = function child (bindings) {
|
||||
if (!bindings) {
|
||||
throw new Error('missing bindings for child Pino')
|
||||
}
|
||||
function Child (parent) {
|
||||
this.error = bind(parent, bindings, 'error')
|
||||
this.fatal = bind(parent, bindings, 'fatal')
|
||||
this.warn = bind(parent, bindings, 'warn')
|
||||
this.info = bind(parent, bindings, 'info')
|
||||
this.debug = bind(parent, bindings, 'debug')
|
||||
this.trace = bind(parent, bindings, 'trace')
|
||||
}
|
||||
Child.prototype = this
|
||||
return new Child(this)
|
||||
}
|
||||
logger.levels = pino.levels
|
||||
return logger
|
||||
}
|
||||
|
||||
pino._Pino = Pino
|
||||
pino.LOG_VERSION = 1
|
||||
|
||||
pino.levels = {
|
||||
values: {
|
||||
fatal: 60,
|
||||
error: 50,
|
||||
warn: 40,
|
||||
info: 30,
|
||||
debug: 20,
|
||||
trace: 10
|
||||
},
|
||||
labels: {
|
||||
'10': 'trace',
|
||||
'20': 'debug',
|
||||
'30': 'info',
|
||||
'40': 'warn',
|
||||
'50': 'error',
|
||||
'60': 'fatal'
|
||||
}
|
||||
}
|
||||
|
||||
pino.stdSerializers = {
|
||||
req: mock,
|
||||
res: mock,
|
||||
err: mock
|
||||
}
|
||||
|
||||
function bind (parent, bindings, level) {
|
||||
return function () {
|
||||
var args = new Array(1 + arguments.length)
|
||||
args[0] = bindings
|
||||
for (var i = 1; i < args.length; i++) {
|
||||
args[i] = arguments[i - 1]
|
||||
}
|
||||
return parent[level].apply(null, args)
|
||||
}
|
||||
}
|
||||
|
||||
function set (logger, val, level, fallback) {
|
||||
logger[level] = val > pino.levels.values[level] ? noop
|
||||
: (logger[level] ? logger[level] : (_console[fallback] || noop))
|
||||
}
|
||||
|
||||
function mock () { return {} }
|
||||
function noop () {}
|
1
noop.js
1
noop.js
|
@ -1 +0,0 @@
|
|||
module.exports = function noop () {}
|
10
package.json
10
package.json
|
@ -3,13 +3,12 @@
|
|||
"version": "2.13.2",
|
||||
"description": "super fast, all natural json logger",
|
||||
"main": "pino.js",
|
||||
"browser": {
|
||||
"./pretty.js": "./noop.js"
|
||||
},
|
||||
"browser": "./browser.js",
|
||||
"bin": {
|
||||
"pino": "./pretty.js"
|
||||
},
|
||||
"scripts": {
|
||||
"browser-test": "zuul tape test/browser.test.js --local",
|
||||
"test": "standard && tap --no-cov test/*test.js",
|
||||
"ci": "standard && tap --cov test/*test.js"
|
||||
},
|
||||
|
@ -37,12 +36,15 @@
|
|||
"debug": "^2.2.0",
|
||||
"fastbench": "^1.0.0",
|
||||
"flush-write-stream": "^1.0.0",
|
||||
"fresh-require": "^1.0.3",
|
||||
"log": "^1.4.0",
|
||||
"loglevel": "^1.4.0",
|
||||
"pre-commit": "^1.1.2",
|
||||
"standard": "^8.0.0",
|
||||
"tap": "^7.0.0",
|
||||
"winston": "^2.1.1"
|
||||
"tape": "^4.6.2",
|
||||
"winston": "^2.1.1",
|
||||
"zuul": "^3.11.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"chalk": "^1.1.1",
|
||||
|
|
8
pino.js
8
pino.js
|
@ -2,10 +2,10 @@
|
|||
|
||||
var stringifySafe = require('fast-safe-stringify')
|
||||
var format = require('quick-format')
|
||||
var EventEmitter = require('events').EventEmitter
|
||||
var os = require('os')
|
||||
var flatstr = require('flatstr')
|
||||
var once = require('once')
|
||||
var noop = require('./noop')
|
||||
var pid = process.pid
|
||||
var hostname = os.hostname()
|
||||
var baseLog = flatstr('{"pid":' + pid + ',"hostname":"' + hostname + '",')
|
||||
|
@ -141,9 +141,7 @@ function Pino (level, stream, serializers, stringify, end, name, timestamp, slow
|
|||
}
|
||||
}
|
||||
|
||||
if (require('eve' + 'nts')) {
|
||||
Pino.prototype = new (require('eve' + 'nts').EventEmitter)()
|
||||
}
|
||||
Pino.prototype = new EventEmitter()
|
||||
|
||||
Pino.prototype.fatal = genLog(levels.fatal)
|
||||
Pino.prototype.error = genLog(levels.error)
|
||||
|
@ -416,6 +414,8 @@ function onExit (fn) {
|
|||
}
|
||||
}
|
||||
|
||||
function noop () {}
|
||||
|
||||
module.exports = pino
|
||||
module.exports.stdSerializers = {
|
||||
req: asReqValue,
|
||||
|
|
|
@ -0,0 +1,255 @@
|
|||
'use strict'
|
||||
var test = require('tape')
|
||||
var fresh = require('fresh-require')
|
||||
var pino = require('../browser')
|
||||
|
||||
levelTest('fatal')
|
||||
levelTest('error')
|
||||
levelTest('warn')
|
||||
levelTest('info')
|
||||
levelTest('debug')
|
||||
levelTest('trace')
|
||||
|
||||
test('throw if creating child without bindings', function (t) {
|
||||
t.plan(1)
|
||||
|
||||
var instance = pino()
|
||||
|
||||
t.throws(function () {
|
||||
instance.child()
|
||||
})
|
||||
})
|
||||
|
||||
test('stubs write, flush and ee methods on instance', function (t) {
|
||||
var instance = pino()
|
||||
|
||||
t.ok(isFunc(instance.setMaxListeners))
|
||||
t.ok(isFunc(instance.getMaxListeners))
|
||||
t.ok(isFunc(instance.emit))
|
||||
t.ok(isFunc(instance.addListener))
|
||||
t.ok(isFunc(instance.on))
|
||||
t.ok(isFunc(instance.prependListener))
|
||||
t.ok(isFunc(instance.once))
|
||||
t.ok(isFunc(instance.prependOnceListener))
|
||||
t.ok(isFunc(instance.removeListener))
|
||||
t.ok(isFunc(instance.removeAllListeners))
|
||||
t.ok(isFunc(instance.listeners))
|
||||
t.ok(isFunc(instance.listenerCount))
|
||||
t.ok(isFunc(instance.eventNames))
|
||||
t.ok(isFunc(instance.write))
|
||||
t.ok(isFunc(instance.flush))
|
||||
|
||||
t.is(instance.on(), undefined)
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
test('exposes levels object', function (t) {
|
||||
t.deepEqual(pino.levels, {
|
||||
values: {
|
||||
fatal: 60,
|
||||
error: 50,
|
||||
warn: 40,
|
||||
info: 30,
|
||||
debug: 20,
|
||||
trace: 10
|
||||
},
|
||||
labels: {
|
||||
'10': 'trace',
|
||||
'20': 'debug',
|
||||
'30': 'info',
|
||||
'40': 'warn',
|
||||
'50': 'error',
|
||||
'60': 'fatal'
|
||||
}
|
||||
})
|
||||
t.end()
|
||||
})
|
||||
|
||||
test('exposes LOG_VERSION', function (t) {
|
||||
t.is(pino.LOG_VERSION, 1)
|
||||
t.end()
|
||||
})
|
||||
|
||||
test('exposes faux _Pino constructor', function (t) {
|
||||
t.ok(isFunc(pino._Pino))
|
||||
t.end()
|
||||
})
|
||||
|
||||
test('exposes faux stdSerializers', function (t) {
|
||||
t.ok(pino.stdSerializers)
|
||||
t.ok(pino.stdSerializers.req)
|
||||
t.ok(pino.stdSerializers.res)
|
||||
t.ok(pino.stdSerializers.err)
|
||||
t.deepEqual(pino.stdSerializers.req(), {})
|
||||
t.deepEqual(pino.stdSerializers.res(), {})
|
||||
t.deepEqual(pino.stdSerializers.err(), {})
|
||||
t.end()
|
||||
})
|
||||
|
||||
consoleMethodTest('error')
|
||||
consoleMethodTest('fatal', 'error')
|
||||
consoleMethodTest('warn')
|
||||
consoleMethodTest('info')
|
||||
consoleMethodTest('debug')
|
||||
consoleMethodTest('trace')
|
||||
absentConsoleMethodTest('error', 'log')
|
||||
absentConsoleMethodTest('warn', 'error')
|
||||
absentConsoleMethodTest('info', 'log')
|
||||
absentConsoleMethodTest('debug', 'log')
|
||||
absentConsoleMethodTest('trace', 'log')
|
||||
|
||||
test('in absence of console, log methods become noops', function (t) {
|
||||
var console = global.console
|
||||
delete global.console
|
||||
var instance = fresh('../browser', require)()
|
||||
global.console = console
|
||||
t.is(fnName(instance.log), 'noop')
|
||||
t.is(fnName(instance.fatal), 'noop')
|
||||
t.is(fnName(instance.error), 'noop')
|
||||
t.is(fnName(instance.warn), 'noop')
|
||||
t.is(fnName(instance.info), 'noop')
|
||||
t.is(fnName(instance.debug), 'noop')
|
||||
t.is(fnName(instance.trace), 'noop')
|
||||
t.end()
|
||||
})
|
||||
|
||||
test('exposes faux _Pino constructor', function (t) {
|
||||
t.ok(isFunc(pino._Pino))
|
||||
t.end()
|
||||
})
|
||||
|
||||
function levelTest (name) {
|
||||
test(name + ' logs', function (t) {
|
||||
var msg = 'hello world'
|
||||
sink(name, function (args) {
|
||||
t.is(args[0], msg)
|
||||
t.end()
|
||||
})
|
||||
pino({level: name})[name](msg)
|
||||
})
|
||||
|
||||
test('passing objects at level ' + name, function (t) {
|
||||
var msg = { hello: 'world' }
|
||||
sink(name, function (args) {
|
||||
t.is(args[0], msg)
|
||||
t.end()
|
||||
})
|
||||
pino({level: name})[name](msg)
|
||||
})
|
||||
|
||||
test('passing an object and a string at level ' + name, function (t) {
|
||||
var a = { hello: 'world' }
|
||||
var b = 'a string'
|
||||
sink(name, function (args) {
|
||||
t.is(args[0], a)
|
||||
t.is(args[1], b)
|
||||
t.end()
|
||||
})
|
||||
pino({level: name})[name](a, b)
|
||||
})
|
||||
|
||||
test('formatting logs as ' + name, function (t) {
|
||||
sink(name, function (args) {
|
||||
t.is(args[0], 'hello %d')
|
||||
t.is(args[1], 42)
|
||||
t.end()
|
||||
})
|
||||
pino({level: name})[name]('hello %d', 42)
|
||||
})
|
||||
|
||||
test('passing error at level ' + name, function (t) {
|
||||
var err = new Error('myerror')
|
||||
sink(name, function (args) {
|
||||
t.is(args[0], err)
|
||||
t.end()
|
||||
})
|
||||
pino({level: name})[name](err)
|
||||
})
|
||||
|
||||
test('passing error with a serializer at level ' + name, function (t) {
|
||||
// in browser - should have no effect (should not crash)
|
||||
var err = new Error('myerror')
|
||||
sink(name, function (args) {
|
||||
t.is(args[0].err, err)
|
||||
t.end()
|
||||
})
|
||||
var instance = pino({
|
||||
level: name,
|
||||
serializers: {
|
||||
err: pino.stdSerializers.err
|
||||
}
|
||||
})
|
||||
instance[name]({err: err})
|
||||
})
|
||||
|
||||
test('child logger for level ' + name, function (t) {
|
||||
var msg = 'hello world'
|
||||
var parent = { hello: 'world' }
|
||||
sink(name, function (args) {
|
||||
t.is(args[0], parent)
|
||||
t.is(args[1], msg)
|
||||
t.end()
|
||||
})
|
||||
var instance = pino({level: name})
|
||||
var child = instance.child(parent)
|
||||
child[name](msg)
|
||||
})
|
||||
|
||||
test('child-child logger for level ' + name, function (t) {
|
||||
var msg = 'hello world'
|
||||
var grandParent = { hello: 'world' }
|
||||
var parent = { hello: 'you' }
|
||||
sink(name, function (args) {
|
||||
t.is(args[0], grandParent)
|
||||
t.is(args[1], parent)
|
||||
t.is(args[2], msg)
|
||||
t.end()
|
||||
})
|
||||
var instance = pino({level: name})
|
||||
var child = instance.child(grandParent).child(parent)
|
||||
child[name](msg)
|
||||
})
|
||||
}
|
||||
|
||||
function consoleMethodTest (level, method) {
|
||||
if (!method) method = level
|
||||
test('pino().' + level + ' uses console.' + method, function (t) {
|
||||
sink(method, function (args) {
|
||||
t.is(args[0], 'test')
|
||||
t.end()
|
||||
})
|
||||
var instance = require('../browser')({level: level})
|
||||
instance[level]('test')
|
||||
})
|
||||
}
|
||||
|
||||
function absentConsoleMethodTest (method, fallback) {
|
||||
test('in absence of console.' + method + ', console.' + fallback + ' is used', function (t) {
|
||||
var fn = console[method]
|
||||
console[method] = undefined
|
||||
sink(fallback, function (args) {
|
||||
t.is(args[0], 'test')
|
||||
t.end()
|
||||
console[method] = fn
|
||||
})
|
||||
var instance = require('../browser')({level: method})
|
||||
instance[method]('test')
|
||||
})
|
||||
}
|
||||
|
||||
function isFunc (fn) { return typeof fn === 'function' }
|
||||
function fnName (fn) {
|
||||
var rx = /^\s*function\s*([^\(]*)/i
|
||||
var match = rx.exec(fn)
|
||||
return match && match[1]
|
||||
}
|
||||
function sink (method, fn) {
|
||||
if (method === 'fatal') method = 'error'
|
||||
var orig = console[method]
|
||||
console[method] = function () {
|
||||
console[method] = orig
|
||||
fn(Array.prototype.slice.call(arguments))
|
||||
}
|
||||
}
|
||||
|
Загрузка…
Ссылка в новой задаче