[
  {
    "path": ".github/workflows/check.yml",
    "content": "name: Check Lint & Style\non: [push, pull_request]\njobs:\n  check:\n    runs-on: ubuntu-latest\n    steps:\n    - uses: actions/checkout@v2\n    - uses: actions/setup-node@v1\n      with:\n        node-version: '12.x'\n    - run: npm ci\n    - run: npm run check\n"
  },
  {
    "path": ".github/workflows/test.yml",
    "content": "name: Test\n\non:\n  push:\n    paths-ignore:\n    - 'docs/**'\n    - '*.md'\n  pull_request:\n    paths-ignore:\n    - 'docs/**'\n    - '*.md'\n\njobs:\n  # Test once on every (available) plat, using LTS node version\n  # (https://nodejs.org/en/about/releases/).\n  test-plats:\n    strategy:\n      matrix:\n        os: [ubuntu-latest, windows-latest, macos-latest]\n    runs-on: ${{ matrix.os }}\n    steps:\n    - uses: actions/checkout@v2\n    - uses: actions/setup-node@v1\n      with:\n        node-version: '12.x'\n    - run: npm ci\n    - run: npm test\n\n  # Test once for every supported node version (don't repeat the LTS\n  # node version from the previous step). Only test on one\n  # platform to not overkill the number of builds.\n  test-vers:\n    strategy:\n      matrix:\n        node: ['8.x', '10.x', '14.x']\n    runs-on: ubuntu-latest\n    steps:\n    - uses: actions/checkout@v2\n    - uses: actions/setup-node@v1\n      with:\n        node-version: ${{ matrix.node }}\n    - run: npm ci\n    - run: npm test\n\n  # Test older versions separately because really old node/npm don't support\n  # 'npm ci'.\n  test-old-vers:\n    strategy:\n      matrix:\n        node: ['0.10.x', '4.x', '6.x']\n    runs-on: ubuntu-latest\n    steps:\n    - uses: actions/checkout@v2\n    - uses: actions/setup-node@v1\n      with:\n        node-version: ${{ matrix.node }}\n    - run: npm install\n    - run: npm test\n"
  },
  {
    "path": ".gitignore",
    "content": "/tmp\n/node_modules\n*.log\n!/test/corpus/*.log\n/*.tgz\n/test/log.test.rot.log.*\n"
  },
  {
    "path": ".npmignore",
    "content": "/tmp\n/node_modules\n*.log\n/examples\n/test\n/*.tgz\n/tools\n"
  },
  {
    "path": "AUTHORS",
    "content": "Trent Mick (http://trentm.com)\nMark Cavage (https://github.com/mcavage)\nDave Pacheco (https://github.com/davepacheco)\nMichael Hart (https://github.com/mhart)\nIsaac Schlueter (https://github.com/isaacs)\nRob Gulewich (https://github.com/rgulewich)\nBryan Cantrill (https://github.com/bcantrill)\nMichael Hart (https://github.com/mhart)\nSimon Wade (https://github.com/aexmachina)\nhttps://github.com/glenn-murray-bse\nChakrit Wichian (https://github.com/chakrit)\nPatrick Mooney (https://github.com/pfmooney)\nJohan Nordberg (https://github.com/jnordberg)\nhttps://github.com/timborodin\nRyan Graham (https://github.com/rmg)\nAlex Kocharin (https://github.com/rlidwka)\nAndrei Neculau (https://github.com/andreineculau)\nMihai Tomescu (https://github.com/matomesc)\nDaniel Juhl (https://github.com/danieljuhl)\nChris Barber (https://github.com/cb1kenobi)\nManuel Schneider (https://github.com/manuelschneider)\nMartin Gausby (https://github.com/gausby)\nStéphan Kochen (https://github.com/stephank)\nShakeel Mohamed (https://github.com/shakeelmohamed)\nDenis Izmaylov (https://github.com/DenisIzmaylov)\nGuillermo Grau Panea (https://github.com/guigrpa)\nMark LeMerise (https://github.com/MarkLeMerise)\nhttps://github.com/sometimesalready\nCharly Koza (https://github.com/Cactusbone)\nThomas Heymann (https://github.com/cyberthom)\nDavid M. Lee (https://github.com/leedm777)\nMarc Udoff (https://github.com/mlucool)\nMark Stosberg (https://github.com/markstos)\nAlexander Ray (https://github.com/aray12)\nAdam Lynch (https://github.com/adam-lynch)\nMichael Nisi (https://github.com/michaelnisi)\nMartijn Schrage (https://github.com/Oblosys)\nPaul Milham (https://github.com/domrein)\nFrankie O'Rourke (https://github.com/psfrankie)\nCody Mello (https://github.com/melloc)\nTodd Whiteman (https://github.com/twhiteman)\nZach Bjornson (https://github.com/zbjornson)\n"
  },
  {
    "path": "CHANGES.md",
    "content": "# bunyan Changelog\n\nSee [the bunyan@1.x changelog](https://github.com/trentm/node-bunyan/blob/1.x/CHANGES.md)\nfor details on recent 1.x releases.\n\nKnown issues:\n\n- [issue #58] Can't install to a dir with spaces. This is [this node-gyp\n  bug](https://github.com/TooTallNate/node-gyp/issues/65).\n\n\n## not yet released\n\n(nothing yet)\n\n## 2.0.5 (beta)\n\n- [pull #575, #278] Change the default \"req\" serializer to accept expressjs's\n  `req.originalUrl` for the \"url\" field per\n  <https://expressjs.com/en/api.html#req.originalUrl>. (By @twelve17 and\n  @kingcody.)\n- Development change: Switch to node-tap for testing (from nodeunit, which is\n  now obsolete). Currently just tap v9 because that is the last major version\n  of node-tap that supports back to node v0.10.\n\n## 2.0.4 (beta)\n\n- [pull #558] Update minimum \"moment\" version to 2.19.3 for CVE-2017-18214.\n- [issue #589] Use `os.EOL` for newlines in bunyan output, which helps with\n  some Unix-EOL-naive apps like notepad. (By @bwknight877.)\n- Development change: Switched to GitHub Actions for CI.\n\n## 2.0.3 (beta)\n\n- Fix a vulnerability from a crafted argument to 'bunyan -p ARG'\n\n  This was reported privately as:\n      https://hackerone.com/reports/902739\n      bunyan - RCE via insecure command formatting\n\n  Previous to this version the 'bunyan' CLI was not escaping a given argument\n  to the '-p' option before executing `ps -A -o pid,command | grep '$ARG'`\n  which could lead to unintended execution.\n\n  (This same change is also in bunyan@1.8.13.)\n\n## 2.0.2 (beta)\n\n- [issue #444] Fix the `bunyan` CLI to not duplicate the \"HTTP/1.1 ...\" status\n  line when serializing a \"res\" field.\n\n\n## 2.0.1 (beta)\n\n- [issue #504] **Backward incompatible change to the `bunyan` CLI:**\n  The `bunyan` CLI no longer adds a `Host` header when rendering a `client_req`\n  field in a log record. In 1.x it used to do this (using `client_req.address`\n  and `client_req.port`), on the *guess* that Node.js' `http.ClientRequest`\n  handling would add it.  However, the guess can be wrong and misleading. It is\n  better not to guess.\n\n\n## 2.0.0 (beta)\n\n- [issue #499] Fix some `bunyan` CLI exit handling problems.\n\n\n## 1.8.10\n\n- Ensure that `bunyan` errors out if attempting to use `-p PID` and\n  file args at the same time.\n\n\n## 1.8.9\n\n- [pull #409, issue #246] Revert a change added to the `bunyan` CLI version\n  1.0.1 where `SIGINT` was ignored, such that Ctrl+C could not be used to\n  terminate bunyan. (By @zbjornson and @davepacheco.)\n- [pull #469] Fix a strict mode (`\"use strict;\"`) error in some versions of\n  Safari.\n\n\n## 1.8.8\n\n- Fix breakage due to a silly last minute \"fix 'make check'\".\n\n\n## 1.8.7\n\nNote: *Bad release.* Use 1.8.8 or later.\n\n- [issue #484] Fix breakage due to #474 in previous release.\n\n\n## 1.8.6\n\nNote: *Bad release.* Use 1.8.7 or later.\n\n- [issue #474] Bunyan's `safeCycles` is too slow when logging large objects.\n\n\n## 1.8.5\n\n- [issue #401] Improved performance when using disabled log levels.\n\n\n## 1.8.4\n\n- [issue #454] Fix `src` usage with node v7.\n\n\n## 1.8.3\n\n- [issue #450] Fix `log.info(null)` crash that resulted from #426 in v1.8.2.\n\n\n## 1.8.2\n\n- [issue #449] Bump dtrace-provider dep to 0.7.0 to help avoid deprecation\n  warnings with node v6 in some cases.\n- [issue #426] Ensure `log.info({err: err})` results in a \"msg\" value, just\n  like `log.info(err)`.\n\n\n## 1.8.1\n\n- [pull #386] Fix bad bug in rotation that could cause a crash with\n  error message \"cannot start a rotation when already rotating\"\n  (by Frankie O'Rourke). The bug was introduced in 1.8.0.\n\n\n## 1.8.0\n\nNote: *Bad release.* An addition in this release broke 'rotating-file' usage.\nUse 1.8.1 or later.\n\n- [issue #370] Fix `bunyan -p ...` (i.e. DTrace integration) on node\n  4.x and 5.x.\n- [issue #329, pull #330] Update the 'rotating-file' stream to do a file\n  rotation on initialization if the mtime on the file path indicates the\n  last rotation time was missed -- i.e. if the app wasn't running at the\n  time. (by Paul Milham.)\n\n\n## 1.7.1\n\n- [issue #332, pull #355] Ensure stream for type='stream' stream is a writable\n  stream. (By Michael Nisi.)\n\n- [issue #344] Fix \"rotating-file\" Bunyan streams to not miss rotations when configured\n  for a period greater than approximately 25 days. Before this there was an issue\n  where periods greater than node.js's maximum `setTimeout` length would fail to rotate.\n  (By Martijn Schrage.)\n\n- [issue #234, pull #345] Improve `bunyan` CLI rendering of \"res\" field\n  HTTP responses to not show two blank lines for an empty body.\n  (By Michael Nisi.)\n\n\n## 1.7.0\n\n- [pull #311, #302, #310] Improve the runtime environment detection to fix\n  running under [NW.js](http://nwjs.io/). Contributions by Adam Lynch, Jeremy\n  Ruppel, and Aleksey Timchenko.\n\n- [pull #318] Add `reemitErrorEvents` optional boolean for streams added to a\n  Bunyan logger to control whether an \"error\" event on the stream will be\n  re-emitted on the `Logger` instance.\n\n        var log = bunyan.createLogger({\n            name: 'foo',\n            streams: [\n                {\n                    type: 'raw',\n                    stream: new MyCustomStream(),\n                    reemitErrorEvents: true\n                }\n            ]\n        });\n\n  Before this change, \"error\" events were re-emitted on [`file`\n  streams](https://github.com/trentm/node-bunyan#stream-type-file) only. The new\n  behaviour is as follows:\n\n    - `reemitErrorEvents` not specified: `file` streams will re-emit error events\n      on the Logger instance.\n    - `reemitErrorEvents: true`: error events will be re-emitted on the Logger\n      for any stream with a `.on()` function -- which includes file streams,\n      process.stdout/stderr, and any object that inherits from EventEmitter.\n    - `reemitErrorEvents: false`: error events will not be re-emitted for any\n      streams.\n\n  Dev Note: Bunyan `Logger` objects don't currently have a `.close()` method\n  in which registered error event handlers can be *un*registered. That means\n  that a (presumably rare) situation where code adds dozens of Bunyan Logger\n  streams to, e.g. process.stdout, and with `reemitErrorEvents: true`, could\n  result in leaking Logger objects.\n\n  Original work for allowing \"error\" re-emitting on non-file streams is\n  by Marc Udoff in pull #318.\n\n\n## 1.6.0\n\n- [pull #304, issue #245] Use [Moment.js][momentjs.com] library to handle\n  `bunyan` CLI time formatting in some cases, especially to fix display of\n  local time. It is now required for local time formatting (i.e. `bunyan -L`\n  or `bunyan --time local`). (By David M. Lee.)\n\n- [pull #252] Fix errant `client_res={}` in `bunyan` CLI rendering, and avoid\n  extra newlines in `client_req` rendering in some cases. (By Thomas Heymann.)\n\n- [pull #291, issue #303] Fix `LOG.child(...)` to *not* override the \"hostname\"\n  field of the parent. A use case is when one manually sets \"hostname\" to\n  something other than `os.hostname()`. (By github.com/Cactusbone.)\n\n- [issue #325] Allow one to set `level: 0` in `createLogger` to turn on\n  logging for all levels. (Adapted from #336 by github.com/sometimesalready.)\n\n- Add guards (to `resolveLevel`) so that all \"level\" values are validated.\n  Before this, a bogus level like \"foo\" or -12 or `['some', 'array']` would\n  silently be accepted -- with undefined results.\n\n- Doc updates for #340 and #305.\n\n- Update `make test` to test against node 5, 4, 0.12 and 0.10.\n\n\n## 1.5.1\n\n- [issue #296] Fix `src: true`, which was broken in v1.5.0.\n\n\n## 1.5.0\n\nNote: *Bad release.* The addition of `'use strict';` broke Bunyan's `src: true`\nfeature. Use 1.5.1 instead.\n\n- [pull #236, issue #231, issue #223] Fix strict mode in the browser.\n- [pull #282, issue #213] Fixes bunyan to work with webpack. By Denis Izmaylov.\n- [pull #294] Update to dtrace-provider 0.6 to fix with node 4.0 and io.js 3.0.\n- Dropped support for 0.8 (can't install deps easily anymore for running\n  test suite). Bump to a recent iojs version for testing.\n\n\n## 1.4.0\n\n(Bumping minor ver b/c I'm wary of dtrace-provider changes. :)\n\n- [issue #258, pull #259] Update to dtrace-provider 0.5 to fix\n  install and tests on recent io.js versions.\n- safe-json-stringify@1.0.3 changed output, breaking some tests. Fix those.\n\n\n## 1.3.6\n\n- [issue #244] Make `bunyan` defensive on `res.header=null`.\n\n\n## 1.3.5\n\n- [issue #233] Make `bunyan` defensive on res.header as a boolean.\n- [issue #242] Make `bunyan` defensive on err.stack not being a string.\n\n\n## 1.3.4\n\n- Allow `log.child(...)` to work even if the logger is a *sub-class*\n  of Bunyan's Logger class.\n- [issue #219] Hide 'source-map-support' require from browserify.\n- [issue #218] Reset `haveNonRawStreams` on `<logger>.addStream`.\n\n\n## 1.3.3\n\n- [pull #127] Update to dtrace-provider 0.4.0, which gives io.js 1.x support\n  for dtrace-y parts of Bunyan.\n\n\n## 1.3.2\n\n- [pull #182] Fallback to using the optional 'safe-json-stringify' module\n  if `JSON.stringify` throws -- possibly with an enumerable property\n  getter than throws. By Martin Gausby.\n\n\n## 1.3.1\n\n- Export `bunyan.RotatingFileStream` which is needed if one wants to\n  customize it. E.g. see issue #194.\n\n- [pull #122] Source Map support for caller line position for [the \"src\"\n  field](https://github.com/trentm/node-bunyan#src). This could be interesting\n  for [CoffeeScript](http://coffeescript.org/documentation/docs/sourcemap.html)\n  users of Bunyan. By Manuel Schneider.\n\n- [issue #164] Ensure a top-level `level` given in `bunyan.createLogger`\n  is *used* for given `streams`. For example, ensure that the following\n  results in the stream having a DEBUG level:\n\n        var log = bunyan.createLogger({\n            name: 'foo',\n            level: 'debug',\n            streams: [\n                {\n                    path: '/var/tmp/foo.log'\n                }\n            ]\n        });\n\n  This was broken in the 1.0.1 release. Between that release and 1.3.0\n  the \"/var/tmp/foo.log\" stream would be at the INFO level (Bunyan's\n  default level).\n\n\n## 1.3.0\n\n- [issue #103] `bunyan -L` (or `bunyan --time local`) to show local time.\n  Bunyan log records store `time` in UTC time. Sometimes it is convenient\n  to display in local time.\n\n- [issue #205] Fix the \"The Bunyan CLI crashed!\" checking to properly warn of\n  the common failure case when `-c CONDITION` is being used.\n\n\n## 1.2.4\n\n- [issue #210] Export `bunyan.nameFromLevel` and `bunyan.levelFromName`. It can\n  be a pain for custom streams to have to reproduce that.\n\n- [issue #100] Gracefully handle the case of an unbound\n  `Logger.{info,debug,...}` being used for logging, e.g.:\n\n        myEmittingThing.on('data', log.info)\n\n  Before this change, bunyan would throw. Now it emits a warning to stderr\n  *once*, and then silently ignores those log attempts, e.g.:\n\n        bunyan usage error: /Users/trentm/tm/node-bunyan/foo.js:12: attempt to log with an unbound log method: `this` is: { _events: { data: [Function] } }\n\n\n## 1.2.3\n\n- [issue #184] Fix log rotation for rotation periods > ~25 days. Before this\n  change, a rotation period longer than this could hit [the maximum setTimeout\n  delay in node.js](https://github.com/joyent/node/issues/8656). By Daniel Juhl.\n\n\n## 1.2.2\n\n- Drop the guard that a bunyan Logger level must be between TRACE (10)\n  and FATAL (60), inclusive. This allows a trick of setting the level\n  to `FATAL + 1` to turn logging off. While the standard named log levels are\n  the golden path, then intention was not to get in the way of using\n  other level numbers.\n\n\n## 1.2.1\n\n- [issue #178, #181] Get at least dtrace-provider 0.3.1 for\n  optionalDependencies to get a fix for install with decoupled npm (e.g. with\n  homebrew's node and npm).\n\n\n## 1.2.0\n\n- [issue #157] Restore dtrace-provider as a dependency (in\n  \"optionalDependencies\").\n\n  Dtrace-provider version 0.3.0 add build sugar that should eliminate the\n  problems from older versions:\n  The build is not attempted on Linux and Windows. The build spew is\n  *not* emitted by default (use `V=1 npm install` to see it); instead a\n  short warning is emitted if the build fails.\n\n  Also, importantly, the new dtrace-provider fixes working with node\n  v0.11/0.12.\n\n\n## 1.1.3\n\n- [issue #165] Include extra `err` fields in `bunyan` CLI output. Before\n  this change only the fields part of the typical node.js error stack\n  (err.stack, err.message, err.name) would be emitted, even though\n  the Bunyan *library* would typically include err.code and err.signal\n  in the raw JSON log record.\n\n\n## 1.1.2\n\n- Fix a breakage in `log.info(err)` on a logger with no serializers.\n\n\n## 1.1.1\n\nNote: *Bad release.* It breaks `log.info(err)` on a logger with no serializers.\nUse version 1.1.2.\n\n- [pull #168] Fix handling of `log.info(err)` to use the `log` Logger's `err`\n  serializer if it has one, instead of always using the core Bunyan err\n  serializer. (By Mihai Tomescu.)\n\n\n## 1.1.0\n\n- [issue #162] Preliminary support for [browserify](http://browserify.org/).\n  See [the section in the README](../README.md#browserify).\n\n\n## 1.0.1\n\n- [issues #105, #138, #151] Export `<Logger>.addStream(...)` and\n  `<Logger>.addSerializers(...)` to be able to add them after Logger creation.\n  Thanks @andreineculau!\n\n- [issue #159] Fix bad handling in construtor guard intending to allow\n  creation without \"new\": `var log = Logger(...)`. Thanks @rmg!\n\n- [issue #156] Smaller install size via .npmignore file.\n\n- [issue #126, #161] Ignore SIGINT (Ctrl+C) when processing stdin. `...| bunyan`\n  should expect the preceding process in the pipeline to handle SIGINT. While\n  it is doing so, `bunyan` should continue to process any remaining output.\n  Thanks @timborodin and @jnordberg!\n\n- [issue #160] Stop using ANSI 'grey' in `bunyan` CLI output, because of the\n  problems that causes with Solarized Dark themes (see\n  <https://github.com/altercation/solarized/issues/220>).\n\n\n## 1.0.0\n\n- [issue #87] **Backward incompatible change to `-c CODE`** improving\n  performance by over 10x (good!), with a backward incompatible change to\n  semantics (unfortunate), and adding some sugar (good!).\n\n  The `-c CODE` implementation was changed to use a JS function for processing\n  rather than `vm.runInNewContext`. The latter was specatularly slow, so\n  won't be missed. Unfortunately this does mean a few semantic differences in\n  the `CODE`, the most noticeable of which is that **`this` is required to\n  access the object fields:**\n\n        # Bad. Works with bunyan 0.x but not 1.x.\n        $ bunyan -c 'pid === 123' foo.log\n        ...\n\n        # Good. Works with all versions of bunyan\n        $ bunyan -c 'this.pid === 123' foo.log\n        ...\n\n  The old behaviour of `-c` can be restored with the `BUNYAN_EXEC=vm`\n  environment variable:\n\n        $ BUNYAN_EXEC=vm bunyan -c 'pid === 123' foo.log\n        ...\n\n  Some sugar was also added: the TRACE, DEBUG, ... constants are defined, so\n  one can:\n\n        $ bunyan -c 'this.level >= ERROR && this.component === \"http\"' foo.log\n        ...\n\n  And example of the speed improvement on a 10 MiB log example:\n\n        $ time BUNYAN_EXEC=vm bunyan -c 'this.level === ERROR' big.log | cat >slow\n\n        real    0m6.349s\n        user    0m6.292s\n        sys    0m0.110s\n\n        $ time bunyan -c 'this.level === ERROR' big.log | cat >fast\n\n        real    0m0.333s\n        user    0m0.303s\n        sys    0m0.028s\n\n  The change was courtesy Patrick Mooney (https://github.com/pfmooney). Thanks!\n\n- Add `bunyan -0 ...` shortcut for `bunyan -o bunyan ...`.\n\n- [issue #135] **Backward incompatible.** Drop dtrace-provider even from\n  `optionalDependencies`. Dtrace-provider has proven a consistent barrier to\n  installing bunyan, because it is a binary dep. Even as an *optional* dep it\n  still caused confusion and install noise.\n\n  Users of Bunyan on dtrace-y platforms (SmartOS, Mac, Illumos, Solaris) will\n  need to manually `npm install dtrace-provider` themselves to get [Bunyan's\n  dtrace support](https://github.com/trentm/node-bunyan#runtime-log-snooping-via-dtrace)\n  to work. If not installed, bunyan should stub it out properly.\n\n\n\n## 0.23.1\n\n- [pull #125, pull #97, issue #73] Unref rotating-file timeout which was\n  preventing processes from exiting (by https://github.com/chakrit and\n  https://github.com/glenn-murray-bse). Note: this only fixes the issue\n  for node 0.10 and above.\n\n\n## 0.23.0\n\n- [issue #139] Fix `bunyan` crash on a log record with `res.header` that is an\n  object. A side effect of this improvement is that a record with `res.statusCode`\n  but no header info will render a response block, for example:\n\n        [2012-08-08T10:25:47.637Z]  INFO: my-service/12859 on my-host: some message (...)\n            ...\n            --\n            HTTP/1.1 200 OK\n            --\n            ...\n\n- [pull #42] Fix `bunyan` crash on a log record with `req.headers` that is a *string*\n  (by https://github.com/aexmachina).\n\n- Drop node 0.6 support. I can't effectively `npm install` with a node 0.6\n  anymore.\n\n- [issue #85] Ensure logging a non-object/non-string doesn't throw (by\n  https://github.com/mhart). This changes fixes:\n\n        log.info(<bool>)     # TypeError: Object.keys called on non-object\n        log.info(<function>) # \"msg\":\"\" (instead of wanted \"msg\":\"[Function]\")\n        log.info(<array>)    # \"msg\":\"\" (instead of wanted \"msg\":util.format(<array>))\n\n\n## 0.22.3\n\n- Republish the same code to npm.\n\n\n## 0.22.2\n\nNote: Bad release. The published package in the npm registry got corrupted. Use 0.22.3 or later.\n\n- [issue #131] Allow `log.info(<number>)` and, most importantly, don't crash on that.\n\n- Update 'mv' optional dep to latest.\n\n\n## 0.22.1\n\n- [issue #111] Fix a crash when attempting to use `bunyan -p` on a platform without\n  dtrace.\n\n- [issue #101] Fix a crash in `bunyan` rendering a record with unexpected \"res.headers\".\n\n\n## 0.22.0\n\n- [issue #104] `log.reopenFileStreams()` convenience method to be used with external log\n  rotation.\n\n\n## 0.21.4\n\n- [issue #96] Fix `bunyan` to default to paging (with `less`) by default in node 0.10.0.\n  The intention has always been to default to paging for node >=0.8.\n\n\n## 0.21.3\n\n- [issue #90] Fix `bunyan -p '*'` breakage in version 0.21.2.\n\n\n## 0.21.2\n\n**Note: Bad release. The switchrate change below broke `bunyan -p '*'` usage\n(see issue #90). Use 0.21.3 or later.**\n\n- [issue #88] Should be able to efficiently combine \"-l\" with \"-p *\".\n\n- Avoid DTrace buffer filling up, e.g. like this:\n\n        $ bunyan -p 42241 > /tmp/all.log\n        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\n        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\n        dtrace: 138 drops on CPU 4\n        ...\n\n  From Bryan: \"the DTrace buffer is filling up because the string size is so\n  large... by increasing the switchrate, you're increasing the rate at\n  which that buffer is emptied.\"\n\n\n## 0.21.1\n\n- [pull #83] Support rendering 'client_res' key in bunyan CLI (by\n  github.com/mcavage).\n\n\n## 0.21.0\n\n- 'make check' clean, 4-space indenting. No functional change here, just\n  lots of code change.\n- [issue #80, #82] Drop assert that broke using 'rotating-file' with\n  a default `period` (by github.com/ricardograca).\n\n\n## 0.20.0\n\n- [Slight backward incompatibility] Fix serializer bug introduced in 0.18.3\n  (see below) to only apply serializers to log records when appropriate.\n\n  This also makes a semantic change to custom serializers. Before this change\n  a serializer function was called for a log record key when that value was\n  truth-y. The semantic change is to call the serializer function as long\n  as the value is not `undefined`. That means that a serializer function\n  should handle falsey values such as `false` and `null`.\n\n- Update to latest 'mv' dep (required for rotating-file support) to support\n  node v0.10.0.\n\n\n## 0.19.0\n\n**WARNING**: This release includes a bug introduced in bunyan 0.18.3 (see\nbelow). Please upgrade to bunyan 0.20.0.\n\n- [Slight backward incompatibility] Change the default error serialization\n  (a.k.a. `bunyan.stdSerializers.err`) to *not* serialize all additional\n  attributes of the given error object. This is an open door to unsafe logging\n  and logging should always be safe. With this change, error serialization\n  will log these attributes: message, name, stack, code, signal. The latter\n  two are added because some core node APIs include those fields (e.g.\n  `child_process.exec`).\n\n  Concrete examples where this has hurt have been the \"domain\" change\n  necessitating 0.18.3 and a case where\n  [node-restify](https://github.com/mcavage/node-restify) uses an error object\n  as the response object. When logging the `err` and `res` in the same log\n  statement (common for restify audit logging), the `res.body` would be JSON\n  stringified as '[Circular]' as it had already been emitted for the `err` key.\n  This results in a WTF with the bunyan CLI because the `err.body` is not\n  rendered.\n\n  If you need the old behaviour back you will need to do this:\n\n        var bunyan = require('bunyan');\n        var errSkips = {\n            // Skip domain keys. `domain` especially can have huge objects that can\n            // OOM your app when trying to JSON.stringify.\n            domain: true,\n            domain_emitter: true,\n            domain_bound: true,\n            domain_thrown: true\n        };\n        bunyan.stdSerializers.err = function err(err) {\n           if (!err || !err.stack)\n               return err;\n           var obj = {\n               message: err.message,\n               name: err.name,\n               stack: getFullErrorStack(err)\n           }\n           Object.keys(err).forEach(function (k) {\n               if (err[k] !== undefined && !errSkips[k]) {\n                   obj[k] = err[k];\n               }\n           });\n           return obj;\n         };\n\n- \"long\" and \"bunyan\" output formats for the CLI. `bunyan -o long` is the default\n  format, the same as before, just called \"long\" now instead of the cheesy \"paul\"\n  name. The \"bunyan\" output format is the same as \"json-0\", just with a more\n  convenient name.\n\n\n## 0.18.3\n\n**WARNING**: This release introduced a bug such that all serializers are\napplied to all log records even if the log record did not contain the key\nfor that serializer. If a logger serializer function does not handle\nbeing given `undefined`, then you'll get warnings like this on stderr:\n\n    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: ...\n        at Object.bunyan.createLogger.serializers.foo (.../myapp.js:20:15)\n        at Logger._applySerializers (.../lib/bunyan.js:644:46)\n        at Array.forEach (native)\n        at Logger._applySerializers (.../lib/bunyan.js:640:33)\n        ...\n\nand the following junk in written log records:\n\n    \"foo\":\"(Error in Bunyan log \"foo\" serializer broke field. See stderr for details.)\"\n\nPlease upgrade to bunyan 0.20.0.\n\n\n- Change the `bunyan.stdSerializers.err` serializer for errors to *exclude*\n  [the \"domain*\" keys](http://nodejs.org/docs/latest/api/all.html#all_additions_to_error_objects).\n  `err.domain` will include its assigned members which can arbitrarily large\n  objects that are not intended for logging.\n\n- Make the \"dtrace-provider\" dependency optional. I hate to do this, but\n  installing bunyan on Windows is made very difficult with this as a required\n  dep.  Even though \"dtrace-provider\" stubs out for non-dtrace-y platforms,\n  without a compiler and Python around, node-gyp just falls over.\n\n\n## 0.18.2\n\n- [pull #67] Remove debugging prints in rotating-file support.\n  (by github.com/chad3814).\n- Update to dtrace-provider@0.2.7.\n\n\n## 0.18.1\n\n- Get the `bunyan` CLI to **not** automatically page (i.e. pipe to `less`)\n  if stdin isn't a TTY, or if following dtrace probe output (via `-p PID`),\n  or if not given log file arguments.\n\n\n## 0.18.0\n\n- Automatic paging support in the `bunyan` CLI (similar to `git log` et al).\n  IOW, `bunyan` will open your pager (by default `less`) and pipe rendered\n  log output through it. A main benefit of this is getting colored logs with\n  a pager without the pain. Before you had to explicit use `--color` to tell\n  bunyan to color output when the output was not a TTY:\n\n        bunyan foo.log --color | less -R        # before\n        bunyan foo.log                          # now\n\n  Disable with the `--no-pager` option or the `BUNYAN_NO_PAGER=1` environment\n  variable.\n\n  Limitations: Only supported for node >=0.8. Windows is not supported (at\n  least not yet).\n\n- Switch test suite to nodeunit (still using a node-tap'ish API via\n  a helper).\n\n\n## 0.17.0\n\n- [issue #33] Log rotation support:\n\n        var bunyan = require('bunyan');\n        var log = bunyan.createLogger({\n            name: 'myapp',\n            streams: [{\n                type: 'rotating-file',\n                path: '/var/log/myapp.log',\n                count: 7,\n                period: 'daily'\n            }]\n        });\n\n\n- Tweak to CLI default pretty output: don't special case \"latency\" field.\n  The special casing was perhaps nice, but less self-explanatory.\n  Before:\n\n        [2012-12-27T21:17:38.218Z]  INFO: audit/45769 on myserver: handled: 200 (15ms, audit=true, bar=baz)\n          GET /foo\n          ...\n\n  After:\n\n        [2012-12-27T21:17:38.218Z]  INFO: audit/45769 on myserver: handled: 200 (audit=true, bar=baz, latency=15)\n          GET /foo\n          ...\n\n- *Exit* CLI on EPIPE, otherwise we sit there useless processing a huge log\n  file with, e.g.  `bunyan huge.log | head`.\n\n\n## 0.16.8\n\n- Guards on `-c CONDITION` usage to attempt to be more user friendly.\n  Bogus JS code will result in this:\n\n        $ bunyan portal.log -c 'this.req.username==boo@foo'\n        bunyan: error: illegal CONDITION code: SyntaxError: Unexpected token ILLEGAL\n          CONDITION script:\n            Object.prototype.TRACE = 10;\n            Object.prototype.DEBUG = 20;\n            Object.prototype.INFO = 30;\n            Object.prototype.WARN = 40;\n            Object.prototype.ERROR = 50;\n            Object.prototype.FATAL = 60;\n            this.req.username==boo@foo\n          Error:\n            SyntaxError: Unexpected token ILLEGAL\n                at new Script (vm.js:32:12)\n                at Function.Script.createScript (vm.js:48:10)\n                at parseArgv (/Users/trentm/tm/node-bunyan-0.x/bin/bunyan:465:27)\n                at main (/Users/trentm/tm/node-bunyan-0.x/bin/bunyan:1252:16)\n                at Object.<anonymous> (/Users/trentm/tm/node-bunyan-0.x/bin/bunyan:1330:3)\n                at Module._compile (module.js:449:26)\n                at Object.Module._extensions..js (module.js:467:10)\n                at Module.load (module.js:356:32)\n                at Function.Module._load (module.js:312:12)\n                at Module.runMain (module.js:492:10)\n\n  And all CONDITION scripts will be run against a minimal valid Bunyan\n  log record to ensure they properly guard against undefined values\n  (at least as much as can reasonably be checked). For example:\n\n        $ bunyan portal.log -c 'this.req.username==\"bob\"'\n        bunyan: error: CONDITION code cannot safely filter a minimal Bunyan log record\n          CONDITION script:\n            Object.prototype.TRACE = 10;\n            Object.prototype.DEBUG = 20;\n            Object.prototype.INFO = 30;\n            Object.prototype.WARN = 40;\n            Object.prototype.ERROR = 50;\n            Object.prototype.FATAL = 60;\n            this.req.username==\"bob\"\n          Minimal Bunyan log record:\n            {\n              \"v\": 0,\n              \"level\": 30,\n              \"name\": \"name\",\n              \"hostname\": \"hostname\",\n              \"pid\": 123,\n              \"time\": 1355514346206,\n              \"msg\": \"msg\"\n            }\n          Filter error:\n            TypeError: Cannot read property 'username' of undefined\n                at bunyan-condition-0:7:9\n                at Script.Object.keys.forEach.(anonymous function) [as runInNewContext] (vm.js:41:22)\n                at parseArgv (/Users/trentm/tm/node-bunyan-0.x/bin/bunyan:477:18)\n                at main (/Users/trentm/tm/node-bunyan-0.x/bin/bunyan:1252:16)\n                at Object.<anonymous> (/Users/trentm/tm/node-bunyan-0.x/bin/bunyan:1330:3)\n                at Module._compile (module.js:449:26)\n                at Object.Module._extensions..js (module.js:467:10)\n                at Module.load (module.js:356:32)\n                at Function.Module._load (module.js:312:12)\n                at Module.runMain (module.js:492:10)\n\n  A proper way to do that condition would be:\n\n        $ bunyan portal.log -c 'this.req && this.req.username==\"bob\"'\n\n\n\n## 0.16.7\n\n- [issue #59] Clear a possibly interrupted ANSI color code on signal\n  termination.\n\n\n## 0.16.6\n\n- [issue #56] Support `bunyan -p NAME` to dtrace all PIDs matching 'NAME' in\n  their command and args (using `ps -A -o pid,command | grep NAME` or, on SunOS\n  `pgrep -lf NAME`). E.g.:\n\n        bunyan -p myappname\n\n  This is useful for usage of node's [cluster\n  module](http://nodejs.org/docs/latest/api/all.html#all_cluster) where you'll\n  have multiple worker processes.\n\n\n## 0.16.5\n\n- Allow `bunyan -p '*'` to capture bunyan dtrace probes from **all** processes.\n- issue #55: Add support for `BUNYAN_NO_COLOR` environment variable to\n  turn off all output coloring. This is still overridden by the `--color`\n  and `--no-color` options.\n\n\n## 0.16.4\n\n- issue #54: Ensure (again, see 0.16.2) that stderr from the dtrace child\n  process (when using `bunyan -p PID`) gets through. There had been a race\n  between exiting bunyan and the flushing of the dtrace process' stderr.\n\n\n## 0.16.3\n\n- Drop 'trentm-dtrace-provider' fork dep now that\n  <https://github.com/chrisa/node-dtrace-provider/pull/24> has been resolved.\n  Back to dtrace-provider.\n\n\n## 0.16.2\n\n- Ensure that stderr from the dtrace child process (when using `bunyan -p PID`)\n  gets through. The `pipe` usage wasn't working on SmartOS. This is important\n  to show the user if they need to 'sudo'.\n\n\n## 0.16.1\n\n- Ensure that a possible dtrace child process (with using `bunyan -p PID`) is\n  terminated on signal termination of the bunyan CLI (at least for SIGINT,\n  SIGQUIT, SIGTERM, SIGHUP).\n\n\n## 0.16.0\n\n- Add `bunyan -p PID` support. This is a convenience wrapper that effectively\n  calls:\n\n        dtrace -x strsize=4k -qn 'bunyan$PID:::log-*{printf(\"%s\", copyinstr(arg0))}' | bunyan\n\n\n## 0.15.0\n\n- issue #48: Dtrace support! The elevator pitch is you can watch all logging\n  from all Bunyan-using process with something like this:\n\n        dtrace -x strsize=4k -qn 'bunyan*:::log-*{printf(\"%d: %s: %s\", pid, probefunc, copyinstr(arg0))}'\n\n  And this can include log levels *below* what the service is actually configured\n  to log. E.g. if the service is only logging at INFO level and you need to see\n  DEBUG log messages, with this you can. Obviously this only works on dtrace-y\n  platforms: Illumos derivatives of SunOS (e.g. SmartOS, OmniOS), Mac, FreeBSD.\n\n  Or get the bunyan CLI to render logs nicely:\n\n        dtrace -x strsize=4k -qn 'bunyan*:::log-*{printf(\"%s\", copyinstr(arg0))}' | bunyan\n\n  See <https://github.com/trentm/node-bunyan#dtrace-support> for details. By\n  Bryan Cantrill.\n\n\n## 0.14.6\n\n- Export `bunyan.safeCycles()`. This may be useful for custom `type == \"raw\"`\n  streams that may do JSON stringification of log records themselves. Usage:\n\n        var str = JSON.stringify(rec, bunyan.safeCycles());\n\n- [issue #49] Allow a `log.child()` to specify the level of inherited streams.\n  For example:\n\n        # Before\n        var childLog = log.child({...});\n        childLog.level('debug');\n\n        # After\n        var childLog = log.child({..., level: 'debug'});\n\n- Improve the Bunyan CLI crash message to make it easier to provide relevant\n  details in a bug report.\n\n\n## 0.14.5\n\n- Fix a bug in the long-stack-trace error serialization added in 0.14.4. The\n  symptom:\n\n        bunyan@0.14.4: .../node_modules/bunyan/lib/bunyan.js:1002\n          var ret = ex.stack || ex.toString();\n                      ^\n        TypeError: Cannot read property 'stack' of undefined\n            at getFullErrorStack (.../node_modules/bunyan/lib/bunyan.js:1002:15)\n            ...\n\n\n## 0.14.4\n\n- **Bad release. Use 0.14.5 instead.**\n- Improve error serialization to walk the chain of `.cause()` errors\n  from the likes of `WError` or `VError` error classes from\n  [verror](https://github.com/davepacheco/node-verror) and\n  [restify v2.0](https://github.com/mcavage/node-restify). Example:\n\n        [2012-10-11T00:30:21.871Z] ERROR: imgapi/99612 on 0525989e-2086-4270-b960-41dd661ebd7d: my-message\n            ValidationFailedError: my-message; caused by TypeError: cause-error-message\n                at Server.apiPing (/opt/smartdc/imgapi/lib/app.js:45:23)\n                at next (/opt/smartdc/imgapi/node_modules/restify/lib/server.js:550:50)\n                at Server.setupReq (/opt/smartdc/imgapi/lib/app.js:178:9)\n                at next (/opt/smartdc/imgapi/node_modules/restify/lib/server.js:550:50)\n                at Server.parseBody (/opt/smartdc/imgapi/node_modules/restify/lib/plugins/body_parser.js:15:33)\n                at next (/opt/smartdc/imgapi/node_modules/restify/lib/server.js:550:50)\n                at Server.parseQueryString (/opt/smartdc/imgapi/node_modules/restify/lib/plugins/query.js:40:25)\n                at next (/opt/smartdc/imgapi/node_modules/restify/lib/server.js:550:50)\n                at Server._run (/opt/smartdc/imgapi/node_modules/restify/lib/server.js:579:17)\n                at Server._handle.log.trace.req (/opt/smartdc/imgapi/node_modules/restify/lib/server.js:480:38)\n            Caused by: TypeError: cause-error-message\n                at Server.apiPing (/opt/smartdc/imgapi/lib/app.js:40:25)\n                at next (/opt/smartdc/imgapi/node_modules/restify/lib/server.js:550:50)\n                at Server.setupReq (/opt/smartdc/imgapi/lib/app.js:178:9)\n                at next (/opt/smartdc/imgapi/node_modules/restify/lib/server.js:550:50)\n                at Server.parseBody (/opt/smartdc/imgapi/node_modules/restify/lib/plugins/body_parser.js:15:33)\n                at next (/opt/smartdc/imgapi/node_modules/restify/lib/server.js:550:50)\n                at Server.parseQueryString (/opt/smartdc/imgapi/node_modules/restify/lib/plugins/query.js:40:25)\n                at next (/opt/smartdc/imgapi/node_modules/restify/lib/server.js:550:50)\n                at Server._run (/opt/smartdc/imgapi/node_modules/restify/lib/server.js:579:17)\n                at Server._handle.log.trace.req (/opt/smartdc/imgapi/node_modules/restify/lib/server.js:480:38)\n\n\n## 0.14.2\n\n- [issue #45] Fix bunyan CLI (default output mode) to not crash on a 'res'\n  field that isn't a response object, but a string.\n\n\n## 0.14.1\n\n- [issue #44] Fix the default `bunyan` CLI output of a `res.body` that is an\n  object instead of a string. See issue#38 for the same with `req.body`.\n\n\n## 0.14.0\n\n- [pull #41] Safe `JSON.stringify`ing of emitted log records to avoid blowing\n  up on circular objects (by Isaac Schlueter).\n\n\n## 0.13.5\n\n- [issue #39] Fix a bug with `client_req` handling in the default output\n  of the `bunyan` CLI.\n\n\n## 0.13.4\n\n- [issue #38] Fix the default `bunyan` CLI output of a `req.body` that is an\n  object instead of a string.\n\n\n## 0.13.3\n\n- Export `bunyan.resolveLevel(NAME-OR-NUM)` to resolve a level name or number\n  to its log level number value:\n\n        > bunyan.resolveLevel('INFO')\n        30\n        > bunyan.resolveLevel('debug')\n        20\n\n  A side-effect of this change is that the uppercase level name is now allowed\n  in the logger constructor.\n\n\n## 0.13.2\n\n- [issue #35] Ensure that an accidental `log.info(BUFFER)`, where BUFFER is\n  a node.js Buffer object, doesn't blow up.\n\n\n## 0.13.1\n\n- [issue #34] Ensure `req.body`, `res.body` and other request/response fields\n  are emitted by the `bunyan` CLI (mostly by Rob Gulewich).\n\n\n\n## 0.13.0\n\n- [issue #31] Re-instate defines for the (uppercase) log level names (TRACE,\n  DEBUG, etc.) in `bunyan -c \"...\"` filtering condition code. E.g.:\n\n        $ ... | bunyan -c 'level >= ERROR'\n\n\n## 0.12.0\n\n- [pull #32] `bunyan -o short` for more concise output (by Dave Pacheco). E.g.:\n\n        22:56:52.856Z  INFO myservice: My message\n\n  instead of:\n\n        [2012-02-08T22:56:52.856Z]  INFO: myservice/123 on example.com: My message\n\n\n## 0.11.3\n\n- Add '--strict' option to `bunyan` CLI to suppress all but legal Bunyan JSON\n  log lines. By default non-JSON, and non-Bunyan lines are passed through.\n\n\n## 0.11.2\n\n- [issue #30] Robust handling of 'req' field without a 'headers' subfield\n  in `bunyan` CLI.\n- [issue #31] Pull the TRACE, DEBUG, et al defines from `bunyan -c \"...\"`\n  filtering code. This was added in v0.11.1, but has a significant adverse\n  affect.\n\n\n## 0.11.1\n\n- **Bad release. The TRACE et al names are bleeding into the log records\n  when using '-c'.**\n- Add defines for the (uppercase) log level names (TRACE, DEBUG, etc.) in\n  `bunyan -c \"...\"` filtering condition code. E.g.:\n\n        $ ... | bunyan -c 'level >= ERROR'\n\n\n## 0.11.0\n\n- [pull #29] Add -l/--level for level filtering, and -c/--condition for\n  arbitrary conditional filtering (by github.com/isaacs):\n\n        $ ... | bunyan -l error   # filter out log records below error\n        $ ... | bunyan -l 50      # numeric value works too\n        $ ... | bunyan -c 'level===50'              # equiv with -c filtering\n        $ ... | bunyan -c 'pid===123'               # filter on any field\n        $ ... | bunyan -c 'pid===123' -c '_audit'   # multiple filters\n\n\n## 0.10.0\n\n- [pull #24] Support for gzip'ed log files in the bunyan CLI (by\n  github.com/mhart):\n\n        $ bunyan foo.log.gz\n        ...\n\n\n## 0.9.0\n\n- [pull #16] Bullet proof the `bunyan.stdSerializers` (by github.com/rlidwka).\n\n- [pull #15] The `bunyan` CLI will now chronologically merge multiple log\n  streams when it is given multiple file arguments. (by github.com/davepacheco)\n\n        $ bunyan foo.log bar.log\n        ... merged log records ...\n\n- [pull #15] A new `bunyan.RingBuffer` stream class that is useful for\n  keeping the last N log messages in memory. This can be a fast way to keep\n  recent, and thus hopefully relevant, log messages. (by @dapsays,\n  github.com/davepacheco)\n\n  Potential uses: Live debugging if a running process could inspect those\n  messages. One could dump recent log messages at a finer log level than is\n  typically logged on\n  [`uncaughtException`](http://nodejs.org/docs/latest/api/all.html#all_event_uncaughtexception).\n\n        var ringbuffer = new bunyan.RingBuffer({ limit: 100 });\n        var log = new bunyan({\n            name: 'foo',\n            streams: [{\n                type: 'raw',\n                stream: ringbuffer,\n                level: 'debug'\n            }]\n        });\n\n        log.info('hello world');\n        console.log(ringbuffer.records);\n\n- Add support for \"raw\" streams. This is a logging stream that is given\n  raw log record objects instead of a JSON-stringified string.\n\n        function Collector() {\n            this.records = [];\n        }\n        Collector.prototype.write = function (rec) {\n            this.records.push(rec);\n        }\n        var log = new Logger({\n            name: 'mylog',\n            streams: [{\n                type: 'raw',\n                stream: new Collector()\n            }]\n        });\n\n  See \"examples/raw-stream.js\". I expect raw streams to be useful for\n  piping Bunyan logging to separate services (e.g. <http://www.loggly.com/>,\n  <https://github.com/etsy/statsd>) or to separate in-process handling.\n\n- Add test/corpus/*.log files (accidentally excluded) so the test suite\n  actually works(!).\n\n\n## 0.8.0\n\n- [pull #21] Bunyan loggers now re-emit `fs.createWriteStream` error events.\n  By github.com/EvanOxfeld. See \"examples/handle-fs-error.js\" and\n  \"test/error-event.js\" for details.\n\n        var log = new Logger({name: 'mylog', streams: [{path: FILENAME}]});\n        log.on('error', function (err, stream) {\n            // Handle error writing to or creating FILENAME.\n        });\n\n- jsstyle'ing (via `make check`)\n\n\n## 0.7.0\n\n- [issue #12] Add `bunyan.createLogger(OPTIONS)` form, as is more typical in\n  node.js APIs.  This'll eventually become the preferred form.\n\n\n## 0.6.9\n\n- Change `bunyan` CLI default output to color \"src\" info red. Before the \"src\"\n  information was uncolored. The \"src\" info is the filename, line number and\n  function name resulting from using `src: true` in `Logger` creation. I.e.,\n  the `(/Users/trentm/tm/node-bunyan/examples/hi.js:10)` in:\n\n        [2012-04-10T22:28:58.237Z]  INFO: myapp/39339 on banana.local (/Users/trentm/tm/node-bunyan/examples/hi.js:10): hi\n\n- Tweak `bunyan` CLI default output to still show an \"err\" field if it doesn't\n  have a \"stack\" attribute.\n\n\n## 0.6.8\n\n- Fix bad bug in `log.child({...}, true);` where the added child fields **would\n  be added to the parent's fields**. This bug only existed for the \"fast child\"\n  path (that second `true` argument). A side-effect of fixing this is that\n  the \"fast child\" path is only 5 times as fast as the regular `log.child`,\n  instead of 10 times faster.\n\n\n## 0.6.7\n\n- [issue #6] Fix bleeding 'type' var to global namespace. (Thanks Mike!)\n\n\n## 0.6.6\n\n- Add support to the `bunyan` CLI taking log file path args, `bunyan foo.log`,\n  in addition to the usual `cat foo.log | bunyan`.\n- Improve reliability of the default output formatting of the `bunyan` CLI.\n  Before it could blow up processing log records missing some expected\n  fields.\n\n\n## 0.6.5\n\n- ANSI coloring output from `bunyan` CLI tool (for the default output mode/style).\n  Also add the '--color' option to force coloring if the output stream is not\n  a TTY, e.g. `cat my.log | bunyan --color | less -R`. Use `--no-color` to\n  disable coloring, e.g. if your terminal doesn't support ANSI codes.\n- Add 'level' field to log record before custom fields for that record. This\n  just means that the raw record JSON will show the 'level' field earlier,\n  which is a bit nicer for raw reading.\n\n\n## 0.6.4\n\n- [issue #5] Fix `log.info() -> boolean` to work properly. Previous all were\n  returning false. Ditto all trace/debug/.../fatal methods.\n\n\n## 0.6.3\n\n- Allow an optional `msg` and arguments to the `log.info(<Error> err)` logging\n  form. For example, before:\n\n        log.debug(my_error_instance)            // good\n        log.debug(my_error_instance, \"boom!\")   // wasn't allowed\n\n  Now the latter is allowed if you want to expliciting set the log msg. Of course\n  this applies to all the `log.{trace|debug|info...}()` methods.\n\n- `bunyan` cli output: clarify extra fields with quoting if empty or have\n  spaces. E.g. 'cmd' and 'stderr' in the following:\n\n        [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\")\n\n\n## 0.6.2\n\n- Fix/guard against unintended inclusion of some files in npm published package\n  due to <https://github.com/isaacs/npm/issues/2144>\n\n\n## 0.6.1\n\n- Internal: starting jsstyle usage.\n- Internal: add .npmignore. Previous packages had reams of bunyan crud in them.\n\n\n## 0.6.0\n\n- Add 'pid' automatic log record field.\n\n\n## 0.5.3\n\n- Add 'client_req' (HTTP client request) standard formatting in `bunyan` CLI\n  default output.\n- Improve `bunyan` CLI default output to include *all* log record keys. Unknown keys\n  are either included in the first line parenthetical (if short) or in the indented\n  subsequent block (if long or multiline).\n\n\n## 0.5.2\n\n- [issue #3] More type checking of `new Logger(...)` and `log.child(...)`\n  options.\n- Start a test suite.\n\n\n## 0.5.1\n\n- [issue #2] Add guard on `JSON.stringify`ing of log records before emission.\n  This will prevent `log.info` et al throwing on record fields that cannot be\n  represented as JSON. An error will be printed on stderr and a clipped log\n  record emitted with a 'bunyanMsg' key including error details. E.g.:\n\n        bunyan: ERROR: could not stringify log record from /Users/trentm/tm/node-bunyan/examples/unstringifyable.js:12: TypeError: Converting circular structure to JSON\n        {\n          \"name\": \"foo\",\n          \"hostname\": \"banana.local\",\n          \"bunyanMsg\": \"bunyan: ERROR: could not stringify log record from /Users/trentm/tm/node-bunyan/examples/unstringifyable.js:12: TypeError: Converting circular structure to JSON\",\n        ...\n\n  Some timing shows this does effect log speed:\n\n        $ node tools/timeguard.js     # before\n        Time try/catch-guard on JSON.stringify:\n         - log.info:  0.07365ms per iteration\n        $ node tools/timeguard.js     # after\n        Time try/catch-guard on JSON.stringify:\n         - log.info:  0.07368ms per iteration\n\n\n## 0.5.0\n\n- Use 10/20/... instead of 1/2/... for level constant values. Ostensibly this\n  allows for intermediary levels from the defined \"trace/debug/...\" set.\n  However, that is discouraged. I'd need a strong user argument to add\n  support for easily using alternative levels. Consider using a separate\n  JSON field instead.\n- s/service/name/ for Logger name field. \"service\" is unnecessarily tied\n  to usage for a service. No need to differ from log4j Logger \"name\".\n- Add `log.level(...)` and `log.levels(...)` API for changing logger stream\n  levels.\n- Add `TRACE|DEBUG|INFO|WARN|ERROR|FATAL` level constants to exports.\n- Add `log.info(err)` special case for logging an `Error` instance. For\n  example `log.info(new TypeError(\"boom\")` will produce:\n\n        ...\n        \"err\": {\n          \"message\": \"boom\",\n          \"name\": \"TypeError\",\n          \"stack\": \"TypeError: boom\\n    at Object.<anonymous> ...\"\n        },\n        \"msg\": \"boom\",\n        ...\n\n\n## 0.4.0\n\n- Add `new Logger({src: true})` config option to have a 'src' attribute be\n  automatically added to log records with the log call source info. Example:\n\n        \"src\": {\n          \"file\": \"/Users/trentm/tm/node-bunyan/examples/src.js\",\n          \"line\": 20,\n          \"func\": \"Wuzzle.woos\"\n        },\n\n\n## 0.3.0\n\n- `log.child(options[, simple])` Added `simple` boolean arg. Set `true` to\n  assert that options only add fields (no config changes). Results in a 10x\n  speed increase in child creation. See \"tools/timechild.js\". On my Mac,\n  \"fast child\" creation takes about 0.001ms. IOW, if your app is dishing\n  10,000 req/s, then creating a log child for each request will take\n  about 1% of the request time.\n- `log.clone` -> `log.child` to better reflect the relationship: streams and\n  serializers are inherited. Streams can't be removed as part of the child\n  creation. The child doesn't own the parent's streams (so can't close them).\n- Clean up Logger creation. The goal here was to ensure `log.child` usage\n  is fast. TODO: measure that.\n- Add `Logger.stdSerializers.err` serializer which is necessary to get good\n  Error object logging with node 0.6 (where core Error object properties\n  are non-enumerable).\n\n\n## 0.2.0\n\n- Spec'ing core/recommended log record fields.\n- Add `LOG_VERSION` to exports.\n- Improvements to request/response serializations.\n\n\n## 0.1.0\n\nFirst release.\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing to node-bunyan\n\nThanks for using node-bunyan and for considering contributing to it! Or perhaps\nyou are just here to get a sniff for what is going on with node-bunyan\ndevelopment.\n\n\n## How you can help\n\nIf you want to help me here, great! Thank you! Some ideas:\n\n- Do you have experience with and/or recommendations for a good automated\n  testing service? Ideally I'd like support for Mac, Linux, SmartOS, and maybe\n  Windows. Also, support for node.js versions 0.10 up to whatever the current\n  latest is. Are those too tall an order? What's more, Bunyan is meant to work\n  (at least partially) in the browser. Is there a good service for that?\n  Please discuss on [issue #342](https://github.com/trentm/node-bunyan/issues/342).\n\n- Fielding issues labelled with \"[Type-Question][Type-Question]\", if you are familiar\n  with Bunyan and know how to answer them, would be great.\n\n- If you want to dive into code, but aren't *that* familiar with node-bunyan,\n  then [issues labelled with Experience-Easy][Experience-Easy] are a good\n  place to start.\n\n- [Once I've made a once over\n  triaging](https://github.com/trentm/node-bunyan/issues/335) and consolidating\n  issues and PRs, volunteering for issues in a particular\n  [component](#component) with which you have familiarity would be great.\n\n[Type-Question]: https://github.com/trentm/node-bunyan/issues?q=is%3Aopen+is%3Aissue+label%3AType-Question\n\n\n## Trent's Biased Rules for Code\n\nIn the hope that it makes it easier to get PRs into Bunyan, here is my biased\nlist of what I typically want. Submitting a PR without all of these is\n*totally fine*! The only side-effect is that it may take longer for me to\nprovide feedback on it and merge it. I'll politely request missing pieces.\n\n\n- Please follow existing code style. Contributed code must pass `make check`.\n  (Note: I intended to [change to eslint\n  soon](https://github.com/trentm/node-bunyan/issues/341), so currently `make\n  check` might be a moving target.)\n\n- Any user visible change in behaviour should almost certainly include an\n  update to the docs. Currently the \"docs\" is the README.md.\n\n- Adding a test case for code changes is **stronly recommended**, else I\n  can't easily promise to not break your fix/feature later. If you don't\n  grok the test suite, please ask. We can use it to form the basis for a\n  \"test/README.md\".\n\n- Typically a code change should have an associated issue or PR. This allows\n  addition of follow-up issues, discussion, test data, etc. to be associated\n  with the commit. Just using GitHub pull requests makes this easy.\n\n- All but the most trivial code changes should have an addition to the\n  [changelog](./CHANGES.md). The audience for the changelog is *Bunyan users*.\n  However, because rebasing longer-lived PRs against master is a pain\n  with a change to CHANGES.md, please **do not include a CHANGES.md change\n  in your PR. Instead suggest a CHANGES.md addition in a comment on the\n  PR.**\n\n- Good commit messages, please:\n    - The first line should be a succinct summary of the issue or fix. A\n      good candidate is to just cut 'n paste the issue title, if there is one.\n    - If the commit is for a particular issue/PR (see previous rule), please\n      list the issue number in the commit message. E.g. \"Fixes #123\" or \"Related\n      to #234\".\n    - The audience for commit messages is *Bunyan developers*.\n\n\n## Pull Request Lifecycle\n\n(Language adapted from\n[terraform](https://github.com/hashicorp/terraform/blob/master/CONTRIBUTING.md).)\n\n- You are welcome to submit your pull request for commentary or review before it\n  is fully completed. Please prefix the title of your pull request with \"[WIP]\"\n  to indicate this. It's also a good idea to include specific questions or items\n  you'd like feedback on.\n\n- Once you believe your pull request is ready to be merged, you can remove any\n  \"[WIP]\" prefix from the title and a core team member will review. See\n  Trent's Biased Rules above to help ensure that your contribution will be\n  merged quickly.\n\n- Trent or, if things go well, a node-bunyan maintainer will look over your\n  contribution and either provide comments letting you know if there is anything\n  left to do. Please be patient. Unfortunately, I'm not able to carve out\n  a *lot* of time for Bunyan development and maintenance.\n\n- Once all outstanding comments and checklist items have been addressed, your\n  contribution will be merged. Merged PRs will be included in the next\n  node-bunyan release.\n\n- In some cases, we might decide that a PR should be closed. We'll make sure to\n  provide clear reasoning when this happens.\n\n\n## Issue labels\n\nThe point of issue labeling for node-bunyan is to help answer \"what should be\nworked on now? what can be left for later?\" I don't want issue labelling to\nbecome a burden for anyone, so (a) don't feel obliged to add them yourself and\n(b) I'm happy to reevaluate their usage.\n\nBunyan shall have categories of [issue\nlabels](https://github.com/trentm/node-bunyan/labels) named \"$category-$value\".\nAn issue should have max *one* label from each set. Users of Google Code's\ndearly departed issue tracker may remember this kind of thing. This is a\npoorman's version of structured issue tracker metadata.\n\nI'm inclined to *not* do priorities right now. *Possibly* we'll use GitHub\nmilestones to basically set targets for upcoming releases. But otherwise my\nsense is that for smaller OSS projects, assigning prios will get in the way.\nIf people would think it helpful, I'd consider \"Difficulty-\" or \"Experience-\"\ncategories (a la Rust's \"E-\" labels) to mark easier and intermediate tasks\nthat someone interested but maybe not very familiar with Bunyan might want\nto tackle.\n\nFor now, here are the various labels and their purpose:\n\n### Meta\n\n- needstriage: Temporary label to help me do a single triage pass through all\n  current open issues and PRs.\n  See [#335](https://github.com/trentm/node-bunyan/issues/335)\n  where I'm working through this.\n\n### Type\n\nColor: green\n\n- Type-Unknown: If it is still unclear or undecided if an issue is an intended\n  feature (perhaps arguing for better docs or examples to avoid confusion) or a\n  bug, I will use this category.\n- Type-Question: Asking a question on Bunyan usage, about the project, etc.\n- Type-Bug: A bug in Bunyan's behaviour.\n- Type-Improvement: A new feature or other improvement.\n- Type-Doc: Issues with Bunyan's documentation.\n- Type-Task: A project task to be done.\n\nTODO: consider Type-Unknown for the \"unclear if bug or feature\" tickets.\n\n### Component\n\nColor: blue\n\n- Component-Project: Project meta stuff like testing, linting, build, install,\n  etc.\n- Component-CLI: The `bunyan` command-line tool.\n- Component-Lib: catch-all for other library stuff\n    - Component-LibRotation: The bunyan library's log rotation support.\n    - Component-LibBrowser: Bunyan's handling/support for running in the browser.\n    - Component-LibFlush: A separate component for collecting the tickets related\n      to closing/flushing bunyan streams on process shutdown.\n\nThe point of components is to find like issues to help with reference, search\nand resolving them. If no component fits an issue/PR, then don't add a label.\n\n### Resolution\n\nColor: red\n\n- Resolution-WontFix\n- Resolution-Duplicate\n- Resolution-Fixed: Also used to indicate \"doc written\", \"question answered\",\n  \"feature implemented\".\n- Resolution-CannotRepro: After some reasonable attempt by maintainers to\n  reproduce a bug report, I want it to be non-controversial to close it\n  and mark it with this. If given more info by someone able to repro, we\n  can happy re-open issues.\n\n### Experience\n\nColor: yellow\n\n- Experience-Easy: Relatively little experience with node-bunyan should be\n  required to complete this issue.\n- Experience-NeedsTest: Typically added to an issue or PR that needs a test\n  case. Someone familiar enough with node-bunyan's test suite could tackle this.\n- Experience-Hard: At a guess, this is a thorny issue that requires known\n  node-bunyan well, knowing node.js well, requires design review or all of\n  these.\n\nOne of the \"Experience-\\*\" labels can optionally be put on an issue or PR to\nindicate what kind of experience a contributor would need with node-bunyan\n(and/or node.js) to complete it. For example, if you're looking for somewhere to\nstart, check out the [Experience-Easy][Experience-Easy] tag. This category idea\nis borrowed from [rust's E-\\* labels][rust-issue-triage].\n\n[Experience-Easy]: https://github.com/trentm/node-bunyan/issues?q=is%3Aopen+is%3Aissue+label%3AExperience-Easy\n[rust-issue-triage]: https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.md#issue-triage\n\n\n## Acknowledgements\n\nAnything good about this document is thanks to inspiration from\n[rust](https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.md) and, more\nrecently\n[terraform](https://github.com/hashicorp/terraform/blob/master/CONTRIBUTING.md).\nAnything bad about it, is my fault.\n"
  },
  {
    "path": "LICENSE.txt",
    "content": "# This is the MIT license\n\nCopyright 2016 Trent Mick\nCopyright 2016 Joyent Inc.\n\nPermission is hereby granted, free of charge, to any person obtaining a\ncopy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be included\nin all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\nOR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n"
  },
  {
    "path": "Makefile",
    "content": "\nSHELL := bash\n\n#---- Tools\n\nTAP_EXEC := ./node_modules/.bin/tap\nSUDO := sudo\nifeq ($(shell uname -s),SunOS)\n\t# On SunOS (e.g. SmartOS) we expect to run the test suite as the\n\t# root user -- necessary to run dtrace. Therefore `pfexec` isn't\n\t# necessary.\n\tSUDO :=\nendif\nDTRACE_UP_IN_HERE=\nifeq ($(shell uname -s),SunOS)\n    DTRACE_UP_IN_HERE=1\nendif\nifeq ($(shell uname -s),Darwin)\n    DTRACE_UP_IN_HERE=1\nendif\n\n\n#---- Files\n\nJSSTYLE_FILES := $(shell find lib test tools examples -name \"*.js\") bin/bunyan\n\n\n#---- Targets\n\nall $(TAP_EXEC):\n\tnpm install $(NPM_INSTALL_FLAGS)\n\n# Ensure all version-carrying files have the same version.\n.PHONY: versioncheck\nversioncheck:\n\t@echo version is: $(shell node -e 'console.log(require(\"./package.json\").version)')\n\t[[ `node -e 'console.log(require(\"./package.json\").version)'` == `grep '^## ' CHANGES.md | head -2 | tail -1 | awk '{print $$2}'` ]]\n\t@echo Version check ok.\n\n.PHONY: cutarelease\ncutarelease: check\n\t[[ -z `git status --short` ]]  # If this fails, the working dir is dirty.\n\t@which json 2>/dev/null 1>/dev/null && \\\n\t    ver=$(shell json -f package.json version) && \\\n\t    name=$(shell json -f package.json name) && \\\n\t    publishedVer=$(shell npm view -j $(shell json -f package.json name)@$(shell json -f package.json version) version 2>/dev/null) && \\\n\t    if [[ -n \"$$publishedVer\" ]]; then \\\n\t\techo \"error: $$name@$$ver is already published to npm\"; \\\n\t\texit 1; \\\n\t    fi && \\\n\t    echo \"** Are you sure you want to tag and publish $$name@$$ver to npm?\" && \\\n\t    echo \"** Enter to continue, Ctrl+C to abort.\" && \\\n\t    read\n\tver=$(shell cat package.json | json version) && \\\n\t    date=$(shell date -u \"+%Y-%m-%d\") && \\\n\t    git tag -a \"$$ver\" -m \"version $$ver ($$date) beta\" && \\\n\t    git push --tags origin && \\\n\t    npm publish --tag beta\n\n.PHONY: docs\ndocs: toc\n\t@[[ `which ronn` ]] || (echo \"No 'ronn' on your PATH. Install with 'gem install ronn'\" && exit 2)\n\tmkdir -p man/man1\n\tronn --style=toc --manual=\"bunyan manual\" --date=$(shell git log -1 --pretty=format:%cd --date=short) --roff --html docs/bunyan.1.ronn\n\tpython -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)'\n\tpython -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)'\n\t@echo \"# test with 'man ./docs/bunyan.1' and 'open ./docs/bunyan.1.html'\"\n\n# Re-generate the README.md table of contents.\ntoc:\n\t./node_modules/.bin/markdown-toc -i README.md\n\n\n.PHONY: publish\npublish:\n\tmkdir -p tmp\n\t[[ -d tmp/bunyan-gh-pages ]] || git clone git@github.com:trentm/node-bunyan.git tmp/bunyan-gh-pages\n\tcd tmp/bunyan-gh-pages && git checkout gh-pages && git pull --rebase origin gh-pages\n\tcp docs/index.html tmp/bunyan-gh-pages/index.html\n\tcp docs/bunyan.1.html tmp/bunyan-gh-pages/bunyan.1.html\n\t(cd tmp/bunyan-gh-pages \\\n\t\t&& git commit -a -m \"publish latest docs\" \\\n\t\t&& git push origin gh-pages || true)\n\n.PHONY: distclean\ndistclean:\n\trm -rf node_modules\n\n\n#---- test\n\n.PHONY: test\ntest: $(TAP_EXEC)\n\ttest -z \"$(DTRACE_UP_IN_HERE)\" || test -n \"$(SKIP_DTRACE)\" || \\\n\t\t(node -e 'require(\"dtrace-provider\").createDTraceProvider(\"isthisthingon\")' && \\\n\t\techo \"\\nNote: Use 'SKIP_DTRACE=1 make test' to skip parts of the test suite that require root.\" && \\\n\t\t$(SUDO) $(TAP_EXEC) test/dtrace/*.test.js)\n\t$(TAP_EXEC) test/*.test.js\n\n\n#---- check\n\n.PHONY: check-jsstyle\ncheck-jsstyle: $(JSSTYLE_FILES)\n\t./tools/jsstyle -o indent=4,doxygen,unparenthesized-return=0,blank-after-start-comment=0,leading-right-paren-ok=1 $(JSSTYLE_FILES)\n\n.PHONY: check\ncheck: check-jsstyle versioncheck\n\t@echo \"Check ok.\"\n\n.PHONY: prepush\nprepush: check testall\n\t@echo \"Okay to push.\"\n"
  },
  {
    "path": "README.md",
    "content": "[![npm version](https://img.shields.io/npm/v/bunyan.svg?style=flat)](https://www.npmjs.com/package/bunyan)\n[![Build Status](https://travis-ci.org/trentm/node-bunyan.svg?branch=master)](https://travis-ci.org/trentm/node-bunyan)\n\nBunyan is **a simple and fast JSON logging library** for node.js services:\n\n```js\nvar bunyan = require('bunyan');\nvar log = bunyan.createLogger({name: \"myapp\"});\nlog.info(\"hi\");\n```\n\nand **a `bunyan` CLI tool** for nicely viewing those logs:\n\n![bunyan CLI screenshot](https://raw.github.com/trentm/node-bunyan/master/tools/screenshot1.png)\n\nManifesto: Server logs should be structured. JSON's a good format. Let's do\nthat. A log record is one line of `JSON.stringify`'d output. Let's also\nspecify some common names for the requisite and common fields for a log\nrecord (see below).\n\n\n## Table of Contents\n\n<!-- toc -->\n\n- [Current Status](#current-status)\n- [Installation](#installation)\n- [Features](#features)\n- [Introduction](#introduction)\n  * [Constructor API](#constructor-api)\n  * [Log Method API](#log-method-api)\n  * [CLI Usage](#cli-usage)\n  * [Streams Introduction](#streams-introduction)\n  * [log.child](#logchild)\n  * [Serializers](#serializers)\n    + [Requirements for serializers functions](#requirements-for-serializers-functions)\n    + [Standard Serializers](#standard-serializers)\n  * [src](#src)\n- [Levels](#levels)\n  * [Level suggestions](#level-suggestions)\n- [Log Record Fields](#log-record-fields)\n  * [Core fields](#core-fields)\n  * [Recommended/Best Practice Fields](#recommendedbest-practice-fields)\n  * [Other fields to consider](#other-fields-to-consider)\n- [Streams](#streams)\n  * [Adding a Stream](#adding-a-stream)\n  * [stream errors](#stream-errors)\n  * [stream type: `stream`](#stream-type-stream)\n  * [stream type: `file`](#stream-type-file)\n  * [stream type: `rotating-file`](#stream-type-rotating-file)\n  * [stream type: `raw`](#stream-type-raw)\n  * [`raw` + RingBuffer Stream](#raw--ringbuffer-stream)\n  * [third-party streams](#third-party-streams)\n- [Runtime log snooping via DTrace](#runtime-log-snooping-via-dtrace)\n  * [DTrace examples](#dtrace-examples)\n- [Runtime environments](#runtime-environments)\n  * [Browserify](#browserify)\n  * [Webpack](#webpack)\n- [Versioning](#versioning)\n- [License](#license)\n- [See Also](#see-also)\n\n<!-- tocstop -->\n\n# Current Status\n\nStable. I do my best to follow semver: i.e. you should only need to worry\nabout code breaking for a *major* version bump.  Bunyan currently supports node\n0.10 and greater. Follow <a href=\"https://twitter.com/intent/user?screen_name=trentmick\"\ntarget=\"_blank\">@trentmick</a> for updates to Bunyan.\n\nThere is an email discussion list\n[bunyan-logging@googlegroups.com](mailto:bunyan-logging@googlegroups.com),\nalso [as a forum in the\nbrowser](https://groups.google.com/forum/?fromgroups#!forum/bunyan-logging).\n\nActive branches:\n- \"1.x\" is for 1.x maintenance work, if any. 1.x releases are still \"latest\" in\n  npm.\n- \"master\" is currently for coming Bunyan 2.x work. For now, 2.x releases are\n  published to npm with the \"beta\" tag, meaning that `npm install bunyan` is\n  still 1.x for now. To install 2.x use `npm install bunyan@2` or\n  `npm install bunyan@beta`.\n\n\n# Installation\n\n```sh\nnpm install bunyan\n```\n\n**Tip**: The `bunyan` CLI tool is written to be compatible (within reason) with\nall versions of Bunyan logs. Therefore you might want to `npm install -g bunyan`\nto get the bunyan CLI on your PATH, then use local bunyan installs for\nnode.js library usage of bunyan in your apps.\n\n**Tip**: Installing without optional dependencies can dramatically reduce\nbunyan's install size. **dtrace-provider** is used for dtrace features,\n**mv** is used for RotatingFileStream, and **moment** is used for local time.\nIf you don't need these features, consider installing with the\n`--no-optional` flag.\n\n\n# Features\n\n- elegant [log method API](#log-method-api)\n- extensible [streams](#streams) system for controlling where log records\n  go (to a stream, to a file, [log file rotation](#stream-type-rotating-file),\n  etc.)\n- [`bunyan` CLI](#cli-usage) for pretty-printing and filtering of Bunyan logs\n- simple include of log call source location (file, line, function) with\n  [`src: true`](#src)\n- lightweight specialization of Logger instances with [`log.child`](#logchild)\n- custom rendering of logged objects with [\"serializers\"](#serializers)\n- [Runtime log snooping via DTrace support](#runtime-log-snooping-via-dtrace)\n- Support for a few [runtime environments](#runtime-environments): Node.js,\n  [Browserify](http://browserify.org/), [Webpack](https://webpack.github.io/), [NW.js](http://nwjs.io/).\n\n\n# Introduction\n\nLike most logging libraries you create a Logger instance and call methods\nnamed after the logging levels:\n\n```js\n// hi.js\nvar bunyan = require('bunyan');\nvar log = bunyan.createLogger({name: 'myapp'});\nlog.info('hi');\nlog.warn({lang: 'fr'}, 'au revoir');\n```\n\nAll loggers must provide a \"name\". This is somewhat akin to the log4j logger\n\"name\", but Bunyan doesn't do hierarchical logger names.\n\n**Bunyan log records are JSON.** A few fields are added automatically:\n\"pid\", \"hostname\", \"time\" and \"v\".\n\n```sh\n$ node hi.js\n{\"name\":\"myapp\",\"hostname\":\"banana.local\",\"pid\":40161,\"level\":30,\"msg\":\"hi\",\"time\":\"2013-01-04T18:46:23.851Z\",\"v\":0}\n{\"name\":\"myapp\",\"hostname\":\"banana.local\",\"pid\":40161,\"level\":40,\"lang\":\"fr\",\"msg\":\"au revoir\",\"time\":\"2013-01-04T18:46:23.853Z\",\"v\":0}\n```\n\n\n## Constructor API\n\n```js\nvar bunyan = require('bunyan');\nvar log = bunyan.createLogger({\n    name: <string>,                     // Required\n    level: <level name or number>,      // Optional, see \"Levels\" section\n    stream: <node.js stream>,           // Optional, see \"Streams\" section\n    streams: [<bunyan streams>, ...],   // Optional, see \"Streams\" section\n    serializers: <serializers mapping>, // Optional, see \"Serializers\" section\n    src: <boolean>,                     // Optional, see \"src\" section\n\n    // Any other fields are added to all log records as is.\n    foo: 'bar',\n    ...\n});\n```\n\n\n## Log Method API\n\nThe example above shows two different ways to call `log.info(...)`. The\nfull API is:\n\n```js\nlog.info();     // Returns a boolean: is the \"info\" level enabled?\n                // This is equivalent to `log.isInfoEnabled()` or\n                // `log.isEnabledFor(INFO)` in log4j.\n\nlog.info('hi');                     // Log a simple string message (or number).\nlog.info('hi %s', bob, anotherVar); // Uses `util.format` for msg formatting.\n\nlog.info({foo: 'bar'}, 'hi');\n                // The first field can optionally be a \"fields\" object, which\n                // is merged into the log record.\n\nlog.info(err);  // Special case to log an `Error` instance to the record.\n                // This adds an \"err\" field with exception details\n                // (including the stack) and sets \"msg\" to the exception\n                // message.\nlog.info(err, 'more on this: %s', more);\n                // ... or you can specify the \"msg\".\n\nlog.info({foo: 'bar', err: err}, 'some msg about this error');\n                // To pass in an Error *and* other fields, use the `err`\n                // field name for the Error instance **and ensure your logger\n                // has a `err` serializer.** One way to ensure the latter is:\n                //      var log = bunyan.createLogger({\n                //          ...,\n                //          serializers: bunyan.stdSerializers\n                //      });\n                // See the \"Serializers\" section below for details.\n```\n\nNote that this implies **you cannot blindly pass any object as the first\nargument to log it** because that object might include fields that collide with\nBunyan's [core record fields](#core-fields). In other words,\n`log.info(mywidget)` may not yield what you expect. Instead of a string\nrepresentation of `mywidget` that other logging libraries may give you, Bunyan\nwill try to JSON-ify your object. It is a Bunyan best practice to always give a\nfield name to included objects, e.g.:\n\n```js\nlog.info({widget: mywidget}, ...)\n```\n\nThis will dove-tail with [Bunyan serializer support](#serializers), discussed\nlater.\n\nThe same goes for all of Bunyan's log levels: `log.trace`, `log.debug`,\n`log.info`, `log.warn`, `log.error`, and `log.fatal`. See the [levels\nsection](#levels) below for details and suggestions.\n\n\n## CLI Usage\n\nBunyan log output is a stream of JSON objects. This is great for processing,\nbut not for reading directly. A **`bunyan` tool** is provided **for\npretty-printing bunyan logs** and for **filtering** (e.g.\n`| bunyan -c 'this.foo == \"bar\"'`). Using our example above:\n\n```sh\n$ node hi.js | ./node_modules/.bin/bunyan\n[2013-01-04T19:01:18.241Z]  INFO: myapp/40208 on banana.local: hi\n[2013-01-04T19:01:18.242Z]  WARN: myapp/40208 on banana.local: au revoir (lang=fr)\n```\n\nSee the screenshot above for an example of the default coloring of rendered\nlog output. That example also shows the nice formatting automatically done for\nsome well-known log record fields (e.g. `req` is formatted like an HTTP request,\n`res` like an HTTP response, `err` like an error stack trace).\n\nOne interesting feature is **filtering** of log content, which can be useful\nfor digging through large log files or for analysis. We can filter only\nrecords above a certain level:\n\n```sh\n$ node hi.js | bunyan -l warn\n[2013-01-04T19:08:37.182Z]  WARN: myapp/40353 on banana.local: au revoir (lang=fr)\n```\n\nOr filter on the JSON fields in the records (e.g. only showing the French\nrecords in our contrived example):\n\n```sh\n$ node hi.js | bunyan -c 'this.lang == \"fr\"'\n[2013-01-04T19:08:26.411Z]  WARN: myapp/40342 on banana.local: au revoir (lang=fr)\n```\n\nSee `bunyan --help` for other facilities.\n\n\n## Streams Introduction\n\nBy default, log output is to stdout and at the \"info\" level. Explicitly that\nlooks like:\n\n```js\nvar log = bunyan.createLogger({\n    name: 'myapp',\n    stream: process.stdout,\n    level: 'info'\n});\n```\n\nThat is an abbreviated form for a single stream. **You can define multiple\nstreams at different levels**.\n\n```js\nvar log = bunyan.createLogger({\n  name: 'myapp',\n  streams: [\n    {\n      level: 'info',\n      stream: process.stdout            // log INFO and above to stdout\n    },\n    {\n      level: 'error',\n      path: '/var/tmp/myapp-error.log'  // log ERROR and above to a file\n    }\n  ]\n});\n```\n\nMore on streams in the [Streams section](#streams) below.\n\n## log.child\n\nBunyan has a concept of a child logger to **specialize a logger for a\nsub-component of your application**, i.e. to create a new logger with\nadditional bound fields that will be included in its log records. A child\nlogger is created with `log.child(...)`.\n\nIn the following example, logging on a \"Wuzzle\" instance's `this.log` will\nbe exactly as on the parent logger with the addition of the `widget_type`\nfield:\n\n```js\nvar bunyan = require('bunyan');\nvar log = bunyan.createLogger({name: 'myapp'});\n\nfunction Wuzzle(options) {\n    this.log = options.log.child({widget_type: 'wuzzle'});\n    this.log.info('creating a wuzzle')\n}\nWuzzle.prototype.woos = function () {\n    this.log.warn('This wuzzle is woosey.')\n}\n\nlog.info('start');\nvar wuzzle = new Wuzzle({log: log});\nwuzzle.woos();\nlog.info('done');\n```\n\nRunning that looks like (raw):\n\n```sh\n$ node myapp.js\n{\"name\":\"myapp\",\"hostname\":\"myhost\",\"pid\":34572,\"level\":30,\"msg\":\"start\",\"time\":\"2013-01-04T07:47:25.814Z\",\"v\":0}\n{\"name\":\"myapp\",\"hostname\":\"myhost\",\"pid\":34572,\"widget_type\":\"wuzzle\",\"level\":30,\"msg\":\"creating a wuzzle\",\"time\":\"2013-01-04T07:47:25.815Z\",\"v\":0}\n{\"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}\n{\"name\":\"myapp\",\"hostname\":\"myhost\",\"pid\":34572,\"level\":30,\"msg\":\"done\",\"time\":\"2013-01-04T07:47:25.816Z\",\"v\":0}\n```\n\nAnd with the `bunyan` CLI (using the \"short\" output mode):\n\n```sh\n$ node myapp.js  | bunyan -o short\n07:46:42.707Z  INFO myapp: start\n07:46:42.709Z  INFO myapp: creating a wuzzle (widget_type=wuzzle)\n07:46:42.709Z  WARN myapp: This wuzzle is woosey. (widget_type=wuzzle)\n07:46:42.709Z  INFO myapp: done\n```\n\nA more practical example is in the\n[node-restify](https://github.com/mcavage/node-restify) web framework.\nRestify uses Bunyan for its logging. One feature of its integration, is that\nif `server.use(restify.requestLogger())` is used, each restify request handler\nincludes a `req.log` logger that is:\n\n```js\nlog.child({req_id: <unique request id>}, true)\n```\n\nApps using restify can then use `req.log` and have all such log records\ninclude the unique request id (as \"req\\_id\"). Handy.\n\n\n## Serializers\n\nBunyan has a concept of **\"serializer\" functions to produce a JSON-able object\nfrom a JavaScript object**, so you can easily do the following:\n\n```js\nlog.info({req: <request object>}, 'something about handling this request');\n```\n\nand have the `req` entry in the log record be just a reasonable subset of\n`<request object>` fields (or computed data about those fields).\n\n\nA logger instance can have a `serializers` mapping of log record field name\n(\"req\" in this example) to a serializer function. When creating the log record,\nBunyan will call the serializer function for *top-level* fields of that name. An\nexample:\n\n```js\nfunction reqSerializer(req) {\n    return {\n        method: req.method,\n        url: req.url,\n        headers: req.headers\n    };\n}\nvar log = bunyan.createLogger({\n    name: 'myapp',\n    serializers: {\n        req: reqSerializer\n    }\n});\n```\n\n\nTypically serializers are added to a logger at creation time via:\n\n```js\nvar log = bunyan.createLogger({\n    name: 'myapp',\n    serializers: {\n        foo: function fooSerializer(foo) { ... },\n        ...\n    }\n});\n\n// or\nvar log = bunyan.createLogger({\n    name: 'myapp',\n    serializers: bunyan.stdSerializers\n});\n```\n\nSerializers can also be added after creation via `<logger>.addSerializers(...)`,\ne.g.:\n\n```js\nvar log = bunyan.createLogger({name: 'myapp'});\nlog.addSerializers({req: reqSerializer});\n```\n\n### Requirements for serializers functions\n\nA serializer function is passed unprotected objects that are passed to the\n`log.info`, `log.debug`, etc. call. This means a poorly written serializer\nfunction can cause side-effects. Logging shouldn't do that. Here are a few\nrules and best practices for serializer functions:\n\n- A serializer function *should never throw*. The bunyan library *does*\n  protect somewhat from this: if the serializer throws an error, then\n  bunyan will (a) write an ugly message on stderr (along with the traceback),\n  and (b) the field in the log record will be replaced with a short error message.\n  For example:\n\n    ```\n    bunyan: ERROR: Exception thrown from the \"foo\" Bunyan serializer. This should never happen. This is a bug in that serializer function.\n    TypeError: Cannot read property 'not' of undefined\n        at Object.fooSerializer [as foo] (/Users/trentm/tm/node-bunyan/bar.js:8:26)\n        at /Users/trentm/tm/node-bunyan/lib/bunyan.js:873:50\n        at Array.forEach (native)\n        at Logger._applySerializers (/Users/trentm/tm/node-bunyan/lib/bunyan.js:865:35)\n        at mkRecord (/Users/trentm/tm/node-bunyan/lib/bunyan.js:978:17)\n        at Logger.info (/Users/trentm/tm/node-bunyan/lib/bunyan.js:1044:19)\n        at Object.<anonymous> (/Users/trentm/tm/node-bunyan/bar.js:13:5)\n        at Module._compile (module.js:409:26)\n        at Object.Module._extensions..js (module.js:416:10)\n        at Module.load (module.js:343:32)\n    {\"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}\n    ```\n\n- A serializer function *should never mutate the given object*. Doing so will\n  change the object in your application.\n\n- A serializer function *should be defensive*. In my experience, it is common to\n  set a serializer in an app, say for field name \"foo\", and then accidentally\n  have a log line that passes a \"foo\" that is undefined, or null, or of some\n  unexpected type. A good start at defensiveness is to start with this:\n\n    ```javascript\n    function fooSerializer(foo) {\n        // Guard against foo be null/undefined. Check that expected fields\n        // are defined.\n        if (!foo || !foo.bar)\n            return foo;\n        var obj = {\n            // Create the object to be logged.\n            bar: foo.bar\n        }\n        return obj;\n    };\n    ```\n\n\n### Standard Serializers\n\nBunyan includes a small set of \"standard serializers\", exported as\n`bunyan.stdSerializers`. Their use is completely optional. An example using\nall of them:\n\n```js\nvar log = bunyan.createLogger({\n    name: 'myapp',\n    serializers: bunyan.stdSerializers\n});\n```\n\nor particular ones:\n\n```js\nvar log = bunyan.createLogger({\n    name: 'myapp',\n    serializers: {err: bunyan.stdSerializers.err}\n});\n```\n\nStandard serializers are:\n\n| Field | Description |\n| ----- | ----------- |\n| 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). |\n| req   | Common fields from a node.js HTTP request object. |\n| res   | Common fields from a node.js HTTP response object. |\n\nNote that the `req` and `res` serializers intentionally do not include the\nrequest/response *body*, as that can be prohibitively large. If helpful, the\n[restify framework's audit logger plugin](https://github.com/restify/node-restify/blob/ac13902ad9716dcb20aaa62295403983075b1841/lib/plugins/audit.js#L38-L87)\nhas its own req/res serializers that include more information (optionally\nincluding the body).\n\n\n## src\n\nThe **source file, line and function of the log call site** can be added to\nlog records by using the `src: true` config option:\n\n```js\nvar log = bunyan.createLogger({src: true, ...});\n```\n\nThis adds the call source info with the 'src' field, like this:\n\n```js\n{\n  \"name\": \"src-example\",\n  \"hostname\": \"banana.local\",\n  \"pid\": 123,\n  \"component\": \"wuzzle\",\n  \"level\": 4,\n  \"msg\": \"This wuzzle is woosey.\",\n  \"time\": \"2012-02-06T04:19:35.605Z\",\n  \"src\": {\n    \"file\": \"/Users/trentm/tm/node-bunyan/examples/src.js\",\n    \"line\": 20,\n    \"func\": \"Wuzzle.woos\"\n  },\n  \"v\": 0\n}\n```\n\n**WARNING: Determining the call source info is slow. Never use this option\nin production.**\n\n\n# Levels\n\nThe log levels in bunyan are as follows. The level descriptions are best\npractice *opinions* of the author.\n\n- \"fatal\" (60): The service/app is going to stop or become unusable now.\n  An operator should definitely look into this soon.\n- \"error\" (50): Fatal for a particular request, but the service/app continues\n  servicing other requests. An operator should look at this soon(ish).\n- \"warn\" (40): A note on something that should probably be looked at by an\n  operator eventually.\n- \"info\" (30): Detail on regular operation.\n- \"debug\" (20): Anything else, i.e. too verbose to be included in \"info\" level.\n- \"trace\" (10): Logging from external libraries used by your app or *very*\n  detailed application logging.\n\nSetting a logger instance (or one of its streams) to a particular level implies\nthat all log records *at that level and above* are logged. E.g. a logger set to\nlevel \"info\" will log records at level info and above (warn, error, fatal).\n\nWhile using log level *names* is preferred, the actual level values are integers\ninternally (10 for \"trace\", ..., 60 for \"fatal\"). Constants are defined for\nthe levels: `bunyan.TRACE` ... `bunyan.FATAL`. The lowercase level names are\naliases supported in the API, e.g. `log.level(\"info\")`. There is one exception:\nDTrace integration uses the level names. The fired DTrace probes are named\n'bunyan-$levelName'.\n\nHere is the API for querying and changing levels on an existing logger.\nRecall that a logger instance has an array of output \"streams\":\n\n```js\nlog.level() -> INFO   // gets current level (lowest level of all streams)\n\nlog.level(INFO)       // set all streams to level INFO\nlog.level(\"info\")     // set all streams to level INFO\n\nlog.levels() -> [DEBUG, INFO]   // get array of levels of all streams\nlog.levels(0) -> DEBUG          // get level of stream at index 0\nlog.levels(\"foo\")               // get level of stream with name \"foo\"\n\nlog.levels(0, INFO)             // set level of stream 0 to INFO\nlog.levels(0, \"info\")           // can use \"info\" et al aliases\nlog.levels(\"foo\", WARN)         // set stream named \"foo\" to WARN\n```\n\n\n## Level suggestions\n\nTrent's biased suggestions for server apps: Use \"debug\" sparingly. Information\nthat will be useful to debug errors *post mortem* should usually be included in\n\"info\" messages if it's generally relevant or else with the corresponding\n\"error\" event. Don't rely on spewing mostly irrelevant debug messages all the\ntime and sifting through them when an error occurs.\n\nTrent's biased suggestions for node.js libraries: IMHO, libraries should only\never log at `trace`-level. Fine control over log output should be up to the\napp using a library. Having a library that spews log output at higher levels\ngets in the way of a clear story in the *app* logs.\n\n\n# Log Record Fields\n\nThis section will describe *rules* for the Bunyan log format: field names,\nfield meanings, required fields, etc. However, a Bunyan library doesn't\nstrictly enforce all these rules while records are being emitted. For example,\nBunyan will add a `time` field with the correct format to your log records,\nbut you can specify your own. It is the caller's responsibility to specify\nthe appropriate format.\n\nThe reason for the above leniency is because IMO logging a message should\nnever break your app. This leads to this rule of logging: **a thrown\nexception from `log.info(...)` or equivalent (other than for calling with the\nincorrect signature) is always a bug in Bunyan.**\n\n\nA typical Bunyan log record looks like this:\n\n```js\n{\"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}\n```\n\nPretty-printed:\n\n```js\n{\n  \"name\": \"myserver\",\n  \"hostname\": \"banana.local\",\n  \"pid\": 123,\n  \"req\": {\n    \"method\": \"GET\",\n    \"url\": \"/path?q=1#anchor\",\n    \"headers\": {\n      \"x-hi\": \"Mom\",\n      \"connection\": \"close\"\n    },\n    \"remoteAddress\": \"120.0.0.1\",\n    \"remotePort\": 51244\n  },\n  \"level\": 3,\n  \"msg\": \"start request\",\n  \"time\": \"2012-02-03T19:02:57.534Z\",\n  \"v\": 0\n}\n```\n\n## Core fields\n\n- `v`: Required. Integer. Added by Bunyan. Cannot be overridden.\n  This is the Bunyan log format version (`require('bunyan').LOG_VERSION`).\n  The log version is a single integer. `0` is until I release a version\n  \"1.0.0\" of node-bunyan. Thereafter, starting with `1`, this will be\n  incremented if there is any backward incompatible change to the log record\n  format. Details will be in \"CHANGES.md\" (the change log).\n- `level`: Required. Integer. Added by Bunyan. Cannot be overridden.\n  See the \"Levels\" section.\n- `name`: Required. String. Provided at Logger creation.\n  You must specify a name for your logger when creating it. Typically this\n  is the name of the service/app using Bunyan for logging.\n- `hostname`: Required. String. Provided or determined at Logger creation.\n  You can specify your hostname at Logger creation or it will be retrieved\n  via `os.hostname()`.\n- `pid`: Required. Integer. Filled in automatically at Logger creation.\n- `time`: Required. String. Added by Bunyan. Can be overridden.\n  The date and time of the event in [ISO 8601\n  Extended Format](http://en.wikipedia.org/wiki/ISO_8601) format and in UTC,\n  as from\n  [`Date.toISOString()`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Date/toISOString).\n- `msg`: Required. String.\n  Every `log.debug(...)` et al call must provide a log message.\n- `src`: Optional. Object giving log call source info. This is added\n  automatically by Bunyan if the \"src: true\" config option is given to the\n  Logger. Never use in production as this is really slow.\n\n\nGo ahead and add more fields, and nested ones are fine (and recommended) as\nwell. This is why we're using JSON. Some suggestions and best practices\nfollow (feedback from actual users welcome).\n\n\n## Recommended/Best Practice Fields\n\n- `err`: Object. A caught JS exception. Log that thing with `log.info(err)`\n    to get:\n\n    ```js\n    ...\n    \"err\": {\n      \"message\": \"boom\",\n      \"name\": \"TypeError\",\n      \"stack\": \"TypeError: boom\\n    at Object.<anonymous> ...\"\n    },\n    \"msg\": \"boom\",\n    ...\n    ```\n\n    Or use the `bunyan.stdSerializers.err` serializer in your Logger and\n    do this `log.error({err: err}, \"oops\")`. See \"examples/err.js\".\n\n- `req_id`: String. A request identifier. Including this field in all logging\n  tied to handling a particular request to your server is strongly suggested.\n  This allows post analysis of logs to easily collate all related logging\n  for a request. This really shines when you have a SOA with multiple services\n  and you carry a single request ID from the top API down through all APIs\n  (as [node-restify](https://github.com/mcavage/node-restify) facilitates\n  with its 'Request-Id' header).\n\n- `req`: An HTTP server request. Bunyan provides `bunyan.stdSerializers.req`\n  to serialize a request with a suggested set of keys. Example:\n\n    ```js\n    {\n      \"method\": \"GET\",\n      \"url\": \"/path?q=1#anchor\",\n      \"headers\": {\n        \"x-hi\": \"Mom\",\n        \"connection\": \"close\"\n      },\n      \"remoteAddress\": \"120.0.0.1\",\n      \"remotePort\": 51244\n    }\n    ```\n\n- `res`: An HTTP server response. Bunyan provides `bunyan.stdSerializers.res`\n  to serialize a response with a suggested set of keys. Example:\n\n    ```js\n    {\n      \"statusCode\": 200,\n      \"header\": \"HTTP/1.1 200 OK\\r\\nContent-Type: text/plain\\r\\nConnection: keep-alive\\r\\nTransfer-Encoding: chunked\\r\\n\\r\\n\"\n    }\n    ```\n\n## Other fields to consider\n\n- `req.username`: Authenticated user (or for a 401, the user attempting to\n  auth).\n- Some mechanism to calculate response latency. \"restify\" users will have\n  an \"X-Response-Time\" header. A `latency` custom field would be fine.\n- `req.body`: If you know that request bodies are small (common in APIs,\n  for example), then logging the request body is good.\n\n\n# Streams\n\nA \"stream\" is Bunyan's name for where it outputs log messages (the equivalent\nto a log4j Appender). Ultimately Bunyan uses a\n[Writable Stream](https://nodejs.org/docs/latest/api/all.html#writable_Stream)\ninterface, but there are some additional attributes used to create and\nmanage the stream. A Bunyan Logger instance has one or more streams.\nIn general streams are specified with the \"streams\" option:\n\n```js\nvar bunyan = require('bunyan');\nvar log = bunyan.createLogger({\n    name: \"foo\",\n    streams: [\n        {\n            stream: process.stderr,\n            level: \"debug\"\n        },\n        ...\n    ]\n});\n```\n\nFor convenience, if there is only one stream, it can be specified with the\n\"stream\" and \"level\" options (internally converted to a `Logger.streams`).\n\n```js\nvar log = bunyan.createLogger({\n    name: \"foo\",\n    stream: process.stderr,\n    level: \"debug\"\n});\n```\n\nNote that \"file\" streams do not support this shortcut (partly for historical\nreasons and partly to not make it difficult to add a literal \"path\" field\non log records).\n\nIf neither \"streams\" nor \"stream\" are specified, the default is a stream of\ntype \"stream\" emitting to `process.stdout` at the \"info\" level.\n\n## Adding a Stream\n\nAfter a bunyan instance has been initialized, you may add additional streams by\ncalling the `addStream` function.\n\n```js\nvar bunyan = require('bunyan');\nvar log = bunyan.createLogger('myLogger');\nlog.addStream({\n  name: \"myNewStream\",\n  stream: process.stderr,\n  level: \"debug\"\n});\n```\n\n## stream errors\n\nA Bunyan logger instance can be made to re-emit \"error\" events from its\nstreams. Bunyan does so by default for [`type === \"file\"`\nstreams](#stream-type-file), so you can do this:\n\n```js\nvar log = bunyan.createLogger({name: 'mylog', streams: [{path: LOG_PATH}]});\nlog.on('error', function (err, stream) {\n    // Handle stream write or create error here.\n});\n```\n\nAs of bunyan@1.7.0, the `reemitErrorEvents` field can be used when adding a\nstream to control whether \"error\" events are re-emitted on the Logger. For\nexample:\n\n    var EventEmitter = require('events').EventEmitter;\n    var util = require('util');\n\n    function MyFlakyStream() {}\n    util.inherits(MyFlakyStream, EventEmitter);\n\n    MyFlakyStream.prototype.write = function (rec) {\n        this.emit('error', new Error('boom'));\n    }\n\n    var log = bunyan.createLogger({\n        name: 'this-is-flaky',\n        streams: [\n            {\n                type: 'raw',\n                stream: new MyFlakyStream(),\n                reemitErrorEvents: true\n            }\n        ]\n    });\n    log.info('hi there');\n\nThe behaviour is as follows:\n\n- `reemitErrorEvents` not specified: `file` streams will re-emit error events\n  on the Logger instance.\n- `reemitErrorEvents: true`: error events will be re-emitted on the Logger\n  for any stream with a `.on()` function -- which includes file streams,\n  process.stdout/stderr, and any object that inherits from EventEmitter.\n- `reemitErrorEvents: false`: error events will not be re-emitted for any\n  streams.\n\nNote: \"error\" events are **not** related to log records at the \"error\" level\nas produced by `log.error(...)`. See [the node.js docs on error\nevents](https://nodejs.org/api/events.html#events_error_events) for details.\n\n\n## stream type: `stream`\n\nA `type === 'stream'` is a plain ol' node.js [Writable\nStream](http://nodejs.org/docs/latest/api/all.html#writable_Stream). A\n\"stream\" (the writable stream) field is required. E.g.: `process.stdout`,\n`process.stderr`.\n\n```js\nvar log = bunyan.createLogger({\n    name: 'foo',\n    streams: [{\n        stream: process.stderr\n        // `type: 'stream'` is implied\n    }]\n});\n```\n\n<table>\n<tr>\n<th>Field</th>\n<th>Required?</th>\n<th>Default</th>\n<th>Description</th>\n</tr>\n<tr>\n<td>stream</td>\n<td>Yes</td>\n<td>-</td>\n<td>A \"Writable Stream\", e.g. a std handle or an open file write stream.</td>\n</tr>\n<tr>\n<td>type</td>\n<td>No</td>\n<td>n/a</td>\n<td>`type == 'stream'` is implied if the `stream` field is given.</td>\n</tr>\n<tr>\n<td>level</td>\n<td>No</td>\n<td>info</td>\n<td>The level to which logging to this stream is enabled. If not\nspecified it defaults to \"info\". If specified this can be one of the\nlevel strings (\"trace\", \"debug\", ...) or constants (`bunyan.TRACE`,\n`bunyan.DEBUG`, ...). This serves as a severity threshold for that stream\nso logs of greater severity will also pass through (i.e. If level=\"warn\",\nerror and fatal will also pass through this stream).</td>\n</tr>\n<tr>\n<td>name</td>\n<td>No</td>\n<td>-</td>\n<td>A name for this stream. This may be useful for usage of `log.level(NAME,\nLEVEL)`. See the [Levels section](#levels) for details. A stream \"name\" isn't\nused for anything else.</td>\n</tr>\n</table>\n\n\n## stream type: `file`\n\nA `type === 'file'` stream requires a \"path\" field. Bunyan will open this\nfile for appending. E.g.:\n\n```js\nvar log = bunyan.createLogger({\n    name: 'foo',\n    streams: [{\n        path: '/var/log/foo.log',\n        // `type: 'file'` is implied\n    }]\n});\n```\n\n<table>\n<tr>\n<th>Field</th>\n<th>Required?</th>\n<th>Default</th>\n<th>Description</th>\n</tr>\n<tr>\n<td>path</td>\n<td>Yes</td>\n<td>-</td>\n<td>A file path to which to log.</td>\n</tr>\n<tr>\n<td>type</td>\n<td>No</td>\n<td>n/a</td>\n<td>`type == 'file'` is implied if the `path` field is given.</td>\n</tr>\n<tr>\n<td>level</td>\n<td>No</td>\n<td>info</td>\n<td>The level to which logging to this stream is enabled. If not\nspecified it defaults to \"info\". If specified this can be one of the\nlevel strings (\"trace\", \"debug\", ...) or constants (`bunyan.TRACE`,\n`bunyan.DEBUG`, ...). This serves as a severity threshold for that\nstream so logs of greater severity will also pass through (i.e. If\nlevel=\"warn\", error and fatal will also pass through this stream).</td>\n</tr>\n<tr>\n<td>name</td>\n<td>No</td>\n<td>-</td>\n<td>A name for this stream. This may be useful for usage of `log.level(NAME,\nLEVEL)`. See the [Levels section](#levels) for details. A stream \"name\" isn't\nused for anything else.</td>\n</tr>\n</table>\n\n\n## stream type: `rotating-file`\n\n**WARNING on node 0.8 usage:** Users of Bunyan's `rotating-file` should (a) be\nusing at least bunyan 0.23.1 (with the fix for [this\nissue](https://github.com/trentm/node-bunyan/pull/97)), and (b) should use at\nleast node 0.10 (node 0.8 does not support the `unref()` method on\n`setTimeout(...)` needed for the mentioned fix). The symptom is that process\ntermination will hang for up to a full rotation period.\n\n**WARNING on [cluster](http://nodejs.org/docs/latest/api/all.html#all_cluster)\nusage:** Using Bunyan's `rotating-file` stream with node.js's \"cluster\" module\ncan result in unexpected file rotation. You must not have multiple processes\nin the cluster logging to the same file path. In other words, you must have\na separate log file path for the master and each worker in the cluster.\nAlternatively, consider using a system file rotation facility such as\n`logrotate` on Linux or `logadm` on SmartOS/Illumos. See\n[this comment on issue #117](https://github.com/trentm/node-bunyan/issues/117#issuecomment-44804938)\nfor details.\n\nA `type === 'rotating-file'` is a file stream that handles file automatic\nrotation.\n\n```js\nvar log = bunyan.createLogger({\n    name: 'foo',\n    streams: [{\n        type: 'rotating-file',\n        path: '/var/log/foo.log',\n        period: '1d',   // daily rotation\n        count: 3        // keep 3 back copies\n    }]\n});\n```\n\nThis will rotate '/var/log/foo.log' every day (at midnight) to:\n\n```sh\n/var/log/foo.log.0     # yesterday\n/var/log/foo.log.1     # 1 day ago\n/var/log/foo.log.2     # 2 days ago\n```\n\n*Currently*, there is no support for providing a template for the rotated\nfiles, or for rotating when the log reaches a threshold size.\n\n<table>\n<tr>\n<th>Field</th>\n<th>Required?</th>\n<th>Default</th>\n<th>Description</th>\n</tr>\n<tr>\n<td>type</td>\n<td>Yes</td>\n<td>-</td>\n<td>\"rotating-file\"</td>\n</tr>\n<tr>\n<td>path</td>\n<td>Yes</td>\n<td>-</td>\n<td>A file path to which to log. Rotated files will be \"$path.0\",\n\"$path.1\", ...</td>\n</tr>\n<tr>\n<td>period</td>\n<td>No</td>\n<td>1d</td>\n<td>The period at which to rotate. This is a string of the format\n\"$number$scope\" where \"$scope\" is one of \"ms\" (milliseconds -- only useful for\ntesting), \"h\" (hours), \"d\" (days), \"w\" (weeks), \"m\" (months), \"y\" (years). Or\none of the following names can be used \"hourly\" (means 1h), \"daily\" (1d),\n\"weekly\" (1w), \"monthly\" (1m), \"yearly\" (1y). Rotation is done at the start of\nthe scope: top of the hour (h), midnight (d), start of Sunday (w), start of the\n1st of the month (m), start of Jan 1st (y).</td>\n</tr>\n<tr>\n<td>count</td>\n<td>No</td>\n<td>10</td>\n<td>The number of rotated files to keep.</td>\n</tr>\n<tr>\n<td>level</td>\n<td>No</td>\n<td>info</td>\n<td>The level at which logging to this stream is enabled. If not\nspecified it defaults to \"info\". If specified this can be one of the\nlevel strings (\"trace\", \"debug\", ...) or constants (`bunyan.TRACE`,\n`bunyan.DEBUG`, ...).</td>\n</tr>\n<tr>\n<td>name</td>\n<td>No</td>\n<td>-</td>\n<td>A name for this stream. This may be useful for usage of `log.level(NAME,\nLEVEL)`. See the [Levels section](#levels) for details. A stream \"name\" isn't\nused for anything else.</td>\n</tr>\n</table>\n\n\n**Note on log rotation**: Often you may be using external log rotation utilities\nlike `logrotate` on Linux or `logadm` on SmartOS/Illumos. In those cases, unless\nyou are ensuring \"copy and truncate\" semantics (via `copytruncate` with\nlogrotate or `-c` with logadm) then the fd for your 'file' stream will change.\nYou can tell bunyan to reopen the file stream with code like this in your\napp:\n\n```js\nvar log = bunyan.createLogger(...);\n...\nprocess.on('SIGUSR2', function () {\n    log.reopenFileStreams();\n});\n```\n\nwhere you'd configure your log rotation to send SIGUSR2 (or some other signal)\nto your process. Any other mechanism to signal your app to run\n`log.reopenFileStreams()` would work as well.\n\n\n## stream type: `raw`\n\n- `raw`: Similar to a \"stream\" writable stream, except that the write method\n  is given raw log record *Object*s instead of a JSON-stringified string.\n  This can be useful for hooking on further processing to all Bunyan logging:\n  pushing to an external service, a RingBuffer (see below), etc.\n\n\n\n## `raw` + RingBuffer Stream\n\nBunyan comes with a special stream called a RingBuffer which keeps the last N\nrecords in memory and does *not* write the data anywhere else.  One common\nstrategy is to log 'info' and higher to a normal log file but log all records\n(including 'trace') to a ringbuffer that you can access via a debugger, or your\nown HTTP interface, or a post-mortem facility like MDB or node-panic.\n\nTo use a RingBuffer:\n\n```js\n/* Create a ring buffer that stores the last 100 records. */\nvar bunyan = require('bunyan');\nvar ringbuffer = new bunyan.RingBuffer({ limit: 100 });\nvar log = bunyan.createLogger({\n    name: 'foo',\n    streams: [\n        {\n            level: 'info',\n            stream: process.stdout\n        },\n        {\n            level: 'trace',\n            type: 'raw',    // use 'raw' to get raw log record objects\n            stream: ringbuffer\n        }\n    ]\n});\n\nlog.info('hello world');\nconsole.log(ringbuffer.records);\n```\n\nThis example emits:\n\n```js\n[ { name: 'foo',\n    hostname: '912d2b29',\n    pid: 50346,\n    level: 30,\n    msg: 'hello world',\n    time: '2012-06-19T21:34:19.906Z',\n    v: 0 } ]\n```\n\n## third-party streams\n\nSee the [user-maintained list in the Bunyan\nwiki](https://github.com/trentm/node-bunyan/wiki/Awesome-Bunyan).\n\n\n# Runtime log snooping via DTrace\n\nOn systems that support DTrace (e.g., illumos derivatives like SmartOS and\nOmniOS, FreeBSD, Mac), Bunyan will create a DTrace provider (`bunyan`) that\nmakes available the following probes:\n\n```sh\nlog-trace\nlog-debug\nlog-info\nlog-warn\nlog-error\nlog-fatal\n```\n\nEach of these probes has a single argument: the string that would be\nwritten to the log.  Note that when a probe is enabled, it will\nfire whenever the corresponding function is called, even if the level of\nthe log message is less than that of any stream.\n\n\n## DTrace examples\n\nTrace all log messages coming from any Bunyan module on the system.\n(The `-x strsize=4k` is to raise dtrace's default 256 byte buffer size\nbecause log messages are longer than typical dtrace probes.)\n\n```sh\ndtrace -x strsize=4k -qn 'bunyan*:::log-*{printf(\"%d: %s: %s\", pid, probefunc, copyinstr(arg0))}'\n```\n\nTrace all log messages coming from the \"wuzzle\" component:\n\n```sh\ndtrace -x strsize=4k -qn 'bunyan*:::log-*/strstr(this->str = copyinstr(arg0), \"\\\"component\\\":\\\"wuzzle\\\"\") != NULL/{printf(\"%s\", this->str)}'\n```\n\nAggregate debug messages from process 1234, by message:\n\n```sh\ndtrace -x strsize=4k -n 'bunyan1234:::log-debug{@[copyinstr(arg0)] = count()}'\n```\n\nHave the bunyan CLI pretty-print the traced logs:\n\n```sh\ndtrace -x strsize=4k -qn 'bunyan1234:::log-*{printf(\"%s\", copyinstr(arg0))}' | bunyan\n```\n\nA convenience handle has been made for this:\n\n```sh\nbunyan -p 1234\n```\n\nOn systems that support the\n[`jstack`](http://dtrace.org/blogs/dap/2012/04/25/profiling-node-js/) action\nvia a node.js helper, get a stack backtrace for any debug message that\nincludes the string \"danger!\":\n\n```sh\ndtrace -x strsize=4k -qn 'log-debug/strstr(copyinstr(arg0), \"danger!\") != NULL/{printf(\"\\n%s\", copyinstr(arg0)); jstack()}'\n```\n\nOutput of the above might be:\n\n```\n{\"name\":\"foo\",\"hostname\":\"763bf293-d65c-42d5-872b-4abe25d5c4c7.local\",\"pid\":12747,\"level\":20,\"msg\":\"danger!\",\"time\":\"2012-10-30T18:28:57.115Z\",\"v\":0}\n\n          node`0x87e2010\n          DTraceProviderBindings.node`usdt_fire_probe+0x32\n          DTraceProviderBindings.node`_ZN4node11DTraceProbe5_fireEN2v85LocalINS1_5ValueEEE+0x32d\n          DTraceProviderBindings.node`_ZN4node11DTraceProbe4FireERKN2v89ArgumentsE+0x77\n          << internal code >>\n          (anon) as (anon) at /root/node-bunyan/lib/bunyan.js position 40484\n          << adaptor >>\n          (anon) as doit at /root/my-prog.js position 360\n          (anon) as list.ontimeout at timers.js position 4960\n          << adaptor >>\n          << internal >>\n          << entry >>\n          node`_ZN2v88internalL6InvokeEbNS0_6HandleINS0_10JSFunctionEEENS1_INS0_6ObjectEEEiPS5_Pb+0x101\n          node`_ZN2v88internal9Execution4CallENS0_6HandleINS0_6ObjectEEES4_iPS4_Pbb+0xcb\n          node`_ZN2v88Function4CallENS_6HandleINS_6ObjectEEEiPNS1_INS_5ValueEEE+0xf0\n          node`_ZN4node12MakeCallbackEN2v86HandleINS0_6ObjectEEENS1_INS0_8FunctionEEEiPNS1_INS0_5ValueEEE+0x11f\n          node`_ZN4node12MakeCallbackEN2v86HandleINS0_6ObjectEEENS1_INS0_6StringEEEiPNS1_INS0_5ValueEEE+0x66\n          node`_ZN4node9TimerWrap9OnTimeoutEP10uv_timer_si+0x63\n          node`uv__run_timers+0x66\n          node`uv__run+0x1b\n          node`uv_run+0x17\n          node`_ZN4node5StartEiPPc+0x1d0\n          node`main+0x1b\n          node`_start+0x83\n\n          node`0x87e2010\n          DTraceProviderBindings.node`usdt_fire_probe+0x32\n          DTraceProviderBindings.node`_ZN4node11DTraceProbe5_fireEN2v85LocalINS1_5ValueEEE+0x32d\n          DTraceProviderBindings.node`_ZN4node11DTraceProbe4FireERKN2v89ArgumentsE+0x77\n          << internal code >>\n          (anon) as (anon) at /root/node-bunyan/lib/bunyan.js position 40484\n          << adaptor >>\n          (anon) as doit at /root/my-prog.js position 360\n          (anon) as list.ontimeout at timers.js position 4960\n          << adaptor >>\n          << internal >>\n          << entry >>\n          node`_ZN2v88internalL6InvokeEbNS0_6HandleINS0_10JSFunctionEEENS1_INS0_6ObjectEEEiPS5_Pb+0x101\n          node`_ZN2v88internal9Execution4CallENS0_6HandleINS0_6ObjectEEES4_iPS4_Pbb+0xcb\n          node`_ZN2v88Function4CallENS_6HandleINS_6ObjectEEEiPNS1_INS_5ValueEEE+0xf0\n          node`_ZN4node12MakeCallbackEN2v86HandleINS0_6ObjectEEENS1_INS0_8FunctionEEEiPNS1_INS0_5ValueEEE+0x11f\n          node`_ZN4node12MakeCallbackEN2v86HandleINS0_6ObjectEEENS1_INS0_6StringEEEiPNS1_INS0_5ValueEEE+0x66\n          node`_ZN4node9TimerWrap9OnTimeoutEP10uv_timer_si+0x63\n          node`uv__run_timers+0x66\n          node`uv__run+0x1b\n          node`uv_run+0x17\n          node`_ZN4node5StartEiPPc+0x1d0\n          node`main+0x1b\n          node`_start+0x83\n```\n\n\n# Runtime environments\n\nNode-bunyan supports running in a few runtime environments:\n\n- [Node.js](https://nodejs.org/)\n- [Browserify](http://browserify.org/): See the\n  [Browserify section](#browserify) below.\n- [Webpack](https://webpack.github.io/): See the [Webpack section](#webpack) below.\n- [NW.js](http://nwjs.io/)\n\nSupport for other runtime environments is welcome. If you have suggestions,\nfixes, or mentions that node-bunyan already works in some other JavaScript\nruntime, please open an [issue](https://github.com/trentm/node-bunyan/issues/new)\nor a pull request.\n\nThe primary target is Node.js. It is the only environment in which I\nregularly test. If you have suggestions for how to automate testing for other\nenvironments, I'd appreciate feedback on [this automated testing\nissue](https://github.com/trentm/node-bunyan/issues/342).\n\n## Browserify\n\nAs the [Browserify](http://browserify.org/) site says it \"lets you\n`require('modules')` in the browser by bundling up all of your dependencies.\"\nIt is a build tool to run on your node.js script to bundle up your script and\nall its node.js dependencies into a single file that is runnable in the\nbrowser via:\n\n```html\n<script src=\"play.browser.js\"></script>\n```\n\nAs of version 1.1.0, node-bunyan supports being run via Browserify. The\ndefault [stream](#streams) when running in the browser is one that emits\nraw log records to `console.log/info/warn/error`.\n\nHere is a quick example showing you how you can get this working for your\nscript.\n\n1. Get browserify and bunyan installed in your module:\n\n    ```sh\n    $ npm install browserify bunyan\n    ```\n\n2. An example script using Bunyan, \"play.js\":\n\n    ```js\n    var bunyan = require('bunyan');\n    var log = bunyan.createLogger({name: 'play', level: 'debug'});\n    log.trace('this one does not emit');\n    log.debug('hi on debug');   // console.log\n    log.info('hi on info');     // console.info\n    log.warn('hi on warn');     // console.warn\n    log.error('hi on error');   // console.error\n    ```\n\n3. Build this into a bundle to run in the browser, \"play.browser.js\":\n\n    ```sh\n    $ ./node_modules/.bin/browserify play.js -o play.browser.js\n    ```\n\n4. Put that into an HTML file, \"play.html\":\n\n    ```html\n    <!DOCTYPE html>\n    <html>\n    <head>\n      <meta charset=\"utf-8\">\n      <script src=\"play.browser.js\"></script>\n    </head>\n    <body>\n      <div>hi</div>\n    </body>\n    </html>\n    ```\n\n5. Open that in your browser and open your browser console:\n\n    ```sh\n    $ open play.html\n    ```\n\nHere is what it looks like in Firefox's console: ![Bunyan + Browserify in the\nFirefox console](./docs/img/bunyan.browserify.png)\n\nFor some, the raw log records might not be desired. To have a rendered log line\nyou'll want to add your own stream, starting with something like this:\n\n```js\nvar bunyan = require('./lib/bunyan');\n\nfunction MyRawStream() {}\nMyRawStream.prototype.write = function (rec) {\n    console.log('[%s] %s: %s',\n        rec.time.toISOString(),\n        bunyan.nameFromLevel[rec.level],\n        rec.msg);\n}\n\nvar log = bunyan.createLogger({\n    name: 'play',\n    streams: [\n        {\n            level: 'info',\n            stream: new MyRawStream(),\n            type: 'raw'\n        }\n    ]\n});\n\nlog.info('hi on info');\n```\n\n## webpack\nTo include bunyan in your webpack bundle you need to tell webpack to\nignore the optional dependencies that are unavailable in browser environments.\n\nMark the following dependencies as\n[externals](https://webpack.js.org/configuration/externals/) in your webpack\nconfiguration file to exclude them from the bundle:\n\n```\nmodule: {\n    externals: ['dtrace-provider', 'fs', 'mv', 'os', 'source-map-support']\n}\n```\n\n# Versioning\n\nAll versions are `<major>.<minor>.<patch>` which will be incremented for\nbreaking backward compat and major reworks, new features without breaking\nchange, and bug fixes, respectively. tl;dr: [Semantic\nversioning](http://semver.org/).\n\n# License\n\n[MIT](./LICENSE.txt).\n\n# See Also\n\nSee the [user-maintained list of Bunyan-related software in the Bunyan\nwiki](https://github.com/trentm/node-bunyan/wiki/Awesome-Bunyan).\n"
  },
  {
    "path": "TODO.md",
    "content": "# v2\n\n- `createLogger(<config-and-fields>, <fields>)` changes (#460)\n    - see section below\n- the dtrace-provider thing (#487)\n    TODO: answer Cody email\n- use package.json version for VERSION\n- use deps\n    - dashdash\n    - assert-plus?\n    - verror?\n- break out to multiple files\n    - want to work through PRs before that, so don't just break them all\n- TODO: a quick pass through tickets and pulls for other things to include\n- get ticket refs for the above, if any\n- formatters: read up again on `glp master..1.x`\n- support for customer formatters\n    - for the CLI as well? How? ~/.bunyanrc?\n- if doing ~/.bunyanrc, then consider color schemes\n\n\n\n# changes to ctor and log.child to separate fields from config\n\n<https://github.com/trentm/node-bunyan/issues/460>\n\nCurrent:\n\n    createLogger(<config-and-fields>)\n    log.child(<config-and-fields>, <just-fields-bool>)\n\nCould be:\n\n    createLogger(<config-and-fields>, <fields>)\n    log.child(<config-and-fields>, <fields>)\n        # Still support: log.child(<config-and-fields>, <just-fields-bool>)\n\nPros: Compat issues are minimal: a change is only required if there is a\ncollision with used field and a new config var name.\nCons: A *slight* con is that my guess is the common usage of child is\n`log.child(<fields>)`, so the more future-proof common usage becomes:\n\n    log.child(null, <fields>)\n\nThat's not too bad. It is clearer at least than:\n\n    log.child(<fields>, true)\n\nTODO:\n\n- is there a ticket for this work already?  #460\n- make the change\n- do a migration guide? i.e. provide the grep commands to find all\n  possible calls to inspect. E.g. if don't have `rg logUndefined` in your\n  code, then you are fine. And one time future-proofing via changing\n  to fields in the *second* arg.\n- list of issues/pulls that wanted to add new config fields\n\n\n# higher prio\n\n- published organized advice for\n  https://github.com/trentm/node-bunyan/issues/37 so can close that out.\n  Perhaps a wiki page with examples and strategies.\n- man page for the bunyan CLI (refer to it in the readme)\n    - perhaps wait for a bunyan new version with deps, and use dashdash\n      with a (vapour) man page generator\n\n\n# docs\n\n- document log.addStream() and log.addSerializers()\n\n\n# someday/maybe\n\n- 2.0 (?) with `v: 1` in log records. Fwd/bwd compat in `bunyan` CLI\n- `tail -f`-like support\n- full-on docs\n- better examples/\n- better coloring\n- look at pino (bunyan style, perf benefits), also logpp (https://github.com/mrkmarron/logpp)\n- would be exciting to have bunyan support in http://lnav.org/ if that\n  made sense\n- \"template\" support for 'rotating-file' stream to get dated rolled files\n- \"all\" or \"off\" levels? log4j? logging.py?\n  logging.py has NOTSET === 0. I think that is only needed/used for\n  multi-level hierarchical effective level.\n- buffered writes to increase speed:\n    - I'd start with a tools/timeoutput.js for some numbers to compare\n      before/after. Sustained high output to a file.\n    - perhaps this would be a \"buffered: true\" option on the stream object\n    - then wrap the \"stream\" with a local class that handles the buffering\n    - to finish this, need the 'log.close' and `process.on('exit', ...)`\n      work that Trent has started.\n- \"canWrite\" handling for full streams. Need to buffer a la log4js\n- test file log with logadm rotation: does it handle that?\n- test suite:\n    - test for a cloned logger double-`stream.end()` causing problems.\n      Perhaps the \"closeOnExit\" for existing streams should be false for\n      clones.\n    - test that a `log.clone(...)` adding a new field matching a serializer\n      works *and* that an existing field in the parent is not *re-serialized*.\n- split out `bunyan` cli to a \"bunyan\" or \"bunyan-reader\" or \"node-bunyan-reader\"\n  as the basis for tools to consume bunyan logs. It can grow indep of node-bunyan\n  for generating the logs.\n  It would take a Bunyan log record object and be expected to emit it.\n\n        node-bunyan-reader\n            .createReadStream(path, [options]) ?\n\n- coloring bug: in less the indented extra info lines only have the first\n  line colored. Do we need the ANSI char on *each* line? That'll be\n  slower.\n- document \"well-known\" keys from bunyan CLI p.o.v.. Add \"client_req\".\n- More `bunyan` output formats and filtering features.\n- Think about a bunyan dashboard that supports organizing and viewing logs\n  from multiple hosts and services.\n- doc the restify RequestCaptureStream usage of RingBuffer. Great example.\n- A vim plugin (a la http://vim.cybermirror.org/runtime/autoload/zip.vim ?) to\n  allow browsing (read-only) a bunyan log in rendered form.\n- Some speed comparisons with others to get a feel for Bunyan's speed.\n- what about promoting 'latency' field and making that easier?\n- `log.close` to close streams and shutdown and `this.closed`\n  process.on('exit', log.close)\n  -> 'end' for the name\n- bunyan cli: more layouts (http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/EnhancedPatternLayout.html)\n  Custom log formats (in config file? in '-f' arg) using printf or hogan.js\n  or whatever. Dap wants field width control for lining up. Hogan.js is\n  probably overkill for this.\n- loggly example using raw streams, hook.io?, whatever.\n- serializer support:\n    - restify-server.js example -> restifyReq ? or have `req` detect that.\n      That is nicer for the \"use all standard ones\". *Does* restify req\n      have anything special?\n    - differential HTTP *client* req/res with *server* req/res.\n- statsd stream? http://codeascraft.etsy.com/2011/02/15/measure-anything-measure-everything/\n  Think about it.\n- web ui. Ideas: http://googlecloudplatform.blogspot.ca/2014/04/a-new-logs-viewer-for-google-cloud.html\n"
  },
  {
    "path": "bin/bunyan",
    "content": "#!/usr/bin/env node\n/**\n * Copyright 2020 Trent Mick\n * Copyright 2020 Joyent Inc.\n *\n * bunyan -- filter and pretty-print Bunyan log files (line-delimited JSON)\n *\n * See <https://github.com/trentm/node-bunyan>.\n *\n * -*- mode: js -*-\n * vim: expandtab:ts=4:sw=4\n */\n\nvar p = console.log;\nvar util = require('util');\nvar pathlib = require('path');\nvar vm = require('vm');\nvar http = require('http');\nvar fs = require('fs');\nvar warn = console.warn;\nvar child_process = require('child_process'),\n    spawn = child_process.spawn,\n    exec = child_process.exec,\n    execFile = child_process.execFile;\nvar assert = require('assert');\n\nvar exeunt = require('exeunt');\n\ntry {\n    var moment = require('moment');\n} catch (e) {\n    moment = null;\n}\n\n\n//---- globals and constants\n\nvar nodeVer = process.versions.node.split('.').map(Number);\nvar nodeSpawnSupportsStdio = (nodeVer[0] > 0 || nodeVer[1] >= 8);\n\n// Internal debug logging via `console.warn`.\nvar _selfTrace = function selfTraceNoop() {};\nif (process.env.BUNYAN_SELF_TRACE === '1') {\n    _selfTrace = function selfTrace() {\n        process.stderr.write('[bunyan self-trace] ');\n        console.warn.apply(null, arguments);\n    }\n}\n\n// Output modes.\nvar OM_LONG = 1;\nvar OM_JSON = 2;\nvar OM_INSPECT = 3;\nvar OM_SIMPLE = 4;\nvar OM_SHORT = 5;\nvar OM_BUNYAN = 6;\nvar OM_FROM_NAME = {\n    'long': OM_LONG,\n    'paul': OM_LONG,  /* backward compat */\n    'json': OM_JSON,\n    'inspect': OM_INSPECT,\n    'simple': OM_SIMPLE,\n    'short': OM_SHORT,\n    'bunyan': OM_BUNYAN\n};\n\n\n// Levels\nvar TRACE = 10;\nvar DEBUG = 20;\nvar INFO = 30;\nvar WARN = 40;\nvar ERROR = 50;\nvar FATAL = 60;\n\nvar levelFromName = {\n    'trace': TRACE,\n    'debug': DEBUG,\n    'info': INFO,\n    'warn': WARN,\n    'error': ERROR,\n    'fatal': FATAL\n};\nvar nameFromLevel = {};\nvar upperNameFromLevel = {};\nvar upperPaddedNameFromLevel = {};\nObject.keys(levelFromName).forEach(function (name) {\n    var lvl = levelFromName[name];\n    nameFromLevel[lvl] = name;\n    upperNameFromLevel[lvl] = name.toUpperCase();\n    upperPaddedNameFromLevel[lvl] = (\n        name.length === 4 ? ' ' : '') + name.toUpperCase();\n});\n\n\n// Display time formats.\nvar TIME_UTC = 1;  // the default, bunyan's native format\nvar TIME_LOCAL = 2;\n\n// Timezone formats: output format -> momentjs format string\nvar TIMEZONE_UTC_FORMATS = {\n    long:  '[[]YYYY-MM-DD[T]HH:mm:ss.SSS[Z][]]',\n    short: 'HH:mm:ss.SSS[Z]'\n};\nvar TIMEZONE_LOCAL_FORMATS = {\n    long:  '[[]YYYY-MM-DD[T]HH:mm:ss.SSSZ[]]',\n    short: 'HH:mm:ss.SSS'\n};\n\n\n// Boolean set to true when we are in the process of exiting. We don't always\n// hard-exit (e.g. when staying alive while the pager completes).\nvar exiting = false;\n\n// The current raw input line being processed. Used for `uncaughtException`.\nvar currLine = null;\n\n// Child dtrace process, if any. Used for signal-handling.\nvar child = null;\n\n// Whether ANSI codes are being used. Used for signal-handling.\nvar usingAnsiCodes = false;\n\n// Used to tell the 'uncaughtException' handler that '-c CODE' is being used.\nvar gUsingConditionOpts = false;\n\n// Pager child process, and output stream to which to write.\nvar pager = null;\nvar stdout = process.stdout;\n\n\n\n//---- support functions\n\nvar _version = null;\nfunction getVersion() {\n    if (_version === null) {\n        _version = require('../package.json').version;\n    }\n    return _version;\n}\n\n\nvar format = util.format;\nif (!format) {\n    /* BEGIN JSSTYLED */\n    // If not node 0.6, then use its `util.format`:\n    // <https://github.com/joyent/node/blob/master/lib/util.js#L22>:\n    var inspect = util.inspect;\n    var formatRegExp = /%[sdj%]/g;\n    format = function format(f) {\n        if (typeof f !== 'string') {\n            var objects = [];\n            for (var i = 0; i < arguments.length; i++) {\n                objects.push(inspect(arguments[i]));\n            }\n            return objects.join(' ');\n        }\n\n        var i = 1;\n        var args = arguments;\n        var len = args.length;\n        var str = String(f).replace(formatRegExp, function (x) {\n            if (i >= len)\n                return x;\n            switch (x) {\n                case '%s': return String(args[i++]);\n                case '%d': return Number(args[i++]);\n                case '%j': return JSON.stringify(args[i++]);\n                case '%%': return '%';\n                default:\n                    return x;\n            }\n        });\n        for (var x = args[i]; i < len; x = args[++i]) {\n            if (x === null || typeof x !== 'object') {\n                str += ' ' + x;\n            } else {\n                str += ' ' + inspect(x);\n            }\n        }\n        return str;\n    };\n    /* END JSSTYLED */\n}\n\nfunction indent(s) {\n    return '    ' + s.split(/\\r?\\n/).join('\\n    ');\n}\n\nfunction objCopy(obj) {\n    if (obj === null) {\n        return null;\n    } else if (Array.isArray(obj)) {\n        return obj.slice();\n    } else {\n        var copy = {};\n        Object.keys(obj).forEach(function (k) {\n            copy[k] = obj[k];\n        });\n        return copy;\n    }\n}\n\nfunction printHelp() {\n    /* BEGIN JSSTYLED */\n    p('Usage:');\n    p('  bunyan [OPTIONS] [FILE ...]');\n    p('  ... | bunyan [OPTIONS]');\n    p('  bunyan [OPTIONS] -p PID');\n    p('');\n    p('Filter and pretty-print Bunyan log file content.');\n    p('');\n    p('General options:');\n    p('  -h, --help    print this help info and exit');\n    p('  --version     print version of this command and exit');\n    p('');\n    p('Runtime log snooping (via DTrace, only on supported platforms):');\n    p('  -p PID        Process bunyan:log-* probes from the process');\n    p('                with the given PID. Can be used multiple times,');\n    p('                or specify all processes with \"*\", or a set of');\n    p('                processes whose command & args match a pattern');\n    p('                with \"-p NAME\".');\n    p('');\n    p('Filtering options:');\n    p('  -l, --level LEVEL');\n    p('                Only show messages at or above the specified level.');\n    p('                You can specify level *names* or the internal numeric');\n    p('                values.');\n    p('  -c, --condition CONDITION');\n    p('                Run each log message through the condition and');\n    p('                only show those that return truish. E.g.:');\n    p('                    -c \\'this.pid == 123\\'');\n    p('                    -c \\'this.level == DEBUG\\'');\n    p('                    -c \\'this.msg.indexOf(\"boom\") != -1\\'');\n    p('                \"CONDITION\" must be legal JS code. `this` holds');\n    p('                the log record. The TRACE, DEBUG, ... FATAL values');\n    p('                are defined to help with comparing `this.level`.');\n    p('  --strict      Suppress all but legal Bunyan JSON log lines. By default');\n    p('                non-JSON, and non-Bunyan lines are passed through.');\n    p('');\n    p('Output options:');\n    p('  --pager       Pipe output into `less` (or $PAGER if set), if');\n    p('                stdout is a TTY. This overrides $BUNYAN_NO_PAGER.');\n    p('                Note: Paging is only supported on node >=0.8.');\n    p('  --no-pager    Do not pipe output into a pager.');\n    p('  --color       Colorize output. Defaults to try if output');\n    p('                stream is a TTY.');\n    p('  --no-color    Force no coloring (e.g. terminal doesn\\'t support it)');\n    p('  -o, --output MODE');\n    p('                Specify an output mode/format. One of');\n    p('                  long: (the default) pretty');\n    p('                  json: JSON output, 2-space indent');\n    p('                  json-N: JSON output, N-space indent, e.g. \"json-4\"');\n    p('                  bunyan: 0 indented JSON, bunyan\\'s native format');\n    p('                  inspect: node.js `util.inspect` output');\n    p('                  short: like \"long\", but more concise');\n    p('                  simple: level, followed by \"-\" and then the message');\n    p('  -j            shortcut for `-o json`');\n    p('  -0            shortcut for `-o bunyan`');\n    p('  -L, --time local');\n    p('                Display time field in local time, rather than UTC.');\n    p('');\n    p('Environment Variables:');\n    p('  BUNYAN_NO_COLOR    Set to a non-empty value to force no output ');\n    p('                     coloring. See \"--no-color\".');\n    p('  BUNYAN_NO_PAGER    Disable piping output to a pager. ');\n    p('                     See \"--no-pager\".');\n    p('');\n    p('See <https://github.com/trentm/node-bunyan> for more complete docs.');\n    p('Please report bugs to <https://github.com/trentm/node-bunyan/issues>.');\n    /* END JSSTYLED */\n}\n\n/*\n * If the user specifies multiple input sources, we want to print out records\n * from all sources in a single, chronologically ordered stream.  To do this\n * efficiently, we first assume that all records within each source are ordered\n * already, so we need only keep track of the next record in each source and\n * the time of the last record emitted.  To avoid excess memory usage, we\n * pause() streams that are ahead of others.\n *\n * 'streams' is an object indexed by source name (file name) which specifies:\n *\n *    stream        Actual stream object, so that we can pause and resume it.\n *\n *    records       Array of log records we've read, but not yet emitted.  Each\n *                  record includes 'line' (the raw line), 'rec' (the JSON\n *                  record), and 'time' (the parsed time value).\n *\n *    done          Whether the stream has any more records to emit.\n */\nvar streams = {};\n\nfunction gotRecord(file, line, rec, opts, stylize)\n{\n    var time = new Date(rec.time);\n\n    streams[file]['records'].push({ line: line, rec: rec, time: time });\n    emitNextRecord(opts, stylize);\n}\n\nfunction filterRecord(rec, opts)\n{\n    if (opts.level && rec.level < opts.level) {\n        return false;\n    }\n\n    if (opts.condFuncs) {\n        var recCopy = objCopy(rec);\n        for (var i = 0; i < opts.condFuncs.length; i++) {\n            var pass = opts.condFuncs[i].call(recCopy);\n            if (!pass)\n                return false;\n        }\n    } else if (opts.condVm) {\n        for (var i = 0; i < opts.condVm.length; i++) {\n            var pass = opts.condVm[i].runInNewContext(rec);\n            if (!pass)\n                return false;\n        }\n    }\n\n    return true;\n}\n\nfunction emitNextRecord(opts, stylize)\n{\n    var ofile, ready, minfile, rec;\n\n    for (;;) {\n        /*\n         * Take a first pass through the input streams to see if we have a\n         * record from all of them.  If not, we'll pause any streams for\n         * which we do already have a record (to avoid consuming excess\n         * memory) and then wait until we have records from the others\n         * before emitting the next record.\n         *\n         * As part of the same pass, we look for the earliest record\n         * we have not yet emitted.\n         */\n        minfile = undefined;\n        ready = true;\n        for (ofile in streams) {\n\n            if (streams[ofile].stream === null ||\n                (!streams[ofile].done && streams[ofile].records.length === 0)) {\n                ready = false;\n                break;\n            }\n\n            if (streams[ofile].records.length > 0 &&\n                (minfile === undefined ||\n                    streams[minfile].records[0].time >\n                        streams[ofile].records[0].time)) {\n                minfile = ofile;\n            }\n        }\n\n        if (!ready || minfile === undefined) {\n            for (ofile in streams) {\n                if (!streams[ofile].stream || streams[ofile].done)\n                    continue;\n\n                if (streams[ofile].records.length > 0) {\n                    if (!streams[ofile].paused) {\n                        streams[ofile].paused = true;\n                        streams[ofile].stream.pause();\n                    }\n                } else if (streams[ofile].paused) {\n                    streams[ofile].paused = false;\n                    streams[ofile].stream.resume();\n                }\n            }\n\n            return;\n        }\n\n        /*\n         * Emit the next record for 'minfile', and invoke ourselves again to\n         * make sure we emit as many records as we can right now.\n         */\n        rec = streams[minfile].records.shift();\n        emitRecord(rec.rec, rec.line, opts, stylize);\n    }\n}\n\n/**\n * Return a function for the given JS code that returns.\n *\n * If no 'return' in the given javascript snippet, then assume we are a single\n * statement and wrap in 'return (...)'. This is for convenience for short\n * '-c ...' snippets.\n */\nfunction funcWithReturnFromSnippet(js) {\n    // auto-\"return\"\n    if (js.indexOf('return') === -1) {\n        if (js.substring(js.length - 1) === ';') {\n            js = js.substring(0, js.length - 1);\n        }\n        js = 'return (' + js + ')';\n    }\n\n    // Expose level definitions to condition func context\n    var varDefs = [];\n    Object.keys(upperNameFromLevel).forEach(function (lvl) {\n        varDefs.push(format('var %s = %d;',\n                upperNameFromLevel[lvl], lvl));\n    });\n    varDefs = varDefs.join('\\n') + '\\n';\n\n    return (new Function(varDefs + js));\n}\n\n/**\n * Parse the command-line options and arguments into an object.\n *\n *    {\n *      'args': [...]       // arguments\n *      'help': true,       // true if '-h' option given\n *       // etc.\n *    }\n *\n * @return {Object} The parsed options. `.args` is the argument list.\n * @throws {Error} If there is an error parsing argv.\n */\nfunction parseArgv(argv) {\n    var parsed = {\n        args: [],\n        help: false,\n        color: null,\n        paginate: null,\n        outputMode: OM_LONG,\n        jsonIndent: 2,\n        level: null,\n        strict: false,\n        pids: null,\n        pidsType: null,\n        timeFormat: TIME_UTC  // one of the TIME_ constants\n    };\n\n    // Turn '-iH' into '-i -H', except for argument-accepting options.\n    var args = argv.slice(2);  // drop ['node', 'scriptname']\n    var newArgs = [];\n    var optTakesArg = {'d': true, 'o': true, 'c': true, 'l': true, 'p': true};\n    for (var i = 0; i < args.length; i++) {\n        if (args[i].charAt(0) === '-' && args[i].charAt(1) !== '-' &&\n            args[i].length > 2)\n        {\n            var splitOpts = args[i].slice(1).split('');\n            for (var j = 0; j < splitOpts.length; j++) {\n                newArgs.push('-' + splitOpts[j]);\n                if (optTakesArg[splitOpts[j]]) {\n                    var optArg = splitOpts.slice(j+1).join('');\n                    if (optArg.length) {\n                        newArgs.push(optArg);\n                    }\n                    break;\n                }\n            }\n        } else {\n            newArgs.push(args[i]);\n        }\n    }\n    args = newArgs;\n\n    // Expose level definitions to condition vm context\n    var condDefines = [];\n    Object.keys(upperNameFromLevel).forEach(function (lvl) {\n        condDefines.push(\n            format('Object.prototype.%s = %s;', upperNameFromLevel[lvl], lvl));\n    });\n    condDefines = condDefines.join('\\n') + '\\n';\n\n    var endOfOptions = false;\n    while (args.length > 0) {\n        var arg = args.shift();\n        switch (arg) {\n            case '--':\n                endOfOptions = true;\n                break;\n            case '-h': // display help and exit\n            case '--help':\n                parsed.help = true;\n                break;\n            case '--version':\n                parsed.version = true;\n                break;\n            case '--strict':\n                parsed.strict = true;\n                break;\n            case '--color':\n                parsed.color = true;\n                break;\n            case '--no-color':\n                parsed.color = false;\n                break;\n            case '--pager':\n                parsed.paginate = true;\n                break;\n            case '--no-pager':\n                parsed.paginate = false;\n                break;\n            case '-o':\n            case '--output':\n                var name = args.shift();\n                var idx = name.lastIndexOf('-');\n                if (idx !== -1) {\n                    var indentation = Number(name.slice(idx+1));\n                    if (! isNaN(indentation)) {\n                        parsed.jsonIndent = indentation;\n                        name = name.slice(0, idx);\n                    }\n                }\n                parsed.outputMode = OM_FROM_NAME[name];\n                if (parsed.outputMode === undefined) {\n                    throw new Error('unknown output mode: \"'+name+'\"');\n                }\n                break;\n            case '-j': // output with JSON.stringify\n                parsed.outputMode = OM_JSON;\n                break;\n            case '-0':\n                parsed.outputMode = OM_BUNYAN;\n                break;\n            case '-L':\n                parsed.timeFormat = TIME_LOCAL;\n                if (!moment) {\n                    throw new Error(\n                        'could not find moment package required for \"-L\"');\n                }\n                break;\n            case '--time':\n                var timeArg = args.shift();\n                switch (timeArg) {\n                case 'utc':\n                    parsed.timeFormat = TIME_UTC;\n                    break\n                case 'local':\n                    parsed.timeFormat = TIME_LOCAL;\n                    if (!moment) {\n                        throw new Error('could not find moment package '\n                            + 'required for \"--time=local\"');\n                    }\n                    break\n                case undefined:\n                    throw new Error('missing argument to \"--time\"');\n                default:\n                    throw new Error(format('invalid time format: \"%s\"',\n                        timeArg));\n                }\n                break;\n            case '-p':\n                if (!parsed.pids) {\n                    parsed.pids = [];\n                }\n                var pidArg = args.shift();\n                var pid = +(pidArg);\n                if (!isNaN(pid) || pidArg === '*') {\n                    if (parsed.pidsType && parsed.pidsType !== 'num') {\n                        throw new Error(format('cannot mix PID name and '\n                            + 'number arguments: \"%s\"', pidArg));\n                    }\n                    parsed.pidsType = 'num';\n                    if (!parsed.pids) {\n                        parsed.pids = [];\n                    }\n                    parsed.pids.push(isNaN(pid) ? pidArg : pid);\n                } else {\n                    if (parsed.pidsType && parsed.pidsType !== 'name') {\n                        throw new Error(format('cannot mix PID name and '\n                            + 'number arguments: \"%s\"', pidArg));\n                    }\n                    parsed.pidsType = 'name';\n                    parsed.pids = pidArg;\n                }\n                break;\n            case '-l':\n            case '--level':\n                var levelArg = args.shift();\n                var level = +(levelArg);\n                if (isNaN(level)) {\n                    level = +levelFromName[levelArg.toLowerCase()];\n                }\n                if (isNaN(level)) {\n                    throw new Error('unknown level value: \"'+levelArg+'\"');\n                }\n                parsed.level = level;\n                break;\n            case '-c':\n            case '--condition':\n                gUsingConditionOpts = true;\n                var condition = args.shift();\n                if (Boolean(process.env.BUNYAN_EXEC &&\n                    process.env.BUNYAN_EXEC === 'vm'))\n                {\n                    parsed.condVm = parsed.condVm || [];\n                    var scriptName = 'bunyan-condition-'+parsed.condVm.length;\n                    var code = condDefines + condition;\n                    var script;\n                    try {\n                        script = vm.createScript(code, scriptName);\n                    } catch (complErr) {\n                        throw new Error(format('illegal CONDITION code: %s\\n'\n                            + '  CONDITION script:\\n'\n                            + '%s\\n'\n                            + '  Error:\\n'\n                            + '%s',\n                            complErr, indent(code), indent(complErr.stack)));\n                    }\n\n                    // Ensure this is a reasonably safe CONDITION.\n                    try {\n                        script.runInNewContext(minValidRecord);\n                    } catch (condErr) {\n                        throw new Error(format(\n                            /* JSSTYLED */\n                            'CONDITION code cannot safely filter a minimal Bunyan log record\\n'\n                            + '  CONDITION script:\\n'\n                            + '%s\\n'\n                            + '  Minimal Bunyan log record:\\n'\n                            + '%s\\n'\n                            + '  Filter error:\\n'\n                            + '%s',\n                            indent(code),\n                            indent(JSON.stringify(minValidRecord, null, 2)),\n                            indent(condErr.stack)\n                            ));\n                    }\n                    parsed.condVm.push(script);\n                } else  {\n                    parsed.condFuncs = parsed.condFuncs || [];\n                    parsed.condFuncs.push(funcWithReturnFromSnippet(condition));\n                }\n                break;\n            default: // arguments\n                if (!endOfOptions && arg.length > 0 && arg[0] === '-') {\n                    throw new Error('unknown option \"'+arg+'\"');\n                }\n                parsed.args.push(arg);\n                break;\n        }\n    }\n    //TODO: '--' handling and error on a first arg that looks like an option.\n\n    return parsed;\n}\n\n\nfunction isInteger(s) {\n    return (s.search(/^-?[0-9]+$/) == 0);\n}\n\n\n// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics\n// Suggested colors (some are unreadable in common cases):\n// - Good: cyan, yellow (limited use), bold, green, magenta, red\n// - Bad: blue (not visible on cmd.exe), grey (same color as background on\n//   Solarized Dark theme from <https://github.com/altercation/solarized>, see\n//   issue #160)\nvar colors = {\n    'bold' : [1, 22],\n    'italic' : [3, 23],\n    'underline' : [4, 24],\n    'inverse' : [7, 27],\n    'white' : [37, 39],\n    'grey' : [90, 39],\n    'black' : [30, 39],\n    'blue' : [34, 39],\n    'cyan' : [36, 39],\n    'green' : [32, 39],\n    'magenta' : [35, 39],\n    'red' : [31, 39],\n    'yellow' : [33, 39]\n};\n\nfunction stylizeWithColor(str, color) {\n    if (!str)\n        return '';\n    var codes = colors[color];\n    if (codes) {\n        return '\\033[' + codes[0] + 'm' + str +\n                     '\\033[' + codes[1] + 'm';\n    } else {\n        return str;\n    }\n}\n\nfunction stylizeWithoutColor(str, color) {\n    return str;\n}\n\n\n/**\n * Is this a valid Bunyan log record.\n */\nfunction isValidRecord(rec) {\n    if (rec.v == null ||\n            rec.level == null ||\n            rec.name == null ||\n            rec.hostname == null ||\n            rec.pid == null ||\n            rec.time == null ||\n            rec.msg == null) {\n        // Not valid Bunyan log.\n        return false;\n    } else {\n        return true;\n    }\n}\nvar minValidRecord = {\n    v: 0,   //TODO: get this from bunyan.LOG_VERSION\n    level: INFO,\n    name: 'name',\n    hostname: 'hostname',\n    pid: 123,\n    time: Date.now(),\n    msg: 'msg'\n};\n\n\n/**\n * Parses the given log line and either emits it right away (for invalid\n * records) or enqueues it for emitting later when it's the next line to show.\n */\nfunction handleLogLine(file, line, opts, stylize) {\n    if (exiting) {\n        _selfTrace('warn: handleLogLine called while exiting');\n        return;\n    }\n\n    currLine = line; // intentionally global\n\n    // Emit non-JSON lines immediately.\n    var rec;\n    if (!line) {\n        if (!opts.strict) emit(line + '\\n');\n        return;\n    } else if (line[0] !== '{') {\n        if (!opts.strict) emit(line + '\\n');  // not JSON\n        return;\n    } else {\n        try {\n            rec = JSON.parse(line);\n        } catch (e) {\n            if (!opts.strict) emit(line + '\\n');\n            return;\n        }\n    }\n\n    if (!isValidRecord(rec)) {\n        if (!opts.strict) emit(line + '\\n');\n        return;\n    }\n\n    if (!filterRecord(rec, opts))\n        return;\n\n    if (file === null)\n        return emitRecord(rec, line, opts, stylize);\n\n    return gotRecord(file, line, rec, opts, stylize);\n}\n\n/**\n * Print out a single result, considering input options.\n */\nfunction emitRecord(rec, line, opts, stylize) {\n    var short = false;\n\n    switch (opts.outputMode) {\n    case OM_SHORT:\n        short = true;\n        /* jsl:fall-thru */\n\n    case OM_LONG:\n        //    [time] LEVEL: name[/comp]/pid on hostname (src): msg* (extras...)\n        //        msg*\n        //        --\n        //        long and multi-line extras\n        //        ...\n        // If 'msg' is single-line, then it goes in the top line.\n        // If 'req', show the request.\n        // If 'res', show the response.\n        // If 'err' and 'err.stack' then show that.\n        if (!isValidRecord(rec)) {\n            return emit(line + '\\n');\n        }\n\n        delete rec.v;\n\n        // Time.\n        var time;\n        if (!short && opts.timeFormat === TIME_UTC) {\n            // Fast default path: We assume the raw `rec.time` is a UTC time\n            // in ISO 8601 format (per spec).\n            time = '[' + rec.time + ']';\n        } else if (!moment && opts.timeFormat === TIME_UTC) {\n            // Don't require momentjs install, as long as not using TIME_LOCAL.\n            time = rec.time.substr(11);\n        } else {\n            var tzFormat;\n            var moTime = moment(rec.time);\n            switch (opts.timeFormat) {\n            case TIME_UTC:\n                tzFormat = TIMEZONE_UTC_FORMATS[short ? 'short' : 'long'];\n                moTime.utc();\n                break;\n            case TIME_LOCAL:\n                tzFormat = TIMEZONE_LOCAL_FORMATS[short ? 'short' : 'long'];\n                break;\n            default:\n                throw new Error('unexpected timeFormat: ' + opts.timeFormat);\n            };\n            time = moTime.format(tzFormat);\n        }\n        time = stylize(time, 'none');\n        delete rec.time;\n\n        var nameStr = rec.name;\n        delete rec.name;\n\n        if (rec.component) {\n            nameStr += '/' + rec.component;\n        }\n        delete rec.component;\n\n        if (!short)\n            nameStr += '/' + rec.pid;\n        delete rec.pid;\n\n        var level = (upperPaddedNameFromLevel[rec.level] || 'LVL' + rec.level);\n        if (opts.color) {\n            var colorFromLevel = {\n                10: 'white',    // TRACE\n                20: 'yellow',   // DEBUG\n                30: 'cyan',     // INFO\n                40: 'magenta',  // WARN\n                50: 'red',      // ERROR\n                60: 'inverse',  // FATAL\n            };\n            level = stylize(level, colorFromLevel[rec.level]);\n        }\n        delete rec.level;\n\n        var src = '';\n        if (rec.src && rec.src.file) {\n            var s = rec.src;\n            if (s.func) {\n                src = format(' (%s:%d in %s)', s.file, s.line, s.func);\n            } else {\n                src = format(' (%s:%d)', s.file, s.line);\n            }\n            src = stylize(src, 'green');\n        }\n        delete rec.src;\n\n        var hostname = rec.hostname;\n        delete rec.hostname;\n\n        var extras = [];\n        var details = [];\n\n        if (rec.req_id) {\n            extras.push('req_id=' + rec.req_id);\n        }\n        delete rec.req_id;\n\n        var onelineMsg;\n        if (rec.msg.indexOf('\\n') !== -1) {\n            onelineMsg = '';\n            details.push(indent(stylize(rec.msg, 'cyan')));\n        } else {\n            onelineMsg = ' ' + stylize(rec.msg, 'cyan');\n        }\n        delete rec.msg;\n\n        if (rec.req && typeof (rec.req) === 'object') {\n            var req = rec.req;\n            delete rec.req;\n            var headers = req.headers;\n            if (!headers) {\n                headers = '';\n            } else if (typeof (headers) === 'string') {\n                headers = '\\n' + headers;\n            } else if (typeof (headers) === 'object') {\n                headers = '\\n' + Object.keys(headers).map(function (h) {\n                    return h + ': ' + headers[h];\n                }).join('\\n');\n            }\n            var s = format('%s %s HTTP/%s%s', req.method,\n                req.url,\n                req.httpVersion || '1.1',\n                headers\n            );\n            delete req.url;\n            delete req.method;\n            delete req.httpVersion;\n            delete req.headers;\n            if (req.body) {\n                s += '\\n\\n' + (typeof (req.body) === 'object'\n                    ? JSON.stringify(req.body, null, 2) : req.body);\n                delete req.body;\n            }\n            if (req.trailers && Object.keys(req.trailers) > 0) {\n                s += '\\n' + Object.keys(req.trailers).map(function (t) {\n                    return t + ': ' + req.trailers[t];\n                }).join('\\n');\n            }\n            delete req.trailers;\n            details.push(indent(s));\n            // E.g. for extra 'foo' field on 'req', add 'req.foo' at\n            // top-level. This *does* have the potential to stomp on a\n            // literal 'req.foo' key.\n            Object.keys(req).forEach(function (k) {\n                rec['req.' + k] = req[k];\n            })\n        }\n\n        if (rec.client_req && typeof (rec.client_req) === 'object') {\n            var client_req = rec.client_req;\n            delete rec.client_req;\n\n            var headers = client_req.headers;\n            delete client_req.headers;\n\n            var s = format('%s %s HTTP/%s%s',\n                client_req.method,\n                client_req.url,\n                client_req.httpVersion || '1.1',\n                (headers ?\n                    '\\n' + Object.keys(headers).map(\n                        function (h) {\n                            return h + ': ' + headers[h];\n                        }).join('\\n') :\n                    ''));\n            delete client_req.method;\n            delete client_req.url;\n            delete client_req.httpVersion;\n\n            if (client_req.body) {\n                s += '\\n\\n' + (typeof (client_req.body) === 'object' ?\n                    JSON.stringify(client_req.body, null, 2) :\n                    client_req.body);\n                delete client_req.body;\n            }\n            // E.g. for extra 'foo' field on 'client_req', add\n            // 'client_req.foo' at top-level. This *does* have the potential\n            // to stomp on a literal 'client_req.foo' key.\n            Object.keys(client_req).forEach(function (k) {\n                rec['client_req.' + k] = client_req[k];\n            });\n            details.push(indent(s));\n        }\n\n        function _res(res) {\n            var s = '';\n\n            /*\n             * Handle `res.header` or `res.headers` as either a string or\n             * an object of header key/value pairs. Prefer `res.header` if set,\n             * because that's what Bunyan's own `res` serializer specifies,\n             * because that's the value in Node.js's core HTTP server response\n             * implementation that has all the implicit headers.\n             *\n             * Note: `res.header` (string) typically includes the 'HTTP/1.1 ...'\n             * status line.\n             */\n            var headerTypes = {string: true, object: true};\n            var headers;\n            var headersStr = '';\n            var headersHaveStatusLine = false;\n            if (res.header && headerTypes[typeof (res.header)]) {\n                headers = res.header;\n                delete res.header;\n            } else if (res.headers && headerTypes[typeof (res.headers)]) {\n                headers = res.headers;\n                delete res.headers;\n            }\n            if (headers === undefined) {\n                /* pass through */\n            } else if (typeof (headers) === 'string') {\n                headersStr = headers.trimRight(); // Trim the CRLF.\n                if (headersStr.slice(0, 5) === 'HTTP/') {\n                    headersHaveStatusLine = true;\n                }\n            } else {\n                headersStr += Object.keys(headers).map(\n                    function (h) { return h + ': ' + headers[h]; }).join('\\n');\n            }\n\n            /*\n             * Add a 'HTTP/1.1 ...' status line if the headers didn't already\n             * include it.\n             */\n            if (!headersHaveStatusLine && res.statusCode !== undefined) {\n                s += format('HTTP/1.1 %s %s\\n', res.statusCode,\n                    http.STATUS_CODES[res.statusCode]);\n            }\n            delete res.statusCode;\n            s += headersStr;\n\n            if (res.body !== undefined) {\n                var body = (typeof (res.body) === 'object'\n                    ? JSON.stringify(res.body, null, 2) : res.body);\n                if (body.length > 0) { s += '\\n\\n' + body };\n                delete res.body;\n            } else {\n                s = s.trimRight();\n            }\n            if (res.trailer) {\n                s += '\\n' + res.trailer;\n            }\n            delete res.trailer;\n            if (s) {\n                details.push(indent(s));\n            }\n            // E.g. for extra 'foo' field on 'res', add 'res.foo' at\n            // top-level. This *does* have the potential to stomp on a\n            // literal 'res.foo' key.\n            Object.keys(res).forEach(function (k) {\n                rec['res.' + k] = res[k];\n            });\n        }\n\n        if (rec.res && typeof (rec.res) === 'object') {\n            _res(rec.res);\n            delete rec.res;\n        }\n        if (rec.client_res && typeof (rec.client_res) === 'object') {\n            _res(rec.client_res);\n            delete rec.client_res;\n        }\n\n        if (rec.err && rec.err.stack) {\n            var err = rec.err\n            if (typeof (err.stack) !== 'string') {\n                details.push(indent(err.stack.toString()));\n            } else {\n                details.push(indent(err.stack));\n            }\n            delete err.message;\n            delete err.name;\n            delete err.stack;\n            // E.g. for extra 'foo' field on 'err', add 'err.foo' at\n            // top-level. This *does* have the potential to stomp on a\n            // literal 'err.foo' key.\n            Object.keys(err).forEach(function (k) {\n                rec['err.' + k] = err[k];\n            })\n            delete rec.err;\n        }\n\n        var leftover = Object.keys(rec);\n        for (var i = 0; i < leftover.length; i++) {\n            var key = leftover[i];\n            var value = rec[key];\n            var stringified = false;\n            if (typeof (value) !== 'string') {\n                value = JSON.stringify(value, null, 2);\n                stringified = true;\n            }\n            if (value.indexOf('\\n') !== -1 || value.length > 50) {\n                details.push(indent(key + ': ' + value));\n            } else if (!stringified && (value.indexOf(' ') != -1 ||\n                value.length === 0))\n            {\n                extras.push(key + '=' + JSON.stringify(value));\n            } else {\n                extras.push(key + '=' + value);\n            }\n        }\n\n        extras = stylize(\n            (extras.length ? ' (' + extras.join(', ') + ')' : ''), 'none');\n        details = stylize(\n            (details.length ? details.join('\\n    --\\n') + '\\n' : ''), 'none');\n        if (!short)\n            emit(format('%s %s: %s on %s%s:%s%s\\n%s',\n                time,\n                level,\n                nameStr,\n                hostname || '<no-hostname>',\n                src,\n                onelineMsg,\n                extras,\n                details));\n        else\n            emit(format('%s %s %s:%s%s\\n%s',\n                time,\n                level,\n                nameStr,\n                onelineMsg,\n                extras,\n                details));\n        break;\n\n    case OM_INSPECT:\n        emit(util.inspect(rec, false, Infinity, true) + '\\n');\n        break;\n\n    case OM_BUNYAN:\n        emit(JSON.stringify(rec, null, 0) + '\\n');\n        break;\n\n    case OM_JSON:\n        emit(JSON.stringify(rec, null, opts.jsonIndent) + '\\n');\n        break;\n\n    case OM_SIMPLE:\n        /* JSSTYLED */\n        // <http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/SimpleLayout.html>\n        if (!isValidRecord(rec)) {\n            return emit(line + '\\n');\n        }\n        emit(format('%s - %s\\n',\n            upperNameFromLevel[rec.level] || 'LVL' + rec.level,\n            rec.msg));\n        break;\n    default:\n        throw new Error('unknown output mode: '+opts.outputMode);\n    }\n}\n\n\nfunction emit(s) {\n    try {\n        stdout.write(s);\n    } catch (writeErr) {\n        _selfTrace('exception from stdout.write:', writeErr)\n        // Handle any exceptions in stdout writing in `stdout.on('error', ...)`.\n    }\n}\n\n\n/**\n * Process all input from stdin.\n *\n * @params opts {Object} Bunyan options object.\n * @param stylize {Function} Output stylize function to use.\n * @param callback {Function} `function ()`\n */\nfunction processStdin(opts, stylize, callback) {\n    var leftover = '';  // Left-over partial line from last chunk.\n    var stdin = process.stdin;\n    stdin.resume();\n    stdin.setEncoding('utf8');\n    stdin.on('data', function (chunk) {\n        var lines = chunk.split(/\\r\\n|\\n/);\n        var length = lines.length;\n        if (length === 1) {\n            leftover += lines[0];\n            return;\n        }\n\n        if (length > 1) {\n            handleLogLine(null, leftover + lines[0], opts, stylize);\n        }\n        leftover = lines.pop();\n        length -= 1;\n        for (var i = 1; i < length; i++) {\n            handleLogLine(null, lines[i], opts, stylize);\n        }\n    });\n    stdin.on('end', function () {\n        if (leftover) {\n            handleLogLine(null, leftover, opts, stylize);\n            leftover = '';\n        }\n        callback();\n    });\n}\n\n\n/**\n * Process bunyan:log-* probes from the given pid.\n *\n * @params opts {Object} Bunyan options object.\n * @param stylize {Function} Output stylize function to use.\n * @param callback {Function} `function (code)`\n */\nfunction processPids(opts, stylize, callback) {\n    var leftover = '';  // Left-over partial line from last chunk.\n\n    /**\n     * Get the PIDs to dtrace.\n     *\n     * @param cb {Function} `function (errCode, pids)`\n     */\n    function getPids(cb) {\n        if (opts.pidsType === 'num') {\n            return cb(null, opts.pids);\n        }\n        if (process.platform === 'sunos') {\n            execFile('/bin/pgrep', ['-lf', opts.pids],\n                function (pidsErr, stdout, stderr) {\n                    if (pidsErr) {\n                        warn('bunyan: error getting PIDs for \"%s\": %s\\n%s\\n%s',\n                            opts.pids, pidsErr.message, stdout, stderr);\n                        return cb(1);\n                    }\n                    var pids = stdout.trim().split('\\n')\n                        .map(function (line) {\n                            return line.trim().split(/\\s+/)[0]\n                        })\n                        .filter(function (pid) {\n                            return Number(pid) !== process.pid\n                        });\n                    if (pids.length === 0) {\n                        warn('bunyan: error: no matching PIDs found for \"%s\"',\n                            opts.pids);\n                        return cb(2);\n                    }\n                    cb(null, pids);\n                }\n            );\n        } else {\n            var regex = opts.pids;\n            if (regex && /[a-zA-Z0-9_]/.test(regex[0])) {\n                // 'foo' -> '[f]oo' trick to exclude the 'grep' PID from its\n                // own search.\n                regex = '[' + regex[0] + ']' + regex.slice(1);\n            }\n            var cmd = format('ps -A -o pid,command | grep \\'%s\\'',\n                // Escape single-quotes to avoid breaking the grep arg quoting\n                // (leading to a possible *code execution*) and backslashes to\n                // avoid undoing that escaping.\n                regex.replace(/\\\\/g, '\\\\\\\\')\n                    // JSSTYLED\n                    .replace(/'/g, \"'\\\\''\"));\n            _selfTrace('exec cmd: %j', cmd);\n            exec(cmd,\n                function (pidsErr, stdout, stderr) {\n                    if (pidsErr) {\n                        warn('bunyan: error getting PIDs for \"%s\": %s\\n%s\\n%s',\n                            opts.pids, pidsErr.message, stdout, stderr);\n                        return cb(1);\n                    }\n                    var pids = stdout.trim().split('\\n')\n                        .map(function (line) {\n                            return line.trim().split(/\\s+/)[0];\n                        })\n                        .filter(function (pid) {\n                            return Number(pid) !== process.pid;\n                        });\n                    if (pids.length === 0) {\n                        warn('bunyan: error: no matching PIDs found for \"%s\"',\n                            opts.pids);\n                        return cb(2);\n                    }\n                    cb(null, pids);\n                }\n            );\n        }\n    }\n\n    getPids(function (errCode, pids) {\n        if (errCode) {\n            return callback(errCode);\n        }\n\n        var probes = pids.map(function (pid) {\n            if (!opts.level)\n                return format('bunyan%s:::log-*', pid);\n\n            var rval = [], l;\n\n            for (l in levelFromName) {\n                if (levelFromName[l] >= opts.level)\n                    rval.push(format('bunyan%s:::log-%s', pid, l));\n            }\n\n            if (rval.length != 0)\n                return rval.join(',');\n\n            warn('bunyan: error: level (%d) exceeds maximum logging level',\n                opts.level);\n            cleanupAndExit(1);\n        }).join(',');\n        var argv = ['dtrace', '-Z', '-x', 'strsize=4k',\n            '-x', 'switchrate=10hz', '-qn',\n            format('%s{printf(\"%s\", copyinstr(arg0))}', probes)];\n        //console.log('dtrace argv: %s', argv);\n        var dtrace = spawn(argv[0], argv.slice(1),\n            // Share the stderr handle to have error output come\n            // straight through. Only supported in v0.8+.\n            {stdio: ['pipe', 'pipe', process.stderr]});\n        dtrace.on('error', function (e) {\n            if (e.syscall === 'spawn' && e.errno === 'ENOENT') {\n                console.error('bunyan: error: could not spawn \"dtrace\" ' +\n                    '(\"bunyan -p\" is only supported on platforms with dtrace)');\n            } else {\n                console.error('bunyan: error: unexpected dtrace error: %s', e);\n            }\n            callback(1);\n        })\n        child = dtrace; // intentionally global\n\n        function finish(code) {\n            if (leftover) {\n                handleLogLine(null, leftover, opts, stylize);\n                leftover = '';\n            }\n            callback(code);\n        }\n\n        dtrace.stdout.setEncoding('utf8');\n        dtrace.stdout.on('data', function (chunk) {\n            var lines = chunk.split(/\\r\\n|\\n/);\n            var length = lines.length;\n            if (length === 1) {\n                leftover += lines[0];\n                return;\n            }\n            if (length > 1) {\n                handleLogLine(null, leftover + lines[0], opts, stylize);\n            }\n            leftover = lines.pop();\n            length -= 1;\n            for (var i = 1; i < length; i++) {\n                handleLogLine(null, lines[i], opts, stylize);\n            }\n        });\n\n        if (nodeSpawnSupportsStdio) {\n            dtrace.on('exit', finish);\n        } else {\n            // Fallback (for < v0.8) to pipe the dtrace process' stderr to\n            // this stderr. Wait for all of (1) process 'exit', (2) stderr\n            // 'end', and (2) stdout 'end' before returning to ensure all\n            // stderr is flushed (issue #54).\n            var returnCode = null;\n            var eventsRemaining = 3;\n            function countdownToFinish(code) {\n                returnCode = code;\n                eventsRemaining--;\n                if (eventsRemaining == 0) {\n                    finish(returnCode);\n                }\n            }\n            dtrace.stderr.pipe(process.stderr);\n            dtrace.stderr.on('end', countdownToFinish);\n            dtrace.stderr.on('end', countdownToFinish);\n            dtrace.on('exit', countdownToFinish);\n        }\n    });\n}\n\n\n/**\n * Process all input from the given log file.\n *\n * @param file {String} Log file path to process.\n * @params opts {Object} Bunyan options object.\n * @param stylize {Function} Output stylize function to use.\n * @param callback {Function} `function ()`\n */\nfunction processFile(file, opts, stylize, callback) {\n    var stream = fs.createReadStream(file);\n    if (/\\.gz$/.test(file)) {\n        stream = stream.pipe(require('zlib').createGunzip());\n    }\n    // Manually decode streams - lazy load here as per node/lib/fs.js\n    var decoder = new (require('string_decoder').StringDecoder)('utf8');\n\n    streams[file].stream = stream;\n\n    stream.on('error', function (err) {\n        streams[file].done = true;\n        callback(err);\n    });\n\n    var leftover = '';  // Left-over partial line from last chunk.\n    stream.on('data', function (data) {\n        if (exiting) {\n            _selfTrace('stop reading file \"%s\" because exiting', file);\n            stream.destroy();\n            return;\n        }\n\n        var chunk = decoder.write(data);\n        if (!chunk.length) {\n            return;\n        }\n        var lines = chunk.split(/\\r\\n|\\n/);\n        var length = lines.length;\n        if (length === 1) {\n            leftover += lines[0];\n            return;\n        }\n\n        if (length > 1) {\n            handleLogLine(file, leftover + lines[0], opts, stylize);\n        }\n        leftover = lines.pop();\n        length -= 1;\n        for (var i = 1; i < length; i++) {\n            handleLogLine(file, lines[i], opts, stylize);\n        }\n    });\n\n    stream.on('end', function () {\n        streams[file].done = true;\n        if (leftover) {\n            handleLogLine(file, leftover, opts, stylize);\n            leftover = '';\n        } else {\n            emitNextRecord(opts, stylize);\n        }\n        callback();\n    });\n}\n\n\n/**\n * From node async module.\n */\n/* BEGIN JSSTYLED */\nfunction asyncForEach(arr, iterator, callback) {\n    callback = callback || function () {};\n    if (!arr.length) {\n        return callback();\n    }\n    var completed = 0;\n    arr.forEach(function (x) {\n        iterator(x, function (err) {\n            if (err) {\n                callback(err);\n                callback = function () {};\n            }\n            else {\n                completed += 1;\n                if (completed === arr.length) {\n                    callback();\n                }\n            }\n        });\n    });\n};\n/* END JSSTYLED */\n\n\n\n/**\n * Cleanup and exit properly.\n *\n * Warning: this doesn't necessarily stop processing, i.e. process exit\n * might be delayed. It is up to the caller to ensure that no subsequent\n * bunyan processing is done after calling this.\n *\n * @param code {Number} exit code.\n * @param signal {String} Optional signal name, if this was exitting because\n *    of a signal.\n */\nfunction cleanupAndExit(code, signal) {\n    // Guard one call.\n    if (exiting) {\n        return;\n    }\n    exiting = true;\n    _selfTrace('cleanupAndExit(%s, %s)', code, signal);\n\n    // Clear possibly interrupted ANSI code (issue #59).\n    if (usingAnsiCodes) {\n        stdout.write('\\033[0m');\n    }\n\n    // Kill possible dtrace child.\n    if (child) {\n        child.kill(signal);\n    }\n\n    if (pager) {\n        // Let pager know that output is done, then wait for pager to exit.\n        pager.removeListener('exit', onPrematurePagerExit);\n        pager.on('exit', function onPagerExit(pagerCode) {\n            _selfTrace('pager exit -> process.exit(%s)', pagerCode || code);\n            process.exit(pagerCode || code);\n        });\n        stdout.end();\n    } else if (code) {\n        // Non-zero exit: Something is wrong. We are very likely still\n        // processing log records -- i.e. we have open handles -- so we need\n        // a hard stop (aka `process.exit`).\n        _selfTrace('process.exit(%s)', code);\n        process.exit(code);\n    } else {\n        // Zero exit: This should be a \"normal\" exit, for which we want to\n        // flush stdout/stderr.\n        exeunt.softExit(code);\n    }\n}\n\n\n\n//---- mainline\n\nprocess.on('SIGINT', function () { cleanupAndExit(1, 'SIGINT'); });\nprocess.on('SIGQUIT', function () { cleanupAndExit(1, 'SIGQUIT'); });\nprocess.on('SIGTERM', function () { cleanupAndExit(1, 'SIGTERM'); });\nprocess.on('SIGHUP', function () { cleanupAndExit(1, 'SIGHUP'); });\n\nprocess.on('uncaughtException', function (err) {\n    function _indent(s) {\n        var lines = s.split(/\\r?\\n/);\n        for (var i = 0; i < lines.length; i++) {\n            lines[i] = '*     ' + lines[i];\n        }\n        return lines.join('\\n');\n    }\n\n    var title = encodeURIComponent(format(\n        'Bunyan %s crashed: %s', getVersion(), String(err)));\n    var e = console.error;\n    e('```');\n    e('* The Bunyan CLI crashed!');\n    e('*');\n    if (err.name === 'ReferenceError' && gUsingConditionOpts) {\n        /* BEGIN JSSTYLED */\n        e('* This crash was due to a \"ReferenceError\", which is often the result of given');\n        e('* `-c CONDITION` code that doesn\\'t guard against undefined values. If that is');\n        /* END JSSTYLED */\n        e('* not the problem:');\n        e('*');\n    }\n    e('* Please report this issue and include the details below:');\n    e('*');\n    e('*    https://github.com/trentm/node-bunyan/issues/new?title=%s', title);\n    e('*');\n    e('* * *');\n    e('* platform:', process.platform);\n    e('* node version:', process.version);\n    e('* bunyan version:', getVersion());\n    e('* argv: %j', process.argv);\n    e('* log line: %j', currLine);\n    e('* stack:');\n    e(_indent(err.stack));\n    e('```');\n    process.exit(1);\n});\n\n\n// Early termination of the pager: just stop.\nfunction onPrematurePagerExit(pagerCode) {\n    _selfTrace('premature pager exit');\n    // 'pager' and 'stdout' are intentionally global.\n    pager = null;\n    stdout.end()\n    stdout = process.stdout;\n    cleanupAndExit(pagerCode);\n}\n\n\nfunction main(argv) {\n    try {\n        var opts = parseArgv(argv);\n    } catch (e) {\n        warn('bunyan: error: %s', e.message);\n        cleanupAndExit(1);\n        return;\n    }\n    if (opts.help) {\n        printHelp();\n        return;\n    }\n    if (opts.version) {\n        console.log('bunyan ' + getVersion());\n        return;\n    }\n    if (opts.pids && opts.args.length > 0) {\n        warn('bunyan: error: can\\'t use both \"-p PID\" (%s) and file (%s) args',\n            opts.pids, opts.args.join(' '));\n        cleanupAndExit(1);\n        return;\n    }\n    if (opts.color === null) {\n        if (process.env.BUNYAN_NO_COLOR &&\n                process.env.BUNYAN_NO_COLOR.length > 0) {\n            opts.color = false;\n        } else {\n            opts.color = process.stdout.isTTY;\n        }\n    }\n    usingAnsiCodes = opts.color; // intentionally global\n    var stylize = (opts.color ? stylizeWithColor : stylizeWithoutColor);\n\n    // Pager.\n    var paginate = (\n        process.stdout.isTTY &&\n        process.stdin.isTTY &&\n        !opts.pids && // Don't page if following process output.\n        opts.args.length > 0 && // Don't page if no file args to process.\n        process.platform !== 'win32' &&\n        (nodeVer[0] > 0 || nodeVer[1] >= 8) &&\n        (opts.paginate === true ||\n            (opts.paginate !== false &&\n                (!process.env.BUNYAN_NO_PAGER ||\n                    process.env.BUNYAN_NO_PAGER.length === 0))));\n    if (paginate) {\n        var pagerCmd = process.env.PAGER || 'less';\n        /* JSSTYLED */\n        assert.ok(pagerCmd.indexOf('\"') === -1 && pagerCmd.indexOf(\"'\") === -1,\n            'cannot parse PAGER quotes yet');\n        var argv = pagerCmd.split(/\\s+/g);\n        var env = objCopy(process.env);\n        if (env.LESS === undefined) {\n            // git's default is LESS=FRSX. I don't like the 'S' here because\n            // lines are *typically* wide with bunyan output and scrolling\n            // horizontally is a royal pain. Note a bug in Mac's `less -F`,\n            // such that SIGWINCH can kill it. If that rears too much then\n            // I'll remove 'F' from here.\n            env.LESS = 'FRX';\n        }\n        _selfTrace('pager: argv=%j, env.LESS=%j', argv, env.LESS);\n        // `pager` and `stdout` intentionally global.\n        pager = spawn(argv[0], argv.slice(1),\n            // Share the stderr handle to have error output come\n            // straight through. Only supported in v0.8+.\n            {env: env, stdio: ['pipe', 1, 2]});\n        stdout = pager.stdin;\n        pager.on('exit', onPrematurePagerExit);\n    }\n\n    // Stdout error handling. (Couldn't setup until `stdout` was determined.)\n    stdout.on('error', function (err) {\n        _selfTrace('stdout error event: %s, exiting=%s', err, exiting);\n        if (exiting) {\n            return;\n        } else if (err.code === 'EPIPE') {\n            cleanupAndExit(0);\n        } else {\n            warn('bunyan: error on output stream: %s', err);\n            cleanupAndExit(1);\n        }\n    });\n\n    var retval = 0;\n    if (opts.pids) {\n        processPids(opts, stylize, function (code) {\n            cleanupAndExit(code);\n        });\n    } else if (opts.args.length > 0) {\n        var files = opts.args;\n        files.forEach(function (file) {\n            streams[file] = { stream: null, records: [], done: false }\n        });\n        asyncForEach(files,\n            function (file, next) {\n                processFile(file, opts, stylize, function (err) {\n                    if (err) {\n                        warn('bunyan: %s', err.message);\n                        retval += 1;\n                    }\n                    next();\n                });\n            },\n            function (err) {\n                if (err) {\n                    warn('bunyan: unexpected error: %s', err.stack || err);\n                    cleanupAndExit(1);\n                } else {\n                    cleanupAndExit(retval);\n                }\n            }\n        );\n    } else {\n        processStdin(opts, stylize, function () {\n            cleanupAndExit(retval);\n        });\n    }\n}\n\nif (require.main === module) {\n    main(process.argv);\n}\n"
  },
  {
    "path": "docs/bunyan.1",
    "content": ".\\\" generated with Ronn/v0.7.3\n.\\\" http://github.com/rtomayko/ronn/tree/0.7.3\n.\n.TH \"BUNYAN\" \"1\" \"January 2015\" \"\" \"bunyan manual\"\n.\n.SH \"NAME\"\n\\fBbunyan\\fR \\- filter and pretty\\-print Bunyan log file content\n.\n.SH \"SYNOPSIS\"\n\\fBbunyan\\fR [OPTIONS]\n.\n.P\n\\&\\.\\.\\. | \\fBbunyan\\fR [OPTIONS]\n.\n.P\n\\fBbunyan\\fR [OPTIONS] \\-p PID\n.\n.SH \"DESCRIPTION\"\n\"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\\.\n.\n.SS \"Pretty\\-printing\"\nA 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:\n.\n.IP \"\" 4\n.\n.nf\n\n$ bunyan foo\\.log          # or `cat foo\\.log | bunyan\n[2012\\-02\\-08T22:56:52\\.856Z]  INFO: myservice/123 on example\\.com: My message\n    extra: multi\n    line\n[2012\\-02\\-08T22:56:54\\.856Z] ERROR: myservice/123 on example\\.com: My message\n\\.\\.\\.\n.\n.fi\n.\n.IP \"\" 0\n.\n.P\nBy default the \"long\" output format is used\\. Use the \\fB\\-o FORMAT\\fR option to emit other formats\\. E\\.g\\.:\n.\n.IP \"\" 4\n.\n.nf\n\n$ bunyan foo\\.log \\-o short\n22:56:52\\.856Z  INFO myservice: My message\n    extra: multi\n    line\n22:56:54\\.856Z ERROR myservice: My message\n\\.\\.\\.\n.\n.fi\n.\n.IP \"\" 0\n.\n.P\nThese will color the output if supported in your terminal\\. See \"OUTPUT FORMATS\" below\\.\n.\n.SS \"Filtering\"\nThe \\fBbunyan\\fR CLI can also be used to filter a bunyan log\\. Use \\fB\\-l LEVEL\\fR to filter by level:\n.\n.IP \"\" 4\n.\n.nf\n\n$ bunyan foo\\.log \\-l error       # show only \\'error\\' level records\n[2012\\-02\\-08T22:56:54\\.856Z] ERROR: myservice/123 on example\\.com: My message\n.\n.fi\n.\n.IP \"\" 0\n.\n.P\nUse \\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:\n.\n.IP \"\" 4\n.\n.nf\n\n$ bunyan foo\\.log \\-c `this\\.three`     # show records with the \\'extra\\' field\n[2012\\-02\\-08T22:56:52\\.856Z]  INFO: myservice/123 on example\\.com: My message\n    extra: multi\n    line\n.\n.fi\n.\n.IP \"\" 0\n.\n.SH \"OPTIONS\"\n.\n.TP\n\\fB\\-h\\fR, \\fB\\-\\-help\\fR\nPrint this help info and exit\\.\n.\n.TP\n\\fB\\-\\-version\\fR\nPrint version of this command and exit\\.\n.\n.TP\n\\fB\\-q\\fR, \\fB\\-\\-quiet\\fR\nDon\\'t warn if input isn\\'t valid JSON\\.\n.\n.P\nDtrace options (only on dtrace\\-supporting platforms):\n.\n.TP\n\\fB\\-p PID\\fR, \\fB\\-p NAME\\fR\nProcess 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\\'\\.\n.\n.P\nFiltering options:\n.\n.TP\n\\fB\\-l\\fR, \\fB\\-\\-level LEVEL\\fR\nOnly show messages at or above the specified level\\. You can specify level \\fInames\\fR or numeric values\\. (See \\'Log Levels\\' below\\.)\n.\n.TP\n\\fB\\-c COND\\fR, \\fB\\-\\-condition COND\\fR\nRun each log message through the condition and only show those that resolve to a truish value\\. E\\.g\\. \\fB\\-c \\'this\\.pid == 123\\'\\fR\\.\n.\n.TP\n\\fB\\-\\-strict\\fR\nSuppress all but legal Bunyan JSON log lines\\. By default non\\-JSON, and non\\-Bunyan lines are passed through\\.\n.\n.P\nOutput options:\n.\n.TP\n\\fB\\-\\-color\\fR\nColorize output\\. Defaults to try if output stream is a TTY\\.\n.\n.TP\n\\fB\\-\\-no\\-color\\fR\nForce no coloring (e\\.g\\. terminal doesn\\'t support it)\n.\n.TP\n\\fB\\-o FORMAT\\fR, \\fB\\-\\-output FORMAT\\fR\nSpecify 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\\.\n.\n.TP\n\\fB\\-j\\fR\nShortcut for \\fB\\-o json\\fR\\.\n.\n.TP\n\\fB\\-L\\fR, \\fB\\-\\-time local\\fR\nDisplay the time field in \\fIlocal\\fR time, rather than the default UTC time\\.\n.\n.SH \"LOG LEVELS\"\nIn 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\\.\n.\n.IP \"\" 4\n.\n.nf\n\nLevel Name      Level Number    Symbol in COND Scripts\ntrace           10              TRACE\ndebug           20              DEBUG\ninfo            30              INFO\nwarn            40              WARN\nerror           50              ERROR\nfatal           60              FATAL\n.\n.fi\n.\n.IP \"\" 0\n.\n.SH \"OUTPUT FORMATS\"\n.\n.nf\n\nFORMAT NAME         DESCRIPTION\nlong (default)      The default output\\. Long form\\. Colored and \"pretty\"\\.\n                    \\'req\\' and \\'res\\' and \\'err\\' fields are rendered specially\n                    as an HTTP request, HTTP response and exception\n                    stack trace, respectively\\. For backward compat, the\n                    name \"paul\" also works for this\\.\nshort               Like the default output, but more concise\\. Some\n                    typically redundant fields are ellided\\.\njson                JSON output, 2\\-space indentation\\.\njson\\-N              JSON output, N\\-space indentation, e\\.g\\. \"json\\-4\"\nbunyan              Alias for \"json\\-0\", the Bunyan \"native\" format\\.\ninspect             Node\\.js `util\\.inspect` output\\.\n.\n.fi\n.\n.SH \"DTRACE SUPPORT\"\nOn 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:\n.\n.IP \"\" 4\n.\n.nf\n\nlog\\-trace\nlog\\-debug\nlog\\-info\nlog\\-warn\nlog\\-error\nlog\\-fatal\n.\n.fi\n.\n.IP \"\" 0\n.\n.P\nEach 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\\.\n.\n.P\nSee \\fIhttps://github\\.com/trentm/node\\-bunyan#dtrace\\-support\\fR for more details and the \\'\\-p PID\\' option above for convenience usage\\.\n.\n.SH \"ENVIRONMENT\"\n.\n.TP\n\\fBBUNYAN_NO_COLOR\\fR\nSet to a non\\-empty value to force no output coloring\\. See \\'\\-\\-no\\-color\\'\\.\n.\n.SH \"PROJECT & BUGS\"\n\\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\"\\.\n.\n.IP \"\\(bu\" 4\nREADME, Install notes: \\fIhttps://github\\.com/trentm/node\\-bunyan#readme\\fR\n.\n.IP \"\\(bu\" 4\nReport bugs to \\fIhttps://github\\.com/trentm/node\\-bunyan/issues\\fR\\.\n.\n.IP \"\\(bu\" 4\nSee the full changelog at: \\fIhttps://github\\.com/trentm/node\\-bunyan/blob/master/CHANGES\\.md\\fR\n.\n.IP \"\" 0\n.\n.SH \"LICENSE\"\nMIT License (see \\fIhttps://github\\.com/trentm/node\\-bunyan/blob/master/LICENSE\\.txt\\fR)\n.\n.SH \"COPYRIGHT\"\nnode\\-bunyan is Copyright (c) 2012 Joyent, Inc\\. Copyright (c) 2012 Trent Mick\\. All rights reserved\\.\n"
  },
  {
    "path": "docs/bunyan.1.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <meta http-equiv='content-type' value='text/html;charset=utf8'>\n  <meta name='generator' value='Ronn/v0.7.3 (http://github.com/rtomayko/ronn/tree/0.7.3)'>\n  <title>bunyan(1) - filter and pretty-print Bunyan log file content</title>\n  <style type='text/css' media='all'>\n  /* style: man */\n  body#manpage {margin:0}\n  .mp {max-width:100ex;padding:0 9ex 1ex 4ex}\n  .mp p,.mp pre,.mp ul,.mp ol,.mp dl {margin:0 0 20px 0}\n  .mp h2 {margin:10px 0 0 0}\n  .mp > p,.mp > pre,.mp > ul,.mp > ol,.mp > dl {margin-left:8ex}\n  .mp h3 {margin:0 0 0 4ex}\n  .mp dt {margin:0;clear:left}\n  \n  .mp dd {margin:0 0 0 9ex}\n  .mp h1,.mp h2,.mp h3,.mp h4 {clear:left}\n  .mp pre {margin-bottom:20px}\n  .mp pre+h2,.mp pre+h3 {margin-top:22px}\n  .mp h2+pre,.mp h3+pre {margin-top:5px}\n  .mp img {display:block;margin:auto}\n  .mp h1.man-title {display:none}\n  .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}\n  .mp h2 {font-size:16px;line-height:1.25}\n  .mp h1 {font-size:20px;line-height:2}\n  .mp {text-align:justify;background:#fff}\n  .mp,.mp code,.mp pre,.mp pre code,.mp tt,.mp kbd,.mp samp {color:#131211}\n  .mp h1,.mp h2,.mp h3,.mp h4 {color:#030201}\n  .mp u {text-decoration:underline}\n  .mp code,.mp strong,.mp b {font-weight:bold;color:#131211}\n  .mp em,.mp var {font-style:italic;color:#232221;text-decoration:none}\n  .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}\n  .mp b.man-ref {font-weight:normal;color:#434241}\n  .mp pre {padding:0 4ex}\n  .mp pre code {font-weight:normal;color:#434241}\n  .mp h2+pre,h3+pre {padding-left:0}\n  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}\n  ol.man-decor {width:100%}\n  ol.man-decor li.tl {text-align:left}\n  ol.man-decor li.tc {text-align:center;letter-spacing:4px}\n  ol.man-decor li.tr {text-align:right;float:right}\n  </style>\n  <style type='text/css' media='all'>\n  /* style: toc */\n  .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}\n  .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}\n  .man-navigation a:hover {color:#111;text-decoration:underline}\n  </style>\n</head>\n<!--\n  The following styles are deprecated and will be removed at some point:\n  div#man, div#man ol.man, div#man ol.head, div#man ol.man.\n\n  The .man-page, .man-decor, .man-head, .man-foot, .man-title, and\n  .man-navigation should be used instead.\n-->\n<body id='manpage'>\n  <div class='mp' id='man'>\n\n  <div class='man-navigation' style='display:none'>\n    <a href=\"#NAME\">NAME</a>\n    <a href=\"#SYNOPSIS\">SYNOPSIS</a>\n    <a href=\"#DESCRIPTION\">DESCRIPTION</a>\n    <a href=\"#OPTIONS\">OPTIONS</a>\n    <a href=\"#LOG-LEVELS\">LOG LEVELS</a>\n    <a href=\"#OUTPUT-FORMATS\">OUTPUT FORMATS</a>\n    <a href=\"#DTRACE-SUPPORT\">DTRACE SUPPORT</a>\n    <a href=\"#ENVIRONMENT\">ENVIRONMENT</a>\n    <a href=\"#PROJECT-BUGS\">PROJECT &amp; BUGS</a>\n    <a href=\"#LICENSE\">LICENSE</a>\n    <a href=\"#COPYRIGHT\">COPYRIGHT</a>\n  </div>\n\n  <ol class='man-decor man-head man head'>\n    <li class='tl'>bunyan(1)</li>\n    <li class='tc'>bunyan manual</li>\n    <li class='tr'>bunyan(1)</li>\n  </ol>\n\n  <h2 id=\"NAME\">NAME</h2>\n<p class=\"man-name\">\n  <code>bunyan</code> - <span class=\"man-whatis\">filter and pretty-print Bunyan log file content</span>\n</p>\n\n<h2 id=\"SYNOPSIS\">SYNOPSIS</h2>\n\n<p><code>bunyan</code> [OPTIONS]</p>\n\n<p>... | <code>bunyan</code> [OPTIONS]</p>\n\n<p><code>bunyan</code> [OPTIONS] -p PID</p>\n\n<h2 id=\"DESCRIPTION\">DESCRIPTION</h2>\n\n<p>\"Bunyan\" is <strong>a simple and fast a JSON logging library</strong> for node.js services,\na one-JSON-object-per-line log format, and <strong>a <code>bunyan</code> CLI tool</strong> for nicely\nviewing those logs. This man page describes the latter.</p>\n\n<h3 id=\"Pretty-printing\">Pretty-printing</h3>\n\n<p>A bunyan log file is a stream of JSON objects, optionally interspersed with\nnon-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,\nfor example:</p>\n\n<pre><code>$ bunyan foo.log          # or `cat foo.log | bunyan\n[2012-02-08T22:56:52.856Z]  INFO: myservice/123 on example.com: My message\n    extra: multi\n    line\n[2012-02-08T22:56:54.856Z] ERROR: myservice/123 on example.com: My message\n...\n</code></pre>\n\n<p>By default the \"long\" output format is used. Use the <code>-o FORMAT</code> option to\nemit other formats. E.g.:</p>\n\n<pre><code>$ bunyan foo.log -o short\n22:56:52.856Z  INFO myservice: My message\n    extra: multi\n    line\n22:56:54.856Z ERROR myservice: My message\n...\n</code></pre>\n\n<p>These will color the output if supported in your terminal.\nSee \"OUTPUT FORMATS\" below.</p>\n\n<h3 id=\"Filtering\">Filtering</h3>\n\n<p>The <code>bunyan</code> CLI can also be used to filter a bunyan log. Use <code>-l LEVEL</code>\nto filter by level:</p>\n\n<pre><code>$ bunyan foo.log -l error       # show only 'error' level records\n[2012-02-08T22:56:54.856Z] ERROR: myservice/123 on example.com: My message\n</code></pre>\n\n<p>Use <code>-c COND</code> to filter on a JavaScript expression returning true on the\nrecord data. In the COND code, <code>this</code> refers to the record object:</p>\n\n<pre><code>$ bunyan foo.log -c `this.three`     # show records with the 'extra' field\n[2012-02-08T22:56:52.856Z]  INFO: myservice/123 on example.com: My message\n    extra: multi\n    line\n</code></pre>\n\n<h2 id=\"OPTIONS\">OPTIONS</h2>\n\n<dl>\n<dt><code>-h</code>, <code>--help</code></dt><dd><p>Print this help info and exit.</p></dd>\n<dt><code>--version</code></dt><dd><p>Print version of this command and exit.</p></dd>\n<dt><code>-q</code>, <code>--quiet</code></dt><dd><p>Don't warn if input isn't valid JSON.</p></dd>\n</dl>\n\n\n<p>Dtrace options (only on dtrace-supporting platforms):</p>\n\n<dl>\n<dt><code>-p PID</code>, <code>-p NAME</code></dt><dd>Process bunyan:log-* probes from the process with the given PID.\nCan be used multiple times, or specify all processes with '*',\nor a set of processes whose command &amp; args match a pattern with\n'-p NAME'.</dd>\n</dl>\n\n\n<p>Filtering options:</p>\n\n<dl>\n<dt><code>-l</code>, <code>--level LEVEL</code></dt><dd><p>Only show messages at or above the specified level. You can specify level\n<em>names</em> or numeric values. (See 'Log Levels' below.)</p></dd>\n<dt><code>-c COND</code>, <code>--condition COND</code></dt><dd><p>Run each log message through the condition and only show those that\nresolve to a truish value. E.g. <code>-c 'this.pid == 123'</code>.</p></dd>\n<dt><code>--strict</code></dt><dd><p>Suppress all but legal Bunyan JSON log lines. By default non-JSON, and\nnon-Bunyan lines are passed through.</p></dd>\n</dl>\n\n\n<p>Output options:</p>\n\n<dl>\n<dt class=\"flush\"><code>--color</code></dt><dd><p>Colorize output. Defaults to try if output stream is a TTY.</p></dd>\n<dt><code>--no-color</code></dt><dd><p>Force no coloring (e.g. terminal doesn't support it)</p></dd>\n<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>,\n<code>json-N</code>, <code>bunyan</code> (the native bunyan 0-indent JSON output) or <code>inspect</code>.</p></dd>\n<dt class=\"flush\"><code>-j</code></dt><dd><p>Shortcut for <code>-o json</code>.</p></dd>\n<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\ntime.</p></dd>\n</dl>\n\n\n<h2 id=\"LOG-LEVELS\">LOG LEVELS</h2>\n\n<p>In Bunyan log records, then <code>level</code> field is a number. For the <code>-l|--level</code>\nargument the level <strong>names</strong> are supported as shortcuts. In <code>-c|--condition</code>\nscripts, uppercase symbols like \"DEBUG\" are defined for convenience.</p>\n\n<pre><code>Level Name      Level Number    Symbol in COND Scripts\ntrace           10              TRACE\ndebug           20              DEBUG\ninfo            30              INFO\nwarn            40              WARN\nerror           50              ERROR\nfatal           60              FATAL\n</code></pre>\n\n<h2 id=\"OUTPUT-FORMATS\">OUTPUT FORMATS</h2>\n\n<pre><code>FORMAT NAME         DESCRIPTION\nlong (default)      The default output. Long form. Colored and \"pretty\".\n                    'req' and 'res' and 'err' fields are rendered specially\n                    as an HTTP request, HTTP response and exception\n                    stack trace, respectively. For backward compat, the\n                    name \"paul\" also works for this.\nshort               Like the default output, but more concise. Some\n                    typically redundant fields are ellided.\njson                JSON output, 2-space indentation.\njson-N              JSON output, N-space indentation, e.g. \"json-4\"\nbunyan              Alias for \"json-0\", the Bunyan \"native\" format.\ninspect             Node.js `util.inspect` output.\n</code></pre>\n\n<h2 id=\"DTRACE-SUPPORT\">DTRACE SUPPORT</h2>\n\n<p>On systems that support DTrace (e.g., MacOS, FreeBSD, illumos derivatives\nlike SmartOS and OmniOS), Bunyan will create a DTrace provider (<code>bunyan</code>)\nthat makes available the following probes:</p>\n\n<pre><code>log-trace\nlog-debug\nlog-info\nlog-warn\nlog-error\nlog-fatal\n</code></pre>\n\n<p>Each of these probes has a single argument: the string that would be\nwritten to the log.  Note that when a probe is enabled, it will\nfire whenever the corresponding function is called, even if the level of\nthe log message is less than that of any stream.</p>\n\n<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\nand the '-p PID' option above for convenience usage.</p>\n\n<h2 id=\"ENVIRONMENT\">ENVIRONMENT</h2>\n\n<dl>\n<dt><code>BUNYAN_NO_COLOR</code></dt><dd>Set to a non-empty value to force no output coloring. See '--no-color'.</dd>\n</dl>\n\n\n<h2 id=\"PROJECT-BUGS\">PROJECT &amp; BUGS</h2>\n\n<p><code>bunyan</code> is written in JavaScript and requires node.js (<code>node</code>). The project\nlives 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\n\"bunyan\".</p>\n\n<ul>\n<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>\n<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>\n<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>\n</ul>\n\n\n<h2 id=\"LICENSE\">LICENSE</h2>\n\n<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>\n\n<h2 id=\"COPYRIGHT\">COPYRIGHT</h2>\n\n<p>node-bunyan is Copyright (c) 2012 Joyent, Inc. Copyright (c) 2012 Trent Mick.\nAll rights reserved.</p>\n\n\n  <ol class='man-decor man-foot man foot'>\n    <li class='tl'></li>\n    <li class='tc'>January 2015</li>\n    <li class='tr'>bunyan(1)</li>\n  </ol>\n\n  </div>\n<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>\n</html>\n"
  },
  {
    "path": "docs/bunyan.1.ronn",
    "content": "# bunyan(1) -- filter and pretty-print Bunyan log file content\n\n\n## SYNOPSIS\n\n`bunyan` \\[OPTIONS\\]\n\n... | `bunyan` \\[OPTIONS\\]\n\n`bunyan` \\[OPTIONS\\] -p PID\n\n\n## DESCRIPTION\n\n\"Bunyan\" is **a simple and fast a JSON logging library** for node.js services,\na one-JSON-object-per-line log format, and **a `bunyan` CLI tool** for nicely\nviewing those logs. This man page describes the latter.\n\n\n### Pretty-printing\n\nA bunyan log file is a stream of JSON objects, optionally interspersed with\nnon-JSON log lines. The primary usage of bunyan(1) is to pretty print,\nfor example:\n\n    $ bunyan foo.log          # or `cat foo.log | bunyan\n    [2012-02-08T22:56:52.856Z]  INFO: myservice/123 on example.com: My message\n        extra: multi\n        line\n    [2012-02-08T22:56:54.856Z] ERROR: myservice/123 on example.com: My message\n    ...\n\nBy default the \"long\" output format is used. Use the `-o FORMAT` option to\nemit other formats. E.g.:\n\n    $ bunyan foo.log -o short\n    22:56:52.856Z  INFO myservice: My message\n        extra: multi\n        line\n    22:56:54.856Z ERROR myservice: My message\n    ...\n\nThese will color the output if supported in your terminal.\nSee \"OUTPUT FORMATS\" below.\n\n\n### Filtering\n\nThe `bunyan` CLI can also be used to filter a bunyan log. Use `-l LEVEL`\nto filter by level:\n\n    $ bunyan foo.log -l error       # show only 'error' level records\n    [2012-02-08T22:56:54.856Z] ERROR: myservice/123 on example.com: My message\n\nUse `-c COND` to filter on a JavaScript expression returning true on the\nrecord data. In the COND code, `this` refers to the record object:\n\n    $ bunyan foo.log -c `this.three`     # show records with the 'extra' field\n    [2012-02-08T22:56:52.856Z]  INFO: myservice/123 on example.com: My message\n        extra: multi\n        line\n\n\n## OPTIONS\n\n  * `-h`, `--help`:\n    Print this help info and exit.\n\n  * `--version`:\n    Print version of this command and exit.\n\n  * `-q`, `--quiet`:\n    Don't warn if input isn't valid JSON.\n\nDtrace options (only on dtrace-supporting platforms):\n\n  * `-p PID`, `-p NAME`:\n    Process bunyan:log-\\* probes from the process with the given PID.\n    Can be used multiple times, or specify all processes with '\\*',\n    or a set of processes whose command & args match a pattern with\n    '-p NAME'.\n\nFiltering options:\n\n  * `-l`, `--level LEVEL`:\n    Only show messages at or above the specified level. You can specify level\n    *names* or numeric values. (See 'Log Levels' below.)\n\n  * `-c COND`, `--condition COND`:\n    Run each log message through the condition and only show those that\n    resolve to a truish value. E.g. `-c 'this.pid == 123'`.\n\n  * `--strict`:\n    Suppress all but legal Bunyan JSON log lines. By default non-JSON, and\n    non-Bunyan lines are passed through.\n\nOutput options:\n\n  * `--color`:\n    Colorize output. Defaults to try if output stream is a TTY.\n\n  * `--no-color`:\n    Force no coloring (e.g. terminal doesn't support it)\n\n  * `-o FORMAT`, `--output FORMAT`:\n    Specify an output format. One of `long` (the default), `short`, `json`,\n    `json-N`, `bunyan` (the native bunyan 0-indent JSON output) or `inspect`.\n\n  * `-j`:\n    Shortcut for `-o json`.\n\n  * `-L`, `--time local`:\n    Display the time field in *local* time, rather than the default UTC\n    time.\n\n\n## LOG LEVELS\n\nIn Bunyan log records, then `level` field is a number. For the `-l|--level`\nargument the level **names** are supported as shortcuts. In `-c|--condition`\nscripts, uppercase symbols like \"DEBUG\" are defined for convenience.\n\n    Level Name      Level Number    Symbol in COND Scripts\n    trace           10              TRACE\n    debug           20              DEBUG\n    info            30              INFO\n    warn            40              WARN\n    error           50              ERROR\n    fatal           60              FATAL\n\n\n## OUTPUT FORMATS\n\n    FORMAT NAME         DESCRIPTION\n    long (default)      The default output. Long form. Colored and \"pretty\".\n                        'req' and 'res' and 'err' fields are rendered specially\n                        as an HTTP request, HTTP response and exception\n                        stack trace, respectively. For backward compat, the\n                        name \"paul\" also works for this.\n    short               Like the default output, but more concise. Some\n                        typically redundant fields are ellided.\n    json                JSON output, 2-space indentation.\n    json-N              JSON output, N-space indentation, e.g. \"json-4\"\n    bunyan              Alias for \"json-0\", the Bunyan \"native\" format.\n    inspect             Node.js `util.inspect` output.\n\n\n## DTRACE SUPPORT\n\nOn systems that support DTrace (e.g., MacOS, FreeBSD, illumos derivatives\nlike SmartOS and OmniOS), Bunyan will create a DTrace provider (`bunyan`)\nthat makes available the following probes:\n\n    log-trace\n    log-debug\n    log-info\n    log-warn\n    log-error\n    log-fatal\n\nEach of these probes has a single argument: the string that would be\nwritten to the log.  Note that when a probe is enabled, it will\nfire whenever the corresponding function is called, even if the level of\nthe log message is less than that of any stream.\n\nSee <https://github.com/trentm/node-bunyan#dtrace-support> for more details\nand the '-p PID' option above for convenience usage.\n\n\n## ENVIRONMENT\n\n  * `BUNYAN_NO_COLOR`:\n    Set to a non-empty value to force no output coloring. See '--no-color'.\n\n\n## PROJECT & BUGS\n\n`bunyan` is written in JavaScript and requires node.js (`node`). The project\nlives at <https://github.com/trentm/node-bunyan> and is published to npm as\n\"bunyan\".\n\n* README, Install notes: <https://github.com/trentm/node-bunyan#readme>\n* Report bugs to <https://github.com/trentm/node-bunyan/issues>.\n* See the full changelog at: <https://github.com/trentm/node-bunyan/blob/master/CHANGES.md>\n\n\n## LICENSE\n\nMIT License (see <https://github.com/trentm/node-bunyan/blob/master/LICENSE.txt>)\n\n\n## COPYRIGHT\n\nnode-bunyan is Copyright (c) 2012 Joyent, Inc. Copyright (c) 2012 Trent Mick.\nAll rights reserved.\n"
  },
  {
    "path": "docs/index.html",
    "content": "<a href=\"bunyan.1.html\">bunyan(1) man page</a>\n"
  },
  {
    "path": "examples/err.js",
    "content": "// Example logging an error:\n\nvar http = require('http');\nvar bunyan = require('../lib/bunyan');\nvar util = require('util');\n\nvar log = bunyan.createLogger({\n    name: 'myserver',\n    serializers: {\n        err: bunyan.stdSerializers.err,   // <--- use this\n    }\n});\n\ntry {\n    throw new TypeError('boom');\n} catch (err) {\n    log.warn({err: err}, 'operation went boom: %s', err)   // <--- here\n}\n\nlog.info(new TypeError('how about this?'))  // <--- alternatively this\n\n\ntry {\n    throw 'boom string';\n} catch (err) {\n    log.error(err)\n}\n\n/* BEGIN JSSTYLED */\n/**\n *\n *  $ node err.js  | ../bin/bunyan -j\n *  {\n *    \"name\": \"myserver\",\n *    \"hostname\": \"banana.local\",\n *    \"err\": {\n *      \"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)\",\n *      \"name\": \"TypeError\",\n *      \"message\": \"boom\"\n *    },\n *    \"level\": 4,\n *    \"msg\": \"operation went boom: TypeError: boom\",\n *    \"time\": \"2012-02-02T04:42:53.206Z\",\n *    \"v\": 0\n *  }\n *  $ node err.js  | ../bin/bunyan\n *  [2012-02-02T05:02:39.412Z] WARN: myserver on banana.local: operation went boom: TypeError: boom\n *      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)\n *\n */\n/* END JSSTYLED */\n"
  },
  {
    "path": "examples/handle-fs-error.js",
    "content": "// 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 = require('fs');\nvar path = require('path');\nvar bunyan = require('../lib/bunyan');\n\nvar FILENAME = 'handle-fs-error.log';\nvar S_IWUSR = 00200; // mask for owner write permission in stat mode\n\nconsole.warn('- Log file is \"%s\".', FILENAME);\nif (!path.existsSync(FILENAME)) {\n        console.warn('- Touch log file.');\n        fs.writeFileSync(FILENAME, 'touch\\n');\n}\nif (fs.statSync(FILENAME).mode & S_IWUSR) {\n        console.warn('- Make log file read-only.');\n        fs.chmodSync(FILENAME, 0444);\n}\n\nconsole.warn('- Create logger.');\nvar log = bunyan.createLogger({\n        name: 'handle-fs-error',\n        streams: [\n                  {path: FILENAME}\n        ]\n});\n\nlog.on('error', function (err) {\n        console.warn('- The logger emitted an error:', err);\n});\n\nconsole.warn('- Call log.info(...).');\nlog.info('info log message');\nconsole.warn('- Called log.info(...).');\n\nsetTimeout(function () {\n        console.warn('- Call log.warn(...).');\n        log.warn('warn log message');\n        console.warn('- Called log.warn(...).');\n}, 1000);\n"
  },
  {
    "path": "examples/hi.js",
    "content": "var bunyan = require('../lib/bunyan');\n\n// Basic usage.\nvar log = bunyan.createLogger({name: 'myapp', level: 'info', src: true});\n\n// isInfoEnabled replacement\nconsole.log('log.info() is:', log.info())\n\n// `util.format`-based printf handling\nlog.info('hi');\nlog.info('hi', 'trent');\nlog.info('hi %s there', true);\n\n// First arg as an object adds fields to the log record.\nlog.info({foo:'bar', multiline:'one\\ntwo\\nthree'}, 'hi %d', 1, 'two', 3);\n\n\n// Shows `log.child(...)` to specialize a logger for a sub-component.\nconsole.log('\\n')\n\nfunction Wuzzle(options) {\n    this.log = options.log;\n    this.log.info('creating a wuzzle')\n}\n\nWuzzle.prototype.woos = function () {\n    this.log.warn('This wuzzle is woosey.')\n}\n\nvar wuzzle = new Wuzzle({log: log.child({component: 'wuzzle'})});\nwuzzle.woos();\nlog.info('done with the wuzzle')\n"
  },
  {
    "path": "examples/level.js",
    "content": "// Play with setting levels.\n//\n// TODO: put this in a damn test suite\n\nvar bunyan = require('../lib/bunyan'),\n    DEBUG = bunyan.DEBUG,\n    INFO = bunyan.INFO,\n    WARN = bunyan.WARN;\nvar assert = require('assert');\n\n// Basic usage.\nvar log = bunyan.createLogger({\n    name: 'example-level',\n    streams: [\n        {\n            name: 'stdout',\n            stream: process.stdout,\n            level: 'debug'\n        },\n        {\n            name: 'stderr',\n            stream: process.stderr\n        }\n    ]\n});\n\nassert.equal(log.level(), DEBUG);\nassert.equal(log.levels()[0], DEBUG);\nassert.equal(log.levels()[1], INFO);\nassert.equal(log.levels(0), DEBUG);\nassert.equal(log.levels(1), INFO);\n\nassert.equal(log.levels('stdout'), DEBUG)\ntry {\n    log.levels('foo')\n} catch (e) {\n    assert.ok(e.message.indexOf('name') !== -1)\n}\n\nlog.trace('no one should see this')\nlog.debug('should see this once (on stdout)')\nlog.info('should see this twice')\nlog.levels('stdout', INFO)\nlog.debug('no one should see this either')\nlog.level('trace')\nlog.trace('should see this twice as 4th and 5th emitted log messages')\n"
  },
  {
    "path": "examples/log-undefined-values.js",
    "content": "#!/usr/bin/env node\n/* BEGIN JSSTYLED */\n/**\n * <https://github.com/trentm/node-bunyan/pull/473> is a change to add a\n * feature to Bunyan's log record stringification to log `undefined` values.\n * Let's attempt that with a custom raw stream.\n *\n * Note that a raw stream here isn't ideal, because using a custom raw stream\n * means that it is a pain to use some of the other built-in stream types\n * (file, rotating-file). However, it might be a satisfactory workaround for\n * some.\n *\n * Example:\n *      $ node log-undefined-values.js\n *      {\"name\":\"log-undefined-values\",\"hostname\":\"danger0.local\",\"pid\":28161,\"level\":30,\"anull\":null,\"aundef\":\"[Undefined]\",\"anum\":42,\"astr\":\"foo\",\"msg\":\"hi\",\"time\":\"2017-03-04T20:53:54.331Z\",\"v\":0}\n *      $ node log-undefined-values.js | bunyan\n *      [2017-03-04T20:54:41.874Z]  INFO: log-undefined-values/28194 on danger0.local: hi (anull=null, aundef=[Undefined], anum=42, astr=foo)\n */\n/* END JSSTYLED */\n\nvar bunyan = require('../lib/bunyan');\nvar fs = require('fs');\n\n\nfunction replacer() {\n    // Note: If node > 0.10, then could use Set here (see `safeCyclesSet()`\n    // in bunyan.js) for a performance improvement.\n    var seen = [];\n    return function (key, val) {\n        if (val === undefined) {\n            return '[Undefined]';\n        } else if (!val || typeof (val) !== 'object') {\n            return val;\n        }\n        if (seen.indexOf(val) !== -1) {\n            return '[Circular]';\n        }\n        seen.push(val);\n        return val;\n    };\n}\n\nfunction LogUndefinedValuesStream(stream) {\n    this.stream = stream;\n}\nLogUndefinedValuesStream.prototype.write = function (rec) {\n    var str = JSON.stringify(rec, replacer()) + '\\n';\n    this.stream.write(str);\n}\n\nvar log = bunyan.createLogger({\n    name: 'log-undefined-values',\n    streams: [ {\n        level: 'info',\n        type: 'raw',\n        stream: new LogUndefinedValuesStream(process.stdout)\n    } ]\n});\n\nlog.info({anull: null, aundef: undefined, anum: 42, astr: 'foo'}, 'hi');\n"
  },
  {
    "path": "examples/long-running.js",
    "content": "/*\n * A long-running process that does some periodic logging. Use bunyan with\n * it some of these ways:\n *\n * 1. Direct piping:\n *        node long-running.js | bunyan\n * 2. Logging to file (e.g. if run via a service system like upstart or\n *    illumos' SMF that sends std output to a log file), then tail -f that\n *    log file.\n *        node long-running.js > long-running.log 2>&1\n *        tail -f long-running.log | bunyan\n * 3. Dtrace to watch the logging. This has the bonus of being able to watch\n *    all log levels... even if not normally emitted.\n *        node long-running.js > long-running.log 2>&1\n *        bunyan -p $(head -1 long-running.log | json pid)\n *\n */\n\nvar fs = require('fs');\nvar bunyan = require('../lib/bunyan');\n\n\nfunction randint(n) {\n    return Math.floor(Math.random() * n);\n}\n\nfunction randchoice(array) {\n    return array[randint(array.length)];\n}\n\n\n//---- mainline\n\nvar words = fs.readFileSync(\n    __dirname + '/long-running.js', 'utf8').split(/\\s+/);\nvar levels = ['trace', 'debug', 'info', 'warn', 'error', 'fatal'];\nvar timeout;\n\nvar log = bunyan.createLogger({name: 'lr', level: 'debug'});\n\n// We're logging to stdout. Let's exit gracefully on EPIPE. E.g. if piped\n// to `head` which will close after N lines.\nprocess.stdout.on('error', function (err) {\n    if (err.code === 'EPIPE') {\n        process.exit(0);\n    }\n})\n\nfunction logOne() {\n    var level = randchoice(levels);\n    var msg = [randchoice(words), randchoice(words)].join(' ');\n    var delay = randint(300);\n    //console.warn('long-running about to log.%s(..., \"%s\")', level, msg)\n    log[level]({'word': randchoice(words), 'delay': delay}, msg);\n    timeout = setTimeout(logOne, delay);\n}\n\nlog.info('hi, this is the start');\ntimeout = setTimeout(logOne, 1000);\n"
  },
  {
    "path": "examples/multi.js",
    "content": "var bunyan = require('../lib/bunyan');\nlog = bunyan.createLogger({\n    name: 'amon',\n    streams: [\n        {\n            level: 'info',\n            stream: process.stdout,\n        },\n        {\n            level: 'error',\n            path: 'multi.log'\n        }\n    ]\n});\n\n\nlog.debug('hi nobody on debug');\nlog.info('hi stdout on info');\nlog.error('hi both on error');\nlog.fatal('hi both on fatal');\n"
  },
  {
    "path": "examples/mute-by-envvars-stream.js",
    "content": "/*\n * Example of a MuteByEnvVars Bunyan stream to mute log records matching some\n * envvars. I.e. as a way to do:\n *      https://github.com/trentm/node-bunyan/issues/175\n *      https://github.com/trentm/node-bunyan/pull/176\n * outside of core.\n *\n * Usage:\n *  $ node mute-by-envvars-stream.js\n *  {\"name\":\"mute-by-envvars-stream\",...,\"msg\":\"hi raw stream\"}\n *  {\"name\":\"mute-by-envvars-stream\",...,\"foo\":\"bar\",\"msg\":\"added \\\"foo\\\" key\"}\n *\n *  $ BUNYAN_MUTE_foo=bar node mute-by-envvars-stream.js\n *  {\"name\":\"mute-by-envvars-stream\",...,\"msg\":\"hi raw stream\"}\n *\n * Dev Notes:\n * - This currently treats all 'BUNYAN_MUTE_foo=bar' envvar values as strings.\n *   That might not be desired.\n * - This is a quick implementation: inefficient and not well tested.\n * - Granted that Bunyan streams are hard to compose. For example, using\n *   `MuteByEnvVars` to be a filter before writing logs to a *file* is a pain\n *   for the file open/close handling. It would be nicer if Bunyan had a\n *   pipeline of \"filters\" (more like core node.js streams).\n */\n\nvar bunyan = require('../lib/bunyan');\n\n\nfunction MuteByEnvVars(opts) {\n    opts = opts || {};\n    this.stream = opts.stream || process.stdout;\n\n    var PREFIX = 'BUNYAN_MUTE_';\n\n    // Process the env once.\n    this.mutes = {};\n    for (k in process.env) {\n        if (k.indexOf(PREFIX) === 0) {\n            this.mutes[k.slice(PREFIX.length)] = process.env[k];\n        }\n    }\n}\n\n/**\n * Returns the given object's \"o\" property named by \"s\" using the dot notation.\n *\n * this({ name: { first: \"value\" } }, name.first) == \"value\"\n *\n * This is a verbatin copy of http://stackoverflow.com/a/6491621/433814\n *\n * @param o {object} is an object.\n * @param s (string} is the string in the \"dot\" notation.\n */\nMuteByEnvVars.prototype._objectFromDotNotation = function (o, s) {\n    s = s.replace(/\\[(\\w+)\\]/g, '.$1');  // convert indexes to properties\n    s = s.replace(/^\\./, ''); // strip leading dot\n    var a = s.split('.');\n    while (a.length) {\n        var n = a.shift();\n        if (n in o) {\n            o = o[n];\n        } else {\n            return;\n        }\n    }\n    return o;\n}\n\nMuteByEnvVars.prototype.write = function (rec) {\n    if (typeof (rec) !== 'object') {\n        console.error('error: MuteByEnvVars raw stream got a non-object '\n            + 'record: %j', rec);\n        return;\n    }\n\n    var muteRec = false;\n    var keys = Object.keys(this.mutes);\n    for (var i = 0; i < keys.length; i++) {\n        var k = keys[i];\n        var match = this._objectFromDotNotation(rec, k);\n        if (match === this.mutes[k]) {\n            muteRec = true;\n            break;\n        }\n    }\n    if (!muteRec) {\n        this.stream.write(JSON.stringify(rec) + '\\n');\n    }\n}\n\n\n\n// ---- example usage of the MuteByEnvVars stream\n\nvar log = bunyan.createLogger({\n    name: 'mute-by-envvars-stream',\n    streams: [\n        {\n            level: 'info',\n            stream: new MuteByEnvVars(),\n            type: 'raw'\n        },\n    ]\n});\n\n\nlog.info('hi raw stream');\nlog.info({foo: 'bar'}, 'added \"foo\" key');\n"
  },
  {
    "path": "examples/raw-stream.js",
    "content": "// Example of a \"raw\" stream in a Bunyan Logger. A raw stream is one to\n// which log record *objects* are written instead of the JSON-serialized\n// string.\n\nvar bunyan = require('../lib/bunyan');\n\n\n/**\n * A raw Bunyan Logger stream object. It takes raw log records and writes\n * them to stdout with an added \"yo\": \"yo\" field.\n */\nfunction MyRawStream() {}\nMyRawStream.prototype.write = function (rec) {\n    if (typeof (rec) !== 'object') {\n        console.error('error: raw stream got a non-object record: %j', rec)\n    } else {\n        rec.yo = 'yo';\n        process.stdout.write(JSON.stringify(rec) + '\\n');\n    }\n}\n\n\n// A Logger using the raw stream.\nvar log = bunyan.createLogger({\n    name: 'raw-example',\n    streams: [\n        {\n            level: 'info',\n            stream: new MyRawStream(),\n            type: 'raw'\n        },\n    ]\n});\n\n\nlog.info('hi raw stream');\nlog.info({foo: 'bar'}, 'added \"foo\" key');\n"
  },
  {
    "path": "examples/ringbuffer.js",
    "content": "/* Create a ring buffer that stores the last 100 records. */\nvar bunyan = require('..');\nvar ringbuffer = new bunyan.RingBuffer({ limit: 100 });\nvar log = new bunyan({\n        name: 'foo',\n        streams: [ {\n                type: 'raw',\n                stream: ringbuffer,\n                level: 'debug'\n        } ]\n});\n\nlog.info('hello world');\nconsole.log(ringbuffer.records);\n"
  },
  {
    "path": "examples/rot-specific-levels.js",
    "content": "var bunyan = require('./'),\n    safeCycles = bunyan.safeCycles;\nvar util = require('util');\n\n\nfunction SpecificLevelStream(levels, stream) {\n    var self = this;\n    this.levels = {};\n    levels.forEach(function (lvl) {\n        self.levels[bunyan.resolveLevel(lvl)] = true;\n    });\n    this.stream = stream;\n}\nSpecificLevelStream.prototype.write = function (rec) {\n    if (this.levels[rec.level] !== undefined) {\n        var str = JSON.stringify(rec, safeCycles()) + '\\n';\n        this.stream.write(str);\n    }\n}\n\n\nvar log = bunyan.createLogger({\n    name: 'rot-specific-levels',\n    streams: [\n        {\n            type: 'raw',\n            level: 'debug',\n            stream: new SpecificLevelStream(\n                ['debug'],\n                new bunyan.RotatingFileStream({\n                    path: './rot-specific-levels.debug.log',\n                    period: '3000ms',\n                    count: 10\n                })\n            )\n        },\n        {\n            type: 'raw',\n            level: 'info',\n            stream: new SpecificLevelStream(\n                ['info'],\n                new bunyan.RotatingFileStream({\n                    path: './rot-specific-levels.info.log',\n                    period: '3000ms',\n                    count: 10\n                })\n            )\n        }\n    ]\n});\n\n\nsetInterval(function () {\n    log.trace('hi on trace')    // goes nowhere\n    log.debug('hi on debug')    // goes to rot-specific-levels.debug.log.*\n    log.info('hi on info')      // goes to rot-specific-levels.info.log.*\n}, 1000);\n"
  },
  {
    "path": "examples/server.js",
    "content": "// Example logging HTTP server request and response objects.\n\nvar http = require('http');\nvar bunyan = require('../lib/bunyan');\n\nvar log = bunyan.createLogger({\n    name: 'myserver',\n    serializers: {\n        req: bunyan.stdSerializers.req,\n        res: bunyan.stdSerializers.res\n    }\n});\n\nvar server = http.createServer(function (req, res) {\n    log.info({req: req}, 'start request');  // <-- this is the guy we're testing\n    res.writeHead(200, {'Content-Type': 'text/plain'});\n    res.end('Hello World\\n');\n    log.info({res: res}, 'done response');  // <-- this is the guy we're testing\n});\nserver.listen(1337, '127.0.0.1', function () {\n    log.info('server listening');\n    var options = {\n        port: 1337,\n        hostname: '127.0.0.1',\n        path: '/path?q=1#anchor',\n        headers: {\n            'X-Hi': 'Mom'\n        }\n    };\n    var req = http.request(options);\n    req.on('response', function (res) {\n        res.on('end', function () {\n            process.exit();\n        })\n    });\n    req.write('hi from the client');\n    req.end();\n});\n\n\n/* BEGIN JSSTYLED */\n/**\n *\n * $ node server.js\n * {\"service\":\"myserver\",\"hostname\":\"banana.local\",\"level\":3,\"msg\":\"server listening\",\"time\":\"2012-02-02T05:32:13.257Z\",\"v\":0}\n * {\"service\":\"myserver\",\"hostname\":\"banana.local\",\"req\":{\"method\":\"GET\",\"url\":\"/path?q=1#anchor\",\"headers\":{\"x-hi\":\"Mom\",\"connection\":\"close\"}},\"level\":3,\"msg\":\"start request\",\"time\":\"2012-02-02T05:32:13.260Z\",\"v\":0}\n * {\"service\":\"myserver\",\"hostname\":\"banana.local\",\"res\":{\"statusCode\":200,\"_hasBody\":true,\"_header\":\"HTTP/1.1 200 OK\\r\\nContent-Type: text/plain\\r\\nConnection: close\\r\\nTransfer-Encoding: chunked\\r\\n\\r\\n\",\"_trailer\":\"\"},\"level\":3,\"msg\":\"done response\",\"time\":\"2012-02-02T05:32:13.261Z\",\"v\":0}\n *\n * $ node server.js | ../bin/bunyan\n * [2012-02-02T05:32:16.006Z] INFO: myserver on banana.local: server listening\n * [2012-02-02T05:32:16.010Z] INFO: myserver on banana.local: start request\n *     GET /path?q=1#anchor\n *     x-hi: Mom\n *     connection: close\n * [2012-02-02T05:32:16.011Z] INFO: myserver on banana.local: done response\n *     HTTP/1.1 200 OK\n *     Content-Type: text/plain\n *     Connection: close\n *     Transfer-Encoding: chunked\n *     (body)\n *\n */\n/* END JSSTYLED */\n"
  },
  {
    "path": "examples/specific-level-streams.js",
    "content": "#!/usr/bin/env node\n/**\n * <https://github.com/trentm/node-bunyan/issues/130> was a request to have\n * bunyan core support logging some levels to one stream and others to another,\n * *not* limited by bunyan's current support that a stream `level` implies\n * \"that level and higher\".\n *\n * Let's do that with a custom raw stream.\n */\n\nvar bunyan = require('../lib/bunyan'),\n    safeCycles = bunyan.safeCycles;\nvar fs = require('fs');\n\n/**\n * Use case #1: cli tool that outputs errors on stderr and everything else on\n * stdout.\n *\n * First make a raw bunyan stream (i.e. an object with a `.write(rec)`).\n */\nfunction SpecificLevelStream(levels, stream) {\n    var self = this;\n    this.levels = {};\n    levels.forEach(function (lvl) {\n        self.levels[bunyan.resolveLevel(lvl)] = true;\n    });\n    this.stream = stream;\n}\nSpecificLevelStream.prototype.write = function (rec) {\n    if (this.levels[rec.level] !== undefined) {\n        var str = JSON.stringify(rec, safeCycles()) + '\\n';\n        this.stream.write(str);\n    }\n}\n\nvar log1 = bunyan.createLogger({\n    name: 'use-case-1',\n    streams: [ {\n        level: 'trace',\n        type: 'raw',\n        stream: new SpecificLevelStream(\n            ['trace', 'debug', 'info', 'warn'],\n            process.stdout)\n    }, {\n        level: 'error',\n        type: 'raw',\n        stream: new SpecificLevelStream(\n            ['error'],\n            process.stderr)\n    } ]\n});\n\nlog1.info('hi at info level (this should be on stdout)');\nlog1.error('alert alert (this should be on stderr)');\n\n\n/**\n * Use case #2: nginx-style logger that separates error- and access-logs\n */\nvar log2 = bunyan.createLogger({\n    name: 'use-case-2',\n    streams: [ {\n        level: 'info',\n        type: 'raw',\n        stream: new SpecificLevelStream(\n            ['info'],\n            fs.createWriteStream('specific-level-streams-http.log',\n                {flags: 'a', encoding: 'utf8'}))\n    }, {\n        level: 'warn',\n        path: 'specific-level-streams-http.err.log'\n    } ]\n});\n\nlog2.info('200 GET /blah');\nlog2.error('500 GET /boom');\n"
  },
  {
    "path": "examples/src.js",
    "content": "// Show the usage of `src: true` config option to get log call source info in\n// log records (the `src` field).\n\nvar bunyan = require('../lib/bunyan');\n\nvar log = bunyan.createLogger({name: 'src-example', src: true});\n\nlog.info('one');\nlog.info('two');\nfunction doSomeFoo() {\n    log.info({foo:'bar'}, 'three');\n}\ndoSomeFoo();\n\nfunction Wuzzle(options) {\n    this.log = options.log;\n    this.log.info('creating a wuzzle')\n}\nWuzzle.prototype.woos = function () {\n    this.log.warn('This wuzzle is woosey.')\n}\n\nvar wuzzle = new Wuzzle({log: log.child({component: 'wuzzle'})});\nwuzzle.woos();\nlog.info('done with the wuzzle')\n"
  },
  {
    "path": "examples/unstringifyable.js",
    "content": "// See how bunyan behaves with an un-stringify-able object.\nvar bunyan = require('../lib/bunyan');\n\nvar log = bunyan.createLogger({src: true, name: 'foo'});\n\n// Make a circular object (cannot be JSON-ified).\nvar myobj = {\n    foo: 'bar'\n};\nmyobj.myobj = myobj;\n\nlog.info({obj: myobj}, 'hi there');   // <--- here\n"
  },
  {
    "path": "lib/bunyan.js",
    "content": "/**\n * Copyright (c) 2017 Trent Mick.\n * Copyright (c) 2017 Joyent Inc.\n *\n * The bunyan logging library for node.js.\n *\n * -*- mode: js -*-\n * vim: expandtab:ts=4:sw=4\n */\n\n/*\n * Bunyan log format version. This becomes the 'v' field on all log records.\n * This will be incremented if there is any backward incompatible change to\n * the log record format. Details will be in 'CHANGES.md' (the change log).\n */\nvar LOG_VERSION = 0;\n\n\nvar xxx = function xxx(s) {     // internal dev/debug logging\n    var args = ['XX' + 'X: '+s].concat(\n        Array.prototype.slice.call(arguments, 1));\n    console.error.apply(this, args);\n};\nvar xxx = function xxx() {};  // comment out to turn on debug logging\n\n\n/*\n * Runtime environment notes:\n *\n * Bunyan is intended to run in a number of runtime environments. Here are\n * some notes on differences for those envs and how the code copes.\n *\n * - node.js: The primary target environment.\n * - NW.js: http://nwjs.io/  An *app* environment that feels like both a\n *   node env -- it has node-like globals (`process`, `global`) and\n *   browser-like globals (`window`, `navigator`). My *understanding* is that\n *   bunyan can operate as if this is vanilla node.js.\n * - browser: Failing the above, we sniff using the `window` global\n *   <https://developer.mozilla.org/en-US/docs/Web/API/Window/window>.\n *      - browserify: http://browserify.org/  A browser-targetting bundler of\n *        node.js deps. The runtime is a browser env, so can't use fs access,\n *        etc. Browserify's build looks for `require(<single-string>)` imports\n *        to bundle. For some imports it won't be able to handle, we \"hide\"\n *        from browserify with `require('frobshizzle' + '')`.\n * - Other? Please open issues if things are broken.\n */\nvar runtimeEnv;\nif (typeof (process) !== 'undefined' && process.versions) {\n    if (process.versions.nw) {\n        runtimeEnv = 'nw';\n    } else if (process.versions.node) {\n        runtimeEnv = 'node';\n    }\n}\nif (!runtimeEnv && typeof (window) !== 'undefined' &&\n    window.window === window) {\n    runtimeEnv = 'browser';\n}\nif (!runtimeEnv) {\n    throw new Error('unknown runtime environment');\n}\n\n\nvar os, fs, dtrace;\nif (runtimeEnv === 'browser') {\n    os = {\n        hostname: function () {\n            return window.location.host;\n        }\n    };\n    fs = {};\n    dtrace = null;\n} else {\n    os = require('os');\n    fs = require('fs');\n    try {\n        dtrace = require('dtrace-provider' + '');\n    } catch (e) {\n        dtrace = null;\n    }\n}\nvar util = require('util');\nvar assert = require('assert');\nvar EventEmitter = require('events').EventEmitter;\nvar stream = require('stream');\n\ntry {\n    var safeJsonStringify = require('safe-json-stringify');\n} catch (e) {\n    safeJsonStringify = null;\n}\nif (process.env.BUNYAN_TEST_NO_SAFE_JSON_STRINGIFY) {\n    safeJsonStringify = null;\n}\n\n// The 'mv' module is required for rotating-file stream support.\ntry {\n    var mv = require('mv' + '');\n} catch (e) {\n    mv = null;\n}\n\ntry {\n    var sourceMapSupport = require('source-map-support' + '');\n} catch (_) {\n    sourceMapSupport = null;\n}\n\n\n//---- Internal support stuff\n\n/**\n * A shallow copy of an object. Bunyan logging attempts to never cause\n * exceptions, so this function attempts to handle non-objects gracefully.\n */\nfunction objCopy(obj) {\n    if (obj == null) {  // null or undefined\n        return obj;\n    } else if (Array.isArray(obj)) {\n        return obj.slice();\n    } else if (typeof (obj) === 'object') {\n        var copy = {};\n        Object.keys(obj).forEach(function (k) {\n            copy[k] = obj[k];\n        });\n        return copy;\n    } else {\n        return obj;\n    }\n}\n\nvar format = util.format;\nif (!format) {\n    // If node < 0.6, then use its `util.format`:\n    // <https://github.com/joyent/node/blob/master/lib/util.js#L22>:\n    var inspect = util.inspect;\n    var formatRegExp = /%[sdj%]/g;\n    format = function format(f) {\n        if (typeof (f) !== 'string') {\n            var objects = [];\n            for (var i = 0; i < arguments.length; i++) {\n                objects.push(inspect(arguments[i]));\n            }\n            return objects.join(' ');\n        }\n\n        var i = 1;\n        var args = arguments;\n        var len = args.length;\n        var str = String(f).replace(formatRegExp, function (x) {\n            if (i >= len)\n                return x;\n            switch (x) {\n                case '%s': return String(args[i++]);\n                case '%d': return Number(args[i++]);\n                case '%j': return fastAndSafeJsonStringify(args[i++]);\n                case '%%': return '%';\n                default:\n                    return x;\n            }\n        });\n        for (var x = args[i]; i < len; x = args[++i]) {\n            if (x === null || typeof (x) !== 'object') {\n                str += ' ' + x;\n            } else {\n                str += ' ' + inspect(x);\n            }\n        }\n        return str;\n    };\n}\n\n\n/**\n * Gather some caller info 3 stack levels up.\n * See <http://code.google.com/p/v8/wiki/JavaScriptStackTraceApi>.\n */\nfunction getCaller3Info() {\n    if (this === undefined) {\n        // Cannot access caller info in 'strict' mode.\n        return;\n    }\n    var obj = {};\n    var saveLimit = Error.stackTraceLimit;\n    var savePrepare = Error.prepareStackTrace;\n    Error.stackTraceLimit = 3;\n\n    Error.prepareStackTrace = function (_, stack) {\n        var caller = stack[2];\n        if (sourceMapSupport) {\n            caller = sourceMapSupport.wrapCallSite(caller);\n        }\n        obj.file = caller.getFileName();\n        obj.line = caller.getLineNumber();\n        var func = caller.getFunctionName();\n        if (func)\n            obj.func = func;\n    };\n    Error.captureStackTrace(this, getCaller3Info);\n    this.stack;\n\n    Error.stackTraceLimit = saveLimit;\n    Error.prepareStackTrace = savePrepare;\n    return obj;\n}\n\n\nfunction _indent(s, indent) {\n    if (!indent) indent = '    ';\n    var lines = s.split(/\\r?\\n/g);\n    return indent + lines.join('\\n' + indent);\n}\n\n\n/**\n * Warn about an bunyan processing error.\n *\n * @param msg {String} Message with which to warn.\n * @param dedupKey {String} Optional. A short string key for this warning to\n *      have its warning only printed once.\n */\nfunction _warn(msg, dedupKey) {\n    assert.ok(msg);\n    if (dedupKey) {\n        if (_warned[dedupKey]) {\n            return;\n        }\n        _warned[dedupKey] = true;\n    }\n    process.stderr.write(msg + '\\n');\n}\nfunction _haveWarned(dedupKey) {\n    return _warned[dedupKey];\n}\nvar _warned = {};\n\n\nfunction ConsoleRawStream() {}\nConsoleRawStream.prototype.write = function (rec) {\n    if (rec.level < INFO) {\n        console.log(rec);\n    } else if (rec.level < WARN) {\n        console.info(rec);\n    } else if (rec.level < ERROR) {\n        console.warn(rec);\n    } else {\n        console.error(rec);\n    }\n};\n\n\n//---- Levels\n\nvar TRACE = 10;\nvar DEBUG = 20;\nvar INFO = 30;\nvar WARN = 40;\nvar ERROR = 50;\nvar FATAL = 60;\n\nvar levelFromName = {\n    'trace': TRACE,\n    'debug': DEBUG,\n    'info': INFO,\n    'warn': WARN,\n    'error': ERROR,\n    'fatal': FATAL\n};\nvar nameFromLevel = {};\nObject.keys(levelFromName).forEach(function (name) {\n    nameFromLevel[levelFromName[name]] = name;\n});\n\n// Dtrace probes.\nvar dtp = undefined;\nvar probes = dtrace && {};\n\n/**\n * Resolve a level number, name (upper or lowercase) to a level number value.\n *\n * @param nameOrNum {String|Number} A level name (case-insensitive) or positive\n *      integer level.\n * @api public\n */\nfunction resolveLevel(nameOrNum) {\n    var level;\n    var type = typeof (nameOrNum);\n    if (type === 'string') {\n        level = levelFromName[nameOrNum.toLowerCase()];\n        if (!level) {\n            throw new Error(format('unknown level name: \"%s\"', nameOrNum));\n        }\n    } else if (type !== 'number') {\n        throw new TypeError(format('cannot resolve level: invalid arg (%s):',\n            type, nameOrNum));\n    } else if (nameOrNum < 0 || Math.floor(nameOrNum) !== nameOrNum) {\n        throw new TypeError(format('level is not a positive integer: %s',\n            nameOrNum));\n    } else {\n        level = nameOrNum;\n    }\n    return level;\n}\n\n\nfunction isWritable(obj) {\n    if (obj instanceof stream.Writable) {\n        return true;\n    }\n    return typeof (obj.write) === 'function';\n}\n\n\n//---- Logger class\n\n/**\n * Create a Logger instance.\n *\n * @param options {Object} See documentation for full details. At minimum\n *    this must include a 'name' string key. Configuration keys:\n *      - `streams`: specify the logger output streams. This is an array of\n *        objects with these fields:\n *          - `type`: The stream type. See README.md for full details.\n *            Often this is implied by the other fields. Examples are\n *            'file', 'stream' and \"raw\".\n *          - `level`: Defaults to 'info'.\n *          - `path` or `stream`: The specify the file path or writeable\n *            stream to which log records are written. E.g.\n *            `stream: process.stdout`.\n *          - `closeOnExit` (boolean): Optional. Default is true for a\n *            'file' stream when `path` is given, false otherwise.\n *        See README.md for full details.\n *      - `level`: set the level for a single output stream (cannot be used\n *        with `streams`)\n *      - `stream`: the output stream for a logger with just one, e.g.\n *        `process.stdout` (cannot be used with `streams`)\n *      - `serializers`: object mapping log record field names to\n *        serializing functions. See README.md for details.\n *      - `src`: Boolean (default false). Set true to enable 'src' automatic\n *        field with log call source info.\n *    All other keys are log record fields.\n *\n * An alternative *internal* call signature is used for creating a child:\n *    new Logger(<parent logger>, <child options>[, <child opts are simple>]);\n *\n * @param _childSimple (Boolean) An assertion that the given `_childOptions`\n *    (a) only add fields (no config) and (b) no serialization handling is\n *    required for them. IOW, this is a fast path for frequent child\n *    creation.\n */\nfunction Logger(options, _childOptions, _childSimple) {\n    xxx('Logger start:', options)\n    if (!(this instanceof Logger)) {\n        return new Logger(options, _childOptions);\n    }\n\n    // Input arg validation.\n    var parent;\n    if (_childOptions !== undefined) {\n        parent = options;\n        options = _childOptions;\n        if (!(parent instanceof Logger)) {\n            throw new TypeError(\n                'invalid Logger creation: do not pass a second arg');\n        }\n    }\n    if (!options) {\n        throw new TypeError('options (object) is required');\n    }\n    if (!parent) {\n        if (!options.name) {\n            throw new TypeError('options.name (string) is required');\n        }\n    } else {\n        if (options.name) {\n            throw new TypeError(\n                'invalid options.name: child cannot set logger name');\n        }\n    }\n    if (options.stream && options.streams) {\n        throw new TypeError('cannot mix \"streams\" and \"stream\" options');\n    }\n    if (options.streams && !Array.isArray(options.streams)) {\n        throw new TypeError('invalid options.streams: must be an array')\n    }\n    if (options.serializers && (typeof (options.serializers) !== 'object' ||\n            Array.isArray(options.serializers))) {\n        throw new TypeError('invalid options.serializers: must be an object')\n    }\n\n    EventEmitter.call(this);\n\n    // Fast path for simple child creation.\n    if (parent && _childSimple) {\n        // `_isSimpleChild` is a signal to stream close handling that this child\n        // owns none of its streams.\n        this._isSimpleChild = true;\n\n        this._level = parent._level;\n        this.streams = parent.streams;\n        this.serializers = parent.serializers;\n        this.src = parent.src;\n        var fields = this.fields = {};\n        var parentFieldNames = Object.keys(parent.fields);\n        for (var i = 0; i < parentFieldNames.length; i++) {\n            var name = parentFieldNames[i];\n            fields[name] = parent.fields[name];\n        }\n        var names = Object.keys(options);\n        for (var i = 0; i < names.length; i++) {\n            var name = names[i];\n            fields[name] = options[name];\n        }\n        return;\n    }\n\n    // Start values.\n    var self = this;\n    if (parent) {\n        this._level = parent._level;\n        this.streams = [];\n        for (var i = 0; i < parent.streams.length; i++) {\n            var s = objCopy(parent.streams[i]);\n            s.closeOnExit = false; // Don't own parent stream.\n            this.streams.push(s);\n        }\n        this.serializers = objCopy(parent.serializers);\n        this.src = parent.src;\n        this.fields = objCopy(parent.fields);\n        if (options.level) {\n            this.level(options.level);\n        }\n    } else {\n        this._level = Number.POSITIVE_INFINITY;\n        this.streams = [];\n        this.serializers = null;\n        this.src = false;\n        this.fields = {};\n    }\n\n    if (!dtp && dtrace) {\n        dtp = dtrace.createDTraceProvider('bunyan');\n\n        for (var level in levelFromName) {\n            var probe;\n\n            probes[levelFromName[level]] = probe =\n                dtp.addProbe('log-' + level, 'char *');\n\n            // Explicitly add a reference to dtp to prevent it from being GC'd\n            probe.dtp = dtp;\n        }\n\n        dtp.enable();\n    }\n\n    // Handle *config* options (i.e. options that are not just plain data\n    // for log records).\n    if (options.stream) {\n        self.addStream({\n            type: 'stream',\n            stream: options.stream,\n            closeOnExit: false,\n            level: options.level\n        });\n    } else if (options.streams) {\n        options.streams.forEach(function (s) {\n            self.addStream(s, options.level);\n        });\n    } else if (parent && options.level) {\n        this.level(options.level);\n    } else if (!parent) {\n        if (runtimeEnv === 'browser') {\n            /*\n             * In the browser we'll be emitting to console.log by default.\n             * Any console.log worth its salt these days can nicely render\n             * and introspect objects (e.g. the Firefox and Chrome console)\n             * so let's emit the raw log record. Are there browsers for which\n             * that breaks things?\n             */\n            self.addStream({\n                type: 'raw',\n                stream: new ConsoleRawStream(),\n                closeOnExit: false,\n                level: options.level\n            });\n        } else {\n            self.addStream({\n                type: 'stream',\n                stream: process.stdout,\n                closeOnExit: false,\n                level: options.level\n            });\n        }\n    }\n    if (options.serializers) {\n        self.addSerializers(options.serializers);\n    }\n    if (options.src) {\n        this.src = true;\n    }\n    xxx('Logger: ', self)\n\n    // Fields.\n    // These are the default fields for log records (minus the attributes\n    // removed in this constructor). To allow storing raw log records\n    // (unrendered), `this.fields` must never be mutated. Create a copy for\n    // any changes.\n    var fields = objCopy(options);\n    delete fields.stream;\n    delete fields.level;\n    delete fields.streams;\n    delete fields.serializers;\n    delete fields.src;\n    if (this.serializers) {\n        this._applySerializers(fields);\n    }\n    if (!fields.hostname && !self.fields.hostname) {\n        fields.hostname = os.hostname();\n    }\n    if (!fields.pid) {\n        fields.pid = process.pid;\n    }\n    Object.keys(fields).forEach(function (k) {\n        self.fields[k] = fields[k];\n    });\n}\n\nutil.inherits(Logger, EventEmitter);\n\n\n/**\n * Add a stream\n *\n * @param stream {Object}. Object with these fields:\n *    - `type`: The stream type. See README.md for full details.\n *      Often this is implied by the other fields. Examples are\n *      'file', 'stream' and \"raw\".\n *    - `path` or `stream`: The specify the file path or writeable\n *      stream to which log records are written. E.g.\n *      `stream: process.stdout`.\n *    - `level`: Optional. Falls back to `defaultLevel`.\n *    - `closeOnExit` (boolean): Optional. Default is true for a\n *      'file' stream when `path` is given, false otherwise.\n *    See README.md for full details.\n * @param defaultLevel {Number|String} Optional. A level to use if\n *      `stream.level` is not set. If neither is given, this defaults to INFO.\n */\nLogger.prototype.addStream = function addStream(s, defaultLevel) {\n    var self = this;\n    if (defaultLevel === null || defaultLevel === undefined) {\n        defaultLevel = INFO;\n    }\n\n    s = objCopy(s);\n\n    // Implicit 'type' from other args.\n    if (!s.type) {\n        if (s.stream) {\n            s.type = 'stream';\n        } else if (s.path) {\n            s.type = 'file'\n        }\n    }\n    s.raw = (s.type === 'raw');  // PERF: Allow for faster check in `_emit`.\n\n    if (s.level !== undefined) {\n        s.level = resolveLevel(s.level);\n    } else {\n        s.level = resolveLevel(defaultLevel);\n    }\n    if (s.level < self._level) {\n        self._level = s.level;\n    }\n\n    switch (s.type) {\n    case 'stream':\n        assert.ok(isWritable(s.stream),\n                  '\"stream\" stream is not writable: ' + util.inspect(s.stream));\n\n        if (!s.closeOnExit) {\n            s.closeOnExit = false;\n        }\n        break;\n    case 'file':\n        if (s.reemitErrorEvents === undefined) {\n            s.reemitErrorEvents = true;\n        }\n        if (!s.stream) {\n            s.stream = fs.createWriteStream(s.path,\n                                            {flags: 'a', encoding: 'utf8'});\n            if (!s.closeOnExit) {\n                s.closeOnExit = true;\n            }\n        } else {\n            if (!s.closeOnExit) {\n                s.closeOnExit = false;\n            }\n        }\n        break;\n    case 'rotating-file':\n        assert.ok(!s.stream,\n                  '\"rotating-file\" stream should not give a \"stream\"');\n        assert.ok(s.path);\n        assert.ok(mv, '\"rotating-file\" stream type is not supported: '\n                      + 'missing \"mv\" module');\n        s.stream = new RotatingFileStream(s);\n        if (!s.closeOnExit) {\n            s.closeOnExit = true;\n        }\n        break;\n    case 'raw':\n        if (!s.closeOnExit) {\n            s.closeOnExit = false;\n        }\n        break;\n    default:\n        throw new TypeError('unknown stream type \"' + s.type + '\"');\n    }\n\n    if (s.reemitErrorEvents && typeof (s.stream.on) === 'function') {\n        // TODO: When we have `<logger>.close()`, it should remove event\n        //      listeners to not leak Logger instances.\n        s.stream.on('error', function onStreamError(err) {\n            self.emit('error', err, s);\n        });\n    }\n\n    self.streams.push(s);\n    delete self.haveNonRawStreams;  // reset\n}\n\n\n/**\n * Add serializers\n *\n * @param serializers {Object} Optional. Object mapping log record field names\n *    to serializing functions. See README.md for details.\n */\nLogger.prototype.addSerializers = function addSerializers(serializers) {\n    var self = this;\n\n    if (!self.serializers) {\n        self.serializers = {};\n    }\n    Object.keys(serializers).forEach(function (field) {\n        var serializer = serializers[field];\n        if (typeof (serializer) !== 'function') {\n            throw new TypeError(format(\n                'invalid serializer for \"%s\" field: must be a function',\n                field));\n        } else {\n            self.serializers[field] = serializer;\n        }\n    });\n}\n\n\n\n/**\n * Create a child logger, typically to add a few log record fields.\n *\n * This can be useful when passing a logger to a sub-component, e.g. a\n * 'wuzzle' component of your service:\n *\n *    var wuzzleLog = log.child({component: 'wuzzle'})\n *    var wuzzle = new Wuzzle({..., log: wuzzleLog})\n *\n * Then log records from the wuzzle code will have the same structure as\n * the app log, *plus the component='wuzzle' field*.\n *\n * @param options {Object} Optional. Set of options to apply to the child.\n *    All of the same options for a new Logger apply here. Notes:\n *      - The parent's streams are inherited and cannot be removed in this\n *        call. Any given `streams` are *added* to the set inherited from\n *        the parent.\n *      - The parent's serializers are inherited, though can effectively be\n *        overwritten by using duplicate keys.\n *      - Can use `level` to set the level of the streams inherited from\n *        the parent. The level for the parent is NOT affected.\n * @param simple {Boolean} Optional. Set to true to assert that `options`\n *    (a) only add fields (no config) and (b) no serialization handling is\n *    required for them. IOW, this is a fast path for frequent child\n *    creation. See 'tools/timechild.js' for numbers.\n */\nLogger.prototype.child = function (options, simple) {\n    return new (this.constructor)(this, options || {}, simple);\n}\n\n\n/**\n * A convenience method to reopen 'file' streams on a logger. This can be\n * useful with external log rotation utilities that move and re-open log files\n * (e.g. logrotate on Linux, logadm on SmartOS/Illumos). Those utilities\n * typically have rotation options to copy-and-truncate the log file, but\n * you may not want to use that. An alternative is to do this in your\n * application:\n *\n *      var log = bunyan.createLogger(...);\n *      ...\n *      process.on('SIGUSR2', function () {\n *          log.reopenFileStreams();\n *      });\n *      ...\n *\n * See <https://github.com/trentm/node-bunyan/issues/104>.\n */\nLogger.prototype.reopenFileStreams = function () {\n    var self = this;\n    self.streams.forEach(function (s) {\n        if (s.type === 'file') {\n            if (s.stream) {\n                // Not sure if typically would want this, or more immediate\n                // `s.stream.destroy()`.\n                s.stream.end();\n                s.stream.destroySoon();\n                delete s.stream;\n            }\n            s.stream = fs.createWriteStream(s.path,\n                {flags: 'a', encoding: 'utf8'});\n            s.stream.on('error', function (err) {\n                self.emit('error', err, s);\n            });\n        }\n    });\n};\n\n\n/* BEGIN JSSTYLED */\n/**\n * Close this logger.\n *\n * This closes streams (that it owns, as per 'endOnClose' attributes on\n * streams), etc. Typically you **don't** need to bother calling this.\nLogger.prototype.close = function () {\n    if (this._closed) {\n        return;\n    }\n    if (!this._isSimpleChild) {\n        self.streams.forEach(function (s) {\n            if (s.endOnClose) {\n                xxx('closing stream s:', s);\n                s.stream.end();\n                s.endOnClose = false;\n            }\n        });\n    }\n    this._closed = true;\n}\n */\n/* END JSSTYLED */\n\n\n/**\n * Get/set the level of all streams on this logger.\n *\n * Get Usage:\n *    // Returns the current log level (lowest level of all its streams).\n *    log.level() -> INFO\n *\n * Set Usage:\n *    log.level(INFO)       // set all streams to level INFO\n *    log.level('info')     // can use 'info' et al aliases\n */\nLogger.prototype.level = function level(value) {\n    if (value === undefined) {\n        return this._level;\n    }\n    var newLevel = resolveLevel(value);\n    var len = this.streams.length;\n    for (var i = 0; i < len; i++) {\n        this.streams[i].level = newLevel;\n    }\n    this._level = newLevel;\n}\n\n\n/**\n * Get/set the level of a particular stream on this logger.\n *\n * Get Usage:\n *    // Returns an array of the levels of each stream.\n *    log.levels() -> [TRACE, INFO]\n *\n *    // Returns a level of the identified stream.\n *    log.levels(0) -> TRACE      // level of stream at index 0\n *    log.levels('foo')           // level of stream with name 'foo'\n *\n * Set Usage:\n *    log.levels(0, INFO)         // set level of stream 0 to INFO\n *    log.levels(0, 'info')       // can use 'info' et al aliases\n *    log.levels('foo', WARN)     // set stream named 'foo' to WARN\n *\n * Stream names: When streams are defined, they can optionally be given\n * a name. For example,\n *       log = new Logger({\n *         streams: [\n *           {\n *             name: 'foo',\n *             path: '/var/log/my-service/foo.log'\n *             level: 'trace'\n *           },\n *         ...\n *\n * @param name {String|Number} The stream index or name.\n * @param value {Number|String} The level value (INFO) or alias ('info').\n *    If not given, this is a 'get' operation.\n * @throws {Error} If there is no stream with the given name.\n */\nLogger.prototype.levels = function levels(name, value) {\n    if (name === undefined) {\n        assert.equal(value, undefined);\n        return this.streams.map(\n            function (s) { return s.level });\n    }\n    var stream;\n    if (typeof (name) === 'number') {\n        stream = this.streams[name];\n        if (stream === undefined) {\n            throw new Error('invalid stream index: ' + name);\n        }\n    } else {\n        var len = this.streams.length;\n        for (var i = 0; i < len; i++) {\n            var s = this.streams[i];\n            if (s.name === name) {\n                stream = s;\n                break;\n            }\n        }\n        if (!stream) {\n            throw new Error(format('no stream with name \"%s\"', name));\n        }\n    }\n    if (value === undefined) {\n        return stream.level;\n    } else {\n        var newLevel = resolveLevel(value);\n        stream.level = newLevel;\n        if (newLevel < this._level) {\n            this._level = newLevel;\n        }\n    }\n}\n\n\n/**\n * Apply registered serializers to the appropriate keys in the given fields.\n *\n * Pre-condition: This is only called if there is at least one serializer.\n *\n * @param fields (Object) The log record fields.\n * @param excludeFields (Object) Optional mapping of keys to `true` for\n *    keys to NOT apply a serializer.\n */\nLogger.prototype._applySerializers = function (fields, excludeFields) {\n    var self = this;\n\n    xxx('_applySerializers: excludeFields', excludeFields);\n\n    // Check each serializer against these (presuming number of serializers\n    // is typically less than number of fields).\n    Object.keys(this.serializers).forEach(function (name) {\n        if (fields[name] === undefined ||\n            (excludeFields && excludeFields[name]))\n        {\n            return;\n        }\n        xxx('_applySerializers; apply to \"%s\" key', name)\n        try {\n            fields[name] = self.serializers[name](fields[name]);\n        } catch (err) {\n            _warn(format('bunyan: ERROR: Exception thrown from the \"%s\" '\n                + 'Bunyan serializer. This should never happen. This is a bug '\n                + 'in that serializer function.\\n%s',\n                name, err.stack || err));\n            fields[name] = format('(Error in Bunyan log \"%s\" serializer '\n                + 'broke field. See stderr for details.)', name);\n        }\n    });\n}\n\n\n/**\n * Emit a log record.\n *\n * @param rec {log record}\n * @param noemit {Boolean} Optional. Set to true to skip emission\n *      and just return the JSON string.\n */\nLogger.prototype._emit = function (rec, noemit) {\n    var i;\n\n    // Lazily determine if this Logger has non-'raw' streams. If there are\n    // any, then we need to stringify the log record.\n    if (this.haveNonRawStreams === undefined) {\n        this.haveNonRawStreams = false;\n        for (i = 0; i < this.streams.length; i++) {\n            if (!this.streams[i].raw) {\n                this.haveNonRawStreams = true;\n                break;\n            }\n        }\n    }\n\n    // Stringify the object (creates a warning str on error).\n    var str;\n    if (noemit || this.haveNonRawStreams) {\n        str = fastAndSafeJsonStringify(rec) + os.EOL;\n    }\n\n    if (noemit)\n        return str;\n\n    var level = rec.level;\n    for (i = 0; i < this.streams.length; i++) {\n        var s = this.streams[i];\n        if (s.level <= level) {\n            xxx('writing log rec \"%s\" to \"%s\" stream (%d <= %d): %j',\n                rec.msg, s.type, s.level, level, rec);\n            s.stream.write(s.raw ? rec : str);\n        }\n    };\n\n    return str;\n}\n\n\n/**\n * Build a record object suitable for emitting from the arguments\n * provided to the a log emitter.\n */\nfunction mkRecord(log, minLevel, args) {\n    var excludeFields, fields, msgArgs;\n    if (args[0] instanceof Error) {\n        // `log.<level>(err, ...)`\n        fields = {\n            // Use this Logger's err serializer, if defined.\n            err: (log.serializers && log.serializers.err\n                ? log.serializers.err(args[0])\n                : Logger.stdSerializers.err(args[0]))\n        };\n        excludeFields = {err: true};\n        if (args.length === 1) {\n            msgArgs = [fields.err.message];\n        } else {\n            msgArgs = args.slice(1);\n        }\n    } else if (typeof (args[0]) !== 'object' || Array.isArray(args[0])) {\n        // `log.<level>(msg, ...)`\n        fields = null;\n        msgArgs = args.slice();\n    } else if (Buffer.isBuffer(args[0])) {  // `log.<level>(buf, ...)`\n        // Almost certainly an error, show `inspect(buf)`. See bunyan\n        // issue #35.\n        fields = null;\n        msgArgs = args.slice();\n        msgArgs[0] = util.inspect(msgArgs[0]);\n    } else {  // `log.<level>(fields, msg, ...)`\n        fields = args[0];\n        if (fields && args.length === 1 && fields.err &&\n            fields.err instanceof Error)\n        {\n            msgArgs = [fields.err.message];\n        } else {\n            msgArgs = args.slice(1);\n        }\n    }\n\n    // Build up the record object.\n    var rec = objCopy(log.fields);\n    var level = rec.level = minLevel;\n    var recFields = (fields ? objCopy(fields) : null);\n    if (recFields) {\n        if (log.serializers) {\n            log._applySerializers(recFields, excludeFields);\n        }\n        Object.keys(recFields).forEach(function (k) {\n            rec[k] = recFields[k];\n        });\n    }\n    rec.msg = format.apply(log, msgArgs);\n    if (!rec.time) {\n        rec.time = (new Date());\n    }\n    // Get call source info\n    if (log.src && !rec.src) {\n        rec.src = getCaller3Info()\n    }\n    rec.v = LOG_VERSION;\n\n    return rec;\n};\n\n\n/**\n * Build an array that dtrace-provider can use to fire a USDT probe. If we've\n * already built the appropriate string, we use it. Otherwise, build the\n * record object and stringify it.\n */\nfunction mkProbeArgs(str, log, minLevel, msgArgs) {\n    return [ str || log._emit(mkRecord(log, minLevel, msgArgs), true) ];\n}\n\n\n/**\n * Build a log emitter function for level minLevel. I.e. this is the\n * creator of `log.info`, `log.error`, etc.\n */\nfunction mkLogEmitter(minLevel) {\n    return function () {\n        var log = this;\n        var str = null;\n        var rec = null;\n\n        if (!this._emit) {\n            /*\n             * Show this invalid Bunyan usage warning *once*.\n             *\n             * See <https://github.com/trentm/node-bunyan/issues/100> for\n             * an example of how this can happen.\n             */\n            var dedupKey = 'unbound';\n            if (!_haveWarned[dedupKey]) {\n                var caller = getCaller3Info();\n                _warn(format('bunyan usage error: %s:%s: attempt to log '\n                    + 'with an unbound log method: `this` is: %s',\n                    caller.file, caller.line, util.inspect(this)),\n                    dedupKey);\n            }\n            return;\n        } else if (arguments.length === 0) {   // `log.<level>()`\n            return (this._level <= minLevel);\n        }\n\n        var msgArgs = new Array(arguments.length);\n        for (var i = 0; i < msgArgs.length; ++i) {\n            msgArgs[i] = arguments[i];\n        }\n\n        if (this._level <= minLevel) {\n            rec = mkRecord(log, minLevel, msgArgs);\n            str = this._emit(rec);\n        }\n\n        if (probes) {\n            probes[minLevel].fire(mkProbeArgs, str, log, minLevel, msgArgs);\n        }\n    }\n}\n\n\n/**\n * The functions below log a record at a specific level.\n *\n * Usages:\n *    log.<level>()  -> boolean is-trace-enabled\n *    log.<level>(<Error> err, [<string> msg, ...])\n *    log.<level>(<string> msg, ...)\n *    log.<level>(<object> fields, <string> msg, ...)\n *\n * where <level> is the lowercase version of the log level. E.g.:\n *\n *    log.info()\n *\n * @params fields {Object} Optional set of additional fields to log.\n * @params msg {String} Log message. This can be followed by additional\n *    arguments that are handled like\n *    [util.format](http://nodejs.org/docs/latest/api/all.html#util.format).\n */\nLogger.prototype.trace = mkLogEmitter(TRACE);\nLogger.prototype.debug = mkLogEmitter(DEBUG);\nLogger.prototype.info = mkLogEmitter(INFO);\nLogger.prototype.warn = mkLogEmitter(WARN);\nLogger.prototype.error = mkLogEmitter(ERROR);\nLogger.prototype.fatal = mkLogEmitter(FATAL);\n\n\n\n//---- Standard serializers\n// A serializer is a function that serializes a JavaScript object to a\n// JSON representation for logging. There is a standard set of presumed\n// interesting objects in node.js-land.\n\nLogger.stdSerializers = {};\n\n// Serialize an HTTP request.\nLogger.stdSerializers.req = function (req) {\n    if (!req || !req.connection)\n        return req;\n    return {\n        method: req.method,\n        // Accept `req.originalUrl` for expressjs usage.\n        // https://expressjs.com/en/api.html#req.originalUrl\n        url: req.originalUrl || req.url,\n        headers: req.headers,\n        remoteAddress: req.connection.remoteAddress,\n        remotePort: req.connection.remotePort\n    };\n    // Trailers: Skipping for speed. If you need trailers in your app, then\n    // make a custom serializer.\n    //if (Object.keys(trailers).length > 0) {\n    //  obj.trailers = req.trailers;\n    //}\n};\n\n// Serialize an HTTP response.\nLogger.stdSerializers.res = function (res) {\n    if (!res || !res.statusCode)\n        return res;\n    return {\n        statusCode: res.statusCode,\n        header: res._header\n    }\n};\n\n\n/*\n * This function dumps long stack traces for exceptions having a cause()\n * method. The error classes from\n * [verror](https://github.com/davepacheco/node-verror) and\n * [restify v2.0](https://github.com/mcavage/node-restify) are examples.\n *\n * Based on `dumpException` in\n * https://github.com/davepacheco/node-extsprintf/blob/master/lib/extsprintf.js\n */\nfunction getFullErrorStack(ex)\n{\n    var ret = ex.stack || ex.toString();\n    if (ex.cause && typeof (ex.cause) === 'function') {\n        var cex = ex.cause();\n        if (cex) {\n            ret += '\\nCaused by: ' + getFullErrorStack(cex);\n        }\n    }\n    return (ret);\n}\n\n// Serialize an Error object\n// (Core error properties are enumerable in node 0.4, not in 0.6).\nvar errSerializer = Logger.stdSerializers.err = function (err) {\n    if (!err || !err.stack)\n        return err;\n    var obj = {\n        message: err.message,\n        name: err.name,\n        stack: getFullErrorStack(err),\n        code: err.code,\n        signal: err.signal\n    }\n    return obj;\n};\n\n\n// A JSON stringifier that handles cycles safely - tracks seen values in a Set.\nfunction safeCyclesSet() {\n    var seen = new Set();\n    return function (key, val) {\n        if (!val || typeof (val) !== 'object') {\n            return val;\n        }\n        if (seen.has(val)) {\n            return '[Circular]';\n        }\n        seen.add(val);\n        return val;\n    };\n}\n\n/**\n * A JSON stringifier that handles cycles safely - tracks seen vals in an Array.\n *\n * Note: This approach has performance problems when dealing with large objects,\n * see trentm/node-bunyan#445, but since this is the only option for node 0.10\n * and earlier (as Set was introduced in Node 0.12), it's used as a fallback\n * when Set is not available.\n */\nfunction safeCyclesArray() {\n    var seen = [];\n    return function (key, val) {\n        if (!val || typeof (val) !== 'object') {\n            return val;\n        }\n        if (seen.indexOf(val) !== -1) {\n            return '[Circular]';\n        }\n        seen.push(val);\n        return val;\n    };\n}\n\n/**\n * A JSON stringifier that handles cycles safely.\n *\n * Usage: JSON.stringify(obj, safeCycles())\n *\n * Choose the best safe cycle function from what is available - see\n * trentm/node-bunyan#445.\n */\nvar safeCycles = typeof (Set) !== 'undefined' ? safeCyclesSet : safeCyclesArray;\n\n/**\n * A fast JSON.stringify that handles cycles and getter exceptions (when\n * safeJsonStringify is installed).\n *\n * This function attempts to use the regular JSON.stringify for speed, but on\n * error (e.g. JSON cycle detection exception) it falls back to safe stringify\n * handlers that can deal with cycles and/or getter exceptions.\n */\nfunction fastAndSafeJsonStringify(rec) {\n    try {\n        return JSON.stringify(rec);\n    } catch (ex) {\n        try {\n            return JSON.stringify(rec, safeCycles());\n        } catch (e) {\n            if (safeJsonStringify) {\n                return safeJsonStringify(rec);\n            } else {\n                var dedupKey = e.stack.split(/\\n/g, 3).join('\\n');\n                _warn('bunyan: ERROR: Exception in '\n                    + '`JSON.stringify(rec)`. You can install the '\n                    + '\"safe-json-stringify\" module to have Bunyan fallback '\n                    + 'to safer stringification. Record:\\n'\n                    + _indent(format('%s\\n%s', util.inspect(rec), e.stack)),\n                    dedupKey);\n                return format('(Exception in JSON.stringify(rec): %j. '\n                    + 'See stderr for details.)', e.message);\n            }\n        }\n    }\n}\n\n\nvar RotatingFileStream = null;\nif (mv) {\n\nRotatingFileStream = function RotatingFileStream(options) {\n    this.path = options.path;\n\n    this.count = (options.count == null ? 10 : options.count);\n    assert.equal(typeof (this.count), 'number',\n        format('rotating-file stream \"count\" is not a number: %j (%s) in %j',\n            this.count, typeof (this.count), this));\n    assert.ok(this.count >= 0,\n        format('rotating-file stream \"count\" is not >= 0: %j in %j',\n            this.count, this));\n\n    // Parse `options.period`.\n    if (options.period) {\n        // <number><scope> where scope is:\n        //    h   hours (at the start of the hour)\n        //    d   days (at the start of the day, i.e. just after midnight)\n        //    w   weeks (at the start of Sunday)\n        //    m   months (on the first of the month)\n        //    y   years (at the start of Jan 1st)\n        // with special values 'hourly' (1h), 'daily' (1d), \"weekly\" (1w),\n        // 'monthly' (1m) and 'yearly' (1y)\n        var period = {\n            'hourly': '1h',\n            'daily': '1d',\n            'weekly': '1w',\n            'monthly': '1m',\n            'yearly': '1y'\n        }[options.period] || options.period;\n        var m = /^([1-9][0-9]*)([hdwmy]|ms)$/.exec(period);\n        if (!m) {\n            throw new Error(format('invalid period: \"%s\"', options.period));\n        }\n        this.periodNum = Number(m[1]);\n        this.periodScope = m[2];\n    } else {\n        this.periodNum = 1;\n        this.periodScope = 'd';\n    }\n\n    var lastModified = null;\n    try {\n        var fileInfo = fs.statSync(this.path);\n        lastModified = fileInfo.mtime.getTime();\n    }\n    catch (err) {\n        // file doesn't exist\n    }\n    var rotateAfterOpen = false;\n    if (lastModified) {\n        var lastRotTime = this._calcRotTime(0);\n        if (lastModified < lastRotTime) {\n            rotateAfterOpen = true;\n        }\n    }\n\n    // TODO: template support for backup files\n    // template: <path to which to rotate>\n    //      default is %P.%n\n    //      '/var/log/archive/foo.log'  -> foo.log.%n\n    //      '/var/log/archive/foo.log.%n'\n    //      codes:\n    //          XXX support strftime codes (per node version of those)\n    //              or whatever module. Pick non-colliding for extra\n    //              codes\n    //          %P      `path` base value\n    //          %n      integer number of rotated log (1,2,3,...)\n    //          %d      datetime in YYYY-MM-DD_HH-MM-SS\n    //                      XXX what should default date format be?\n    //                          prior art? Want to avoid ':' in\n    //                          filenames (illegal on Windows for one).\n\n    this.stream = fs.createWriteStream(this.path,\n        {flags: 'a', encoding: 'utf8'});\n\n    this.rotQueue = [];\n    this.rotating = false;\n    if (rotateAfterOpen) {\n        this._debug('rotateAfterOpen -> call rotate()');\n        this.rotate();\n    } else {\n        this._setupNextRot();\n    }\n}\n\nutil.inherits(RotatingFileStream, EventEmitter);\n\nRotatingFileStream.prototype._debug = function () {\n    // Set this to `true` to add debug logging.\n    if (false) {\n        if (arguments.length === 0) {\n            return true;\n        }\n        var args = Array.prototype.slice.call(arguments);\n        args[0] = '[' + (new Date().toISOString()) + ', '\n            + this.path + '] ' + args[0];\n        console.log.apply(this, args);\n    } else {\n        return false;\n    }\n};\n\nRotatingFileStream.prototype._setupNextRot = function () {\n    this.rotAt = this._calcRotTime(1);\n    this._setRotationTimer();\n}\n\nRotatingFileStream.prototype._setRotationTimer = function () {\n    var self = this;\n    var delay = this.rotAt - Date.now();\n    // Cap timeout to Node's max setTimeout, see\n    // <https://github.com/joyent/node/issues/8656>.\n    var TIMEOUT_MAX = 2147483647; // 2^31-1\n    if (delay > TIMEOUT_MAX) {\n        delay = TIMEOUT_MAX;\n    }\n    this.timeout = setTimeout(\n        function () {\n            self._debug('_setRotationTimer timeout -> call rotate()');\n            self.rotate();\n        },\n        delay);\n    if (typeof (this.timeout.unref) === 'function') {\n        this.timeout.unref();\n    }\n}\n\nRotatingFileStream.prototype._calcRotTime =\nfunction _calcRotTime(periodOffset) {\n    this._debug('_calcRotTime: %s%s', this.periodNum, this.periodScope);\n    var d = new Date();\n\n    this._debug('  now local: %s', d);\n    this._debug('    now utc: %s', d.toISOString());\n    var rotAt;\n    switch (this.periodScope) {\n    case 'ms':\n        // Hidden millisecond period for debugging.\n        if (this.rotAt) {\n            rotAt = this.rotAt + this.periodNum * periodOffset;\n        } else {\n            rotAt = Date.now() + this.periodNum * periodOffset;\n        }\n        break;\n    case 'h':\n        if (this.rotAt) {\n            rotAt = this.rotAt + this.periodNum * 60 * 60 * 1000 * periodOffset;\n        } else {\n            // First time: top of the next hour.\n            rotAt = Date.UTC(d.getUTCFullYear(), d.getUTCMonth(),\n                d.getUTCDate(), d.getUTCHours() + periodOffset);\n        }\n        break;\n    case 'd':\n        if (this.rotAt) {\n            rotAt = this.rotAt + this.periodNum * 24 * 60 * 60 * 1000\n                * periodOffset;\n        } else {\n            // First time: start of tomorrow (i.e. at the coming midnight) UTC.\n            rotAt = Date.UTC(d.getUTCFullYear(), d.getUTCMonth(),\n                d.getUTCDate() + periodOffset);\n        }\n        break;\n    case 'w':\n        // Currently, always on Sunday morning at 00:00:00 (UTC).\n        if (this.rotAt) {\n            rotAt = this.rotAt + this.periodNum * 7 * 24 * 60 * 60 * 1000\n                * periodOffset;\n        } else {\n            // First time: this coming Sunday.\n            var dayOffset = (7 - d.getUTCDay());\n            if (periodOffset < 1) {\n                dayOffset = -d.getUTCDay();\n            }\n            if (periodOffset > 1 || periodOffset < -1) {\n                dayOffset += 7 * periodOffset;\n            }\n            rotAt = Date.UTC(d.getUTCFullYear(), d.getUTCMonth(),\n                d.getUTCDate() + dayOffset);\n        }\n        break;\n    case 'm':\n        if (this.rotAt) {\n            rotAt = Date.UTC(d.getUTCFullYear(),\n                d.getUTCMonth() + this.periodNum * periodOffset, 1);\n        } else {\n            // First time: the start of the next month.\n            rotAt = Date.UTC(d.getUTCFullYear(),\n                d.getUTCMonth() + periodOffset, 1);\n        }\n        break;\n    case 'y':\n        if (this.rotAt) {\n            rotAt = Date.UTC(d.getUTCFullYear() + this.periodNum * periodOffset,\n                0, 1);\n        } else {\n            // First time: the start of the next year.\n            rotAt = Date.UTC(d.getUTCFullYear() + periodOffset, 0, 1);\n        }\n        break;\n    default:\n        assert.fail(format('invalid period scope: \"%s\"', this.periodScope));\n    }\n\n    if (this._debug()) {\n        this._debug('  **rotAt**: %s (utc: %s)', rotAt,\n            new Date(rotAt).toUTCString());\n        var now = Date.now();\n        this._debug('        now: %s (%sms == %smin == %sh to go)',\n            now,\n            rotAt - now,\n            (rotAt-now)/1000/60,\n            (rotAt-now)/1000/60/60);\n    }\n    return rotAt;\n};\n\nRotatingFileStream.prototype.rotate = function rotate() {\n    // XXX What about shutdown?\n    var self = this;\n\n    // If rotation period is > ~25 days, we have to break into multiple\n    // setTimeout's. See <https://github.com/joyent/node/issues/8656>.\n    if (self.rotAt && self.rotAt > Date.now()) {\n        return self._setRotationTimer();\n    }\n\n    this._debug('rotate');\n    if (self.rotating) {\n        throw new TypeError('cannot start a rotation when already rotating');\n    }\n    self.rotating = true;\n\n    self.stream.end();  // XXX can do moves sync after this? test at high rate\n\n    function del() {\n        var toDel = self.path + '.' + String(n - 1);\n        if (n === 0) {\n            toDel = self.path;\n        }\n        n -= 1;\n        self._debug('  rm %s', toDel);\n        fs.unlink(toDel, function (delErr) {\n            //XXX handle err other than not exists\n            moves();\n        });\n    }\n\n    function moves() {\n        if (self.count === 0 || n < 0) {\n            return finish();\n        }\n        var before = self.path;\n        var after = self.path + '.' + String(n);\n        if (n > 0) {\n            before += '.' + String(n - 1);\n        }\n        n -= 1;\n        fs.exists(before, function (exists) {\n            if (!exists) {\n                moves();\n            } else {\n                self._debug('  mv %s %s', before, after);\n                mv(before, after, function (mvErr) {\n                    if (mvErr) {\n                        self.emit('error', mvErr);\n                        finish(); // XXX finish here?\n                    } else {\n                        moves();\n                    }\n                });\n            }\n        })\n    }\n\n    function finish() {\n        self._debug('  open %s', self.path);\n        self.stream = fs.createWriteStream(self.path,\n            {flags: 'a', encoding: 'utf8'});\n        var q = self.rotQueue, len = q.length;\n        for (var i = 0; i < len; i++) {\n            self.stream.write(q[i]);\n        }\n        self.rotQueue = [];\n        self.rotating = false;\n        self.emit('drain');\n        self._setupNextRot();\n    }\n\n    var n = this.count;\n    del();\n};\n\nRotatingFileStream.prototype.write = function write(s) {\n    if (this.rotating) {\n        this.rotQueue.push(s);\n        return false;\n    } else {\n        return this.stream.write(s);\n    }\n};\n\nRotatingFileStream.prototype.end = function end(s) {\n    this.stream.end();\n};\n\nRotatingFileStream.prototype.destroy = function destroy(s) {\n    this.stream.destroy();\n};\n\nRotatingFileStream.prototype.destroySoon = function destroySoon(s) {\n    this.stream.destroySoon();\n};\n\n} /* if (mv) */\n\n\n\n/**\n * RingBuffer is a Writable Stream that just stores the last N records in\n * memory.\n *\n * @param options {Object}, with the following fields:\n *\n *    - limit: number of records to keep in memory\n */\nfunction RingBuffer(options) {\n    this.limit = options && options.limit ? options.limit : 100;\n    this.writable = true;\n    this.records = [];\n    EventEmitter.call(this);\n}\n\nutil.inherits(RingBuffer, EventEmitter);\n\nRingBuffer.prototype.write = function (record) {\n    if (!this.writable)\n        throw (new Error('RingBuffer has been ended already'));\n\n    this.records.push(record);\n\n    if (this.records.length > this.limit)\n        this.records.shift();\n\n    return (true);\n};\n\nRingBuffer.prototype.end = function () {\n    if (arguments.length > 0)\n        this.write.apply(this, Array.prototype.slice.call(arguments));\n    this.writable = false;\n};\n\nRingBuffer.prototype.destroy = function () {\n    this.writable = false;\n    this.emit('close');\n};\n\nRingBuffer.prototype.destroySoon = function () {\n    this.destroy();\n};\n\n\n//---- Exports\n\nmodule.exports = Logger;\n\nmodule.exports.TRACE = TRACE;\nmodule.exports.DEBUG = DEBUG;\nmodule.exports.INFO = INFO;\nmodule.exports.WARN = WARN;\nmodule.exports.ERROR = ERROR;\nmodule.exports.FATAL = FATAL;\nmodule.exports.resolveLevel = resolveLevel;\nmodule.exports.levelFromName = levelFromName;\nmodule.exports.nameFromLevel = nameFromLevel;\n\nmodule.exports.VERSION = require('../package.json').version;\nmodule.exports.LOG_VERSION = LOG_VERSION;\n\nmodule.exports.createLogger = function createLogger(options) {\n    return new Logger(options);\n};\n\nmodule.exports.RingBuffer = RingBuffer;\nmodule.exports.RotatingFileStream = RotatingFileStream;\n\n// Useful for custom `type == 'raw'` streams that may do JSON stringification\n// of log records themselves. Usage:\n//    var str = JSON.stringify(rec, bunyan.safeCycles());\nmodule.exports.safeCycles = safeCycles;\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"bunyan\",\n  \"version\": \"2.0.5\",\n  \"description\": \"a JSON logging library for node.js services\",\n  \"author\": \"Trent Mick <trentm@gmail.com> (http://trentm.com)\",\n  \"main\": \"./lib/bunyan.js\",\n  \"bin\": {\n    \"bunyan\": \"./bin/bunyan\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git://github.com/trentm/node-bunyan.git\"\n  },\n  \"engines\": [\n    \"node >=0.10.0\"\n  ],\n  \"keywords\": [\n    \"log\",\n    \"logging\",\n    \"log4j\",\n    \"json\",\n    \"bunyan\"\n  ],\n  \"license\": \"MIT\",\n  \"files\": [\n    \"bin\",\n    \"lib\"\n  ],\n  \"dependencies\": {\n    \"exeunt\": \"1.1.0\"\n  },\n  \"// dtrace-provider\": \"required for dtrace features\",\n  \"// mv\": \"required for RotatingFileStream\",\n  \"// moment\": \"required for local time with CLI\",\n  \"optionalDependencies\": {\n    \"dtrace-provider\": \"~0.8\",\n    \"mv\": \"~2\",\n    \"safe-json-stringify\": \"~1\",\n    \"moment\": \"^2.19.3\"\n  },\n  \"devDependencies\": {\n    \"ben\": \"0.0.0\",\n    \"markdown-toc\": \"0.12.x\",\n    \"tap\": \"^9.0.3\",\n    \"vasync\": \"1.4.3\",\n    \"verror\": \"1.3.3\"\n  },\n  \"scripts\": {\n    \"check\": \"make check\",\n    \"test\": \"tap test/*.test.js  # skip dtrace tests\"\n  }\n}\n"
  },
  {
    "path": "test/add-stream.test.js",
    "content": "/*\n * Copyright (c) 2016 Trent Mick. All rights reserved.\n *\n * Test stream adding.\n */\n\nvar test = require('tap').test;\n\nvar bunyan = require('../lib/bunyan');\n\n\ntest('non-writables passed as stream', function (t) {\n    var things = ['process.stdout', {}];\n    things.forEach(function (thing) {\n        function createLogger() {\n            bunyan.createLogger({\n                name: 'foo',\n                stream: thing\n            });\n        }\n        t.throws(createLogger,\n            /stream is not writable/,\n            '\"stream\" stream is not writable');\n    })\n    t.end();\n});\n\ntest('proper stream', function (t) {\n    var log = bunyan.createLogger({\n        name: 'foo',\n        stream: process.stdout\n    });\n    t.ok('should not throw');\n    t.end();\n});\n"
  },
  {
    "path": "test/buffer.test.js",
    "content": "/*\n * Copyright 2020 Trent Mick\n * Copyright 2012 Joyent Inc.\n *\n * Test logging with (accidental) usage of buffers.\n */\n\nvar util = require('util'),\n    inspect = util.inspect,\n    format = util.format;\nvar test = require('tap').test;\n\nvar bunyan = require('../lib/bunyan');\n\n\nfunction Catcher() {\n    this.records = [];\n}\nCatcher.prototype.write = function (record) {\n    this.records.push(record);\n}\n\nvar catcher = new Catcher();\nvar log = new bunyan.createLogger({\n    name: 'buffer.test',\n    streams: [\n        {\n            type: 'raw',\n            stream: catcher,\n            level: 'trace'\n        }\n    ]\n});\n\n\ntest('log.info(BUFFER)', function (t) {\n    var b = Buffer.from ? Buffer.from('foo') : new Buffer('foo');\n\n    ['trace',\n     'debug',\n     'info',\n     'warn',\n     'error',\n     'fatal'].forEach(function (lvl) {\n        log[lvl].call(log, b);\n        var rec = catcher.records[catcher.records.length - 1];\n        t.equal(rec.msg, inspect(b),\n            format('log.%s msg is inspect(BUFFER)', lvl));\n        t.ok(rec['0'] === undefined,\n            'no \"0\" array index key in record: ' + inspect(rec['0']));\n        t.ok(rec['parent'] === undefined,\n            'no \"parent\" array index key in record: ' + inspect(rec['parent']));\n\n        log[lvl].call(log, b, 'bar');\n        var rec = catcher.records[catcher.records.length - 1];\n        t.equal(rec.msg, inspect(b) + ' bar', format(\n            'log.%s(BUFFER, \"bar\") msg is inspect(BUFFER) + \" bar\"', lvl));\n    });\n\n    t.end();\n});\n\n\n//test('log.info({buf: BUFFER})', function (t) {\n//  var b = Buffer.from ? Buffer.from('foo') : new Buffer('foo');\n//\n//  // Really there isn't much Bunyan can do here. See\n//  // <https://github.com/joyent/node/issues/3905>. An unwelcome hack would\n//  // be to monkey-patch in Buffer.toJSON. Bletch.\n//  log.info({buf: b}, 'my message');\n//  var rec = catcher.records[catcher.records.length - 1];\n//\n//  t.end();\n//});\n"
  },
  {
    "path": "test/child-behaviour.test.js",
    "content": "/*\n * Copyright 2020 Trent Mick\n *\n * Test some `<Logger>.child(...)` behaviour.\n */\n\nvar test = require('tap').test;\n\nvar bunyan = require('../lib/bunyan');\n\n\nfunction CapturingStream(recs) {\n    this.recs = recs || [];\n}\nCapturingStream.prototype.write = function (rec) {\n    this.recs.push(rec);\n}\n\n\n\ntest('child can add stream', function (t) {\n    var dadStream = new CapturingStream();\n    var dad = bunyan.createLogger({\n        name: 'surname',\n        streams: [ {\n            type: 'raw',\n            stream: dadStream,\n            level: 'info'\n        } ]\n    });\n\n    var sonStream = new CapturingStream();\n    var son = dad.child({\n        component: 'son',\n        streams: [ {\n            type: 'raw',\n            stream: sonStream,\n            level: 'debug'\n        } ]\n    });\n\n    dad.info('info from dad');\n    dad.debug('debug from dad');\n    son.debug('debug from son');\n\n    var rec;\n    t.equal(dadStream.recs.length, 1);\n    rec = dadStream.recs[0];\n    t.equal(rec.msg, 'info from dad');\n    t.equal(sonStream.recs.length, 1);\n    rec = sonStream.recs[0];\n    t.equal(rec.msg, 'debug from son');\n\n    t.end();\n});\n\n\ntest('child can set level of inherited streams', function (t) {\n    var dadStream = new CapturingStream();\n    var dad = bunyan.createLogger({\n        name: 'surname',\n        streams: [ {\n            type: 'raw',\n            stream: dadStream,\n            level: 'info'\n        } ]\n    });\n\n    // Intention here is that the inherited `dadStream` logs at 'debug' level\n    // for the son.\n    var son = dad.child({\n        component: 'son',\n        level: 'debug'\n    });\n\n    dad.info('info from dad');\n    dad.debug('debug from dad');\n    son.debug('debug from son');\n\n    var rec;\n    t.equal(dadStream.recs.length, 2);\n    rec = dadStream.recs[0];\n    t.equal(rec.msg, 'info from dad');\n    rec = dadStream.recs[1];\n    t.equal(rec.msg, 'debug from son');\n\n    t.end();\n});\n\n\ntest('child can set level of inherited streams and add streams', function (t) {\n    var dadStream = new CapturingStream();\n    var dad = bunyan.createLogger({\n        name: 'surname',\n        streams: [ {\n            type: 'raw',\n            stream: dadStream,\n            level: 'info'\n        } ]\n    });\n\n    // Intention here is that the inherited `dadStream` logs at 'debug' level\n    // for the son.\n    var sonStream = new CapturingStream();\n    var son = dad.child({\n        component: 'son',\n        level: 'trace',\n        streams: [ {\n            type: 'raw',\n            stream: sonStream,\n            level: 'debug'\n        } ]\n    });\n\n    dad.info('info from dad');\n    dad.trace('trace from dad');\n    son.trace('trace from son');\n    son.debug('debug from son');\n\n    t.equal(dadStream.recs.length, 3);\n    t.equal(dadStream.recs[0].msg, 'info from dad');\n    t.equal(dadStream.recs[1].msg, 'trace from son');\n    t.equal(dadStream.recs[2].msg, 'debug from son');\n\n    t.equal(sonStream.recs.length, 1);\n    t.equal(sonStream.recs[0].msg, 'debug from son');\n\n    t.end();\n});\n\n// issue #291\ntest('child should not lose parent \"hostname\"', function (t) {\n    var stream = new CapturingStream();\n    var dad = bunyan.createLogger({\n        name: 'hostname-test',\n        hostname: 'bar0',\n        streams: [ {\n            type: 'raw',\n            stream: stream,\n            level: 'info'\n        } ]\n    });\n    var son = dad.child({component: 'son'});\n\n    dad.info('HI');\n    son.info('hi');\n\n    t.equal(stream.recs.length, 2);\n    t.equal(stream.recs[0].hostname, 'bar0');\n    t.equal(stream.recs[1].hostname, 'bar0');\n    t.equal(stream.recs[1].component, 'son');\n\n    t.end();\n});\n"
  },
  {
    "path": "test/cli-client-req.test.js",
    "content": "/*\n * Copyright 2020 Trent Mick\n *\n * Test the bunyan CLI's handling of the \"client_req\" field.\n * \"client_req\" is a common-ish Bunyan log field from restify-clients. See:\n *      // JSSTYLED\n *      https://github.com/restify/clients/blob/85374f87db9f4469de2605b6b15632b317cc12be/lib/helpers/bunyan.js#L213\n */\n\nvar exec = require('child_process').exec;\nvar fs = require('fs');\nvar os = require('os');\nvar path = require('path');\nvar _ = require('util').format;\nvar test = require('tap').test;\n\n\n// ---- globals\n\nvar BUNYAN = path.resolve(__dirname, '../bin/bunyan');\nif (os.platform() === 'win32') {\n    BUNYAN = process.execPath + ' ' + BUNYAN;\n}\n\n\n// ---- tests\n\ntest('client_req extra newlines, client_res={} (pull #252)', function (t) {\n    var expect = [\n        /* BEGIN JSSTYLED */\n        '[2016-02-10T07:28:40.510Z] TRACE: aclientreq/23280 on danger0.local: request sent',\n        '    GET /--ping HTTP/1.1',\n        '[2016-02-10T07:28:41.419Z] TRACE: aclientreq/23280 on danger0.local: Response received',\n        '    HTTP/1.1 200 OK',\n        '    request-id: e8a5a700-cfc7-11e5-a3dc-3b85d20f26ef',\n        '    content-type: application/json'\n        /* END JSSTYLED */\n    ].join('\\n') + '\\n';\n    exec(_('%s %s/corpus/clientreqres.log', BUNYAN, __dirname),\n            function (err, stdout, stderr) {\n        t.ifError(err);\n        t.equal(stdout, expect);\n        t.end();\n    });\n});\n\n\ntest('client_req.address is not used for Host header in 2.x (issue #504)',\n        function (t) {\n    exec(_('%s %s/corpus/client-req-with-address.log', BUNYAN, __dirname),\n             function (err, stdout, stderr) {\n        t.ifError(err)\n        t.equal(stdout, [\n            // JSSTYLED\n            '[2017-05-12T23:59:15.877Z] TRACE: minfo/66266 on sharptooth.local: request sent (client_req.address=127.0.0.1)',\n            '    HEAD /dap/stor HTTP/1.1',\n            '    accept: application/json, */*',\n            '    host: foo.example.com',\n            '    date: Fri, 12 May 2017 23:59:15 GMT',\n            ''\n        ].join('\\n'));\n        t.end();\n    });\n});\n"
  },
  {
    "path": "test/cli-res.test.js",
    "content": "/*\n * Copyright 2020 Trent Mick\n *\n * Test the bunyan CLI's handling of the \"res\" field.\n */\n\nvar exec = require('child_process').exec;\nvar fs = require('fs');\nvar os = require('os');\nvar path = require('path');\nvar _ = require('util').format;\nvar test = require('tap').test;\n\n\n// ---- globals\n\nvar BUNYAN = path.resolve(__dirname, '../bin/bunyan');\nif (os.platform() === 'win32') {\n    BUNYAN = process.execPath + ' ' + BUNYAN;\n}\n\n\n// ---- tests\n\ntest('res with \"header\" string (issue #444)', function (t) {\n    var expect = [\n        /* BEGIN JSSTYLED */\n        '[2017-08-02T22:37:34.798Z]  INFO: res-header/76488 on danger0.local: response sent',\n        '    HTTP/1.1 200 OK',\n        '    Foo: bar',\n        '    Date: Wed, 02 Aug 2017 22:37:34 GMT',\n        '    Connection: keep-alive',\n        '    Content-Length: 21'\n        /* END JSSTYLED */\n    ].join('\\n') + '\\n';\n    exec(_('%s %s/corpus/res-header.log', BUNYAN, __dirname),\n            function (err, stdout, stderr) {\n        t.ifError(err);\n        t.equal(stdout, expect);\n        t.end();\n    });\n});\n\ntest('res without \"header\"', function (t) {\n    var expect = [\n        /* BEGIN JSSTYLED */\n        '[2017-08-02T22:37:34.798Z]  INFO: res-header/76488 on danger0.local: response sent',\n        '    HTTP/1.1 200 OK'\n        /* END JSSTYLED */\n    ].join('\\n') + '\\n';\n    exec(_('%s %s/corpus/res-without-header.log', BUNYAN, __dirname),\n            function (err, stdout, stderr) {\n        t.ifError(err);\n        t.equal(stdout, expect);\n        t.end();\n    });\n});\n"
  },
  {
    "path": "test/cli.test.js",
    "content": "/*\n * Copyright 2020 Trent Mick\n *\n * Test the `bunyan` CLI.\n */\n\nvar p = console.warn;\nvar exec = require('child_process').exec;\nvar fs = require('fs');\nvar os = require('os');\nvar path = require('path');\nvar _ = require('util').format;\nvar test = require('tap').test;\nvar vasync = require('vasync');\n\n\n// ---- globals\n\nvar BUNYAN = path.resolve(__dirname, '../bin/bunyan');\nif (os.platform() === 'win32') {\n    BUNYAN = process.execPath + ' ' + BUNYAN;\n}\n\n\n// ---- support stuff\n\n/**\n * Copies over all keys in `from` to `to`, or\n * to a new object if `to` is not given.\n */\nfunction objCopy(from, to) {\n    if (to === undefined) {\n        to = {};\n    }\n    for (var k in from) {\n        to[k] = from[k];\n    }\n    return to;\n}\n\n\n// ---- tests\n\ntest('--version', function (t) {\n    var version = require('../package.json').version;\n    exec(BUNYAN + ' --version', function (err, stdout, stderr) {\n        t.ifError(err)\n        t.equal(stdout, 'bunyan ' + version + '\\n');\n        t.end();\n    });\n});\n\ntest('--help', function (t) {\n    exec(BUNYAN + ' --help', function (err, stdout, stderr) {\n        t.ifError(err)\n        t.ok(stdout.indexOf('General options:') !== -1);\n        t.end();\n    });\n});\n\ntest('-h', function (t) {\n    exec(BUNYAN + ' -h', function (err, stdout, stderr) {\n        t.ifError(err)\n        t.ok(stdout.indexOf('General options:') !== -1);\n        t.end();\n    });\n});\n\ntest('--bogus', function (t) {\n    exec(BUNYAN + ' --bogus', function (err, stdout, stderr) {\n        t.ok(err, 'should error out')\n        t.equal(err.code, 1, '... with exit code 1')\n        t.end();\n    });\n});\n\ntest('simple.log', function (t) {\n    exec(_('%s %s/corpus/simple.log', BUNYAN, __dirname),\n             function (err, stdout, stderr) {\n        t.ifError(err)\n        t.equal(stdout,\n            '[2012-02-08T22:56:52.856Z]  INFO: myservice/123 on example.com: '\n            + 'My message\\n');\n        t.end();\n    });\n});\ntest('cat simple.log', function (t) {\n    exec(_('cat %s/corpus/simple.log | %s', __dirname, BUNYAN),\n        function (err, stdout, stderr) {\n            t.ifError(err)\n            t.equal(stdout,\n                /* JSSTYLED */\n                '[2012-02-08T22:56:52.856Z]  INFO: myservice/123 on example.com: My message\\n');\n            t.end();\n        }\n    );\n});\n\n// Test some local/UTC time handling by changing to a known non-UTC timezone\n// for some tests.\n//\n// I don't know how to effectively do this on Windows (at least\n// https://stackoverflow.com/questions/2611017 concurs), so we skip these on\n// Windows. Help is welcome if you know how to do this on Windows.\ntest('time TZ tests', {\n    skip: os.platform() === 'win32'\n        ? 'do not know how to set timezone on Windows' : false\n}, function (suite) {\n    // A stable 'TZ' for 'local' timezone output.\n    tzEnv = objCopy(process.env);\n    tzEnv.TZ = 'Pacific/Honolulu';\n\n    test('time: simple.log local long', function (t) {\n        exec(_('%s -o long -L %s/corpus/simple.log', BUNYAN, __dirname),\n                {env: tzEnv}, function (err, stdout, stderr) {\n            t.ifError(err)\n            t.equal(stdout,\n                // JSSTYLED\n                '[2012-02-08T12:56:52.856-10:00]  INFO: myservice/123 on example.com: '\n                + 'My message\\n');\n            t.end();\n        });\n    });\n    test('time: simple.log utc long', function (t) {\n        exec(_('%s -o long --time utc %s/corpus/simple.log', BUNYAN, __dirname),\n                {env: tzEnv}, function (err, stdout, stderr) {\n            t.ifError(err)\n            t.equal(stdout,\n                // JSSTYLED\n                '[2012-02-08T22:56:52.856Z]  INFO: myservice/123 on example.com: '\n                + 'My message\\n');\n            t.end();\n        });\n    });\n    test('time: simple.log local short', function (t) {\n        exec(_('%s -o short -L %s/corpus/simple.log', BUNYAN, __dirname),\n                {env: tzEnv}, function (err, stdout, stderr) {\n            t.ifError(err)\n            t.equal(stdout,\n                '12:56:52.856  INFO myservice: '\n                + 'My message\\n');\n            t.end();\n        });\n    });\n    test('time: simple.log utc short', function (t) {\n        exec(_('%s -o short %s/corpus/simple.log', BUNYAN, __dirname),\n                {env: tzEnv}, function (err, stdout, stderr) {\n            t.ifError(err)\n            t.equal(stdout,\n                '22:56:52.856Z  INFO myservice: '\n                + 'My message\\n');\n            t.end();\n        });\n    });\n\n    suite.end();\n});\n\ntest('simple.log with color', function (t) {\n    exec(_('%s --color %s/corpus/simple.log', BUNYAN, __dirname),\n        function (err, stdout, stderr) {\n        t.ifError(err)\n        t.equal(stdout,\n            /* JSSTYLED */\n            '[2012-02-08T22:56:52.856Z] \\u001b[36m INFO\\u001b[39m: myservice/123 on example.com: \\u001b[36mMy message\\u001b[39m\\n\\u001b[0m');\n        t.end();\n    });\n});\n\ntest('extrafield.log', function (t) {\n    exec(_('%s %s/corpus/extrafield.log', BUNYAN, __dirname),\n             function (err, stdout, stderr) {\n        t.ifError(err)\n        t.equal(stdout,\n            '[2012-02-08T22:56:52.856Z]  INFO: myservice/123 on example.com: '\n            + 'My message (extra=field)\\n');\n        t.end();\n    });\n});\ntest('extrafield.log with color', function (t) {\n    exec(_('%s --color %s/corpus/extrafield.log', BUNYAN, __dirname),\n             function (err, stdout, stderr) {\n        t.ifError(err)\n        t.equal(stdout,\n            '[2012-02-08T22:56:52.856Z] \\u001b[36m INFO\\u001b[39m: '\n            + 'myservice/123 '\n            + 'on example.com: \\u001b[36mMy message\\u001b[39m'\n            + ' (extra=field)\\n\\u001b[0m');\n        t.end();\n    });\n});\n\ntest('bogus.log', function (t) {\n    exec(_('%s %s/corpus/bogus.log', BUNYAN, __dirname),\n             function (err, stdout, stderr) {\n        t.ifError(err)\n        t.equal(stdout, 'not a JSON line\\n{\"hi\": \"there\"}\\n');\n        t.end();\n    });\n});\n\ntest('bogus.log -j', function (t) {\n    exec(_('%s -j %s/corpus/bogus.log', BUNYAN, __dirname),\n             function (err, stdout, stderr) {\n        t.ifError(err)\n        t.equal(stdout, 'not a JSON line\\n{\"hi\": \"there\"}\\n');\n        t.end();\n    });\n});\n\ntest('all.log', function (t) {\n    exec(_('%s %s/corpus/all.log', BUNYAN, __dirname),\n             function (err, stdout, stderr) {\n        // Just make sure don't blow up on this.\n        t.ifError(err)\n        t.end();\n    });\n});\n\ntest('simple.log doesnotexist1.log doesnotexist2.log', function (t) {\n    exec(_('%s %s/corpus/simple.log doesnotexist1.log doesnotexist2.log',\n                 BUNYAN, __dirname),\n        function (err, stdout, stderr) {\n            t.ok(err)\n            t.equal(err.code, 2)\n            t.equal(stdout,\n                /* JSSTYLED */\n                '[2012-02-08T22:56:52.856Z]  INFO: myservice/123 on example.com: My message\\n');\n            // Note: node v0.6.10:\n            //   ENOENT, no such file or directory 'asdf.log'\n            // but node v0.6.14:\n            //   ENOENT, open 'asdf.log'\n            // io.js 2.2 (at least):\n            //   ENOENT: no such file or directory, open 'doesnotexist1.log'\n            // in GitHub Actions windows-latest runner:\n            //   JSSTYLED\n            //   ENOENT: no such file or directory, open 'D:\\\\a\\\\node-bunyan\\\\node-bunyan\\\\doesnotexist1.log\n            var matches = [\n                /^bunyan: ENOENT.*?, open '.*?doesnotexist1.log'/m,\n                /^bunyan: ENOENT.*?, open '.*?doesnotexist2.log'/m,\n            ];\n            matches.forEach(function (match) {\n                t.ok(match.test(stderr), 'stderr matches ' + match.toString() +\n                    ', stderr=' + JSON .stringify(stderr));\n            });\n            t.end();\n        }\n    );\n});\n\ntest('multiple logs', function (t) {\n    var cmd = _('%s %s/corpus/log1.log %s/corpus/log2.log',\n        BUNYAN, __dirname, __dirname);\n    exec(cmd, function (err, stdout, stderr) {\n        t.ifError(err);\n        t.equal(stdout, [\n            /* BEGIN JSSTYLED */\n            '[2012-05-08T16:57:55.586Z]  INFO: agent1/73267 on headnode: message\\n',\n            '[2012-05-08T16:58:55.586Z]  INFO: agent2/73267 on headnode: message\\n',\n            '[2012-05-08T17:01:49.339Z]  INFO: agent2/73267 on headnode: message\\n',\n            '[2012-05-08T17:02:47.404Z]  INFO: agent2/73267 on headnode: message\\n',\n            '[2012-05-08T17:02:49.339Z]  INFO: agent1/73267 on headnode: message\\n',\n            '[2012-05-08T17:02:49.404Z]  INFO: agent1/73267 on headnode: message\\n',\n            '[2012-05-08T17:02:49.404Z]  INFO: agent1/73267 on headnode: message\\n',\n            '[2012-05-08T17:02:57.404Z]  INFO: agent2/73267 on headnode: message\\n',\n            '[2012-05-08T17:08:01.105Z]  INFO: agent2/76156 on headnode: message\\n',\n            /* END JSSTYLED */\n        ].join(''));\n        t.end();\n    });\n});\n\ntest('multiple logs, bunyan format', function (t) {\n    var cmd = _('%s -o bunyan %s/corpus/log1.log %s/corpus/log2.log',\n        BUNYAN, __dirname, __dirname);\n    exec(cmd, function (err, stdout, stderr) {\n        t.ifError(err);\n        t.equal(stdout, [\n            /* BEGIN JSSTYLED */\n            '{\"name\":\"agent1\",\"pid\":73267,\"hostname\":\"headnode\",\"level\":30,\"msg\":\"message\",\"time\":\"2012-05-08T16:57:55.586Z\",\"v\":0}',\n            '{\"name\":\"agent2\",\"pid\":73267,\"hostname\":\"headnode\",\"level\":30,\"msg\":\"message\",\"time\":\"2012-05-08T16:58:55.586Z\",\"v\":0}',\n            '{\"name\":\"agent2\",\"pid\":73267,\"hostname\":\"headnode\",\"level\":30,\"msg\":\"message\",\"time\":\"2012-05-08T17:01:49.339Z\",\"v\":0}',\n            '{\"name\":\"agent2\",\"pid\":73267,\"hostname\":\"headnode\",\"level\":30,\"msg\":\"message\",\"time\":\"2012-05-08T17:02:47.404Z\",\"v\":0}',\n            '{\"name\":\"agent1\",\"pid\":73267,\"hostname\":\"headnode\",\"level\":30,\"msg\":\"message\",\"time\":\"2012-05-08T17:02:49.339Z\",\"v\":0}',\n            '{\"name\":\"agent1\",\"pid\":73267,\"hostname\":\"headnode\",\"level\":30,\"msg\":\"message\",\"time\":\"2012-05-08T17:02:49.404Z\",\"v\":0}',\n            '{\"name\":\"agent1\",\"pid\":73267,\"hostname\":\"headnode\",\"level\":30,\"msg\":\"message\",\"time\":\"2012-05-08T17:02:49.404Z\",\"v\":0}',\n            '{\"name\":\"agent2\",\"pid\":73267,\"hostname\":\"headnode\",\"level\":30,\"msg\":\"message\",\"time\":\"2012-05-08T17:02:57.404Z\",\"v\":0}',\n            '{\"name\":\"agent2\",\"pid\":76156,\"hostname\":\"headnode\",\"level\":30,\"msg\":\"message\",\"time\":\"2012-05-08T17:08:01.105Z\",\"v\":0}',\n            ''\n            /* END JSSTYLED */\n        ].join('\\n'));\n        t.end();\n    });\n});\n\ntest('log1.log.gz', function (t) {\n    exec(_('%s %s/corpus/log1.log.gz', BUNYAN, __dirname),\n             function (err, stdout, stderr) {\n        t.ifError(err);\n        t.equal(stdout, [\n            /* BEGIN JSSTYLED */\n            '[2012-05-08T16:57:55.586Z]  INFO: agent1/73267 on headnode: message\\n',\n            '[2012-05-08T17:02:49.339Z]  INFO: agent1/73267 on headnode: message\\n',\n            '[2012-05-08T17:02:49.404Z]  INFO: agent1/73267 on headnode: message\\n',\n            '[2012-05-08T17:02:49.404Z]  INFO: agent1/73267 on headnode: message\\n',\n            /* END JSSTYLED */\n        ].join(''));\n        t.end();\n    });\n});\n\ntest('mixed text and gzip logs', function (t) {\n    var cmd = _('%s %s/corpus/log1.log.gz %s/corpus/log2.log',\n        BUNYAN, __dirname, __dirname);\n    exec(cmd, function (err, stdout, stderr) {\n        t.ifError(err);\n        t.equal(stdout, [\n            /* BEGIN JSSTYLED */\n            '[2012-05-08T16:57:55.586Z]  INFO: agent1/73267 on headnode: message\\n',\n            '[2012-05-08T16:58:55.586Z]  INFO: agent2/73267 on headnode: message\\n',\n            '[2012-05-08T17:01:49.339Z]  INFO: agent2/73267 on headnode: message\\n',\n            '[2012-05-08T17:02:47.404Z]  INFO: agent2/73267 on headnode: message\\n',\n            '[2012-05-08T17:02:49.339Z]  INFO: agent1/73267 on headnode: message\\n',\n            '[2012-05-08T17:02:49.404Z]  INFO: agent1/73267 on headnode: message\\n',\n            '[2012-05-08T17:02:49.404Z]  INFO: agent1/73267 on headnode: message\\n',\n            '[2012-05-08T17:02:57.404Z]  INFO: agent2/73267 on headnode: message\\n',\n            '[2012-05-08T17:08:01.105Z]  INFO: agent2/76156 on headnode: message\\n',\n            /* END JSSTYLED */\n        ].join(''));\n        t.end();\n    });\n});\n\ntest('--level 40', function (t) {\n    expect = [\n        /* BEGIN JSSTYLED */\n        '# levels\\n',\n        '[2012-02-08T22:56:53.856Z]  WARN: myservice/123 on example.com: My message\\n',\n        '[2012-02-08T22:56:54.856Z] ERROR: myservice/123 on example.com: My message\\n',\n        '[2012-02-08T22:56:55.856Z] LVL55: myservice/123 on example.com: My message\\n',\n        '[2012-02-08T22:56:56.856Z] FATAL: myservice/123 on example.com: My message\\n',\n        '\\n',\n        '# extra fields\\n',\n        '\\n',\n        '# bogus\\n',\n        'not a JSON line\\n',\n        '{\"hi\": \"there\"}\\n'\n        /* END JSSTYLED */\n    ].join('');\n    exec(_('%s -l 40 %s/corpus/all.log', BUNYAN, __dirname),\n             function (err, stdout, stderr) {\n        t.ifError(err);\n        t.equal(stdout, expect);\n        exec(_('%s --level 40 %s/corpus/all.log', BUNYAN, __dirname),\n                 function (err, stdout, stderr) {\n            t.ifError(err);\n            t.equal(stdout, expect);\n            t.end();\n        });\n    });\n});\n\ntest('--condition \"this.level === 10 && this.pid === 123\"', function (t) {\n    var expect = [\n        '# levels\\n',\n        /* JSSTYLED */\n        '[2012-02-08T22:56:50.856Z] TRACE: myservice/123 on example.com: My message\\n',\n        '\\n',\n        '# extra fields\\n',\n        '\\n',\n        '# bogus\\n',\n        'not a JSON line\\n',\n        '{\"hi\": \"there\"}\\n'\n    ].join('');\n    var cmd = _('%s -c \"this.level === 10 && this.pid === 123\"'\n                + ' %s/corpus/all.log', BUNYAN, __dirname);\n    exec(cmd, function (err, stdout, stderr) {\n        t.ifError(err);\n        t.equal(stdout, expect);\n        var cmd = _(\n            '%s --condition \"this.level === 10 && this.pid === 123\"'\n            + ' %s/corpus/all.log', BUNYAN, __dirname);\n        exec(cmd, function (err, stdout, stderr) {\n            t.ifError(err);\n            t.equal(stdout, expect);\n            t.end();\n        });\n    });\n});\n\ntest('--condition \"this.level === TRACE', function (t) {\n    var expect = [\n        '# levels\\n',\n        /* JSSTYLED */\n        '[2012-02-08T22:56:50.856Z] TRACE: myservice/123 on example.com: My message\\n',\n        '\\n',\n        '# extra fields\\n',\n        '\\n',\n        '# bogus\\n',\n        'not a JSON line\\n',\n        '{\"hi\": \"there\"}\\n'\n    ].join('');\n    var cmd = _('%s -c \"this.level === TRACE\" %s/corpus/all.log',\n        BUNYAN, __dirname);\n    exec(cmd, function (err, stdout, stderr) {\n        t.ifError(err);\n        t.equal(stdout, expect);\n        t.end();\n    });\n});\n\n// multiple\ntest('multiple --conditions', function (t) {\n    var expect = [\n        '# levels\\n',\n        /* JSSTYLED */\n        '[2012-02-08T22:56:53.856Z]  WARN: myservice/123 on example.com: My message\\n',\n        '\\n',\n        '# extra fields\\n',\n        '\\n',\n        '# bogus\\n',\n        'not a JSON line\\n',\n        '{\"hi\": \"there\"}\\n'\n    ].join('');\n    exec(_('%s %s/corpus/all.log -c \"this.level === 40\" -c \"this.pid === 123\"',\n            BUNYAN, __dirname), function (err, stdout, stderr) {\n        t.ifError(err);\n        t.equal(stdout, expect);\n        t.end();\n    });\n});\n\n// https://github.com/trentm/node-bunyan/issues/30\n//\n// One of the records in corpus/withreq.log has a 'req'\n// field with no 'headers'. Ditto for the 'res' field.\ntest('robust req handling', function (t) {\n    var expect = [\n        /* BEGIN JSSTYLED */\n        '[2012-08-08T10:25:47.636Z] DEBUG: amon-master/12859 on 9724a190-27b6-4fd8-830b-a574f839c67d: headAgentProbes respond (req_id=cce79d15-ffc2-487c-a4e4-e940bdaac31e, route=HeadAgentProbes, contentMD5=11FxOYiYfpMxmANj4kGJzg==)',\n        '[2012-08-08T10:25:47.637Z]  INFO: amon-master/12859 on 9724a190-27b6-4fd8-830b-a574f839c67d: HeadAgentProbes handled: 200 (req_id=cce79d15-ffc2-487c-a4e4-e940bdaac31e, audit=true, remoteAddress=10.2.207.2, remotePort=50394, latency=3, secure=false, _audit=true, req.version=*)',\n        '    HEAD /agentprobes?agent=ccf92af9-0b24-46b6-ab60-65095fdd3037 HTTP/1.1',\n        '    accept: application/json',\n        '    content-type: application/json',\n        '    host: 10.2.207.16',\n        '    connection: keep-alive',\n        '    --',\n        '    HTTP/1.1 200 OK',\n        '    content-md5: 11FxOYiYfpMxmANj4kGJzg==',\n        '    access-control-allow-origin: *',\n        '    access-control-allow-headers: Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version',\n        '    access-control-allow-methods: HEAD',\n        '    access-control-expose-headers: X-Api-Version, X-Request-Id, X-Response-Time',\n        '    connection: Keep-Alive',\n        '    date: Wed, 08 Aug 2012 10:25:47 GMT',\n        '    server: Amon Master/1.0.0',\n        '    x-request-id: cce79d15-ffc2-487c-a4e4-e940bdaac31e',\n        '    x-response-time: 3',\n        '    --',\n        '    route: {',\n        '      \"name\": \"HeadAgentProbes\",',\n        '      \"version\": false',\n        '    }',\n        '[2012-08-08T10:25:47.637Z]  INFO: amon-master/12859 on 9724a190-27b6-4fd8-830b-a574f839c67d: HeadAgentProbes handled: 200 (req_id=cce79d15-ffc2-487c-a4e4-e940bdaac31e, audit=true, remoteAddress=10.2.207.2, remotePort=50394, latency=3, secure=false, _audit=true, req.version=*)',\n        '    HEAD /agentprobes?agent=ccf92af9-0b24-46b6-ab60-65095fdd3037 HTTP/1.1',\n        '    --',\n        '    HTTP/1.1 200 OK',\n        '    --',\n        '    route: {',\n        '      \"name\": \"HeadAgentProbes\",',\n        '      \"version\": false',\n        '    }'\n        /* END JSSTYLED */\n    ].join('\\n') + '\\n';\n    exec(_('%s %s/corpus/withreq.log', BUNYAN, __dirname),\n             function (err, stdout, stderr) {\n        t.ifError(err);\n        t.equal(stdout, expect);\n        t.end();\n    });\n});\n\n// Some past crashes from issues.\ntest('should not crash on corpus/old-crashers/*.log', function (t) {\n    var oldCrashers = fs.readdirSync(\n        path.resolve(__dirname, 'corpus/old-crashers'))\n        .filter(function (f) { return f.slice(-4) === '.log'; });\n    vasync.forEachPipeline({\n        inputs: oldCrashers,\n        func: function (logName, next) {\n            exec(_('%s %s/corpus/old-crashers/%s', BUNYAN, __dirname, logName),\n                    function (err, stdout, stderr) {\n                next(err);\n            });\n        }\n    }, function (err, results) {\n        t.ifError(err);\n        t.end();\n    });\n});\n\n\ntest('should only show nonempty response bodies', function (t) {\n    var expect = [\n        /* BEGIN JSSTYLED */\n        '[2016-02-10T07:28:41.419Z]  INFO: myservice/123 on example.com: UnauthorizedError',\n        '    HTTP/1.1 401 Unauthorized',\n        '    content-type: text/plain',\n        '    date: Sat, 07 Mar 2015 06:58:43 GMT',\n        '[2016-02-10T07:28:41.419Z]  INFO: myservice/123 on example.com: hello',\n        '    HTTP/1.1 200 OK',\n        '    content-type: text/plain',\n        '    content-length: 0',\n        '    date: Sat, 07 Mar 2015 06:58:43 GMT',\n        '    ',\n        '    hello',\n        '[2016-02-10T07:28:41.419Z]  INFO: myservice/123 on example.com: UnauthorizedError',\n        '    HTTP/1.1 401 Unauthorized',\n        '    content-type: text/plain',\n        '    date: Sat, 07 Mar 2015 06:58:43 GMT'\n        /* END JSSTYLED */\n    ].join('\\n') + '\\n';\n    exec(_('%s %s/corpus/content-length-0-res.log', BUNYAN, __dirname),\n            function (err, stdout, stderr) {\n        t.ifError(err);\n        t.equal(stdout, expect);\n        t.end();\n    });\n});\n"
  },
  {
    "path": "test/corpus/all.log",
    "content": "# levels\n{\"name\":\"myservice\",\"pid\":123,\"hostname\":\"example.com\",\"level\":10,\"msg\":\"My message\",\"time\":\"2012-02-08T22:56:50.856Z\",\"v\":0}\n{\"name\":\"myservice\",\"pid\":123,\"hostname\":\"example.com\",\"level\":20,\"msg\":\"My message\",\"time\":\"2012-02-08T22:56:51.856Z\",\"v\":0}\n{\"name\":\"myservice\",\"pid\":123,\"hostname\":\"example.com\",\"level\":30,\"msg\":\"My message\",\"time\":\"2012-02-08T22:56:52.856Z\",\"v\":0}\n{\"name\":\"myservice\",\"pid\":123,\"hostname\":\"example.com\",\"level\":40,\"msg\":\"My message\",\"time\":\"2012-02-08T22:56:53.856Z\",\"v\":0}\n{\"name\":\"myservice\",\"pid\":123,\"hostname\":\"example.com\",\"level\":50,\"msg\":\"My message\",\"time\":\"2012-02-08T22:56:54.856Z\",\"v\":0}\n{\"name\":\"myservice\",\"pid\":123,\"hostname\":\"example.com\",\"level\":55,\"msg\":\"My message\",\"time\":\"2012-02-08T22:56:55.856Z\",\"v\":0}\n{\"name\":\"myservice\",\"pid\":123,\"hostname\":\"example.com\",\"level\":60,\"msg\":\"My message\",\"time\":\"2012-02-08T22:56:56.856Z\",\"v\":0}\n\n# extra fields\n{\"name\":\"myservice\",\"pid\":123,\"hostname\":\"example.com\",\"level\":30,\"one\":\"short\",\"msg\":\"My message\",\"time\":\"2012-02-08T22:56:52.856Z\",\"v\":0}\n{\"name\":\"myservice\",\"pid\":123,\"hostname\":\"example.com\",\"level\":30,\"two\":\"short with space\",\"msg\":\"My message\",\"time\":\"2012-02-08T22:56:52.856Z\",\"v\":0}\n{\"name\":\"myservice\",\"pid\":123,\"hostname\":\"example.com\",\"level\":30,\"three\":\"multi\\nline\",\"msg\":\"My message\",\"time\":\"2012-02-08T22:56:52.856Z\",\"v\":0}\n{\"name\":\"myservice\",\"pid\":123,\"hostname\":\"example.com\",\"level\":30,\"four\":\"over 50 chars long long long long long long long long long\",\"msg\":\"My message\",\"time\":\"2012-02-08T22:56:52.856Z\",\"v\":0}\n{\"name\":\"myservice\",\"pid\":123,\"hostname\":\"example.com\",\"level\":30,\"five\":{\"a\": \"json object\"},\"msg\":\"My message\",\"time\":\"2012-02-08T22:56:52.856Z\",\"v\":0}\n{\"name\":\"myservice\",\"pid\":123,\"hostname\":\"example.com\",\"level\":30,\"six\":[\"a\", \"json\", \"array\"],\"msg\":\"My message\",\"time\":\"2012-02-08T22:56:52.856Z\",\"v\":0}\n\n# bogus\nnot a JSON line\n{\"hi\": \"there\"}\n"
  },
  {
    "path": "test/corpus/bogus.log",
    "content": "not a JSON line\n{\"hi\": \"there\"}\n"
  },
  {
    "path": "test/corpus/client-req-with-address.log",
    "content": "{\"name\":\"minfo\",\"hostname\":\"sharptooth.local\",\"pid\":66266,\"level\":10,\"client_req\":{\"method\":\"HEAD\",\"url\":\"/dap/stor\",\"address\":\"127.0.0.1\",\"headers\":{\"accept\":\"application/json, */*\",\"host\":\"foo.example.com\",\"date\":\"Fri, 12 May 2017 23:59:15 GMT\"}},\"msg\":\"request sent\",\"time\":\"2017-05-12T23:59:15.877Z\",\"v\":0}\n"
  },
  {
    "path": "test/corpus/clientreqres.log",
    "content": "{\"name\":\"aclientreq\",\"hostname\":\"danger0.local\",\"pid\":23280,\"level\":10,\"client_req\":{\"method\":\"GET\",\"url\":\"/--ping\"},\"msg\":\"request sent\",\"time\":\"2016-02-10T07:28:40.510Z\",\"v\":0}\n{\"name\":\"aclientreq\",\"hostname\":\"danger0.local\",\"pid\":23280,\"level\":10,\"client_res\":{\"statusCode\":200,\"headers\":{\"request-id\":\"e8a5a700-cfc7-11e5-a3dc-3b85d20f26ef\",\"content-type\":\"application/json\"}},\"msg\":\"Response received\",\"time\":\"2016-02-10T07:28:41.419Z\",\"v\":0}\n"
  },
  {
    "path": "test/corpus/content-length-0-res.log",
    "content": "{\"name\":\"myservice\",\"hostname\":\"example.com\",\"pid\":123,\"level\":30,\"client_res\":{\"statusCode\":401,\"headers\":{\"content-type\":\"text/plain\",\"date\":\"Sat, 07 Mar 2015 06:58:43 GMT\"},\"body\":\"\"},\"msg\":\"UnauthorizedError\",\"time\":\"2016-02-10T07:28:41.419Z\",\"v\":0}\n{\"name\":\"myservice\",\"hostname\":\"example.com\",\"pid\":123,\"level\":30,\"client_res\":{\"statusCode\":200,\"headers\":{\"content-type\":\"text/plain\",\"content-length\":0,\"date\":\"Sat, 07 Mar 2015 06:58:43 GMT\"},\"body\":\"hello\"},\"msg\":\"hello\",\"time\":\"2016-02-10T07:28:41.419Z\",\"v\":0}\n{\"name\":\"myservice\",\"hostname\":\"example.com\",\"pid\":123,\"level\":30,\"client_res\":{\"statusCode\":401,\"headers\":{\"content-type\":\"text/plain\",\"date\":\"Sat, 07 Mar 2015 06:58:43 GMT\"}},\"msg\":\"UnauthorizedError\",\"time\":\"2016-02-10T07:28:41.419Z\",\"v\":0}\n"
  },
  {
    "path": "test/corpus/extrafield.log",
    "content": "{\"name\":\"myservice\",\"pid\":123,\"hostname\":\"example.com\",\"level\":30,\"extra\":\"field\",\"msg\":\"My message\",\"time\":\"2012-02-08T22:56:52.856Z\",\"v\":0}\n"
  },
  {
    "path": "test/corpus/log1.log",
    "content": "{\"name\":\"agent1\",\"pid\":73267,\"hostname\":\"headnode\",\"level\":30,\"msg\":\"message\",\"time\":\"2012-05-08T16:57:55.586Z\",\"v\":0}\n{\"name\":\"agent1\",\"pid\":73267,\"hostname\":\"headnode\",\"level\":30,\"msg\":\"message\",\"time\":\"2012-05-08T17:02:49.339Z\",\"v\":0}\n{\"name\":\"agent1\",\"pid\":73267,\"hostname\":\"headnode\",\"level\":30,\"msg\":\"message\",\"time\":\"2012-05-08T17:02:49.404Z\",\"v\":0}\n{\"name\":\"agent1\",\"pid\":73267,\"hostname\":\"headnode\",\"level\":30,\"msg\":\"message\",\"time\":\"2012-05-08T17:02:49.404Z\",\"v\":0}\n"
  },
  {
    "path": "test/corpus/log2.log",
    "content": "{\"name\":\"agent2\",\"pid\":73267,\"hostname\":\"headnode\",\"level\":30,\"msg\":\"message\",\"time\":\"2012-05-08T16:58:55.586Z\",\"v\":0}\n{\"name\":\"agent2\",\"pid\":73267,\"hostname\":\"headnode\",\"level\":30,\"msg\":\"message\",\"time\":\"2012-05-08T17:01:49.339Z\",\"v\":0}\n{\"name\":\"agent2\",\"pid\":73267,\"hostname\":\"headnode\",\"level\":30,\"msg\":\"message\",\"time\":\"2012-05-08T17:02:47.404Z\",\"v\":0}\n{\"name\":\"agent2\",\"pid\":73267,\"hostname\":\"headnode\",\"level\":30,\"msg\":\"message\",\"time\":\"2012-05-08T17:02:57.404Z\",\"v\":0}\n{\"name\":\"agent2\",\"pid\":76156,\"hostname\":\"headnode\",\"level\":30,\"msg\":\"message\",\"time\":\"2012-05-08T17:08:01.105Z\",\"v\":0}\n"
  },
  {
    "path": "test/corpus/non-object-res.log",
    "content": "{\"name\":\"cnapi.get_existing_nics\",\"job_uuid\":\"3499b13e-dbca-4331-b13a-f164c0da320a\",\"hostname\":\"710c784f-6aa5-428c-9074-e046c3af884e\",\"pid\":24440,\"level\":30,\"nic\":\"<unknown>\",\"res\":\"error: Unknown nic \\\"020820d753e0\\\"\",\"msg\":\"got existing: 02:08:20:d7:53:e0\",\"time\":\"2012-10-10T16:14:07.610Z\",\"v\":0}\n"
  },
  {
    "path": "test/corpus/old-crashers/README.md",
    "content": "Log lines that used to crash `bunyan`.\nTypically the file name is the bunyan issue number.\n"
  },
  {
    "path": "test/corpus/res-header.log",
    "content": "{\"name\":\"res-header\",\"hostname\":\"danger0.local\",\"pid\":76488,\"level\":30,\"res\":{\"statusCode\":200,\"header\":\"HTTP/1.1 200 OK\\r\\nFoo: bar\\r\\nDate: Wed, 02 Aug 2017 22:37:34 GMT\\r\\nConnection: keep-alive\\r\\nContent-Length: 21\\r\\n\\r\\n\"},\"msg\":\"response sent\",\"time\":\"2017-08-02T22:37:34.798Z\",\"v\":0}\n"
  },
  {
    "path": "test/corpus/res-without-header.log",
    "content": "{\"name\":\"res-header\",\"hostname\":\"danger0.local\",\"pid\":76488,\"level\":30,\"res\":{\"statusCode\":200},\"msg\":\"response sent\",\"time\":\"2017-08-02T22:37:34.798Z\",\"v\":0}\n"
  },
  {
    "path": "test/corpus/simple.log",
    "content": "{\"name\":\"myservice\",\"pid\":123,\"hostname\":\"example.com\",\"level\":30,\"msg\":\"My message\",\"time\":\"2012-02-08T22:56:52.856Z\",\"v\":0}\n"
  },
  {
    "path": "test/corpus/withreq.log",
    "content": "{\"name\":\"amon-master\",\"hostname\":\"9724a190-27b6-4fd8-830b-a574f839c67d\",\"pid\":12859,\"route\":\"HeadAgentProbes\",\"req_id\":\"cce79d15-ffc2-487c-a4e4-e940bdaac31e\",\"level\":20,\"contentMD5\":\"11FxOYiYfpMxmANj4kGJzg==\",\"msg\":\"headAgentProbes respond\",\"time\":\"2012-08-08T10:25:47.636Z\",\"v\":0}\n{\"name\":\"amon-master\",\"hostname\":\"9724a190-27b6-4fd8-830b-a574f839c67d\",\"pid\":12859,\"audit\":true,\"level\":30,\"remoteAddress\":\"10.2.207.2\",\"remotePort\":50394,\"req_id\":\"cce79d15-ffc2-487c-a4e4-e940bdaac31e\",\"req\":{\"method\":\"HEAD\",\"url\":\"/agentprobes?agent=ccf92af9-0b24-46b6-ab60-65095fdd3037\",\"headers\":{\"accept\":\"application/json\",\"content-type\":\"application/json\",\"host\":\"10.2.207.16\",\"connection\":\"keep-alive\"},\"httpVersion\":\"1.1\",\"trailers\":{},\"version\":\"*\"},\"res\":{\"statusCode\":200,\"headers\":{\"content-md5\":\"11FxOYiYfpMxmANj4kGJzg==\",\"access-control-allow-origin\":\"*\",\"access-control-allow-headers\":\"Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version\",\"access-control-allow-methods\":\"HEAD\",\"access-control-expose-headers\":\"X-Api-Version, X-Request-Id, X-Response-Time\",\"connection\":\"Keep-Alive\",\"date\":\"Wed, 08 Aug 2012 10:25:47 GMT\",\"server\":\"Amon Master/1.0.0\",\"x-request-id\":\"cce79d15-ffc2-487c-a4e4-e940bdaac31e\",\"x-response-time\":3},\"trailer\":false},\"route\":{\"name\":\"HeadAgentProbes\",\"version\":false},\"latency\":3,\"secure\":false,\"_audit\":true,\"msg\":\"HeadAgentProbes handled: 200\",\"time\":\"2012-08-08T10:25:47.637Z\",\"v\":0}\n{\"name\":\"amon-master\",\"hostname\":\"9724a190-27b6-4fd8-830b-a574f839c67d\",\"pid\":12859,\"audit\":true,\"level\":30,\"remoteAddress\":\"10.2.207.2\",\"remotePort\":50394,\"req_id\":\"cce79d15-ffc2-487c-a4e4-e940bdaac31e\",\"req\":{\"method\":\"HEAD\",\"url\":\"/agentprobes?agent=ccf92af9-0b24-46b6-ab60-65095fdd3037\",\"httpVersion\":\"1.1\",\"trailers\":{},\"version\":\"*\"},\"res\":{\"statusCode\":200,\"trailer\":false},\"route\":{\"name\":\"HeadAgentProbes\",\"version\":false},\"latency\":3,\"secure\":false,\"_audit\":true,\"msg\":\"HeadAgentProbes handled: 200\",\"time\":\"2012-08-08T10:25:47.637Z\",\"v\":0}\n"
  },
  {
    "path": "test/ctor.test.js",
    "content": "/*\n * Copyright 2020 Trent Mick\n *\n * Test type checking on creation of the Logger.\n */\n\nvar test = require('tap').test;\n\nvar bunyan = require('../lib/bunyan')\nvar Logger = bunyan;\n\n\ntest('ensure Logger creation options', function (t) {\n    t.throws(function () { new Logger(); },\n        /options \\(object\\) is required/,\n        'no options should throw');\n\n    t.throws(function () { new Logger({}); },\n        /options\\.name \\(string\\) is required/,\n        'no options.name should throw');\n\n    t.doesNotThrow(function () { new Logger({name: 'foo'}); },\n        'just options.name should be sufficient');\n\n    var options = {name: 'foo', stream: process.stdout, streams: []};\n    t.throws(function () { new Logger(options); },\n        /* JSSTYLED */\n        /cannot mix \"streams\" and \"stream\" options/,\n        'cannot use \"stream\" and \"streams\"');\n\n    // https://github.com/trentm/node-bunyan/issues/3\n    options = {name: 'foo', streams: {}};\n    t.throws(function () { new Logger(options); },\n        /invalid options.streams: must be an array/,\n        '\"streams\" must be an array');\n\n    options = {name: 'foo', serializers: 'a string'};\n    t.throws(function () { new Logger(options); },\n        /invalid options.serializers: must be an object/,\n        '\"serializers\" cannot be a string');\n\n    options = {name: 'foo', serializers: [1, 2, 3]};\n    t.throws(function () { new Logger(options); },\n        /invalid options.serializers: must be an object/,\n        '\"serializers\" cannot be an array');\n\n    t.end();\n});\n\n\ntest('ensure Logger constructor is safe without new', function (t) {\n    t.doesNotThrow(function () { Logger({name: 'foo'}); },\n        'constructor should call self with new if necessary');\n\n    t.end();\n});\n\n\ntest('ensure Logger creation options (createLogger)', function (t) {\n    t.throws(function () { bunyan.createLogger(); },\n        /options \\(object\\) is required/,\n        'no options should throw');\n\n    t.throws(function () { bunyan.createLogger({}); },\n        /options\\.name \\(string\\) is required/,\n        'no options.name should throw');\n\n    t.doesNotThrow(function () { bunyan.createLogger({name: 'foo'}); },\n        'just options.name should be sufficient');\n\n    var options = {name: 'foo', stream: process.stdout, streams: []};\n    t.throws(function () { bunyan.createLogger(options); },\n        /* JSSTYLED */\n        /cannot mix \"streams\" and \"stream\" options/,\n        'cannot use \"stream\" and \"streams\"');\n\n    // https://github.com/trentm/node-bunyan/issues/3\n    options = {name: 'foo', streams: {}};\n    t.throws(function () { bunyan.createLogger(options); },\n        /invalid options.streams: must be an array/,\n        '\"streams\" must be an array');\n\n    options = {name: 'foo', serializers: 'a string'};\n    t.throws(function () { bunyan.createLogger(options); },\n        /invalid options.serializers: must be an object/,\n        '\"serializers\" cannot be a string');\n\n    options = {name: 'foo', serializers: [1, 2, 3]};\n    t.throws(function () { bunyan.createLogger(options); },\n        /invalid options.serializers: must be an object/,\n        '\"serializers\" cannot be an array');\n\n    t.end();\n});\n\n\ntest('ensure Logger child() options', function (t) {\n    var log = new Logger({name: 'foo'});\n\n    t.doesNotThrow(function () { log.child(); },\n        'no options should be fine');\n\n    t.doesNotThrow(function () { log.child({}); },\n        'empty options should be fine too');\n\n    t.throws(function () { log.child({name: 'foo'}); },\n        /invalid options.name: child cannot set logger name/,\n        'child cannot change name');\n\n    var options = {stream: process.stdout, streams: []};\n    t.throws(function () { log.child(options); },\n        /* JSSTYLED */\n        /cannot mix \"streams\" and \"stream\" options/,\n        'cannot use \"stream\" and \"streams\"');\n\n    // https://github.com/trentm/node-bunyan/issues/3\n    options = {streams: {}};\n    t.throws(function () { log.child(options); },\n        /invalid options.streams: must be an array/,\n        '\"streams\" must be an array');\n\n    options = {serializers: 'a string'};\n    t.throws(function () { log.child(options); },\n        /invalid options.serializers: must be an object/,\n        '\"serializers\" cannot be a string');\n\n    options = {serializers: [1, 2, 3]};\n    t.throws(function () { log.child(options); },\n        /invalid options.serializers: must be an object/,\n        '\"serializers\" cannot be an array');\n\n    t.end();\n});\n\n\ntest('ensure Logger() rejects non-Logger parents', function (t) {\n    var dad = new Logger({name: 'dad', streams: []});\n\n    t.throws(function () { new Logger({}, {}); },\n        /invalid Logger creation: do not pass a second arg/,\n        'Logger arguments must be valid');\n\n    t.doesNotThrow(function () { new Logger(dad, {}); },\n        'Logger allows Logger instance as parent');\n\n    t.end();\n});\n"
  },
  {
    "path": "test/cycles.test.js",
    "content": "/*\n * Copyright 2020 Trent Mick.\n *\n * Make sure cycles are safe.\n */\n\nvar Logger = require('../lib/bunyan.js');\nvar test = require('tap').test;\n\n\nvar Stream = require('stream').Stream;\nvar outstr = new Stream;\noutstr.writable = true;\nvar output = [];\noutstr.write = function (c) {\n    output.push(JSON.parse(c + ''));\n};\noutstr.end = function (c) {\n    if (c) this.write(c);\n    this.emit('end');\n};\n\nvar expect =\n        [\n            {\n                'name': 'blammo',\n                'level': 30,\n                'msg': 'bango { bang: \\'boom\\', KABOOM: [Circular] }',\n                'v': 0\n            },\n            {\n                'name': 'blammo',\n                'level': 30,\n                'msg': 'kaboom { bang: \\'boom\\', KABOOM: [Circular] }',\n                'v': 0\n            },\n            {\n                'name': 'blammo',\n                'level': 30,\n                'bang': 'boom',\n                'KABOOM': {\n                    'bang': 'boom',\n                    'KABOOM': '[Circular]'\n                },\n                'msg': '',\n                'v': 0\n            }\n        ];\n\nvar log = new Logger({\n    name: 'blammo',\n    streams: [\n        {\n            type: 'stream',\n            level: 'info',\n            stream: outstr\n        }\n    ]\n});\n\ntest('cycles', function (t) {\n    var rec;\n\n    outstr.on('end', function () {\n        output.forEach(function (o, i) {\n            // Drop variable parts for comparison.\n            delete o.hostname;\n            delete o.pid;\n            delete o.time;\n\n            // In change https://github.com/nodejs/node/pull/27685 (part of\n            // node v14), how objects with circular references are stringified\n            // with `util.inspect` changed.\n            if (Number(process.versions.node.split('.')[0]) >= 14) {\n                expect[i].msg = expect[i].msg.replace(\n                    // JSSTYLED\n                    /{ bang: 'boom', KABOOM: \\[Circular\\] }/,\n                    '<ref *1> { bang: \\'boom\\', KABOOM: [Circular *1] }'\n                );\n            }\n\n            t.equal(JSON.stringify(o), JSON.stringify(expect[i]),\n                'log record ' + i + ' matches');\n        });\n        t.end();\n    });\n\n    var obj = { bang: 'boom' };\n    obj.KABOOM = obj;  // This creates a circular reference.\n\n    log.info('bango', obj);\n    log.info('kaboom', obj.KABOOM);\n    log.info(obj);\n\n    t.ok('did not throw');\n\n    outstr.end();\n});\n"
  },
  {
    "path": "test/dtrace/dtrace.test.js",
    "content": "/*\n * Copyright 2020 Trent Mick\n *\n * If available, test dtrace support.\n */\n\nvar spawn = require('child_process').spawn;\nvar format = require('util').format;\nvar test = require('tap').test;\n\nvar bunyan = require('../../lib/bunyan');\n\n\n// Determine if we can run the dtrace tests.\nvar dtracePlats = ['sunos', 'darwin', 'freebsd'];\nvar runDtraceTests = true;\ntry {\n    require('dtrace-provider');\n} catch (e) {\n    console.log('# skip dtrace tests: no dtrace-provider module');\n    runDtraceTests = false;\n}\nif (!runDtraceTests) {\n    /* pass through */\n} else if (dtracePlats.indexOf(process.platform) === -1) {\n    console.log('# skip dtrace tests: not on a platform with dtrace');\n    runDtraceTests = false;\n} else if (process.env.SKIP_DTRACE) {\n    console.log('# skip dtrace tests: SKIP_DTRACE envvar set');\n    runDtraceTests = false;\n} else if (process.getgid() !== 0) {\n    console.log('# skip dtrace tests: gid is not 0, run with `sudo`');\n    runDtraceTests = false;\n}\nif (runDtraceTests) {\n\n\ntest('basic dtrace', function (t) {\n    var argv = ['dtrace', '-Z', '-x', 'strsize=4k', '-qn',\n        'bunyan$target:::log-*{printf(\"%s\", copyinstr(arg0))}',\n        '-c', format('node %s/log-some.js', __dirname)];\n    var dtrace = spawn(argv[0], argv.slice(1));\n    //console.error('ARGV: %j', argv);\n    //console.error('CMD: %s', argv.join(' '));\n\n    var traces = [];\n    dtrace.stdout.on('data', function (data) {\n        //console.error('DTRACE STDOUT:', data.toString());\n        traces.push(data.toString());\n    });\n    dtrace.stderr.on('data', function (data) {\n        console.error('DTRACE STDERR:', data.toString());\n    });\n    dtrace.on('exit', function (code) {\n        t.notOk(code, 'dtrace exited cleanly');\n        traces = traces.join('').split('\\n')\n            .filter(function (t) { return t.trim().length })\n            .map(function (t) { return JSON.parse(t) });\n        t.equal(traces.length, 2, 'got 2 log records');\n        if (traces.length) {\n            t.equal(traces[0].level, bunyan.DEBUG);\n            t.equal(traces[0].foo, 'bar');\n            t.equal(traces[1].level, bunyan.TRACE);\n            t.equal(traces[1].msg, 'hi at trace');\n        }\n        t.end();\n    });\n});\n\n\n/*\n * Run a logger that logs a couple records every second.\n * Then run `bunyan -p PID` to capture.\n * Let those run for a few seconds to ensure dtrace has time to attach and\n * capture something.\n */\ntest('bunyan -p', function (t) {\n    var p = spawn('node', [__dirname + '/log-some-loop.js']);\n\n    var bunyanP = spawn('node',\n        [__dirname + '/../bin/bunyan', '-p', String(p.pid), '-0']);\n    var traces = [];\n    bunyanP.stdout.on('data', function (data) {\n        //console.error('BUNYAN -P STDOUT:', data.toString());\n        traces.push(data.toString());\n    });\n    bunyanP.stderr.on('data', function (data) {\n        console.error('BUNYAN -P STDERR:', data.toString());\n    });\n    bunyanP.on('exit', function (code) {\n        traces = traces.join('').split('\\n')\n            .filter(function (t) { return t.trim().length })\n            .map(function (t) { return JSON.parse(t) });\n        t.ok(traces.length >= 3, 'got >=3 log records: ' + traces.length);\n        if (traces.length >= 3) {\n            if (traces[0].level !== bunyan.DEBUG) {\n                traces.shift();\n            }\n            t.equal(traces[0].level, bunyan.DEBUG);\n            t.equal(traces[0].foo, 'bar');\n            t.equal(traces[1].level, bunyan.TRACE);\n            t.equal(traces[1].msg, 'hi at trace');\n        }\n        t.end();\n    });\n\n    // Give it a few seconds to ensure we get some traces.\n    setTimeout(function () {\n        p.kill();\n        bunyanP.kill();\n    }, 5000);\n});\n\n\n} /* end of `if (runDtraceTests)` */\n"
  },
  {
    "path": "test/error-event.test.js",
    "content": "/*\n * Copyright 2020 Trent Mick\n *\n * Test emission and handling of 'error' event in a logger with a 'path'\n * stream.\n */\n\nvar EventEmitter = require('events').EventEmitter;\nvar test = require('tap').test;\nvar util = require('util');\n\nvar bunyan = require('../lib/bunyan');\n\n\nvar BOGUS_PATH = '/this/path/is/bogus.log';\n\ntest('error event on file stream (reemitErrorEvents=undefined)', function (t) {\n    var log = bunyan.createLogger(\n        {name: 'error-event-1', streams: [ {path: BOGUS_PATH} ]});\n    log.on('error', function (err, stream) {\n        t.ok(err, 'got err in error event: ' + err);\n        t.equal(err.code, 'ENOENT', 'error code is ENOENT');\n        t.ok(stream, 'got a stream argument');\n        t.equal(stream.path, BOGUS_PATH);\n        t.equal(stream.type, 'file');\n        t.end();\n    });\n    log.info('info log message');\n});\n\ntest('error event on file stream (reemitErrorEvents=true)', function (t) {\n    var log = bunyan.createLogger({\n        name: 'error-event-2',\n        streams: [ {\n            path: BOGUS_PATH,\n            reemitErrorEvents: true\n        } ]\n    });\n    log.on('error', function (err, stream) {\n        t.ok(err, 'got err in error event: ' + err);\n        t.equal(err.code, 'ENOENT', 'error code is ENOENT');\n        t.ok(stream, 'got a stream argument');\n        t.equal(stream.path, BOGUS_PATH);\n        t.equal(stream.type, 'file');\n        t.end();\n    });\n    log.info('info log message');\n});\n\ntest('error event on file stream (reemitErrorEvents=false)',\n        function (t) {\n    var log = bunyan.createLogger({\n        name: 'error-event-3',\n        streams: [ {\n            path: BOGUS_PATH,\n            reemitErrorEvents: false\n        } ]\n    });\n    // Hack into the underlying created file stream to catch the error event.\n    log.streams[0].stream.on('error', function (err) {\n        t.ok(err, 'got error event on the file stream');\n        t.end();\n    });\n    log.on('error', function (err, stream) {\n        t.fail('should not have gotten error event on logger');\n        t.end();\n    });\n    log.info('info log message');\n});\n\n\nfunction MyErroringStream() {}\nutil.inherits(MyErroringStream, EventEmitter);\nMyErroringStream.prototype.write = function (rec) {\n    this.emit('error', new Error('boom'));\n}\n\ntest('error event on raw stream (reemitErrorEvents=undefined)', function (t) {\n    var estream = new MyErroringStream();\n    var log = bunyan.createLogger({\n        name: 'error-event-raw',\n        streams: [\n            {\n                stream: estream,\n                type: 'raw'\n            }\n        ]\n    });\n    estream.on('error', function (err) {\n        t.ok(err, 'got error event on the raw stream');\n        t.end();\n    });\n    log.on('error', function (err, stream) {\n        t.fail('should not have gotten error event on logger');\n        t.end();\n    });\n    log.info('info log message');\n});\n\ntest('error event on raw stream (reemitErrorEvents=false)', function (t) {\n    var estream = new MyErroringStream();\n    var log = bunyan.createLogger({\n        name: 'error-event-raw',\n        streams: [\n            {\n                stream: estream,\n                type: 'raw',\n                reemitErrorEvents: false\n            }\n        ]\n    });\n    estream.on('error', function (err) {\n        t.ok(err, 'got error event on the raw stream');\n        t.end();\n    });\n    log.on('error', function (err, stream) {\n        t.fail('should not have gotten error event on logger');\n        t.end();\n    });\n    log.info('info log message');\n});\n\ntest('error event on raw stream (reemitErrorEvents=true)', function (t) {\n    var estream = new MyErroringStream();\n    var log = bunyan.createLogger({\n        name: 'error-event-raw',\n        streams: [\n            {\n                stream: estream,\n                type: 'raw',\n                reemitErrorEvents: true\n            }\n        ]\n    });\n    log.on('error', function (err, stream) {\n        t.ok(err, 'got err in error event: ' + err);\n        t.equal(err.message, 'boom');\n        t.ok(stream, 'got a stream argument');\n        t.ok(stream.stream instanceof MyErroringStream);\n        t.equal(stream.type, 'raw');\n        t.end();\n    });\n    log.info('info log message');\n});\n"
  },
  {
    "path": "test/level.test.js",
    "content": "/*\n * Copyright 2020 Trent Mick\n *\n * Test the `log.level(...)`.\n */\n\nvar test = require('tap').test;\nvar util = require('util'),\n    format = util.format,\n    inspect = util.inspect;\nvar p = console.log;\n\nvar bunyan = require('../lib/bunyan');\n\n\n// ---- test boolean `log.<level>()` calls\n\nvar log1 = bunyan.createLogger({\n    name: 'log1',\n    streams: [\n        {\n            path: __dirname + '/level.test.log1.log',\n            level: 'info'\n        }\n    ]\n});\n\n\ntest('log.level() -> level num', function (t) {\n    t.equal(log1.level(), bunyan.INFO);\n    t.end();\n});\n\ntest('log.level(<const>)', function (t) {\n    log1.level(bunyan.DEBUG);\n    t.equal(log1.level(), bunyan.DEBUG);\n    t.end();\n});\n\ntest('log.level(<num>)', function (t) {\n    log1.level(10);\n    t.equal(log1.level(), bunyan.TRACE);\n    t.end();\n});\n\ntest('log.level(<name>)', function (t) {\n    log1.level('error');\n    t.equal(log1.level(), bunyan.ERROR);\n    t.end();\n});\n\n// A trick to turn logging off.\n// See <https://github.com/trentm/node-bunyan/pull/148#issuecomment-53232979>.\ntest('log.level(FATAL + 1)', function (t) {\n    log1.level(bunyan.FATAL + 1);\n    t.equal(log1.level(), bunyan.FATAL + 1);\n    t.end();\n});\n\ntest('log.level(<weird numbers>)', function (t) {\n    log1.level(0);\n    t.equal(log1.level(), 0);\n    log1.level(Number.MAX_VALUE);\n    t.equal(log1.level(), Number.MAX_VALUE);\n    log1.level(Infinity);\n    t.equal(log1.level(), Infinity);\n    t.end();\n});\n\ntest('log.level(<invalid values>)', function (t) {\n    t.throws(function () {\n        var log = bunyan.createLogger({name: 'invalid', level: 'booga'});\n    // JSSTYLED\n    }, /unknown level name: \"booga\"/);\n    t.throws(function () {\n        var log = bunyan.createLogger({name: 'invalid', level: []});\n    }, /cannot resolve level: invalid arg \\(object\\): \\[\\]/);\n    t.throws(function () {\n        var log = bunyan.createLogger({name: 'invalid', level: true});\n    }, /cannot resolve level: invalid arg \\(boolean\\): true/);\n    t.throws(function () {\n        var log = bunyan.createLogger({name: 'invalid', level: -1});\n    }, /level is not a positive integer: -1/);\n    t.throws(function () {\n        var log = bunyan.createLogger({name: 'invalid', level: 3.14});\n    }, /level is not a positive integer: 3.14/);\n    t.throws(function () {\n        var log = bunyan.createLogger({name: 'invalid', level: -Infinity});\n    }, /level is not a positive integer: -Infinity/);\n    t.end();\n});\n"
  },
  {
    "path": "test/log-some-loop.js",
    "content": "\n// A helper script to log a few times, pause, repeat. We attempt to NOT emit\n// to stdout or stderr because this is used for dtrace testing\n// and we don't want to mix output.\n\nvar bunyan = require('../lib/bunyan');\nvar log = bunyan.createLogger({\n    name: 'play',\n    serializers: bunyan.stdSerializers\n});\n\nsetInterval(function logSome() {\n    log.debug({foo: 'bar'}, 'hi at debug')\n    log.trace('hi at trace')\n}, 1000);\n"
  },
  {
    "path": "test/log-some.js",
    "content": "\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 testing\n// and we don't want to mix output.\n\nvar bunyan = require('../lib/bunyan');\nvar log = bunyan.createLogger({\n        name: 'play',\n        serializers: bunyan.stdSerializers\n});\nlog.debug({foo: 'bar'}, 'hi at debug')\nlog.trace('hi at trace')\n"
  },
  {
    "path": "test/log.test.js",
    "content": "/*\n * Copyright 2020 Trent Mick\n *\n * Test the `log.trace(...)`, `log.debug(...)`, ..., `log.fatal(...)` API.\n */\n\nvar util = require('util'),\n    format = util.format,\n    inspect = util.inspect;\nvar test = require('tap').test;\nvar p = console.log;\n\nvar bunyan = require('../lib/bunyan');\n\n\n// ---- test boolean `log.<level>()` calls\n\nvar log1 = bunyan.createLogger({\n    name: 'log1',\n    streams: [\n        {\n            path: __dirname + '/log.test.log1.log',\n            level: 'info'\n        }\n    ]\n});\n\nvar log2 = bunyan.createLogger({\n    name: 'log2',\n    streams: [\n        {\n            path: __dirname + '/log.test.log2a.log',\n            level: 'error'\n        },\n        {\n            path: __dirname + '/log.test.log2b.log',\n            level: 'debug'\n        }\n    ]\n})\n\ntest('log.LEVEL() -> boolean', function (t) {\n    t.equal(log1.trace(), false, 'log1.trace() is false')\n    t.equal(log1.debug(), false)\n    t.equal(log1.info(), true)\n    t.equal(log1.warn(), true)\n    t.equal(log1.error(), true)\n    t.equal(log1.fatal(), true)\n\n    // Level is the *lowest* level of all streams.\n    t.equal(log2.trace(), false)\n    t.equal(log2.debug(), true)\n    t.equal(log2.info(), true)\n    t.equal(log2.warn(), true)\n    t.equal(log2.error(), true)\n    t.equal(log2.fatal(), true)\n    t.end();\n});\n\n\n// ---- test `log.<level>(...)` calls which various input types\n\nfunction Catcher() {\n    this.records = [];\n}\nCatcher.prototype.write = function (record) {\n    this.records.push(record);\n}\nvar catcher = new Catcher();\nvar log3 = new bunyan.createLogger({\n    name: 'log3',\n    streams: [\n        {\n            type: 'raw',\n            stream: catcher,\n            level: 'trace'\n        }\n    ]\n});\n\nvar names = ['trace', 'debug', 'info', 'warn', 'error', 'fatal'];\nvar fields = {one: 'un'};\n\ntest('log.info(undefined, <msg>)', function (t) {\n    // https://github.com/nodejs/node/pull/23162 (starting in node v12) changed\n    // util.format() handling such that this test case expected string differs.\n    var expect;\n    if (Number(process.versions.node.split('.')[0]) >= 12) {\n        expect = 'undefined some message';\n    } else {\n        expect = 'undefined \\'some message\\'';\n    }\n\n    names.forEach(function (lvl) {\n        log3[lvl].call(log3, undefined, 'some message');\n        var rec = catcher.records[catcher.records.length - 1];\n        t.equal(rec.msg, expect,\n            format('log.%s(undefined, \"some message\")', lvl));\n    });\n    t.end();\n});\n\ntest('log.info(<fields>, undefined)', function (t) {\n    names.forEach(function (lvl) {\n        log3[lvl].call(log3, fields, undefined);\n        var rec = catcher.records[catcher.records.length - 1];\n        t.equal(rec.msg, 'undefined',\n            format('log.%s msg: expect \"undefined\", got %j', lvl, rec.msg));\n        t.equal(rec.one, 'un');\n    });\n    t.end();\n});\n\ntest('log.info(null, <msg>)', function (t) {\n    names.forEach(function (lvl) {\n        log3[lvl].call(log3, null, 'some message');\n        var rec = catcher.records[catcher.records.length - 1];\n        t.equal(rec.msg, 'some message',\n            format('log.%s msg is \"some message\"', lvl));\n    });\n    t.end();\n});\n\ntest('log.info(<fields>, null)', function (t) {\n    names.forEach(function (lvl) {\n        log3[lvl].call(log3, fields, null);\n        var rec = catcher.records[catcher.records.length - 1];\n        t.equal(rec.msg, 'null',\n            format('log.%s msg: expect \"null\", got %j', lvl, rec.msg));\n        t.equal(rec.one, 'un');\n    });\n    t.end();\n});\n\ntest('log.info(<str>)', function (t) {\n    names.forEach(function (lvl) {\n        log3[lvl].call(log3, 'some message');\n        var rec = catcher.records[catcher.records.length - 1];\n        t.equal(rec.msg, 'some message',\n            format('log.%s msg is \"some message\"', lvl));\n    });\n    t.end();\n});\n\ntest('log.info(<fields>, <str>)', function (t) {\n    names.forEach(function (lvl) {\n        log3[lvl].call(log3, fields, 'some message');\n        var rec = catcher.records[catcher.records.length - 1];\n        t.equal(rec.msg, 'some message',\n            format('log.%s msg: got %j', lvl, rec.msg));\n        t.equal(rec.one, 'un');\n    });\n    t.end();\n});\n\ntest('log.info(<bool>)', function (t) {\n    names.forEach(function (lvl) {\n        log3[lvl].call(log3, true);\n        var rec = catcher.records[catcher.records.length - 1];\n        t.equal(rec.msg, 'true',\n            format('log.%s msg is \"true\"', lvl));\n    });\n    t.end();\n});\n\ntest('log.info(<fields>, <bool>)', function (t) {\n    names.forEach(function (lvl) {\n        log3[lvl].call(log3, fields, true);\n        var rec = catcher.records[catcher.records.length - 1];\n        t.equal(rec.msg, 'true',\n            format('log.%s msg: got %j', lvl, rec.msg));\n        t.equal(rec.one, 'un');\n    });\n    t.end();\n});\n\ntest('log.info(<num>)', function (t) {\n    names.forEach(function (lvl) {\n        log3[lvl].call(log3, 3.14);\n        var rec = catcher.records[catcher.records.length - 1];\n        t.equal(rec.msg, '3.14',\n            format('log.%s msg: got %j', lvl, rec.msg));\n    });\n    t.end();\n});\n\ntest('log.info(<fields>, <num>)', function (t) {\n    names.forEach(function (lvl) {\n        log3[lvl].call(log3, fields, 3.14);\n        var rec = catcher.records[catcher.records.length - 1];\n        t.equal(rec.msg, '3.14',\n            format('log.%s msg: got %j', lvl, rec.msg));\n        t.equal(rec.one, 'un');\n    });\n    t.end();\n});\n\ntest('log.info(<function>)', function (t) {\n    var func = function func1() {};\n    names.forEach(function (lvl) {\n        log3[lvl].call(log3, func);\n        var rec = catcher.records[catcher.records.length - 1];\n        t.equal(rec.msg, '[Function: func1]',\n            format('log.%s msg: got %j', lvl, rec.msg));\n    });\n    t.end();\n});\n\ntest('log.info(<fields>, <function>)', function (t) {\n    var func = function func2() {};\n    names.forEach(function (lvl) {\n        log3[lvl].call(log3, fields, func);\n        var rec = catcher.records[catcher.records.length - 1];\n        t.equal(rec.msg, '[Function: func2]',\n            format('log.%s msg: got %j', lvl, rec.msg));\n        t.equal(rec.one, 'un');\n    });\n    t.end();\n});\n\ntest('log.info(<array>)', function (t) {\n    var arr = ['a', 1, {two: 'deux'}];\n    names.forEach(function (lvl) {\n        log3[lvl].call(log3, arr);\n        var rec = catcher.records[catcher.records.length - 1];\n        t.equal(rec.msg, format(arr),\n            format('log.%s msg: got %j', lvl, rec.msg));\n    });\n    t.end();\n});\n\ntest('log.info(<fields>, <array>)', function (t) {\n    var arr = ['a', 1, {two: 'deux'}];\n    names.forEach(function (lvl) {\n        log3[lvl].call(log3, fields, arr);\n        var rec = catcher.records[catcher.records.length - 1];\n        t.equal(rec.msg, format(arr),\n            format('log.%s msg: got %j', lvl, rec.msg));\n        t.equal(rec.one, 'un');\n    });\n    t.end();\n});\n\n\n/*\n * By accident (starting with trentm/node-bunyan#85 in bunyan@0.23.0),\n *      log.info(null, ...)\n * was interpreted as `null` being the object of fields. It is gracefully\n * handled, which is good. However, had I to do it again, I would have made\n * that interpret `null` as the *message*, and no fields having been passed.\n * I think it is baked now. It would take a major bunyan rev to change it,\n * but I don't think it is worth it: passing `null` as the first arg isn't\n * really an intended way to call these Bunyan methods for either case.\n */\n\ntest('log.info(null)', function (t) {\n    names.forEach(function (lvl) {\n        log3[lvl].call(log3, null);\n        var rec = catcher.records[catcher.records.length - 1];\n        t.equal(rec.msg, '', format('log.%s msg: got %j', lvl, rec.msg));\n    });\n    t.end();\n});\n\ntest('log.info(null, <msg>)', function (t) {\n    names.forEach(function (lvl) {\n        log3[lvl].call(log3, null, 'my message');\n        var rec = catcher.records[catcher.records.length - 1];\n        t.equal(rec.msg, 'my message',\n            format('log.%s msg: got %j', lvl, rec.msg));\n    });\n    t.end();\n});\n"
  },
  {
    "path": "test/other-api.test.js",
    "content": "/*\n * Copyright 2020 Trent Mick\n *\n * Test other parts of the exported API.\n */\n\nvar test = require('tap').test;\n\nvar bunyan = require('../lib/bunyan');\n\n\ntest('bunyan.<LEVEL>s', function (t) {\n    t.ok(bunyan.TRACE, 'TRACE');\n    t.ok(bunyan.DEBUG, 'DEBUG');\n    t.ok(bunyan.INFO, 'INFO');\n    t.ok(bunyan.WARN, 'WARN');\n    t.ok(bunyan.ERROR, 'ERROR');\n    t.ok(bunyan.FATAL, 'FATAL');\n    t.end();\n});\n\ntest('bunyan.resolveLevel()', function (t) {\n    t.equal(bunyan.resolveLevel('trace'), bunyan.TRACE, 'TRACE');\n    t.equal(bunyan.resolveLevel('TRACE'), bunyan.TRACE, 'TRACE');\n    t.equal(bunyan.resolveLevel('debug'), bunyan.DEBUG, 'DEBUG');\n    t.equal(bunyan.resolveLevel('DEBUG'), bunyan.DEBUG, 'DEBUG');\n    t.equal(bunyan.resolveLevel('info'), bunyan.INFO, 'INFO');\n    t.equal(bunyan.resolveLevel('INFO'), bunyan.INFO, 'INFO');\n    t.equal(bunyan.resolveLevel('warn'), bunyan.WARN, 'WARN');\n    t.equal(bunyan.resolveLevel('WARN'), bunyan.WARN, 'WARN');\n    t.equal(bunyan.resolveLevel('error'), bunyan.ERROR, 'ERROR');\n    t.equal(bunyan.resolveLevel('ERROR'), bunyan.ERROR, 'ERROR');\n    t.equal(bunyan.resolveLevel('fatal'), bunyan.FATAL, 'FATAL');\n    t.equal(bunyan.resolveLevel('FATAL'), bunyan.FATAL, 'FATAL');\n    t.end();\n});\n"
  },
  {
    "path": "test/process-exit.js",
    "content": "var bunyan = require('../lib/bunyan');\nvar log = bunyan.createLogger({\n    name: 'default',\n    streams: [ {\n        type: 'rotating-file',\n        path: __dirname + '/log.test.rot.log',\n        period: '1d',\n        count: 7\n    } ]\n});\nconsole.log('done');\n"
  },
  {
    "path": "test/process-exit.test.js",
    "content": "'use strict';\n/*\n * Test that bunyan process will terminate.\n *\n * Note: Currently (bunyan 0.23.1) this fails on node 0.8, because there is\n * no `unref` in node 0.8 and bunyan doesn't yet have `Logger.prototype.close()`\n * support.\n */\n\nvar exec = require('child_process').exec;\nvar test = require('tap').test;\n\nvar nodeVer = process.versions.node.split('.').map(Number);\n\nif (nodeVer[0] <= 0 && nodeVer[1] <= 8) {\n    console.warn('skip test (node <= 0.8)');\n} else {\n    test('log with rotating file stream will terminate', function (t) {\n        exec('node ' +__dirname + '/process-exit.js', {timeout: 1000},\n                function (err, stdout, stderr) {\n            t.ifError(err);\n            t.equal(stdout, 'done\\n');\n            t.equal(stderr, '');\n            t.end();\n        });\n    });\n}\n"
  },
  {
    "path": "test/raw-stream.test.js",
    "content": "/*\n * Copyright 2020 Trent Mick\n *\n * Test `type: 'raw'` Logger streams.\n */\n\nvar format = require('util').format;\nvar test = require('tap').test;\n\nvar Logger = require('../lib/bunyan');\n\n\nfunction CapturingStream(recs) {\n    this.recs = recs;\n}\nCapturingStream.prototype.write = function (rec) {\n    this.recs.push(rec);\n}\n\n\ntest('raw stream', function (t) {\n    var recs = [];\n\n    var log = new Logger({\n        name: 'raw-stream-test',\n        streams: [\n            {\n                stream: new CapturingStream(recs),\n                type: 'raw'\n            }\n        ]\n    });\n    log.info('first');\n    log.info({two: 'deux'}, 'second');\n\n    t.equal(recs.length, 2);\n    t.equal(typeof (recs[0]), 'object', 'first rec is an object');\n    t.equal(recs[1].two, 'deux', '\"two\" field made it through');\n    t.end();\n});\n\n\ntest('raw streams and regular streams can mix', function (t) {\n    var rawRecs = [];\n    var nonRawRecs = [];\n\n    var log = new Logger({\n        name: 'raw-stream-test',\n        streams: [\n            {\n                stream: new CapturingStream(rawRecs),\n                type: 'raw'\n            },\n            {\n                stream: new CapturingStream(nonRawRecs)\n            }\n        ]\n    });\n    log.info('first');\n    log.info({two: 'deux'}, 'second');\n\n    t.equal(rawRecs.length, 2);\n    t.equal(typeof (rawRecs[0]), 'object', 'first rawRec is an object');\n    t.equal(rawRecs[1].two, 'deux', '\"two\" field made it through');\n\n    t.equal(nonRawRecs.length, 2);\n    t.equal(typeof (nonRawRecs[0]), 'string', 'first nonRawRec is a string');\n\n    t.end();\n});\n\n\ntest('child adding a non-raw stream works', function (t) {\n    var parentRawRecs = [];\n    var rawRecs = [];\n    var nonRawRecs = [];\n\n    var logParent = new Logger({\n        name: 'raw-stream-test',\n        streams: [\n            {\n                stream: new CapturingStream(parentRawRecs),\n                type: 'raw'\n            }\n        ]\n    });\n    var logChild = logParent.child({\n        child: true,\n        streams: [\n            {\n                stream: new CapturingStream(rawRecs),\n                type: 'raw'\n            },\n            {\n                stream: new CapturingStream(nonRawRecs)\n            }\n        ]\n    });\n    logParent.info('first');\n    logChild.info({two: 'deux'}, 'second');\n\n    t.equal(rawRecs.length, 1,\n        format('rawRecs length should be 1 (is %d)', rawRecs.length));\n    t.equal(typeof (rawRecs[0]), 'object', 'rawRec entry is an object');\n    t.equal(rawRecs[0].two, 'deux', '\"two\" field made it through');\n\n    t.equal(nonRawRecs.length, 1);\n    t.equal(typeof (nonRawRecs[0]), 'string', 'first nonRawRec is a string');\n\n    t.end();\n});\n"
  },
  {
    "path": "test/ringbuffer.test.js",
    "content": "/*\n * Test the RingBuffer output stream.\n */\n\nvar test = require('tap').test;\n\nvar Logger = require('../lib/bunyan');\n\nvar ringbuffer = new Logger.RingBuffer({ 'limit': 5 });\n\nvar log1 = new Logger({\n    name: 'log1',\n    streams: [\n        {\n            stream: ringbuffer,\n            type: 'raw',\n            level: 'info'\n        }\n    ]\n});\n\ntest('ringbuffer', function (t) {\n    log1.info('hello');\n    log1.trace('there');\n    log1.error('android');\n    t.equal(ringbuffer.records.length, 2);\n    t.equal(ringbuffer.records[0]['msg'], 'hello');\n    t.equal(ringbuffer.records[1]['msg'], 'android');\n    log1.error('one');\n    log1.error('two');\n    log1.error('three');\n    t.equal(ringbuffer.records.length, 5);\n    log1.error('four');\n    t.equal(ringbuffer.records.length, 5);\n    t.equal(ringbuffer.records[0]['msg'], 'android');\n    t.equal(ringbuffer.records[1]['msg'], 'one');\n    t.equal(ringbuffer.records[2]['msg'], 'two');\n    t.equal(ringbuffer.records[3]['msg'], 'three');\n    t.equal(ringbuffer.records[4]['msg'], 'four');\n    t.end();\n});\n"
  },
  {
    "path": "test/safe-json-stringify-1.js",
    "content": "var bunyan = require('../lib/bunyan');\n\nvar log = bunyan.createLogger({\n    name: 'safe-json-stringify-1'\n});\n\nvar obj = {};\nobj.__defineGetter__('boom',\n    function () { throw new Error('__defineGetter__ ouch!'); });\nlog.info({obj: obj}, 'using __defineGetter__');\n"
  },
  {
    "path": "test/safe-json-stringify-2.js",
    "content": "process.env.BUNYAN_TEST_NO_SAFE_JSON_STRINGIFY = '1';\nvar bunyan = require('../lib/bunyan');\n\nvar log = bunyan.createLogger({\n    name: 'safe-json-stringify-2'\n});\n\nvar obj = {};\nobj.__defineGetter__('boom',\n    function () { throw new Error('__defineGetter__ ouch!'); });\nlog.info({obj: obj}, 'using __defineGetter__');\n"
  },
  {
    "path": "test/safe-json-stringify-3.js",
    "content": "var bunyan = require('../lib/bunyan');\n\nvar log = bunyan.createLogger({\n    name: 'safe-json-stringify-3'\n});\n\n// And using `Object.defineProperty`.\nvar obj = {};\nObject.defineProperty(obj, 'boom', {\n    get: function () { throw new Error('defineProperty ouch!'); },\n    enumerable: true // enumerable is false by default\n});\n// Twice to test the 'warnKey' usage.\nfor (var i = 0; i < 2; i++) {\n    log.info({obj: obj}, 'using defineProperty');\n}\n"
  },
  {
    "path": "test/safe-json-stringify-4.js",
    "content": "process.env.BUNYAN_TEST_NO_SAFE_JSON_STRINGIFY = '1';\nvar bunyan = require('../lib/bunyan');\n\nvar log = bunyan.createLogger({\n    name: 'safe-json-stringify-4'\n});\n\n// And using `Object.defineProperty`.\nvar obj = {};\nObject.defineProperty(obj, 'boom', {\n    get: function () { throw new Error('defineProperty ouch!'); },\n    enumerable: true // enumerable is false by default\n});\n// Twice to test the 'warnKey' usage.\nfor (var i = 0; i < 2; i++) {\n    log.info({obj: obj}, 'using defineProperty');\n}\n"
  },
  {
    "path": "test/safe-json-stringify.test.js",
    "content": "/*\n * Copyright 2020 Trent Mick\n *\n * If available, use `safe-json-stringfy` as a fallback stringifier.\n * This covers the case where an enumerable property throws an error\n * in its getter.\n *\n * See <https://github.com/trentm/node-bunyan/pull/182>\n */\n\nvar exec = require('child_process').exec;\nvar test = require('tap').test;\n\ntest('__defineGetter__ boom', function (t) {\n    var cmd = process.execPath + ' ' + __dirname + '/safe-json-stringify-1.js';\n    exec(cmd, function (err, stdout, stderr) {\n        t.ifError(err, err);\n        var rec = JSON.parse(stdout.trim());\n        t.equal(rec.obj.boom, '[Throws: __defineGetter__ ouch!]');\n        t.end();\n    });\n});\n\ntest('__defineGetter__ boom, without safe-json-stringify', function (t) {\n    var cmd = process.execPath + ' ' + __dirname + '/safe-json-stringify-2.js';\n    exec(cmd, function (err, stdout, stderr) {\n        t.ifError(err, err);\n        t.ok(stdout.indexOf('Exception in JSON.stringify') !== -1);\n        t.ok(stderr.indexOf(\n            'You can install the \"safe-json-stringify\" module') !== -1);\n        t.end();\n    });\n});\n\ntest('defineProperty boom', function (t) {\n    var cmd = process.execPath + ' ' + __dirname + '/safe-json-stringify-3.js';\n    exec(cmd, function (err, stdout, stderr) {\n        t.ifError(err, err);\n        var recs = stdout.trim().split(/\\n/g);\n        t.equal(recs.length, 2);\n        var rec = JSON.parse(recs[0]);\n        t.equal(rec.obj.boom, '[Throws: defineProperty ouch!]');\n        t.end();\n    });\n});\n\ntest('defineProperty boom, without safe-json-stringify', function (t) {\n    var cmd = process.execPath + ' ' + __dirname + '/safe-json-stringify-4.js';\n    exec(cmd, function (err, stdout, stderr) {\n        t.ifError(err, err);\n        t.ok(stdout.indexOf('Exception in JSON.stringify') !== -1);\n        t.equal(stdout.match(/Exception in JSON.stringify/g).length, 2);\n        t.ok(stderr.indexOf(\n            'You can install the \"safe-json-stringify\" module') !== -1);\n        t.equal(stderr.match(\n            /* JSSTYLED */\n            /You can install the \"safe-json-stringify\" module/g).length, 1);\n        t.end();\n    });\n});\n"
  },
  {
    "path": "test/serializers.test.js",
    "content": "/*\n * Copyright 2020 Trent Mick\n *\n * Test the standard serializers in Bunyan.\n */\n\nvar http = require('http');\nvar test = require('tap').test;\nvar verror = require('verror');\n\nvar bunyan = require('../lib/bunyan');\n\n\nfunction CapturingStream(recs) {\n    this.recs = recs;\n}\nCapturingStream.prototype.write = function (rec) {\n    this.recs.push(rec);\n}\n\nfunction createLogger(serializers, records) {\n    return bunyan.createLogger({\n        name: 'serializer-test',\n        streams: [\n            {\n                stream: new CapturingStream(records),\n                type: 'raw'\n            }\n        ],\n        serializers: serializers\n    });\n}\n\ntest('req serializer', function (t) {\n    var records = [];\n    var log = createLogger({ req: bunyan.stdSerializers.req }, records);\n\n    // None of these should blow up.\n    var bogusReqs = [\n        undefined,\n        null,\n        {},\n        1,\n        'string',\n        [1, 2, 3],\n        {'foo':'bar'}\n    ];\n    for (var i = 0; i < bogusReqs.length; i++) {\n        log.info({req: bogusReqs[i]}, 'hi');\n        t.equal(records[i].req, bogusReqs[i]);\n    }\n\n    // Get http request and response objects to play with and test.\n    var theReq, theRes;\n    var server = http.createServer(function (req, res) {\n        theReq = req;\n        theRes = res;\n        res.writeHead(200, {'Content-Type': 'text/plain'});\n        res.end('Hello World\\n');\n    })\n    server.listen(8765, function () {\n        http.get({host: '127.0.0.1', port: 8765, path: '/'}, function (res) {\n            res.resume();\n            log.info({req: theReq}, 'the request');\n            var lastRecord = records[records.length-1];\n            t.equal(lastRecord.req.method, 'GET');\n            t.equal(lastRecord.req.url, theReq.url);\n            t.equal(lastRecord.req.remoteAddress,\n                theReq.connection.remoteAddress);\n            t.equal(lastRecord.req.remotePort, theReq.connection.remotePort);\n            server.close();\n            t.end();\n        }).on('error', function (err) {\n            t.ok(false, 'error requesting to our test server: ' + err);\n            server.close();\n            t.end();\n        });\n    });\n});\n\n\ntest('req serializer - express.originalUrl', function (t) {\n    var records = [];\n    var log = createLogger({ req: bunyan.stdSerializers.req }, records);\n\n    // Get http request and response objects to play with and test.\n    var theReq, theRes;\n    var server = http.createServer(function (req, res) {\n        theReq = req;\n        theRes = res;\n        req.originalUrl = '/original-url' + req.url;\n        res.writeHead(200, {'Content-Type': 'text/plain'});\n        res.end('Hello World\\n');\n    })\n    server.listen(8765, function () {\n        http.get({host: '127.0.0.1', port: 8765, path: '/'}, function (res) {\n            res.resume();\n            log.info({req: theReq}, 'the request');\n            var lastRecord = records[records.length-1];\n            t.equal(lastRecord.req.method, 'GET');\n            t.equal(lastRecord.req.url, '/original-url' + theReq.url);\n            t.equal(lastRecord.req.remoteAddress,\n                theReq.connection.remoteAddress);\n            t.equal(lastRecord.req.remotePort, theReq.connection.remotePort);\n            server.close();\n            t.end();\n        }).on('error', function (err) {\n            t.ok(false, 'error requesting to our test server: ' + err);\n            server.close();\n            t.end();\n        });\n    });\n});\n\ntest('res serializer', function (t) {\n    var records = [];\n    var log = createLogger({ res: bunyan.stdSerializers.res }, records);\n\n    // None of these should blow up.\n    var bogusRess = [\n        undefined,\n        null,\n        {},\n        1,\n        'string',\n        [1, 2, 3],\n        {'foo':'bar'}\n    ];\n    for (var i = 0; i < bogusRess.length; i++) {\n        log.info({res: bogusRess[i]}, 'hi');\n        t.equal(records[i].res, bogusRess[i]);\n    }\n\n    // Get http request and response objects to play with and test.\n    var theReq, theRes;\n    var server = http.createServer(function (req, res) {\n        theReq = req;\n        theRes = res;\n        res.writeHead(200, {'Content-Type': 'text/plain'});\n        res.end('Hello World\\n');\n    })\n    server.listen(8765, function () {\n        http.get({host: '127.0.0.1', port: 8765, path: '/'}, function (res) {\n            res.resume();\n            log.info({res: theRes}, 'the response');\n            var lastRecord = records[records.length-1];\n            t.equal(lastRecord.res.statusCode, theRes.statusCode);\n            t.equal(lastRecord.res.header, theRes._header);\n            server.close();\n            t.end();\n        }).on('error', function (err) {\n            t.ok(false, 'error requesting to our test server: ' + err);\n            server.close();\n            t.end();\n        });\n    });\n});\n\n\ntest('err serializer', function (t) {\n    var records = [];\n    var log = createLogger({ err: bunyan.stdSerializers.err }, records);\n\n    // None of these should blow up.\n    var bogusErrs = [\n        undefined,\n        null,\n        {},\n        1,\n        'string',\n        [1, 2, 3],\n        {'foo':'bar'}\n    ];\n    for (var i = 0; i < bogusErrs.length; i++) {\n        log.info({err: bogusErrs[i]}, 'hi');\n        t.equal(records[i].err, bogusErrs[i]);\n    }\n\n    var theErr = new TypeError('blah');\n\n    log.info(theErr, 'the error');\n    var lastRecord = records[records.length-1];\n    t.equal(lastRecord.err.message, theErr.message);\n    t.equal(lastRecord.err.name, theErr.name);\n    t.equal(lastRecord.err.stack, theErr.stack);\n    t.end();\n});\n\ntest('err serializer: custom serializer', function (t) {\n    var records = [];\n\n    function customSerializer(err) {\n        return {\n            message: err.message,\n            name: err.name,\n            stack: err.stack,\n            beep: err.beep\n        };\n    }\n\n    var log = createLogger({ err: customSerializer }, records);\n\n    var e1 = new Error('message1');\n    e1.beep = 'bop';\n    var e2 = new Error('message2');\n    var errs = [e1, e2];\n\n    for (var i = 0; i < errs.length; i++) {\n        log.info(errs[i]);\n        t.equal(records[i].err.message, errs[i].message);\n        t.equal(records[i].err.beep, errs[i].beep);\n    }\n    t.end();\n});\n\ntest('err serializer: long stack', function (t) {\n    var records = [];\n    var log = createLogger({ err: bunyan.stdSerializers.err }, records);\n\n    var topErr, midErr, bottomErr;\n\n    // Just a VError.\n    topErr = new verror.VError('top err');\n    log.info(topErr, 'the error');\n    var lastRecord = records[records.length-1];\n    t.equal(lastRecord.err.message, topErr.message, 'Just a VError');\n    t.equal(lastRecord.err.name, topErr.name, 'Just a VError');\n    t.equal(lastRecord.err.stack, topErr.stack, 'Just a VError');\n\n    // Just a WError.\n    topErr = new verror.WError('top err');\n    log.info(topErr, 'the error');\n    var lastRecord = records[records.length-1];\n    t.equal(lastRecord.err.message, topErr.message, 'Just a WError');\n    t.equal(lastRecord.err.name, topErr.name, 'Just a WError');\n    t.equal(lastRecord.err.stack, topErr.stack, 'Just a WError');\n\n    // WError <- TypeError\n    bottomErr = new TypeError('bottom err');\n    topErr = new verror.WError(bottomErr, 'top err');\n    log.info(topErr, 'the error');\n    var lastRecord = records[records.length-1];\n    t.equal(lastRecord.err.message, topErr.message, 'WError <- TypeError');\n    t.equal(lastRecord.err.name, topErr.name, 'WError <- TypeError');\n    var expectedStack = topErr.stack + '\\nCaused by: ' + bottomErr.stack;\n    t.equal(lastRecord.err.stack, expectedStack, 'WError <- TypeError');\n\n    // WError <- WError\n    bottomErr = new verror.WError('bottom err');\n    topErr = new verror.WError(bottomErr, 'top err');\n    log.info(topErr, 'the error');\n    var lastRecord = records[records.length-1];\n    t.equal(lastRecord.err.message, topErr.message, 'WError <- WError');\n    t.equal(lastRecord.err.name, topErr.name, 'WError <- WError');\n    var expectedStack = topErr.stack + '\\nCaused by: ' + bottomErr.stack;\n    t.equal(lastRecord.err.stack, expectedStack, 'WError <- WError');\n\n    // WError <- WError <- TypeError\n    bottomErr = new TypeError('bottom err');\n    midErr = new verror.WError(bottomErr, 'mid err');\n    topErr = new verror.WError(midErr, 'top err');\n    log.info(topErr, 'the error');\n    var lastRecord = records[records.length-1];\n    t.equal(lastRecord.err.message, topErr.message,\n        'WError <- WError <- TypeError');\n    t.equal(lastRecord.err.name, topErr.name, 'WError <- WError <- TypeError');\n    var expectedStack = (topErr.stack\n        + '\\nCaused by: ' + midErr.stack\n        + '\\nCaused by: ' + bottomErr.stack);\n    t.equal(lastRecord.err.stack, expectedStack,\n            'WError <- WError <- TypeError');\n\n    // WError <- WError <- WError\n    bottomErr = new verror.WError('bottom err');\n    midErr = new verror.WError(bottomErr, 'mid err');\n    topErr = new verror.WError(midErr, 'top err');\n    log.info(topErr, 'the error');\n    var lastRecord = records[records.length-1];\n    t.equal(lastRecord.err.message, topErr.message,\n        'WError <- WError <- WError');\n    t.equal(lastRecord.err.name, topErr.name, 'WError <- WError <- WError');\n    var expectedStack = (topErr.stack\n        + '\\nCaused by: ' + midErr.stack\n        + '\\nCaused by: ' + bottomErr.stack);\n    t.equal(lastRecord.err.stack, expectedStack, 'WError <- WError <- WError');\n\n    t.end();\n});\n\n\n// Bunyan 0.18.3 introduced a bug where *all* serializers are applied\n// even if the log record doesn't have the associated key. That means\n// serializers that don't handle an `undefined` value will blow up.\ntest('do not apply serializers if no record key', function (t) {\n    var records = [];\n    var log = createLogger({\n            err: bunyan.stdSerializers.err,\n            boom: function (value) {\n                throw new Error('boom');\n            }\n        },\n        records\n    );\n\n    log.info({foo: 'bar'}, 'record one');\n    log.info({err: new Error('record two err')}, 'record two');\n\n    t.equal(records[0].boom, undefined);\n    t.equal(records[0].foo, 'bar');\n    t.equal(records[1].boom, undefined);\n    t.equal(records[1].err.message, 'record two err');\n\n    t.end();\n});\n"
  },
  {
    "path": "test/src.test.js",
    "content": "/*\n * Copyright 2020 Trent Mick.\n *\n * Test `src: true` usage.\n */\n\n// Intentionally on line 8 for tests below:\nfunction logSomething(log) { log.info('something'); }\n\nvar test = require('tap').test;\nvar format = require('util').format;\n\nvar Logger = require('../lib/bunyan');\n\n\nfunction CapturingStream(recs) {\n    this.recs = recs;\n}\nCapturingStream.prototype.write = function (rec) {\n    this.recs.push(rec);\n}\n\n\ntest('src', function (t) {\n    var recs = [];\n\n    var log = new Logger({\n        name: 'src-test',\n        src: true,\n        streams: [\n            {\n                stream: new CapturingStream(recs),\n                type: 'raw'\n            }\n        ]\n    });\n\n    log.info('top-level');\n    logSomething(log);\n\n    t.equal(recs.length, 2);\n    recs.forEach(function (rec) {\n        t.ok(rec.src);\n        t.equal(typeof (rec.src), 'object');\n        t.equal(rec.src.file, __filename);\n        t.ok(rec.src.line);\n        t.equal(typeof (rec.src.line), 'number');\n    });\n    var rec = recs[1];\n    t.ok(rec.src.func);\n    t.equal(rec.src.func, 'logSomething');\n    t.equal(rec.src.line, 8);\n\n    t.end();\n});\n"
  },
  {
    "path": "test/stream-levels.test.js",
    "content": "/*\n * Copyright 2020 Trent Mick\n *\n * Test that streams (the various way they can be added to\n * a Logger instance) get the appropriate level.\n */\n\nvar util = require('util'),\n    format = util.format,\n    inspect = util.inspect;\nvar test = require('tap').test;\n\nvar bunyan = require('../lib/bunyan');\n\n\n// ---- Tests\n\nvar log1 = bunyan.createLogger({\n    name: 'log1',\n    streams: [\n        {\n            path: __dirname + '/level.test.log1.log',\n            level: 'info'\n        }\n    ]\n});\n\n\ntest('default stream log level', function (t) {\n    var log = bunyan.createLogger({\n        name: 'foo'\n    });\n    t.equal(log.level(), bunyan.INFO);\n    t.equal(log.streams[0].level, bunyan.INFO);\n    t.end();\n});\n\ntest('default stream, level=DEBUG specified', function (t) {\n    var log = bunyan.createLogger({\n        name: 'foo',\n        level: bunyan.DEBUG\n    });\n    t.equal(log.level(), bunyan.DEBUG);\n    t.equal(log.streams[0].level, bunyan.DEBUG);\n    t.end();\n});\n\ntest('default stream, level=\"trace\" specified', function (t) {\n    var log = bunyan.createLogger({\n        name: 'foo',\n        level: 'trace'\n    });\n    t.equal(log.level(), bunyan.TRACE);\n    t.equal(log.streams[0].level, bunyan.TRACE);\n    t.end();\n});\n\ntest('stream & level=\"trace\" specified', function (t) {\n    var log = bunyan.createLogger({\n        name: 'foo',\n        stream: process.stderr,\n        level: 'trace'\n    });\n    t.equal(log.level(), bunyan.TRACE);\n    t.equal(log.streams[0].level, bunyan.TRACE);\n    t.end();\n});\n\ntest('one stream, default level', function (t) {\n    var log = bunyan.createLogger({\n        name: 'foo',\n        streams: [\n            {\n                stream: process.stderr\n            }\n        ]\n    });\n    t.equal(log.level(), bunyan.INFO);\n    t.equal(log.streams[0].level, bunyan.INFO);\n    t.end();\n});\n\ntest('one stream, top-\"level\" specified', function (t) {\n    var log = bunyan.createLogger({\n        name: 'foo',\n        level: 'error',\n        streams: [\n            {\n                stream: process.stderr\n            }\n        ]\n    });\n    t.equal(log.level(), bunyan.ERROR);\n    t.equal(log.streams[0].level, bunyan.ERROR);\n    t.end();\n});\n\ntest('one stream, stream-\"level\" specified', function (t) {\n    var log = bunyan.createLogger({\n        name: 'foo',\n        streams: [\n            {\n                stream: process.stderr,\n                level: 'error'\n            }\n        ]\n    });\n    t.equal(log.level(), bunyan.ERROR);\n    t.equal(log.streams[0].level, bunyan.ERROR);\n    t.end();\n});\n\ntest('one stream, both-\"level\" specified', function (t) {\n    var log = bunyan.createLogger({\n        name: 'foo',\n        level: 'debug',\n        streams: [\n            {\n                stream: process.stderr,\n                level: 'error'\n            }\n        ]\n    });\n    t.equal(log.level(), bunyan.ERROR);\n    t.equal(log.streams[0].level, bunyan.ERROR);\n    t.end();\n});\n\ntest('two streams, both-\"level\" specified', function (t) {\n    var log = bunyan.createLogger({\n        name: 'foo',\n        level: 'debug',\n        streams: [\n            {\n                stream: process.stdout,\n                level: 'trace'\n            },\n            {\n                stream: process.stderr,\n                level: 'fatal'\n            }\n        ]\n    });\n    t.equal(log.level(), bunyan.TRACE, 'log.level()');\n    t.equal(log.streams[0].level, bunyan.TRACE);\n    t.equal(log.streams[1].level, bunyan.FATAL);\n    t.end();\n});\n\ntest('two streams, one with \"level\" specified', function (t) {\n    var log = bunyan.createLogger({\n        name: 'foo',\n        streams: [\n            {\n                stream: process.stdout,\n            },\n            {\n                stream: process.stderr,\n                level: 'fatal'\n            }\n        ]\n    });\n    t.equal(log.level(), bunyan.INFO);\n    t.equal(log.streams[0].level, bunyan.INFO);\n    t.equal(log.streams[1].level, bunyan.FATAL);\n    t.end();\n});\n\n// Issue #335\ntest('log level 0 to turn on all logging', function (t) {\n    var log = bunyan.createLogger({\n        name: 'foo',\n        level: 0\n    });\n    t.equal(log.level(), 0);\n    t.equal(log.streams[0].level, 0);\n    t.end();\n});\n"
  },
  {
    "path": "tools/cutarelease.py",
    "content": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n# Copyright (c) 2009-2012 Trent Mick\n\n\"\"\"cutarelease -- Cut a release of your project.\n\nA script that will help cut a release for a git-based project that follows\na few conventions. It'll update your changelog (CHANGES.md), add a git\ntag, push those changes, update your version to the next patch level release\nand create a new changelog section for that new version.\n\nConventions:\n- XXX\n\"\"\"\n\n__version_info__ = (1, 0, 7)\n__version__ = '.'.join(map(str, __version_info__))\n\nimport sys\nimport os\nfrom os.path import join, dirname, normpath, abspath, exists, basename, splitext\nfrom glob import glob\nfrom pprint import pprint\nimport re\nimport codecs\nimport logging\nimport optparse\nimport json\nimport time\n\n\n\n#---- globals and config\n\nlog = logging.getLogger(\"cutarelease\")\n\nclass Error(Exception):\n    pass\n\n\n\n#---- main functionality\n\ndef cutarelease(project_name, version_files, dry_run=False):\n    \"\"\"Cut a release.\n\n    @param project_name {str}\n    @param version_files {list} List of paths to files holding the version\n        info for this project.\n\n        If none are given it attempts to guess the version file:\n        package.json or VERSION.txt or VERSION or $project_name.py\n        or lib/$project_name.py or $project_name.js or lib/$project_name.js.\n\n        The version file can be in one of the following forms:\n\n        - A .py file, in which case the file is expect to have a top-level\n          global called \"__version_info__\" as follows. [1]\n\n            __version_info__ = (0, 7, 6)\n\n          Note that I typically follow that with the following to get a\n          string version attribute on my modules:\n\n            __version__ = '.'.join(map(str, __version_info__))\n\n        - A .js file, in which case the file is expected to have a top-level\n          global called \"VERSION\" as follows:\n\n            ver VERSION = \"1.2.3\";\n\n        - A \"package.json\" file, typical of a node.js npm-using project.\n          The package.json file must have a \"version\" field.\n\n        - TODO: A simple version file whose only content is a \"1.2.3\"-style version\n          string.\n\n    [1]: This is a convention I tend to follow in my projects.\n        Granted it might not be your cup of tea. I should add support for\n        just `__version__ = \"1.2.3\"`. I'm open to other suggestions too.\n    \"\"\"\n    dry_run_str = dry_run and \" (dry-run)\" or \"\"\n\n    if not version_files:\n        log.info(\"guessing version file\")\n        candidates = [\n            \"package.json\",\n            \"VERSION.txt\",\n            \"VERSION\",\n            \"%s.py\" % project_name,\n            \"lib/%s.py\" % project_name,\n            \"%s.js\" % project_name,\n            \"lib/%s.js\" % project_name,\n        ]\n        for candidate in candidates:\n            if exists(candidate):\n                version_files = [candidate]\n                break\n        else:\n            raise Error(\"could not find a version file: specify its path or \"\n                \"add one of the following to your project: '%s'\"\n                % \"', '\".join(candidates))\n        log.info(\"using '%s' as version file\", version_files[0])\n\n    parsed_version_files = [_parse_version_file(f) for f in version_files]\n    version_file_type, version_info = parsed_version_files[0]\n    version = _version_from_version_info(version_info)\n\n    # Confirm\n    if not dry_run:\n        answer = query_yes_no(\"* * *\\n\"\n            \"Are you sure you want cut a %s release?\\n\"\n            \"This will involved commits and a push.\" % version,\n            default=\"no\")\n        print \"* * *\"\n        if answer != \"yes\":\n            log.info(\"user abort\")\n            return\n    log.info(\"cutting a %s release%s\", version, dry_run_str)\n\n    # Checks: Ensure there is a section in changes for this version.\n\n\n\n    changes_path = \"CHANGES.md\"\n    changes_txt, changes, nyr = parse_changelog(changes_path)\n    #pprint(changes)\n    top_ver = changes[0][\"version\"]\n    if top_ver != version:\n        raise Error(\"changelog '%s' top section says \"\n            \"version %r, expected version %r: aborting\"\n            % (changes_path, top_ver, version))\n    top_verline = changes[0][\"verline\"]\n    if not top_verline.endswith(nyr):\n        answer = query_yes_no(\"\\n* * *\\n\"\n            \"The changelog '%s' top section doesn't have the expected\\n\"\n            \"'%s' marker. Has this been released already?\"\n            % (changes_path, nyr), default=\"yes\")\n        print \"* * *\"\n        if answer != \"no\":\n            log.info(\"abort\")\n            return\n    top_body = changes[0][\"body\"]\n    if top_body.strip() == \"(nothing yet)\":\n        raise Error(\"top section body is `(nothing yet)': it looks like \"\n            \"nothing has been added to this release\")\n\n    # Commits to prepare release.\n    changes_txt_before = changes_txt\n    changes_txt = changes_txt.replace(\" (not yet released)\", \"\", 1)\n    if not dry_run and changes_txt != changes_txt_before:\n        log.info(\"prepare `%s' for release\", changes_path)\n        f = codecs.open(changes_path, 'w', 'utf-8')\n        f.write(changes_txt)\n        f.close()\n        run('git commit %s -m \"%s\"'\n            % (changes_path, version))\n\n    # Tag version and push.\n    curr_tags = set(t for t in _capture_stdout([\"git\", \"tag\", \"-l\"]).split('\\n') if t)\n    if not dry_run and version not in curr_tags:\n        log.info(\"tag the release\")\n        date = time.strftime(\"%Y-%m-%d\")\n        run('git tag -a \"%s\" -m \"version %s (%s)\"' % (version, version, date))\n        run('git push --tags')\n\n    # Optionally release.\n    if exists(\"package.json\"):\n        answer = query_yes_no(\"\\n* * *\\nPublish to npm?\", default=\"yes\")\n        print \"* * *\"\n        if answer == \"yes\":\n            if dry_run:\n                log.info(\"skipping npm publish (dry-run)\")\n            else:\n                run('npm publish')\n    elif exists(\"setup.py\"):\n        answer = query_yes_no(\"\\n* * *\\nPublish to pypi?\", default=\"yes\")\n        print \"* * *\"\n        if answer == \"yes\":\n            if dry_run:\n                log.info(\"skipping pypi publish (dry-run)\")\n            else:\n                run(\"%spython setup.py sdist --formats zip upload\"\n                    % _setup_command_prefix())\n\n    # Commits to prepare for future dev and push.\n    # - update changelog file\n    next_version_info = _get_next_version_info(version_info)\n    next_version = _version_from_version_info(next_version_info)\n    log.info(\"prepare for future dev (version %s)\", next_version)\n    marker = \"## \" + changes[0][\"verline\"]\n    if marker.endswith(nyr):\n        marker = marker[0:-len(nyr)]\n    if marker not in changes_txt:\n        raise Error(\"couldn't find `%s' marker in `%s' \"\n            \"content: can't prep for subsequent dev\" % (marker, changes_path))\n    next_verline = \"%s %s%s\" % (marker.rsplit(None, 1)[0], next_version, nyr)\n    changes_txt = changes_txt.replace(marker + '\\n',\n        \"%s\\n\\n(nothing yet)\\n\\n\\n%s\\n\" % (next_verline, marker))\n    if not dry_run:\n        f = codecs.open(changes_path, 'w', 'utf-8')\n        f.write(changes_txt)\n        f.close()\n\n    # - update version file\n    next_version_tuple = _tuple_from_version(next_version)\n    for i, ver_file in enumerate(version_files):\n        ver_content = codecs.open(ver_file, 'r', 'utf-8').read()\n        ver_file_type, ver_info = parsed_version_files[i]\n        if ver_file_type == \"json\":\n            marker = '\"version\": \"%s\"' % version\n            if marker not in ver_content:\n                raise Error(\"couldn't find `%s' version marker in `%s' \"\n                    \"content: can't prep for subsequent dev\" % (marker, ver_file))\n            ver_content = ver_content.replace(marker,\n                '\"version\": \"%s\"' % next_version)\n        elif ver_file_type == \"javascript\":\n            candidates = [\n                (\"single\", \"var VERSION = '%s';\" % version),\n                (\"double\", 'var VERSION = \"%s\";' % version),\n            ]\n            for quote_type, marker in candidates:\n                if marker in ver_content:\n                    break\n            else:\n                raise Error(\"couldn't find any candidate version marker in \"\n                    \"`%s' content: can't prep for subsequent dev: %r\"\n                    % (ver_file, candidates))\n            if quote_type == \"single\":\n                ver_content = ver_content.replace(marker,\n                    \"var VERSION = '%s';\" % next_version)\n            else:\n                ver_content = ver_content.replace(marker,\n                    'var VERSION = \"%s\";' % next_version)\n        elif ver_file_type == \"python\":\n            marker = \"__version_info__ = %r\" % (version_info,)\n            if marker not in ver_content:\n                raise Error(\"couldn't find `%s' version marker in `%s' \"\n                    \"content: can't prep for subsequent dev\" % (marker, ver_file))\n            ver_content = ver_content.replace(marker,\n                \"__version_info__ = %r\" % (next_version_tuple,))\n        elif ver_file_type == \"version\":\n            ver_content = next_version\n        else:\n            raise Error(\"unknown ver_file_type: %r\" % ver_file_type)\n        if not dry_run:\n            log.info(\"update version to '%s' in '%s'\", next_version, ver_file)\n            f = codecs.open(ver_file, 'w', 'utf-8')\n            f.write(ver_content)\n            f.close()\n\n    if not dry_run:\n        run('git commit %s %s -m \"bumpver for subsequent work\"' % (\n            changes_path, ' '.join(version_files)))\n        run('git push')\n\n\n\n#---- internal support routines\n\ndef _indent(s, indent='    '):\n    return indent + indent.join(s.splitlines(True))\n\ndef _tuple_from_version(version):\n    def _intify(s):\n        try:\n            return int(s)\n        except ValueError:\n            return s\n    return tuple(_intify(b) for b in version.split('.'))\n\ndef _get_next_version_info(version_info):\n    next = list(version_info[:])\n    next[-1] += 1\n    return tuple(next)\n\ndef _version_from_version_info(version_info):\n    v = str(version_info[0])\n    state_dot_join = True\n    for i in version_info[1:]:\n        if state_dot_join:\n            try:\n                int(i)\n            except ValueError:\n                state_dot_join = False\n            else:\n                pass\n        if state_dot_join:\n            v += \".\" + str(i)\n        else:\n            v += str(i)\n    return v\n\n_version_re = re.compile(r\"^(\\d+)\\.(\\d+)(?:\\.(\\d+)([abc](\\d+)?)?)?$\")\ndef _version_info_from_version(version):\n    m = _version_re.match(version)\n    if not m:\n        raise Error(\"could not convert '%s' version to version info\" % version)\n    version_info = []\n    for g in m.groups():\n        if g is None:\n            break\n        try:\n            version_info.append(int(g))\n        except ValueError:\n            version_info.append(g)\n    return tuple(version_info)\n\ndef _parse_version_file(version_file):\n    \"\"\"Get version info from the given file. It can be any of:\n\n    Supported version file types (i.e. types of files from which we know\n    how to parse the version string/number -- often by some convention):\n    - json: use the \"version\" key\n    - javascript: look for a `var VERSION = \"1.2.3\";` or\n      `var VERSION = '1.2.3';`\n    - python: Python script/module with `__version_info__ = (1, 2, 3)`\n    - version: a VERSION.txt or VERSION file where the whole contents are\n      the version string\n\n    @param version_file {str} Can be a path or \"type:path\", where \"type\"\n        is one of the supported types.\n    \"\"\"\n    # Get version file *type*.\n    version_file_type = None\n    match = re.compile(\"^([a-z]+):(.*)$\").search(version_file)\n    if match:\n        version_file = match.group(2)\n        version_file_type = match.group(1)\n        aliases = {\n            \"js\": \"javascript\"\n        }\n        if version_file_type in aliases:\n            version_file_type = aliases[version_file_type]\n\n    f = codecs.open(version_file, 'r', 'utf-8')\n    content = f.read()\n    f.close()\n\n    if not version_file_type:\n        # Guess the type.\n        base = basename(version_file)\n        ext = splitext(base)[1]\n        if ext == \".json\":\n            version_file_type = \"json\"\n        elif ext == \".py\":\n            version_file_type = \"python\"\n        elif ext == \".js\":\n            version_file_type = \"javascript\"\n        elif content.startswith(\"#!\"):\n            shebang = content.splitlines(False)[0]\n            shebang_bits = re.split(r'[/ \\t]', shebang)\n            for name, typ in {\"python\": \"python\", \"node\": \"javascript\"}.items():\n                if name in shebang_bits:\n                    version_file_type = typ\n                    break\n        elif base in (\"VERSION\", \"VERSION.txt\"):\n            version_file_type = \"version\"\n    if not version_file_type:\n        raise RuntimeError(\"can't extract version from '%s': no idea \"\n            \"what type of file it it\" % version_file)\n\n    if version_file_type == \"json\":\n        obj = json.loads(content)\n        version_info = _version_info_from_version(obj[\"version\"])\n    elif version_file_type == \"python\":\n        m = re.search(r'^__version_info__ = (.*?)$', content, re.M)\n        version_info = eval(m.group(1))\n    elif version_file_type == \"javascript\":\n        m = re.search(r'^var VERSION = (\\'|\")(.*?)\\1;$', content, re.M)\n        version_info = _version_info_from_version(m.group(2))\n    elif version_file_type == \"version\":\n        version_info = _version_info_from_version(content.strip())\n    else:\n        raise RuntimeError(\"unexpected version_file_type: %r\"\n            % version_file_type)\n    return version_file_type, version_info\n\n\ndef parse_changelog(changes_path):\n    \"\"\"Parse the given changelog path and return `(content, parsed, nyr)`\n    where `nyr` is the ' (not yet released)' marker and `parsed` looks like:\n\n        [{'body': u'\\n(nothing yet)\\n\\n',\n          'verline': u'restify 1.0.1 (not yet released)',\n          'version': u'1.0.1'},    # version is parsed out for top section only\n         {'body': u'...',\n          'verline': u'1.0.0'},\n         {'body': u'...',\n          'verline': u'1.0.0-rc2'},\n         {'body': u'...',\n          'verline': u'1.0.0-rc1'}]\n\n    A changelog (CHANGES.md) is expected to look like this:\n\n        # $project Changelog\n\n        ## $next_version (not yet released)\n\n        ...\n\n        ## $version1\n\n        ...\n\n        ## $version2\n\n        ... and so on\n\n    The version lines are enforced as follows:\n\n    - The top entry should have a \" (not yet released)\" suffix. \"Should\"\n      because recovery from half-cutarelease failures is supported.\n    - A version string must be extractable from there, but it tries to\n      be loose (though strict \"X.Y.Z\" versioning is preferred). Allowed\n\n            ## 1.0.0\n            ## my project 1.0.1\n            ## foo 1.2.3-rc2\n\n      Basically, (a) the \" (not yet released)\" is stripped, (b) the\n      last token is the version, and (c) that version must start with\n      a digit (sanity check).\n    \"\"\"\n    if not exists(changes_path):\n        raise Error(\"changelog file '%s' not found\" % changes_path)\n    content = codecs.open(changes_path, 'r', 'utf-8').read()\n\n    parser = re.compile(\n        r'^##\\s*(?P<verline>[^\\n]*?)\\s*$(?P<body>.*?)(?=^##|\\Z)',\n        re.M | re.S)\n    sections = parser.findall(content)\n\n    # Sanity checks on changelog format.\n    if not sections:\n        template = \"## 1.0.0 (not yet released)\\n\\n(nothing yet)\\n\"\n        raise Error(\"changelog '%s' must have at least one section, \"\n            \"suggestion:\\n\\n%s\" % (changes_path, _indent(template)))\n    first_section_verline = sections[0][0]\n    nyr = ' (not yet released)'\n    #if not first_section_verline.endswith(nyr):\n    #    eg = \"## %s%s\" % (first_section_verline, nyr)\n    #    raise Error(\"changelog '%s' top section must end with %r, \"\n    #        \"naive e.g.: '%s'\" % (changes_path, nyr, eg))\n\n    items = []\n    for i, section in enumerate(sections):\n        item = {\n            \"verline\": section[0],\n            \"body\": section[1]\n        }\n        if i == 0:\n            # We only bother to pull out 'version' for the top section.\n            verline = section[0]\n            if verline.endswith(nyr):\n                verline = verline[0:-len(nyr)]\n            version = verline.split()[-1]\n            try:\n                int(version[0])\n            except ValueError:\n                msg = ''\n                if version.endswith(')'):\n                    msg = \" (cutarelease is picky about the trailing %r \" \\\n                        \"on the top version line. Perhaps you misspelled \" \\\n                        \"that?)\" % nyr\n                raise Error(\"changelog '%s' top section version '%s' is \"\n                    \"invalid: first char isn't a number%s\"\n                    % (changes_path, version, msg))\n            item[\"version\"] = version\n        items.append(item)\n\n    return content, items, nyr\n\n## {{{ http://code.activestate.com/recipes/577058/ (r2)\ndef query_yes_no(question, default=\"yes\"):\n    \"\"\"Ask a yes/no question via raw_input() and return their answer.\n\n    \"question\" is a string that is presented to the user.\n    \"default\" is the presumed answer if the user just hits <Enter>.\n        It must be \"yes\" (the default), \"no\" or None (meaning\n        an answer is required of the user).\n\n    The \"answer\" return value is one of \"yes\" or \"no\".\n    \"\"\"\n    valid = {\"yes\":\"yes\",   \"y\":\"yes\",  \"ye\":\"yes\",\n             \"no\":\"no\",     \"n\":\"no\"}\n    if default == None:\n        prompt = \" [y/n] \"\n    elif default == \"yes\":\n        prompt = \" [Y/n] \"\n    elif default == \"no\":\n        prompt = \" [y/N] \"\n    else:\n        raise ValueError(\"invalid default answer: '%s'\" % default)\n\n    while 1:\n        sys.stdout.write(question + prompt)\n        choice = raw_input().lower()\n        if default is not None and choice == '':\n            return default\n        elif choice in valid.keys():\n            return valid[choice]\n        else:\n            sys.stdout.write(\"Please respond with 'yes' or 'no' \"\\\n                             \"(or 'y' or 'n').\\n\")\n## end of http://code.activestate.com/recipes/577058/ }}}\n\ndef _capture_stdout(argv):\n    import subprocess\n    p = subprocess.Popen(argv, stdout=subprocess.PIPE)\n    return p.communicate()[0]\n\nclass _NoReflowFormatter(optparse.IndentedHelpFormatter):\n    \"\"\"An optparse formatter that does NOT reflow the description.\"\"\"\n    def format_description(self, description):\n        return description or \"\"\n\ndef run(cmd):\n    \"\"\"Run the given command.\n\n    Raises OSError is the command returns a non-zero exit status.\n    \"\"\"\n    log.debug(\"running '%s'\", cmd)\n    fixed_cmd = cmd\n    if sys.platform == \"win32\" and cmd.count('\"') > 2:\n        fixed_cmd = '\"' + cmd + '\"'\n    retval = os.system(fixed_cmd)\n    if hasattr(os, \"WEXITSTATUS\"):\n        status = os.WEXITSTATUS(retval)\n    else:\n        status = retval\n    if status:\n        raise OSError(status, \"error running '%s'\" % cmd)\n\ndef _setup_command_prefix():\n    prefix = \"\"\n    if sys.platform == \"darwin\":\n        # http://forums.macosxhints.com/archive/index.php/t-43243.html\n        # This is an Apple customization to `tar` to avoid creating\n        # '._foo' files for extended-attributes for archived files.\n        prefix = \"COPY_EXTENDED_ATTRIBUTES_DISABLE=1 \"\n    return prefix\n\n\n#---- mainline\n\ndef main(argv):\n    logging.basicConfig(format=\"%(name)s: %(levelname)s: %(message)s\")\n    log.setLevel(logging.INFO)\n\n    # Parse options.\n    parser = optparse.OptionParser(prog=\"cutarelease\", usage='',\n        version=\"%prog \" + __version__, description=__doc__,\n        formatter=_NoReflowFormatter())\n    parser.add_option(\"-v\", \"--verbose\", dest=\"log_level\",\n        action=\"store_const\", const=logging.DEBUG,\n        help=\"more verbose output\")\n    parser.add_option(\"-q\", \"--quiet\", dest=\"log_level\",\n        action=\"store_const\", const=logging.WARNING,\n        help=\"quieter output (just warnings and errors)\")\n    parser.set_default(\"log_level\", logging.INFO)\n    parser.add_option(\"--test\", action=\"store_true\",\n        help=\"run self-test and exit (use 'eol.py -v --test' for verbose test output)\")\n    parser.add_option(\"-p\", \"--project-name\", metavar=\"NAME\",\n        help='the name of this project (default is the base dir name)',\n        default=basename(os.getcwd()))\n    parser.add_option(\"-f\", \"--version-file\", metavar=\"[TYPE:]PATH\",\n        action='append', dest=\"version_files\",\n        help='The path to the project file holding the version info. Can be '\n             'specified multiple times if more than one file should be updated '\n             'with new version info. If excluded, it will be guessed.')\n    parser.add_option(\"-n\", \"--dry-run\", action=\"store_true\",\n        help='Do a dry-run', default=False)\n    opts, args = parser.parse_args()\n    log.setLevel(opts.log_level)\n\n    cutarelease(opts.project_name, opts.version_files, dry_run=opts.dry_run)\n\n\n## {{{ http://code.activestate.com/recipes/577258/ (r5+)\nif __name__ == \"__main__\":\n    try:\n        retval = main(sys.argv)\n    except KeyboardInterrupt:\n        sys.exit(1)\n    except SystemExit:\n        raise\n    except:\n        import traceback, logging\n        if not log.handlers and not logging.root.handlers:\n            logging.basicConfig()\n        skip_it = False\n        exc_info = sys.exc_info()\n        if hasattr(exc_info[0], \"__name__\"):\n            exc_class, exc, tb = exc_info\n            if isinstance(exc, IOError) and exc.args[0] == 32:\n                # Skip 'IOError: [Errno 32] Broken pipe': often a cancelling of `less`.\n                skip_it = True\n            if not skip_it:\n                tb_path, tb_lineno, tb_func = traceback.extract_tb(tb)[-1][:3]\n                log.error(\"%s (%s:%s in %s)\", exc_info[1], tb_path,\n                    tb_lineno, tb_func)\n        else:  # string exception\n            log.error(exc_info[0])\n        if not skip_it:\n            if log.isEnabledFor(logging.DEBUG):\n                traceback.print_exception(*exc_info)\n            sys.exit(1)\n    else:\n        sys.exit(retval)\n## end of http://code.activestate.com/recipes/577258/ }}}\n"
  },
  {
    "path": "tools/jsstyle",
    "content": "#!/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 Development and Distribution License (the \"License\").\n# You may not use this file except in compliance with the License.\n#\n# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE\n# or http://www.opensolaris.org/os/licensing.\n# See the License for the specific language governing permissions\n# and limitations under the License.\n#\n# When distributing Covered Code, include this CDDL HEADER in each\n# file and include the License file at usr/src/OPENSOLARIS.LICENSE.\n# If applicable, add the following below this CDDL HEADER, with the\n# fields enclosed by brackets \"[]\" replaced with your own identifying\n# information: Portions Copyright [yyyy] [name of copyright owner]\n#\n# CDDL HEADER END\n#\n#\n# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.\n# Use is subject to license terms.\n#\n# Copyright 2011 Joyent, Inc. All rights reserved.\n#\n# jsstyle - check for some common stylistic errors.\n#\n#\tjsstyle is a sort of \"lint\" for Javascript coding style.  This tool is\n#\tderived from the cstyle tool, used to check for the style used in the\n#\tSolaris kernel, sometimes known as \"Bill Joy Normal Form\".\n#\n#\tThere's a lot this can't check for, like proper indentation of code\n#\tblocks.  There's also a lot more this could check for.\n#\n#\tA note to the non perl literate:\n#\n#\t\tperl regular expressions are pretty much like egrep\n#\t\tregular expressions, with the following special symbols\n#\n#\t\t\\s\tany space character\n#\t\t\\S\tany non-space character\n#\t\t\\w\tany \"word\" character [a-zA-Z0-9_]\n#\t\t\\W\tany non-word character\n#\t\t\\d\ta digit [0-9]\n#\t\t\\D\ta non-digit\n#\t\t\\b\tword boundary (between \\w and \\W)\n#\t\t\\B\tnon-word boundary\n#\n\nrequire 5.0;\nuse IO::File;\nuse Getopt::Std;\nuse strict;\n\nmy $usage =\n\"Usage: jsstyle [-h?vcC] [-t <num>] [-f <path>] [-o <config>] file ...\n\nCheck your JavaScript file for style.\nSee <https://github.com/davepacheco/jsstyle> for details on config options.\nReport bugs to <https://github.com/davepacheco/jsstyle/issues>.\n\nOptions:\n\t-h \tprint this help and exit\n\t-v\tverbose\n\n\t-c\tcheck continuation indentation inside functions\n\t-t\tspecify tab width for line length calculation\n\t-C\tdon't check anything in header block comments\n\n\t-f PATH\n\t\tpath to a jsstyle config file\n\t-o OPTION1,OPTION2\n\t\tset config options, e.g. '-o doxygen,indent=2'\n\n\";\n\nmy %opts;\n\nif (!getopts(\"ch?o:t:f:vC\", \\%opts)) {\n\tprint $usage;\n\texit 2;\n}\n\nif (defined($opts{'h'}) || defined($opts{'?'})) {\n\tprint $usage;\n\texit;\n}\n\nmy $check_continuation = $opts{'c'};\nmy $verbose = $opts{'v'};\nmy $ignore_hdr_comment = $opts{'C'};\nmy $tab_width = $opts{'t'};\n\n# By default, tabs are 8 characters wide\nif (! defined($opts{'t'})) {\n\t$tab_width = 8;\n}\n\n\n# Load config\nmy %config = (\n\tindent => \"tab\",\n\tdoxygen => 0,  \t# doxygen comments: /** ... */\n\tsplint => 0,    # splint comments. Needed?\n\t\"unparenthesized-return\" => 1,\n\t\"literal-string-quote\" => \"single\",  # 'single' or 'double'\n\t\"blank-after-start-comment\" => 1,\n\t\"continuation-at-front\" => 0,\n\t\"leading-right-paren-ok\" => 0,\n\t\"strict-indent\" => 0\n);\nsub add_config_var ($$) {\n\tmy ($scope, $str) = @_;\n\n\tif ($str !~ /^([\\w-]+)(?:\\s*=\\s*(.*?))?$/) {\n\t\tdie \"$scope: invalid option: '$str'\";\n\t}\n\tmy $name = $1;\n\tmy $value = ($2 eq '' ? 1 : $2);\n\t#print \"scope: '$scope', str: '$str', name: '$name', value: '$value'\\n\";\n\n\t# Validate config var.\n\tif ($name eq \"indent\") {\n\t\t# A number of spaces or \"tab\".\n\t\tif ($value !~ /^\\d+$/ && $value ne \"tab\") {\n\t\t\tdie \"$scope: invalid '$name': must be a number (of \".\n\t\t\t\t\"spaces) or 'tab'\";\n\t\t}\n\t} elsif ($name eq \"doxygen\" ||   # boolean vars\n\t\t $name eq \"splint\" ||\n\t\t $name eq \"unparenthesized-return\" ||\n\t\t $name eq \"continuation-at-front\" ||\n\t\t $name eq \"leading-right-paren-ok\" ||\n\t\t $name eq \"leading-comma-ok\" ||\n\t\t $name eq \"uncuddled-else-ok\" ||\n\t\t $name eq \"whitespace-after-left-paren-ok\" ||\n\t\t $name eq \"strict-indent\" ||\n\t\t $name eq \"blank-after-start-comment\") {\n\n\t\tif ($value != 1 && $value != 0) {\n\t\t\tdie \"$scope: invalid '$name': don't give a value\";\n\t\t}\n\t} elsif ($name eq \"literal-string-quote\") {\n\t\tif ($value !~ /single|double/) {\n\t\t\tdie \"$scope: invalid '$name': must be 'single' \".\n\t\t\t\t\"or 'double'\";\n\t\t}\n\t} else {\n\t\tdie \"$scope: unknown config var: $name\";\n\t}\n\t$config{$name} = $value;\n}\n\nif (defined($opts{'f'})) {\n\tmy $path = $opts{'f'};\n\tmy $fh = new IO::File $path, \"r\";\n\tif (!defined($fh)) {\n\t\tdie \"cannot open config path '$path'\";\n\t}\n\tmy $line = 0;\n\twhile (<$fh>) {\n\t\t$line++;\n\t\ts/^\\s*//;  # drop leading space\n\t\ts/\\s*$//;  # drop trailing space\n\t\tnext if ! $_;  # skip empty line\n\t\tnext if /^#/;  # skip comments\n\t\tadd_config_var \"$path:$line\", $_;\n\t}\n}\n\nif (defined($opts{'o'})) {\n\tfor my $x (split /,/, $opts{'o'}) {\n\t\tadd_config_var \"'-o' option\", $x;\n\t}\n}\n\n\nmy ($filename, $line, $prev);\t\t# shared globals\n\nmy $fmt;\nmy $hdr_comment_start;\n\nif ($verbose) {\n\t$fmt = \"%s: %d: %s\\n%s\\n\";\n} else {\n\t$fmt = \"%s: %d: %s\\n\";\n}\n\nif ($config{\"doxygen\"}) {\n\t# doxygen comments look like \"/*!\" or \"/**\"; allow them.\n\t$hdr_comment_start = qr/^\\s*\\/\\*[\\!\\*]?$/;\n} else {\n\t$hdr_comment_start = qr/^\\s*\\/\\*$/;\n}\n\n# Note, following must be in single quotes so that \\s and \\w work right.\nmy $lint_re = qr/\\/\\*(?:\n\tjsl:\\w+?|ARGSUSED[0-9]*|NOTREACHED|LINTLIBRARY|VARARGS[0-9]*|\n\tCONSTCOND|CONSTANTCOND|CONSTANTCONDITION|EMPTY|\n\tFALLTHRU|FALLTHROUGH|LINTED.*?|PRINTFLIKE[0-9]*|\n\tPROTOLIB[0-9]*|SCANFLIKE[0-9]*|JSSTYLED.*?\n    )\\*\\//x;\n\nmy $splint_re = qr/\\/\\*@.*?@\\*\\//x;\n\nmy $err_stat = 0;\t\t# exit status\n\nif ($#ARGV >= 0) {\n\tforeach my $arg (@ARGV) {\n\t\tmy $fh = new IO::File $arg, \"r\";\n\t\tif (!defined($fh)) {\n\t\t\tprintf \"%s: cannot open\\n\", $arg;\n\t\t} else {\n\t\t\t&jsstyle($arg, $fh);\n\t\t\tclose $fh;\n\t\t}\n\t}\n} else {\n\t&jsstyle(\"<stdin>\", *STDIN);\n}\nexit $err_stat;\n\nmy $no_errs = 0;\t\t# set for JSSTYLED-protected lines\n\nsub err($) {\n\tmy ($error) = @_;\n\tunless ($no_errs) {\n\t\tprintf $fmt, $filename, $., $error, $line;\n\t\t$err_stat = 1;\n\t}\n}\n\nsub err_prefix($$) {\n\tmy ($prevline, $error) = @_;\n\tmy $out = $prevline.\"\\n\".$line;\n\tunless ($no_errs) {\n\t\tprintf $fmt, $filename, $., $error, $out;\n\t\t$err_stat = 1;\n\t}\n}\n\nsub err_prev($) {\n\tmy ($error) = @_;\n\tunless ($no_errs) {\n\t\tprintf $fmt, $filename, $. - 1, $error, $prev;\n\t\t$err_stat = 1;\n\t}\n}\n\nsub jsstyle($$) {\n\nmy ($fn, $filehandle) = @_;\n$filename = $fn;\t\t\t# share it globally\n\nmy $in_cpp = 0;\nmy $next_in_cpp = 0;\n\nmy $in_comment = 0;\nmy $in_header_comment = 0;\nmy $comment_done = 0;\nmy $in_function = 0;\nmy $in_function_header = 0;\nmy $in_declaration = 0;\nmy $note_level = 0;\nmy $nextok = 0;\nmy $nocheck = 0;\n\nmy $in_string = 0;\n\nmy ($okmsg, $comment_prefix);\n\n$line = '';\n$prev = '';\nreset_indent();\n\nline: while (<$filehandle>) {\n\ts/\\r?\\n$//;\t# strip return and newline\n\n\t# save the original line, then remove all text from within\n\t# double or single quotes, we do not want to check such text.\n\n\t$line = $_;\n\n\t#\n\t# C allows strings to be continued with a backslash at the end of\n\t# the line.  We translate that into a quoted string on the previous\n\t# line followed by an initial quote on the next line.\n\t#\n\t# (we assume that no-one will use backslash-continuation with character\n\t# constants)\n\t#\n\t$_ = '\"' . $_\t\tif ($in_string && !$nocheck && !$in_comment);\n\n\t#\n\t# normal strings and characters\n\t#\n\ts/'([^\\\\']|\\\\.)*'/\\'\\'/g;\n\ts/\"([^\\\\\"]|\\\\.)*\"/\\\"\\\"/g;\n\n\t#\n\t# detect string continuation\n\t#\n\tif ($nocheck || $in_comment) {\n\t\t$in_string = 0;\n\t} else {\n\t\t#\n\t\t# Now that all full strings are replaced with \"\", we check\n\t\t# for unfinished strings continuing onto the next line.\n\t\t#\n\t\t$in_string =\n\t\t    (s/([^\"](?:\"\")*)\"([^\\\\\"]|\\\\.)*\\\\$/$1\"\"/ ||\n\t\t    s/^(\"\")*\"([^\\\\\"]|\\\\.)*\\\\$/\"\"/);\n\t}\n\n\t#\n\t# figure out if we are in a cpp directive\n\t#\n\t$in_cpp = $next_in_cpp || /^\\s*#/;\t# continued or started\n\t$next_in_cpp = $in_cpp && /\\\\$/;\t# only if continued\n\n\t# strip off trailing backslashes, which appear in long macros\n\ts/\\s*\\\\$//;\n\n\t# an /* END JSSTYLED */ comment ends a no-check block.\n\tif ($nocheck) {\n\t\tif (/\\/\\* *END *JSSTYLED *\\*\\//) {\n\t\t\t$nocheck = 0;\n\t\t} else {\n\t\t\treset_indent();\n\t\t\tnext line;\n\t\t}\n\t}\n\n\t# a /*JSSTYLED*/ comment indicates that the next line is ok.\n\tif ($nextok) {\n\t\tif ($okmsg) {\n\t\t\terr($okmsg);\n\t\t}\n\t\t$nextok = 0;\n\t\t$okmsg = 0;\n\t\tif (/\\/\\* *JSSTYLED.*\\*\\//) {\n\t\t\t/^.*\\/\\* *JSSTYLED *(.*) *\\*\\/.*$/;\n\t\t\t$okmsg = $1;\n\t\t\t$nextok = 1;\n\t\t}\n\t\t$no_errs = 1;\n\t} elsif ($no_errs) {\n\t\t$no_errs = 0;\n\t}\n\n\t# check length of line.\n\t# first, a quick check to see if there is any chance of being too long.\n\tif ((($line =~ tr/\\t/\\t/) * ($tab_width - 1)) + length($line) > 80) {\n\t\t# yes, there is a chance.\n\t\t# replace tabs with spaces and check again.\n\t\tmy $eline = $line;\n\t\t1 while $eline =~\n\t\t    s/\\t+/' ' x\n\t\t\t(length($&) * $tab_width - length($`) % $tab_width)/e;\n\t\tif (length($eline) > 80) {\n\t\t\terr(\"line > 80 characters\");\n\t\t}\n\t}\n\n\t# ignore NOTE(...) annotations (assumes NOTE is on lines by itself).\n\tif ($note_level || /\\b_?NOTE\\s*\\(/) { # if in NOTE or this is NOTE\n\t\ts/[^()]//g;\t\t\t  # eliminate all non-parens\n\t\t$note_level += s/\\(//g - length;  # update paren nest level\n\t\tnext;\n\t}\n\n\t# a /* BEGIN JSSTYLED */ comment starts a no-check block.\n\tif (/\\/\\* *BEGIN *JSSTYLED *\\*\\//) {\n\t\t$nocheck = 1;\n\t}\n\n\t# a /*JSSTYLED*/ comment indicates that the next line is ok.\n\tif (/\\/\\* *JSSTYLED.*\\*\\//) {\n\t\t/^.*\\/\\* *JSSTYLED *(.*) *\\*\\/.*$/;\n\t\t$okmsg = $1;\n\t\t$nextok = 1;\n\t}\n\tif (/\\/\\/ *JSSTYLED/) {\n\t\t/^.*\\/\\/ *JSSTYLED *(.*)$/;\n\t\t$okmsg = $1;\n\t\t$nextok = 1;\n\t}\n\n\t# universal checks; apply to everything\n\tif (/\\t +\\t/) {\n\t\terr(\"spaces between tabs\");\n\t}\n\tif (/ \\t+ /) {\n\t\terr(\"tabs between spaces\");\n\t}\n\tif (/\\s$/) {\n\t\terr(\"space or tab at end of line\");\n\t}\n\tif (/[^ \\t(]\\/\\*/ && !/\\w\\(\\/\\*.*\\*\\/\\);/) {\n\t\terr(\"comment preceded by non-blank\");\n\t}\n\n\t# is this the beginning or ending of a function?\n\t# (not if \"struct foo\\n{\\n\")\n\tif (/^{$/ && $prev =~ /\\)\\s*(const\\s*)?(\\/\\*.*\\*\\/\\s*)?\\\\?$/) {\n\t\t$in_function = 1;\n\t\t$in_declaration = 1;\n\t\t$in_function_header = 0;\n\t\t$prev = $line;\n\t\tnext line;\n\t}\n\tif (/^}\\s*(\\/\\*.*\\*\\/\\s*)*$/) {\n\t\tif ($prev =~ /^\\s*return\\s*;/) {\n\t\t\terr_prev(\"unneeded return at end of function\");\n\t\t}\n\t\t$in_function = 0;\n\t\treset_indent();\t\t# we don't check between functions\n\t\t$prev = $line;\n\t\tnext line;\n\t}\n\tif (/^\\w*\\($/) {\n\t\t$in_function_header = 1;\n\t}\n\n\t# a blank line terminates the declarations within a function.\n\t# XXX - but still a problem in sub-blocks.\n\tif ($in_declaration && /^$/) {\n\t\t$in_declaration = 0;\n\t}\n\n\tif ($comment_done) {\n\t\t$in_comment = 0;\n\t\t$in_header_comment = 0;\n\t\t$comment_done = 0;\n\t}\n\t# does this looks like the start of a block comment?\n\tif (/$hdr_comment_start/) {\n\t\tif ($config{\"indent\"} eq \"tab\") {\n\t\t\tif (!/^\\t*\\/\\*/) {\n\t\t\t\terr(\"block comment not indented by tabs\");\n\t\t\t}\n\t\t} elsif (!/^ *\\/\\*/) {\n\t\t\terr(\"block comment not indented by spaces\");\n\t\t}\n\t\t$in_comment = 1;\n\t\t/^(\\s*)\\//;\n\t\t$comment_prefix = $1;\n\t\tif ($comment_prefix eq \"\") {\n\t\t\t$in_header_comment = 1;\n\t\t}\n\t\t$prev = $line;\n\t\tnext line;\n\t}\n\t# are we still in the block comment?\n\tif ($in_comment) {\n\t\tif (/^$comment_prefix \\*\\/$/) {\n\t\t\t$comment_done = 1;\n\t\t} elsif (/\\*\\//) {\n\t\t\t$comment_done = 1;\n\t\t\terr(\"improper block comment close\")\n\t\t\t    unless ($ignore_hdr_comment && $in_header_comment);\n\t\t} elsif (!/^$comment_prefix \\*[ \\t]/ &&\n\t\t    !/^$comment_prefix \\*$/) {\n\t\t\terr(\"improper block comment\")\n\t\t\t    unless ($ignore_hdr_comment && $in_header_comment);\n\t\t}\n\t}\n\n\tif ($in_header_comment && $ignore_hdr_comment) {\n\t\t$prev = $line;\n\t\tnext line;\n\t}\n\n\t# check for errors that might occur in comments and in code.\n\n\t# allow spaces to be used to draw pictures in header comments.\n\t#if (/[^ ]     / && !/\".*     .*\"/ && !$in_header_comment) {\n\t#\terr(\"spaces instead of tabs\");\n\t#}\n\t#if (/^ / && !/^ \\*[ \\t\\/]/ && !/^ \\*$/ &&\n\t#    (!/^    \\w/ || $in_function != 0)) {\n\t#\terr(\"indent by spaces instead of tabs\");\n\t#}\n\tif ($config{\"indent\"} eq \"tab\") {\n\t\tif (/^ {2,}/ && !/^    [^ ]/) {\n\t\t\terr(\"indent by spaces instead of tabs\");\n\t\t}\n\t} elsif (/^\\t/) {\n\t\terr(\"indent by tabs instead of spaces\")\n\t} elsif (/^( +)/ && !$in_comment) {\n\t\tmy $indent = $1;\n\t\tif (length($indent) < $config{\"indent\"}) {\n\t\t\terr(\"indent of \" . length($indent) .\n\t\t\t\t\" space(s) instead of \" . $config{\"indent\"});\n\t\t} elsif ($config{\"strict-indent\"} &&\n\t\t\tlength($indent) % $config{\"indent\"} != 0) {\n\t\t\terr(\"indent is \" . length($indent) .\n\t\t\t\t\" not a multiple of \" . $config{'indent'} . \" spaces\");\n\t\t}\n\t}\n\tif (/^\\t+ [^ \\t\\*]/ || /^\\t+  \\S/ || /^\\t+   \\S/) {\n\t\terr(\"continuation line not indented by 4 spaces\");\n\t}\n\n\t# A multi-line block comment must not have content on the first line.\n\tif (/^\\s*\\/\\*./ && !/^\\s*\\/\\*.*\\*\\// && !/$hdr_comment_start/) {\n\t\terr(\"improper first line of block comment\");\n\t}\n\n\tif ($in_comment) {\t# still in comment, don't do further checks\n\t\t$prev = $line;\n\t\tnext line;\n\t}\n\n\tif ((/[^(]\\/\\*\\S/ || /^\\/\\*\\S/) &&\n\t    !(/$lint_re/ || ($config{\"splint\"} && /$splint_re/))) {\n\t\terr(\"missing blank after open comment\");\n\t}\n\tif (/\\S\\*\\/[^)]|\\S\\*\\/$/ &&\n\t    !(/$lint_re/ || ($config{\"splint\"} && /$splint_re/))) {\n\t\terr(\"missing blank before close comment\");\n\t}\n\tif ($config{\"blank-after-start-comment\"} && /(?<!\\w:)\\/\\/\\S/) {\t# C++ comments\n\t\terr(\"missing blank after start comment\");\n\t}\n\t# check for unterminated single line comments, but allow them when\n\t# they are used to comment out the argument list of a function\n\t# declaration.\n\tif (/\\S.*\\/\\*/ && !/\\S.*\\/\\*.*\\*\\// && !/\\(\\/\\*/) {\n\t\terr(\"unterminated single line comment\");\n\t}\n\n\tif (/^(#else|#endif|#include)(.*)$/) {\n\t\t$prev = $line;\n\t\tnext line;\n\t}\n\n\t#\n\t# delete any comments and check everything else.  Note that\n\t# \".*?\" is a non-greedy match, so that we don't get confused by\n\t# multiple comments on the same line.\n\t#\n\ts/\\/\\*.*?\\*\\//\u0001/g;\n\ts/\\/\\/.*$/\u0001/;\t\t# C++ comments\n\n\t# delete any trailing whitespace; we have already checked for that.\n\ts/\\s*$//;\n\n\t# following checks do not apply to text in comments.\n\tmy $quote = $config{\"literal-string-quote\"};\n\tif ($quote eq \"single\") {\n\t\tif (/\"/) {\n\t\t\terr(\"literal string using double-quote instead of single\");\n\t\t}\n\t} elsif ($quote eq \"double\") {\n\t\tif (/'/) {\n\t\t\terr(\"literal string using single-quote instead of double\");\n\t\t}\n\t}\n\n\tif (/[^=!<>\\s][!<>=]=/ || /[^<>!=][!<>=]==?[^\\s,=]/ ||\n\t    (/[^->]>[^,=>\\s]/ && !/[^->]>$/) ||\n\t    (/[^<]<[^,=<\\s]/ && !/[^<]<$/) ||\n\t    /[^<\\s]<[^<]/ || /[^->\\s]>[^>]/) {\n\t\terr(\"missing space around relational operator\");\n\t}\n\tif (/\\S>>=/ || /\\S<<=/ || />>=\\S/ || /<<=\\S/ || /\\S[-+*\\/&|^%]=/ ||\n\t    (/[^-+*\\/&|^%!<>=\\s]=[^=]/ && !/[^-+*\\/&|^%!<>=\\s]=$/) ||\n\t    (/[^!<>=]=[^=\\s]/ && !/[^!<>=]=$/)) {\n\t\t# XXX - should only check this for C++ code\n\t\t# XXX - there are probably other forms that should be allowed\n\t\tif (!/\\soperator=/) {\n\t\t\terr(\"missing space around assignment operator\");\n\t\t}\n\t}\n\tif (/[,;]\\S/ && !/\\bfor \\(;;\\)/ &&\n\t    # Allow a comma in a regex quantifier.\n\t    !/\\/.*?\\{\\d+,?\\d*\\}.*?\\//) {\n\t\terr(\"comma or semicolon followed by non-blank\");\n\t}\n\t# check for commas preceded by blanks\n\tif ((!$config{\"leading-comma-ok\"} && /^\\s*,/) || (!/^\\s*,/ && /\\s,/)) {\n\t\terr(\"comma preceded by blank\");\n\t}\n\t# check for semicolons preceded by blanks\n\t# allow \"for\" statements to have empty \"while\" clauses\n\tif (/\\s;/ && !/^[\\t]+;$/ && !/^\\s*for \\([^;]*; ;[^;]*\\)/) {\n\t\terr(\"semicolon preceded by blank\");\n\t}\n\tif (!$config{\"continuation-at-front\"} && /^\\s*(&&|\\|\\|)/) {\n\t\terr(\"improper boolean continuation\");\n\t} elsif ($config{\"continuation-at-front\"} && /(&&|\\|\\||\\+)$/) {\n\t\terr(\"improper continuation\");\n\t}\n\tif (/\\S   *(&&|\\|\\|)/ || /(&&|\\|\\|)   *\\S/) {\n\t\terr(\"more than one space around boolean operator\");\n\t}\n\t# We allow methods which look like obj.delete() but not keywords without\n\t# spaces ala: delete(obj)\n\tif (/(?<!\\.)\\b(delete|typeof|instanceof|throw|with|catch|new|function|in|for|if|while|switch|return|case)\\(/) {\n\t\terr(\"missing space between keyword and paren\");\n\t}\n\tif (/(\\b(catch|for|if|with|while|switch|return)\\b.*){2,}/) {\n\t\t# multiple \"case\" and \"sizeof\" allowed\n\t\terr(\"more than one keyword on line\");\n\t}\n\tif (/\\b(delete|typeof|instanceOf|with|throw|catch|new|function|in|for|if|while|switch|return|case)\\s\\s+\\(/ &&\n\t    !/^#if\\s+\\(/) {\n\t\terr(\"extra space between keyword and paren\");\n\t}\n\t# try to detect \"func (x)\" but not \"if (x)\" or\n\t# \"#define foo (x)\" or \"int (*func)();\"\n\tif (/\\w\\s\\(/) {\n\t\tmy $s = $_;\n\t\t# strip off all keywords on the line\n\t\ts/\\b(delete|typeof|instanceOf|throw|with|catch|new|function|in|for|if|while|switch|return|case)\\s\\(/XXX(/g;\n\t\ts/#elif\\s\\(/XXX(/g;\n\t\ts/^#define\\s+\\w+\\s+\\(/XXX(/;\n\t\t# do not match things like \"void (*f)();\"\n\t\t# or \"typedef void (func_t)();\"\n\t\ts/\\w\\s\\(+\\*/XXX(*/g;\n\t\ts/\\b(void)\\s+\\(+/XXX(/og;\n\t\tif (/\\w\\s\\(/) {\n\t\t\terr(\"extra space between function name and left paren\");\n\t\t}\n\t\t$_ = $s;\n\t}\n\n\tif ($config{\"unparenthesized-return\"} &&\n\t    /^\\s*return\\W[^;]*;/ && !/^\\s*return\\s*\\(.*\\);/) {\n\t\terr(\"unparenthesized return expression\");\n\t}\n\tif (/\\btypeof\\b/ && !/\\btypeof\\s*\\(.*\\)/) {\n\t\terr(\"unparenthesized typeof expression\");\n\t}\n\tif (!$config{\"whitespace-after-left-paren-ok\"} && /\\(\\s/) {\n\t\terr(\"whitespace after left paren\");\n\t}\n\t# allow \"for\" statements to have empty \"continue\" clauses\n\tif (/\\s\\)/ && !/^\\s*for \\([^;]*;[^;]*; \\)/) {\n\t\tif ($config{\"leading-right-paren-ok\"} && /^\\s+\\)/) {\n\t\t\t# this is allowed\n\t\t} else {\n\t\t\terr(\"whitespace before right paren\");\n\t\t}\n\t}\n\tif (/^\\s*\\(void\\)[^ ]/) {\n\t\terr(\"missing space after (void) cast\");\n\t}\n\tif (/\\S\\{/ && !/(\\{|\\()\\{/ &&\n\t    # Allow a brace in a regex quantifier.\n\t    !/\\/.*?\\{\\d+,?\\d*\\}.*?\\//) {\n\t\terr(\"missing space before left brace\");\n\t}\n\tif ($in_function && /^\\s+{/ &&\n\t    ($prev =~ /\\)\\s*$/ || $prev =~ /\\bstruct\\s+\\w+$/)) {\n\t\terr(\"left brace starting a line\");\n\t}\n\tif (/}(else|while)/) {\n\t\terr(\"missing space after right brace\");\n\t}\n\tif (/}\\s\\s+(else|while)/) {\n\t\terr(\"extra space after right brace\");\n\t}\n\tif (/^\\s+#/) {\n\t\terr(\"preprocessor statement not in column 1\");\n\t}\n\tif (/^#\\s/) {\n\t\terr(\"blank after preprocessor #\");\n\t}\n\n\t#\n\t# We completely ignore, for purposes of indentation:\n\t#  * lines outside of functions\n\t#  * preprocessor lines\n\t#\n\tif ($check_continuation && $in_function && !$in_cpp) {\n\t\tprocess_indent($_);\n\t}\n\n\tif (/^\\s*else\\W/) {\n\t\tif (!$config{\"uncuddled-else-ok\"} && $prev =~ /^\\s*}$/) {\n\t\t\terr_prefix($prev,\n\t\t\t    \"else and right brace should be on same line\");\n\t\t}\n\t}\n\t$prev = $line;\n}\n\nif ($prev eq \"\") {\n\terr(\"last line in file is blank\");\n}\n\n}\n\n#\n# Continuation-line checking\n#\n# The rest of this file contains the code for the continuation checking\n# engine.  It's a pretty simple state machine which tracks the expression\n# depth (unmatched '('s and '['s).\n#\n# Keep in mind that the argument to process_indent() has already been heavily\n# processed; all comments have been replaced by control-A, and the contents of\n# strings and character constants have been elided.\n#\n\nmy $cont_in;\t\t# currently inside of a continuation\nmy $cont_off;\t\t# skipping an initializer or definition\nmy $cont_noerr;\t\t# suppress cascading errors\nmy $cont_start;\t\t# the line being continued\nmy $cont_base;\t\t# the base indentation\nmy $cont_first;\t\t# this is the first line of a statement\nmy $cont_multiseg;\t# this continuation has multiple segments\n\nmy $cont_special;\t# this is a C statement (if, for, etc.)\nmy $cont_macro;\t\t# this is a macro\nmy $cont_case;\t\t# this is a multi-line case\n\nmy @cont_paren;\t\t# the stack of unmatched ( and [s we've seen\n\nsub\nreset_indent()\n{\n\t$cont_in = 0;\n\t$cont_off = 0;\n}\n\nsub\ndelabel($)\n{\n\t#\n\t# replace labels with tabs.  Note that there may be multiple\n\t# labels on a line.\n\t#\n\tlocal $_ = $_[0];\n\n\twhile (/^(\\t*)( *(?:(?:\\w+\\s*)|(?:case\\b[^:]*)): *)(.*)$/) {\n\t\tmy ($pre_tabs, $label, $rest) = ($1, $2, $3);\n\t\t$_ = $pre_tabs;\n\t\twhile ($label =~ s/^([^\\t]*)(\\t+)//) {\n\t\t\t$_ .= \"\\t\" x (length($2) + length($1) / 8);\n\t\t}\n\t\t$_ .= (\"\\t\" x (length($label) / 8)).$rest;\n\t}\n\n\treturn ($_);\n}\n\nsub\nprocess_indent($)\n{\n\trequire strict;\n\tlocal $_ = $_[0];\t\t\t# preserve the global $_\n\n\ts/\u0001//g;\t# No comments\n\ts/\\s+$//;\t# Strip trailing whitespace\n\n\treturn\t\t\tif (/^$/);\t# skip empty lines\n\n\t# regexps used below; keywords taking (), macros, and continued cases\n\tmy $special = '(?:(?:\\}\\s*)?else\\s+)?(?:if|for|while|switch)\\b';\n\tmy $macro = '[A-Z_][A-Z_0-9]*\\(';\n\tmy $case = 'case\\b[^:]*$';\n\n\t# skip over enumerations, array definitions, initializers, etc.\n\tif ($cont_off <= 0 && !/^\\s*$special/ &&\n\t    (/(?:(?:\\b(?:enum|struct|union)\\s*[^\\{]*)|(?:\\s+=\\s*))\\{/ ||\n\t    (/^\\s*\\{/ && $prev =~ /=\\s*(?:\\/\\*.*\\*\\/\\s*)*$/))) {\n\t\t$cont_in = 0;\n\t\t$cont_off = tr/{/{/ - tr/}/}/;\n\t\treturn;\n\t}\n\tif ($cont_off) {\n\t\t$cont_off += tr/{/{/ - tr/}/}/;\n\t\treturn;\n\t}\n\n\tif (!$cont_in) {\n\t\t$cont_start = $line;\n\n\t\tif (/^\\t* /) {\n\t\t\terr(\"non-continuation indented 4 spaces\");\n\t\t\t$cont_noerr = 1;\t\t# stop reporting\n\t\t}\n\t\t$_ = delabel($_);\t# replace labels with tabs\n\n\t\t# check if the statement is complete\n\t\treturn\t\tif (/^\\s*\\}?$/);\n\t\treturn\t\tif (/^\\s*\\}?\\s*else\\s*\\{?$/);\n\t\treturn\t\tif (/^\\s*do\\s*\\{?$/);\n\t\treturn\t\tif (/{$/);\n\t\treturn\t\tif (/}[,;]?$/);\n\n\t\t# Allow macros on their own lines\n\t\treturn\t\tif (/^\\s*[A-Z_][A-Z_0-9]*$/);\n\n\t\t# cases we don't deal with, generally non-kosher\n\t\tif (/{/) {\n\t\t\terr(\"stuff after {\");\n\t\t\treturn;\n\t\t}\n\n\t\t# Get the base line, and set up the state machine\n\t\t/^(\\t*)/;\n\t\t$cont_base = $1;\n\t\t$cont_in = 1;\n\t\t@cont_paren = ();\n\t\t$cont_first = 1;\n\t\t$cont_multiseg = 0;\n\n\t\t# certain things need special processing\n\t\t$cont_special = /^\\s*$special/? 1 : 0;\n\t\t$cont_macro = /^\\s*$macro/? 1 : 0;\n\t\t$cont_case = /^\\s*$case/? 1 : 0;\n\t} else {\n\t\t$cont_first = 0;\n\n\t\t# Strings may be pulled back to an earlier (half-)tabstop\n\t\tunless ($cont_noerr || /^$cont_base    / ||\n\t\t    (/^\\t*(?:    )?(?:gettext\\()?\\\"/ && !/^$cont_base\\t/)) {\n\t\t\terr_prefix($cont_start,\n\t\t\t    \"continuation should be indented 4 spaces\");\n\t\t}\n\t}\n\n\tmy $rest = $_;\t\t\t# keeps the remainder of the line\n\n\t#\n\t# The split matches 0 characters, so that each 'special' character\n\t# is processed separately.  Parens and brackets are pushed and\n\t# popped off the @cont_paren stack.  For normal processing, we wait\n\t# until a ; or { terminates the statement.  \"special\" processing\n\t# (if/for/while/switch) is allowed to stop when the stack empties,\n\t# as is macro processing.  Case statements are terminated with a :\n\t# and an empty paren stack.\n\t#\n\tforeach $_ (split /[^\\(\\)\\[\\]\\{\\}\\;\\:]*/) {\n\t\tnext\t\tif (length($_) == 0);\n\n\t\t# rest contains the remainder of the line\n\t\tmy $rxp = \"[^\\Q$_\\E]*\\Q$_\\E\";\n\t\t$rest =~ s/^$rxp//;\n\n\t\tif (/\\(/ || /\\[/) {\n\t\t\tpush @cont_paren, $_;\n\t\t} elsif (/\\)/ || /\\]/) {\n\t\t\tmy $cur = $_;\n\t\t\ttr/\\)\\]/\\(\\[/;\n\n\t\t\tmy $old = (pop @cont_paren);\n\t\t\tif (!defined($old)) {\n\t\t\t\terr(\"unexpected '$cur'\");\n\t\t\t\t$cont_in = 0;\n\t\t\t\tlast;\n\t\t\t} elsif ($old ne $_) {\n\t\t\t\terr(\"'$cur' mismatched with '$old'\");\n\t\t\t\t$cont_in = 0;\n\t\t\t\tlast;\n\t\t\t}\n\n\t\t\t#\n\t\t\t# If the stack is now empty, do special processing\n\t\t\t# for if/for/while/switch and macro statements.\n\t\t\t#\n\t\t\tnext\t\tif (@cont_paren != 0);\n\t\t\tif ($cont_special) {\n\t\t\t\tif ($rest =~ /^\\s*{?$/) {\n\t\t\t\t\t$cont_in = 0;\n\t\t\t\t\tlast;\n\t\t\t\t}\n\t\t\t\tif ($rest =~ /^\\s*;$/) {\n\t\t\t\t\terr(\"empty if/for/while body \".\n\t\t\t\t\t    \"not on its own line\");\n\t\t\t\t\t$cont_in = 0;\n\t\t\t\t\tlast;\n\t\t\t\t}\n\t\t\t\tif (!$cont_first && $cont_multiseg == 1) {\n\t\t\t\t\terr_prefix($cont_start,\n\t\t\t\t\t    \"multiple statements continued \".\n\t\t\t\t\t    \"over multiple lines\");\n\t\t\t\t\t$cont_multiseg = 2;\n\t\t\t\t} elsif ($cont_multiseg == 0) {\n\t\t\t\t\t$cont_multiseg = 1;\n\t\t\t\t}\n\t\t\t\t# We've finished this section, start\n\t\t\t\t# processing the next.\n\t\t\t\tgoto section_ended;\n\t\t\t}\n\t\t\tif ($cont_macro) {\n\t\t\t\tif ($rest =~ /^$/) {\n\t\t\t\t\t$cont_in = 0;\n\t\t\t\t\tlast;\n\t\t\t\t}\n\t\t\t}\n\t\t} elsif (/\\;/) {\n\t\t\tif ($cont_case) {\n\t\t\t\terr(\"unexpected ;\");\n\t\t\t} elsif (!$cont_special) {\n\t\t\t\terr(\"unexpected ;\")\tif (@cont_paren != 0);\n\t\t\t\tif (!$cont_first && $cont_multiseg == 1) {\n\t\t\t\t\terr_prefix($cont_start,\n\t\t\t\t\t    \"multiple statements continued \".\n\t\t\t\t\t    \"over multiple lines\");\n\t\t\t\t\t$cont_multiseg = 2;\n\t\t\t\t} elsif ($cont_multiseg == 0) {\n\t\t\t\t\t$cont_multiseg = 1;\n\t\t\t\t}\n\t\t\t\tif ($rest =~ /^$/) {\n\t\t\t\t\t$cont_in = 0;\n\t\t\t\t\tlast;\n\t\t\t\t}\n\t\t\t\tif ($rest =~ /^\\s*special/) {\n\t\t\t\t\terr(\"if/for/while/switch not started \".\n\t\t\t\t\t    \"on its own line\");\n\t\t\t\t}\n\t\t\t\tgoto section_ended;\n\t\t\t}\n\t\t} elsif (/\\{/) {\n\t\t\terr(\"{ while in parens/brackets\") if (@cont_paren != 0);\n\t\t\terr(\"stuff after {\")\t\tif ($rest =~ /[^\\s}]/);\n\t\t\t$cont_in = 0;\n\t\t\tlast;\n\t\t} elsif (/\\}/) {\n\t\t\terr(\"} while in parens/brackets\") if (@cont_paren != 0);\n\t\t\tif (!$cont_special && $rest !~ /^\\s*(while|else)\\b/) {\n\t\t\t\tif ($rest =~ /^$/) {\n\t\t\t\t\terr(\"unexpected }\");\n\t\t\t\t} else {\n\t\t\t\t\terr(\"stuff after }\");\n\t\t\t\t}\n\t\t\t\t$cont_in = 0;\n\t\t\t\tlast;\n\t\t\t}\n\t\t} elsif (/\\:/ && $cont_case && @cont_paren == 0) {\n\t\t\terr(\"stuff after multi-line case\") if ($rest !~ /$^/);\n\t\t\t$cont_in = 0;\n\t\t\tlast;\n\t\t}\n\t\tnext;\nsection_ended:\n\t\t# End of a statement or if/while/for loop.  Reset\n\t\t# cont_special and cont_macro based on the rest of the\n\t\t# line.\n\t\t$cont_special = ($rest =~ /^\\s*$special/)? 1 : 0;\n\t\t$cont_macro = ($rest =~ /^\\s*$macro/)? 1 : 0;\n\t\t$cont_case = 0;\n\t\tnext;\n\t}\n\t$cont_noerr = 0\t\t\tif (!$cont_in);\n}\n"
  },
  {
    "path": "tools/statsd-notes.txt",
    "content": "\n# building pycairo (needed for graphite)\n\nwget http://cairographics.org/releases/pycairo-1.10.0.tar.bz2\n \nhack pycairo wscript:\n      #ctx.check_python_version((3,1,0))\n\nbrew install cairo\n    LDFLAGS  -L/usr/local/Cellar/cairo/1.10.2/lib\n    CPPFLAGS -I/usr/local/Cellar/cairo/1.10.2/include\n\nPKG_CONFIG_PATH=/usr/local/Cellar/cairo/1.10.2/lib/pkgconfig ./waf configure\n\nFAIL so far.\n"
  },
  {
    "path": "tools/timechild.js",
    "content": "#!/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 the\n * `log.child` would be about 1% of the time handling that request.\n * Could do better. I.e. consider a hackish fast path.\n *\n * ...\n *\n * Added: `log.fastchild({...}, true)`. Use the `true` to assert that\n * the given options are just new fields (and no serializers).\n * Result: Another order of magnitude.\n */\n\nvar ben = require('ben');  // npm install ben\nvar Logger = require('../lib/bunyan');\n\nvar log = new Logger({\n    name: 'svc',\n    streams: [\n        {\n            path: __dirname + '/timechild.log'\n        },\n        {\n            stream: process.stdout\n        }\n    ],\n    serializers: {\n        err: Logger.stdSerializers.err\n    }\n});\n\nconsole.log('Time `log.child`:');\n\nvar ms = ben(1e5, function () {\n    var child = log.child();\n});\nconsole.log(' - adding no fields:  %dms per iteration', ms);\n\nvar ms = ben(1e5, function () {\n    var child = log.child({a:1});\n});\nconsole.log(' - adding one field:  %dms per iteration', ms);\n\nvar ms = ben(1e5, function () {\n    var child = log.child({a:1, b:2});\n});\nconsole.log(' - adding two fields: %dms per iteration', ms);\n\nfunction fooSerializer(obj) {\n    return {bar: obj.bar};\n}\nvar ms = ben(1e5, function () {\n    var child = log.child({\n        a: 1,\n        serializers: {foo: fooSerializer}\n    });\n});\nconsole.log(' - adding serializer and one field: %dms per iteration', ms);\n\nvar ms = ben(1e5, function () {\n    var child = log.child({\n        a: 1,\n        streams: [ {stream: process.stderr} ]\n    });\n});\nconsole.log(' - adding a (stderr) stream and one field: %dms per iteration',\n    ms);\n\nvar ms = ben(1e6, function () {\n    var child = log.child({}, true);\n});\nconsole.log(' - [fast] adding no fields:  %dms per iteration', ms);\n\nvar ms = ben(1e6, function () {\n    var child = log.child({a:1}, true);\n});\nconsole.log(' - [fast] adding one field:  %dms per iteration', ms);\n\nvar ms = ben(1e6, function () {\n    var child = log.child({a:1, b:2}, true);\n});\nconsole.log(' - [fast] adding two fields: %dms per iteration', ms);\n"
  },
  {
    "path": "tools/timeguard.js",
    "content": "#!/usr/bin/env node\n/*\n * Time logging with/without a try/catch-guard on the JSON.stringify\n * and other code options around that section (see #427).\n */\n\nconsole.log('Time JSON.stringify and alternatives in Logger._emit:');\n\nvar ben = require('ben');  // npm install ben\nvar bunyan = require('../lib/bunyan');\n\nfunction Collector() {}\nCollector.prototype.write = function (s) {};\n\nvar log = bunyan.createLogger({\n    name: 'timeguard',\n    stream: new Collector()\n});\n\nvar ms, fields;\nms = ben(1e5, function () { log.info('hi'); });\nconsole.log(' - log.info with no fields:     %dms per iteration', ms);\n\nfields = {foo: 'bar'};\nms = ben(1e5, function () { log.info(fields, 'hi'); });\nconsole.log(' - log.info with small fields:  %dms per iteration', ms);\n\nfields = {\n    versions: process.versions,\n    moduleLoadList: process.moduleLoadList\n};\nms = ben(1e5, function () { log.info(fields, 'hi'); });\nconsole.log(' - log.info with medium fields: %dms per iteration', ms);\n\n// JSSTYLED\nfields = {\"name\":\"cloudapi\",\"hostname\":\"5bac70c2-fad9-426d-99f1-2854efdad922\",\"pid\":53688,\"component\":\"audit\",\"audit\":true,\"level\":30,\"remoteAddress\":\"172.25.1.28\",\"remotePort\":49596,\"req_id\":\"574e5560-6a9d-11e6-af76-3dadd30aa0da\",\"req\":{\"method\":\"POST\",\"url\":\"/my/machines\",\"headers\":{\"host\":\"cloudapi.nightly-1.joyent.us\",\"user-agent\":\"curl/7.42.0\",\"accept\":\"application/json\",\"content-type\":\"application/json\",\"x-api-version\":\"~7\",\"authorization\":\"Signature keyId=\\\"/admin/keys/64:86:e3:ef:0a:76:bc:43:02:8c:02:04\\\",algorithm=\\\"rsa-sha256\\\" PJmFgjoiW/+MqhYyzjtckFptmcFrHqV1zuETRh+hv8ApxyKZ/+xO6G8q4PPNDxfbhAsP6/kKrV7DJklyIn0KunkyHbonAUGuUb4eq0CghmVX0jwma2ttdvNB2n8k3rvUDlQXp+X/Bi2PNj7D1zjcBQlkRhx118JTtR+QZp+bdTrJ+g6lIs1CMPnRHEQkGOYw3mjDjRNwPiPqcQPmGj7qY/DW0lEfIj/41z7dWS6vUA50RrV1EeM1hD7VCKYZAC41hFC/VLSG1Lbhq7gTykZ3QjM0WyOaDX06cKWxdS+x4VveyvFMVUaiGCeiWpOXmbiLbGomII2AR8NK1+LWfaqH4C31y0bjZ+iK7SBMQ+XY3QjlFv/di3CdlEylUKXsJoKxGqhuCzg+7eXzCNqMj5tdvOdKwizPpazwzPbjAyDeU2l8dTwggMQSuLy7qC7UVtRN2AUgWxw8fxGqivnmbGfRE+KFxA+VrizG+DLFQBve/bd3ZQvKmS/HKM1ATomYyW9g7W8Z2lKbmPtbv91A77bLRh7f6OA2fwaBPW3HP89adC2Gsyj+0sCcPq3F+r/lAT3gEw+tuVBlBbJsS1IV19FQAl0ajCd9ZJ/mJMPGt5hLwbVA7mU6yyU5J71elaBs6klmaKBNPesGLBSv55/xnZlU6mS9FXPdC5Sg=\",\"date\":\"Thu, 25 Aug 2016 08:24:28 GMT\",\"content-length\":\"184\",\"x-forwarded-for\":\"::ffff:172.25.1.28\"},\"httpVersion\":\"1.1\",\"trailers\":{},\"timers\":{\"parseAccept\":743,\"parseAuthorization\":2051,\"parseDate\":20,\"parseQueryString\":50,\"bunyan\":79,\"readBody\":1699,\"parseBody\":218,\"restifyResponseHeaders\":9,\"xForwardedFor\":108,\"setupSDCProxies\":18,\"accountMgmt\":114,\"signatureAuth\":182865,\"tokenAuth\":42,\"assertAuthenticated\":15,\"loadAccount\":56,\"resourceName\":55,\"loadDatasets\":10765,\"loadPackages\":9280}},\"res\":{\"statusCode\":404,\"headers\":{\"content-type\":\"application/json\",\"content-length\":99,\"access-control-allow-origin\":\"*\",\"access-control-allow-headers\":\"Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, Api-Version, Response-Time\",\"access-control-allow-methods\":\"POST, GET, HEAD\",\"access-control-expose-headers\":\"Api-Version, Request-Id, Response-Time\",\"connection\":\"Keep-Alive\",\"content-md5\":\"O3boRcASC7JNu/huA6qnPw==\",\"date\":\"Thu, 25 Aug 2016 08:24:29 GMT\",\"server\":\"Joyent Triton 8.0.2\",\"api-version\":\"8.0.0\",\"request-id\":\"574e5560-6a9d-11e6-af76-3dadd30aa0da\",\"response-time\":210},\"trailer\":false},\"err\":{\"message\":\"Package 92e2b20a-0c37-11e3-9605-63a778146273 does not exist\",\"name\":\"ResourceNotFoundError\",\"stack\":\"ResourceNotFoundError: Package 92e2b20a-0c37-11e3-9605-63a778146273 does not exist\\n    at parseResponse (/opt/smartdc/cloudapi/node_modules/sdc-clients/node_modules/restify/lib/clients/json_client.js:67:23)\\n    at IncomingMessage.done (/opt/smartdc/cloudapi/node_modules/sdc-clients/node_modules/restify/lib/clients/string_client.js:151:17)\\n    at IncomingMessage.g (events.js:180:16)\\n    at IncomingMessage.emit (events.js:117:20)\\n    at _stream_readable.js:944:16\\n    at process._tickDomainCallback (node.js:502:13)\"},\"latency\":210,\"route\":\"createmachine\",\"_audit\":true,\"msg\":\"handled: 404\",\"time\":\"2016-08-25T08:24:29.063Z\",\"v\":0};\nms = ben(1e5, function () { log.info(fields, 'hi'); });\nconsole.log(' - log.info with large fields:  %dms per iteration', ms);\n\nconsole.log('\\nNow you need to manually change `Logger.prototype._emit` in'\n    + '\"../lib/bunyan.js\"\\nto an alternative impl. Then re-run this a '\n    + 'few times to compare speed.');\n"
  },
  {
    "path": "tools/timenop.js",
    "content": "#!/usr/bin/env node\n/*\n * Time logging below the current level, which should do very little work.\n */\n\nconsole.log('Time log.trace() when log level is \"info\":');\n\nvar ben = require('ben');  // npm install ben\nvar bunyan = require('../lib/bunyan');\n\nfunction Collector() {}\nCollector.prototype.write = function (s) {};\n\nvar log = bunyan.createLogger({\n    name: 'timeguard',\n    level: 'info',\n    stream: new Collector()\n});\n\nvar i = 0;\nvar ms, fields;\n\nms = ben(1e7, function () {\n    log.trace({ count: i++ }, 'hello');\n});\nconsole.log(' - log.trace:     %dms per iteration', ms);\n"
  },
  {
    "path": "tools/timesrc.js",
    "content": "#!/usr/bin/env node\n/*\n * Time 'src' fields (getting log call source info). This is expensive.\n */\n\nconsole.log('Time adding \"src\" field with call source info:');\n\nvar ben = require('ben');  // npm install ben\nvar Logger = require('../lib/bunyan');\n\nvar records = [];\nfunction Collector() {\n}\nCollector.prototype.write = function (s) {\n    //records.push(s);\n}\nvar collector = new Collector();\n\nvar logwith = new Logger({\n    name: 'with-src',\n    src: true,\n    stream: collector\n});\n\nvar ms = ben(1e5, function () {\n    logwith.info('hi');\n});\nconsole.log(' - log.info with    src:  %dms per iteration', ms);\n\nvar logwithout = new Logger({\n    name: 'without-src',\n    stream: collector\n});\nvar ms = ben(1e5, function () {\n    logwithout.info('hi');\n});\nconsole.log(' - log.info without src:  %dms per iteration', ms);\n"
  }
]