Full Code of trentm/node-bunyan for AI

master 5c2258ecb1d3 cached
81 files
402.5 KB
113.8k tokens
64 symbols
1 requests
Download .txt
Showing preview only (425K chars total). Download the full file or copy to clipboard to get everything.
Repository: trentm/node-bunyan
Branch: master
Commit: 5c2258ecb1d3
Files: 81
Total size: 402.5 KB

Directory structure:
gitextract_zuzdlxm5/

├── .github/
│   └── workflows/
│       ├── check.yml
│       └── test.yml
├── .gitignore
├── .npmignore
├── AUTHORS
├── CHANGES.md
├── CONTRIBUTING.md
├── LICENSE.txt
├── Makefile
├── README.md
├── TODO.md
├── bin/
│   └── bunyan
├── docs/
│   ├── bunyan.1
│   ├── bunyan.1.html
│   ├── bunyan.1.ronn
│   └── index.html
├── examples/
│   ├── err.js
│   ├── handle-fs-error.js
│   ├── hi.js
│   ├── level.js
│   ├── log-undefined-values.js
│   ├── long-running.js
│   ├── multi.js
│   ├── mute-by-envvars-stream.js
│   ├── raw-stream.js
│   ├── ringbuffer.js
│   ├── rot-specific-levels.js
│   ├── server.js
│   ├── specific-level-streams.js
│   ├── src.js
│   └── unstringifyable.js
├── lib/
│   └── bunyan.js
├── package.json
├── test/
│   ├── add-stream.test.js
│   ├── buffer.test.js
│   ├── child-behaviour.test.js
│   ├── cli-client-req.test.js
│   ├── cli-res.test.js
│   ├── cli.test.js
│   ├── corpus/
│   │   ├── all.log
│   │   ├── bogus.log
│   │   ├── client-req-with-address.log
│   │   ├── clientreqres.log
│   │   ├── content-length-0-res.log
│   │   ├── extrafield.log
│   │   ├── log1.log
│   │   ├── log2.log
│   │   ├── non-object-res.log
│   │   ├── old-crashers/
│   │   │   └── README.md
│   │   ├── res-header.log
│   │   ├── res-without-header.log
│   │   ├── simple.log
│   │   └── withreq.log
│   ├── ctor.test.js
│   ├── cycles.test.js
│   ├── dtrace/
│   │   └── dtrace.test.js
│   ├── error-event.test.js
│   ├── level.test.js
│   ├── log-some-loop.js
│   ├── log-some.js
│   ├── log.test.js
│   ├── other-api.test.js
│   ├── process-exit.js
│   ├── process-exit.test.js
│   ├── raw-stream.test.js
│   ├── ringbuffer.test.js
│   ├── safe-json-stringify-1.js
│   ├── safe-json-stringify-2.js
│   ├── safe-json-stringify-3.js
│   ├── safe-json-stringify-4.js
│   ├── safe-json-stringify.test.js
│   ├── serializers.test.js
│   ├── src.test.js
│   └── stream-levels.test.js
└── tools/
    ├── cutarelease.py
    ├── jsstyle
    ├── statsd-notes.txt
    ├── timechild.js
    ├── timeguard.js
    ├── timenop.js
    └── timesrc.js

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/workflows/check.yml
================================================
name: Check Lint & Style
on: [push, pull_request]
jobs:
  check:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - uses: actions/setup-node@v1
      with:
        node-version: '12.x'
    - run: npm ci
    - run: npm run check


================================================
FILE: .github/workflows/test.yml
================================================
name: Test

on:
  push:
    paths-ignore:
    - 'docs/**'
    - '*.md'
  pull_request:
    paths-ignore:
    - 'docs/**'
    - '*.md'

jobs:
  # Test once on every (available) plat, using LTS node version
  # (https://nodejs.org/en/about/releases/).
  test-plats:
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
    runs-on: ${{ matrix.os }}
    steps:
    - uses: actions/checkout@v2
    - uses: actions/setup-node@v1
      with:
        node-version: '12.x'
    - run: npm ci
    - run: npm test

  # Test once for every supported node version (don't repeat the LTS
  # node version from the previous step). Only test on one
  # platform to not overkill the number of builds.
  test-vers:
    strategy:
      matrix:
        node: ['8.x', '10.x', '14.x']
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - uses: actions/setup-node@v1
      with:
        node-version: ${{ matrix.node }}
    - run: npm ci
    - run: npm test

  # Test older versions separately because really old node/npm don't support
  # 'npm ci'.
  test-old-vers:
    strategy:
      matrix:
        node: ['0.10.x', '4.x', '6.x']
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - uses: actions/setup-node@v1
      with:
        node-version: ${{ matrix.node }}
    - run: npm install
    - run: npm test


