Repository: pinojs/pino
Pull request #426 (2018-05-30 19:13:05)

Updates from master into next-major
As titled


Patch:
diff --git a/.travis.yml b/.travis.yml
index e73a649..57d39c0 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,7 +3,7 @@ sudo: false
 node_js:
   - 6
   - 8
-  - 9
+  - 10
 script:
   - npm run ci
 env:
diff --git a/.zuul.yml b/.zuul.yml
deleted file mode 100644
index b6c29ac..0000000
--- a/.zuul.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-name: pino
-ui: tape
-browserify:
-  - transform: fresh-require/transform
\ No newline at end of file
diff --git a/CNAME b/CNAME
new file mode 100644
index 0000000..6a32cdb
--- /dev/null
+++ b/CNAME
@@ -0,0 +1,1 @@
+getpino.io
\ No newline at end of file
diff --git a/browser.js b/browser.js
index 2581a03..a3f1cca 100644
--- a/browser.js
+++ b/browser.js
@@ -41,25 +41,25 @@ function pino (opts) {
   }
   if (opts.enabled === false) opts.level = 'silent'
   var level = opts.level || 'info'
-  var val = pino.levels.values[level]
-  if (level === 'silent') val = Infinity
   var logger = Object.create(proto)
   if (!logger.log) logger.log = noop
 
+  Object.defineProperty(logger, 'levelVal', {
+    get: getLevelVal
+  })
+  Object.defineProperty(logger, 'level', {
+    get: getLevel,
+    set: setLevel
+  })
+
   var setOpts = {
     transmit: transmit,
     serialize: serialize,
     asObject: opts.browser.asObject,
-    levels: levels,
-    level: level
+    levels: levels
   }
-
-  set(setOpts, logger, val, 'error', 'log') // <-- must stay first
-  set(setOpts, logger, val, 'fatal', 'error')
-  set(setOpts, logger, val, 'warn', 'error')
-  set(setOpts, logger, val, 'info', 'log')
-  set(setOpts, logger, val, 'debug', 'log')
-  set(setOpts, logger, val, 'trace', 'log')
+  logger.levels = pino.levels
+  logger.level = level
 
   logger.setMaxListeners = logger.getMaxListeners =
   logger.emit = logger.addListener = logger.on =