================================================
FILE: .gitignore
================================================
/tmp
/node_modules
*.log
!/test/corpus/*.log
/*.tgz
/test/log.test.rot.log.*


================================================
FILE: .npmignore
================================================
/tmp
/node_modules
*.log
/examples
/test
/*.tgz
/tools


================================================
FILE: AUTHORS
================================================
Trent Mick (http://trentm.com)
Mark Cavage (https://github.com/mcavage)
Dave Pacheco (https://github.com/davepacheco)
Michael Hart (https://github.com/mhart)
Isaac Schlueter (https://github.com/isaacs)
Rob Gulewich (https://github.com/rgulewich)
Bryan Cantrill (https://github.com/bcantrill)
Michael Hart (https://github.com/mhart)
Simon Wade (https://github.com/aexmachina)
https://github.com/glenn-murray-bse
Chakrit Wichian (https://github.com/chakrit)
Patrick Mooney (https://github.com/pfmooney)
Johan Nordberg (https://github.com/jnordberg)
https://github.com/timborodin
Ryan Graham (https://github.com/rmg)
Alex Kocharin (https://github.com/rlidwka)
Andrei Neculau (https://github.com/andreineculau)
Mihai Tomescu (https://github.com/matomesc)
Daniel Juhl (https://github.com/danieljuhl)
Chris Barber (https://github.com/cb1kenobi)
Manuel Schneider (https://github.com/manuelschneider)
Martin Gausby (https://github.com/gausby)
Stéphan Kochen (https://github.com/stephank)
Shakeel Mohamed (https://github.com/shakeelmohamed)
Denis Izmaylov (https://github.com/DenisIzmaylov)
Guillermo Grau Panea (https://github.com/guigrpa)
Mark LeMerise (https://github.com/MarkLeMerise)
https://github.com/sometimesalready
Charly Koza (https://github.com/Cactusbone)
Thomas Heymann (https://github.com/cyberthom)
David M. Lee (https://github.com/leedm777)
Marc Udoff (https://github.com/mlucool)
Mark Stosberg (https://github.com/markstos)
Alexander Ray (https://github.com/aray12)
Adam Lynch (https://github.com/adam-lynch)
Michael Nisi (https://github.com/michaelnisi)
Martijn Schrage (https://github.com/Oblosys)
Paul Milham (https://github.com/domrein)
Frankie O'Rourke (https://github.com/psfrankie)
Cody Mello (https://github.com/melloc)
Todd Whiteman (https://github.com/twhiteman)
Zach Bjornson (https://github.com/zbjornson)


================================================
FILE: CHANGES.md
================================================
# bunyan Changelog

See [the bunyan@1.x changelog](https://github.com/trentm/node-bunyan/blob/1.x/CHANGES.md)
for details on recent 1.x releases.

Known issues:

- [issue #58] Can't install to a dir with spaces. This is [this node-gyp
  bug](https://github.com/TooTallNate/node-gyp/issues/65).


## not yet released

(nothing yet)

## 2.0.5 (beta)

- [pull #575, #278] Change the default "req" serializer to accept expressjs's
  `req.originalUrl` for the "url" field per
  <https://expressjs.com/en/api.html#req.originalUrl>. (By @twelve17 and
  @kingcody.)
- Development change: Switch to node-tap for testing (from nodeunit, which is
  now obsolete). Currently just tap v9 because that is the last major version
  of node-tap that supports back to node v0.10.

## 2.0.4 (beta)

- [pull #558] Update minimum "moment" version to 2.19.3 for CVE-2017-18214.
- [issue #589] Use `os.EOL` for newlines in bunyan output, which helps with
  some Unix-EOL-naive apps like notepad. (By @bwknight877.)
- Development change: Switched to GitHub Actions for CI.

## 2.0.3 (beta)

- Fix a vulnerability from a crafted argument to 'bunyan -p ARG'

  This was reported privately as:
      https://hackerone.com/reports/902739
      bunyan - RCE via insecure command formatting

  Previous to this version the 'bunyan' CLI was not escaping a given argument
  to the '-p' option before executing `ps -A -o pid,command | grep '$ARG'`
  which could lead to unintended execution.

  (This same change is also in bunyan@1.8.13.)

## 2.0.2 (beta)

- [issue #444] Fix the `bunyan` CLI to not duplicate the "HTTP/1.1 ..." status
  line when serializing a "res" field.


## 2.0.1 (beta)

- [issue #504] **Backward incompatible change to the `bunyan` CLI:**
  The `bunyan` CLI no longer adds a `Host` header when rendering a `client_req`
  field in a log record. In 1.x it used to do this (using `client_req.address`
  and `client_req.port`), on the *guess* that Node.js' `http.ClientRequest`
  handling would add it.  However, the guess can be wrong and misleading. It is
  better not to guess.


## 2.0.0 (beta)

- [issue #499] Fix some `bunyan` CLI exit handling problems.


## 1.8.10

- Ensure that `bunyan` errors out if attempting to use `-p PID` and
  file args at the same time.


## 1.8.9

- [pull #409, issue #246] Revert a change added to the `bunyan` CLI version
  1.0.1 where `SIGINT` was ignored, such that Ctrl+C could not be used to
  terminate bunyan. (By @zbjornson and @davepacheco.)
- [pull #469] Fix a strict mode (`"use strict;"`) error in some versions of
  Safari.


## 1.8.8

- Fix breakage due to a silly last minute "fix 'make check'".


## 1.8.7

Note: *Bad release.* Use 1.8.8 or later.

- [issue #484] Fix breakage due to #474 in previous release.


## 1.8.6

Note: *Bad release.* Use 1.8.7 or later.

- [issue #474] Bunyan's `safeCycles` is too slow when logging large objects.


## 1.8.5

- [issue #401] Improved performance when using disabled log levels.


## 1.8.4

- [issue #454] Fix `src` usage with node v7.


## 1.8.3

- [issue #450] Fix `log.info(null)` crash that resulted from #426 in v1.8.2.


## 1.8.2

- [issue #449] Bump dtrace-provider dep to 0.7.0 to help avoid deprecation
  warnings with node v6 in some cases.
- [issue #426] Ensure `log.info({err: err})` results in a "msg" value, just
  like `log.info(err)`.


## 1.8.1

- [pull #386] Fix bad bug in rotation that could cause a crash with
  error message "cannot start a rotation when already rotating"
  (by Frankie O'Rourke). The bug was introduced in 1.8.0.


## 1.8.0

Note: *Bad release.* An addition in this release broke 'rotating-file' usage.
Use 1.8.1 or later.

- [issue #370] Fix `bunyan -p ...` (i.e. DTrace integration) on node
  4.x and 5.x.
- [issue #329, pull #330] Update the 'rotating-file' stream to do a file
  rotation on initialization if the mtime on the file path indicates the
  last rotation time was missed -- i.e. if the app wasn't running at the
  time. (by Paul Milham.)


## 1.7.1

- [issue #332, pull #355] Ensure stream for type='stream' stream is a writable
  stream. (By Michael Nisi.)

- [issue #344] Fix "rotating-file" Bunyan streams to not miss rotations when configured
  for a period greater than approximately 25 days. Before this there was an issue
  where periods greater than node.js's maximum `setTimeout` length would fail to rotate.
  (By Martijn Schrage.)

- [issue #234, pull #345] Improve `bunyan` CLI rendering of "res" field
  HTTP responses to not show two blank lines for an empty body.
  (By Michael Nisi.)


## 1.7.0

- [pull #311, #302, #310] Improve the runtime environment detection to fix
  running under [NW.js](http://nwjs.io/). Contributions by Adam Lynch, Jeremy
  Ruppel, and Aleksey Timchenko.

- [pull #318] Add `reemitErrorEvents` optional boolean for streams added to a
  Bunyan logger to control whether an "error" event on the stream will be
  re-emitted on the `Logger` instance.

        var log = bunyan.createLogger({
            name: 'foo',
            streams: [
                {
                    type: 'raw',
                    stream: new MyCustomStream(),
                    reemitErrorEvents: true
                }
            ]
        });

  Before this change, "error" events were re-emitted on [`file`
  streams](https://github.com/trentm/node-bunyan#stream-type-file) only. The new
  behaviour is as follows:

    - `reemitErrorEvents` not specified: `file` streams will re-emit error events
      on the Logger instance.
    - `reemitErrorEvents: true`: error events will be re-emitted on the Logger
      for any stream with a `.on()` function -- which includes file streams,
      process.stdout/stderr, and any object that inherits from EventEmitter.
    - `reemitErrorEvents: false`: error events will not be re-emitted for any
      streams.

  Dev Note: Bunyan `Logger` objects don't currently have a `.close()` method
  in which registered error event handlers can be *un*registered. That means
  that a (presumably rare) situation where code adds dozens of Bunyan Logger
  streams to, e.g. process.stdout, and with `reemitErrorEvents: true`, could
  result in leaking Logger objects.

  Original work for allowing "error" re-emitting on non-file streams is
  by Marc Udoff in pull #318.


## 1.6.0

- [pull #304, issue #245] Use [Moment.js][momentjs.com] library to handle
  `bunyan` CLI time formatting in some cases, especially to fix display of
  local time. It is now required for local time formatting (i.e. `bunyan -L`
  or `bunyan --time local`). (By David M. Lee.)

- [pull #252] Fix errant `client_res={}` in `bunyan` CLI rendering, and avoid
  extra newlines in `client_req` rendering in some cases. (By Thomas Heymann.)

- [pull #291, issue #303] Fix `LOG.child(...)` to *not* override the "hostname"
  field of the parent. A use case is when one manually sets "hostname" to
  something other than `os.hostname()`. (By github.com/Cactusbone.)

- [issue #325] Allow one to set `level: 0` in `createLogger` to turn on
  logging for all levels. (Adapted from #336 by github.com/sometimesalready.)

- Add guards (to `resolveLevel`) so that all "level" values are validated.
  Before this, a bogus level like "foo" or -12 or `['some', 'array']` would
  silently be accepted -- with undefined results.

- Doc updates for #340 and #305.

- Update `make test` to test against node 5, 4, 0.12 and 0.10.


## 1.5.1

- [issue #296] Fix `src: true`, which was broken in v1.5.0.


## 1.5.0

Note: *Bad release.* The addition of `'use strict';` broke Bunyan's `src: true`
feature. Use 1.5.1 instead.

- [pull #236, issue #231, issue #223] Fix strict mode in the browser.
- [pull #282, issue #213] Fixes bunyan to work with webpack. By Denis Izmaylov.
- [pull #294] Update to dtrace-provider 0.6 to fix with node 4.0 and io.js 3.0.
- Dropped support for 0.8 (can't install deps easily anymore for running
  test suite). Bump to a recent iojs version for testing.


## 1.4.0

(Bumping minor ver b/c I'm wary of dtrace-provider changes. :)

- [issue #258, pull #259] Update to dtrace-provider 0.5 to fix
  install and tests on recent io.js versions.
- safe-json-stringify@1.0.3 changed output, breaking some tests. Fix those.


## 1.3.6

- [issue #244] Make `bunyan` defensive on `res.header=null`.


## 1.3.5

- [issue #233] Make `bunyan` defensive on res.header as a boolean.
- [issue #242] Make `bunyan` defensive on err.stack not being a string.


## 1.3.4

- Allow `log.child(...)` to work even if the logger is a *sub-class*
  of Bunyan's Logger class.
- [issue #219] Hide 'source-map-support' require from browserify.
- [issue #218] Reset `haveNonRawStreams` on `<logger>.addStream`.


## 1.3.3

- [pull #127] Update to dtrace-provider 0.4.0, which gives io.js 1.x support
  for dtrace-y parts of Bunyan.


## 1.3.2

- [pull #182] Fallback to using the optional 'safe-json-stringify' module
  if `JSON.stringify` throws -- possibly with an enumerable property
  getter than throws. By Martin Gausby.


## 1.3.1

- Export `bunyan.RotatingFileStream` which is needed if one wants to
  customize it. E.g. see issue #194.

- [pull #122] Source Map support for caller line position for [the "src"
  field](https://github.com/trentm/node-bunyan#src). This could be interesting
  for [CoffeeScript](http://coffeescript.org/documentation/docs/sourcemap.html)
  users of Bunyan. By Manuel Schneider.

- [issue #164] Ensure a top-level `level` given in `bunyan.createLogger`
  is *used* for given `streams`. For example, ensure that the following
  results in the stream having a DEBUG level:

        var log = bunyan.createLogger({
            name: 'foo',
            level: 'debug',
            streams: [
                {
                    path: '/var/tmp/foo.log'
                }
            ]
        });

  This was broken in the 1.0.1 release. Between that release and 1.3.0
  the "/var/tmp/foo.log" stream would be at the INFO level (Bunyan's
  default level).


## 1.3.0

- [issue #103] `bunyan -L` (or `bunyan --time local`) to show local time.
  Bunyan log records store `time` in UTC time. Sometimes it is convenient
  to display in local time.

- [issue #205] Fix the "The Bunyan CLI crashed!" checking to properly warn of
  the common failure case when `-c CONDITION` is being used.


## 1.2.4

- [issue #210] Export `bunyan.nameFromLevel` and `bunyan.levelFromName`. It can
  be a pain for custom streams to have to reproduce that.

- [issue #100] Gracefully handle the case of an unbound
  `Logger.{info,debug,...}` being used for logging, e.g.:

        myEmittingThing.on('data', log.info)

  Before this change, bunyan would throw. Now it emits a warning to stderr
  *once*, and then silently ignores those log attempts, e.g.:

        bunyan usage error: /Users/trentm/tm/node-bunyan/foo.js:12: attempt to log with an unbound log method: `this` is: { _events: { data: [Function] } }


## 1.2.3

- [issue #184] Fix log rotation for rotation periods > ~25 days. Before this
  change, a rotation period longer than this could hit [the maximum setTimeout
  delay in node.js](https://github.com/joyent/node/issues/8656). By Daniel Juhl.


## 1.2.2

- Drop the guard that a bunyan Logger level must be between TRACE (10)
  and FATAL (60), inclusive. This allows a trick of setting the level
  to `FATAL + 1` to turn logging off. While the standard named log levels are
  the golden path, then intention was not to get in the way of using
  other level numbers.


## 1.2.1

- [issue #178, #181] Get at least dtrace-provider 0.3.1 for
  optionalDependencies to get a fix for install with decoupled npm (e.g. with
  homebrew's node and npm).


## 1.2.0

- [issue #157] Restore dtrace-provider as a dependency (in
  "optionalDependencies").

  Dtrace-provider version 0.3.0 add build sugar that should eliminate the
  problems from older versions:
  The build is not attempted on Linux and Windows. The build spew is
  *not* emitted by default (use `V=1 npm install` to see it); instead a
  short warning is emitted if the build fails.

  Also, importantly, the new dtrace-provider fixes working with node
  v0.11/0.12.


## 1.1.3

- [issue #165] Include extra `err` fields in `bunyan` CLI output. Before
  this change only the fields part of the typical node.js error stack
  (err.stack, err.message, err.name) would be emitted, even though
  the Bunyan *library* would typically include err.code and err.signal
  in the raw JSON log record.


## 1.1.2

- Fix a breakage in `log.info(err)` on a logger with no serializers.


## 1.1.1

Note: *Bad release.* It breaks `log.info(err)` on a logger with no serializers.
Use version 1.1.2.

- [pull #168] Fix handling of `log.info(err)` to use the `log` Logger's `err`
  serializer if it has one, instead of always using the core Bunyan err
  serializer. (By Mihai Tomescu.)


## 1.1.0

- [issue #162] Preliminary support for [browserify](http://browserify.org/).
  See [the section in the README](../README.md#browserify).


## 1.0.1

- [issues #105, #138, #151] Export `<Logger>.addStream(...)` and
  `<Logger>.addSerializers(...)` to be able to add them after Logger creation.
  Thanks @andreineculau!

- [issue #159] Fix bad handling in construtor guard intending to allow
  creation without "new": `var log = Logger(...)`. Thanks @rmg!

- [issue #156] Smaller install size via .npmignore file.

- [issue #126, #161] Ignore SIGINT (Ctrl+C) when processing stdin. `...| bunyan`
  should expect the preceding process in the pipeline to handle SIGINT. While
  it is doing so, `bunyan` should continue to process any remaining output.
  Thanks @timborodin and @jnordberg!

- [issue #160] Stop using ANSI 'grey' in `bunyan` CLI output, because of the
  problems that causes with Solarized Dark themes (see
  <https://github.com/altercation/solarized/issues/220>).


## 1.0.0

- [issue #87] **Backward incompatible change to `-c CODE`** improving
  performance by over 10x (good!), with a backward incompatible change to
  semantics (unfortunate), and adding some sugar (good!).

  The `-c CODE` implementation was changed to use a JS function for processing
  rather than `vm.runInNewContext`. The latter was specatularly slow, so
  won't be missed. Unfortunately this does mean a few semantic differences in
  the `CODE`, the most noticeable of which is that **`this` is required to
  access the object fields:**

        # Bad. Works with bunyan 0.x but not 1.x.
        $ bunyan -c 'pid === 123' foo.log
        ...

        # Good. Works with all versions of bunyan
        $ bunyan -c 'this.pid === 123' foo.log
        ...

  The old behaviour of `-c` can be restored with the `BUNYAN_EXEC=vm`
  environment variable:

        $ BUNYAN_EXEC=vm bunyan -c 'pid === 123' foo.log
        ...

  Some sugar was also added: the TRACE, DEBUG, ... constants are defined, so
  one can:

        $ bunyan -c 'this.level >= ERROR && this.component === "http"' foo.log
        ...

  And example of the speed improvement on a 10 MiB log example:

        $ time BUNYAN_EXEC=vm bunyan -c 'this.level === ERROR' big.log | cat >slow

        real    0m6.349s
        user    0m6.292s
        sys    0m0.110s

        $ time bunyan -c 'this.level === ERROR' big.log | cat >fast

        real    0m0.333s
        user    0m0.303s
        sys    0m0.028s

  The change was courtesy Patrick Mooney (https://github.com/pfmooney). Thanks!

- Add `bunyan -0 ...` shortcut for `bunyan -o bunyan ...`.

- [issue #135] **Backward incompatible.** Drop dtrace-provider even from
  `optionalDependencies`. Dtrace-provider has proven a consistent barrier to
  installing bunyan, because it is a binary dep. Even as an *optional* dep it
  still caused confusion and install noise.

  Users of Bunyan on dtrace-y platforms (SmartOS, Mac, Illumos, Solaris) will
  need to manually `npm install dtrace-provider` themselves to get [Bunyan's
  dtrace support](https://github.com/trentm/node-bunyan#runtime-log-snooping-via-dtrace)
  to work. If not installed, bunyan should stub it out properly.



## 0.23.1

- [pull #125, pull #97, issue #73] Unref rotating-file timeout which was
  preventing processes from exiting (by https://github.com/chakrit and
  https://github.com/glenn-murray-bse). Note: this only fixes the issue
  for node 0.10 and above.


## 0.23.0

- [issue #139] Fix `bunyan` crash on a log record with `res.header` that is an
  object. A side effect of this improvement is that a record with `res.statusCode`
  but no header info will render a response block, for example:

        [2012-08-08T10:25:47.637Z]  INFO: my-service/12859 on my-host: some message (...)
            ...
            --
            HTTP/1.1 200 OK
            --
            ...

- [pull #42] Fix `bunyan` crash on a log record with `req.headers` that is a *string*
  (by https://github.com/aexmachina).

- Drop node 0.6 support. I can't effectively `npm install` with a node 0.6
  anymore.

- [issue #85] Ensure logging a non-object/non-string doesn't throw (by
  https://github.com/mhart). This changes fixes:

        log.info(<bool>)     # TypeError: Object.keys called on non-object
        log.info(<function>) # "msg":"" (instead of wanted "msg":"[Function]")
        log.info(<array>)    # "msg":"" (instead of wanted "msg":util.format(<array>))


## 0.22.3

- Republish the same code to npm.


## 0.22.2

Note: Bad release. The published package in the npm registry got corrupted. Use 0.22.3 or later.

- [issue #131] Allow `log.info(<number>)` and, most importantly, don't crash on that.

- Update 'mv' optional dep to latest.


## 0.22.1

- [issue #111] Fix a crash when attempting to use `bunyan -p` on a platform without
  dtrace.

- [issue #101] Fix a crash in `bunyan` rendering a record with unexpected "res.headers".


## 0.22.0

- [issue #104] `log.reopenFileStreams()` convenience method to be used with external log
  rotation.


## 0.21.4

- [issue #96] Fix `bunyan` to default to paging (with `less`) by default in node 0.10.0.
  The intention has always been to default to paging for node >=0.8.


## 0.21.3

- [issue #90] Fix `bunyan -p '*'` breakage in version 0.21.2.


## 0.21.2

**Note: Bad release. The switchrate change below broke `bunyan -p '*'` usage
(see issue #90). Use 0.21.3 or later.**

- [issue #88] Should be able to efficiently combine "-l" with "-p *".

- Avoid DTrace buffer filling up, e.g. like this:

        $ bunyan -p 42241 > /tmp/all.log
        dtrace: error on enabled probe ID 3 (ID 75795: bunyan42241:mod-87ea640:log-trace:log-trace): out of scratch space in action #1 at DIF offset 12
        dtrace: error on enabled probe ID 3 (ID 75795: bunyan42241:mod-87ea640:log-trace:log-trace): out of scratch space in action #1 at DIF offset 12
        dtrace: 138 drops on CPU 4
        ...

  From Bryan: "the DTrace buffer is filling up because the string size is so
  large... by increasing the switchrate, you're increasing the rate at
  which that buffer is emptied."


## 0.21.1

- [pull #83] Support rendering 'client_res' key in bunyan CLI (by
  github.com/mcavage).


## 0.21.0

- 'make check' clean, 4-space indenting. No functional change here, just
  lots of code change.
- [issue #80, #82] Drop assert that broke using 'rotating-file' with
  a default `period` (by github.com/ricardograca).


## 0.20.0

- [Slight backward incompatibility] Fix serializer bug introduced in 0.18.3
  (see below) to only apply serializers to log records when appropriate.

  This also makes a semantic change to custom serializers. Before this change
  a serializer function was called for a log record key when that value was
  truth-y. The semantic change is to call the serializer function as long
  as the value is not `undefined`. That means that a serializer function
  should handle falsey values such as `false` and `null`.

- Update to latest 'mv' dep (required for rotating-file support) to support
  node v0.10.0.


## 0.19.0

**WARNING**: This release includes a bug introduced in bunyan 0.18.3 (see
below). Please upgrade to bunyan 0.20.0.

- [Slight backward incompatibility] Change the default error serialization
  (a.k.a. `bunyan.stdSerializers.err`) to *not* serialize all additional
  attributes of the given error object. This is an open door to unsafe logging
  and logging should always be safe. With this change, error serialization
  will log these attributes: message, name, stack, code, signal. The latter
  two are added because some core node APIs include those fields (e.g.
  `child_process.exec`).

  Concrete examples where this has hurt have been the "domain" change
  necessitating 0.18.3 and a case where
  [node-restify](https://github.com/mcavage/node-restify) uses an error object
  as the response object. When logging the `err` and `res` in the same log
  statement (common for restify audit logging), the `res.body` would be JSON
  stringified as '[Circular]' as it had already been emitted for the `err` key.
  This results in a WTF with the bunyan CLI because the `err.body` is not
  rendered.

  If you need the old behaviour back you will need to do this:

        var bunyan = require('bunyan');
        var errSkips = {
            // Skip domain keys. `domain` especially can have huge objects that can
            // OOM your app when trying to JSON.stringify.
            domain: true,
            domain_emitter: true,
            domain_bound: true,
            domain_thrown: true
        };
        bunyan.stdSerializers.err = function err(err) {
           if (!err || !err.stack)
               return err;
           var obj = {
               message: err.message,
               name: err.name,
               stack: getFullErrorStack(err)
           }
           Object.keys(err).forEach(function (k) {
               if (err[k] !== undefined && !errSkips[k]) {
                   obj[k] = err[k];
               }
           });
           return obj;
         };

- "long" and "bunyan" output formats for the CLI. `bunyan -o long` is the default
  format, the same as before, just called "long" now instead of the cheesy "paul"
  name. The "bunyan" output format is the same as "json-0", just with a more
  convenient name.


## 0.18.3

**WARNING**: This release introduced a bug such that all serializers are
applied to all log records even if the log record did not contain the key
for that serializer. If a logger serializer function does not handle
being given `undefined`, then you'll get warnings like this on stderr:

    bunyan: ERROR: This should never happen. This is a bug in <https://github.com/trentm/node-bunyan> or in this application. Exception from "foo" Logger serializer: Error: ...
        at Object.bunyan.createLogger.serializers.foo (.../myapp.js:20:15)
        at Logger._applySerializers (.../lib/bunyan.js:644:46)
        at Array.forEach (native)
        at Logger._applySerializers (.../lib/bunyan.js:640:33)
        ...

and the following junk in written log records:

    "foo":"(Error in Bunyan log "foo" serializer broke field. See stderr for details.)"

Please upgrade to bunyan 0.20.0.


- Change the `bunyan.stdSerializers.err` serializer for errors to *exclude*
  [the "domain*" keys](http://nodejs.org/docs/latest/api/all.html#all_additions_to_error_objects).
  `err.domain` will include its assigned members which can arbitrarily large
  objects that are not intended for logging.

- Make the "dtrace-provider" dependency optional. I hate to do this, but
  installing bunyan on Windows is made very difficult with this as a required
  dep.  Even though "dtrace-provider" stubs out for non-dtrace-y platforms,
  without a compiler and Python around, node-gyp just falls over.


## 0.18.2

- [pull #67] Remove debugging prints in rotating-file support.
  (by github.com/chad3814).
- Update to dtrace-provider@0.2.7.


## 0.18.1

- Get the `bunyan` CLI to **not** automatically page (i.e. pipe to `less`)
  if stdin isn't a TTY, or if following dtrace probe output (via `-p PID`),
  or if not given log file arguments.


## 0.18.0

- Automatic paging support in the `bunyan` CLI (similar to `git log` et al).
  IOW, `bunyan` will open your pager (by default `less`) and pipe rendered
  log output through it. A main benefit of this is getting colored logs with
  a pager without the pain. Before you had to explicit use `--color` to tell
  bunyan to color output when the output was not a TTY:

        bunyan foo.log --color | less -R        # before
        bunyan foo.log                          # now

  Disable with the `--no-pager` option or the `BUNYAN_NO_PAGER=1` environment
  variable.

  Limitations: Only supported for node >=0.8. Windows is not supported (at
  least not yet).

- Switch test suite to nodeunit (still using a node-tap'ish API via
  a helper).


## 0.17.0

- [issue #33] Log rotation support:

        var bunyan = require('bunyan');
        var log = bunyan.createLogger({
            name: 'myapp',
            streams: [{
                type: 'rotating-file',
                path: '/var/log/myapp.log',
                count: 7,
                period: 'daily'
            }]
        });


- Tweak to CLI default pretty output: don't special case "latency" field.
  The special casing was perhaps nice, but less self-explanatory.
  Before:

        [2012-12-27T21:17:38.218Z]  INFO: audit/45769 on myserver: handled: 200 (15ms, audit=true, bar=baz)
          GET /foo
          ...

  After:

        [2012-12-27T21:17:38.218Z]  INFO: audit/45769 on myserver: handled: 200 (audit=true, bar=baz, latency=15)
          GET /foo
          ...

- *Exit* CLI on EPIPE, otherwise we sit there useless processing a huge log
  file with, e.g.  `bunyan huge.log | head`.


## 0.16.8

- Guards on `-c CONDITION` usage to attempt to be more user friendly.
  Bogus JS code will result in this:

        $ bunyan portal.log -c 'this.req.username==boo@foo'
        bunyan: error: illegal CONDITION code: SyntaxError: Unexpected token ILLEGAL
          CONDITION script:
            Object.prototype.TRACE = 10;
            Object.prototype.DEBUG = 20;
            Object.prototype.INFO = 30;
            Object.prototype.WARN = 40;
            Object.prototype.ERROR = 50;
            Object.prototype.FATAL = 60;
            this.req.username==boo@foo
          Error:
            SyntaxError: Unexpected token ILLEGAL
                at new Script (vm.js:32:12)
                at Function.Script.createScript (vm.js:48:10)
                at parseArgv (/Users/trentm/tm/node-bunyan-0.x/bin/bunyan:465:27)
                at main (/Users/trentm/tm/node-bunyan-0.x/bin/bunyan:1252:16)
                at Object.<anonymous> (/Users/trentm/tm/node-bunyan-0.x/bin/bunyan:1330:3)
                at Module._compile (module.js:449:26)
                at Object.Module._extensions..js (module.js:467:10)
                at Module.load (module.js:356:32)
                at Function.Module._load (module.js:312:12)
                at Module.runMain (module.js:492:10)

  And all CONDITION scripts will be run against a minimal valid Bunyan
  log record to ensure they properly guard against undefined values
  (at least as much as can reasonably be checked). For example:

        $ bunyan portal.log -c 'this.req.username=="bob"'
        bunyan: error: CONDITION code cannot safely filter a minimal Bunyan log record
          CONDITION script:
            Object.prototype.TRACE = 10;
            Object.prototype.DEBUG = 20;
            Object.prototype.INFO = 30;
            Object.prototype.WARN = 40;
            Object.prototype.ERROR = 50;
            Object.prototype.FATAL = 60;
            this.req.username=="bob"
          Minimal Bunyan log record:
            {
              "v": 0,
              "level": 30,
              "name": "name",
              "hostname": "hostname",
              "pid": 123,
              "time": 1355514346206,
              "msg": "msg"
            }
          Filter error:
            TypeError: Cannot read property 'username' of undefined
                at bunyan-condition-0:7:9
                at Script.Object.keys.forEach.(anonymous function) [as runInNewContext] (vm.js:41:22)
                at parseArgv (/Users/trentm/tm/node-bunyan-0.x/bin/bunyan:477:18)
                at main (/Users/trentm/tm/node-bunyan-0.x/bin/bunyan:1252:16)
                at Object.<anonymous> (/Users/trentm/tm/node-bunyan-0.x/bin/bunyan:1330:3)
                at Module._compile (module.js:449:26)
                at Object.Module._extensions..js (module.js:467:10)
                at Module.load (module.js:356:32)
                at Function.Module._load (module.js:312:12)
                at Module.runMain (module.js:492:10)

  A proper way to do that condition would be:

        $ bunyan portal.log -c 'this.req && this.req.username=="bob"'



## 0.16.7

- [issue #59] Clear a possibly interrupted ANSI color code on signal
  termination.


## 0.16.6

- [issue #56] Support `bunyan -p NAME` to dtrace all PIDs matching 'NAME' in
  their command and args (using `ps -A -o pid,command | grep NAME` or, on SunOS
  `pgrep -lf NAME`). E.g.:

        bunyan -p myappname

  This is useful for usage of node's [cluster
  module](http://nodejs.org/docs/latest/api/all.html#all_cluster) where you'll
  have multiple worker processes.


## 0.16.5

- Allow `bunyan -p '*'` to capture bunyan dtrace probes from **all** processes.
- issue #55: Add support for `BUNYAN_NO_COLOR` environment variable to
  turn off all output coloring. This is still overridden by the `--color`
  and `--no-color` options.


## 0.16.4

- issue #54: Ensure (again, see 0.16.2) that stderr from the dtrace child
  process (when using `bunyan -p PID`) gets through. There had been a race
  between exiting bunyan and the flushing of the dtrace process' stderr.


## 0.16.3

- Drop 'trentm-dtrace-provider' fork dep now that
  <https://github.com/chrisa/node-dtrace-provider/pull/24> has been resolved.
  Back to dtrace-provider.


## 0.16.2

- Ensure that stderr from the dtrace child process (when using `bunyan -p PID`)
  gets through. The `pipe` usage wasn't working on SmartOS. This is important
  to show the user if they need to 'sudo'.


## 0.16.1

- Ensure that a possible dtrace child process (with using `bunyan -p PID`) is
  terminated on signal termination of the bunyan CLI (at least for SIGINT,
  SIGQUIT, SIGTERM, SIGHUP).


## 0.16.0

- Add `bunyan -p PID` support. This is a convenience wrapper that effectively
  calls:

        dtrace -x strsize=4k -qn 'bunyan$PID:::log-*{printf("%s", copyinstr(arg0))}' | bunyan


## 0.15.0

- issue #48: Dtrace support! The elevator pitch is you can watch all logging
  from all Bunyan-using process with something like this:

        dtrace -x strsize=4k -qn 'bunyan*:::log-*{printf("%d: %s: %s", pid, probefunc, copyinstr(arg0))}'

  And this can include log levels *below* what the service is actually configured
  to log. E.g. if the service is only logging at INFO level and you need to see
  DEBUG log messages, with this you can. Obviously this only works on dtrace-y
  platforms: Illumos derivatives of SunOS (e.g. SmartOS, OmniOS), Mac, FreeBSD.

  Or get the bunyan CLI to render logs nicely:

        dtrace -x strsize=4k -qn 'bunyan*:::log-*{printf("%s", copyinstr(arg0))}' | bunyan

  See <https://github.com/trentm/node-bunyan#dtrace-support> for details. By
  Bryan Cantrill.


## 0.14.6

- Export `bunyan.safeCycles()`. This may be useful for custom `type == "raw"`
  streams that may do JSON stringification of log records themselves. Usage:

        var str = JSON.stringify(rec, bunyan.safeCycles());

- [issue #49] Allow a `log.child()` to specify the level of inherited streams.
  For example:

        # Before
        var childLog = log.child({...});
        childLog.level('debug');

        # After
        var childLog = log.child({..., level: 'debug'});

- Improve the Bunyan CLI crash message to make it easier to provide relevant
  details in a bug report.


## 0.14.5

- Fix a bug in the long-stack-trace error serialization added in 0.14.4. The
  symptom:

        bunyan@0.14.4: .../node_modules/bunyan/lib/bunyan.js:1002
          var ret = ex.stack || ex.toString();
                      ^
        TypeError: Cannot read property 'stack' of undefined
            at getFullErrorStack (.../node_modules/bunyan/lib/bunyan.js:1002:15)
            ...


## 0.14.4

- **Bad release. Use 0.14.5 instead.**
- Improve error serialization to walk the chain of `.cause()` errors
  from the likes of `WError` or `VError` error classes from
  [verror](https://github.com/davepacheco/node-verror) and
  [restify v2.0](https://github.com/mcavage/node-restify). Example:

        [2012-10-11T00:30:21.871Z] ERROR: imgapi/99612 on 0525989e-2086-4270-b960-41dd661ebd7d: my-message
            ValidationFailedError: my-message; caused by TypeError: cause-error-message
                at Server.apiPing (/opt/smartdc/imgapi/lib/app.js:45:23)
                at next (/opt/smartdc/imgapi/node_modules/restify/lib/server.js:550:50)
                at Server.setupReq (/opt/smartdc/imgapi/lib/app.js:178:9)
                at next (/opt/smartdc/imgapi/node_modules/restify/lib/server.js:550:50)
                at Server.parseBody (/opt/smartdc/imgapi/node_modules/restify/lib/plugins/body_parser.js:15:33)
                at next (/opt/smartdc/imgapi/node_modules/restify/lib/server.js:550:50)
                at Server.parseQueryString (/opt/smartdc/imgapi/node_modules/restify/lib/plugins/query.js:40:25)
                at next (/opt/smartdc/imgapi/node_modules/restify/lib/server.js:550:50)
                at Server._run (/opt/smartdc/imgapi/node_modules/restify/lib/server.js:579:17)
                at Server._handle.log.trace.req (/opt/smartdc/imgapi/node_modules/restify/lib/server.js:480:38)
            Caused by: TypeError: cause-error-message
                at Server.apiPing (/opt/smartdc/imgapi/lib/app.js:40:25)
                at next (/opt/smartdc/imgapi/node_modules/restify/lib/server.js:550:50)
                at Server.setupReq (/opt/smartdc/imgapi/lib/app.js:178:9)
                at next (/opt/smartdc/imgapi/node_modules/restify/lib/server.js:550:50)
                at Server.parseBody (/opt/smartdc/imgapi/node_modules/restify/lib/plugins/body_parser.js:15:33)
                at next (/opt/smartdc/imgapi/node_modules/restify/lib/server.js:550:50)
                at Server.parseQueryString (/opt/smartdc/imgapi/node_modules/restify/lib/plugins/query.js:40:25)
                at next (/opt/smartdc/imgapi/node_modules/restify/lib/server.js:550:50)
                at Server._run (/opt/smartdc/imgapi/node_modules/restify/lib/server.js:579:17)
                at Server._handle.log.trace.req (/opt/smartdc/imgapi/node_modules/restify/lib/server.js:480:38)


## 0.14.2

- [issue #45] Fix bunyan CLI (default output mode) to not crash on a 'res'
  field that isn't a response object, but a string.


## 0.14.1

- [issue #44] Fix the default `bunyan` CLI output of a `res.body` that is an
  object instead of a string. See issue#38 for the same with `req.body`.


## 0.14.0

- [pull #41] Safe `JSON.stringify`ing of emitted log records to avoid blowing
  up on circular objects (by Isaac Schlueter).


## 0.13.5

- [issue #39] Fix a bug with `client_req` handling in the default output
  of the `bunyan` CLI.


## 0.13.4

- [issue #38] Fix the default `bunyan` CLI output of a `req.body` that is an
  object instead of a string.


## 0.13.3

- Export `bunyan.resolveLevel(NAME-OR-NUM)` to resolve a level name or number
  to its log level number value:

        > bunyan.resolveLevel('INFO')
        30
        > bunyan.resolveLevel('debug')
        20

  A side-effect of this change is that the uppercase level name is now allowed
  in the logger constructor.


## 0.13.2

- [issue #35] Ensure that an accidental `log.info(BUFFER)`, where BUFFER is
  a node.js Buffer object, doesn't blow up.


## 0.13.1

- [issue #34] Ensure `req.body`, `res.body` and other request/response fields
  are emitted by the `bunyan` CLI (mostly by Rob Gulewich).



## 0.13.0

- [issue #31] Re-instate defines for the (uppercase) log level names (TRACE,
  DEBUG, etc.) in `bunyan -c "..."` filtering condition code. E.g.:

        $ ... | bunyan -c 'level >= ERROR'


## 0.12.0

- [pull #32] `bunyan -o short` for more concise output (by Dave Pacheco). E.g.:

        22:56:52.856Z  INFO myservice: My message

  instead of:

        [2012-02-08T22:56:52.856Z]  INFO: myservice/123 on example.com: My message


## 0.11.3

- Add '--strict' option to `bunyan` CLI to suppress all but legal Bunyan JSON
  log lines. By default non-JSON, and non-Bunyan lines are passed through.


## 0.11.2

- [issue #30] Robust handling of 'req' field without a 'headers' subfield
  in `bunyan` CLI.
- [issue #31] Pull the TRACE, DEBUG, et al defines from `bunyan -c "..."`
  filtering code. This was added in v0.11.1, but has a significant adverse
  affect.


## 0.11.1

- **Bad release. The TRACE et al names are bleeding into the log records
  when using '-c'.**
- Add defines for the (uppercase) log level names (TRACE, DEBUG, etc.) in
  `bunyan -c "..."` filtering condition code. E.g.:

        $ ... | bunyan -c 'level >= ERROR'


## 0.11.0

- [pull #29] Add -l/--level for level filtering, and -c/--condition for
  arbitrary conditional filtering (by github.com/isaacs):

        $ ... | bunyan -l error   # filter out log records below error
        $ ... | bunyan -l 50      # numeric value works too
        $ ... | bunyan -c 'level===50'              # equiv with -c filtering
        $ ... | bunyan -c 'pid===123'               # filter on any field
        $ ... | bunyan -c 'pid===123' -c '_audit'   # multiple filters


## 0.10.0

- [pull #24] Support for gzip'ed log files in the bunyan CLI (by
  github.com/mhart):

        $ bunyan foo.log.gz
        ...


## 0.9.0

- [pull #16] Bullet proof the `bunyan.stdSerializers` (by github.com/rlidwka).

- [pull #15] The `bunyan` CLI will now chronologically merge multiple log
  streams when it is given multiple file arguments. (by github.com/davepacheco)

        $ bunyan foo.log bar.log
        ... merged log records ...

- [pull #15] A new `bunyan.RingBuffer` stream class that is useful for
  keeping the last N log messages in memory. This can be a fast way to keep
  recent, and thus hopefully relevant, log messages. (by @dapsays,
  github.com/davepacheco)

  Potential uses: Live debugging if a running process could inspect those
  messages. One could dump recent log messages at a finer log level than is
  typically logged on
  [`uncaughtException`](http://nodejs.org/docs/latest/api/all.html#all_event_uncaughtexception).

        var ringbuffer = new bunyan.RingBuffer({ limit: 100 });
        var log = new bunyan({
            name: 'foo',
            streams: [{
                type: 'raw',
                stream: ringbuffer,
                level: 'debug'
            }]
        });

        log.info('hello world');
        console.log(ringbuffer.records);

- Add support for "raw" streams. This is a logging stream that is given
  raw log record objects instead of a JSON-stringified string.

        function Collector() {
            this.records = [];
        }
        Collector.prototype.write = function (rec) {
            this.records.push(rec);
        }
        var log = new Logger({
            name: 'mylog',
            streams: [{
                type: 'raw',
                stream: new Collector()
            }]
        });

  See "examples/raw-stream.js". I expect raw streams to be useful for
  piping Bunyan logging to separate services (e.g. <http://www.loggly.com/>,
  <https://github.com/etsy/statsd>) or to separate in-process handling.

- Add test/corpus/*.log files (accidentally excluded) so the test suite
  actually works(!).


## 0.8.0

- [pull #21] Bunyan loggers now re-emit `fs.createWriteStream` error events.
  By github.com/EvanOxfeld. See "examples/handle-fs-error.js" and
  "test/error-event.js" for details.

        var log = new Logger({name: 'mylog', streams: [{path: FILENAME}]});
        log.on('error', function (err, stream) {
            // Handle error writing to or creating FILENAME.
        });

- jsstyle'ing (via `make check`)


## 0.7.0

- [issue #12] Add `bunyan.createLogger(OPTIONS)` form, as is more typical in
  node.js APIs.  This'll eventually become the preferred form.


## 0.6.9

- Change `bunyan` CLI default output to color "src" info red. Before the "src"
  information was uncolored. The "src" info is the filename, line number and
  function name resulting from using `src: true` in `Logger` creation. I.e.,
  the `(/Users/trentm/tm/node-bunyan/examples/hi.js:10)` in:

        [2012-04-10T22:28:58.237Z]  INFO: myapp/39339 on banana.local (/Users/trentm/tm/node-bunyan/examples/hi.js:10): hi

- Tweak `bunyan` CLI default output to still show an "err" field if it doesn't
  have a "stack" attribute.


## 0.6.8

- Fix bad bug in `log.child({...}, true);` where the added child fields **would
  be added to the parent's fields**. This bug only existed for the "fast child"
  path (that second `true` argument). A side-effect of fixing this is that
  the "fast child" path is only 5 times as fast as the regular `log.child`,
  instead of 10 times faster.


## 0.6.7

- [issue #6] Fix bleeding 'type' var to global namespace. (Thanks Mike!)


## 0.6.6

- Add support to the `bunyan` CLI taking log file path args, `bunyan foo.log`,
  in addition to the usual `cat foo.log | bunyan`.
- Improve reliability of the default output formatting of the `bunyan` CLI.
  Before it could blow up processing log records missing some expected
  fields.


## 0.6.5

- ANSI coloring output from `bunyan` CLI tool (for the default output mode/style).
  Also add the '--color' option to force coloring if the output stream is not
  a TTY, e.g. `cat my.log | bunyan --color | less -R`. Use `--no-color` to
  disable coloring, e.g. if your terminal doesn't support ANSI codes.
- Add 'level' field to log record before custom fields for that record. This
  just means that the raw record JSON will show the 'level' field earlier,
  which is a bit nicer for raw reading.


## 0.6.4

- [issue #5] Fix `log.info() -> boolean` to work properly. Previous all were
  returning false. Ditto all trace/debug/.../fatal methods.


## 0.6.3

- Allow an optional `msg` and arguments to the `log.info(<Error> err)` logging
  form. For example, before:

        log.debug(my_error_instance)            // good
        log.debug(my_error_instance, "boom!")   // wasn't allowed

  Now the latter is allowed if you want to expliciting set the log msg. Of course
  this applies to all the `log.{trace|debug|info...}()` methods.

- `bunyan` cli output: clarify extra fields with quoting if empty or have
  spaces. E.g. 'cmd' and 'stderr' in the following:

        [2012-02-12T00:30:43.736Z] INFO: mo-docs/43194 on banana.local: buildDocs results (req_id=185edca2-2886-43dc-911c-fe41c09ec0f5, route=PutDocset, error=null, stderr="", cmd="make docs")


## 0.6.2

- Fix/guard against unintended inclusion of some files in npm published package
  due to <https://github.com/isaacs/npm/issues/2144>


## 0.6.1

- Internal: starting jsstyle usage.
- Internal: add .npmignore. Previous packages had reams of bunyan crud in them.


## 0.6.0

- Add 'pid' automatic log record field.


## 0.5.3

- Add 'client_req' (HTTP client request) standard formatting in `bunyan` CLI
  default output.
- Improve `bunyan` CLI default output to include *all* log record keys. Unknown keys
  are either included in the first line parenthetical (if short) or in the indented
  subsequent block (if long or multiline).


## 0.5.2

- [issue #3] More type checking of `new Logger(...)` and `log.child(...)`
  options.
- Start a test suite.


## 0.5.1

- [issue #2] Add guard on `JSON.stringify`ing of log records before emission.
  This will prevent `log.info` et al throwing on record fields that cannot be
  represented as JSON. An error will be printed on stderr and a clipped log
  record emitted with a 'bunyanMsg' key including error details. E.g.:

        bunyan: ERROR: could not stringify log record from /Users/trentm/tm/node-bunyan/examples/unstringifyable.js:12: TypeError: Converting circular structure to JSON
        {
          "name": "foo",
          "hostname": "banana.local",
          "bunyanMsg": "bunyan: ERROR: could not stringify log record from /Users/trentm/tm/node-bunyan/examples/unstringifyable.js:12: TypeError: Converting circular structure to JSON",
        ...

  Some timing shows this does effect log speed:

        $ node tools/timeguard.js     # before
        Time try/catch-guard on JSON.stringify:
         - log.info:  0.07365ms per iteration
        $ node tools/timeguard.js     # after
        Time try/catch-guard on JSON.stringify:
         - log.info:  0.07368ms per iteration


## 0.5.0

- Use 10/20/... instead of 1/2/... for level constant values. Ostensibly this
  allows for intermediary levels from the defined "trace/debug/..." set.
  However, that is discouraged. I'd need a strong user argument to add
  support for easily using alternative levels. Consider using a separate
  JSON field instead.
- s/service/name/ for Logger name field. "service" is unnecessarily tied
  to usage for a service. No need to differ from log4j Logger "name".
- Add `log.level(...)` and `log.levels(...)` API for changing logger stream
  levels.
- Add `TRACE|DEBUG|INFO|WARN|ERROR|FATAL` level constants to exports.
- Add `log.info(err)` special case for logging an `Error` instance. For
  example `log.info(new TypeError("boom")` will produce:

        ...
        "err": {
          "message": "boom",
          "name": "TypeError",
          "stack": "TypeError: boom\n    at Object.<anonymous> ..."
        },
        "msg": "boom",
        ...


## 0.4.0

- Add `new Logger({src: true})` config option to have a 'src' attribute be
  automatically added to log records with the log call source info. Example:

        "src": {
          "file": "/Users/trentm/tm/node-bunyan/examples/src.js",
          "line": 20,
          "func": "Wuzzle.woos"
        },


## 0.3.0

- `log.child(options[, simple])` Added `simple` boolean arg. Set `true` to
  assert that options only add fields (no config changes). Results in a 10x
  speed increase in child creation. See "tools/timechild.js". On my Mac,
  "fast child" creation takes about 0.001ms. IOW, if your app is dishing
  10,000 req/s, then creating a log child for each request will take
  about 1% of the request time.
- `log.clone` -> `log.child` to better reflect the relationship: streams and
  serializers are inherited. Streams can't be removed as part of the child
  creation. The child doesn't own the parent's streams (so can't close them).
- Clean up Logger creation. The goal here was to ensure `log.child` usage
  is fast. TODO: measure that.
- Add `Logger.stdSerializers.err` serializer which is necessary to get good
  Error object logging with node 0.6 (where core Error object properties
  are non-enumerable).


## 0.2.0

- Spec'ing core/recommended log record fields.
- Add `LOG_VERSION` to exports.
- Improvements to request/response serializations.


## 0.1.0

First release.


================================================
FILE: CONTRIBUTING.md
================================================
# Contributing to node-bunyan

Thanks for using node-bunyan and for considering contributing to it! Or perhaps
you are just here to get a sniff for what is going on with node-bunyan
development.


## How you can help

If you want to help me here, great! Thank you! Some ideas:

- Do you have experience with and/or recommendations for a good automated
  testing service? Ideally I'd like support for Mac, Linux, SmartOS, and maybe
  Windows. Also, support for node.js versions 0.10 up to whatever the current
  latest is. Are those too tall an order? What's more, Bunyan is meant to work
  (at least partially) in the browser. Is there a good service for that?
  Please discuss on [issue #342](https://github.com/trentm/node-bunyan/issues/342).

- Fielding issues labelled with "[Type-Question][Type-Question]", if you are familiar
  with Bunyan and know how to answer them, would be great.

- If you want to dive into code, but aren't *that* familiar with node-bunyan,
  then [issues labelled with Experience-Easy][Experience-Easy] are a good
  place to start.

- [Once I've made a once over
  triaging](https://github.com/trentm/node-bunyan/issues/335) and consolidating
  issues and PRs, volunteering for issues in a particular
  [component](#component) with which you have familiarity would be great.

[Type-Question]: https://github.com/trentm/node-bunyan/issues?q=is%3Aopen+is%3Aissue+label%3AType-Question


## Trent's Biased Rules for Code

In the hope that it makes it easier to get PRs into Bunyan, here is my biased
list of what I typically want. Submitting a PR without all of these is
*totally fine*! The only side-effect is that it may take longer for me to
provide feedback on it and merge it. I'll politely request missing pieces.


- Please follow existing code style. Contributed code must pass `make check`.
  (Note: I intended to [change to eslint
  soon](https://github.com/trentm/node-bunyan/issues/341), so currently `make
  check` might be a moving target.)

- Any user visible change in behaviour should almost certainly include an
  update to the docs. Currently the "docs" is the README.md.

- Adding a test case for code changes is **stronly recommended**, else I
  can't easily promise to not break your fix/feature later. If you don't
  grok the test suite, please ask. We can use it to form the basis for a
  "test/README.md".

- Typically a code change should have an associated issue or PR. This allows
  addition of follow-up issues, discussion, test data, etc. to be associated
  with the commit. Just using GitHub pull requests makes this easy.

- All but the most trivial code changes should have an addition to the
  [changelog](./CHANGES.md). The audience for the changelog is *Bunyan users*.
  However, because rebasing longer-lived PRs against master is a pain
  with a change to CHANGES.md, please **do not include a CHANGES.md change
  in your PR. Instead suggest a CHANGES.md addition in a comment on the
  PR.**

- Good commit messages, please:
    - The first line should be a succinct summary of the issue or fix. A
      good candidate is to just cut 'n paste the issue title, if there is one.
    - If the commit is for a particular issue/PR (see previous rule), please
      list the issue number in the commit message. E.g. "Fixes #123" or "Related
      to #234".
    - The audience for commit messages is *Bunyan developers*.


## Pull Request Lifecycle

(Language adapted from
[terraform](https://github.com/hashicorp/terraform/blob/master/CONTRIBUTING.md).)

- You are welcome to submit your pull request for commentary or review before it
  is fully completed. Please prefix the title of your pull request with "[WIP]"
  to indicate this. It's also a good idea to include specific questions or items
  you'd like feedback on.

- Once you believe your pull request is ready to be merged, you can remove any
  "[WIP]" prefix from the title and a core team member will review. See
  Trent's Biased Rules above to help ensure that your contribution will be
  merged quickly.

- Trent or, if things go well, a node-bunyan maintainer will look over your
  contribution and either provide comments letting you know if there is anything
  left to do. Please be patient. Unfortunately, I'm not able to carve out
  a *lot* of time for Bunyan development and maintenance.

- Once all outstanding comments and checklist items have been addressed, your
  contribution will be merged. Merged PRs will be included in the next
  node-bunyan release.

- In some cases, we might decide that a PR should be closed. We'll make sure to
  provide clear reasoning when this happens.


## Issue labels

The point of issue labeling for node-bunyan is to help answer "what should be
worked on now? what can be left for later?" I don't want issue labelling to
become a burden for anyone, so (a) don't feel obliged to add them yourself and
(b) I'm happy to reevaluate their usage.

Bunyan shall have categories of [issue
labels](https://github.com/trentm/node-bunyan/labels) named "$category-$value".
An issue should have max *one* label from each set. Users of Google Code's
dearly departed issue tracker may remember this kind of thing. This is a
poorman's version of structured issue tracker metadata.

I'm inclined to *not* do priorities right now. *Possibly* we'll use GitHub
milestones to basically set targets for upcoming releases. But otherwise my
sense is that for smaller OSS projects, assigning prios will get in the way.
If people would think it helpful, I'd consider "Difficulty-" or "Experience-"
categories (a la Rust's "E-" labels) to mark easier and intermediate tasks
that someone interested but maybe not very familiar with Bunyan might want
to tackle.

For now, here are the various labels and their purpose:

### Meta

- needstriage: Temporary label to help me do a single triage pass through all
  current open issues and PRs.
  See [#335](https://github.com/trentm/node-bunyan/issues/335)
  where I'm working through this.

### Type

Color: green

- Type-Unknown: If it is still unclear or undecided if an issue is an intended
  feature (perhaps arguing for better docs or examples to avoid confusion) or a
  bug, I will use this category.
- Type-Question: Asking a question on Bunyan usage, about the project, etc.
- Type-Bug: A bug in Bunyan's behaviour.
- Type-Improvement: A new feature or other improvement.
- Type-Doc: Issues with Bunyan's documentation.
- Type-Task: A project task to be done.

TODO: consider Type-Unknown for the "unclear if bug or feature" tickets.

### Component

Color: blue

- Component-Project: Project meta stuff like testing, linting, build, install,
  etc.
- Component-CLI: The `bunyan` command-line tool.
- Component-Lib: catch-all for other library stuff
    - Component-LibRotation: The bunyan library's log rotation support.
    - Component-LibBrowser: Bunyan's handling/support for running in the browser.
    - Component-LibFlush: A separate component for collecting the tickets related
      to closing/flushing bunyan streams on process shutdown.

The point of components is to find like issues to help with reference, search
and resolving them. If no component fits an issue/PR, then don't add a label.

### Resolution

Color: red

- Resolution-WontFix
- Resolution-Duplicate
- Resolution-Fixed: Also used to indicate "doc written", "question answered",
  "feature implemented".
- Resolution-CannotRepro: After some reasonable attempt by maintainers to
  reproduce a bug report, I want it to be non-controversial to close it
  and mark it with this. If given more info by someone able to repro, we
  can happy re-open issues.

### Experience

Color: yellow

- Experience-Easy: Relatively little experience with node-bunyan should be
  required to complete this issue.
- Experience-NeedsTest: Typically added to an issue or PR that needs a test
  case. Someone familiar enough with node-bunyan's test suite could tackle this.
- Experience-Hard: At a guess, this is a thorny issue that requires known
  node-bunyan well, knowing node.js well, requires design review or all of
  these.

One of the "Experience-\*" labels can optionally be put on an issue or PR to
indicate what kind of experience a contributor would need with node-bunyan
(and/or node.js) to complete it. For example, if you're looking for somewhere to
start, check out the [Experience-Easy][Experience-Easy] tag. This category idea
is borrowed from [rust's E-\* labels][rust-issue-triage].

[Experience-Easy]: https://github.com/trentm/node-bunyan/issues?q=is%3Aopen+is%3Aissue+label%3AExperience-Easy
[rust-issue-triage]: https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.md#issue-triage


## Acknowledgements

Anything good about this document is thanks to inspiration from
[rust](https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.md) and, more
recently
[terraform](https://github.com/hashicorp/terraform/blob/master/CONTRIBUTING.md).
Anything bad about it, is my fault.


================================================
FILE: LICENSE.txt
================================================
# This is the MIT license

Copyright 2016 Trent Mick
Copyright 2016 Joyent Inc.

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.



================================================
FILE: Makefile
================================================

SHELL := bash

#---- Tools

TAP_EXEC := ./node_modules/.bin/tap
SUDO := sudo
ifeq ($(shell uname -s),SunOS)
	# On SunOS (e.g. SmartOS) we expect to run the test suite as the
	# root user -- necessary to run dtrace. Therefore `pfexec` isn't
	# necessary.
	SUDO :=
endif
DTRACE_UP_IN_HERE=
ifeq ($(shell uname -s),SunOS)
    DTRACE_UP_IN_HERE=1
endif
ifeq ($(shell uname -s),Darwin)
    DTRACE_UP_IN_HERE=1
endif


#---- Files

JSSTYLE_FILES := $(shell find lib test tools examples -name "*.js") bin/bunyan


#---- Targets

all $(TAP_EXEC):
	npm install $(NPM_INSTALL_FLAGS)

# Ensure all version-carrying files have the same version.
.PHONY: versioncheck
versioncheck:
	@echo version is: $(shell node -e 'console.log(require("./package.json").version)')
	[[ `node -e 'console.log(require("./package.json").version)'` == `grep '^## ' CHANGES.md | head -2 | tail -1 | awk '{print $$2}'` ]]
	@echo Version check ok.

.PHONY: cutarelease
cutarelease: check
	[[ -z `git status --short` ]]  # If this fails, the working dir is dirty.
	@which json 2>/dev/null 1>/dev/null && \
	    ver=$(shell json -f package.json version) && \
	    name=$(shell json -f package.json name) && \
	    publishedVer=$(shell npm view -j $(shell json -f package.json name)@$(shell json -f package.json version) version 2>/dev/null) && \
	    if [[ -n "$$publishedVer" ]]; then \
		echo "error: $$name@$$ver is already published to npm"; \
		exit 1; \
	    fi && \
	    echo "** Are you sure you want to tag and publish $$name@$$ver to npm?" && \
	    echo "** Enter to continue, Ctrl+C to abort." && \
	    read
	ver=$(shell cat package.json | json version) && \
	    date=$(shell date -u "+%Y-%m-%d") && \
	    git tag -a "$$ver" -m "version $$ver ($$date) beta" && \
	    git push --tags origin && \
	    npm publish --tag beta

.PHONY: docs
docs: toc
	@[[ `which ronn` ]] || (echo "No 'ronn' on your PATH. Install with 'gem install ronn'" && exit 2)
	mkdir -p man/man1
	ronn --style=toc --manual="bunyan manual" --date=$(shell git log -1 --pretty=format:%cd --date=short) --roff --html docs/bunyan.1.ronn
	python -c 'import sys; h = open("docs/bunyan.1.html").read(); h = h.replace(".mp dt.flush {float:left;width:8ex}", ""); open("docs/bunyan.1.html", "w").write(h)'
	python -c 'import sys; h = open("docs/bunyan.1.html").read(); h = h.replace("</body>", """<a href="https://github.com/trentm/node-bunyan"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on GitHub"></a></body>"""); open("docs/bunyan.1.html", "w").write(h)'
	@echo "# test with 'man ./docs/bunyan.1' and 'open ./docs/bunyan.1.html'"

# Re-generate the README.md table of contents.
toc:
	./node_modules/.bin/markdown-toc -i README.md


.PHONY: publish
publish:
	mkdir -p tmp
	[[ -d tmp/bunyan-gh-pages ]] || git clone git@github.com:trentm/node-bunyan.git tmp/bunyan-gh-pages
	cd tmp/bunyan-gh-pages && git checkout gh-pages && git pull --rebase origin gh-pages
	cp docs/index.html tmp/bunyan-gh-pages/index.html
	cp docs/bunyan.1.html tmp/bunyan-gh-pages/bunyan.1.html
	(cd tmp/bunyan-gh-pages \
		&& git commit -a -m "publish latest docs" \
		&& git push origin gh-pages || true)

.PHONY: distclean
distclean:
	rm -rf node_modules


#---- test

.PHONY: test
test: $(TAP_EXEC)
	test -z "$(DTRACE_UP_IN_HERE)" || test -n "$(SKIP_DTRACE)" || \
		(node -e 'require("dtrace-provider").createDTraceProvider("isthisthingon")' && \
		echo "\nNote: Use 'SKIP_DTRACE=1 make test' to skip parts of the test suite that require root." && \
		$(SUDO) $(TAP_EXEC) test/dtrace/*.test.js)
	$(TAP_EXEC) test/*.test.js


#---- check

.PHONY: check-jsstyle
check-jsstyle: $(JSSTYLE_FILES)
	./tools/jsstyle -o indent=4,doxygen,unparenthesized-return=0,blank-after-start-comment=0,leading-right-paren-ok=1 $(JSSTYLE_FILES)

.PHONY: check
check: check-jsstyle versioncheck
	@echo "Check ok."

.PHONY: prepush
prepush: check testall
	@echo "Okay to push."


================================================
FILE: README.md
================================================
[![npm version](https://img.shields.io/npm/v/bunyan.svg?style=flat)](https://www.npmjs.com/package/bunyan)
[![Build Status](https://travis-ci.org/trentm/node-bunyan.svg?branch=master)](https://travis-ci.org/trentm/node-bunyan)

Bunyan is **a simple and fast JSON logging library** for node.js services:

```js
var bunyan = require('bunyan');
var log = bunyan.createLogger({name: "myapp"});
log.info("hi");
```

and **a `bunyan` CLI tool** for nicely viewing those logs:

![bunyan CLI screenshot](https://raw.github.com/trentm/node-bunyan/master/tools/screenshot1.png)

Manifesto: Server logs should be structured. JSON's a good format. Let's do
that. A log record is one line of `JSON.stringify`'d output. Let's also
specify some common names for the requisite and common fields for a log
record (see below).


## Table of Contents

<!-- toc -->

- [Current Status](#current-status)
- [Installation](#installation)
- [Features](#features)
- [Introduction](#introduction)
  * [Constructor API](#constructor-api)
  * [Log Method API](#log-method-api)
  * [CLI Usage](#cli-usage)
  * [Streams Introduction](#streams-introduction)
  * [log.child](#logchild)
  * [Serializers](#serializers)
    + [Requirements for serializers functions](#requirements-for-serializers-functions)
    + [Standard Serializers](#standard-serializers)
  * [src](#src)
- [Levels](#levels)
  * [Level suggestions](#level-suggestions)
- [Log Record Fields](#log-record-fields)
  * [Core fields](#core-fields)
  * [Recommended/Best Practice Fields](#recommendedbest-practice-fields)
  * [Other fields to consider](#other-fields-to-consider)
- [Streams](#streams)
  * [Adding a Stream](#adding-a-stream)
  * [stream errors](#stream-errors)
  * [stream type: `stream`](#stream-type-stream)
  * [stream type: `file`](#stream-type-file)
  * [stream type: `rotating-file`](#stream-type-rotating-file)
  * [stream type: `raw`](#stream-type-raw)
  * [`raw` + RingBuffer Stream](#raw--ringbuffer-stream)
  * [third-party streams](#third-party-streams)
- [Runtime log snooping via DTrace](#runtime-log-snooping-via-dtrace)
  * [DTrace examples](#dtrace-examples)
- [Runtime environments](#runtime-environments)
  * [Browserify](#browserify)
  * [Webpack](#webpack)
- [Versioning](#versioning)
- [License](#license)
- [See Also](#see-also)

<!-- tocstop -->

# Current Status

Stable. I do my best to follow semver: i.e. you should only need to worry
about code breaking for a *major* version bump.  Bunyan currently supports node
0.10 and greater. Follow <a href="https://twitter.com/intent/user?screen_name=trentmick"
target="_blank">@trentmick</a> for updates to Bunyan.

There is an email discussion list
[bunyan-logging@googlegroups.com](mailto:bunyan-logging@googlegroups.com),
also [as a forum in the
browser](https://groups.google.com/forum/?fromgroups#!forum/bunyan-logging).

Active branches:
- "1.x" is for 1.x maintenance work, if any. 1.x releases are still "latest" in
  npm.
- "master" is currently for coming Bunyan 2.x work. For now, 2.x releases are
  published to npm with the "beta" tag, meaning that `npm install bunyan` is
  still 1.x for now. To install 2.x use `npm install bunyan@2` or
  `npm install bunyan@beta`.


# Installation

```sh
npm install bunyan
```

**Tip**: The `bunyan` CLI tool is written to be compatible (within reason) with
all versions of Bunyan logs. Therefore you might want to `npm install -g bunyan`
to get the bunyan CLI on your PATH, then use local bunyan installs for
node.js library usage of bunyan in your apps.

**Tip**: Installing without optional dependencies can dramatically reduce
bunyan's install size. **dtrace-provider** is used for dtrace features,
**mv** is used for RotatingFileStream, and **moment** is used for local time.
If you don't need these features, consider installing with the
`--no-optional` flag.


# Features

- elegant [log method API](#log-method-api)
- extensible [streams](#streams) system for controlling where log records
  go (to a stream, to a file, [log file rotation](#stream-type-rotating-file),
  etc.)
- [`bunyan` CLI](#cli-usage) for pretty-printing and filtering of Bunyan logs
- simple include of log call source location (file, line, function) with
  [`src: true`](#src)
- lightweight specialization of Logger instances with [`log.child`](#logchild)
- custom rendering of logged objects with ["serializers"](#serializers)
- [Runtime log snooping via DTrace support](#runtime-log-snooping-via-dtrace)
- Support for a few [runtime environments](#runtime-environments): Node.js,
  [Browserify](http://browserify.org/), [Webpack](https://webpack.github.io/), [NW.js](http://nwjs.io/).


# Introduction

Like most logging libraries you create a Logger instance and call methods
named after the logging levels:

```js
// hi.js
var bunyan = require('bunyan');
var log = bunyan.createLogger({name: 'myapp'});
log.info('hi');
log.warn({lang: 'fr'}, 'au revoir');
```

All loggers must provide a "name". This is somewhat akin to the log4j logger
"name", but Bunyan doesn't do hierarchical logger names.

**Bunyan log records are JSON.** A few fields are added automatically:
"pid", "hostname", "time" and "v".

```sh
$ node hi.js
{"name":"myapp","hostname":"banana.local","pid":40161,"level":30,"msg":"hi","time":"2013-01-04T18:46:23.851Z","v":0}
{"name":"myapp","hostname":"banana.local","pid":40161,"level":40,"lang":"fr","msg":"au revoir","time":"2013-01-04T18:46:23.853Z","v":0}
```


## Constructor API

```js
var bunyan = require('bunyan');
var log = bunyan.createLogger({
    name: <string>,                     // Required
    level: <level name or number>,      // Optional, see "Levels" section
    stream: <node.js stream>,           // Optional, see "Streams" section
    streams: [<bunyan streams>, ...],   // Optional, see "Streams" section
    serializers: <serializers mapping>, // Optional, see "Serializers" section
    src: <boolean>,                     // Optional, see "src" section

    // Any other fields are added to all log records as is.
    foo: 'bar',
    ...
});
```


## Log Method API

The example above shows two different ways to call `log.info(...)`. The
full API is:

```js
log.info();     // Returns a boolean: is the "info" level enabled?
                // This is equivalent to `log.isInfoEnabled()` or
                // `log.isEnabledFor(INFO)` in log4j.

log.info('hi');                     // Log a simple string message (or number).
log.info('hi %s', bob, anotherVar); // Uses `util.format` for msg formatting.

log.info({foo: 'bar'}, 'hi');
                // The first field can optionally be a "fields" object, which
                // is merged into the log record.

log.info(err);  // Special case to log an `Error` instance to the record.
                // This adds an "err" field with exception details
                // (including the stack) and sets "msg" to the exception
                // message.
log.info(err, 'more on this: %s', more);
                // ... or you can specify the "msg".

log.info({foo: 'bar', err: err}, 'some msg about this error');
                // To pass in an Error *and* other fields, use the `err`
                // field name for the Error instance **and ensure your logger
                // has a `err` serializer.** One way to ensure the latter is:
                //      var log = bunyan.createLogger({
                //          ...,
                //          serializers: bunyan.stdSerializers
                //      });
                // See the "Serializers" section below for details.
```

Note that this implies **you cannot blindly pass any object as the first
argument to log it** because that object might include fields that collide with
Bunyan's [core record fields](#core-fields). In other words,
`log.info(mywidget)` may not yield what you expect. Instead of a string
representation of `mywidget` that other logging libraries may give you, Bunyan
will try to JSON-ify your object. It is a Bunyan best practice to always give a
field name to included objects, e.g.:

```js
log.info({widget: mywidget}, ...)
```

This will dove-tail with [Bunyan serializer support](#serializers), discussed
later.

The same goes for all of Bunyan's log levels: `log.trace`, `log.debug`,
`log.info`, `log.warn`, `log.error`, and `log.fatal`. See the [levels
section](#levels) below for details and suggestions.


## CLI Usage

Bunyan log output is a stream of JSON objects. This is great for processing,
but not for reading directly. A **`bunyan` tool** is provided **for
pretty-printing bunyan logs** and for **filtering** (e.g.
`| bunyan -c 'this.foo == "bar"'`). Using our example above:

```sh
$ node hi.js | ./node_modules/.bin/bunyan
[2013-01-04T19:01:18.241Z]  INFO: myapp/40208 on banana.local: hi
[2013-01-04T19:01:18.242Z]  WARN: myapp/40208 on banana.local: au revoir (lang=fr)
```

See the screenshot above for an example of the default coloring of rendered
log output. That example also shows the nice formatting automatically done for
some well-known log record fields (e.g. `req` is formatted like an HTTP request,
`res` like an HTTP response, `err` like an error stack trace).

One interesting feature is **filtering** of log content, which can be useful
for digging through large log files or for analysis. We can filter only
records above a certain level:

```sh
$ node hi.js | bunyan -l warn
[2013-01-04T19:08:37.182Z]  WARN: myapp/40353 on banana.local: au revoir (lang=fr)
```

Or filter on the JSON fields in the records (e.g. only showing the French
records in our contrived example):

```sh
$ node hi.js | bunyan -c 'this.lang == "fr"'
[2013-01-04T19:08:26.411Z]  WARN: myapp/40342 on banana.local: au revoir (lang=fr)
```

See `bunyan --help` for other facilities.


## Streams Introduction

By default, log output is to stdout and at the "info" level. Explicitly that
looks like:

```js
var log = bunyan.createLogger({
    name: 'myapp',
    stream: process.stdout,
    level: 'info'
});
```

That is an abbreviated form for a single stream. **You can define multiple
streams at different levels**.

```js
var log = bunyan.createLogger({
  name: 'myapp',
  streams: [
    {
      level: 'info',
      stream: process.stdout            // log INFO and above to stdout
    },
    {
      level: 'error',
      path: '/var/tmp/myapp-error.log'  // log ERROR and above to a file
    }
  ]
});
```

More on streams in the [Streams section](#streams) below.

## log.child

Bunyan has a concept of a child logger to **specialize a logger for a
sub-component of your application**, i.e. to create a new logger with
additional bound fields that will be included in its log records. A child
logger is created with `log.child(...)`.

In the following example, logging on a "Wuzzle" instance's `this.log` will
be exactly as on the parent logger with the addition of the `widget_type`
field:

```js
var bunyan = require('bunyan');
var log = bunyan.createLogger({name: 'myapp'});

function Wuzzle(options) {
    this.log = options.log.child({widget_type: 'wuzzle'});
    this.log.info('creating a wuzzle')
}
Wuzzle.prototype.woos = function () {
    this.log.warn('This wuzzle is woosey.')
}

log.info('start');
var wuzzle = new Wuzzle({log: log});
wuzzle.woos();
log.info('done');
```

Running that looks like (raw):

```sh
$ node myapp.js
{"name":"myapp","hostname":"myhost","pid":34572,"level":30,"msg":"start","time":"2013-01-04T07:47:25.814Z","v":0}
{"name":"myapp","hostname":"myhost","pid":34572,"widget_type":"wuzzle","level":30,"msg":"creating a wuzzle","time":"2013-01-04T07:47:25.815Z","v":0}
{"name":"myapp","hostname":"myhost","pid":34572,"widget_type":"wuzzle","level":40,"msg":"This wuzzle is woosey.","time":"2013-01-04T07:47:25.815Z","v":0}
{"name":"myapp","hostname":"myhost","pid":34572,"level":30,"msg":"done","time":"2013-01-04T07:47:25.816Z","v":0}
```

And with the `bunyan` CLI (using the "short" output mode):

```sh
$ node myapp.js  | bunyan -o short
07:46:42.707Z  INFO myapp: start
07:46:42.709Z  INFO myapp: creating a wuzzle (widget_type=wuzzle)
07:46:42.709Z  WARN myapp: This wuzzle is woosey. (widget_type=wuzzle)
07:46:42.709Z  INFO myapp: done
```

A more practical example is in the
[node-restify](https://github.com/mcavage/node-restify) web framework.
Restify uses Bunyan for its logging. One feature of its integration, is that
if `server.use(restify.requestLogger())` is used, each restify request handler
includes a `req.log` logger that is:

```js
log.child({req_id: <unique request id>}, true)
```

Apps using restify can then use `req.log` and have all such log records
include the unique request id (as "req\_id"). Handy.


## Serializers

Bunyan has a concept of **"serializer" functions to produce a JSON-able object
from a JavaScript object**, so you can easily do the following:

```js
log.info({req: <request object>}, 'something about handling this request');
```

and have the `req` entry in the log record be just a reasonable subset of
`<request object>` fields (or computed data about those fields).


A logger instance can have a `serializers` mapping of log record field name
("req" in this example) to a serializer function. When creating the log record,
Bunyan will call the serializer function for *top-level* fields of that name. An
example:

```js
function reqSerializer(req) {
    return {
        method: req.method,
        url: req.url,
        headers: req.headers
    };
}
var log = bunyan.createLogger({
    name: 'myapp',
    serializers: {
        req: reqSerializer
    }
});
```


Typically serializers are added to a logger at creation time via:

```js
var log = bunyan.createLogger({
    name: 'myapp',
    serializers: {
        foo: function fooSerializer(foo) { ... },
        ...
    }
});

// or
var log = bunyan.createLogger({
    name: 'myapp',
    serializers: bunyan.stdSerializers
});
```

Serializers can also be added after creation via `<logger>.addSerializers(...)`,
e.g.:

```js
var log = bunyan.createLogger({name: 'myapp'});
log.addSerializers({req: reqSerializer});
```

### Requirements for serializers functions

A serializer function is passed unprotected objects that are passed to the
`log.info`, `log.debug`, etc. call. This means a poorly written serializer
function can cause side-effects. Logging shouldn't do that. Here are a few
rules and best practices for serializer functions:

- A serializer function *should never throw*. The bunyan library *does*
  protect somewhat from this: if the serializer throws an error, then
  bunyan will (a) write an ugly message on stderr (along with the traceback),
  and (b) the field in the log record will be replaced with a short error message.
  For example:

    ```
    bunyan: ERROR: Exception thrown from the "foo" Bunyan serializer. This should never happen. This is a bug in that serializer function.
    TypeError: Cannot read property 'not' of undefined
        at Object.fooSerializer [as foo] (/Users/trentm/tm/node-bunyan/bar.js:8:26)
        at /Users/trentm/tm/node-bunyan/lib/bunyan.js:873:50
        at Array.forEach (native)
        at Logger._applySerializers (/Users/trentm/tm/node-bunyan/lib/bunyan.js:865:35)
        at mkRecord (/Users/trentm/tm/node-bunyan/lib/bunyan.js:978:17)
        at Logger.info (/Users/trentm/tm/node-bunyan/lib/bunyan.js:1044:19)
        at Object.<anonymous> (/Users/trentm/tm/node-bunyan/bar.js:13:5)
        at Module._compile (module.js:409:26)
        at Object.Module._extensions..js (module.js:416:10)
        at Module.load (module.js:343:32)
    {"name":"bar","hostname":"danger0.local","pid":47411,"level":30,"foo":"(Error in Bunyan log \"foo\" serializer broke field. See stderr for details.)","msg":"one","time":"2017-03-08T02:53:51.173Z","v":0}
    ```

- A serializer function *should never mutate the given object*. Doing so will
  change the object in your application.

- A serializer function *should be defensive*. In my experience, it is common to
  set a serializer in an app, say for field name "foo", and then accidentally
  have a log line that passes a "foo" that is undefined, or null, or of some
  unexpected type. A good start at defensiveness is to start with this:

    ```javascript
    function fooSerializer(foo) {
        // Guard against foo be null/undefined. Check that expected fields
        // are defined.
        if (!foo || !foo.bar)
            return foo;
        var obj = {
            // Create the object to be logged.
            bar: foo.bar
        }
        return obj;
    };
    ```


### Standard Serializers

Bunyan includes a small set of "standard serializers", exported as
`bunyan.stdSerializers`. Their use is completely optional. An example using
all of them:

```js
var log = bunyan.createLogger({
    name: 'myapp',
    serializers: bunyan.stdSerializers
});
```

or particular ones:

```js
var log = bunyan.createLogger({
    name: 'myapp',
    serializers: {err: bunyan.stdSerializers.err}
});
```

Standard serializers are:

| Field | Description |
| ----- | ----------- |
| err   | Used for serializing JavaScript error objects, including traversing an error's cause chain for error objects with a `.cause()` -- e.g. as from [verror](https://github.com/joyent/node-verror). |
| req   | Common fields from a node.js HTTP request object. |
| res   | Common fields from a node.js HTTP response object. |

Note that the `req` and `res` serializers intentionally do not include the
request/response *body*, as that can be prohibitively large. If helpful, the
[restify framework's audit logger plugin](https://github.com/restify/node-restify/blob/ac13902ad9716dcb20aaa62295403983075b1841/lib/plugins/audit.js#L38-L87)
has its own req/res serializers that include more information (optionally
including the body).


## src

The **source file, line and function of the log call site** can be added to
log records by using the `src: true` config option:

```js
var log = bunyan.createLogger({src: true, ...});
```

This adds the call source info with the 'src' field, like this:

```js
{
  "name": "src-example",
  "hostname": "banana.local",
  "pid": 123,
  "component": "wuzzle",
  "level": 4,
  "msg": "This wuzzle is woosey.",
  "time": "2012-02-06T04:19:35.605Z",
  "src": {
    "file": "/Users/trentm/tm/node-bunyan/examples/src.js",
    "line": 20,
    "func": "Wuzzle.woos"
  },
  "v": 0
}
```

**WARNING: Determining the call source info is slow. Never use this option
in production.**


# Levels

The log levels in bunyan are as follows. The level descriptions are best
practice *opinions* of the author.

- "fatal" (60): The service/app is going to stop or become unusable now.
  An operator should definitely look into this soon.
- "error" (50): Fatal for a particular request, but the service/app continues
  servicing other requests. An operator should look at this soon(ish).
- "warn" (40): A note on something that should probably be looked at by an
  operator eventually.
- "info" (30): Detail on regular operation.
- "debug" (20): Anything else, i.e. too verbose to be included in "info" level.
- "trace" (10): Logging from external libraries used by your app or *very*
  detailed application logging.

Setting a logger instance (or one of its streams) to a particular level implies
that all log records *at that level and above* are logged. E.g. a logger set to
level "info" will log records at level info and above (warn, error, fatal).

While using log level *names* is preferred, the actual level values are integers
internally (10 for "trace", ..., 60 for "fatal"). Constants are defined for
the levels: `bunyan.TRACE` ... `bunyan.FATAL`. The lowercase level names are
aliases supported in the API, e.g. `log.level("info")`. There is one exception:
DTrace integration uses the level names. The fired DTrace probes are named
'bunyan-$levelName'.

Here is the API for querying and changing levels on an existing logger.
Recall that a logger instance has an array of output "streams":

```js
log.level() -> INFO   // gets current level (lowest level of all streams)

log.level(INFO)       // set all streams to level INFO
log.level("info")     // set all streams to level INFO

log.levels() -> [DEBUG, INFO]   // get array of levels of all streams
log.levels(0) -> DEBUG          // get level of stream at index 0
log.levels("foo")               // get level of stream with name "foo"

log.levels(0, INFO)             // set level of stream 0 to INFO
log.levels(0, "info")           // can use "info" et al aliases
log.levels("foo", WARN)         // set stream named "foo" to WARN
```


## Level suggestions

Trent's biased suggestions for server apps: Use "debug" sparingly. Information
that will be useful to debug errors *post mortem* should usually be included in
"info" messages if it's generally relevant or else with the corresponding
"error" event. Don't rely on spewing mostly irrelevant debug messages all the
time and sifting through them when an error occurs.

Trent's biased suggestions for node.js libraries: IMHO, libraries should only
ever log at `trace`-level. Fine control over log output should be up to the
app using a library. Having a library that spews log output at higher levels
gets in the way of a clear story in the *app* logs.


# Log Record Fields

This section will describe *rules* for the Bunyan log format: field names,
field meanings, required fields, etc. However, a Bunyan library doesn't
strictly enforce all these rules while records are being emitted. For example,
Bunyan will add a `time` field with the correct format to your log records,
but you can specify your own. It is the caller's responsibility to specify
the appropriate format.

The reason for the above leniency is because IMO logging a message should
never break your app. This leads to this rule of logging: **a thrown
exception from `log.info(...)` or equivalent (other than for calling with the
incorrect signature) is always a bug in Bunyan.**


A typical Bunyan log record looks like this:

```js
{"name":"myserver","hostname":"banana.local","pid":123,"req":{"method":"GET","url":"/path?q=1#anchor","headers":{"x-hi":"Mom","connection":"close"}},"level":3,"msg":"start request","time":"2012-02-03T19:02:46.178Z","v":0}
```

Pretty-printed:

```js
{
  "name": "myserver",
  "hostname": "banana.local",
  "pid": 123,
  "req": {
    "method": "GET",
    "url": "/path?q=1#anchor",
    "headers": {
      "x-hi": "Mom",
      "connection": "close"
    },
    "remoteAddress": "120.0.0.1",
    "remotePort": 51244
  },
  "level": 3,
  "msg": "start request",
  "time": "2012-02-03T19:02:57.534Z",
  "v": 0
}
```

## Core fields

- `v`: Required. Integer. Added by Bunyan. Cannot be overridden.
  This is the Bunyan log format version (`require('bunyan').LOG_VERSION`).
  The log version is a single integer. `0` is until I release a version
  "1.0.0" of node-bunyan. Thereafter, starting with `1`, this will be
  incremented if there is any backward incompatible change to the log record
  format. Details will be in "CHANGES.md" (the change log).
- `level`: Required. Integer. Added by Bunyan. Cannot be overridden.
  See the "Levels" section.
- `name`: Required. String. Provided at Logger creation.
  You must specify a name for your logger when creating it. Typically this
  is the name of the service/app using Bunyan for logging.
- `hostname`: Required. String. Provided or determined at Logger creation.
  You can specify your hostname at Logger creation or it will be retrieved
  via `os.hostname()`.
- `pid`: Required. Integer. Filled in automatically at Logger creation.
- `time`: Required. String. Added by Bunyan. Can be overridden.
  The date and time of the event in [ISO 8601
  Extended Format](http://en.wikipedia.org/wiki/ISO_8601) format and in UTC,
  as from
  [`Date.toISOString()`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Date/toISOString).
- `msg`: Required. String.
  Every `log.debug(...)` et al call must provide a log message.
- `src`: Optional. Object giving log call source info. This is added
  automatically by Bunyan if the "src: true" config option is given to the
  Logger. Never use in production as this is really slow.


Go ahead and add more fields, and nested ones are fine (and recommended) as
well. This is why we're using JSON. Some suggestions and best practices
follow (feedback from actual users welcome).


## Recommended/Best Practice Fields

- `err`: Object. A caught JS exception. Log that thing with `log.info(err)`
    to get:

    ```js
    ...
    "err": {
      "message": "boom",
      "name": "TypeError",
      "stack": "TypeError: boom\n    at Object.<anonymous> ..."
    },
    "msg": "boom",
    ...
    ```

    Or use the `bunyan.stdSerializers.err` serializer in your Logger and
    do this `log.error({err: err}, "oops")`. See "examples/err.js".

- `req_id`: String. A request identifier. Including this field in all logging
  tied to handling a particular request to your server is strongly suggested.
  This allows post analysis of logs to easily collate all related logging
  for a request. This really shines when you have a SOA with multiple services
  and you carry a single request ID from the top API down through all APIs
  (as [node-restify](https://github.com/mcavage/node-restify) facilitates
  with its 'Request-Id' header).

- `req`: An HTTP server request. Bunyan provides `bunyan.stdSerializers.req`
  to serialize a request with a suggested set of keys. Example:

    ```js
    {
      "method": "GET",
      "url": "/path?q=1#anchor",
      "headers": {
        "x-hi": "Mom",
        "connection": "close"
      },
      "remoteAddress": "120.0.0.1",
      "remotePort": 51244
    }
    ```

- `res`: An HTTP server response. Bunyan provides `bunyan.stdSerializers.res`
  to serialize a response with a suggested set of keys. Example:

    ```js
    {
      "statusCode": 200,
      "header": "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nConnection: keep-alive\r\nTransfer-Encoding: chunked\r\n\r\n"
    }
    ```

## Other fields to consider

- `req.username`: Authenticated user (or for a 401, the user attempting to
  auth).
- Some mechanism to calculate response latency. "restify" users will have
  an "X-Response-Time" header. A `latency` custom field would be fine.
- `req.body`: If you know that request bodies are small (common in APIs,
  for example), then logging the request body is good.


# Streams

A "stream" is Bunyan's name for where it outputs log messages (the equivalent
to a log4j Appender). Ultimately Bunyan uses a
[Writable Stream](https://nodejs.org/docs/latest/api/all.html#writable_Stream)
interface, but there are some additional attributes used to create and
manage the stream. A Bunyan Logger instance has one or more streams.
In general streams are specified with the "streams" option:

```js
var bunyan = require('bunyan');
var log = bunyan.createLogger({
    name: "foo",
    streams: [
        {
            stream: process.stderr,
            level: "debug"
        },
        ...
    ]
});
```

For convenience, if there is only one stream, it can be specified with the
"stream" and "level" options (internally converted to a `Logger.streams`).

```js
var log = bunyan.createLogger({
    name: "foo",
    stream: process.stderr,
    level: "debug"
});
```

Note that "file" streams do not support this shortcut (partly for historical
reasons and partly to not make it difficult to add a literal "path" field
on log records).

If neither "streams" nor "stream" are specified, the default is a stream of
type "stream" emitting to `process.stdout` at the "info" level.

## Adding a Stream

After a bunyan instance has been initialized, you may add additional streams by
calling the `addStream` function.

```js
var bunyan = require('bunyan');
var log = bunyan.createLogger('myLogger');
log.addStream({
  name: "myNewStream",
  stream: process.stderr,
  level: "debug"
});
```

## stream errors

A Bunyan logger instance can be made to re-emit "error" events from its
streams. Bunyan does so by default for [`type === "file"`
streams](#stream-type-file), so you can do this:

```js
var log = bunyan.createLogger({name: 'mylog', streams: [{path: LOG_PATH}]});
log.on('error', function (err, stream) {
    // Handle stream write or create error here.
});
```

As of bunyan@1.7.0, the `reemitErrorEvents` field can be used when adding a
stream to control whether "error" events are re-emitted on the Logger. For
example:

    var EventEmitter = require('events').EventEmitter;
    var util = require('util');

    function MyFlakyStream() {}
    util.inherits(MyFlakyStream, EventEmitter);

    MyFlakyStream.prototype.write = function (rec) {
        this.emit('error', new Error('boom'));
    }

    var log = bunyan.createLogger({
        name: 'this-is-flaky',
        streams: [
            {
                type: 'raw',
                stream: new MyFlakyStream(),
                reemitErrorEvents: true
            }
        ]
    });
    log.info('hi there');

The behaviour is as follows:

- `reemitErrorEvents` not specified: `file` streams will re-emit error events
  on the Logger instance.
- `reemitErrorEvents: true`: error events will be re-emitted on the Logger
  for any stream with a `.on()` function -- which includes file streams,
  process.stdout/stderr, and any object that inherits from EventEmitter.
- `reemitErrorEvents: false`: error events will not be re-emitted for any
  streams.

Note: "error" events are **not** related to log records at the "error" level
as produced by `log.error(...)`. See [the node.js docs on error
events](https://nodejs.org/api/events.html#events_error_events) for details.


## stream type: `stream`

A `type === 'stream'` is a plain ol' node.js [Writable
Stream](http://nodejs.org/docs/latest/api/all.html#writable_Stream). A
"stream" (the writable stream) field is required. E.g.: `process.stdout`,
`process.stderr`.

```js
var log = bunyan.createLogger({
    name: 'foo',
    streams: [{
        stream: process.stderr
        // `type: 'stream'` is implied
    }]
});
```

<table>
<tr>
<th>Field</th>
<th>Required?</th>
<th>Default</th>
<th>Description</th>
</tr>
<tr>
<td>stream</td>
<td>Yes</td>
<td>-</td>
<td>A "Writable Stream", e.g. a std handle or an open file write stream.</td>
</tr>
<tr>
<td>type</td>
<td>No</td>
<td>n/a</td>
<td>`type == 'stream'` is implied if the `stream` field is given.</td>
</tr>
<tr>
<td>level</td>
<td>No</td>
<td>info</td>
<td>The level to which logging to this stream is enabled. If not
specified it defaults to "info". If specified this can be one of the
level strings ("trace", "debug", ...) or constants (`bunyan.TRACE`,
`bunyan.DEBUG`, ...). This serves as a severity threshold for that stream
so logs of greater severity will also pass through (i.e. If level="warn",
error and fatal will also pass through this stream).</td>
</tr>
<tr>
<td>name</td>
<td>No</td>
<td>-</td>
<td>A name for this stream. This may be useful for usage of `log.level(NAME,
LEVEL)`. See the [Levels section](#levels) for details. A stream "name" isn't
used for anything else.</td>
</tr>
</table>


## stream type: `file`

A `type === 'file'` stream requires a "path" field. Bunyan will open this
file for appending. E.g.:

```js
var log = bunyan.createLogger({
    name: 'foo',
    streams: [{
        path: '/var/log/foo.log',
        // `type: 'file'` is implied
    }]
});
```

<table>
<tr>
<th>Field</th>
<th>Required?</th>
<th>Default</th>
<th>Description</th>
</tr>
<tr>
<td>path</td>
<td>Yes</td>
<td>-</td>
<td>A file path to which to log.</td>
</tr>
<tr>
<td>type</td>
<td>No</td>
<td>n/a</td>
<td>`type == 'file'` is implied if the `path` field is given.</td>
</tr>
<tr>
<td>level</td>
<td>No</td>
<td>info</td>
<td>The level to which logging to this stream is enabled. If not
specified it defaults to "info". If specified this can be one of the
level strings ("trace", "debug", ...) or constants (`bunyan.TRACE`,
`bunyan.DEBUG`, ...). This serves as a severity threshold for that
stream so logs of greater severity will also pass through (i.e. If
level="warn", error and fatal will also pass through this stream).</td>
</tr>
<tr>
<td>name</td>
<td>No</td>
<td>-</td>
<td>A name for this stream. This may be useful for usage of `log.level(NAME,
LEVEL)`. See the [Levels section](#levels) for details. A stream "name" isn't
used for anything else.</td>
</tr>
</table>


## stream type: `rotating-file`

**WARNING on node 0.8 usage:** Users of Bunyan's `rotating-file` should (a) be
using at least bunyan 0.23.1 (with the fix for [this
issue](https://github.com/trentm/node-bunyan/pull/97)), and (b) should use at
least node 0.10 (node 0.8 does not support the `unref()` method on
`setTimeout(...)` needed for the mentioned fix). The symptom is that process
termination will hang for up to a full rotation period.

**WARNING on [cluster](http://nodejs.org/docs/latest/api/all.html#all_cluster)
usage:** Using Bunyan's `rotating-file` stream with node.js's "cluster" module
can result in unexpected file rotation. You must not have multiple processes
in the cluster logging to the same file path. In other words, you must have
a separate log file path for the master and each worker in the cluster.
Alternatively, consider using a system file rotation facility such as
`logrotate` on Linux or `logadm` on SmartOS/Illumos. See
[this comment on issue #117](https://github.com/trentm/node-bunyan/issues/117#issuecomment-44804938)
for details.

A `type === 'rotating-file'` is a file stream that handles file automatic
rotation.

```js
var log = bunyan.createLogger({
    name: 'foo',
    streams: [{
        type: 'rotating-file',
        path: '/var/log/foo.log',
        period: '1d',   // daily rotation
        count: 3        // keep 3 back copies
    }]
});
```

This will rotate '/var/log/foo.log' every day (at midnight) to:

```sh
/var/log/foo.log.0     # yesterday
/var/log/foo.log.1     # 1 day ago
/var/log/foo.log.2     # 2 days ago
```

*Currently*, there is no support for providing a template for the rotated
files, or for rotating when the log reaches a threshold size.

<table>
<tr>
<th>Field</th>
<th>Required?</th>
<th>Default</th>
<th>Description</th>
</tr>
<tr>
<td>type</td>
<td>Yes</td>
<td>-</td>
<td>"rotating-file"</td>
</tr>
<tr>
<td>path</td>
<td>Yes</td>
<td>-</td>
<td>A file path to which to log. Rotated files will be "$path.0",
"$path.1", ...</td>
</tr>
<tr>
<td>period</td>
<td>No</td>
<td>1d</td>
<td>The period at which to rotate. This is a string of the format
"$number$scope" where "$scope" is one of "ms" (milliseconds -- only useful for
testing), "h" (hours), "d" (days), "w" (weeks), "m" (months), "y" (years). Or
one of the following names can be used "hourly" (means 1h), "daily" (1d),
"weekly" (1w), "monthly" (1m), "yearly" (1y). Rotation is done at the start of
the scope: top of the hour (h), midnight (d), start of Sunday (w), start of the
1st of the month (m), start of Jan 1st (y).</td>
</tr>
<tr>
<td>count</td>
<td>No</td>
<td>10</td>
<td>The number of rotated files to keep.</td>
</tr>
<tr>
<td>level</td>
<td>No</td>
<td>info</td>
<td>The level at which logging to this stream is enabled. If not
specified it defaults to "info". If specified this can be one of the
level strings ("trace", "debug", ...) or constants (`bunyan.TRACE`,
`bunyan.DEBUG`, ...).</td>
</tr>
<tr>
<td>name</td>
<td>No</td>
<td>-</td>
<td>A name for this stream. This may be useful for usage of `log.level(NAME,
LEVEL)`. See the [Levels section](#levels) for details. A stream "name" isn't
used for anything else.</td>
</tr>
</table>


**Note on log rotation**: Often you may be using external log rotation utilities
like `logrotate` on Linux or `logadm` on SmartOS/Illumos. In those cases, unless
you are ensuring "copy and truncate" semantics (via `copytruncate` with
logrotate or `-c` with logadm) then the fd for your 'file' stream will change.
You can tell bunyan to reopen the file stream with code like this in your
app:

```js
var log = bunyan.createLogger(...);
...
process.on('SIGUSR2', function () {
    log.reopenFileStreams();
});
```

where you'd configure your log rotation to send SIGUSR2 (or some other signal)
to your process. Any other mechanism to signal your app to run
`log.reopenFileStreams()` would work as well.


## stream type: `raw`

- `raw`: Similar to a "stream" writable stream, except that the write method
  is given raw log record *Object*s instead of a JSON-stringified string.
  This can be useful for hooking on further processing to all Bunyan logging:
  pushing to an external service, a RingBuffer (see below), etc.



## `raw` + RingBuffer Stream

Bunyan comes with a special stream called a RingBuffer which keeps the last N
records in memory and does *not* write the data anywhere else.  One common
strategy is to log 'info' and higher to a normal log file but log all records
(including 'trace') to a ringbuffer that you can access via a debugger, or your
own HTTP interface, or a post-mortem facility like MDB or node-panic.

To use a RingBuffer:

```js
/* Create a ring buffer that stores the last 100 records. */
var bunyan = require('bunyan');
var ringbuffer = new bunyan.RingBuffer({ limit: 100 });
var log = bunyan.createLogger({
    name: 'foo',
    streams: [
        {
            level: 'info',
            stream: process.stdout
        },
        {
            level: 'trace',
            type: 'raw',    // use 'raw' to get raw log record objects
            stream: ringbuffer
        }
    ]
});

log.info('hello world');
console.log(ringbuffer.records);
```

This example emits:

```js
[ { name: 'foo',
    hostname: '912d2b29',
    pid: 50346,
    level: 30,
    msg: 'hello world',
    time: '2012-06-19T21:34:19.906Z',
    v: 0 } ]
```

## third-party streams

See the [user-maintained list in the Bunyan
wiki](https://github.com/trentm/node-bunyan/wiki/Awesome-Bunyan).


# Runtime log snooping via DTrace

On systems that support DTrace (e.g., illumos derivatives like SmartOS and
OmniOS, FreeBSD, Mac), Bunyan will create a DTrace provider (`bunyan`) that
makes available the following probes:

```sh
log-trace
log-debug
log-info
log-warn
log-error
log-fatal
```

Each of these probes has a single argument: the string that would be
written to the log.  Note that when a probe is enabled, it will
fire whenever the corresponding function is called, even if the level of
the log message is less than that of any stream.


## DTrace examples

Trace all log messages coming from any Bunyan module on the system.
(The `-x strsize=4k` is to raise dtrace's default 256 byte buffer size
because log messages are longer than typical dtrace probes.)

```sh
dtrace -x strsize=4k -qn 'bunyan*:::log-*{printf("%d: %s: %s", pid, probefunc, copyinstr(arg0))}'
```

Trace all log messages coming from the "wuzzle" component:

```sh
dtrace -x strsize=4k -qn 'bunyan*:::log-*/strstr(this->str = copyinstr(arg0), "\"component\":\"wuzzle\"") != NULL/{printf("%s", this->str)}'
```

Aggregate debug messages from process 1234, by message:

```sh
dtrace -x strsize=4k -n 'bunyan1234:::log-debug{@[copyinstr(arg0)] = count()}'
```

Have the bunyan CLI pretty-print the traced logs:

```sh
dtrace -x strsize=4k -qn 'bunyan1234:::log-*{printf("%s", copyinstr(arg0))}' | bunyan
```

A convenience handle has been made for this:

```sh
bunyan -p 1234
```

On systems that support the
[`jstack`](http://dtrace.org/blogs/dap/2012/04/25/profiling-node-js/) action
via a node.js helper, get a stack backtrace for any debug message that
includes the string "danger!":

```sh
dtrace -x strsize=4k -qn 'log-debug/strstr(copyinstr(arg0), "danger!") != NULL/{printf("\n%s", copyinstr(arg0)); jstack()}'
```

Output of the above might be:

```
{"name":"foo","hostname":"763bf293-d65c-42d5-872b-4abe25d5c4c7.local","pid":12747,"level":20,"msg":"danger!","time":"2012-10-30T18:28:57.115Z","v":0}

          node`0x87e2010
          DTraceProviderBindings.node`usdt_fire_probe+0x32
          DTraceProviderBindings.node`_ZN4node11DTraceProbe5_fireEN2v85LocalINS1_5ValueEEE+0x32d
          DTraceProviderBindings.node`_ZN4node11DTraceProbe4FireERKN2v89ArgumentsE+0x77
          << internal code >>
          (anon) as (anon) at /root/node-bunyan/lib/bunyan.js position 40484
          << adaptor >>
          (anon) as doit at /root/my-prog.js position 360
          (anon) as list.ontimeout at timers.js position 4960
          << adaptor >>
          << internal >>
          << entry >>
          node`_ZN2v88internalL6InvokeEbNS0_6HandleINS0_10JSFunctionEEENS1_INS0_6ObjectEEEiPS5_Pb+0x101
          node`_ZN2v88internal9Execution4CallENS0_6HandleINS0_6ObjectEEES4_iPS4_Pbb+0xcb
          node`_ZN2v88Function4CallENS_6HandleINS_6ObjectEEEiPNS1_INS_5ValueEEE+0xf0
          node`_ZN4node12MakeCallbackEN2v86HandleINS0_6ObjectEEENS1_INS0_8FunctionEEEiPNS1_INS0_5ValueEEE+0x11f
          node`_ZN4node12MakeCallbackEN2v86HandleINS0_6ObjectEEENS1_INS0_6StringEEEiPNS1_INS0_5ValueEEE+0x66
          node`_ZN4node9TimerWrap9OnTimeoutEP10uv_timer_si+0x63
          node`uv__run_timers+0x66
          node`uv__run+0x1b
          node`uv_run+0x17
          node`_ZN4node5StartEiPPc+0x1d0
          node`main+0x1b
          node`_start+0x83

          node`0x87e2010
          DTraceProviderBindings.node`usdt_fire_probe+0x32
          DTraceProviderBindings.node`_ZN4node11DTraceProbe5_fireEN2v85LocalINS1_5ValueEEE+0x32d
          DTraceProviderBindings.node`_ZN4node11DTraceProbe4FireERKN2v89ArgumentsE+0x77
          << internal code >>
          (anon) as (anon) at /root/node-bunyan/lib/bunyan.js position 40484
          << adaptor >>
          (anon) as doit at /root/my-prog.js position 360
          (anon) as list.ontimeout at timers.js position 4960
          << adaptor >>
          << internal >>
          << entry >>
          node`_ZN2v88internalL6InvokeEbNS0_6HandleINS0_10JSFunctionEEENS1_INS0_6ObjectEEEiPS5_Pb+0x101
          node`_ZN2v88internal9Execution4CallENS0_6HandleINS0_6ObjectEEES4_iPS4_Pbb+0xcb
          node`_ZN2v88Function4CallENS_6HandleINS_6ObjectEEEiPNS1_INS_5ValueEEE+0xf0
          node`_ZN4node12MakeCallbackEN2v86HandleINS0_6ObjectEEENS1_INS0_8FunctionEEEiPNS1_INS0_5ValueEEE+0x11f
          node`_ZN4node12MakeCallbackEN2v86HandleINS0_6ObjectEEENS1_INS0_6StringEEEiPNS1_INS0_5ValueEEE+0x66
          node`_ZN4node9TimerWrap9OnTimeoutEP10uv_timer_si+0x63
          node`uv__run_timers+0x66
          node`uv__run+0x1b
          node`uv_run+0x17
          node`_ZN4node5StartEiPPc+0x1d0
          node`main+0x1b
          node`_start+0x83