@@ -75,6 +75,29 @@ function pino (opts) {
 
   if (transmit) logger._logEvent = createLogEventShape()
 
+  function getLevelVal () {
+    return this.level === 'silent'
+      ? Infinity
+      : this.levels.values[this.level]
+  }
+
+  function getLevel () {
+    return this._level
+  }
+  function setLevel (level) {
+    if (level !== 'silent' && !this.levels.values[level]) {
+      throw Error('unknown level ' + level)
+    }
+    this._level = level
+
+    set(setOpts, logger, 'error', 'log') // <-- must stay first
+    set(setOpts, logger, 'fatal', 'error')
+    set(setOpts, logger, 'warn', 'error')
+    set(setOpts, logger, 'info', 'log')
+    set(setOpts, logger, 'debug', 'log')
+    set(setOpts, logger, 'trace', 'log')
+  }
+
   function child (bindings) {
     if (!bindings) {
       throw new Error('missing bindings for child Pino')
@@ -105,7 +128,6 @@ function pino (opts) {
     Child.prototype = this
     return new Child(this)
   }
-  logger.levels = pino.levels
   return logger
 }
 
@@ -132,14 +154,15 @@ pino.levels = {
 
 pino.stdSerializers = stdSerializers
 
-function set (opts, logger, val, level, fallback) {
-  logger[level] = val > pino.levels.values[level] ? noop
-    : (logger[level] ? logger[level] : (_console[level] || _console[fallback] || noop))
+function set (opts, logger, level, fallback) {
+  var proto = Object.getPrototypeOf(logger)
+  logger[level] = logger.levelVal > logger.levels.values[level] ? noop
+    : (proto[level] ? proto[level] : (_console[level] || _console[fallback] || noop))
 
-  wrap(opts, logger, val, level)
+  wrap(opts, logger, level)
 }
 
-function wrap (opts, logger, val, level) {
+function wrap (opts, logger, level) {
   if (!opts.transmit && logger[level] === noop) return
 
   logger[level] = (function (write) {
@@ -156,7 +179,7 @@ function wrap (opts, logger, val, level) {
       else write.apply(proto, args)
 
       if (opts.transmit) {
-        var transmitLevel = opts.transmit.level || opts.level
+        var transmitLevel = opts.transmit.level || logger.level
         var transmitValue = pino.levels.values[transmitLevel]
         var methodValue = pino.levels.values[level]
         if (methodValue < transmitValue) return
@@ -165,9 +188,9 @@ function wrap (opts, logger, val, level) {
           methodLevel: level,
           methodValue: methodValue,
           transmitLevel: transmitLevel,
-          transmitValue: pino.levels.values[opts.transmit.level || opts.level],
+          transmitValue: pino.levels.values[opts.transmit.level || logger.level],
           send: opts.transmit.send,
-          val: val
+          val: logger.levelVal
         }, args)
       }
     }
diff --git a/docs/API.md b/docs/API.md
index 3510e9b..7b83975 100644
--- a/docs/API.md
+++ b/docs/API.md
@@ -603,7 +603,7 @@ The default time function for Pino. Returns a string like `,"time":1493426328206
 Returns a unix time in seconds, like `,"time":1493426328`.
 
 <a id="nullTimeFunction"></a>
-### .nulltime
+### .nullTime
 
 Returns an empty string. This function is used when the `timestamp` option
 is set to `false`.
diff --git a/package.json b/package.json
index af35328..8654813 100644
--- a/package.json
+++ b/package.json
@@ -16,7 +16,7 @@
     "lib"
   ],
   "scripts": {
-    "browser-test": "zuul tape test/browser*test.js --local",
+    "browser-test": "airtap --local 8080 test/browser*test.js",
     "test": "standard | snazzy && NODE_OPTIONS='--no-warnings' tap -j 4 --no-cov test/*test.js",
     "ci": "standard | snazzy &&  NODE_OPTIONS='--no-warnings' tap -j 4 --cov test/*test.js",
     "bench-all": "node benchmarks/runbench all",
@@ -55,6 +55,7 @@
   },
   "homepage": "http://getpino.io",
   "devDependencies": {
+    "airtap": "0.0.7",
     "benchmark": "^2.1.4",
     "bole": "^3.0.2",
     "bunyan": "^1.8.12",
@@ -71,11 +72,10 @@
     "split2": "^2.2.0",
     "standard": "^11.0.1",
     "steed": "^1.1.3",
-    "tap": "^11.1.3",
+    "tap": "^11.1.5",
     "tape": "^4.9.0",
     "through2": "^2.0.3",
-    "winston": "^3.0.0-rc3",
-    "zuul": "^3.11.1"
+    "winston": "^3.0.0-rc5"
   },
   "dependencies": {
     "fast-json-parse": "^1.0.3",


Test patch:
diff --git a/test/browser.levels.test.js b/test/browser.levels.test.js
new file mode 100644
index 0000000..d63575d
--- /dev/null
+++ b/test/browser.levels.test.js
@@ -0,0 +1,217 @@
+'use strict'
+var test = require('tape')
+var pino = require('../browser')
+
+test('set the level by string', function (t) {
+  t.plan(4)
+  var expected = [
+    {
+      level: 50,
+      msg: 'this is an error'
+    },
+    {
+      level: 60,
+      msg: 'this is fatal'
+    }
+  ]
+  var instance = pino({
+    browser: {
+      write: function (actual) {
+        checkLogObjects(t, actual, expected.shift())
+      }
+    }
+  })
+
+  instance.level = 'error'
+  instance.info('hello world')
+  instance.error('this is an error')
+  instance.fatal('this is fatal')
+  t.end()
+})
+
+test('set the level by string. init with silent', function (t) {
+  t.plan(4)
+  var expected = [
+    {
+      level: 50,
+      msg: 'this is an error'
+    },
+    {
+      level: 60,
+      msg: 'this is fatal'
+    }
+  ]
+  var instance = pino({
+    level: 'silent',
+    browser: {
+      write: function (actual) {
+        checkLogObjects(t, actual, expected.shift())
+      }
+    }
+  })
+
+  instance.level = 'error'
+  instance.info('hello world')
+  instance.error('this is an error')
+  instance.fatal('this is fatal')
+  t.end()
+})
+
+test('set the level by string. init with silent and transmit', function (t) {
+  t.plan(4)
+  var expected = [
+    {
+      level: 50,
+      msg: 'this is an error'
+    },
+    {
+      level: 60,
+      msg: 'this is fatal'
+    }
+  ]
+  var instance = pino({
+    level: 'silent',
+    browser: {
+      write: function (actual) {
+        checkLogObjects(t, actual, expected.shift())
+      }
+    },
+    transmit: {
+      send: function () {
+      }
+    }
+  })
+
+  instance.level = 'error'
+  instance.info('hello world')
+  instance.error('this is an error')
+  instance.fatal('this is fatal')
+  t.end()
+})
+
+test('set the level via constructor', function (t) {
+  t.plan(4)
+  var expected = [
+    {
+      level: 50,
+      msg: 'this is an error'
+    },
+    {
+      level: 60,
+      msg: 'this is fatal'
+    }
+  ]
+  var instance = pino({
+    level: 'error',
+    browser: {
+      write: function (actual) {
+        checkLogObjects(t, actual, expected.shift())
+      }
+    }
+  })
+
+  instance.info('hello world')
+  instance.error('this is an error')
+  instance.fatal('this is fatal')
+  t.end()
+})
+
+test('the wrong level throws', function (t) {
+  t.plan(1)
+  var instance = pino()
+  t.throws(function () {
+    instance.level = 'kaboom'
+  })
+})
+
+test('the wrong level by number throws', function (t) {
+  t.plan(1)
+  var instance = pino()
+  t.throws(function () {
+    instance.levelVal = 55
+  })
+})
+
+test('exposes level string mappings', function (t) {
+  t.plan(1)
+  t.equal(pino.levels.values.error, 50)
+})
+
+test('exposes level number mappings', function (t) {
+  t.plan(1)
+  t.equal(pino.levels.labels[50], 'error')
+})
+
+test('returns level integer', function (t) {
+  t.plan(1)
+  var instance = pino({level: 'error'})
+  t.equal(instance.levelVal, 50)
+})
+
+test('silent level via constructor', function (t) {
+  var instance = pino({
+    level: 'silent',
+    browser: {
+      write: function (actual) {
+        t.fail('no data should be logged')
+      }
+    }
+  })
+
+  Object.keys(pino.levels.values).forEach(function (level) {
+    instance[level]('hello world')
+  })
+  t.end()
+})
+
+test('silent level by string', function (t) {
+  var instance = pino({
+    browser: {
+      write: function (actual) {
+        t.fail('no data should be logged')
+      }
+    }
+  })
+
+  instance.level = 'silent'
+
+  Object.keys(pino.levels.values).forEach(function (level) {
+    instance[level]('hello world')
+  })
+  t.end()
+})
+
+test('exposed levels', function (t) {
+  t.plan(1)
+  t.deepEqual(Object.keys(pino.levels.values), [
+    'fatal',
+    'error',
+    'warn',
+    'info',
+    'debug',
+    'trace'
+  ])
+})
+
+test('exposed labels', function (t) {
+  t.plan(1)
+  t.deepEqual(Object.keys(pino.levels.labels), [
+    '10',
+    '20',
+    '30',
+    '40',
+    '50',
+    '60'
+  ])
+})
+
+function checkLogObjects (t, actual, expected) {
+  t.ok(actual.time <= Date.now(), 'time is greater than Date.now()')
+
+  var actualCopy = Object.assign({}, actual)
+  var expectedCopy = Object.assign({}, expected)
+  delete actualCopy.time
+  delete expectedCopy.time
+
+  t.deepEqual(actualCopy, expectedCopy)
+}
diff --git a/test/browser.serializers.test.js b/test/browser.serializers.test.js
index 1012faa..ddba55f 100644
--- a/test/browser.serializers.test.js
+++ b/test/browser.serializers.test.js
@@ -38,173 +38,175 @@ test('without the serialize option, serializers do not override values', functio
   parent.fatal({test: 'test'})
 })
 
-test('if serialize option is true, standard error serializer is auto enabled', function (t) {
-  t.plan(1)
-  var err = Error('test')
-  err.code = 'test'
-  err.type = 'Error' // get that cov
-  var expect = pino.stdSerializers.err(err)
-
-  var consoleError = console.error
-  console.error = function (err) {
-    t.deepEqual(err, expect)
-  }
-
-  var logger = fresh('../browser', require)({
-    browser: { serialize: true }
+if (process.title !== 'browser') {
+  test('if serialize option is true, standard error serializer is auto enabled', function (t) {
+    t.plan(1)
+    var err = Error('test')
+    err.code = 'test'
+    err.type = 'Error' // get that cov
+    var expect = pino.stdSerializers.err(err)
+
+    var consoleError = console.error
+    console.error = function (err) {
+      t.deepEqual(err, expect)
+    }
+
+    var logger = fresh('../browser', require)({
+      browser: { serialize: true }
+    })
+
+    console.error = consoleError
+
+    logger.fatal(err)
   })
 
-  console.error = consoleError
+  test('if serialize option is array, standard error serializer is auto enabled', function (t) {
+    t.plan(1)
+    var err = Error('test')
+    err.code = 'test'
+    var expect = pino.stdSerializers.err(err)
 
-  logger.fatal(err)
-})
+    var consoleError = console.error
+    console.error = function (err) {
+      t.deepEqual(err, expect)
+    }
 
-test('if serialize option is array, standard error serializer is auto enabled', function (t) {
-  t.plan(1)
-  var err = Error('test')
-  err.code = 'test'
-  var expect = pino.stdSerializers.err(err)
+    var logger = fresh('../browser', require)({
+      browser: { serialize: [] }
+    })
 
-  var consoleError = console.error
-  console.error = function (err) {
-    t.deepEqual(err, expect)
-  }
+    console.error = consoleError
 
-  var logger = fresh('../browser', require)({
-    browser: { serialize: [] }
+    logger.fatal(err)
   })
 
-  console.error = consoleError
+  test('if serialize option is array containing !stdSerializers.err, standard error serializer is disabled', function (t) {
+    t.plan(1)
+    var err = Error('test')
+    err.code = 'test'
+    var expect = err
 
-  logger.fatal(err)
-})
+    var consoleError = console.error
+    console.error = function (err) {
+      t.is(err, expect)
+    }
 
-test('if serialize option is array containing !stdSerializers.err, standard error serializer is disabled', function (t) {
-  t.plan(1)
-  var err = Error('test')
-  err.code = 'test'
-  var expect = err
+    var logger = fresh('../browser', require)({
+      browser: { serialize: ['!stdSerializers.err'] }
+    })
 
-  var consoleError = console.error
-  console.error = function (err) {
-    t.is(err, expect)
-  }
+    console.error = consoleError
 
-  var logger = fresh('../browser', require)({
-    browser: { serialize: ['!stdSerializers.err'] }
+    logger.fatal(err)
   })
 
-  console.error = consoleError
-
-  logger.fatal(err)
-})
-
-test('in browser, serializers apply to all objects', function (t) {
-  t.plan(3)
-  var consoleError = console.error
-  console.error = function (test, test2, test3, test4, test5) {
-    t.is(test.key, 'serialized')
-    t.is(test2.key2, 'serialized2')
-    t.is(test5.key3, 'serialized3')
-  }
-
-  var logger = fresh('../browser', require)({
-    serializers: {
-      key: function () { return 'serialized' },
-      key2: function () { return 'serialized2' },
-      key3: function () { return 'serialized3' }
-    },
-    browser: { serialize: true }
+  test('in browser, serializers apply to all objects', function (t) {
+    t.plan(3)
+    var consoleError = console.error
+    console.error = function (test, test2, test3, test4, test5) {
+      t.is(test.key, 'serialized')
+      t.is(test2.key2, 'serialized2')
+      t.is(test5.key3, 'serialized3')
+    }
+
+    var logger = fresh('../browser', require)({
+      serializers: {
+        key: function () { return 'serialized' },
+        key2: function () { return 'serialized2' },
+        key3: function () { return 'serialized3' }
+      },
+      browser: { serialize: true }
+    })
+
+    console.error = consoleError
+
+    logger.fatal({key: 'test'}, {key2: 'test'}, 'str should skip', [{foo: 'array should skip'}], {key3: 'test'})
   })
 
-  console.error = consoleError
-
-  logger.fatal({key: 'test'}, {key2: 'test'}, 'str should skip', [{foo: 'array should skip'}], {key3: 'test'})
-})
-
-test('serialize can be an array of selected serializers', function (t) {
-  t.plan(3)
-  var consoleError = console.error
-  console.error = function (test, test2, test3, test4, test5) {
-    t.is(test.key, 'test')
-    t.is(test2.key2, 'serialized2')
-    t.is(test5.key3, 'test')
-  }
-
-  var logger = fresh('../browser', require)({
-    serializers: {
-      key: function () { return 'serialized' },
-      key2: function () { return 'serialized2' },
-      key3: function () { return 'serialized3' }
-    },
-    browser: { serialize: ['key2'] }
+  test('serialize can be an array of selected serializers', function (t) {
+    t.plan(3)
+    var consoleError = console.error
+    console.error = function (test, test2, test3, test4, test5) {
+      t.is(test.key, 'test')
+      t.is(test2.key2, 'serialized2')
+      t.is(test5.key3, 'test')
+    }
+
+    var logger = fresh('../browser', require)({
+      serializers: {
+        key: function () { return 'serialized' },
+        key2: function () { return 'serialized2' },
+        key3: function () { return 'serialized3' }
+      },
+      browser: { serialize: ['key2'] }
+    })
+
+    console.error = consoleError
+
+    logger.fatal({key: 'test'}, {key2: 'test'}, 'str should skip', [{foo: 'array should skip'}], {key3: 'test'})
   })
 
-  console.error = consoleError
-
-  logger.fatal({key: 'test'}, {key2: 'test'}, 'str should skip', [{foo: 'array should skip'}], {key3: 'test'})
-})
-
-test('serialize filter applies to child loggers', function (t) {
-  t.plan(3)
-  var consoleError = console.error
-  console.error = function (binding, test, test2, test3, test4, test5) {
-    t.is(test.key, 'test')
-    t.is(test2.key2, 'serialized2')
-    t.is(test5.key3, 'test')
-  }
-
-  var logger = fresh('../browser', require)({
-    browser: { serialize: ['key2'] }
+  test('serialize filter applies to child loggers', function (t) {
+    t.plan(3)
+    var consoleError = console.error
+    console.error = function (binding, test, test2, test3, test4, test5) {
+      t.is(test.key, 'test')
+      t.is(test2.key2, 'serialized2')
+      t.is(test5.key3, 'test')
+    }
+
+    var logger = fresh('../browser', require)({
+      browser: { serialize: ['key2'] }
+    })
+
+    console.error = consoleError
+
+    logger.child({aBinding: 'test',
+      serializers: {
+        key: function () { return 'serialized' },
+        key2: function () { return 'serialized2' },
+        key3: function () { return 'serialized3' }
+      }}).fatal({key: 'test'}, {key2: 'test'}, 'str should skip', [{foo: 'array should skip'}], {key3: 'test'})
   })
 
-  console.error = consoleError
+  test('parent serializers apply to child bindings', function (t) {
+    t.plan(1)
+    var consoleError = console.error
+    console.error = function (binding) {
+      t.is(binding.key, 'serialized')
+    }
 
-  logger.child({aBinding: 'test',
-    serializers: {
-      key: function () { return 'serialized' },
-      key2: function () { return 'serialized2' },
-      key3: function () { return 'serialized3' }
-    }}).fatal({key: 'test'}, {key2: 'test'}, 'str should skip', [{foo: 'array should skip'}], {key3: 'test'})
-})
+    var logger = fresh('../browser', require)({
+      serializers: {
+        key: function () { return 'serialized' }
+      },
+      browser: { serialize: true }
+    })
 
-test('parent serializers apply to child bindings', function (t) {
-  t.plan(1)
-  var consoleError = console.error
-  console.error = function (binding) {
-    t.is(binding.key, 'serialized')
-  }
+    console.error = consoleError
 
-  var logger = fresh('../browser', require)({
-    serializers: {
-      key: function () { return 'serialized' }
-    },
-    browser: { serialize: true }
+    logger.child({key: 'test'}).fatal({test: 'test'})
   })
 
-  console.error = consoleError
+  test('child serializers apply to child bindings', function (t) {
+    t.plan(1)
+    var consoleError = console.error
+    console.error = function (binding) {
+      t.is(binding.key, 'serialized')
+    }
 
-  logger.child({key: 'test'}).fatal({test: 'test'})
-})
+    var logger = fresh('../browser', require)({
+      browser: { serialize: true }
+    })
 
-test('child serializers apply to child bindings', function (t) {
-  t.plan(1)
-  var consoleError = console.error
-  console.error = function (binding) {
-    t.is(binding.key, 'serialized')
-  }
+    console.error = consoleError
 
-  var logger = fresh('../browser', require)({
-    browser: { serialize: true }
+    logger.child({key: 'test',
+      serializers: {
+        key: function () { return 'serialized' }
+      }}).fatal({test: 'test'})
   })
-
-  console.error = consoleError
-
-  logger.child({key: 'test',
-    serializers: {
-      key: function () { return 'serialized' }
-    }}).fatal({test: 'test'})
-})
+}
 
 test('child does not overwrite parent serializers', function (t) {
   t.plan(2)
diff --git a/test/browser.test.js b/test/browser.test.js
index bb62740..81ac2e6 100644
--- a/test/browser.test.js
+++ b/test/browser.test.js
@@ -136,20 +136,23 @@ 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()
-})
+// do not run this with airtap
+if (process.title !== 'browser') {
+  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.ok(fnName(instance.log).match(/noop/))
+    t.ok(fnName(instance.fatal).match(/noop/))
+    t.ok(fnName(instance.error).match(/noop/))
+    t.ok(fnName(instance.warn).match(/noop/))
+    t.ok(fnName(instance.info).match(/noop/))
+    t.ok(fnName(instance.debug).match(/noop/))
+    t.ok(fnName(instance.trace).match(/noop/))
+    t.end()
+  })
+}
 
 test('opts.browser.asObject logs pino-like object to console', function (t) {
   t.plan(3)