```


# Runtime environments

Node-bunyan supports running in a few runtime environments:

- [Node.js](https://nodejs.org/)
- [Browserify](http://browserify.org/): See the
  [Browserify section](#browserify) below.
- [Webpack](https://webpack.github.io/): See the [Webpack section](#webpack) below.
- [NW.js](http://nwjs.io/)

Support for other runtime environments is welcome. If you have suggestions,
fixes, or mentions that node-bunyan already works in some other JavaScript
runtime, please open an [issue](https://github.com/trentm/node-bunyan/issues/new)
or a pull request.

The primary target is Node.js. It is the only environment in which I
regularly test. If you have suggestions for how to automate testing for other
environments, I'd appreciate feedback on [this automated testing
issue](https://github.com/trentm/node-bunyan/issues/342).

## Browserify

As the [Browserify](http://browserify.org/) site says it "lets you
`require('modules')` in the browser by bundling up all of your dependencies."
It is a build tool to run on your node.js script to bundle up your script and
all its node.js dependencies into a single file that is runnable in the
browser via:

```html
<script src="play.browser.js"></script>
```

As of version 1.1.0, node-bunyan supports being run via Browserify. The
default [stream](#streams) when running in the browser is one that emits
raw log records to `console.log/info/warn/error`.

Here is a quick example showing you how you can get this working for your
script.

1. Get browserify and bunyan installed in your module:

    ```sh
    $ npm install browserify bunyan
    ```

2. An example script using Bunyan, "play.js":

    ```js
    var bunyan = require('bunyan');
    var log = bunyan.createLogger({name: 'play', level: 'debug'});
    log.trace('this one does not emit');
    log.debug('hi on debug');   // console.log
    log.info('hi on info');     // console.info
    log.warn('hi on warn');     // console.warn
    log.error('hi on error');   // console.error
    ```

3. Build this into a bundle to run in the browser, "play.browser.js":

    ```sh
    $ ./node_modules/.bin/browserify play.js -o play.browser.js
    ```

4. Put that into an HTML file, "play.html":

    ```html
    <!DOCTYPE html>
    <html>
    <head>
      <meta charset="utf-8">
      <script src="play.browser.js"></script>
    </head>
    <body>
      <div>hi</div>
    </body>
    </html>
    ```

5. Open that in your browser and open your browser console:

    ```sh
    $ open play.html
    ```

Here is what it looks like in Firefox's console: ![Bunyan + Browserify in the
Firefox console](./docs/img/bunyan.browserify.png)

For some, the raw log records might not be desired. To have a rendered log line
you'll want to add your own stream, starting with something like this:

```js
var bunyan = require('./lib/bunyan');

function MyRawStream() {}
MyRawStream.prototype.write = function (rec) {
    console.log('[%s] %s: %s',
        rec.time.toISOString(),
        bunyan.nameFromLevel[rec.level],
        rec.msg);
}

var log = bunyan.createLogger({
    name: 'play',
    streams: [
        {
            level: 'info',
            stream: new MyRawStream(),
            type: 'raw'
        }
    ]
});

log.info('hi on info');
```

## webpack
To include bunyan in your webpack bundle you need to tell webpack to
ignore the optional dependencies that are unavailable in browser environments.

Mark the following dependencies as
[externals](https://webpack.js.org/configuration/externals/) in your webpack
configuration file to exclude them from the bundle:

```
module: {
    externals: ['dtrace-provider', 'fs', 'mv', 'os', 'source-map-support']
}
```

# Versioning

All versions are `<major>.<minor>.<patch>` which will be incremented for
breaking backward compat and major reworks, new features without breaking
change, and bug fixes, respectively. tl;dr: [Semantic
versioning](http://semver.org/).

# License

[MIT](./LICENSE.txt).

# See Also

See the [user-maintained list of Bunyan-related software in the Bunyan
wiki](https://github.com/trentm/node-bunyan/wiki/Awesome-Bunyan).


================================================
FILE: TODO.md
================================================
# v2

- `createLogger(<config-and-fields>, <fields>)` changes (#460)
    - see section below
- the dtrace-provider thing (#487)
    TODO: answer Cody email
- use package.json version for VERSION
- use deps
    - dashdash
    - assert-plus?
    - verror?
- break out to multiple files
    - want to work through PRs before that, so don't just break them all
- TODO: a quick pass through tickets and pulls for other things to include
- get ticket refs for the above, if any
- formatters: read up again on `glp master..1.x`
- support for customer formatters
    - for the CLI as well? How? ~/.bunyanrc?
- if doing ~/.bunyanrc, then consider color schemes



# changes to ctor and log.child to separate fields from config

<https://github.com/trentm/node-bunyan/issues/460>

Current:

    createLogger(<config-and-fields>)
    log.child(<config-and-fields>, <just-fields-bool>)

Could be:

    createLogger(<config-and-fields>, <fields>)
    log.child(<config-and-fields>, <fields>)
        # Still support: log.child(<config-and-fields>, <just-fields-bool>)

Pros: Compat issues are minimal: a change is only required if there is a
collision with used field and a new config var name.
Cons: A *slight* con is that my guess is the common usage of child is
`log.child(<fields>)`, so the more future-proof common usage becomes:

    log.child(null, <fields>)

That's not too bad. It is clearer at least than:

    log.child(<fields>, true)

TODO:

- is there a ticket for this work already?  #460
- make the change
- do a migration guide? i.e. provide the grep commands to find all
  possible calls to inspect. E.g. if don't have `rg logUndefined` in your
  code, then you are fine. And one time future-proofing via changing
  to fields in the *second* arg.
- list of issues/pulls that wanted to add new config fields


# higher prio

- published organized advice for
  https://github.com/trentm/node-bunyan/issues/37 so can close that out.
  Perhaps a wiki page with examples and strategies.
- man page for the bunyan CLI (refer to it in the readme)
    - perhaps wait for a bunyan new version with deps, and use dashdash
      with a (vapour) man page generator


# docs

- document log.addStream() and log.addSerializers()


# someday/maybe

- 2.0 (?) with `v: 1` in log records. Fwd/bwd compat in `bunyan` CLI
- `tail -f`-like support
- full-on docs
- better examples/
- better coloring
- look at pino (bunyan style, perf benefits), also logpp (https://github.com/mrkmarron/logpp)
- would be exciting to have bunyan support in http://lnav.org/ if that
  made sense
- "template" support for 'rotating-file' stream to get dated rolled files
- "all" or "off" levels? log4j? logging.py?
  logging.py has NOTSET === 0. I think that is only needed/used for
  multi-level hierarchical effective level.
- buffered writes to increase speed:
    - I'd start with a tools/timeoutput.js for some numbers to compare
      before/after. Sustained high output to a file.
    - perhaps this would be a "buffered: true" option on the stream object
    - then wrap the "stream" with a local class that handles the buffering
    - to finish this, need the 'log.close' and `process.on('exit', ...)`
      work that Trent has started.
- "canWrite" handling for full streams. Need to buffer a la log4js
- test file log with logadm rotation: does it handle that?
- test suite:
    - test for a cloned logger double-`stream.end()` causing problems.
      Perhaps the "closeOnExit" for existing streams should be false for
      clones.
    - test that a `log.clone(...)` adding a new field matching a serializer
      works *and* that an existing field in the parent is not *re-serialized*.
- split out `bunyan` cli to a "bunyan" or "bunyan-reader" or "node-bunyan-reader"
  as the basis for tools to consume bunyan logs. It can grow indep of node-bunyan
  for generating the logs.
  It would take a Bunyan log record object and be expected to emit it.

        node-bunyan-reader
            .createReadStream(path, [options]) ?

- coloring bug: in less the indented extra info lines only have the first
  line colored. Do we need the ANSI char on *each* line? That'll be
  slower.
- document "well-known" keys from bunyan CLI p.o.v.. Add "client_req".
- More `bunyan` output formats and filtering features.
- Think about a bunyan dashboard that supports organizing and viewing logs
  from multiple hosts and services.
- doc the restify RequestCaptureStream usage of RingBuffer. Great example.
- A vim plugin (a la http://vim.cybermirror.org/runtime/autoload/zip.vim ?) to
  allow browsing (read-only) a bunyan log in rendered form.
- Some speed comparisons with others to get a feel for Bunyan's speed.
- what about promoting 'latency' field and making that easier?
- `log.close` to close streams and shutdown and `this.closed`
  process.on('exit', log.close)
  -> 'end' for the name
- bunyan cli: more layouts (http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/EnhancedPatternLayout.html)
  Custom log formats (in config file? in '-f' arg) using printf or hogan.js
  or whatever. Dap wants field width control for lining up. Hogan.js is
  probably overkill for this.
- loggly example using raw streams, hook.io?, whatever.
- serializer support:
    - restify-server.js example -> restifyReq ? or have `req` detect that.
      That is nicer for the "use all standard ones". *Does* restify req
      have anything special?
    - differential HTTP *client* req/res with *server* req/res.
- statsd stream? http://codeascraft.etsy.com/2011/02/15/measure-anything-measure-everything/
  Think about it.
- web ui. Ideas: http://googlecloudplatform.blogspot.ca/2014/04/a-new-logs-viewer-for-google-cloud.html


================================================
FILE: bin/bunyan
================================================
#!/usr/bin/env node
/**
 * Copyright 2020 Trent Mick
 * Copyright 2020 Joyent Inc.
 *
 * bunyan -- filter and pretty-print Bunyan log files (line-delimited JSON)
 *
 * See <https://github.com/trentm/node-bunyan>.
 *
 * -*- mode: js -*-
 * vim: expandtab:ts=4:sw=4
 */

var p = console.log;
var util = require('util');
var pathlib = require('path');
var vm = require('vm');
var http = require('http');
var fs = require('fs');
var warn = console.warn;
var child_process = require('child_process'),
    spawn = child_process.spawn,
    exec = child_process.exec,
    execFile = child_process.execFile;
var assert = require('assert');

var exeunt = require('exeunt');

try {
    var moment = require('moment');
} catch (e) {
    moment = null;
}


//---- globals and constants

var nodeVer = process.versions.node.split('.').map(Number);
var nodeSpawnSupportsStdio = (nodeVer[0] > 0 || nodeVer[1] >= 8);

// Internal debug logging via `console.warn`.
var _selfTrace = function selfTraceNoop() {};
if (process.env.BUNYAN_SELF_TRACE === '1') {
    _selfTrace = function selfTrace() {
        process.stderr.write('[bunyan self-trace] ');
        console.warn.apply(null, arguments);
    }
}

// Output modes.
var OM_LONG = 1;
var OM_JSON = 2;
var OM_INSPECT = 3;
var OM_SIMPLE = 4;
var OM_SHORT = 5;
var OM_BUNYAN = 6;
var OM_FROM_NAME = {
    'long': OM_LONG,
    'paul': OM_LONG,  /* backward compat */
    'json': OM_JSON,
    'inspect': OM_INSPECT,
    'simple': OM_SIMPLE,
    'short': OM_SHORT,
    'bunyan': OM_BUNYAN
};


// Levels
var TRACE = 10;
var DEBUG = 20;
var INFO = 30;
var WARN = 40;
var ERROR = 50;
var FATAL = 60;

var levelFromName = {
    'trace': TRACE,
    'debug': DEBUG,
    'info': INFO,
    'warn': WARN,
    'error': ERROR,
    'fatal': FATAL
};
var nameFromLevel = {};
var upperNameFromLevel = {};
var upperPaddedNameFromLevel = {};
Object.keys(levelFromName).forEach(function (name) {
    var lvl = levelFromName[name];
    nameFromLevel[lvl] = name;
    upperNameFromLevel[lvl] = name.toUpperCase();
    upperPaddedNameFromLevel[lvl] = (
        name.length === 4 ? ' ' : '') + name.toUpperCase();
});


// Display time formats.
var TIME_UTC = 1;  // the default, bunyan's native format
var TIME_LOCAL = 2;

// Timezone formats: output format -> momentjs format string
var TIMEZONE_UTC_FORMATS = {
    long:  '[[]YYYY-MM-DD[T]HH:mm:ss.SSS[Z][]]',
    short: 'HH:mm:ss.SSS[Z]'
};
var TIMEZONE_LOCAL_FORMATS = {
    long:  '[[]YYYY-MM-DD[T]HH:mm:ss.SSSZ[]]',
    short: 'HH:mm:ss.SSS'
};


// Boolean set to true when we are in the process of exiting. We don't always
// hard-exit (e.g. when staying alive while the pager completes).
var exiting = false;

// The current raw input line being processed. Used for `uncaughtException`.
var currLine = null;

// Child dtrace process, if any. Used for signal-handling.
var child = null;

// Whether ANSI codes are being used. Used for signal-handling.
var usingAnsiCodes = false;

// Used to tell the 'uncaughtException' handler that '-c CODE' is being used.
var gUsingConditionOpts = false;

// Pager child process, and output stream to which to write.
var pager = null;
var stdout = process.stdout;



//---- support functions

var _version = null;
function getVersion() {
    if (_version === null) {
        _version = require('../package.json').version;
    }
    return _version;
}


var format = util.format;
if (!format) {
    /* BEGIN JSSTYLED */
    // If not node 0.6, then use its `util.format`:
    // <https://github.com/joyent/node/blob/master/lib/util.js#L22>:
    var inspect = util.inspect;
    var formatRegExp = /%[sdj%]/g;
    format = function format(f) {
        if (typeof f !== 'string') {
            var objects = [];
            for (var i = 0; i < arguments.length; i++) {
                objects.push(inspect(arguments[i]));
            }
            return objects.join(' ');
        }

        var i = 1;
        var args = arguments;
        var len = args.length;
        var str = String(f).replace(formatRegExp, function (x) {
            if (i >= len)
                return x;
            switch (x) {
                case '%s': return String(args[i++]);
                case '%d': return Number(args[i++]);
                case '%j': return JSON.stringify(args[i++]);
                case '%%': return '%';
                default:
                    return x;
            }
        });
        for (var x = args[i]; i < len; x = args[++i]) {
            if (x === null || typeof x !== 'object') {
                str += ' ' + x;
            } else {
                str += ' ' + inspect(x);
            }
        }
        return str;
    };
    /* END JSSTYLED */
}

function indent(s) {
    return '    ' + s.split(/\r?\n/).join('\n    ');
}

function objCopy(obj) {
    if (obj === null) {
        return null;
    } else if (Array.isArray(obj)) {
        return obj.slice();
    } else {
        var copy = {};
        Object.keys(obj).forEach(function (k) {
            copy[k] = obj[k];
        });
        return copy;
    }
}

function printHelp() {
    /* BEGIN JSSTYLED */
    p('Usage:');
    p('  bunyan [OPTIONS] [FILE ...]');
    p('  ... | bunyan [OPTIONS]');
    p('  bunyan [OPTIONS] -p PID');
    p('');
    p('Filter and pretty-print Bunyan log file content.');
    p('');
    p('General options:');
    p('  -h, --help    print this help info and exit');
    p('  --version     print version of this command and exit');
    p('');
    p('Runtime log snooping (via DTrace, only on supported platforms):');
    p('  -p PID        Process bunyan:log-* probes from the process');
    p('                with the given PID. Can be used multiple times,');
    p('                or specify all processes with "*", or a set of');
    p('                processes whose command & args match a pattern');
    p('                with "-p NAME".');
    p('');
    p('Filtering options:');
    p('  -l, --level LEVEL');
    p('                Only show messages at or above the specified level.');
    p('                You can specify level *names* or the internal numeric');
    p('                values.');
    p('  -c, --condition CONDITION');
    p('                Run each log message through the condition and');
    p('                only show those that return truish. E.g.:');
    p('                    -c \'this.pid == 123\'');
    p('                    -c \'this.level == DEBUG\'');
    p('                    -c \'this.msg.indexOf("boom") != -1\'');
    p('                "CONDITION" must be legal JS code. `this` holds');
    p('                the log record. The TRACE, DEBUG, ... FATAL values');
    p('                are defined to help with comparing `this.level`.');
    p('  --strict      Suppress all but legal Bunyan JSON log lines. By default');
    p('                non-JSON, and non-Bunyan lines are passed through.');
    p('');
    p('Output options:');
    p('  --pager       Pipe output into `less` (or $PAGER if set), if');
    p('                stdout is a TTY. This overrides $BUNYAN_NO_PAGER.');
    p('                Note: Paging is only supported on node >=0.8.');
    p('  --no-pager    Do not pipe output into a pager.');
    p('  --color       Colorize output. Defaults to try if output');
    p('                stream is a TTY.');
    p('  --no-color    Force no coloring (e.g. terminal doesn\'t support it)');
    p('  -o, --output MODE');
    p('                Specify an output mode/format. One of');
    p('                  long: (the default) pretty');
    p('                  json: JSON output, 2-space indent');
    p('                  json-N: JSON output, N-space indent, e.g. "json-4"');
    p('                  bunyan: 0 indented JSON, bunyan\'s native format');
    p('                  inspect: node.js `util.inspect` output');
    p('                  short: like "long", but more concise');
    p('                  simple: level, followed by "-" and then the message');
    p('  -j            shortcut for `-o json`');
    p('  -0            shortcut for `-o bunyan`');
    p('  -L, --time local');
    p('                Display time field in local time, rather than UTC.');
    p('');
    p('Environment Variables:');
    p('  BUNYAN_NO_COLOR    Set to a non-empty value to force no output ');
    p('                     coloring. See "--no-color".');
    p('  BUNYAN_NO_PAGER    Disable piping output to a pager. ');
    p('                     See "--no-pager".');
    p('');
    p('See <https://github.com/trentm/node-bunyan> for more complete docs.');
    p('Please report bugs to <https://github.com/trentm/node-bunyan/issues>.');
    /* END JSSTYLED */
}

/*
 * If the user specifies multiple input sources, we want to print out records
 * from all sources in a single, chronologically ordered stream.  To do this
 * efficiently, we first assume that all records within each source are ordered
 * already, so we need only keep track of the next record in each source and
 * the time of the last record emitted.  To avoid excess memory usage, we
 * pause() streams that are ahead of others.
 *
 * 'streams' is an object indexed by source name (file name) which specifies:
 *
 *    stream        Actual stream object, so that we can pause and resume it.
 *
 *    records       Array of log records we've read, but not yet emitted.  Each
 *                  record includes 'line' (the raw line), 'rec' (the JSON
 *                  record), and 'time' (the parsed time value).
 *
 *    done          Whether the stream has any more records to emit.
 */
var streams = {};

function gotRecord(file, line, rec, opts, stylize)
{
    var time = new Date(rec.time);

    streams[file]['records'].push({ line: line, rec: rec, time: time });
    emitNextRecord(opts, stylize);
}

function filterRecord(rec, opts)
{
    if (opts.level && rec.level < opts.level) {
        return false;
    }

    if (opts.condFuncs) {
        var recCopy = objCopy(rec);
        for (var i = 0; i < opts.condFuncs.length; i++) {
            var pass = opts.condFuncs[i].call(recCopy);
            if (!pass)
                return false;
        }
    } else if (opts.condVm) {
        for (var i = 0; i < opts.condVm.length; i++) {
            var pass = opts.condVm[i].runInNewContext(rec);
            if (!pass)
                return false;
        }
    }

    return true;
}

function emitNextRecord(opts, stylize)
{
    var ofile, ready, minfile, rec;

    for (;;) {
        /*
         * Take a first pass through the input streams to see if we have a
         * record from all of them.  If not, we'll pause any streams for
         * which we do already have a record (to avoid consuming excess
         * memory) and then wait until we have records from the others
         * before emitting the next record.
         *
         * As part of the same pass, we look for the earliest record
         * we have not yet emitted.
         */
        minfile = undefined;
        ready = true;
        for (ofile in streams) {

            if (streams[ofile].stream === null ||
                (!streams[ofile].done && streams[ofile].records.length === 0)) {
                ready = false;
                break;
            }

            if (streams[ofile].records.length > 0 &&
                (minfile === undefined ||
                    streams[minfile].records[0].time >
                        streams[ofile].records[0].time)) {
                minfile = ofile;
            }
        }

        if (!ready || minfile === undefined) {
            for (ofile in streams) {
                if (!streams[ofile].stream || streams[ofile].done)
                    continue;

                if (streams[ofile].records.length > 0) {
                    if (!streams[ofile].paused) {
                        streams[ofile].paused = true;
                        streams[ofile].stream.pause();
                    }
                } else if (streams[ofile].paused) {
                    streams[ofile].paused = false;
                    streams[ofile].stream.resume();
                }
            }

            return;
        }

        /*
         * Emit the next record for 'minfile', and invoke ourselves again to
         * make sure we emit as many records as we can right now.
         */
        rec = streams[minfile].records.shift();
        emitRecord(rec.rec, rec.line, opts, stylize);
    }
}

/**
 * Return a function for the given JS code that returns.
 *
 * If no 'return' in the given javascript snippet, then assume we are a single
 * statement and wrap in 'return (...)'. This is for convenience for short
 * '-c ...' snippets.
 */
function funcWithReturnFromSnippet(js) {
    // auto-"return"
    if (js.indexOf('return') === -1) {
        if (js.substring(js.length - 1) === ';') {
            js = js.substring(0, js.length - 1);
        }
        js = 'return (' + js + ')';
    }

    // Expose level definitions to condition func context
    var varDefs = [];
    Object.keys(upperNameFromLevel).forEach(function (lvl) {
        varDefs.push(format('var %s = %d;',
                upperNameFromLevel[lvl], lvl));
    });
    varDefs = varDefs.join('\n') + '\n';

    return (new Function(varDefs + js));
}

/**
 * Parse the command-line options and arguments into an object.
 *
 *    {
 *      'args': [...]       // arguments
 *      'help': true,       // true if '-h' option given
 *       // etc.
 *    }
 *
 * @return {Object} The parsed options. `.args` is the argument list.
 * @throws {Error} If there is an error parsing argv.
 */
function parseArgv(argv) {
    var parsed = {
        args: [],
        help: false,
        color: null,
        paginate: null,
        outputMode: OM_LONG,
        jsonIndent: 2,
        level: null,
        strict: false,
        pids: null,
        pidsType: null,
        timeFormat: TIME_UTC  // one of the TIME_ constants
    };

    // Turn '-iH' into '-i -H', except for argument-accepting options.
    var args = argv.slice(2);  // drop ['node', 'scriptname']
    var newArgs = [];
    var optTakesArg = {'d': true, 'o': true, 'c': true, 'l': true, 'p': true};
    for (var i = 0; i < args.length; i++) {
        if (args[i].charAt(0) === '-' && args[i].charAt(1) !== '-' &&
            args[i].length > 2)
        {
            var splitOpts = args[i].slice(1).split('');
            for (var j = 0; j < splitOpts.length; j++) {
                newArgs.push('-' + splitOpts[j]);
                if (optTakesArg[splitOpts[j]]) {
                    var optArg = splitOpts.slice(j+1).join('');
                    if (optArg.length) {
                        newArgs.push(optArg);
                    }
                    break;
                }
            }
        } else {
            newArgs.push(args[i]);
        }
    }
    args = newArgs;

    // Expose level definitions to condition vm context
    var condDefines = [];
    Object.keys(upperNameFromLevel).forEach(function (lvl) {
        condDefines.push(
            format('Object.prototype.%s = %s;', upperNameFromLevel[lvl], lvl));
    });
    condDefines = condDefines.join('\n') + '\n';

    var endOfOptions = false;
    while (args.length > 0) {
        var arg = args.shift();
        switch (arg) {
            case '--':
                endOfOptions = true;
                break;
            case '-h': // display help and exit
            case '--help':
                parsed.help = true;
                break;
            case '--version':
                parsed.version = true;
                break;
            case '--strict':
                parsed.strict = true;
                break;
            case '--color':
                parsed.color = true;
                break;
            case '--no-color':
                parsed.color = false;
                break;
            case '--pager':
                parsed.paginate = true;
                break;
            case '--no-pager':
                parsed.paginate = false;
                break;
            case '-o':
            case '--output':
                var name = args.shift();
                var idx = name.lastIndexOf('-');
                if (idx !== -1) {
                    var indentation = Number(name.slice(idx+1));
                    if (! isNaN(indentation)) {
                        parsed.jsonIndent = indentation;
                        name = name.slice(0, idx);
                    }
                }
                parsed.outputMode = OM_FROM_NAME[name];
                if (parsed.outputMode === undefined) {
                    throw new Error('unknown output mode: "'+name+'"');
                }
                break;
            case '-j': // output with JSON.stringify
                parsed.outputMode = OM_JSON;
                break;
            case '-0':
                parsed.outputMode = OM_BUNYAN;
                break;
            case '-L':
                parsed.timeFormat = TIME_LOCAL;
                if (!moment) {
                    throw new Error(
                        'could not find moment package required for "-L"');
                }
                break;
            case '--time':
                var timeArg = args.shift();
                switch (timeArg) {
                case 'utc':
                    parsed.timeFormat = TIME_UTC;
                    break
                case 'local':
                    parsed.timeFormat = TIME_LOCAL;
                    if (!moment) {
                        throw new Error('could not find moment package '
                            + 'required for "--time=local"');
                    }
                    break
                case undefined:
                    throw new Error('missing argument to "--time"');
                default:
                    throw new Error(format('invalid time format: "%s"',
                        timeArg));
                }
                break;
            case '-p':
                if (!parsed.pids) {
                    parsed.pids = [];
                }
                var pidArg = args.shift();
                var pid = +(pidArg);
                if (!isNaN(pid) || pidArg === '*') {
                    if (parsed.pidsType && parsed.pidsType !== 'num') {
                        throw new Error(format('cannot mix PID name and '
                            + 'number arguments: "%s"', pidArg));
                    }
                    parsed.pidsType = 'num';
                    if (!parsed.pids) {
                        parsed.pids = [];
                    }
                    parsed.pids.push(isNaN(pid) ? pidArg : pid);
                } else {
                    if (parsed.pidsType && parsed.pidsType !== 'name') {
                        throw new Error(format('cannot mix PID name and '
                            + 'number arguments: "%s"', pidArg));
                    }
                    parsed.pidsType = 'name';
                    parsed.pids = pidArg;
                }
                break;
            case '-l':
            case '--level':
                var levelArg = args.shift();
                var level = +(levelArg);
                if (isNaN(level)) {
                    level = +levelFromName[levelArg.toLowerCase()];
                }
                if (isNaN(level)) {
                    throw new Error('unknown level value: "'+levelArg+'"');
                }
                parsed.level = level;
                break;
            case '-c':
            case '--condition':
                gUsingConditionOpts = true;
                var condition = args.shift();
                if (Boolean(process.env.BUNYAN_EXEC &&
                    process.env.BUNYAN_EXEC === 'vm'))
                {
                    parsed.condVm = parsed.condVm || [];
                    var scriptName = 'bunyan-condition-'+parsed.condVm.length;
                    var code = condDefines + condition;
                    var script;
                    try {
                        script = vm.createScript(code, scriptName);
                    } catch (complErr) {
                        throw new Error(format('illegal CONDITION code: %s\n'
                            + '  CONDITION script:\n'
                            + '%s\n'
                            + '  Error:\n'
                            + '%s',
                            complErr, indent(code), indent(complErr.stack)));
                    }

                    // Ensure this is a reasonably safe CONDITION.
                    try {
                        script.runInNewContext(minValidRecord);
                    } catch (condErr) {
                        throw new Error(format(
                            /* JSSTYLED */
                            'CONDITION code cannot safely filter a minimal Bunyan log record\n'
                            + '  CONDITION script:\n'
                            + '%s\n'
                            + '  Minimal Bunyan log record:\n'
                            + '%s\n'
                            + '  Filter error:\n'
                            + '%s',
                            indent(code),
                            indent(JSON.stringify(minValidRecord, null, 2)),
                            indent(condErr.stack)
                            ));
                    }
                    parsed.condVm.push(script);
                } else  {
                    parsed.condFuncs = parsed.condFuncs || [];
                    parsed.condFuncs.push(funcWithReturnFromSnippet(condition));
                }
                break;
            default: // arguments
                if (!endOfOptions && arg.length > 0 && arg[0] === '-') {
                    throw new Error('unknown option "'+arg+'"');
                }
                parsed.args.push(arg);
                break;
        }
    }
    //TODO: '--' handling and error on a first arg that looks like an option.

    return parsed;
}


function isInteger(s) {
    return (s.search(/^-?[0-9]+$/) == 0);
}


// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics
// Suggested colors (some are unreadable in common cases):
// - Good: cyan, yellow (limited use), bold, green, magenta, red
// - Bad: blue (not visible on cmd.exe), grey (same color as background on
//   Solarized Dark theme from <https://github.com/altercation/solarized>, see
//   issue #160)
var colors = {
    'bold' : [1, 22],
    'italic' : [3, 23],
    'underline' : [4, 24],
    'inverse' : [7, 27],
    'white' : [37, 39],
    'grey' : [90, 39],
    'black' : [30, 39],
    'blue' : [34, 39],
    'cyan' : [36, 39],
    'green' : [32, 39],
    'magenta' : [35, 39],
    'red' : [31, 39],
    'yellow' : [33, 39]
};

function stylizeWithColor(str, color) {
    if (!str)
        return '';
    var codes = colors[color];
    if (codes) {
        return '\033[' + codes[0] + 'm' + str +
                     '\033[' + codes[1] + 'm';
    } else {
        return str;
    }
}

function stylizeWithoutColor(str, color) {
    return str;
}


/**
 * Is this a valid Bunyan log record.
 */
function isValidRecord(rec) {
    if (rec.v == null ||
            rec.level == null ||
            rec.name == null ||
            rec.hostname == null ||
            rec.pid == null ||
            rec.time == null ||
            rec.msg == null) {
        // Not valid Bunyan log.
        return false;
    } else {
        return true;
    }
}
var minValidRecord = {
    v: 0,   //TODO: get this from bunyan.LOG_VERSION
    level: INFO,
    name: 'name',
    hostname: 'hostname',
    pid: 123,
    time: Date.now(),
    msg: 'msg'
};


/**
 * Parses the given log line and either emits it right away (for invalid
 * records) or enqueues it for emitting later when it's the next line to show.
 */
function handleLogLine(file, line, opts, stylize) {
    if (exiting) {
        _selfTrace('warn: handleLogLine called while exiting');
        return;
    }

    currLine = line; // intentionally global

    // Emit non-JSON lines immediately.
    var rec;
    if (!line) {
        if (!opts.strict) emit(line + '\n');
        return;
    } else if (line[0] !== '{') {
        if (!opts.strict) emit(line + '\n');  // not JSON
        return;
    } else {
        try {
            rec = JSON.parse(line);
        } catch (e) {
            if (!opts.strict) emit(line + '\n');
            return;
        }
    }

    if (!isValidRecord(rec)) {
        if (!opts.strict) emit(line + '\n');
        return;
    }

    if (!filterRecord(rec, opts))
        return;

    if (file === null)
        return emitRecord(rec, line, opts, stylize);

    return gotRecord(file, line, rec, opts, stylize);
}

/**
 * Print out a single result, considering input options.
 */
function emitRecord(rec, line, opts, stylize) {
    var short = false;

    switch (opts.outputMode) {
    case OM_SHORT:
        short = true;
        /* jsl:fall-thru */

    case OM_LONG:
        //    [time] LEVEL: name[/comp]/pid on hostname (src): msg* (extras...)
        //        msg*
        //        --
        //        long and multi-line extras
        //        ...
        // If 'msg' is single-line, then it goes in the top line.
        // If 'req', show the request.
        // If 'res', show the response.
        // If 'err' and 'err.stack' then show that.
        if (!isValidRecord(rec)) {
            return emit(line + '\n');
        }

        delete rec.v;

        // Time.
        var time;
        if (!short && opts.timeFormat === TIME_UTC) {
            // Fast default path: We assume the raw `rec.time` is a UTC time
            // in ISO 8601 format (per spec).
            time = '[' + rec.time + ']';
        } else if (!moment && opts.timeFormat === TIME_UTC) {
            // Don't require momentjs install, as long as not using TIME_LOCAL.
            time = rec.time.substr(11);
        } else {
            var tzFormat;
            var moTime = moment(rec.time);
            switch (opts.timeFormat) {
            case TIME_UTC:
                tzFormat = TIMEZONE_UTC_FORMATS[short ? 'short' : 'long'];
                moTime.utc();
                break;
            case TIME_LOCAL:
                tzFormat = TIMEZONE_LOCAL_FORMATS[short ? 'short' : 'long'];
                break;
            default:
                throw new Error('unexpected timeFormat: ' + opts.timeFormat);
            };
            time = moTime.format(tzFormat);
        }
        time = stylize(time, 'none');
        delete rec.time;

        var nameStr = rec.name;
        delete rec.name;

        if (rec.component) {
            nameStr += '/' + rec.component;
        }
        delete rec.component;

        if (!short)
            nameStr += '/' + rec.pid;
        delete rec.pid;

        var level = (upperPaddedNameFromLevel[rec.level] || 'LVL' + rec.level);
        if (opts.color) {
            var colorFromLevel = {
                10: 'white',    // TRACE
                20: 'yellow',   // DEBUG
                30: 'cyan',     // INFO
                40: 'magenta',  // WARN
                50: 'red',      // ERROR
                60: 'inverse',  // FATAL
            };
            level = stylize(level, colorFromLevel[rec.level]);
        }
        delete rec.level;

        var src = '';
        if (rec.src && rec.src.file) {
            var s = rec.src;
            if (s.func) {
                src = format(' (%s:%d in %s)', s.file, s.line, s.func);
            } else {
                src = format(' (%s:%d)', s.file, s.line);
            }
            src = stylize(src, 'green');
        }
        delete rec.src;

        var hostname = rec.hostname;
        delete rec.hostname;

        var extras = [];
        var details = [];

        if (rec.req_id) {
            extras.push('req_id=' + rec.req_id);
        }
        delete rec.req_id;

        var onelineMsg;
        if (rec.msg.indexOf('\n') !== -1) {
            onelineMsg = '';
            details.push(indent(stylize(rec.msg, 'cyan')));
        } else {
            onelineMsg = ' ' + stylize(rec.msg, 'cyan');
        }
        delete rec.msg;

        if (rec.req && typeof (rec.req) === 'object') {
            var req = rec.req;
            delete rec.req;
            var headers = req.headers;
            if (!headers) {
                headers = '';
            } else if (typeof (headers) === 'string') {
                headers = '\n' + headers;
            } else if (typeof (headers) === 'object') {
                headers = '\n' + Object.keys(headers).map(function (h) {
                    return h + ': ' + headers[h];
                }).join('\n');
            }
            var s = format('%s %s HTTP/%s%s', req.method,
                req.url,
                req.httpVersion || '1.1',
                headers
            );
            delete req.url;
            delete req.method;
            delete req.httpVersion;
            delete req.headers;
            if (req.body) {
                s += '\n\n' + (typeof (req.body) === 'object'
                    ? JSON.stringify(req.body, null, 2) : req.body);
                delete req.body;
            }
            if (req.trailers && Object.keys(req.trailers) > 0) {
                s += '\n' + Object.keys(req.trailers).map(function (t) {
                    return t + ': ' + req.trailers[t];
                }).join('\n');
            }
            delete req.trailers;
            details.push(indent(s));
            // E.g. for extra 'foo' field on 'req', add 'req.foo' at
            // top-level. This *does* have the potential to stomp on a
            // literal 'req.foo' key.
            Object.keys(req).forEach(function (k) {
                rec['req.' + k] = req[k];
            })
        }

        if (rec.client_req && typeof (rec.client_req) === 'object') {
            var client_req = rec.client_req;
            delete rec.client_req;

            var headers = client_req.headers;
            delete client_req.headers;

            var s = format('%s %s HTTP/%s%s',
                client_req.method,
                client_req.url,
                client_req.httpVersion || '1.1',
                (headers ?
                    '\n' + Object.keys(headers).map(
                        function (h) {
                            return h + ': ' + headers[h];
                        }).join('\n') :
                    ''));
            delete client_req.method;
            delete client_req.url;
            delete client_req.httpVersion;

            if (client_req.body) {
                s += '\n\n' + (typeof (client_req.body) === 'object' ?
                    JSON.stringify(client_req.body, null, 2) :
                    client_req.body);
                delete client_req.body;
            }
            // E.g. for extra 'foo' field on 'client_req', add
            // 'client_req.foo' at top-level. This *does* have the potential
            // to stomp on a literal 'client_req.foo' key.
            Object.keys(client_req).forEach(function (k) {
                rec['client_req.' + k] = client_req[k];
            });
            details.push(indent(s));
        }

        function _res(res) {
            var s = '';

            /*
             * Handle `res.header` or `res.headers` as either a string or
             * an object of header key/value pairs. Prefer `res.header` if set,
             * because that's what Bunyan's own `res` serializer specifies,
             * because that's the value in Node.js's core HTTP server response
             * implementation that has all the implicit headers.
             *
             * Note: `res.header` (string) typically includes the 'HTTP/1.1 ...'
             * status line.
             */
            var headerTypes = {string: true, object: true};
            var headers;
            var headersStr = '';
            var headersHaveStatusLine = false;
            if (res.header && headerTypes[typeof (res.header)]) {
                headers = res.header;
                delete res.header;
            } else if (res.headers && headerTypes[typeof (res.headers)]) {
                headers = res.headers;
                delete res.headers;
            }
            if (headers === undefined) {
                /* pass through */
            } else if (typeof (headers) === 'string') {
                headersStr = headers.trimRight(); // Trim the CRLF.
                if (headersStr.slice(0, 5) === 'HTTP/') {
                    headersHaveStatusLine = true;
                }
            } else {
                headersStr += Object.keys(headers).map(
                    function (h) { return h + ': ' + headers[h]; }).join('\n');
            }

            /*
             * Add a 'HTTP/1.1 ...' status line if the headers didn't already
             * include it.
             */
            if (!headersHaveStatusLine && res.statusCode !== undefined) {
                s += format('HTTP/1.1 %s %s\n', res.statusCode,
                    http.STATUS_CODES[res.statusCode]);
            }
            delete res.statusCode;
            s += headersStr;

            if (res.body !== undefined) {
                var body = (typeof (res.body) === 'object'
                    ? JSON.stringify(res.body, null, 2) : res.body);
                if (body.length > 0) { s += '\n\n' + body };
                delete res.body;
            } else {
                s = s.trimRight();
            }
            if (res.trailer) {
                s += '\n' + res.trailer;
            }
            delete res.trailer;
            if (s) {
                details.push(indent(s));
            }
            // E.g. for extra 'foo' field on 'res', add 'res.foo' at
            // top-level. This *does* have the potential to stomp on a
            // literal 'res.foo' key.
            Object.keys(res).forEach(function (k) {
                rec['res.' + k] = res[k];
            });
        }

        if (rec.res && typeof (rec.res) === 'object') {
            _res(rec.res);
            delete rec.res;
        }
        if (rec.client_res && typeof (rec.client_res) === 'object') {
            _res(rec.client_res);
            delete rec.client_res;
        }

        if (rec.err && rec.err.stack) {
            var err = rec.err
            if (typeof (err.stack) !== 'string') {
                details.push(indent(err.stack.toString()));
            } else {
                details.push(indent(err.stack));
            }
            delete err.message;
            delete err.name;
            delete err.stack;
            // E.g. for extra 'foo' field on 'err', add 'err.foo' at
            // top-level. This *does* have the potential to stomp on a
            // literal 'err.foo' key.
            Object.keys(err).forEach(function (k) {
                rec['err.' + k] = err[k];
            })
            delete rec.err;
        }

        var leftover = Object.keys(rec);
        for (var i = 0; i < leftover.length; i++) {
            var key = leftover[i];
            var value = rec[key];
            var stringified = false;
            if (typeof (value) !== 'string') {
                value = JSON.stringify(value, null, 2);
                stringified = true;
            }
            if (value.indexOf('\n') !== -1 || value.length > 50) {
                details.push(indent(key + ': ' + value));
            } else if (!stringified && (value.indexOf(' ') != -1 ||
                value.length === 0))
            {
                extras.push(key + '=' + JSON.stringify(value));
            } else {
                extras.push(key + '=' + value);
            }
        }

        extras = stylize(
            (extras.length ? ' (' + extras.join(', ') + ')' : ''), 'none');
        details = stylize(
            (details.length ? details.join('\n    --\n') + '\n' : ''), 'none');
        if (!short)
            emit(format('%s %s: %s on %s%s:%s%s\n%s',
                time,
                level,
                nameStr,
                hostname || '<no-hostname>',
                src,
                onelineMsg,
                extras,
                details));
        else
            emit(format('%s %s %s:%s%s\n%s',
                time,
                level,
                nameStr,
                onelineMsg,
                extras,
                details));
        break;

    case OM_INSPECT:
        emit(util.inspect(rec, false, Infinity, true) + '\n');
        break;

    case OM_BUNYAN:
        emit(JSON.stringify(rec, null, 0) + '\n');
        break;

    case OM_JSON:
        emit(JSON.stringify(rec, null, opts.jsonIndent) + '\n');
        break;

    case OM_SIMPLE:
        /* JSSTYLED */
        // <http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/SimpleLayout.html>
        if (!isValidRecord(rec)) {
            return emit(line + '\n');
        }
        emit(format('%s - %s\n',
            upperNameFromLevel[rec.level] || 'LVL' + rec.level,
            rec.msg));
        break;
    default:
        throw new Error('unknown output mode: '+opts.outputMode);
    }
}


function emit(s) {
    try {
        stdout.write(s);
    } catch (writeErr) {
        _selfTrace('exception from stdout.write:', writeErr)
        // Handle any exceptions in stdout writing in `stdout.on('error', ...)`.
    }
}


/**
 * Process all input from stdin.
 *
 * @params opts {Object} Bunyan options object.
 * @param stylize {Function} Output stylize function to use.
 * @param callback {Function} `function ()`
 */
function processStdin(opts, stylize, callback) {
    var leftover = '';  // Left-over partial line from last chunk.
    var stdin = process.stdin;
    stdin.resume();
    stdin.setEncoding('utf8');
    stdin.on('data', function (chunk) {
        var lines = chunk.split(/\r\n|\n/);
        var length = lines.length;
        if (length === 1) {
            leftover += lines[0];
            return;
        }

        if (length > 1) {
            handleLogLine(null, leftover + lines[0], opts, stylize);
        }
        leftover = lines.pop();
        length -= 1;
        for (var i = 1; i < length; i++) {
            handleLogLine(null, lines[i], opts, stylize);
        }
    });
    stdin.on('end', function () {
        if (leftover) {
            handleLogLine(null, leftover, opts, stylize);
            leftover = '';
        }
        callback();
    });
}


/**
 * Process bunyan:log-* probes from the given pid.
 *
 * @params opts {Object} Bunyan options object.
 * @param stylize {Function} Output stylize function to use.
 * @param callback {Function} `function (code)`
 */
function processPids(opts, stylize, callback) {
    var leftover = '';  // Left-over partial line from last chunk.

    /**
     * Get the PIDs to dtrace.
     *
     * @param cb {Function} `function (errCode, pids)`
     */
    function getPids(cb) {
        if (opts.pidsType === 'num') {
            return cb(null, opts.pids);
        }
        if (process.platform === 'sunos') {
            execFile('/bin/pgrep', ['-lf', opts.pids],
                function (pidsErr, stdout, stderr) {
                    if (pidsErr) {
                        warn('bunyan: error getting PIDs for "%s": %s\n%s\n%s',
                            opts.pids, pidsErr.message, stdout, stderr);
                        return cb(1);
                    }
                    var pids = stdout.trim().split('\n')
                        .map(function (line) {
                            return line.trim().split(/\s+/)[0]
                        })
                        .filter(function (pid) {
                            return Number(pid) !== process.pid
                        });
                    if (pids.length === 0) {
                        warn('bunyan: error: no matching PIDs found for "%s"',
                            opts.pids);
                        return cb(2);
                    }
                    cb(null, pids);
                }
            );
        } else {
            var regex = opts.pids;
            if (regex && /[a-zA-Z0-9_]/.test(regex[0])) {
                // 'foo' -> '[f]oo' trick to exclude the 'grep' PID from its
                // own search.
                regex = '[' + regex[0] + ']' + regex.slice(1);
            }
            var cmd = format('ps -A -o pid,command | grep \'%s\'',
                // Escape single-quotes to avoid breaking the grep arg quoting
                // (leading to a possible *code execution*) and backslashes to
                // avoid undoing that escaping.
                regex.replace(/\\/g, '\\\\')
                    // JSSTYLED
                    .replace(/'/g, "'\\''"));
            _selfTrace('exec cmd: %j', cmd);
            exec(cmd,
                function (pidsErr, stdout, stderr) {
                    if (pidsErr) {
                        warn('bunyan: error getting PIDs for "%s": %s\n%s\n%s',
                            opts.pids, pidsErr.message, stdout, stderr);
                        return cb(1);
                    }
                    var pids = stdout.trim().split('\n')
                        .map(function (line) {
                            return line.trim().split(/\s+/)[0];
                        })
                        .filter(function (pid) {
                            return Number(pid) !== process.pid;
                        });
                    if (pids.length === 0) {
                        warn('bunyan: error: no matching PIDs found for "%s"',
                            opts.pids);
                        return cb(2);
                    }
                    cb(null, pids);
                }
            );
        }
    }

    getPids(function (errCode, pids) {
        if (errCode) {
            return callback(errCode);
        }

        var probes = pids.map(function (pid) {
            if (!opts.level)
                return format('bunyan%s:::log-*', pid);

            var rval = [], l;

            for (l in levelFromName) {
                if (levelFromName[l] >= opts.level)
                    rval.push(format('bunyan%s:::log-%s', pid, l));
            }

            if (rval.length != 0)
                return rval.join(',');

            warn('bunyan: error: level (%d) exceeds maximum logging level',
                opts.level);
            cleanupAndExit(1);
        }).join(',');
        var argv = ['dtrace', '-Z', '-x', 'strsize=4k',
            '-x', 'switchrate=10hz', '-qn',
            format('%s{printf("%s", copyinstr(arg0))}', probes)];
        //console.log('dtrace argv: %s', argv);
        var dtrace = spawn(argv[0], argv.slice(1),
            // Share the stderr handle to have error output come
            // straight through. Only supported in v0.8+.
            {stdio: ['pipe', 'pipe', process.stderr]});
        dtrace.on('error', function (e) {
            if (e.syscall === 'spawn' && e.errno === 'ENOENT') {
                console.error('bunyan: error: could not spawn "dtrace" ' +
                    '("bunyan -p" is only supported on platforms with dtrace)');
            } else {
                console.error('bunyan: error: unexpected dtrace error: %s', e);
            }
            callback(1);
        })
        child = dtrace; // intentionally global

        function finish(code) {
            if (leftover) {
                handleLogLine(null, leftover, opts, stylize);
                leftover = '';
            }
            callback(code);
        }

        dtrace.stdout.setEncoding('utf8');
        dtrace.stdout.on('data', function (chunk) {
            var lines = chunk.split(/\r\n|\n/);
            var length = lines.length;
            if (length === 1) {
                leftover += lines[0];
                return;
            }
            if (length > 1) {
                handleLogLine(null, leftover + lines[0], opts, stylize);
            }
            leftover = lines.pop();
            length -= 1;
            for (var i = 1; i < length; i++) {
                handleLogLine(null, lines[i], opts, stylize);
            }
        });

        if (nodeSpawnSupportsStdio) {
            dtrace.on('exit', finish);
        } else {
            // Fallback (for < v0.8) to pipe the dtrace process' stderr to
            // this stderr. Wait for all of (1) process 'exit', (2) stderr
            // 'end', and (2) stdout 'end' before returning to ensure all
            // stderr is flushed (issue #54).
            var returnCode = null;
            var eventsRemaining = 3;
            function countdownToFinish(code) {
                returnCode = code;
                eventsRemaining--;
                if (eventsRemaining == 0) {
                    finish(returnCode);
                }
            }
            dtrace.stderr.pipe(process.stderr);
            dtrace.stderr.on('end', countdownToFinish);
            dtrace.stderr.on('end', countdownToFinish);
            dtrace.on('exit', countdownToFinish);
        }
    });
}


/**
 * Process all input from the given log file.
 *
 * @param file {String} Log file path to process.
 * @params opts {Object} Bunyan options object.
 * @param stylize {Function} Output stylize function to use.
 * @param callback {Function} `function ()`
 */
function processFile(file, opts, stylize, callback) {
    var stream = fs.createReadStream(file);
    if (/\.gz$/.test(file)) {
        stream = stream.pipe(require('zlib').createGunzip());
    }
    // Manually decode streams - lazy load here as per node/lib/fs.js
    var decoder = new (require('string_decoder').StringDecoder)('utf8');

    streams[file].stream = stream;

    stream.on('error', function (err) {
        streams[file].done = true;
        callback(err);
    });

    var leftover = '';  // Left-over partial line from last chunk.
    stream.on('data', function (data) {
        if (exiting) {
            _selfTrace('stop reading file "%s" because exiting', file);
            stream.destroy();
            return;
        }

        var chunk = decoder.write(data);
        if (!chunk.length) {
            return;
        }
        var lines = chunk.split(/\r\n|\n/);
        var length = lines.length;
        if (length === 1) {
            leftover += lines[0];
            return;
        }

        if (length > 1) {
            handleLogLine(file, leftover + lines[0], opts, stylize);
        }
        leftover = lines.pop();
        length -= 1;
        for (var i = 1; i < length; i++) {
            handleLogLine(file, lines[i], opts, stylize);
        }
    });

    stream.on('end', function () {
        streams[file].done = true;
        if (leftover) {
            handleLogLine(file, leftover, opts, stylize);
            leftover = '';
        } else {
            emitNextRecord(opts, stylize);
        }
        callback();
    });
}


/**
 * From node async module.
 */
/* BEGIN JSSTYLED */
function asyncForEach(arr, iterator, callback) {
    callback = callback || function () {};
    if (!arr.length) {
        return callback();
    }
    var completed = 0;
    arr.forEach(function (x) {
        iterator(x, function (err) {
            if (err) {
                callback(err);
                callback = function () {};
            }
            else {
                completed += 1;
                if (completed === arr.length) {
                    callback();
                }
            }
        });
    });
};
/* END JSSTYLED */



/**
 * Cleanup and exit properly.
 *
 * Warning: this doesn't necessarily stop processing, i.e. process exit
 * might be delayed. It is up to the caller to ensure that no subsequent
 * bunyan processing is done after calling this.
 *
 * @param code {Number} exit code.
 * @param signal {String} Optional signal name, if this was exitting because
 *    of a signal.
 */
function cleanupAndExit(code, signal) {
    // Guard one call.
    if (exiting) {
        return;
    }
    exiting = true;
    _selfTrace('cleanupAndExit(%s, %s)', code, signal);

    // Clear possibly interrupted ANSI code (issue #59).
    if (usingAnsiCodes) {
        stdout.write('\033[0m');
    }

    // Kill possible dtrace child.
    if (child) {
        child.kill(signal);
    }

    if (pager) {
        // Let pager know that output is done, then wait for pager to exit.
        pager.removeListener('exit', onPrematurePagerExit);
        pager.on('exit', function onPagerExit(pagerCode) {
            _selfTrace('pager exit -> process.exit(%s)', pagerCode || code);
            process.exit(pagerCode || code);
        });
        stdout.end();
    } else if (code) {
        // Non-zero exit: Something is wrong. We are very likely still
        // processing log records -- i.e. we have open handles -- so we need
        // a hard stop (aka `process.exit`).
        _selfTrace('process.exit(%s)', code);
        process.exit(code);
    } else {
        // Zero exit: This should be a "normal" exit, for which we want to
        // flush stdout/stderr.
        exeunt.softExit(code);
    }
}



//---- mainline

process.on('SIGINT', function () { cleanupAndExit(1, 'SIGINT'); });
process.on('SIGQUIT', function () { cleanupAndExit(1, 'SIGQUIT'); });
process.on('SIGTERM', function () { cleanupAndExit(1, 'SIGTERM'); });
process.on('SIGHUP', function () { cleanupAndExit(1, 'SIGHUP'); });

process.on('uncaughtException', function (err) {
    function _indent(s) {
        var lines = s.split(/\r?\n/);
        for (var i = 0; i < lines.length; i++) {
            lines[i] = '*     ' + lines[i];
        }
        return lines.join('\n');
    }

    var title = encodeURIComponent(format(
        'Bunyan %s crashed: %s', getVersion(), String(err)));
    var e = console.error;
    e('```');
    e('* The Bunyan CLI crashed!');
    e('*');
    if (err.name === 'ReferenceError' && gUsingConditionOpts) {
        /* BEGIN JSSTYLED */
        e('* This crash was due to a "ReferenceError", which is often the result of given');
        e('* `-c CONDITION` code that doesn\'t guard against undefined values. If that is');
        /* END JSSTYLED */
        e('* not the problem:');
        e('*');
    }
    e('* Please report this issue and include the details below:');
    e('*');
    e('*    https://github.com/trentm/node-bunyan/issues/new?title=%s', title);
    e('*');
    e('* * *');
    e('* platform:', process.platform);
    e('* node version:', process.version);
    e('* bunyan version:', getVersion());
    e('* argv: %j', process.argv);
    e('* log line: %j', currLine);
    e('* stack:');
    e(_indent(err.stack));
    e('```');
    process.exit(1);
});


// Early termination of the pager: just stop.
function onPrematurePagerExit(pagerCode) {
    _selfTrace('premature pager exit');
    // 'pager' and 'stdout' are intentionally global.
    pager = null;
    stdout.end()
    stdout = process.stdout;
    cleanupAndExit(pagerCode);
}


function main(argv) {
    try {
        var opts = parseArgv(argv);
    } catch (e) {
        warn('bunyan: error: %s', e.message);
        cleanupAndExit(1);
        return;
    }
    if (opts.help) {
        printHelp();
        return;
    }
    if (opts.version) {
        console.log('bunyan ' + getVersion());
        return;
    }
    if (opts.pids && opts.args.length > 0) {
        warn('bunyan: error: can\'t use both "-p PID" (%s) and file (%s) args',
            opts.pids, opts.args.join(' '));
        cleanupAndExit(1);
        return;
    }
    if (opts.color === null) {
        if (process.env.BUNYAN_NO_COLOR &&
                process.env.BUNYAN_NO_COLOR.length > 0) {
            opts.color = false;
        } else {
            opts.color = process.stdout.isTTY;
        }
    }
    usingAnsiCodes = opts.color; // intentionally global
    var stylize = (opts.color ? stylizeWithColor : stylizeWithoutColor);

    // Pager.
    var paginate = (
        process.stdout.isTTY &&
        process.stdin.isTTY &&
        !opts.pids && // Don't page if following process output.
        opts.args.length > 0 && // Don't page if no file args to process.
        process.platform !== 'win32' &&
        (nodeVer[0] > 0 || nodeVer[1] >= 8) &&
        (opts.paginate === true ||
            (opts.paginate !== false &&
                (!process.env.BUNYAN_NO_PAGER ||
                    process.env.BUNYAN_NO_PAGER.length === 0))));
    if (paginate) {
        var pagerCmd = process.env.PAGER || 'less';
        /* JSSTYLED */
        assert.ok(pagerCmd.indexOf('"') === -1 && pagerCmd.indexOf("'") === -1,
            'cannot parse PAGER quotes yet');
        var argv = pagerCmd.split(/\s+/g);
        var env = objCopy(process.env);
        if (env.LESS === undefined) {
            // git's default is LESS=FRSX. I don't like the 'S' here because
            // lines are *typically* wide with bunyan output and scrolling
            // horizontally is a royal pain. Note a bug in Mac's `less -F`,
            // such that SIGWINCH can kill it. If that rears too much then
            // I'll remove 'F' from here.
            env.LESS = 'FRX';
        }
        _selfTrace('pager: argv=%j, env.LESS=%j', argv, env.LESS);
        // `pager` and `stdout` intentionally global.
        pager = spawn(argv[0], argv.slice(1),
            // Share the stderr handle to have error output come
            // straight through. Only supported in v0.8+.
            {env: env, stdio: ['pipe', 1, 2]});
        stdout = pager.stdin;
        pager.on('exit', onPrematurePagerExit);
    }

    // Stdout error handling. (Couldn't setup until `stdout` was determined.)
    stdout.on('error', function (err) {
        _selfTrace('stdout error event: %s, exiting=%s', err, exiting);
        if (exiting) {
            return;
        } else if (err.code === 'EPIPE') {
            cleanupAndExit(0);
        } else {
            warn('bunyan: error on output stream: %s', err);
            cleanupAndExit(1);
        }
    });

    var retval = 0;
    if (opts.pids) {
        processPids(opts, stylize, function (code) {
            cleanupAndExit(code);
        });
    } else if (opts.args.length > 0) {
        var files = opts.args;
        files.forEach(function (file) {
            streams[file] = { stream: null, records: [], done: false }
        });
        asyncForEach(files,
            function (file, next) {
                processFile(file, opts, stylize, function (err) {
                    if (err) {
                        warn('bunyan: %s', err.message);
                        retval += 1;
                    }
                    next();
                });
            },
            function (err) {
                if (err) {
                    warn('bunyan: unexpected error: %s', err.stack || err);
                    cleanupAndExit(1);
                } else {
                    cleanupAndExit(retval);
                }
            }
        );
    } else {
        processStdin(opts, stylize, function () {
            cleanupAndExit(retval);
        });
    }
}

if (require.main === module) {
    main(process.argv);
}


================================================
FILE: docs/bunyan.1
================================================
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
.TH "BUNYAN" "1" "January 2015" "" "bunyan manual"
.
.SH "NAME"
\fBbunyan\fR \- filter and pretty\-print Bunyan log file content
.
.SH "SYNOPSIS"
\fBbunyan\fR [OPTIONS]
.
.P
\&\.\.\. | \fBbunyan\fR [OPTIONS]
.
.P
\fBbunyan\fR [OPTIONS] \-p PID
.
.SH "DESCRIPTION"
"Bunyan" is \fBa simple and fast a JSON logging library\fR for node\.js services, a one\-JSON\-object\-per\-line log format, and \fBa \fBbunyan\fR CLI tool\fR for nicely viewing those logs\. This man page describes the latter\.
.
.SS "Pretty\-printing"
A bunyan log file is a stream of JSON objects, optionally interspersed with non\-JSON log lines\. The primary usage of bunyan(1) is to pretty print, for example:
.
.IP "" 4
.
.nf

$ bunyan foo\.log          # or `cat foo\.log | bunyan
[2012\-02\-08T22:56:52\.856Z]  INFO: myservice/123 on example\.com: My message
    extra: multi
    line
[2012\-02\-08T22:56:54\.856Z] ERROR: myservice/123 on example\.com: My message
\.\.\.
.
.fi
.
.IP "" 0
.
.P
By default the "long" output format is used\. Use the \fB\-o FORMAT\fR option to emit other formats\. E\.g\.:
.
.IP "" 4
.
.nf

$ bunyan foo\.log \-o short
22:56:52\.856Z  INFO myservice: My message
    extra: multi
    line
22:56:54\.856Z ERROR myservice: My message
\.\.\.
.
.fi
.
.IP "" 0
.
.P
These will color the output if supported in your terminal\. See "OUTPUT FORMATS" below\.
.
.SS "Filtering"
The \fBbunyan\fR CLI can also be used to filter a bunyan log\. Use \fB\-l LEVEL\fR to filter by level:
.
.IP "" 4
.
.nf

$ bunyan foo\.log \-l error       # show only \'error\' level records
[2012\-02\-08T22:56:54\.856Z] ERROR: myservice/123 on example\.com: My message
.
.fi
.
.IP "" 0
.
.P
Use \fB\-c COND\fR to filter on a JavaScript expression returning true on the record data\. In the COND code, \fBthis\fR refers to the record object:
.
.IP "" 4
.
.nf

$ bunyan foo\.log \-c `this\.three`     # show records with the \'extra\' field
[2012\-02\-08T22:56:52\.856Z]  INFO: myservice/123 on example\.com: My message
    extra: multi
    line
.
.fi
.
.IP "" 0
.
.SH "OPTIONS"
.
.TP
\fB\-h\fR, \fB\-\-help\fR
Print this help info and exit\.
.
.TP
\fB\-\-version\fR
Print version of this command and exit\.
.
.TP
\fB\-q\fR, \fB\-\-quiet\fR
Don\'t warn if input isn\'t valid JSON\.
.
.P
Dtrace options (only on dtrace\-supporting platforms):
.
.TP
\fB\-p PID\fR, \fB\-p NAME\fR
Process bunyan:log\-* probes from the process with the given PID\. Can be used multiple times, or specify all processes with \'*\', or a set of processes whose command & args match a pattern with \'\-p NAME\'\.
.
.P
Filtering options:
.
.TP
\fB\-l\fR, \fB\-\-level LEVEL\fR
Only show messages at or above the specified level\. You can specify level \fInames\fR or numeric values\. (See \'Log Levels\' below\.)
.
.TP
\fB\-c COND\fR, \fB\-\-condition COND\fR
Run each log message through the condition and only show those that resolve to a truish value\. E\.g\. \fB\-c \'this\.pid == 123\'\fR\.
.
.TP
\fB\-\-strict\fR
Suppress all but legal Bunyan JSON log lines\. By default non\-JSON, and non\-Bunyan lines are passed through\.
.
.P
Output options:
.
.TP
\fB\-\-color\fR
Colorize output\. Defaults to try if output stream is a TTY\.
.
.TP
\fB\-\-no\-color\fR
Force no coloring (e\.g\. terminal doesn\'t support it)
.
.TP
\fB\-o FORMAT\fR, \fB\-\-output FORMAT\fR
Specify an output format\. One of \fBlong\fR (the default), \fBshort\fR, \fBjson\fR, \fBjson\-N\fR, \fBbunyan\fR (the native bunyan 0\-indent JSON output) or \fBinspect\fR\.
.
.TP
\fB\-j\fR
Shortcut for \fB\-o json\fR\.
.
.TP
\fB\-L\fR, \fB\-\-time local\fR
Display the time field in \fIlocal\fR time, rather than the default UTC time\.
.
.SH "LOG LEVELS"
In Bunyan log records, then \fBlevel\fR field is a number\. For the \fB\-l|\-\-level\fR argument the level \fBnames\fR are supported as shortcuts\. In \fB\-c|\-\-condition\fR scripts, uppercase symbols like "DEBUG" are defined for convenience\.
.
.IP "" 4
.
.nf

Level Name      Level Number    Symbol in COND Scripts
trace           10              TRACE
debug           20              DEBUG
info            30              INFO
warn            40              WARN
error           50              ERROR
fatal           60              FATAL
.
.fi
.
.IP "" 0
.
.SH "OUTPUT FORMATS"
.
.nf

FORMAT NAME         DESCRIPTION
long (default)      The default output\. Long form\. Colored and "pretty"\.
                    \'req\' and \'res\' and \'err\' fields are rendered specially
                    as an HTTP request, HTTP response and exception
                    stack trace, respectively\. For backward compat, the
                    name "paul" also works for this\.
short               Like the default output, but more concise\. Some
                    typically redundant fields are ellided\.
json                JSON output, 2\-space indentation\.
json\-N              JSON output, N\-space indentation, e\.g\. "json\-4"
bunyan              Alias for "json\-0", the Bunyan "native" format\.
inspect             Node\.js `util\.inspect` output\.
.
.fi
.
.SH "DTRACE SUPPORT"
On systems that support DTrace (e\.g\., MacOS, FreeBSD, illumos derivatives like SmartOS and OmniOS), Bunyan will create a DTrace provider (\fBbunyan\fR) that makes available the following probes:
.
.IP "" 4
.
.nf

log\-trace
log\-debug
log\-info
log\-warn
log\-error
log\-fatal
.
.fi
.
.IP "" 0
.
.P
Each of these probes has a single argument: the string that would be written to the log\. Note that when a probe is enabled, it will fire whenever the corresponding function is called, even if the level of the log message is less than that of any stream\.
.
.P
See \fIhttps://github\.com/trentm/node\-bunyan#dtrace\-support\fR for more details and the \'\-p PID\' option above for convenience usage\.
.
.SH "ENVIRONMENT"
.
.TP
\fBBUNYAN_NO_COLOR\fR
Set to a non\-empty value to force no output coloring\. See \'\-\-no\-color\'\.
.
.SH "PROJECT & BUGS"
\fBbunyan\fR is written in JavaScript and requires node\.js (\fBnode\fR)\. The project lives at \fIhttps://github\.com/trentm/node\-bunyan\fR and is published to npm as "bunyan"\.
.
.IP "\(bu" 4
README, Install notes: \fIhttps://github\.com/trentm/node\-bunyan#readme\fR
.
.IP "\(bu" 4
Report bugs to \fIhttps://github\.com/trentm/node\-bunyan/issues\fR\.
.
.IP "\(bu" 4
See the full changelog at: \fIhttps://github\.com/trentm/node\-bunyan/blob/master/CHANGES\.md\fR
.
.IP "" 0
.
.SH "LICENSE"
MIT License (see \fIhttps://github\.com/trentm/node\-bunyan/blob/master/LICENSE\.txt\fR)
.
.SH "COPYRIGHT"
node\-bunyan is Copyright (c) 2012 Joyent, Inc\. Copyright (c) 2012 Trent Mick\. All rights reserved\.


================================================
FILE: docs/bunyan.1.html
================================================
<!DOCTYPE html>
<html>
<head>
  <meta http-equiv='content-type' value='text/html;charset=utf8'>
  <meta name='generator' value='Ronn/v0.7.3 (http://github.com/rtomayko/ronn/tree/0.7.3)'>
  <title>bunyan(1) - filter and pretty-print Bunyan log file content</title>
  <style type='text/css' media='all'>
  /* style: man */
  body#manpage {margin:0}
  .mp {max-width:100ex;padding:0 9ex 1ex 4ex}
  .mp p,.mp pre,.mp ul,.mp ol,.mp dl {margin:0 0 20px 0}
  .mp h2 {margin:10px 0 0 0}
  .mp > p,.mp > pre,.mp > ul,.mp > ol,.mp > dl {margin-left:8ex}
  .mp h3 {margin:0 0 0 4ex}
  .mp dt {margin:0;clear:left}
  
  .mp dd {margin:0 0 0 9ex}
  .mp h1,.mp h2,.mp h3,.mp h4 {clear:left}
  .mp pre {margin-bottom:20px}
  .mp pre+h2,.mp pre+h3 {margin-top:22px}
  .mp h2+pre,.mp h3+pre {margin-top:5px}
  .mp img {display:block;margin:auto}
  .mp h1.man-title {display:none}
  .mp,.mp code,.mp pre,.mp tt,.mp kbd,.mp samp,.mp h3,.mp h4 {font-family:monospace;font-size:14px;line-height:1.42857142857143}
  .mp h2 {font-size:16px;line-height:1.25}
  .mp h1 {font-size:20px;line-height:2}
  .mp {text-align:justify;background:#fff}
  .mp,.mp code,.mp pre,.mp pre code,.mp tt,.mp kbd,.mp samp {color:#131211}
  .mp h1,.mp h2,.mp h3,.mp h4 {color:#030201}
  .mp u {text-decoration:underline}
  .mp code,.mp strong,.mp b {font-weight:bold;color:#131211}
  .mp em,.mp var {font-style:italic;color:#232221;text-decoration:none}
  .mp a,.mp a:link,.mp a:hover,.mp a code,.mp a pre,.mp a tt,.mp a kbd,.mp a samp {color:#0000ff}
  .mp b.man-ref {font-weight:normal;color:#434241}
  .mp pre {padding:0 4ex}
  .mp pre code {font-weight:normal;color:#434241}
  .mp h2+pre,h3+pre {padding-left:0}
  ol.man-decor,ol.man-decor li {margin:3px 0 10px 0;padding:0;float:left;width:33%;list-style-type:none;text-transform:uppercase;color:#999;letter-spacing:1px}
  ol.man-decor {width:100%}
  ol.man-decor li.tl {text-align:left}
  ol.man-decor li.tc {text-align:center;letter-spacing:4px}
  ol.man-decor li.tr {text-align:right;float:right}
  </style>
  <style type='text/css' media='all'>
  /* style: toc */
  .man-navigation {display:block !important;position:fixed;top:0;left:113ex;height:100%;width:100%;padding:48px 0 0 0;border-left:1px solid #dbdbdb;background:#eee}
  .man-navigation a,.man-navigation a:hover,.man-navigation a:link,.man-navigation a:visited {display:block;margin:0;padding:5px 2px 5px 30px;color:#999;text-decoration:none}
  .man-navigation a:hover {color:#111;text-decoration:underline}
  </style>
</head>
<!--
  The following styles are deprecated and will be removed at some point:
  div#man, div#man ol.man, div#man ol.head, div#man ol.man.

  The .man-page, .man-decor, .man-head, .man-foot, .man-title, and
  .man-navigation should be used instead.
-->
<body id='manpage'>
  <div class='mp' id='man'>

  <div class='man-navigation' style='display:none'>
    <a href="#NAME">NAME</a>
    <a href="#SYNOPSIS">SYNOPSIS</a>
    <a href="#DESCRIPTION">DESCRIPTION</a>
    <a href="#OPTIONS">OPTIONS</a>
    <a href="#LOG-LEVELS">LOG LEVELS</a>
    <a href="#OUTPUT-FORMATS">OUTPUT FORMATS</a>
    <a href="#DTRACE-SUPPORT">DTRACE SUPPORT</a>
    <a href="#ENVIRONMENT">ENVIRONMENT</a>
    <a href="#PROJECT-BUGS">PROJECT &amp; BUGS</a>
    <a href="#LICENSE">LICENSE</a>
    <a href="#COPYRIGHT">COPYRIGHT</a>
  </div>

  <ol class='man-decor man-head man head'>
    <li class='tl'>bunyan(1)</li>
    <li class='tc'>bunyan manual</li>
    <li class='tr'>bunyan(1)</li>
  </ol>

  <h2 id="NAME">NAME</h2>
<p class="man-name">
  <code>bunyan</code> - <span class="man-whatis">filter and pretty-print Bunyan log file content</span>
</p>

<h2 id="SYNOPSIS">SYNOPSIS</h2>

<p><code>bunyan</code> [OPTIONS]</p>

<p>... | <code>bunyan</code> [OPTIONS]</p>

<p><code>bunyan</code> [OPTIONS] -p PID</p>

<h2 id="DESCRIPTION">DESCRIPTION</h2>

<p>"Bunyan" is <strong>a simple and fast a JSON logging library</strong> for node.js services,
a one-JSON-object-per-line log format, and <strong>a <code>bunyan</code> CLI tool</strong> for nicely
viewing those logs. This man page describes the latter.</p>

<h3 id="Pretty-printing">Pretty-printing</h3>

<p>A bunyan log file is a stream of JSON objects, optionally interspersed with
non-JSON log lines. The primary usage of <a href="bunyan.1.html" class="man-ref">bunyan<span class="s">(1)</span></a> is to pretty print,
for example:</p>

<pre><code>$ bunyan foo.log          # or `cat foo.log | bunyan
[2012-02-08T22:56:52.856Z]  INFO: myservice/123 on example.com: My message
    extra: multi
    line
[2012-02-08T22:56:54.856Z] ERROR: myservice/123 on example.com: My message
...
</code></pre>

<p>By default the "long" output format is used. Use the <code>-o FORMAT</code> option to
emit other formats. E.g.:</p>

<pre><code>$ bunyan foo.log -o short
22:56:52.856Z  INFO myservice: My message
    extra: multi
    line
22:56:54.856Z ERROR myservice: My message
...
</code></pre>

<p>These will color the output if supported in your terminal.
See "OUTPUT FORMATS" below.</p>

<h3 id="Filtering">Filtering</h3>

<p>The <code>bunyan</code> CLI can also be used to filter a bunyan log. Use <code>-l LEVEL</code>
to filter by level:</p>

<pre><code>$ bunyan foo.log -l error       # show only 'error' level records
[2012-02-08T22:56:54.856Z] ERROR: myservice/123 on example.com: My message
</code></pre>

<p>Use <code>-c COND</code> to filter on a JavaScript expression returning true on the
record data. In the COND code, <code>this</code> refers to the record object:</p>

<pre><code>$ bunyan foo.log -c `this.three`     # show records with the 'extra' field
[2012-02-08T22:56:52.856Z]  INFO: myservice/123 on example.com: My message
    extra: multi
    line
</code></pre>

<h2 id="OPTIONS">OPTIONS</h2>

<dl>
<dt><code>-h</code>, <code>--help</code></dt><dd><p>Print this help info and exit.</p></dd>
<dt><code>--version</code></dt><dd><p>Print version of this command and exit.</p></dd>
<dt><code>-q</code>, <code>--quiet</code></dt><dd><p>Don't warn if input isn't valid JSON.</p></dd>
</dl>


<p>Dtrace options (only on dtrace-supporting platforms):</p>

<dl>
<dt><code>-p PID</code>, <code>-p NAME</code></dt><dd>Process bunyan:log-* probes from the process with the given PID.
Can be used multiple times, or specify all processes with '*',
or a set of processes whose command &amp; args match a pattern with
'-p NAME'.</dd>
</dl>


<p>Filtering options:</p>

<dl>
<dt><code>-l</code>, <code>--level LEVEL</code></dt><dd><p>Only show messages at or above the specified level. You can specify level
<em>names</em> or numeric values. (See 'Log Levels' below.)</p></dd>
<dt><code>-c COND</code>, <code>--condition COND</code></dt><dd><p>Run each log message through the condition and only show those that
resolve to a truish value. E.g. <code>-c 'this.pid == 123'</code>.</p></dd>
<dt><code>--strict</code></dt><dd><p>Suppress all but legal Bunyan JSON log lines. By default non-JSON, and
non-Bunyan lines are passed through.</p></dd>
</dl>


<p>Output options:</p>

<dl>
<dt class="flush"><code>--color</code></dt><dd><p>Colorize output. Defaults to try if output stream is a TTY.</p></dd>
<dt><code>--no-color</code></dt><dd><p>Force no coloring (e.g. terminal doesn't support it)</p></dd>
<dt><code>-o FORMAT</code>, <code>--output FORMAT</code></dt><dd><p>Specify an output format. One of <code>long</code> (the default), <code>short</code>, <code>json</code>,
<code>json-N</code>, <code>bunyan</code> (the native bunyan 0-indent JSON output) or <code>inspect</code>.</p></dd>
<dt class="flush"><code>-j</code></dt><dd><p>Shortcut for <code>-o json</code>.</p></dd>
<dt><code>-L</code>, <code>--time local</code></dt><dd><p>Display the time field in <em>local</em> time, rather than the default UTC
time.</p></dd>
</dl>


<h2 id="LOG-LEVELS">LOG LEVELS</h2>

<p>In Bunyan log records, then <code>level</code> field is a number. For the <code>-l|--level</code>
argument the level <strong>names</strong> are supported as shortcuts. In <code>-c|--condition</code>
scripts, uppercase symbols like "DEBUG" are defined for convenience.</p>

<pre><code>Level Name      Level Number    Symbol in COND Scripts
trace           10              TRACE
debug           20              DEBUG
info            30              INFO
warn            40              WARN
error           50              ERROR
fatal           60              FATAL
</code></pre>

<h2 id="OUTPUT-FORMATS">OUTPUT FORMATS</h2>

<pre><code>FORMAT NAME         DESCRIPTION
long (default)      The default output. Long form. Colored and "pretty".
                    'req' and 'res' and 'err' fields are rendered specially
                    as an HTTP request, HTTP response and exception
                    stack trace, respectively. For backward compat, the
                    name "paul" also works for this.
short               Like the default output, but more concise. Some
                    typically redundant fields are ellided.
json                JSON output, 2-space indentation.
json-N              JSON output, N-space indentation, e.g. "json-4"
bunyan              Alias for "json-0", the Bunyan "native" format.
inspect             Node.js `util.inspect` output.
</code></pre>

<h2 id="DTRACE-SUPPORT">DTRACE SUPPORT</h2>

<p>On systems that support DTrace (e.g., MacOS, FreeBSD, illumos derivatives
like SmartOS and OmniOS), Bunyan will create a DTrace provider (<code>bunyan</code>)
that makes available the following probes:</p>

<pre><code>log-trace
log-debug
log-info
log-warn
log-error
log-fatal
</code></pre>

<p>Each of these probes has a single argument: the string that would be
written to the log.  Note that when a probe is enabled, it will
fire whenever the corresponding function is called, even if the level of
the log message is less than that of any stream.</p>

<p>See <a href="https://github.com/trentm/node-bunyan#dtrace-support" data-bare-link="true">https://github.com/trentm/node-bunyan#dtrace-support</a> for more details
and the '-p PID' option above for convenience usage.</p>

<h2 id="ENVIRONMENT">ENVIRONMENT</h2>

<dl>
<dt><code>BUNYAN_NO_COLOR</code></dt><dd>Set to a non-empty value to force no output coloring. See '--no-color'.</dd>
</dl>


<h2 id="PROJECT-BUGS">PROJECT &amp; BUGS</h2>

<p><code>bunyan</code> is written in JavaScript and requires node.js (<code>node</code>). The project
lives at <a href="https://github.com/trentm/node-bunyan" data-bare-link="true">https://github.com/trentm/node-bunyan</a> and is published to npm as
"bunyan".</p>

<ul>
<li>README, Install notes: <a href="https://github.com/trentm/node-bunyan#readme" data-bare-link="true">https://github.com/trentm/node-bunyan#readme</a></li>
<li>Report bugs to <a href="https://github.com/trentm/node-bunyan/issues" data-bare-link="true">https://github.com/trentm/node-bunyan/issues</a>.</li>
<li>See the full changelog at: <a href="https://github.com/trentm/node-bunyan/blob/master/CHANGES.md" data-bare-link="true">https://github.com/trentm/node-bunyan/blob/master/CHANGES.md</a></li>
</ul>


<h2 id="LICENSE">LICENSE</h2>

<p>MIT License (see <a href="https://github.com/trentm/node-bunyan/blob/master/LICENSE.txt" data-bare-link="true">https://github.com/trentm/node-bunyan/blob/master/LICENSE.txt</a>)</p>

<h2 id="COPYRIGHT">COPYRIGHT</h2>

<p>node-bunyan is Copyright (c) 2012 Joyent, Inc. Copyright (c) 2012 Trent Mick.
All rights reserved.</p>


  <ol class='man-decor man-foot man foot'>
    <li class='tl'></li>
    <li class='tc'>January 2015</li>
    <li class='tr'>bunyan(1)</li>
  </ol>

  </div>
<a href="https://github.com/trentm/node-bunyan"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on GitHub"></a></body>
</html>


================================================
FILE: docs/bunyan.1.ronn
================================================
# bunyan(1) -- filter and pretty-print Bunyan log file content


## SYNOPSIS

`bunyan` \[OPTIONS\]

... | `bunyan` \[OPTIONS\]

`bunyan` \[OPTIONS\] -p PID


## DESCRIPTION

"Bunyan" is **a simple and fast a JSON logging library** for node.js services,
a one-JSON-object-per-line log format, and **a `bunyan` CLI tool** for nicely
viewing those logs. This man page describes the latter.


### Pretty-printing

A bunyan log file is a stream of JSON objects, optionally interspersed with
non-JSON log lines. The primary usage of bunyan(1) is to pretty print,
for example:

    $ bunyan foo.log          # or `cat foo.log | bunyan
    [2012-02-08T22:56:52.856Z]  INFO: myservice/123 on example.com: My message
        extra: multi
        line
    [2012-02-08T22:56:54.856Z] ERROR: myservice/123 on example.com: My message
    ...

By default the "long" output format is used. Use the `-o FORMAT` option to
emit other formats. E.g.:

    $ bunyan foo.log -o short
    22:56:52.856Z  INFO myservice: My message
        extra: multi
        line
    22:56:54.856Z ERROR myservice: My message
    ...

These will color the output if supported in your terminal.
See "OUTPUT FORMATS" below.


### Filtering

The `bunyan` CLI can also be used to filter a bunyan log. Use `-l LEVEL`
to filter by level:

    $ bunyan foo.log -l error       # show only 'error' level records
    [2012-02-08T22:56:54.856Z] ERROR: myservice/123 on example.com: My message

Use `-c COND` to filter on a JavaScript expression returning true on the
record data. In the COND code, `this` refers to the record object:

    $ bunyan foo.log -c `this.three`     # show records with the 'extra' field
    [2012-02-08T22:56:52.856Z]  INFO: myservice/123 on example.com: My message
        extra: multi
        line


## OPTIONS

  * `-h`, `--help`:
    Print this help info and exit.

  * `--version`:
    Print version of this command and exit.

  * `-q`, `--quiet`:
    Don't warn if input isn't valid JSON.

Dtrace options (only on dtrace-supporting platforms):

  * `-p PID`, `-p NAME`:
    Process bunyan:log-\* probes from the process with the given PID.
    Can be used multiple times, or specify all processes with '\*',
    or a set of processes whose command & args match a pattern with
    '-p NAME'.

Filtering options:

  * `-l`, `--level LEVEL`:
    Only show messages at or above the specified level. You can specify level
    *names* or numeric values. (See 'Log Levels' below.)

  * `-c COND`, `--condition COND`:
    Run each log message through the condition and only show those that
    resolve to a truish value. E.g. `-c 'this.pid == 123'`.

  * `--strict`:
    Suppress all but legal Bunyan JSON log lines. By default non-JSON, and
    non-Bunyan lines are passed through.

Output options:

  * `--color`:
    Colorize output. Defaults to try if output stream is a TTY.

  * `--no-color`:
    Force no coloring (e.g. terminal doesn't support it)

  * `-o FORMAT`, `--output FORMAT`:
    Specify an output format. One of `long` (the default), `short`, `json`,
    `json-N`, `bunyan` (the native bunyan 0-indent JSON output) or `inspect`.

  * `-j`:
    Shortcut for `-o json`.

  * `-L`, `--time local`:
    Display the time field in *local* time, rather than the default UTC
    time.


## LOG LEVELS

In Bunyan log records, then `level` field is a number. For the `-l|--level`
argument the level **names** are supported as shortcuts. In `-c|--condition`
scripts, uppercase symbols like "DEBUG" are defined for convenience.

    Level Name      Level Number    Symbol in COND Scripts
    trace           10              TRACE
    debug           20              DEBUG
    info            30              INFO
    warn            40              WARN
    error           50              ERROR
    fatal           60              FATAL


## OUTPUT FORMATS

    FORMAT NAME         DESCRIPTION
    long (default)      The default output. Long form. Colored and "pretty".
                        'req' and 'res' and 'err' fields are rendered specially
                        as an HTTP request, HTTP response and exception
                        stack trace, respectively. For backward compat, the
                        name "paul" also works for this.
    short               Like the default output, but more concise. Some
                        typically redundant fields are ellided.
    json                JSON output, 2-space indentation.
    json-N              JSON output, N-space indentation, e.g. "json-4"
    bunyan              Alias for "json-0", the Bunyan "native" format.
    inspect             Node.js `util.inspect` output.


## DTRACE SUPPORT

On systems that support DTrace (e.g., MacOS, FreeBSD, illumos derivatives
like SmartOS and OmniOS), Bunyan will create a DTrace provider (`bunyan`)
that makes available the following probes:

    log-trace
    log-debug
    log-info
    log-warn
    log-error
    log-fatal

Each of these probes has a single argument: the string that would be
written to the log.  Note that when a probe is enabled, it will
fire whenever the corresponding function is called, even if the level of
the log message is less than that of any stream.

See <https://github.com/trentm/node-bunyan#dtrace-support> for more details
and the '-p PID' option above for convenience usage.


## ENVIRONMENT

  * `BUNYAN_NO_COLOR`:
    Set to a non-empty value to force no output coloring. See '--no-color'.


## PROJECT & BUGS

`bunyan` is written in JavaScript and requires node.js (`node`). The project
lives at <https://github.com/trentm/node-bunyan> and is published to npm as
"bunyan".

* README, Install notes: <https://github.com/trentm/node-bunyan#readme>
* Report bugs to <https://github.com/trentm/node-bunyan/issues>.
* See the full changelog at: <https://github.com/trentm/node-bunyan/blob/master/CHANGES.md>


## LICENSE

MIT License (see <https://github.com/trentm/node-bunyan/blob/master/LICENSE.txt>)


## COPYRIGHT

node-bunyan is Copyright (c) 2012 Joyent, Inc. Copyright (c) 2012 Trent Mick.
All rights reserved.


================================================
FILE: docs/index.html
================================================
<a href="bunyan.1.html">bunyan(1) man page</a>


================================================
FILE: examples/err.js
================================================
// Example logging an error:

var http = require('http');
var bunyan = require('../lib/bunyan');
var util = require('util');

var log = bunyan.createLogger({
    name: 'myserver',
    serializers: {
        err: bunyan.stdSerializers.err,   // <--- use this
    }
});

try {
    throw new TypeError('boom');
} catch (err) {
    log.warn({err: err}, 'operation went boom: %s', err)   // <--- here
}

log.info(new TypeError('how about this?'))  // <--- alternatively this


try {
    throw 'boom string';
} catch (err) {
    log.error(err)
}

/* BEGIN JSSTYLED */
/**
 *
 *  $ node err.js  | ../bin/bunyan -j
 *  {
 *    "name": "myserver",
 *    "hostname": "banana.local",
 *    "err": {
 *      "stack": "TypeError: boom\n    at Object.<anonymous> (/Users/trentm/tm/node-bunyan/examples/err.js:15:9)\n    at Module._compile (module.js:411:26)\n    at Object..js (module.js:417:10)\n    at Module.load (module.js:343:31)\n    at Function._load (module.js:302:12)\n    at Array.0 (module.js:430:10)\n    at EventEmitter._tickCallback (node.js:126:26)",
 *      "name": "TypeError",
 *      "message": "boom"
 *    },
 *    "level": 4,
 *    "msg": "operation went boom: TypeError: boom",
 *    "time": "2012-02-02T04:42:53.206Z",
 *    "v": 0
 *  }
 *  $ node err.js  | .
Download .txt
gitextract_zuzdlxm5/

├── .github/
│   └── workflows/
│       ├── check.yml
│       └── test.yml
├── .gitignore
├── .npmignore
├── AUTHORS
├── CHANGES.md
├── CONTRIBUTING.md
├── LICENSE.txt
├── Makefile
├── README.md
├── TODO.md
├── bin/
│   └── bunyan
├── docs/
│   ├── bunyan.1
│   ├── bunyan.1.html
│   ├── bunyan.1.ronn
│   └── index.html
├── examples/
│   ├── err.js
│   ├── handle-fs-error.js
│   ├── hi.js
│   ├── level.js
│   ├── log-undefined-values.js
│   ├── long-running.js
│   ├── multi.js
│   ├── mute-by-envvars-stream.js
│   ├── raw-stream.js
│   ├── ringbuffer.js
│   ├── rot-specific-levels.js
│   ├── server.js
│   ├── specific-level-streams.js
│   ├── src.js
│   └── unstringifyable.js
├── lib/
│   └── bunyan.js
├── package.json
├── test/
│   ├── add-stream.test.js
│   ├── buffer.test.js
│   ├── child-behaviour.test.js
│   ├── cli-client-req.test.js
│   ├── cli-res.test.js
│   ├── cli.test.js
│   ├── corpus/
│   │   ├── all.log
│   │   ├── bogus.log
│   │   ├── client-req-with-address.log
│   │   ├── clientreqres.log
│   │   ├── content-length-0-res.log
│   │   ├── extrafield.log
│   │   ├── log1.log
│   │   ├── log2.log
│   │   ├── non-object-res.log
│   │   ├── old-crashers/
│   │   │   └── README.md
│   │   ├── res-header.log
│   │   ├── res-without-header.log
│   │   ├── simple.log
│   │   └── withreq.log
│   ├── ctor.test.js
│   ├── cycles.test.js
│   ├── dtrace/
│   │   └── dtrace.test.js
│   ├── error-event.test.js
│   ├── level.test.js
│   ├── log-some-loop.js
│   ├── log-some.js
│   ├── log.test.js
│   ├── other-api.test.js
│   ├── process-exit.js
│   ├── process-exit.test.js
│   ├── raw-stream.test.js
│   ├── ringbuffer.test.js
│   ├── safe-json-stringify-1.js
│   ├── safe-json-stringify-2.js
│   ├── safe-json-stringify-3.js
│   ├── safe-json-stringify-4.js
│   ├── safe-json-stringify.test.js
│   ├── serializers.test.js
│   ├── src.test.js
│   └── stream-levels.test.js
└── tools/
    ├── cutarelease.py
    ├── jsstyle
    ├── statsd-notes.txt
    ├── timechild.js
    ├── timeguard.js
    ├── timenop.js
    └── timesrc.js
Download .txt
SYMBOL INDEX (64 symbols across 23 files)

FILE: examples/hi.js
  function Wuzzle (line 21) | function Wuzzle(options) {

FILE: examples/log-undefined-values.js
  function replacer (line 25) | function replacer() {
  function LogUndefinedValuesStream (line 43) | function LogUndefinedValuesStream(stream) {

FILE: examples/long-running.js
  function randint (line 23) | function randint(n) {
  function randchoice (line 27) | function randchoice(array) {
  function logOne (line 49) | function logOne() {

FILE: examples/mute-by-envvars-stream.js
  function MuteByEnvVars (line 29) | function MuteByEnvVars(opts) {

FILE: examples/raw-stream.js
  function MyRawStream (line 12) | function MyRawStream() {}

FILE: examples/rot-specific-levels.js
  function SpecificLevelStream (line 6) | function SpecificLevelStream(levels, stream) {

FILE: examples/specific-level-streams.js
  function SpecificLevelStream (line 21) | function SpecificLevelStream(levels, stream) {

FILE: examples/src.js
  function doSomeFoo (line 10) | function doSomeFoo() {
  function Wuzzle (line 15) | function Wuzzle(options) {

FILE: lib/bunyan.js
  function objCopy (line 116) | function objCopy(obj) {
  function getCaller3Info (line 178) | function getCaller3Info() {
  function _indent (line 208) | function _indent(s, indent) {
  function _warn (line 222) | function _warn(msg, dedupKey) {
  function _haveWarned (line 232) | function _haveWarned(dedupKey) {
  function ConsoleRawStream (line 238) | function ConsoleRawStream() {}
  function resolveLevel (line 285) | function resolveLevel(nameOrNum) {
  function isWritable (line 306) | function isWritable(obj) {
  function Logger (line 351) | function Logger(options, _childOptions, _childSimple) {
  function mkRecord (line 933) | function mkRecord(log, minLevel, args) {
  function mkProbeArgs (line 1001) | function mkProbeArgs(str, log, minLevel, msgArgs) {
  function mkLogEmitter (line 1010) | function mkLogEmitter(minLevel) {
  function getFullErrorStack (line 1127) | function getFullErrorStack(ex)
  function safeCyclesSet (line 1156) | function safeCyclesSet() {
  function safeCyclesArray (line 1178) | function safeCyclesArray() {
  function fastAndSafeJsonStringify (line 1210) | function fastAndSafeJsonStringify(rec) {
  function del (line 1472) | function del() {
  function moves (line 1485) | function moves() {
  function finish (line 1512) | function finish() {
  function RingBuffer (line 1563) | function RingBuffer(options) {

FILE: test/add-stream.test.js
  function createLogger (line 15) | function createLogger() {

FILE: test/buffer.test.js
  function Catcher (line 16) | function Catcher() {

FILE: test/child-behaviour.test.js
  function CapturingStream (line 12) | function CapturingStream(recs) {

FILE: test/cli.test.js
  function objCopy (line 31) | function objCopy(from, to) {

FILE: test/error-event.test.js
  function MyErroringStream (line 72) | function MyErroringStream() {}

FILE: test/log.test.js
  function Catcher (line 63) | function Catcher() {

FILE: test/raw-stream.test.js
  function CapturingStream (line 13) | function CapturingStream(recs) {

FILE: test/serializers.test.js
  function CapturingStream (line 14) | function CapturingStream(recs) {
  function createLogger (line 21) | function createLogger(serializers, records) {
  function customSerializer (line 192) | function customSerializer(err) {

FILE: test/src.test.js
  function logSomething (line 8) | function logSomething(log) { log.info('something'); }
  function CapturingStream (line 16) | function CapturingStream(recs) {

FILE: tools/cutarelease.py
  class Error (line 37) | class Error(Exception):
  function cutarelease (line 44) | def cutarelease(project_name, version_files, dry_run=False):
  function _indent (line 261) | def _indent(s, indent='    '):
  function _tuple_from_version (line 264) | def _tuple_from_version(version):
  function _get_next_version_info (line 272) | def _get_next_version_info(version_info):
  function _version_from_version_info (line 277) | def _version_from_version_info(version_info):
  function _version_info_from_version (line 295) | def _version_info_from_version(version):
  function _parse_version_file (line 309) | def _parse_version_file(version_file):
  function parse_changelog (line 380) | def parse_changelog(changes_path):
  function query_yes_no (line 475) | def query_yes_no(question, default="yes"):
  function _capture_stdout (line 508) | def _capture_stdout(argv):
  class _NoReflowFormatter (line 513) | class _NoReflowFormatter(optparse.IndentedHelpFormatter):
    method format_description (line 515) | def format_description(self, description):
  function run (line 518) | def run(cmd):
  function _setup_command_prefix (line 535) | def _setup_command_prefix():
  function main (line 547) | def main(argv):

FILE: tools/timechild.js
  function fooSerializer (line 51) | function fooSerializer(obj) {

FILE: tools/timeguard.js
  function Collector (line 12) | function Collector() {}

FILE: tools/timenop.js
  function Collector (line 11) | function Collector() {}

FILE: tools/timesrc.js
  function Collector (line 12) | function Collector() {
Condensed preview — 81 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (436K chars).
[
  {
    "path": ".github/workflows/check.yml",
    "chars": 253,
    "preview": "name: Check Lint & Style\non: [push, pull_request]\njobs:\n  check:\n    runs-on: ubuntu-latest\n    steps:\n    - uses: actio"
  },
  {
    "path": ".github/workflows/test.yml",
    "chars": 1366,
    "preview": "name: Test\n\non:\n  push:\n    paths-ignore:\n    - 'docs/**'\n    - '*.md'\n  pull_request:\n    paths-ignore:\n    - 'docs/**'"
  },
  {
    "path": ".gitignore",
    "chars": 77,
    "preview": "/tmp\n/node_modules\n*.log\n!/test/corpus/*.log\n/*.tgz\n/test/log.test.rot.log.*\n"
  },
  {
    "path": ".npmignore",
    "chars": 55,
    "preview": "/tmp\n/node_modules\n*.log\n/examples\n/test\n/*.tgz\n/tools\n"
  },
  {
    "path": "AUTHORS",
    "chars": 1827,
    "preview": "Trent Mick (http://trentm.com)\nMark Cavage (https://github.com/mcavage)\nDave Pacheco (https://github.com/davepacheco)\nMi"
  },
  {
    "path": "CHANGES.md",
    "chars": 47201,
    "preview": "# bunyan Changelog\n\nSee [the bunyan@1.x changelog](https://github.com/trentm/node-bunyan/blob/1.x/CHANGES.md)\nfor detail"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 8950,
    "preview": "# Contributing to node-bunyan\n\nThanks for using node-bunyan and for considering contributing to it! Or perhaps\nyou are j"
  },
  {
    "path": "LICENSE.txt",
    "chars": 1105,
    "preview": "# This is the MIT license\n\nCopyright 2016 Trent Mick\nCopyright 2016 Joyent Inc.\n\nPermission is hereby granted, free of c"
  },
  {
    "path": "Makefile",
    "chars": 3981,
    "preview": "\nSHELL := bash\n\n#---- Tools\n\nTAP_EXEC := ./node_modules/.bin/tap\nSUDO := sudo\nifeq ($(shell uname -s),SunOS)\n\t# On SunOS"
  },
  {
    "path": "README.md",
    "chars": 46605,
    "preview": "[![npm version](https://img.shields.io/npm/v/bunyan.svg?style=flat)](https://www.npmjs.com/package/bunyan)\n[![Build Stat"
  },
  {
    "path": "TODO.md",
    "chars": 5688,
    "preview": "# v2\n\n- `createLogger(<config-and-fields>, <fields>)` changes (#460)\n    - see section below\n- the dtrace-provider thing"
  },
  {
    "path": "bin/bunyan",
    "chars": 55015,
    "preview": "#!/usr/bin/env node\n/**\n * Copyright 2020 Trent Mick\n * Copyright 2020 Joyent Inc.\n *\n * bunyan -- filter and pretty-pri"
  },
  {
    "path": "docs/bunyan.1",
    "chars": 6691,
    "preview": ".\\\" generated with Ronn/v0.7.3\n.\\\" http://github.com/rtomayko/ronn/tree/0.7.3\n.\n.TH \"BUNYAN\" \"1\" \"January 2015\" \"\" \"buny"
  },
  {
    "path": "docs/bunyan.1.html",
    "chars": 11769,
    "preview": "<!DOCTYPE html>\n<html>\n<head>\n  <meta http-equiv='content-type' value='text/html;charset=utf8'>\n  <meta name='generator'"
  },
  {
    "path": "docs/bunyan.1.ronn",
    "chars": 6054,
    "preview": "# bunyan(1) -- filter and pretty-print Bunyan log file content\n\n\n## SYNOPSIS\n\n`bunyan` \\[OPTIONS\\]\n\n... | `bunyan` \\[OPT"
  },
  {
    "path": "docs/index.html",
    "chars": 47,
    "preview": "<a href=\"bunyan.1.html\">bunyan(1) man page</a>\n"
  },
  {
    "path": "examples/err.js",
    "chars": 1810,
    "preview": "// Example logging an error:\n\nvar http = require('http');\nvar bunyan = require('../lib/bunyan');\nvar util = require('uti"
  },
  {
    "path": "examples/handle-fs-error.js",
    "chars": 1170,
    "preview": "// Example handling an fs error for a Bunyan-created\n// stream: we create a logger to a file that is read-only.\n\nvar fs "
  },
  {
    "path": "examples/hi.js",
    "chars": 833,
    "preview": "var bunyan = require('../lib/bunyan');\n\n// Basic usage.\nvar log = bunyan.createLogger({name: 'myapp', level: 'info', src"
  },
  {
    "path": "examples/level.js",
    "chars": 1106,
    "preview": "// Play with setting levels.\n//\n// TODO: put this in a damn test suite\n\nvar bunyan = require('../lib/bunyan'),\n    DEBUG"
  },
  {
    "path": "examples/log-undefined-values.js",
    "chars": 2004,
    "preview": "#!/usr/bin/env node\n/* BEGIN JSSTYLED */\n/**\n * <https://github.com/trentm/node-bunyan/pull/473> is a change to add a\n *"
  },
  {
    "path": "examples/long-running.js",
    "chars": 1771,
    "preview": "/*\n * A long-running process that does some periodic logging. Use bunyan with\n * it some of these ways:\n *\n * 1. Direct "
  },
  {
    "path": "examples/multi.js",
    "chars": 400,
    "preview": "var bunyan = require('../lib/bunyan');\nlog = bunyan.createLogger({\n    name: 'amon',\n    streams: [\n        {\n          "
  },
  {
    "path": "examples/mute-by-envvars-stream.js",
    "chars": 3063,
    "preview": "/*\n * Example of a MuteByEnvVars Bunyan stream to mute log records matching some\n * envvars. I.e. as a way to do:\n *    "
  },
  {
    "path": "examples/raw-stream.js",
    "chars": 919,
    "preview": "// Example of a \"raw\" stream in a Bunyan Logger. A raw stream is one to\n// which log record *objects* are written instea"
  },
  {
    "path": "examples/ringbuffer.js",
    "chars": 381,
    "preview": "/* Create a ring buffer that stores the last 100 records. */\nvar bunyan = require('..');\nvar ringbuffer = new bunyan.Rin"
  },
  {
    "path": "examples/rot-specific-levels.js",
    "chars": 1548,
    "preview": "var bunyan = require('./'),\n    safeCycles = bunyan.safeCycles;\nvar util = require('util');\n\n\nfunction SpecificLevelStre"
  },
  {
    "path": "examples/server.js",
    "chars": 2245,
    "preview": "// Example logging HTTP server request and response objects.\n\nvar http = require('http');\nvar bunyan = require('../lib/b"
  },
  {
    "path": "examples/specific-level-streams.js",
    "chars": 2069,
    "preview": "#!/usr/bin/env node\n/**\n * <https://github.com/trentm/node-bunyan/issues/130> was a request to have\n * bunyan core suppo"
  },
  {
    "path": "examples/src.js",
    "chars": 623,
    "preview": "// Show the usage of `src: true` config option to get log call source info in\n// log records (the `src` field).\n\nvar bun"
  },
  {
    "path": "examples/unstringifyable.js",
    "chars": 313,
    "preview": "// See how bunyan behaves with an un-stringify-able object.\nvar bunyan = require('../lib/bunyan');\n\nvar log = bunyan.cre"
  },
  {
    "path": "lib/bunyan.js",
    "chars": 49616,
    "preview": "/**\n * Copyright (c) 2017 Trent Mick.\n * Copyright (c) 2017 Joyent Inc.\n *\n * The bunyan logging library for node.js.\n *"
  },
  {
    "path": "package.json",
    "chars": 1109,
    "preview": "{\n  \"name\": \"bunyan\",\n  \"version\": \"2.0.5\",\n  \"description\": \"a JSON logging library for node.js services\",\n  \"author\": "
  },
  {
    "path": "test/add-stream.test.js",
    "chars": 771,
    "preview": "/*\n * Copyright (c) 2016 Trent Mick. All rights reserved.\n *\n * Test stream adding.\n */\n\nvar test = require('tap').test;"
  },
  {
    "path": "test/buffer.test.js",
    "chars": 1938,
    "preview": "/*\n * Copyright 2020 Trent Mick\n * Copyright 2012 Joyent Inc.\n *\n * Test logging with (accidental) usage of buffers.\n */"
  },
  {
    "path": "test/child-behaviour.test.js",
    "chars": 3626,
    "preview": "/*\n * Copyright 2020 Trent Mick\n *\n * Test some `<Logger>.child(...)` behaviour.\n */\n\nvar test = require('tap').test;\n\nv"
  },
  {
    "path": "test/cli-client-req.test.js",
    "chars": 2080,
    "preview": "/*\n * Copyright 2020 Trent Mick\n *\n * Test the bunyan CLI's handling of the \"client_req\" field.\n * \"client_req\" is a com"
  },
  {
    "path": "test/cli-res.test.js",
    "chars": 1543,
    "preview": "/*\n * Copyright 2020 Trent Mick\n *\n * Test the bunyan CLI's handling of the \"res\" field.\n */\n\nvar exec = require('child_"
  },
  {
    "path": "test/cli.test.js",
    "chars": 19727,
    "preview": "/*\n * Copyright 2020 Trent Mick\n *\n * Test the `bunyan` CLI.\n */\n\nvar p = console.warn;\nvar exec = require('child_proces"
  },
  {
    "path": "test/corpus/all.log",
    "chars": 1890,
    "preview": "# levels\n{\"name\":\"myservice\",\"pid\":123,\"hostname\":\"example.com\",\"level\":10,\"msg\":\"My message\",\"time\":\"2012-02-08T22:56:5"
  },
  {
    "path": "test/corpus/bogus.log",
    "chars": 32,
    "preview": "not a JSON line\n{\"hi\": \"there\"}\n"
  },
  {
    "path": "test/corpus/client-req-with-address.log",
    "chars": 311,
    "preview": "{\"name\":\"minfo\",\"hostname\":\"sharptooth.local\",\"pid\":66266,\"level\":10,\"client_req\":{\"method\":\"HEAD\",\"url\":\"/dap/stor\",\"ad"
  },
  {
    "path": "test/corpus/clientreqres.log",
    "chars": 447,
    "preview": "{\"name\":\"aclientreq\",\"hostname\":\"danger0.local\",\"pid\":23280,\"level\":10,\"client_req\":{\"method\":\"GET\",\"url\":\"/--ping\"},\"ms"
  },
  {
    "path": "test/corpus/content-length-0-res.log",
    "chars": 764,
    "preview": "{\"name\":\"myservice\",\"hostname\":\"example.com\",\"pid\":123,\"level\":30,\"client_res\":{\"statusCode\":401,\"headers\":{\"content-typ"
  },
  {
    "path": "test/corpus/extrafield.log",
    "chars": 142,
    "preview": "{\"name\":\"myservice\",\"pid\":123,\"hostname\":\"example.com\",\"level\":30,\"extra\":\"field\",\"msg\":\"My message\",\"time\":\"2012-02-08T"
  },
  {
    "path": "test/corpus/log1.log",
    "chars": 476,
    "preview": "{\"name\":\"agent1\",\"pid\":73267,\"hostname\":\"headnode\",\"level\":30,\"msg\":\"message\",\"time\":\"2012-05-08T16:57:55.586Z\",\"v\":0}\n{"
  },
  {
    "path": "test/corpus/log2.log",
    "chars": 595,
    "preview": "{\"name\":\"agent2\",\"pid\":73267,\"hostname\":\"headnode\",\"level\":30,\"msg\":\"message\",\"time\":\"2012-05-08T16:58:55.586Z\",\"v\":0}\n{"
  },
  {
    "path": "test/corpus/non-object-res.log",
    "chars": 300,
    "preview": "{\"name\":\"cnapi.get_existing_nics\",\"job_uuid\":\"3499b13e-dbca-4331-b13a-f164c0da320a\",\"hostname\":\"710c784f-6aa5-428c-9074-"
  },
  {
    "path": "test/corpus/old-crashers/README.md",
    "chars": 91,
    "preview": "Log lines that used to crash `bunyan`.\nTypically the file name is the bunyan issue number.\n"
  },
  {
    "path": "test/corpus/res-header.log",
    "chars": 293,
    "preview": "{\"name\":\"res-header\",\"hostname\":\"danger0.local\",\"pid\":76488,\"level\":30,\"res\":{\"statusCode\":200,\"header\":\"HTTP/1.1 200 OK"
  },
  {
    "path": "test/corpus/res-without-header.log",
    "chars": 159,
    "preview": "{\"name\":\"res-header\",\"hostname\":\"danger0.local\",\"pid\":76488,\"level\":30,\"res\":{\"statusCode\":200},\"msg\":\"response sent\",\"t"
  },
  {
    "path": "test/corpus/simple.log",
    "chars": 126,
    "preview": "{\"name\":\"myservice\",\"pid\":123,\"hostname\":\"example.com\",\"level\":30,\"msg\":\"My message\",\"time\":\"2012-02-08T22:56:52.856Z\",\""
  },
  {
    "path": "test/corpus/withreq.log",
    "chars": 1997,
    "preview": "{\"name\":\"amon-master\",\"hostname\":\"9724a190-27b6-4fd8-830b-a574f839c67d\",\"pid\":12859,\"route\":\"HeadAgentProbes\",\"req_id\":\""
  },
  {
    "path": "test/ctor.test.js",
    "chars": 4871,
    "preview": "/*\n * Copyright 2020 Trent Mick\n *\n * Test type checking on creation of the Logger.\n */\n\nvar test = require('tap').test;"
  },
  {
    "path": "test/cycles.test.js",
    "chars": 2441,
    "preview": "/*\n * Copyright 2020 Trent Mick.\n *\n * Make sure cycles are safe.\n */\n\nvar Logger = require('../lib/bunyan.js');\nvar tes"
  },
  {
    "path": "test/dtrace/dtrace.test.js",
    "chars": 3741,
    "preview": "/*\n * Copyright 2020 Trent Mick\n *\n * If available, test dtrace support.\n */\n\nvar spawn = require('child_process').spawn"
  },
  {
    "path": "test/error-event.test.js",
    "chars": 4229,
    "preview": "/*\n * Copyright 2020 Trent Mick\n *\n * Test emission and handling of 'error' event in a logger with a 'path'\n * stream.\n "
  },
  {
    "path": "test/level.test.js",
    "chars": 2451,
    "preview": "/*\n * Copyright 2020 Trent Mick\n *\n * Test the `log.level(...)`.\n */\n\nvar test = require('tap').test;\nvar util = require"
  },
  {
    "path": "test/log-some-loop.js",
    "chars": 426,
    "preview": "\n// A helper script to log a few times, pause, repeat. We attempt to NOT emit\n// to stdout or stderr because this is use"
  },
  {
    "path": "test/log-some.js",
    "chars": 367,
    "preview": "\n// A helper script to log a few times. We attempt to NOT emit\n// to stdout or stderr because this is used for dtrace te"
  },
  {
    "path": "test/log.test.js",
    "chars": 8040,
    "preview": "/*\n * Copyright 2020 Trent Mick\n *\n * Test the `log.trace(...)`, `log.debug(...)`, ..., `log.fatal(...)` API.\n */\n\nvar u"
  },
  {
    "path": "test/other-api.test.js",
    "chars": 1248,
    "preview": "/*\n * Copyright 2020 Trent Mick\n *\n * Test other parts of the exported API.\n */\n\nvar test = require('tap').test;\n\nvar bu"
  },
  {
    "path": "test/process-exit.js",
    "chars": 259,
    "preview": "var bunyan = require('../lib/bunyan');\nvar log = bunyan.createLogger({\n    name: 'default',\n    streams: [ {\n        typ"
  },
  {
    "path": "test/process-exit.test.js",
    "chars": 805,
    "preview": "'use strict';\n/*\n * Test that bunyan process will terminate.\n *\n * Note: Currently (bunyan 0.23.1) this fails on node 0."
  },
  {
    "path": "test/raw-stream.test.js",
    "chars": 2693,
    "preview": "/*\n * Copyright 2020 Trent Mick\n *\n * Test `type: 'raw'` Logger streams.\n */\n\nvar format = require('util').format;\nvar t"
  },
  {
    "path": "test/ringbuffer.test.js",
    "chars": 1061,
    "preview": "/*\n * Test the RingBuffer output stream.\n */\n\nvar test = require('tap').test;\n\nvar Logger = require('../lib/bunyan');\n\nv"
  },
  {
    "path": "test/safe-json-stringify-1.js",
    "chars": 267,
    "preview": "var bunyan = require('../lib/bunyan');\n\nvar log = bunyan.createLogger({\n    name: 'safe-json-stringify-1'\n});\n\nvar obj ="
  },
  {
    "path": "test/safe-json-stringify-2.js",
    "chars": 321,
    "preview": "process.env.BUNYAN_TEST_NO_SAFE_JSON_STRINGIFY = '1';\nvar bunyan = require('../lib/bunyan');\n\nvar log = bunyan.createLog"
  },
  {
    "path": "test/safe-json-stringify-3.js",
    "chars": 446,
    "preview": "var bunyan = require('../lib/bunyan');\n\nvar log = bunyan.createLogger({\n    name: 'safe-json-stringify-3'\n});\n\n// And us"
  },
  {
    "path": "test/safe-json-stringify-4.js",
    "chars": 500,
    "preview": "process.env.BUNYAN_TEST_NO_SAFE_JSON_STRINGIFY = '1';\nvar bunyan = require('../lib/bunyan');\n\nvar log = bunyan.createLog"
  },
  {
    "path": "test/safe-json-stringify.test.js",
    "chars": 2150,
    "preview": "/*\n * Copyright 2020 Trent Mick\n *\n * If available, use `safe-json-stringfy` as a fallback stringifier.\n * This covers t"
  },
  {
    "path": "test/serializers.test.js",
    "chars": 10248,
    "preview": "/*\n * Copyright 2020 Trent Mick\n *\n * Test the standard serializers in Bunyan.\n */\n\nvar http = require('http');\nvar test"
  },
  {
    "path": "test/src.test.js",
    "chars": 1128,
    "preview": "/*\n * Copyright 2020 Trent Mick.\n *\n * Test `src: true` usage.\n */\n\n// Intentionally on line 8 for tests below:\nfunction"
  },
  {
    "path": "test/stream-levels.test.js",
    "chars": 4186,
    "preview": "/*\n * Copyright 2020 Trent Mick\n *\n * Test that streams (the various way they can be added to\n * a Logger instance) get "
  },
  {
    "path": "tools/cutarelease.py",
    "chars": 22095,
    "preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n# Copyright (c) 2009-2012 Trent Mick\n\n\"\"\"cutarelease -- Cut a release of y"
  },
  {
    "path": "tools/jsstyle",
    "chars": 24945,
    "preview": "#!/usr/bin/env perl\n#\n# CDDL HEADER START\n#\n# The contents of this file are subject to the terms of the\n# Common Develop"
  },
  {
    "path": "tools/statsd-notes.txt",
    "chars": 386,
    "preview": "\n# building pycairo (needed for graphite)\n\nwget http://cairographics.org/releases/pycairo-1.10.0.tar.bz2\n \nhack pycairo "
  },
  {
    "path": "tools/timechild.js",
    "chars": 2125,
    "preview": "#!/usr/bin/env node\n/*\n * Time `log.child(...)`.\n *\n * Getting 0.011ms on my Mac. For about 1000 req/s that means that t"
  },
  {
    "path": "tools/timeguard.js",
    "chars": 4404,
    "preview": "#!/usr/bin/env node\n/*\n * Time logging with/without a try/catch-guard on the JSON.stringify\n * and other code options ar"
  },
  {
    "path": "tools/timenop.js",
    "chars": 583,
    "preview": "#!/usr/bin/env node\n/*\n * Time logging below the current level, which should do very little work.\n */\n\nconsole.log('Time"
  },
  {
    "path": "tools/timesrc.js",
    "chars": 821,
    "preview": "#!/usr/bin/env node\n/*\n * Time 'src' fields (getting log call source info). This is expensive.\n */\n\nconsole.log('Time ad"
  }
]

About this extraction

This page contains the full source code of the trentm/node-bunyan GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 81 files (402.5 KB), approximately 113.8k tokens, and a symbol index with 64 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!