├── test
├── corpus
│ ├── bogus.log
│ ├── log1.log.gz
│ ├── old-crashers
│ │ ├── README.md
│ │ ├── 244.log
│ │ ├── 242.log
│ │ ├── 233.log
│ │ ├── 139.log
│ │ └── 144.log
│ ├── simple.log
│ ├── extrafield.log
│ ├── res-without-header.log
│ ├── res-header.log
│ ├── non-object-res.log
│ ├── client-req-with-address.log
│ ├── clientreqres.log
│ ├── log1.log
│ ├── log2.log
│ ├── content-length-0-res.log
│ ├── all.log
│ └── withreq.log
├── process-exit.js
├── safe-json-stringify-1.js
├── safe-json-stringify-2.js
├── log-some.js
├── log-some-loop.js
├── safe-json-stringify-3.js
├── safe-json-stringify-4.js
├── add-stream.test.js
├── process-exit.test.js
├── ringbuffer.test.js
├── src.test.js
├── other-api.test.js
├── tap4nodeunit.js
├── cli-res.test.js
├── buffer.test.js
├── cli-client-req.test.js
├── cycles.test.js
├── safe-json-stringify.test.js
├── level.test.js
├── raw-stream.test.js
├── dtrace.test.js
├── child-behaviour.test.js
├── error-event.test.js
├── stream-levels.test.js
├── ctor.test.js
├── log.test.js
├── serializers.test.js
└── cli.test.js
├── docs
├── index.html
├── img
│ └── bunyan.browserify.png
├── bunyan.1.ronn
├── bunyan.1
└── bunyan.1.html
├── .npmignore
├── .gitignore
├── tools
├── screenshot1.png
├── statsd-notes.txt
├── timenop.js
├── timesrc.js
├── colors.log
├── timechild.js
├── timeguard.js
└── cutarelease.py
├── .travis.yml
├── examples
├── unstringifyable.js
├── ringbuffer.js
├── multi.js
├── src.js
├── hi.js
├── raw-stream.js
├── level.js
├── handle-fs-error.js
├── rot-specific-levels.js
├── long-running.js
├── err.js
├── log-undefined-values.js
├── specific-level-streams.js
├── server.js
└── mute-by-envvars-stream.js
├── package.json
├── LICENSE.txt
├── AUTHORS
├── Makefile
├── TODO.md
└── CONTRIBUTING.md
/test/corpus/bogus.log:
--------------------------------------------------------------------------------
1 | not a JSON line
2 | {"hi": "there"}
3 |
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 | bunyan(1) man page
2 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | /tmp
2 | /node_modules
3 | *.log
4 | /examples
5 | /test
6 | /*.tgz
7 | /tools
8 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /tmp
2 | /node_modules
3 | *.log
4 | !/test/corpus/*.log
5 | /*.tgz
6 | /test/*.log.0
7 |
--------------------------------------------------------------------------------
/tools/screenshot1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jimmy319/node-bunyan/master/tools/screenshot1.png
--------------------------------------------------------------------------------
/test/corpus/log1.log.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jimmy319/node-bunyan/master/test/corpus/log1.log.gz
--------------------------------------------------------------------------------
/docs/img/bunyan.browserify.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jimmy319/node-bunyan/master/docs/img/bunyan.browserify.png
--------------------------------------------------------------------------------
/test/corpus/old-crashers/README.md:
--------------------------------------------------------------------------------
1 | Log lines that used to crash `bunyan`.
2 | Typically the file name is the bunyan issue number.
3 |
--------------------------------------------------------------------------------
/test/corpus/simple.log:
--------------------------------------------------------------------------------
1 | {"name":"myservice","pid":123,"hostname":"example.com","level":30,"msg":"My message","time":"2012-02-08T22:56:52.856Z","v":0}
2 |
--------------------------------------------------------------------------------
/test/corpus/extrafield.log:
--------------------------------------------------------------------------------
1 | {"name":"myservice","pid":123,"hostname":"example.com","level":30,"extra":"field","msg":"My message","time":"2012-02-08T22:56:52.856Z","v":0}
2 |
--------------------------------------------------------------------------------
/test/corpus/res-without-header.log:
--------------------------------------------------------------------------------
1 | {"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}
2 |
--------------------------------------------------------------------------------
/test/corpus/old-crashers/244.log:
--------------------------------------------------------------------------------
1 | {"name":"multichannel","hostname":"macbook-dev","pid":44973,"level":30,"res":{"statusCode":401,"header":null},"response":"{\"error\":\"InvalidCredentials\",\"description\":\"The access token provided has expired.\"}","msg":"","time":"2015-04-15T10:37:39.557Z","v":0}
2 |
--------------------------------------------------------------------------------
/test/corpus/res-header.log:
--------------------------------------------------------------------------------
1 | {"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}
2 |
--------------------------------------------------------------------------------
/test/corpus/non-object-res.log:
--------------------------------------------------------------------------------
1 | {"name":"cnapi.get_existing_nics","job_uuid":"3499b13e-dbca-4331-b13a-f164c0da320a","hostname":"710c784f-6aa5-428c-9074-e046c3af884e","pid":24440,"level":30,"nic":"","res":"error: Unknown nic \"020820d753e0\"","msg":"got existing: 02:08:20:d7:53:e0","time":"2012-10-10T16:14:07.610Z","v":0}
2 |
--------------------------------------------------------------------------------
/test/process-exit.js:
--------------------------------------------------------------------------------
1 | var bunyan = require('../lib/bunyan');
2 | var log = bunyan.createLogger({
3 | name: 'default',
4 | streams: [ {
5 | type: 'rotating-file',
6 | path: __dirname + '/log.test.rot.log',
7 | period: '1d',
8 | count: 7
9 | } ]
10 | });
11 | console.log('done');
12 |
--------------------------------------------------------------------------------
/test/safe-json-stringify-1.js:
--------------------------------------------------------------------------------
1 | var bunyan = require('../lib/bunyan');
2 |
3 | var log = bunyan.createLogger({
4 | name: 'safe-json-stringify-1'
5 | });
6 |
7 | var obj = {};
8 | obj.__defineGetter__('boom',
9 | function () { throw new Error('__defineGetter__ ouch!'); });
10 | log.info({obj: obj}, 'using __defineGetter__');
11 |
--------------------------------------------------------------------------------
/test/corpus/client-req-with-address.log:
--------------------------------------------------------------------------------
1 | {"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}
2 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 |
3 | os:
4 | - linux
5 | - osx
6 |
7 | env:
8 | - SKIP_DTRACE=1
9 |
10 | node_js:
11 | - '0.10'
12 | - '0.12'
13 | - '4'
14 | - '5'
15 | - '6'
16 | - 'node'
17 |
18 |
19 | # Gives us faster boot time, see https://docs.travis-ci.com/user/ci-environment/
20 | sudo: false
21 |
22 | script:
23 | - npm test
24 |
--------------------------------------------------------------------------------
/test/corpus/old-crashers/242.log:
--------------------------------------------------------------------------------
1 | {"name":"AKP48","module":"Server","hostname":"AKP48.akpwebdesign.com","pid":32421,"level":60,"err":{"message":"Function.prototype.apply: Arguments list has wrong type","name":"TypeError","stack":[{},{},{},{},{},{}]},"msg":"Exception caught: TypeError: Function.prototype.apply: Arguments list has wrong type","time":"2015-04-13T04:03:46.206Z","v":0}
2 |
--------------------------------------------------------------------------------
/examples/unstringifyable.js:
--------------------------------------------------------------------------------
1 | // See how bunyan behaves with an un-stringify-able object.
2 | var bunyan = require('../lib/bunyan');
3 |
4 | var log = bunyan.createLogger({src: true, name: 'foo'});
5 |
6 | // Make a circular object (cannot be JSON-ified).
7 | var myobj = {
8 | foo: 'bar'
9 | };
10 | myobj.myobj = myobj;
11 |
12 | log.info({obj: myobj}, 'hi there'); // <--- here
13 |
--------------------------------------------------------------------------------
/test/safe-json-stringify-2.js:
--------------------------------------------------------------------------------
1 | process.env.BUNYAN_TEST_NO_SAFE_JSON_STRINGIFY = '1';
2 | var bunyan = require('../lib/bunyan');
3 |
4 | var log = bunyan.createLogger({
5 | name: 'safe-json-stringify-2'
6 | });
7 |
8 | var obj = {};
9 | obj.__defineGetter__('boom',
10 | function () { throw new Error('__defineGetter__ ouch!'); });
11 | log.info({obj: obj}, 'using __defineGetter__');
12 |
--------------------------------------------------------------------------------
/test/corpus/old-crashers/233.log:
--------------------------------------------------------------------------------
1 | {"name":"server","hostname":"iZ25apt4ethZ","pid":958,"level":30,"req":{"method":"GET","url":"/ground/fetch?id=null","headers":{"host":"123.123.123.123","connection":"Keep-Alive","accept-encoding":"gzip"},"remoteAddress":"::ffff:123.123.123.123"},"res":{"headers":true,"content":{"status":0,"message":"success","messages":[]}},"msg":"Time used: 30ms","time":"2015-03-07T07:28:32.431Z","v":0}
2 |
--------------------------------------------------------------------------------
/test/log-some.js:
--------------------------------------------------------------------------------
1 |
2 | // A helper script to log a few times. We attempt to NOT emit
3 | // to stdout or stderr because this is used for dtrace testing
4 | // and we don't want to mix output.
5 |
6 | var bunyan = require('../lib/bunyan');
7 | var log = bunyan.createLogger({
8 | name: 'play',
9 | serializers: bunyan.stdSerializers
10 | });
11 | log.debug({foo: 'bar'}, 'hi at debug')
12 | log.trace('hi at trace')
13 |
--------------------------------------------------------------------------------
/examples/ringbuffer.js:
--------------------------------------------------------------------------------
1 | /* Create a ring buffer that stores the last 100 records. */
2 | var bunyan = require('..');
3 | var ringbuffer = new bunyan.RingBuffer({ limit: 100 });
4 | var log = new bunyan({
5 | name: 'foo',
6 | streams: [ {
7 | type: 'raw',
8 | stream: ringbuffer,
9 | level: 'debug'
10 | } ]
11 | });
12 |
13 | log.info('hello world');
14 | console.log(ringbuffer.records);
15 |
--------------------------------------------------------------------------------
/test/corpus/clientreqres.log:
--------------------------------------------------------------------------------
1 | {"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}
2 | {"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}
3 |
--------------------------------------------------------------------------------
/tools/statsd-notes.txt:
--------------------------------------------------------------------------------
1 |
2 | # building pycairo (needed for graphite)
3 |
4 | wget http://cairographics.org/releases/pycairo-1.10.0.tar.bz2
5 |
6 | hack pycairo wscript:
7 | #ctx.check_python_version((3,1,0))
8 |
9 | brew install cairo
10 | LDFLAGS -L/usr/local/Cellar/cairo/1.10.2/lib
11 | CPPFLAGS -I/usr/local/Cellar/cairo/1.10.2/include
12 |
13 | PKG_CONFIG_PATH=/usr/local/Cellar/cairo/1.10.2/lib/pkgconfig ./waf configure
14 |
15 | FAIL so far.
16 |
--------------------------------------------------------------------------------
/test/corpus/log1.log:
--------------------------------------------------------------------------------
1 | {"name":"agent1","pid":73267,"hostname":"headnode","level":30,"msg":"message","time":"2012-05-08T16:57:55.586Z","v":0}
2 | {"name":"agent1","pid":73267,"hostname":"headnode","level":30,"msg":"message","time":"2012-05-08T17:02:49.339Z","v":0}
3 | {"name":"agent1","pid":73267,"hostname":"headnode","level":30,"msg":"message","time":"2012-05-08T17:02:49.404Z","v":0}
4 | {"name":"agent1","pid":73267,"hostname":"headnode","level":30,"msg":"message","time":"2012-05-08T17:02:49.404Z","v":0}
5 |
--------------------------------------------------------------------------------
/examples/multi.js:
--------------------------------------------------------------------------------
1 | var bunyan = require('../lib/bunyan');
2 | log = bunyan.createLogger({
3 | name: 'amon',
4 | streams: [
5 | {
6 | level: 'info',
7 | stream: process.stdout,
8 | },
9 | {
10 | level: 'error',
11 | path: 'multi.log'
12 | }
13 | ]
14 | });
15 |
16 |
17 | log.debug('hi nobody on debug');
18 | log.info('hi stdout on info');
19 | log.error('hi both on error');
20 | log.fatal('hi both on fatal');
21 |
--------------------------------------------------------------------------------
/test/log-some-loop.js:
--------------------------------------------------------------------------------
1 |
2 | // A helper script to log a few times, pause, repeat. We attempt to NOT emit
3 | // to stdout or stderr because this is used for dtrace testing
4 | // and we don't want to mix output.
5 |
6 | var bunyan = require('../lib/bunyan');
7 | var log = bunyan.createLogger({
8 | name: 'play',
9 | serializers: bunyan.stdSerializers
10 | });
11 |
12 | setInterval(function logSome() {
13 | log.debug({foo: 'bar'}, 'hi at debug')
14 | log.trace('hi at trace')
15 | }, 1000);
16 |
--------------------------------------------------------------------------------
/test/safe-json-stringify-3.js:
--------------------------------------------------------------------------------
1 | var bunyan = require('../lib/bunyan');
2 |
3 | var log = bunyan.createLogger({
4 | name: 'safe-json-stringify-3'
5 | });
6 |
7 | // And using `Object.defineProperty`.
8 | var obj = {};
9 | Object.defineProperty(obj, 'boom', {
10 | get: function () { throw new Error('defineProperty ouch!'); },
11 | enumerable: true // enumerable is false by default
12 | });
13 | // Twice to test the 'warnKey' usage.
14 | for (var i = 0; i < 2; i++) {
15 | log.info({obj: obj}, 'using defineProperty');
16 | }
17 |
--------------------------------------------------------------------------------
/test/corpus/old-crashers/139.log:
--------------------------------------------------------------------------------
1 | {"name":"wgaf","hostname":"vmac.local","pid":38947,"level":30,"res":{"statusCode":201,"header":{"connection":"close","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","access-control-expose-headers":"Api-Version, Request-Id, Response-Time","date":"Wed, 23 Apr 2014 13:11:53 GMT","server":"wgaf","request-id":"d6cfe8a0-cae8-11e3-9f88-ff29f9d02103","response-time":7}},"msg":"finished","time":"2014-04-23T13:11:53.644Z","v":0}
2 |
--------------------------------------------------------------------------------
/test/safe-json-stringify-4.js:
--------------------------------------------------------------------------------
1 | process.env.BUNYAN_TEST_NO_SAFE_JSON_STRINGIFY = '1';
2 | var bunyan = require('../lib/bunyan');
3 |
4 | var log = bunyan.createLogger({
5 | name: 'safe-json-stringify-4'
6 | });
7 |
8 | // And using `Object.defineProperty`.
9 | var obj = {};
10 | Object.defineProperty(obj, 'boom', {
11 | get: function () { throw new Error('defineProperty ouch!'); },
12 | enumerable: true // enumerable is false by default
13 | });
14 | // Twice to test the 'warnKey' usage.
15 | for (var i = 0; i < 2; i++) {
16 | log.info({obj: obj}, 'using defineProperty');
17 | }
18 |
--------------------------------------------------------------------------------
/test/corpus/log2.log:
--------------------------------------------------------------------------------
1 | {"name":"agent2","pid":73267,"hostname":"headnode","level":30,"msg":"message","time":"2012-05-08T16:58:55.586Z","v":0}
2 | {"name":"agent2","pid":73267,"hostname":"headnode","level":30,"msg":"message","time":"2012-05-08T17:01:49.339Z","v":0}
3 | {"name":"agent2","pid":73267,"hostname":"headnode","level":30,"msg":"message","time":"2012-05-08T17:02:47.404Z","v":0}
4 | {"name":"agent2","pid":73267,"hostname":"headnode","level":30,"msg":"message","time":"2012-05-08T17:02:57.404Z","v":0}
5 | {"name":"agent2","pid":76156,"hostname":"headnode","level":30,"msg":"message","time":"2012-05-08T17:08:01.105Z","v":0}
6 |
--------------------------------------------------------------------------------
/tools/timenop.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | /*
3 | * Time logging below the current level, which should do very little work.
4 | */
5 |
6 | console.log('Time log.trace() when log level is "info":');
7 |
8 | var ben = require('ben'); // npm install ben
9 | var bunyan = require('../lib/bunyan');
10 |
11 | function Collector() {}
12 | Collector.prototype.write = function (s) {};
13 |
14 | var log = bunyan.createLogger({
15 | name: 'timeguard',
16 | level: 'info',
17 | stream: new Collector()
18 | });
19 |
20 | var i = 0;
21 | var ms, fields;
22 |
23 | ms = ben(1e7, function () {
24 | log.trace({ count: i++ }, 'hello');
25 | });
26 | console.log(' - log.trace: %dms per iteration', ms);
27 |
--------------------------------------------------------------------------------
/examples/src.js:
--------------------------------------------------------------------------------
1 | // Show the usage of `src: true` config option to get log call source info in
2 | // log records (the `src` field).
3 |
4 | var bunyan = require('../lib/bunyan');
5 |
6 | var log = bunyan.createLogger({name: 'src-example', src: true});
7 |
8 | log.info('one');
9 | log.info('two');
10 | function doSomeFoo() {
11 | log.info({foo:'bar'}, 'three');
12 | }
13 | doSomeFoo();
14 |
15 | function Wuzzle(options) {
16 | this.log = options.log;
17 | this.log.info('creating a wuzzle')
18 | }
19 | Wuzzle.prototype.woos = function () {
20 | this.log.warn('This wuzzle is woosey.')
21 | }
22 |
23 | var wuzzle = new Wuzzle({log: log.child({component: 'wuzzle'})});
24 | wuzzle.woos();
25 | log.info('done with the wuzzle')
26 |
--------------------------------------------------------------------------------
/test/corpus/content-length-0-res.log:
--------------------------------------------------------------------------------
1 | {"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}
2 | {"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}
3 | {"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}
4 |
--------------------------------------------------------------------------------
/examples/hi.js:
--------------------------------------------------------------------------------
1 | var bunyan = require('../lib/bunyan');
2 |
3 | // Basic usage.
4 | var log = bunyan.createLogger({name: 'myapp', level: 'info', src: true});
5 |
6 | // isInfoEnabled replacement
7 | console.log('log.info() is:', log.info())
8 |
9 | // `util.format`-based printf handling
10 | log.info('hi');
11 | log.info('hi', 'trent');
12 | log.info('hi %s there', true);
13 |
14 | // First arg as an object adds fields to the log record.
15 | log.info({foo:'bar', multiline:'one\ntwo\nthree'}, 'hi %d', 1, 'two', 3);
16 |
17 |
18 | // Shows `log.child(...)` to specialize a logger for a sub-component.
19 | console.log('\n')
20 |
21 | function Wuzzle(options) {
22 | this.log = options.log;
23 | this.log.info('creating a wuzzle')
24 | }
25 |
26 | Wuzzle.prototype.woos = function () {
27 | this.log.warn('This wuzzle is woosey.')
28 | }
29 |
30 | var wuzzle = new Wuzzle({log: log.child({component: 'wuzzle'})});
31 | wuzzle.woos();
32 | log.info('done with the wuzzle')
33 |
--------------------------------------------------------------------------------
/tools/timesrc.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | /*
3 | * Time 'src' fields (getting log call source info). This is expensive.
4 | */
5 |
6 | console.log('Time adding "src" field with call source info:');
7 |
8 | var ben = require('ben'); // npm install ben
9 | var Logger = require('../lib/bunyan');
10 |
11 | var records = [];
12 | function Collector() {
13 | }
14 | Collector.prototype.write = function (s) {
15 | //records.push(s);
16 | }
17 | var collector = new Collector();
18 |
19 | var logwith = new Logger({
20 | name: 'with-src',
21 | src: true,
22 | stream: collector
23 | });
24 |
25 | var ms = ben(1e5, function () {
26 | logwith.info('hi');
27 | });
28 | console.log(' - log.info with src: %dms per iteration', ms);
29 |
30 | var logwithout = new Logger({
31 | name: 'without-src',
32 | stream: collector
33 | });
34 | var ms = ben(1e5, function () {
35 | logwithout.info('hi');
36 | });
37 | console.log(' - log.info without src: %dms per iteration', ms);
38 |
--------------------------------------------------------------------------------
/examples/raw-stream.js:
--------------------------------------------------------------------------------
1 | // Example of a "raw" stream in a Bunyan Logger. A raw stream is one to
2 | // which log record *objects* are written instead of the JSON-serialized
3 | // string.
4 |
5 | var bunyan = require('../lib/bunyan');
6 |
7 |
8 | /**
9 | * A raw Bunyan Logger stream object. It takes raw log records and writes
10 | * them to stdout with an added "yo": "yo" field.
11 | */
12 | function MyRawStream() {}
13 | MyRawStream.prototype.write = function (rec) {
14 | if (typeof (rec) !== 'object') {
15 | console.error('error: raw stream got a non-object record: %j', rec)
16 | } else {
17 | rec.yo = 'yo';
18 | process.stdout.write(JSON.stringify(rec) + '\n');
19 | }
20 | }
21 |
22 |
23 | // A Logger using the raw stream.
24 | var log = bunyan.createLogger({
25 | name: 'raw-example',
26 | streams: [
27 | {
28 | level: 'info',
29 | stream: new MyRawStream(),
30 | type: 'raw'
31 | },
32 | ]
33 | });
34 |
35 |
36 | log.info('hi raw stream');
37 | log.info({foo: 'bar'}, 'added "foo" key');
38 |
--------------------------------------------------------------------------------
/test/add-stream.test.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016 Trent Mick. All rights reserved.
3 | *
4 | * Test stream adding.
5 | */
6 |
7 | var bunyan = require('../lib/bunyan');
8 |
9 | // node-tap API
10 | if (require.cache[__dirname + '/tap4nodeunit.js'])
11 | delete require.cache[__dirname + '/tap4nodeunit.js'];
12 | var tap4nodeunit = require('./tap4nodeunit.js');
13 | var test = tap4nodeunit.test;
14 |
15 |
16 | test('non-writables passed as stream', function (t) {
17 | var things = ['process.stdout', {}];
18 | things.forEach(function (thing) {
19 | function createLogger() {
20 | bunyan.createLogger({
21 | name: 'foo',
22 | stream: thing
23 | });
24 | }
25 | t.throws(createLogger,
26 | /stream is not writable/,
27 | '"stream" stream is not writable');
28 | })
29 | t.end();
30 | });
31 |
32 | test('proper stream', function (t) {
33 | var log = bunyan.createLogger({
34 | name: 'foo',
35 | stream: process.stdout
36 | });
37 | t.ok('should not throw');
38 | t.end();
39 | });
40 |
--------------------------------------------------------------------------------
/test/process-exit.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | /*
3 | * Test that bunyan process will terminate.
4 | *
5 | * Note: Currently (bunyan 0.23.1) this fails on node 0.8, because there is
6 | * no `unref` in node 0.8 and bunyan doesn't yet have `Logger.prototype.close()`
7 | * support.
8 | */
9 |
10 | var exec = require('child_process').exec;
11 |
12 | // node-tap API
13 | if (require.cache[__dirname + '/tap4nodeunit.js'])
14 | delete require.cache[__dirname + '/tap4nodeunit.js'];
15 | var tap4nodeunit = require('./tap4nodeunit.js');
16 | var test = tap4nodeunit.test;
17 |
18 | var nodeVer = process.versions.node.split('.').map(Number);
19 |
20 | if (nodeVer[0] <= 0 && nodeVer[1] <= 8) {
21 | console.warn('skip test (node <= 0.8)');
22 | } else {
23 | test('log with rotating file stream will terminate', function (t) {
24 | exec('node ' +__dirname + '/process-exit.js', {timeout: 1000},
25 | function (err, stdout, stderr) {
26 | t.ifError(err);
27 | t.equal(stdout, 'done\n');
28 | t.equal(stderr, '');
29 | t.end();
30 | });
31 | });
32 | }
33 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bunyan",
3 | "version": "2.0.2",
4 | "description": "a JSON logging library for node.js services",
5 | "author": "Trent Mick (http://trentm.com)",
6 | "main": "./lib/bunyan.js",
7 | "bin": {
8 | "bunyan": "./bin/bunyan"
9 | },
10 |
11 | "repository": {
12 | "type": "git",
13 | "url": "git://github.com/trentm/node-bunyan.git"
14 | },
15 | "engines": ["node >=0.10.0"],
16 | "keywords": ["log", "logging", "log4j", "json", "bunyan"],
17 | "license": "MIT",
18 |
19 | "dependencies": {
20 | "exeunt": "1.1.0"
21 | },
22 | "// dtrace-provider": "required for dtrace features",
23 | "// mv": "required for RotatingFileStream",
24 | "// moment": "required for local time with CLI",
25 | "optionalDependencies": {
26 | "dtrace-provider": "~0.8",
27 | "mv": "~2",
28 | "safe-json-stringify": "~1",
29 | "moment": "^2.10.6"
30 | },
31 | "devDependencies": {
32 | "nodeunit": "0.9",
33 | "ben": "0.0.0",
34 | "markdown-toc": "0.12.x",
35 | "verror": "1.3.3",
36 | "vasync": "1.4.3"
37 | },
38 |
39 | "scripts": {
40 | "test": "make test"
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | # This is the MIT license
2 |
3 | Copyright 2016 Trent Mick
4 | Copyright 2016 Joyent Inc.
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a
7 | copy of this software and associated documentation files (the
8 | "Software"), to deal in the Software without restriction, including
9 | without limitation the rights to use, copy, modify, merge, publish,
10 | distribute, sublicense, and/or sell copies of the Software, and to
11 | permit persons to whom the Software is furnished to do so, subject to
12 | the following conditions:
13 |
14 | The above copyright notice and this permission notice shall be included
15 | in all copies or substantial portions of the Software.
16 |
17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
21 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 |
25 |
--------------------------------------------------------------------------------
/examples/level.js:
--------------------------------------------------------------------------------
1 | // Play with setting levels.
2 | //
3 | // TODO: put this in a damn test suite
4 |
5 | var bunyan = require('../lib/bunyan'),
6 | DEBUG = bunyan.DEBUG,
7 | INFO = bunyan.INFO,
8 | WARN = bunyan.WARN;
9 | var assert = require('assert');
10 |
11 | // Basic usage.
12 | var log = bunyan.createLogger({
13 | name: 'example-level',
14 | streams: [
15 | {
16 | name: 'stdout',
17 | stream: process.stdout,
18 | level: 'debug'
19 | },
20 | {
21 | name: 'stderr',
22 | stream: process.stderr
23 | }
24 | ]
25 | });
26 |
27 | assert.equal(log.level(), DEBUG);
28 | assert.equal(log.levels()[0], DEBUG);
29 | assert.equal(log.levels()[1], INFO);
30 | assert.equal(log.levels(0), DEBUG);
31 | assert.equal(log.levels(1), INFO);
32 |
33 | assert.equal(log.levels('stdout'), DEBUG)
34 | try {
35 | log.levels('foo')
36 | } catch (e) {
37 | assert.ok(e.message.indexOf('name') !== -1)
38 | }
39 |
40 | log.trace('no one should see this')
41 | log.debug('should see this once (on stdout)')
42 | log.info('should see this twice')
43 | log.levels('stdout', INFO)
44 | log.debug('no one should see this either')
45 | log.level('trace')
46 | log.trace('should see this twice as 4th and 5th emitted log messages')
47 |
--------------------------------------------------------------------------------
/examples/handle-fs-error.js:
--------------------------------------------------------------------------------
1 | // Example handling an fs error for a Bunyan-created
2 | // stream: we create a logger to a file that is read-only.
3 |
4 | var fs = require('fs');
5 | var path = require('path');
6 | var bunyan = require('../lib/bunyan');
7 |
8 | var FILENAME = 'handle-fs-error.log';
9 | var S_IWUSR = 00200; // mask for owner write permission in stat mode
10 |
11 | console.warn('- Log file is "%s".', FILENAME);
12 | if (!path.existsSync(FILENAME)) {
13 | console.warn('- Touch log file.');
14 | fs.writeFileSync(FILENAME, 'touch\n');
15 | }
16 | if (fs.statSync(FILENAME).mode & S_IWUSR) {
17 | console.warn('- Make log file read-only.');
18 | fs.chmodSync(FILENAME, 0444);
19 | }
20 |
21 | console.warn('- Create logger.');
22 | var log = bunyan.createLogger({
23 | name: 'handle-fs-error',
24 | streams: [
25 | {path: FILENAME}
26 | ]
27 | });
28 |
29 | log.on('error', function (err) {
30 | console.warn('- The logger emitted an error:', err);
31 | });
32 |
33 | console.warn('- Call log.info(...).');
34 | log.info('info log message');
35 | console.warn('- Called log.info(...).');
36 |
37 | setTimeout(function () {
38 | console.warn('- Call log.warn(...).');
39 | log.warn('warn log message');
40 | console.warn('- Called log.warn(...).');
41 | }, 1000);
42 |
--------------------------------------------------------------------------------
/test/ringbuffer.test.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Test the RingBuffer output stream.
3 | */
4 |
5 | var Logger = require('../lib/bunyan');
6 | var ringbuffer = new Logger.RingBuffer({ 'limit': 5 });
7 |
8 | // node-tap API
9 | if (require.cache[__dirname + '/tap4nodeunit.js'])
10 | delete require.cache[__dirname + '/tap4nodeunit.js'];
11 | var tap4nodeunit = require('./tap4nodeunit.js');
12 | var after = tap4nodeunit.after;
13 | var before = tap4nodeunit.before;
14 | var test = tap4nodeunit.test;
15 |
16 |
17 | var log1 = new Logger({
18 | name: 'log1',
19 | streams: [
20 | {
21 | stream: ringbuffer,
22 | type: 'raw',
23 | level: 'info'
24 | }
25 | ]
26 | });
27 |
28 | test('ringbuffer', function (t) {
29 | log1.info('hello');
30 | log1.trace('there');
31 | log1.error('android');
32 | t.equal(ringbuffer.records.length, 2);
33 | t.equal(ringbuffer.records[0]['msg'], 'hello');
34 | t.equal(ringbuffer.records[1]['msg'], 'android');
35 | log1.error('one');
36 | log1.error('two');
37 | log1.error('three');
38 | t.equal(ringbuffer.records.length, 5);
39 | log1.error('four');
40 | t.equal(ringbuffer.records.length, 5);
41 | t.equal(ringbuffer.records[0]['msg'], 'android');
42 | t.equal(ringbuffer.records[1]['msg'], 'one');
43 | t.equal(ringbuffer.records[2]['msg'], 'two');
44 | t.equal(ringbuffer.records[3]['msg'], 'three');
45 | t.equal(ringbuffer.records[4]['msg'], 'four');
46 | t.end();
47 | });
48 |
--------------------------------------------------------------------------------
/test/corpus/old-crashers/144.log:
--------------------------------------------------------------------------------
1 | {"name":"blah_api","hostname":"myhost.home","pid":69312,"level":30,"remote-address":"127.0.0.1","ip":"127.0.0.1","method":"GET","url":"/v0/opps?q=software","referer":"-","user-agent":{"family":"Chrome","major":"34","minor":"0","patch":"1847","device":{"family":"Other"},"os":{"family":"Mac OS X","major":"10","minor":"9","patch":"2"}},"body":{},"short-body":"{}","http-version":"1.1","response-time":232,"status-code":304,"req-headers":{"host":"localhost:3000","connection":"keep-alive","cache-control":"max-age=0","accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8","user-agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.137 Safari/537.36","accept-encoding":"gzip,deflate,sdch","accept-language":"en-US,en;q=0.8","cookie":"__atuvc=2%7C47; csrftoken=XXX; _ga=GA1.1.2104887628.1398883175","if-none-match":"\"-929537647\""},"res-headers":{"x-powered-by":"Express","access-control-allow-origin":"*","access-control-allow-headers":"X-Requested-With","etag":"\"-929537647\""},"req":{"method":"GET","url":"/v0/opps?q=software","headers":"[Circular]","remoteAddress":"127.0.0.1","remotePort":58387},"res":{"statusCode":304,"header":"HTTP/1.1 304 Not Modified\r\nX-Powered-By: Express\r\nAccess-Control-Allow-Origin: *\r\nAccess-Control-Allow-Headers: X-Requested-With\r\nETag: \"-929537647\"\r\nDate: Fri, 16 May 2014 03:04:13 GMT\r\nConnection: keep-alive\r\n\r\n"},"incoming":"<--","msg":"127.0.0.1 <-- GET /v0/opps?q=software HTTP/1.1 304 - - Chrome 34.0 Mac OS X 10.9.2 232 ms","time":"2014-05-16T03:04:13.961Z","v":0}
2 |
--------------------------------------------------------------------------------
/test/src.test.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2015 Trent Mick.
3 | *
4 | * Test `src: true` usage.
5 | */
6 |
7 | // Intentionally on line 8 for tests below:
8 | function logSomething(log) { log.info('something'); }
9 |
10 |
11 | var format = require('util').format;
12 | var Logger = require('../lib/bunyan');
13 |
14 | // node-tap API
15 | if (require.cache[__dirname + '/tap4nodeunit.js'])
16 | delete require.cache[__dirname + '/tap4nodeunit.js'];
17 | var tap4nodeunit = require('./tap4nodeunit.js');
18 | var after = tap4nodeunit.after;
19 | var before = tap4nodeunit.before;
20 | var test = tap4nodeunit.test;
21 |
22 |
23 | function CapturingStream(recs) {
24 | this.recs = recs;
25 | }
26 | CapturingStream.prototype.write = function (rec) {
27 | this.recs.push(rec);
28 | }
29 |
30 |
31 | test('src', function (t) {
32 | var recs = [];
33 |
34 | var log = new Logger({
35 | name: 'src-test',
36 | src: true,
37 | streams: [
38 | {
39 | stream: new CapturingStream(recs),
40 | type: 'raw'
41 | }
42 | ]
43 | });
44 |
45 | log.info('top-level');
46 | logSomething(log);
47 |
48 | t.equal(recs.length, 2);
49 | recs.forEach(function (rec) {
50 | t.ok(rec.src);
51 | t.equal(typeof (rec.src), 'object');
52 | t.equal(rec.src.file, __filename);
53 | t.ok(rec.src.line);
54 | t.equal(typeof (rec.src.line), 'number');
55 | });
56 | var rec = recs[1];
57 | t.ok(rec.src.func);
58 | t.equal(rec.src.func, 'logSomething');
59 | t.equal(rec.src.line, 8);
60 |
61 | t.end();
62 | });
63 |
--------------------------------------------------------------------------------
/test/other-api.test.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012 Trent Mick. All rights reserved.
3 | *
4 | * Test other parts of the exported API.
5 | */
6 |
7 | var bunyan = require('../lib/bunyan');
8 |
9 | // node-tap API
10 | if (require.cache[__dirname + '/tap4nodeunit.js'])
11 | delete require.cache[__dirname + '/tap4nodeunit.js'];
12 | var tap4nodeunit = require('./tap4nodeunit.js');
13 | var after = tap4nodeunit.after;
14 | var before = tap4nodeunit.before;
15 | var test = tap4nodeunit.test;
16 |
17 |
18 | test('bunyan.s', function (t) {
19 | t.ok(bunyan.TRACE, 'TRACE');
20 | t.ok(bunyan.DEBUG, 'DEBUG');
21 | t.ok(bunyan.INFO, 'INFO');
22 | t.ok(bunyan.WARN, 'WARN');
23 | t.ok(bunyan.ERROR, 'ERROR');
24 | t.ok(bunyan.FATAL, 'FATAL');
25 | t.end();
26 | });
27 |
28 | test('bunyan.resolveLevel()', function (t) {
29 | t.equal(bunyan.resolveLevel('trace'), bunyan.TRACE, 'TRACE');
30 | t.equal(bunyan.resolveLevel('TRACE'), bunyan.TRACE, 'TRACE');
31 | t.equal(bunyan.resolveLevel('debug'), bunyan.DEBUG, 'DEBUG');
32 | t.equal(bunyan.resolveLevel('DEBUG'), bunyan.DEBUG, 'DEBUG');
33 | t.equal(bunyan.resolveLevel('info'), bunyan.INFO, 'INFO');
34 | t.equal(bunyan.resolveLevel('INFO'), bunyan.INFO, 'INFO');
35 | t.equal(bunyan.resolveLevel('warn'), bunyan.WARN, 'WARN');
36 | t.equal(bunyan.resolveLevel('WARN'), bunyan.WARN, 'WARN');
37 | t.equal(bunyan.resolveLevel('error'), bunyan.ERROR, 'ERROR');
38 | t.equal(bunyan.resolveLevel('ERROR'), bunyan.ERROR, 'ERROR');
39 | t.equal(bunyan.resolveLevel('fatal'), bunyan.FATAL, 'FATAL');
40 | t.equal(bunyan.resolveLevel('FATAL'), bunyan.FATAL, 'FATAL');
41 | t.end();
42 | });
43 |
--------------------------------------------------------------------------------
/examples/rot-specific-levels.js:
--------------------------------------------------------------------------------
1 | var bunyan = require('./'),
2 | safeCycles = bunyan.safeCycles;
3 | var util = require('util');
4 |
5 |
6 | function SpecificLevelStream(levels, stream) {
7 | var self = this;
8 | this.levels = {};
9 | levels.forEach(function (lvl) {
10 | self.levels[bunyan.resolveLevel(lvl)] = true;
11 | });
12 | this.stream = stream;
13 | }
14 | SpecificLevelStream.prototype.write = function (rec) {
15 | if (this.levels[rec.level] !== undefined) {
16 | var str = JSON.stringify(rec, safeCycles()) + '\n';
17 | this.stream.write(str);
18 | }
19 | }
20 |
21 |
22 | var log = bunyan.createLogger({
23 | name: 'rot-specific-levels',
24 | streams: [
25 | {
26 | type: 'raw',
27 | level: 'debug',
28 | stream: new SpecificLevelStream(
29 | ['debug'],
30 | new bunyan.RotatingFileStream({
31 | path: './rot-specific-levels.debug.log',
32 | period: '3000ms',
33 | count: 10
34 | })
35 | )
36 | },
37 | {
38 | type: 'raw',
39 | level: 'info',
40 | stream: new SpecificLevelStream(
41 | ['info'],
42 | new bunyan.RotatingFileStream({
43 | path: './rot-specific-levels.info.log',
44 | period: '3000ms',
45 | count: 10
46 | })
47 | )
48 | }
49 | ]
50 | });
51 |
52 |
53 | setInterval(function () {
54 | log.trace('hi on trace') // goes nowhere
55 | log.debug('hi on debug') // goes to rot-specific-levels.debug.log.*
56 | log.info('hi on info') // goes to rot-specific-levels.info.log.*
57 | }, 1000);
58 |
--------------------------------------------------------------------------------
/test/tap4nodeunit.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012 Mark Cavage. All rights reserved.
3 | *
4 | * Help nodeunit API feel like node-tap's.
5 | *
6 | * Usage:
7 | * if (require.cache[__dirname + '/tap4nodeunit.js'])
8 | * delete require.cache[__dirname + '/tap4nodeunit.js'];
9 | * var tap4nodeunit = require('./tap4nodeunit.js');
10 | * var after = tap4nodeunit.after;
11 | * var before = tap4nodeunit.before;
12 | * var test = tap4nodeunit.test;
13 | */
14 |
15 |
16 |
17 | //---- Exports
18 |
19 | module.exports = {
20 | after: function after(teardown) {
21 | module.parent.exports.tearDown = function _teardown(callback) {
22 | try {
23 | teardown.call(this, callback);
24 | } catch (e) {
25 | console.error('after:\n' + e.stack);
26 | process.exit(1);
27 | }
28 | };
29 | },
30 |
31 | before: function before(setup) {
32 | module.parent.exports.setUp = function _setup(callback) {
33 | try {
34 | setup.call(this, callback);
35 | } catch (e) {
36 | console.error('before:\n' + e.stack);
37 | process.exit(1);
38 | }
39 | };
40 | },
41 |
42 | test: function test(name, tester) {
43 | module.parent.exports[name] = function _(t) {
44 | var _done = false;
45 | t.end = function end() {
46 | if (!_done) {
47 | _done = true;
48 | t.done();
49 | }
50 | };
51 | t.notOk = function notOk(ok, message) {
52 | return (t.ok(!ok, message));
53 | };
54 | t.error = t.ifError;
55 |
56 | tester.call(this, t);
57 | };
58 | }
59 |
60 |
61 | };
62 |
--------------------------------------------------------------------------------
/tools/colors.log:
--------------------------------------------------------------------------------
1 | # A log file to use to show bunyan colors
2 | {"name":"colors","hostname":"grape.local","pid":52694,"level":10,"msg":"this is the msg","time":"2014-08-10T07:02:40.552Z","src":{"file":"/Users/trentm/tm/node-bunyan/examples/src.js","line":8},"v":0}
3 | {"name":"colors","hostname":"grape.local","pid":52694,"level":20,"msg":"this is the msg","time":"2014-08-10T07:02:40.550Z","v":0}
4 | {"name":"colors","hostname":"grape.local","pid":52694,"level":30,"msg":"this is the msg","time":"2014-08-10T07:02:40.551Z","v":0}
5 | {"name":"colors","hostname":"grape.local","pid":52694,"level":40,"msg":"this is the msg","time":"2014-08-10T07:02:40.551Z","v":0}
6 | {"name":"colors","hostname":"grape.local","pid":52694,"level":50,"msg":"this is the msg","time":"2014-08-10T07:02:40.551Z","v":0}
7 | {"name":"colors","hostname":"grape.local","pid":52694,"agent":"a6359483-80b7-4a71-bb12-84cab83816b3","level":30,"req_id":"baf281a7-e4f3-4e6c-80d3-8154b5b220d0","req":{"method":"HEAD","url":"/agentprobes","headers":{"accept":"application/json","content-type":"application/json","host":"localhost","connection":"close"},"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":"close","date":"Fri, 14 Dec 2012 01:11:04 GMT","server":"Amon Relay/1.0.0","x-request-id":"baf281a7-e4f3-4e6c-80d3-8154b5b220d0","x-response-time":0},"trailer":false},"route":{"name":"HeadAgentProbes","version":false},"latency":0,"secure":false,"_audit":true,"msg":"HeadAgentProbes handled: 200","time":"2012-12-14T01:11:04.218Z","v":0}
8 | {"name":"colors","hostname":"grape.local","pid":52694,"level":60,"msg":"this is the msg","time":"2014-08-10T07:02:40.551Z","v":0}
9 |
--------------------------------------------------------------------------------
/test/corpus/all.log:
--------------------------------------------------------------------------------
1 | # levels
2 | {"name":"myservice","pid":123,"hostname":"example.com","level":10,"msg":"My message","time":"2012-02-08T22:56:50.856Z","v":0}
3 | {"name":"myservice","pid":123,"hostname":"example.com","level":20,"msg":"My message","time":"2012-02-08T22:56:51.856Z","v":0}
4 | {"name":"myservice","pid":123,"hostname":"example.com","level":30,"msg":"My message","time":"2012-02-08T22:56:52.856Z","v":0}
5 | {"name":"myservice","pid":123,"hostname":"example.com","level":40,"msg":"My message","time":"2012-02-08T22:56:53.856Z","v":0}
6 | {"name":"myservice","pid":123,"hostname":"example.com","level":50,"msg":"My message","time":"2012-02-08T22:56:54.856Z","v":0}
7 | {"name":"myservice","pid":123,"hostname":"example.com","level":55,"msg":"My message","time":"2012-02-08T22:56:55.856Z","v":0}
8 | {"name":"myservice","pid":123,"hostname":"example.com","level":60,"msg":"My message","time":"2012-02-08T22:56:56.856Z","v":0}
9 |
10 | # extra fields
11 | {"name":"myservice","pid":123,"hostname":"example.com","level":30,"one":"short","msg":"My message","time":"2012-02-08T22:56:52.856Z","v":0}
12 | {"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}
13 | {"name":"myservice","pid":123,"hostname":"example.com","level":30,"three":"multi\nline","msg":"My message","time":"2012-02-08T22:56:52.856Z","v":0}
14 | {"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}
15 | {"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}
16 | {"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}
17 |
18 | # bogus
19 | not a JSON line
20 | {"hi": "there"}
21 |
--------------------------------------------------------------------------------
/test/corpus/withreq.log:
--------------------------------------------------------------------------------
1 | {"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}
2 | {"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}
3 | {"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}
4 |
--------------------------------------------------------------------------------
/AUTHORS:
--------------------------------------------------------------------------------
1 | Trent Mick (http://trentm.com)
2 | Mark Cavage (https://github.com/mcavage)
3 | Dave Pacheco (https://github.com/davepacheco)
4 | Michael Hart (https://github.com/mhart)
5 | Isaac Schlueter (https://github.com/isaacs)
6 | Rob Gulewich (https://github.com/rgulewich)
7 | Bryan Cantrill (https://github.com/bcantrill)
8 | Michael Hart (https://github.com/mhart)
9 | Simon Wade (https://github.com/aexmachina)
10 | https://github.com/glenn-murray-bse
11 | Chakrit Wichian (https://github.com/chakrit)
12 | Patrick Mooney (https://github.com/pfmooney)
13 | Johan Nordberg (https://github.com/jnordberg)
14 | https://github.com/timborodin
15 | Ryan Graham (https://github.com/rmg)
16 | Alex Kocharin (https://github.com/rlidwka)
17 | Andrei Neculau (https://github.com/andreineculau)
18 | Mihai Tomescu (https://github.com/matomesc)
19 | Daniel Juhl (https://github.com/danieljuhl)
20 | Chris Barber (https://github.com/cb1kenobi)
21 | Manuel Schneider (https://github.com/manuelschneider)
22 | Martin Gausby (https://github.com/gausby)
23 | Stéphan Kochen (https://github.com/stephank)
24 | Shakeel Mohamed (https://github.com/shakeelmohamed)
25 | Denis Izmaylov (https://github.com/DenisIzmaylov)
26 | Guillermo Grau Panea (https://github.com/guigrpa)
27 | Mark LeMerise (https://github.com/MarkLeMerise)
28 | https://github.com/sometimesalready
29 | Charly Koza (https://github.com/Cactusbone)
30 | Thomas Heymann (https://github.com/cyberthom)
31 | David M. Lee (https://github.com/leedm777)
32 | Marc Udoff (https://github.com/mlucool)
33 | Mark Stosberg (https://github.com/markstos)
34 | Alexander Ray (https://github.com/aray12)
35 | Adam Lynch (https://github.com/adam-lynch)
36 | Michael Nisi (https://github.com/michaelnisi)
37 | Martijn Schrage (https://github.com/Oblosys)
38 | Paul Milham (https://github.com/domrein)
39 | Frankie O'Rourke (https://github.com/psfrankie)
40 | Cody Mello (https://github.com/melloc)
41 | Todd Whiteman (https://github.com/twhiteman)
42 | Zach Bjornson (https://github.com/zbjornson)
43 |
--------------------------------------------------------------------------------
/test/cli-res.test.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017, Trent Mick.
3 | *
4 | * Test the bunyan CLI's handling of the "res" field.
5 | */
6 |
7 | var exec = require('child_process').exec;
8 | var fs = require('fs');
9 | var path = require('path');
10 | var _ = require('util').format;
11 | var vasync = require('vasync');
12 |
13 | // node-tap API
14 | if (require.cache[__dirname + '/tap4nodeunit.js'])
15 | delete require.cache[__dirname + '/tap4nodeunit.js'];
16 | var tap4nodeunit = require('./tap4nodeunit.js');
17 | var after = tap4nodeunit.after;
18 | var before = tap4nodeunit.before;
19 | var test = tap4nodeunit.test;
20 |
21 |
22 | // ---- globals
23 |
24 | var BUNYAN = path.resolve(__dirname, '../bin/bunyan');
25 |
26 |
27 | // ---- tests
28 |
29 | test('res with "header" string (issue #444)', function (t) {
30 | var expect = [
31 | /* BEGIN JSSTYLED */
32 | '[2017-08-02T22:37:34.798Z] INFO: res-header/76488 on danger0.local: response sent',
33 | ' HTTP/1.1 200 OK',
34 | ' Foo: bar',
35 | ' Date: Wed, 02 Aug 2017 22:37:34 GMT',
36 | ' Connection: keep-alive',
37 | ' Content-Length: 21'
38 | /* END JSSTYLED */
39 | ].join('\n') + '\n';
40 | exec(_('%s %s/corpus/res-header.log', BUNYAN, __dirname),
41 | function (err, stdout, stderr) {
42 | t.ifError(err);
43 | t.equal(stdout, expect);
44 | t.end();
45 | });
46 | });
47 |
48 | test('res without "header"', function (t) {
49 | var expect = [
50 | /* BEGIN JSSTYLED */
51 | '[2017-08-02T22:37:34.798Z] INFO: res-header/76488 on danger0.local: response sent',
52 | ' HTTP/1.1 200 OK'
53 | /* END JSSTYLED */
54 | ].join('\n') + '\n';
55 | exec(_('%s %s/corpus/res-without-header.log', BUNYAN, __dirname),
56 | function (err, stdout, stderr) {
57 | t.ifError(err);
58 | t.equal(stdout, expect);
59 | t.end();
60 | });
61 | });
62 |
--------------------------------------------------------------------------------
/examples/long-running.js:
--------------------------------------------------------------------------------
1 | /*
2 | * A long-running process that does some periodic logging. Use bunyan with
3 | * it some of these ways:
4 | *
5 | * 1. Direct piping:
6 | * node long-running.js | bunyan
7 | * 2. Logging to file (e.g. if run via a service system like upstart or
8 | * illumos' SMF that sends std output to a log file), then tail -f that
9 | * log file.
10 | * node long-running.js > long-running.log 2>&1
11 | * tail -f long-running.log | bunyan
12 | * 3. Dtrace to watch the logging. This has the bonus of being able to watch
13 | * all log levels... even if not normally emitted.
14 | * node long-running.js > long-running.log 2>&1
15 | * bunyan -p $(head -1 long-running.log | json pid)
16 | *
17 | */
18 |
19 | var fs = require('fs');
20 | var bunyan = require('../lib/bunyan');
21 |
22 |
23 | function randint(n) {
24 | return Math.floor(Math.random() * n);
25 | }
26 |
27 | function randchoice(array) {
28 | return array[randint(array.length)];
29 | }
30 |
31 |
32 | //---- mainline
33 |
34 | var words = fs.readFileSync(
35 | __dirname + '/long-running.js', 'utf8').split(/\s+/);
36 | var levels = ['trace', 'debug', 'info', 'warn', 'error', 'fatal'];
37 | var timeout;
38 |
39 | var log = bunyan.createLogger({name: 'lr', level: 'debug'});
40 |
41 | // We're logging to stdout. Let's exit gracefully on EPIPE. E.g. if piped
42 | // to `head` which will close after N lines.
43 | process.stdout.on('error', function (err) {
44 | if (err.code === 'EPIPE') {
45 | process.exit(0);
46 | }
47 | })
48 |
49 | function logOne() {
50 | var level = randchoice(levels);
51 | var msg = [randchoice(words), randchoice(words)].join(' ');
52 | var delay = randint(300);
53 | //console.warn('long-running about to log.%s(..., "%s")', level, msg)
54 | log[level]({'word': randchoice(words), 'delay': delay}, msg);
55 | timeout = setTimeout(logOne, delay);
56 | }
57 |
58 | log.info('hi, this is the start');
59 | timeout = setTimeout(logOne, 1000);
60 |
--------------------------------------------------------------------------------
/examples/err.js:
--------------------------------------------------------------------------------
1 | // Example logging an error:
2 |
3 | var http = require('http');
4 | var bunyan = require('../lib/bunyan');
5 | var util = require('util');
6 |
7 | var log = bunyan.createLogger({
8 | name: 'myserver',
9 | serializers: {
10 | err: bunyan.stdSerializers.err, // <--- use this
11 | }
12 | });
13 |
14 | try {
15 | throw new TypeError('boom');
16 | } catch (err) {
17 | log.warn({err: err}, 'operation went boom: %s', err) // <--- here
18 | }
19 |
20 | log.info(new TypeError('how about this?')) // <--- alternatively this
21 |
22 |
23 | try {
24 | throw 'boom string';
25 | } catch (err) {
26 | log.error(err)
27 | }
28 |
29 | /* BEGIN JSSTYLED */
30 | /**
31 | *
32 | * $ node err.js | ../bin/bunyan -j
33 | * {
34 | * "name": "myserver",
35 | * "hostname": "banana.local",
36 | * "err": {
37 | * "stack": "TypeError: boom\n at Object. (/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)",
38 | * "name": "TypeError",
39 | * "message": "boom"
40 | * },
41 | * "level": 4,
42 | * "msg": "operation went boom: TypeError: boom",
43 | * "time": "2012-02-02T04:42:53.206Z",
44 | * "v": 0
45 | * }
46 | * $ node err.js | ../bin/bunyan
47 | * [2012-02-02T05:02:39.412Z] WARN: myserver on banana.local: operation went boom: TypeError: boom
48 | * TypeError: boom
49 | * at Object. (/Users/trentm/tm/node-bunyan/examples/err.js:15:9)
50 | * at Module._compile (module.js:411:26)
51 | * at Object..js (module.js:417:10)
52 | * at Module.load (module.js:343:31)
53 | * at Function._load (module.js:302:12)
54 | * at Array.0 (module.js:430:10)
55 | * at EventEmitter._tickCallback (node.js:126:26)
56 | *
57 | */
58 | /* END JSSTYLED */
59 |
--------------------------------------------------------------------------------
/examples/log-undefined-values.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | /* BEGIN JSSTYLED */
3 | /**
4 | * is a change to add a
5 | * feature to Bunyan's log record stringification to log `undefined` values.
6 | * Let's attempt that with a custom raw stream.
7 | *
8 | * Note that a raw stream here isn't ideal, because using a custom raw stream
9 | * means that it is a pain to use some of the other built-in stream types
10 | * (file, rotating-file). However, it might be a satisfactory workaround for
11 | * some.
12 | *
13 | * Example:
14 | * $ node log-undefined-values.js
15 | * {"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}
16 | * $ node log-undefined-values.js | bunyan
17 | * [2017-03-04T20:54:41.874Z] INFO: log-undefined-values/28194 on danger0.local: hi (anull=null, aundef=[Undefined], anum=42, astr=foo)
18 | */
19 | /* END JSSTYLED */
20 |
21 | var bunyan = require('../lib/bunyan');
22 | var fs = require('fs');
23 |
24 |
25 | function replacer() {
26 | // Note: If node > 0.10, then could use Set here (see `safeCyclesSet()`
27 | // in bunyan.js) for a performance improvement.
28 | var seen = [];
29 | return function (key, val) {
30 | if (val === undefined) {
31 | return '[Undefined]';
32 | } else if (!val || typeof (val) !== 'object') {
33 | return val;
34 | }
35 | if (seen.indexOf(val) !== -1) {
36 | return '[Circular]';
37 | }
38 | seen.push(val);
39 | return val;
40 | };
41 | }
42 |
43 | function LogUndefinedValuesStream(stream) {
44 | this.stream = stream;
45 | }
46 | LogUndefinedValuesStream.prototype.write = function (rec) {
47 | var str = JSON.stringify(rec, replacer()) + '\n';
48 | this.stream.write(str);
49 | }
50 |
51 | var log = bunyan.createLogger({
52 | name: 'log-undefined-values',
53 | streams: [ {
54 | level: 'info',
55 | type: 'raw',
56 | stream: new LogUndefinedValuesStream(process.stdout)
57 | } ]
58 | });
59 |
60 | log.info({anull: null, aundef: undefined, anum: 42, astr: 'foo'}, 'hi');
61 |
--------------------------------------------------------------------------------
/examples/specific-level-streams.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | /**
3 | * was a request to have
4 | * bunyan core support logging some levels to one stream and others to another,
5 | * *not* limited by bunyan's current support that a stream `level` implies
6 | * "that level and higher".
7 | *
8 | * Let's do that with a custom raw stream.
9 | */
10 |
11 | var bunyan = require('../lib/bunyan'),
12 | safeCycles = bunyan.safeCycles;
13 | var fs = require('fs');
14 |
15 | /**
16 | * Use case #1: cli tool that outputs errors on stderr and everything else on
17 | * stdout.
18 | *
19 | * First make a raw bunyan stream (i.e. an object with a `.write(rec)`).
20 | */
21 | function SpecificLevelStream(levels, stream) {
22 | var self = this;
23 | this.levels = {};
24 | levels.forEach(function (lvl) {
25 | self.levels[bunyan.resolveLevel(lvl)] = true;
26 | });
27 | this.stream = stream;
28 | }
29 | SpecificLevelStream.prototype.write = function (rec) {
30 | if (this.levels[rec.level] !== undefined) {
31 | var str = JSON.stringify(rec, safeCycles()) + '\n';
32 | this.stream.write(str);
33 | }
34 | }
35 |
36 | var log1 = bunyan.createLogger({
37 | name: 'use-case-1',
38 | streams: [ {
39 | level: 'trace',
40 | type: 'raw',
41 | stream: new SpecificLevelStream(
42 | ['trace', 'debug', 'info', 'warn'],
43 | process.stdout)
44 | }, {
45 | level: 'error',
46 | type: 'raw',
47 | stream: new SpecificLevelStream(
48 | ['error'],
49 | process.stderr)
50 | } ]
51 | });
52 |
53 | log1.info('hi at info level (this should be on stdout)');
54 | log1.error('alert alert (this should be on stderr)');
55 |
56 |
57 | /**
58 | * Use case #2: nginx-style logger that separates error- and access-logs
59 | */
60 | var log2 = bunyan.createLogger({
61 | name: 'use-case-2',
62 | streams: [ {
63 | level: 'info',
64 | type: 'raw',
65 | stream: new SpecificLevelStream(
66 | ['info'],
67 | fs.createWriteStream('specific-level-streams-http.log',
68 | {flags: 'a', encoding: 'utf8'}))
69 | }, {
70 | level: 'warn',
71 | path: 'specific-level-streams-http.err.log'
72 | } ]
73 | });
74 |
75 | log2.info('200 GET /blah');
76 | log2.error('500 GET /boom');
77 |
--------------------------------------------------------------------------------
/tools/timechild.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | /*
3 | * Time `log.child(...)`.
4 | *
5 | * Getting 0.011ms on my Mac. For about 1000 req/s that means that the
6 | * `log.child` would be about 1% of the time handling that request.
7 | * Could do better. I.e. consider a hackish fast path.
8 | *
9 | * ...
10 | *
11 | * Added: `log.fastchild({...}, true)`. Use the `true` to assert that
12 | * the given options are just new fields (and no serializers).
13 | * Result: Another order of magnitude.
14 | */
15 |
16 | var ben = require('ben'); // npm install ben
17 | var Logger = require('../lib/bunyan');
18 |
19 | var log = new Logger({
20 | name: 'svc',
21 | streams: [
22 | {
23 | path: __dirname + '/timechild.log'
24 | },
25 | {
26 | stream: process.stdout
27 | }
28 | ],
29 | serializers: {
30 | err: Logger.stdSerializers.err
31 | }
32 | });
33 |
34 | console.log('Time `log.child`:');
35 |
36 | var ms = ben(1e5, function () {
37 | var child = log.child();
38 | });
39 | console.log(' - adding no fields: %dms per iteration', ms);
40 |
41 | var ms = ben(1e5, function () {
42 | var child = log.child({a:1});
43 | });
44 | console.log(' - adding one field: %dms per iteration', ms);
45 |
46 | var ms = ben(1e5, function () {
47 | var child = log.child({a:1, b:2});
48 | });
49 | console.log(' - adding two fields: %dms per iteration', ms);
50 |
51 | function fooSerializer(obj) {
52 | return {bar: obj.bar};
53 | }
54 | var ms = ben(1e5, function () {
55 | var child = log.child({
56 | a: 1,
57 | serializers: {foo: fooSerializer}
58 | });
59 | });
60 | console.log(' - adding serializer and one field: %dms per iteration', ms);
61 |
62 | var ms = ben(1e5, function () {
63 | var child = log.child({
64 | a: 1,
65 | streams: [ {stream: process.stderr} ]
66 | });
67 | });
68 | console.log(' - adding a (stderr) stream and one field: %dms per iteration',
69 | ms);
70 |
71 | var ms = ben(1e6, function () {
72 | var child = log.child({}, true);
73 | });
74 | console.log(' - [fast] adding no fields: %dms per iteration', ms);
75 |
76 | var ms = ben(1e6, function () {
77 | var child = log.child({a:1}, true);
78 | });
79 | console.log(' - [fast] adding one field: %dms per iteration', ms);
80 |
81 | var ms = ben(1e6, function () {
82 | var child = log.child({a:1, b:2}, true);
83 | });
84 | console.log(' - [fast] adding two fields: %dms per iteration', ms);
85 |
--------------------------------------------------------------------------------
/examples/server.js:
--------------------------------------------------------------------------------
1 | // Example logging HTTP server request and response objects.
2 |
3 | var http = require('http');
4 | var bunyan = require('../lib/bunyan');
5 |
6 | var log = bunyan.createLogger({
7 | name: 'myserver',
8 | serializers: {
9 | req: bunyan.stdSerializers.req,
10 | res: bunyan.stdSerializers.res
11 | }
12 | });
13 |
14 | var server = http.createServer(function (req, res) {
15 | log.info({req: req}, 'start request'); // <-- this is the guy we're testing
16 | res.writeHead(200, {'Content-Type': 'text/plain'});
17 | res.end('Hello World\n');
18 | log.info({res: res}, 'done response'); // <-- this is the guy we're testing
19 | });
20 | server.listen(1337, '127.0.0.1', function () {
21 | log.info('server listening');
22 | var options = {
23 | port: 1337,
24 | hostname: '127.0.0.1',
25 | path: '/path?q=1#anchor',
26 | headers: {
27 | 'X-Hi': 'Mom'
28 | }
29 | };
30 | var req = http.request(options);
31 | req.on('response', function (res) {
32 | res.on('end', function () {
33 | process.exit();
34 | })
35 | });
36 | req.write('hi from the client');
37 | req.end();
38 | });
39 |
40 |
41 | /* BEGIN JSSTYLED */
42 | /**
43 | *
44 | * $ node server.js
45 | * {"service":"myserver","hostname":"banana.local","level":3,"msg":"server listening","time":"2012-02-02T05:32:13.257Z","v":0}
46 | * {"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}
47 | * {"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}
48 | *
49 | * $ node server.js | ../bin/bunyan
50 | * [2012-02-02T05:32:16.006Z] INFO: myserver on banana.local: server listening
51 | * [2012-02-02T05:32:16.010Z] INFO: myserver on banana.local: start request
52 | * GET /path?q=1#anchor
53 | * x-hi: Mom
54 | * connection: close
55 | * [2012-02-02T05:32:16.011Z] INFO: myserver on banana.local: done response
56 | * HTTP/1.1 200 OK
57 | * Content-Type: text/plain
58 | * Connection: close
59 | * Transfer-Encoding: chunked
60 | * (body)
61 | *
62 | */
63 | /* END JSSTYLED */
64 |
--------------------------------------------------------------------------------
/test/buffer.test.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012 Trent Mick. All rights reserved.
3 | * Copyright (c) 2012 Joyent Inc. All rights reserved.
4 | *
5 | * Test logging with (accidental) usage of buffers.
6 | */
7 |
8 | var util = require('util'),
9 | inspect = util.inspect,
10 | format = util.format;
11 | var bunyan = require('../lib/bunyan');
12 |
13 | // node-tap API
14 | if (require.cache[__dirname + '/tap4nodeunit.js'])
15 | delete require.cache[__dirname + '/tap4nodeunit.js'];
16 | var tap4nodeunit = require('./tap4nodeunit.js');
17 | var after = tap4nodeunit.after;
18 | var before = tap4nodeunit.before;
19 | var test = tap4nodeunit.test;
20 |
21 |
22 |
23 | function Catcher() {
24 | this.records = [];
25 | }
26 | Catcher.prototype.write = function (record) {
27 | this.records.push(record);
28 | }
29 |
30 | var catcher = new Catcher();
31 | var log = new bunyan.createLogger({
32 | name: 'buffer.test',
33 | streams: [
34 | {
35 | type: 'raw',
36 | stream: catcher,
37 | level: 'trace'
38 | }
39 | ]
40 | });
41 |
42 |
43 | test('log.info(BUFFER)', function (t) {
44 | var b = new Buffer('foo');
45 |
46 | ['trace',
47 | 'debug',
48 | 'info',
49 | 'warn',
50 | 'error',
51 | 'fatal'].forEach(function (lvl) {
52 | log[lvl].call(log, b);
53 | var rec = catcher.records[catcher.records.length - 1];
54 | t.equal(rec.msg, inspect(b),
55 | format('log.%s msg is inspect(BUFFER)', lvl));
56 | t.ok(rec['0'] === undefined,
57 | 'no "0" array index key in record: ' + inspect(rec['0']));
58 | t.ok(rec['parent'] === undefined,
59 | 'no "parent" array index key in record: ' + inspect(rec['parent']));
60 |
61 | log[lvl].call(log, b, 'bar');
62 | var rec = catcher.records[catcher.records.length - 1];
63 | t.equal(rec.msg, inspect(b) + ' bar', format(
64 | 'log.%s(BUFFER, "bar") msg is inspect(BUFFER) + " bar"', lvl));
65 | });
66 |
67 | t.end();
68 | });
69 |
70 |
71 | //test('log.info({buf: BUFFER})', function (t) {
72 | // var b = new Buffer('foo');
73 | //
74 | // // Really there isn't much Bunyan can do here. See
75 | // // . An unwelcome hack would
76 | // // be to monkey-patch in Buffer.toJSON. Bletch.
77 | // log.info({buf: b}, 'my message');
78 | // var rec = catcher.records[catcher.records.length - 1];
79 | //
80 | // t.end();
81 | //});
82 |
--------------------------------------------------------------------------------
/test/cli-client-req.test.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017, Trent Mick.
3 | *
4 | * Test the bunyan CLI's handling of the "client_req" field.
5 | * "client_req" is a common-ish Bunyan log field from restify-clients. See:
6 | * // JSSTYLED
7 | * https://github.com/restify/clients/blob/85374f87db9f4469de2605b6b15632b317cc12be/lib/helpers/bunyan.js#L213
8 | */
9 |
10 | var exec = require('child_process').exec;
11 | var fs = require('fs');
12 | var path = require('path');
13 | var _ = require('util').format;
14 | var vasync = require('vasync');
15 |
16 | // node-tap API
17 | if (require.cache[__dirname + '/tap4nodeunit.js'])
18 | delete require.cache[__dirname + '/tap4nodeunit.js'];
19 | var tap4nodeunit = require('./tap4nodeunit.js');
20 | var after = tap4nodeunit.after;
21 | var before = tap4nodeunit.before;
22 | var test = tap4nodeunit.test;
23 |
24 |
25 | // ---- globals
26 |
27 | var BUNYAN = path.resolve(__dirname, '../bin/bunyan');
28 |
29 |
30 | // ---- tests
31 |
32 | test('client_req extra newlines, client_res={} (pull #252)', function (t) {
33 | var expect = [
34 | /* BEGIN JSSTYLED */
35 | '[2016-02-10T07:28:40.510Z] TRACE: aclientreq/23280 on danger0.local: request sent',
36 | ' GET /--ping HTTP/1.1',
37 | '[2016-02-10T07:28:41.419Z] TRACE: aclientreq/23280 on danger0.local: Response received',
38 | ' HTTP/1.1 200 OK',
39 | ' request-id: e8a5a700-cfc7-11e5-a3dc-3b85d20f26ef',
40 | ' content-type: application/json'
41 | /* END JSSTYLED */
42 | ].join('\n') + '\n';
43 | exec(_('%s %s/corpus/clientreqres.log', BUNYAN, __dirname),
44 | function (err, stdout, stderr) {
45 | t.ifError(err);
46 | t.equal(stdout, expect);
47 | t.end();
48 | });
49 | });
50 |
51 |
52 | test('client_req.address is not used for Host header in 2.x (issue #504)',
53 | function (t) {
54 | exec(_('%s %s/corpus/client-req-with-address.log', BUNYAN, __dirname),
55 | function (err, stdout, stderr) {
56 | t.ifError(err)
57 | t.equal(stdout, [
58 | // JSSTYLED
59 | '[2017-05-12T23:59:15.877Z] TRACE: minfo/66266 on sharptooth.local: request sent (client_req.address=127.0.0.1)',
60 | ' HEAD /dap/stor HTTP/1.1',
61 | ' accept: application/json, */*',
62 | ' host: foo.example.com',
63 | ' date: Fri, 12 May 2017 23:59:15 GMT',
64 | ''
65 | ].join('\n'));
66 | t.end();
67 | });
68 | });
69 |
--------------------------------------------------------------------------------
/test/cycles.test.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012 Trent Mick. All rights reserved.
3 | *
4 | * Make sure cycles are safe.
5 | */
6 |
7 | var Logger = require('../lib/bunyan.js');
8 |
9 | // node-tap API
10 | if (require.cache[__dirname + '/tap4nodeunit.js'])
11 | delete require.cache[__dirname + '/tap4nodeunit.js'];
12 | var tap4nodeunit = require('./tap4nodeunit.js');
13 | var after = tap4nodeunit.after;
14 | var before = tap4nodeunit.before;
15 | var test = tap4nodeunit.test;
16 |
17 |
18 | var Stream = require('stream').Stream;
19 | var outstr = new Stream;
20 | outstr.writable = true;
21 | var output = [];
22 | outstr.write = function (c) {
23 | output.push(JSON.parse(c + ''));
24 | };
25 | outstr.end = function (c) {
26 | if (c) this.write(c);
27 | this.emit('end');
28 | };
29 |
30 | // these are lacking a few fields that will probably never match
31 | var expect =
32 | [
33 | {
34 | 'name': 'blammo',
35 | 'level': 30,
36 | 'msg': 'bango { bang: \'boom\', KABOOM: [Circular] }',
37 | 'v': 0
38 | },
39 | {
40 | 'name': 'blammo',
41 | 'level': 30,
42 | 'msg': 'kaboom { bang: \'boom\', KABOOM: [Circular] }',
43 | 'v': 0
44 | },
45 | {
46 | 'name': 'blammo',
47 | 'level': 30,
48 | 'bang': 'boom',
49 | 'KABOOM': {
50 | 'bang': 'boom',
51 | 'KABOOM': '[Circular]'
52 | },
53 | 'msg': '',
54 | 'v': 0
55 | }
56 | ];
57 |
58 | var log = new Logger({
59 | name: 'blammo',
60 | streams: [
61 | {
62 | type: 'stream',
63 | level: 'info',
64 | stream: outstr
65 | }
66 | ]
67 | });
68 |
69 | test('cycles', function (t) {
70 | outstr.on('end', function () {
71 | output.forEach(function (o, i) {
72 | // Drop variable parts for comparison.
73 | delete o.hostname;
74 | delete o.pid;
75 | delete o.time;
76 | // Hack object/dict comparison: JSONify.
77 | t.equal(JSON.stringify(o), JSON.stringify(expect[i]),
78 | 'log item ' + i + ' matches');
79 | });
80 | t.end();
81 | });
82 |
83 | var obj = { bang: 'boom' };
84 | obj.KABOOM = obj;
85 | log.info('bango', obj);
86 | log.info('kaboom', obj.KABOOM);
87 | log.info(obj);
88 | outstr.end();
89 | t.ok('did not throw');
90 | });
91 |
--------------------------------------------------------------------------------
/test/safe-json-stringify.test.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2015 Trent Mick. All rights reserved.
3 | *
4 | * If available, use `safe-json-stringfy` as a fallback stringifier.
5 | * This covers the case where an enumerable property throws an error
6 | * in its getter.
7 | *
8 | * See
9 | */
10 |
11 | var p = console.warn;
12 | var exec = require('child_process').exec;
13 |
14 | // node-tap API
15 | if (require.cache[__dirname + '/tap4nodeunit.js'])
16 | delete require.cache[__dirname + '/tap4nodeunit.js'];
17 | var tap4nodeunit = require('./tap4nodeunit.js');
18 | var after = tap4nodeunit.after;
19 | var before = tap4nodeunit.before;
20 | var test = tap4nodeunit.test;
21 |
22 |
23 | test('__defineGetter__ boom', function (t) {
24 | var cmd = process.execPath + ' ' + __dirname + '/safe-json-stringify-1.js';
25 | exec(cmd, function (err, stdout, stderr) {
26 | t.ifError(err, err);
27 | var rec = JSON.parse(stdout.trim());
28 | t.equal(rec.obj.boom, '[Throws: __defineGetter__ ouch!]');
29 | t.end();
30 | });
31 | });
32 |
33 | test('__defineGetter__ boom, without safe-json-stringify', function (t) {
34 | var cmd = process.execPath + ' ' + __dirname + '/safe-json-stringify-2.js';
35 | exec(cmd, function (err, stdout, stderr) {
36 | t.ifError(err, err);
37 | t.ok(stdout.indexOf('Exception in JSON.stringify') !== -1);
38 | t.ok(stderr.indexOf(
39 | 'You can install the "safe-json-stringify" module') !== -1);
40 | t.end();
41 | });
42 | });
43 |
44 | test('defineProperty boom', function (t) {
45 | var cmd = process.execPath + ' ' + __dirname + '/safe-json-stringify-3.js';
46 | exec(cmd, function (err, stdout, stderr) {
47 | t.ifError(err, err);
48 | var recs = stdout.trim().split(/\n/g);
49 | t.equal(recs.length, 2);
50 | var rec = JSON.parse(recs[0]);
51 | t.equal(rec.obj.boom, '[Throws: defineProperty ouch!]');
52 | t.end();
53 | });
54 | });
55 |
56 | test('defineProperty boom, without safe-json-stringify', function (t) {
57 | var cmd = process.execPath + ' ' + __dirname + '/safe-json-stringify-4.js';
58 | exec(cmd, function (err, stdout, stderr) {
59 | t.ifError(err, err);
60 | t.ok(stdout.indexOf('Exception in JSON.stringify') !== -1);
61 | t.equal(stdout.match(/Exception in JSON.stringify/g).length, 2);
62 | t.ok(stderr.indexOf(
63 | 'You can install the "safe-json-stringify" module') !== -1);
64 | t.equal(stderr.match(
65 | /* JSSTYLED */
66 | /You can install the "safe-json-stringify" module/g).length, 1);
67 | t.end();
68 | });
69 | });
70 |
--------------------------------------------------------------------------------
/test/level.test.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2014 Trent Mick. All rights reserved.
3 | *
4 | * Test the `log.level(...)`.
5 | */
6 |
7 | var util = require('util'),
8 | format = util.format,
9 | inspect = util.inspect;
10 | var p = console.log;
11 |
12 | var bunyan = require('../lib/bunyan');
13 |
14 | // node-tap API
15 | if (require.cache[__dirname + '/tap4nodeunit.js'])
16 | delete require.cache[__dirname + '/tap4nodeunit.js'];
17 | var tap4nodeunit = require('./tap4nodeunit.js');
18 | var after = tap4nodeunit.after;
19 | var before = tap4nodeunit.before;
20 | var test = tap4nodeunit.test;
21 |
22 |
23 | // ---- test boolean `log.()` calls
24 |
25 | var log1 = bunyan.createLogger({
26 | name: 'log1',
27 | streams: [
28 | {
29 | path: __dirname + '/level.test.log1.log',
30 | level: 'info'
31 | }
32 | ]
33 | });
34 |
35 |
36 | test('log.level() -> level num', function (t) {
37 | t.equal(log1.level(), bunyan.INFO);
38 | t.end();
39 | });
40 |
41 | test('log.level()', function (t) {
42 | log1.level(bunyan.DEBUG);
43 | t.equal(log1.level(), bunyan.DEBUG);
44 | t.end();
45 | });
46 |
47 | test('log.level()', function (t) {
48 | log1.level(10);
49 | t.equal(log1.level(), bunyan.TRACE);
50 | t.end();
51 | });
52 |
53 | test('log.level()', function (t) {
54 | log1.level('error');
55 | t.equal(log1.level(), bunyan.ERROR);
56 | t.end();
57 | });
58 |
59 | // A trick to turn logging off.
60 | // See .
61 | test('log.level(FATAL + 1)', function (t) {
62 | log1.level(bunyan.FATAL + 1);
63 | t.equal(log1.level(), bunyan.FATAL + 1);
64 | t.end();
65 | });
66 |
67 | test('log.level()', function (t) {
68 | log1.level(0);
69 | t.equal(log1.level(), 0);
70 | log1.level(Number.MAX_VALUE);
71 | t.equal(log1.level(), Number.MAX_VALUE);
72 | log1.level(Infinity);
73 | t.equal(log1.level(), Infinity);
74 | t.end();
75 | });
76 |
77 | test('log.level()', function (t) {
78 | t.throws(function () {
79 | var log = bunyan.createLogger({name: 'invalid', level: 'booga'});
80 | // JSSTYLED
81 | }, /unknown level name: "booga"/);
82 | t.throws(function () {
83 | var log = bunyan.createLogger({name: 'invalid', level: []});
84 | }, /cannot resolve level: invalid arg \(object\): \[\]/);
85 | t.throws(function () {
86 | var log = bunyan.createLogger({name: 'invalid', level: true});
87 | }, /cannot resolve level: invalid arg \(boolean\): true/);
88 | t.throws(function () {
89 | var log = bunyan.createLogger({name: 'invalid', level: -1});
90 | }, /level is not a positive integer: -1/);
91 | t.throws(function () {
92 | var log = bunyan.createLogger({name: 'invalid', level: 3.14});
93 | }, /level is not a positive integer: 3.14/);
94 | t.throws(function () {
95 | var log = bunyan.createLogger({name: 'invalid', level: -Infinity});
96 | }, /level is not a positive integer: -Infinity/);
97 | t.end();
98 | });
99 |
--------------------------------------------------------------------------------
/test/raw-stream.test.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012 Trent Mick. All rights reserved.
3 | *
4 | * Test `type: 'raw'` Logger streams.
5 | */
6 |
7 | var format = require('util').format;
8 | var Logger = require('../lib/bunyan');
9 |
10 | // node-tap API
11 | if (require.cache[__dirname + '/tap4nodeunit.js'])
12 | delete require.cache[__dirname + '/tap4nodeunit.js'];
13 | var tap4nodeunit = require('./tap4nodeunit.js');
14 | var after = tap4nodeunit.after;
15 | var before = tap4nodeunit.before;
16 | var test = tap4nodeunit.test;
17 |
18 |
19 | function CapturingStream(recs) {
20 | this.recs = recs;
21 | }
22 | CapturingStream.prototype.write = function (rec) {
23 | this.recs.push(rec);
24 | }
25 |
26 |
27 | test('raw stream', function (t) {
28 | var recs = [];
29 |
30 | var log = new Logger({
31 | name: 'raw-stream-test',
32 | streams: [
33 | {
34 | stream: new CapturingStream(recs),
35 | type: 'raw'
36 | }
37 | ]
38 | });
39 | log.info('first');
40 | log.info({two: 'deux'}, 'second');
41 |
42 | t.equal(recs.length, 2);
43 | t.equal(typeof (recs[0]), 'object', 'first rec is an object');
44 | t.equal(recs[1].two, 'deux', '"two" field made it through');
45 | t.end();
46 | });
47 |
48 |
49 | test('raw streams and regular streams can mix', function (t) {
50 | var rawRecs = [];
51 | var nonRawRecs = [];
52 |
53 | var log = new Logger({
54 | name: 'raw-stream-test',
55 | streams: [
56 | {
57 | stream: new CapturingStream(rawRecs),
58 | type: 'raw'
59 | },
60 | {
61 | stream: new CapturingStream(nonRawRecs)
62 | }
63 | ]
64 | });
65 | log.info('first');
66 | log.info({two: 'deux'}, 'second');
67 |
68 | t.equal(rawRecs.length, 2);
69 | t.equal(typeof (rawRecs[0]), 'object', 'first rawRec is an object');
70 | t.equal(rawRecs[1].two, 'deux', '"two" field made it through');
71 |
72 | t.equal(nonRawRecs.length, 2);
73 | t.equal(typeof (nonRawRecs[0]), 'string', 'first nonRawRec is a string');
74 |
75 | t.end();
76 | });
77 |
78 |
79 | test('child adding a non-raw stream works', function (t) {
80 | var parentRawRecs = [];
81 | var rawRecs = [];
82 | var nonRawRecs = [];
83 |
84 | var logParent = new Logger({
85 | name: 'raw-stream-test',
86 | streams: [
87 | {
88 | stream: new CapturingStream(parentRawRecs),
89 | type: 'raw'
90 | }
91 | ]
92 | });
93 | var logChild = logParent.child({
94 | child: true,
95 | streams: [
96 | {
97 | stream: new CapturingStream(rawRecs),
98 | type: 'raw'
99 | },
100 | {
101 | stream: new CapturingStream(nonRawRecs)
102 | }
103 | ]
104 | });
105 | logParent.info('first');
106 | logChild.info({two: 'deux'}, 'second');
107 |
108 | t.equal(rawRecs.length, 1,
109 | format('rawRecs length should be 1 (is %d)', rawRecs.length));
110 | t.equal(typeof (rawRecs[0]), 'object', 'rawRec entry is an object');
111 | t.equal(rawRecs[0].two, 'deux', '"two" field made it through');
112 |
113 | t.equal(nonRawRecs.length, 1);
114 | t.equal(typeof (nonRawRecs[0]), 'string', 'first nonRawRec is a string');
115 |
116 | t.end();
117 | });
118 |
--------------------------------------------------------------------------------
/examples/mute-by-envvars-stream.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Example of a MuteByEnvVars Bunyan stream to mute log records matching some
3 | * envvars. I.e. as a way to do:
4 | * https://github.com/trentm/node-bunyan/issues/175
5 | * https://github.com/trentm/node-bunyan/pull/176
6 | * outside of core.
7 | *
8 | * Usage:
9 | * $ node mute-by-envvars-stream.js
10 | * {"name":"mute-by-envvars-stream",...,"msg":"hi raw stream"}
11 | * {"name":"mute-by-envvars-stream",...,"foo":"bar","msg":"added \"foo\" key"}
12 | *
13 | * $ BUNYAN_MUTE_foo=bar node mute-by-envvars-stream.js
14 | * {"name":"mute-by-envvars-stream",...,"msg":"hi raw stream"}
15 | *
16 | * Dev Notes:
17 | * - This currently treats all 'BUNYAN_MUTE_foo=bar' envvar values as strings.
18 | * That might not be desired.
19 | * - This is a quick implementation: inefficient and not well tested.
20 | * - Granted that Bunyan streams are hard to compose. For example, using
21 | * `MuteByEnvVars` to be a filter before writing logs to a *file* is a pain
22 | * for the file open/close handling. It would be nicer if Bunyan had a
23 | * pipeline of "filters" (more like core node.js streams).
24 | */
25 |
26 | var bunyan = require('../lib/bunyan');
27 |
28 |
29 | function MuteByEnvVars(opts) {
30 | opts = opts || {};
31 | this.stream = opts.stream || process.stdout;
32 |
33 | var PREFIX = 'BUNYAN_MUTE_';
34 |
35 | // Process the env once.
36 | this.mutes = {};
37 | for (k in process.env) {
38 | if (k.indexOf(PREFIX) === 0) {
39 | this.mutes[k.slice(PREFIX.length)] = process.env[k];
40 | }
41 | }
42 | }
43 |
44 | /**
45 | * Returns the given object's "o" property named by "s" using the dot notation.
46 | *
47 | * this({ name: { first: "value" } }, name.first) == "value"
48 | *
49 | * This is a verbatin copy of http://stackoverflow.com/a/6491621/433814
50 | *
51 | * @param o {object} is an object.
52 | * @param s (string} is the string in the "dot" notation.
53 | */
54 | MuteByEnvVars.prototype._objectFromDotNotation = function (o, s) {
55 | s = s.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties
56 | s = s.replace(/^\./, ''); // strip leading dot
57 | var a = s.split('.');
58 | while (a.length) {
59 | var n = a.shift();
60 | if (n in o) {
61 | o = o[n];
62 | } else {
63 | return;
64 | }
65 | }
66 | return o;
67 | }
68 |
69 | MuteByEnvVars.prototype.write = function (rec) {
70 | if (typeof (rec) !== 'object') {
71 | console.error('error: MuteByEnvVars raw stream got a non-object '
72 | + 'record: %j', rec);
73 | return;
74 | }
75 |
76 | var muteRec = false;
77 | var keys = Object.keys(this.mutes);
78 | for (var i = 0; i < keys.length; i++) {
79 | var k = keys[i];
80 | var match = this._objectFromDotNotation(rec, k);
81 | if (match === this.mutes[k]) {
82 | muteRec = true;
83 | break;
84 | }
85 | }
86 | if (!muteRec) {
87 | this.stream.write(JSON.stringify(rec) + '\n');
88 | }
89 | }
90 |
91 |
92 |
93 | // ---- example usage of the MuteByEnvVars stream
94 |
95 | var log = bunyan.createLogger({
96 | name: 'mute-by-envvars-stream',
97 | streams: [
98 | {
99 | level: 'info',
100 | stream: new MuteByEnvVars(),
101 | type: 'raw'
102 | },
103 | ]
104 | });
105 |
106 |
107 | log.info('hi raw stream');
108 | log.info({foo: 'bar'}, 'added "foo" key');
109 |
--------------------------------------------------------------------------------
/tools/timeguard.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | /*
3 | * Time logging with/without a try/catch-guard on the JSON.stringify
4 | * and other code options around that section (see #427).
5 | */
6 |
7 | console.log('Time JSON.stringify and alternatives in Logger._emit:');
8 |
9 | var ben = require('ben'); // npm install ben
10 | var bunyan = require('../lib/bunyan');
11 |
12 | function Collector() {}
13 | Collector.prototype.write = function (s) {};
14 |
15 | var log = bunyan.createLogger({
16 | name: 'timeguard',
17 | stream: new Collector()
18 | });
19 |
20 | var ms, fields;
21 | ms = ben(1e5, function () { log.info('hi'); });
22 | console.log(' - log.info with no fields: %dms per iteration', ms);
23 |
24 | fields = {foo: 'bar'};
25 | ms = ben(1e5, function () { log.info(fields, 'hi'); });
26 | console.log(' - log.info with small fields: %dms per iteration', ms);
27 |
28 | fields = {
29 | versions: process.versions,
30 | moduleLoadList: process.moduleLoadList
31 | };
32 | ms = ben(1e5, function () { log.info(fields, 'hi'); });
33 | console.log(' - log.info with medium fields: %dms per iteration', ms);
34 |
35 | // JSSTYLED
36 | fields = {"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};
37 | ms = ben(1e5, function () { log.info(fields, 'hi'); });
38 | console.log(' - log.info with large fields: %dms per iteration', ms);
39 |
40 | console.log('\nNow you need to manually change `Logger.prototype._emit` in'
41 | + '"../lib/bunyan.js"\nto an alternative impl. Then re-run this a '
42 | + 'few times to compare speed.');
43 |
--------------------------------------------------------------------------------
/test/dtrace.test.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016 Trent Mick
3 | *
4 | * If available, test dtrace support.
5 | */
6 |
7 | var spawn = require('child_process').spawn;
8 | var format = require('util').format;
9 |
10 | var bunyan = require('../lib/bunyan');
11 |
12 | // node-tap API
13 | if (require.cache[__dirname + '/tap4nodeunit.js'])
14 | delete require.cache[__dirname + '/tap4nodeunit.js'];
15 | var tap4nodeunit = require('./tap4nodeunit.js');
16 | var after = tap4nodeunit.after;
17 | var before = tap4nodeunit.before;
18 | var test = tap4nodeunit.test;
19 |
20 |
21 | // Determine if we can run the dtrace tests.
22 | var dtracePlats = ['sunos', 'darwin', 'freebsd'];
23 | var runDtraceTests = true;
24 | try {
25 | require('dtrace-provider');
26 | } catch (e) {
27 | console.log('# skip dtrace tests: no dtrace-provider module');
28 | runDtraceTests = false;
29 | }
30 | if (!runDtraceTests) {
31 | /* pass through */
32 | } else if (dtracePlats.indexOf(process.platform) === -1) {
33 | console.log('# skip dtrace tests: not on a platform with dtrace');
34 | runDtraceTests = false;
35 | } else if (process.env.SKIP_DTRACE) {
36 | console.log('# skip dtrace tests: SKIP_DTRACE envvar set');
37 | runDtraceTests = false;
38 | } else if (process.getgid() !== 0) {
39 | console.log('# skip dtrace tests: gid is not 0, run with `sudo`');
40 | runDtraceTests = false;
41 | }
42 | if (runDtraceTests) {
43 |
44 |
45 | test('basic dtrace', function (t) {
46 | var argv = ['dtrace', '-Z', '-x', 'strsize=4k', '-qn',
47 | 'bunyan$target:::log-*{printf("%s", copyinstr(arg0))}',
48 | '-c', format('node %s/log-some.js', __dirname)];
49 | var dtrace = spawn(argv[0], argv.slice(1));
50 | //console.error('ARGV: %j', argv);
51 | //console.error('CMD: %s', argv.join(' '));
52 |
53 | var traces = [];
54 | dtrace.stdout.on('data', function (data) {
55 | //console.error('DTRACE STDOUT:', data.toString());
56 | traces.push(data.toString());
57 | });
58 | dtrace.stderr.on('data', function (data) {
59 | console.error('DTRACE STDERR:', data.toString());
60 | });
61 | dtrace.on('exit', function (code) {
62 | t.notOk(code, 'dtrace exited cleanly');
63 | traces = traces.join('').split('\n')
64 | .filter(function (t) { return t.trim().length })
65 | .map(function (t) { return JSON.parse(t) });
66 | t.equal(traces.length, 2, 'got 2 log records');
67 | if (traces.length) {
68 | t.equal(traces[0].level, bunyan.DEBUG);
69 | t.equal(traces[0].foo, 'bar');
70 | t.equal(traces[1].level, bunyan.TRACE);
71 | t.equal(traces[1].msg, 'hi at trace');
72 | }
73 | t.end();
74 | });
75 | });
76 |
77 |
78 | /*
79 | * Run a logger that logs a couple records every second.
80 | * Then run `bunyan -p PID` to capture.
81 | * Let those run for a few seconds to ensure dtrace has time to attach and
82 | * capture something.
83 | */
84 | test('bunyan -p', function (t) {
85 | var p = spawn('node', [__dirname + '/log-some-loop.js']);
86 |
87 | var bunyanP = spawn('node',
88 | [__dirname + '/../bin/bunyan', '-p', String(p.pid), '-0']);
89 | var traces = [];
90 | bunyanP.stdout.on('data', function (data) {
91 | //console.error('BUNYAN -P STDOUT:', data.toString());
92 | traces.push(data.toString());
93 | });
94 | bunyanP.stderr.on('data', function (data) {
95 | console.error('BUNYAN -P STDERR:', data.toString());
96 | });
97 | bunyanP.on('exit', function (code) {
98 | traces = traces.join('').split('\n')
99 | .filter(function (t) { return t.trim().length })
100 | .map(function (t) { return JSON.parse(t) });
101 | t.ok(traces.length >= 3, 'got >=3 log records: ' + traces.length);
102 | if (traces.length >= 3) {
103 | if (traces[0].level !== bunyan.DEBUG) {
104 | traces.shift();
105 | }
106 | t.equal(traces[0].level, bunyan.DEBUG);
107 | t.equal(traces[0].foo, 'bar');
108 | t.equal(traces[1].level, bunyan.TRACE);
109 | t.equal(traces[1].msg, 'hi at trace');
110 | }
111 | t.end();
112 | });
113 |
114 | // Give it a few seconds to ensure we get some traces.
115 | setTimeout(function () {
116 | p.kill();
117 | bunyanP.kill();
118 | }, 5000);
119 | });
120 |
121 |
122 | } /* end of `if (runDtraceTests)` */
123 |
--------------------------------------------------------------------------------
/test/child-behaviour.test.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012 Trent Mick. All rights reserved.
3 | *
4 | * Test some `.child(...)` behaviour.
5 | */
6 |
7 | var bunyan = require('../lib/bunyan');
8 |
9 | // node-tap API
10 | if (require.cache[__dirname + '/tap4nodeunit.js'])
11 | delete require.cache[__dirname + '/tap4nodeunit.js'];
12 | var tap4nodeunit = require('./tap4nodeunit.js');
13 | var after = tap4nodeunit.after;
14 | var before = tap4nodeunit.before;
15 | var test = tap4nodeunit.test;
16 |
17 |
18 |
19 | function CapturingStream(recs) {
20 | this.recs = recs || [];
21 | }
22 | CapturingStream.prototype.write = function (rec) {
23 | this.recs.push(rec);
24 | }
25 |
26 |
27 |
28 | test('child can add stream', function (t) {
29 | var dadStream = new CapturingStream();
30 | var dad = bunyan.createLogger({
31 | name: 'surname',
32 | streams: [ {
33 | type: 'raw',
34 | stream: dadStream,
35 | level: 'info'
36 | } ]
37 | });
38 |
39 | var sonStream = new CapturingStream();
40 | var son = dad.child({
41 | component: 'son',
42 | streams: [ {
43 | type: 'raw',
44 | stream: sonStream,
45 | level: 'debug'
46 | } ]
47 | });
48 |
49 | dad.info('info from dad');
50 | dad.debug('debug from dad');
51 | son.debug('debug from son');
52 |
53 | var rec;
54 | t.equal(dadStream.recs.length, 1);
55 | rec = dadStream.recs[0];
56 | t.equal(rec.msg, 'info from dad');
57 | t.equal(sonStream.recs.length, 1);
58 | rec = sonStream.recs[0];
59 | t.equal(rec.msg, 'debug from son');
60 |
61 | t.end();
62 | });
63 |
64 |
65 | test('child can set level of inherited streams', function (t) {
66 | var dadStream = new CapturingStream();
67 | var dad = bunyan.createLogger({
68 | name: 'surname',
69 | streams: [ {
70 | type: 'raw',
71 | stream: dadStream,
72 | level: 'info'
73 | } ]
74 | });
75 |
76 | // Intention here is that the inherited `dadStream` logs at 'debug' level
77 | // for the son.
78 | var son = dad.child({
79 | component: 'son',
80 | level: 'debug'
81 | });
82 |
83 | dad.info('info from dad');
84 | dad.debug('debug from dad');
85 | son.debug('debug from son');
86 |
87 | var rec;
88 | t.equal(dadStream.recs.length, 2);
89 | rec = dadStream.recs[0];
90 | t.equal(rec.msg, 'info from dad');
91 | rec = dadStream.recs[1];
92 | t.equal(rec.msg, 'debug from son');
93 |
94 | t.end();
95 | });
96 |
97 |
98 | test('child can set level of inherited streams and add streams', function (t) {
99 | var dadStream = new CapturingStream();
100 | var dad = bunyan.createLogger({
101 | name: 'surname',
102 | streams: [ {
103 | type: 'raw',
104 | stream: dadStream,
105 | level: 'info'
106 | } ]
107 | });
108 |
109 | // Intention here is that the inherited `dadStream` logs at 'debug' level
110 | // for the son.
111 | var sonStream = new CapturingStream();
112 | var son = dad.child({
113 | component: 'son',
114 | level: 'trace',
115 | streams: [ {
116 | type: 'raw',
117 | stream: sonStream,
118 | level: 'debug'
119 | } ]
120 | });
121 |
122 | dad.info('info from dad');
123 | dad.trace('trace from dad');
124 | son.trace('trace from son');
125 | son.debug('debug from son');
126 |
127 | t.equal(dadStream.recs.length, 3);
128 | t.equal(dadStream.recs[0].msg, 'info from dad');
129 | t.equal(dadStream.recs[1].msg, 'trace from son');
130 | t.equal(dadStream.recs[2].msg, 'debug from son');
131 |
132 | t.equal(sonStream.recs.length, 1);
133 | t.equal(sonStream.recs[0].msg, 'debug from son');
134 |
135 | t.end();
136 | });
137 |
138 | // issue #291
139 | test('child should not lose parent "hostname"', function (t) {
140 | var stream = new CapturingStream();
141 | var dad = bunyan.createLogger({
142 | name: 'hostname-test',
143 | hostname: 'bar0',
144 | streams: [ {
145 | type: 'raw',
146 | stream: stream,
147 | level: 'info'
148 | } ]
149 | });
150 | var son = dad.child({component: 'son'});
151 |
152 | dad.info('HI');
153 | son.info('hi');
154 |
155 | t.equal(stream.recs.length, 2);
156 | t.equal(stream.recs[0].hostname, 'bar0');
157 | t.equal(stream.recs[1].hostname, 'bar0');
158 | t.equal(stream.recs[1].component, 'son');
159 |
160 | t.end();
161 | });
162 |
--------------------------------------------------------------------------------
/test/error-event.test.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016 Trent Mick
3 | *
4 | * Test emission and handling of 'error' event in a logger with a 'path'
5 | * stream.
6 | */
7 |
8 | var EventEmitter = require('events').EventEmitter;
9 | var util = require('util');
10 |
11 | var bunyan = require('../lib/bunyan');
12 |
13 | // node-tap API
14 | if (require.cache[__dirname + '/tap4nodeunit.js'])
15 | delete require.cache[__dirname + '/tap4nodeunit.js'];
16 | var tap4nodeunit = require('./tap4nodeunit.js');
17 | var after = tap4nodeunit.after;
18 | var before = tap4nodeunit.before;
19 | var test = tap4nodeunit.test;
20 |
21 |
22 | var BOGUS_PATH = '/this/path/is/bogus.log';
23 |
24 | test('error event on file stream (reemitErrorEvents=undefined)', function (t) {
25 | var log = bunyan.createLogger(
26 | {name: 'error-event-1', streams: [ {path: BOGUS_PATH} ]});
27 | log.on('error', function (err, stream) {
28 | t.ok(err, 'got err in error event: ' + err);
29 | t.equal(err.code, 'ENOENT', 'error code is ENOENT');
30 | t.ok(stream, 'got a stream argument');
31 | t.equal(stream.path, BOGUS_PATH);
32 | t.equal(stream.type, 'file');
33 | t.end();
34 | });
35 | log.info('info log message');
36 | });
37 |
38 | test('error event on file stream (reemitErrorEvents=true)', function (t) {
39 | var log = bunyan.createLogger({
40 | name: 'error-event-2',
41 | streams: [ {
42 | path: BOGUS_PATH,
43 | reemitErrorEvents: true
44 | } ]
45 | });
46 | log.on('error', function (err, stream) {
47 | t.ok(err, 'got err in error event: ' + err);
48 | t.equal(err.code, 'ENOENT', 'error code is ENOENT');
49 | t.ok(stream, 'got a stream argument');
50 | t.equal(stream.path, BOGUS_PATH);
51 | t.equal(stream.type, 'file');
52 | t.end();
53 | });
54 | log.info('info log message');
55 | });
56 |
57 | test('error event on file stream (reemitErrorEvents=false)',
58 | function (t) {
59 | var log = bunyan.createLogger({
60 | name: 'error-event-3',
61 | streams: [ {
62 | path: BOGUS_PATH,
63 | reemitErrorEvents: false
64 | } ]
65 | });
66 | // Hack into the underlying created file stream to catch the error event.
67 | log.streams[0].stream.on('error', function (err) {
68 | t.ok(err, 'got error event on the file stream');
69 | t.end();
70 | });
71 | log.on('error', function (err, stream) {
72 | t.fail('should not have gotten error event on logger');
73 | t.end();
74 | });
75 | log.info('info log message');
76 | });
77 |
78 |
79 | function MyErroringStream() {}
80 | util.inherits(MyErroringStream, EventEmitter);
81 | MyErroringStream.prototype.write = function (rec) {
82 | this.emit('error', new Error('boom'));
83 | }
84 |
85 | test('error event on raw stream (reemitErrorEvents=undefined)', function (t) {
86 | var estream = new MyErroringStream();
87 | var log = bunyan.createLogger({
88 | name: 'error-event-raw',
89 | streams: [
90 | {
91 | stream: estream,
92 | type: 'raw'
93 | }
94 | ]
95 | });
96 | estream.on('error', function (err) {
97 | t.ok(err, 'got error event on the raw stream');
98 | t.end();
99 | });
100 | log.on('error', function (err, stream) {
101 | t.fail('should not have gotten error event on logger');
102 | t.end();
103 | });
104 | log.info('info log message');
105 | });
106 |
107 | test('error event on raw stream (reemitErrorEvents=false)', function (t) {
108 | var estream = new MyErroringStream();
109 | var log = bunyan.createLogger({
110 | name: 'error-event-raw',
111 | streams: [
112 | {
113 | stream: estream,
114 | type: 'raw',
115 | reemitErrorEvents: false
116 | }
117 | ]
118 | });
119 | estream.on('error', function (err) {
120 | t.ok(err, 'got error event on the raw stream');
121 | t.end();
122 | });
123 | log.on('error', function (err, stream) {
124 | t.fail('should not have gotten error event on logger');
125 | t.end();
126 | });
127 | log.info('info log message');
128 | });
129 |
130 | test('error event on raw stream (reemitErrorEvents=true)', function (t) {
131 | var estream = new MyErroringStream();
132 | var log = bunyan.createLogger({
133 | name: 'error-event-raw',
134 | streams: [
135 | {
136 | stream: estream,
137 | type: 'raw',
138 | reemitErrorEvents: true
139 | }
140 | ]
141 | });
142 | log.on('error', function (err, stream) {
143 | t.ok(err, 'got err in error event: ' + err);
144 | t.equal(err.message, 'boom');
145 | t.ok(stream, 'got a stream argument');
146 | t.ok(stream.stream instanceof MyErroringStream);
147 | t.equal(stream.type, 'raw');
148 | t.end();
149 | });
150 | log.info('info log message');
151 | });
152 |
--------------------------------------------------------------------------------
/test/stream-levels.test.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2015 Trent Mick. All rights reserved.
3 | *
4 | * Test that streams (the various way they can be added to
5 | * a Logger instance) get the appropriate level.
6 | */
7 |
8 | var util = require('util'),
9 | format = util.format,
10 | inspect = util.inspect;
11 | var p = console.log;
12 |
13 | var bunyan = require('../lib/bunyan');
14 |
15 | // node-tap API
16 | if (require.cache[__dirname + '/tap4nodeunit.js'])
17 | delete require.cache[__dirname + '/tap4nodeunit.js'];
18 | var tap4nodeunit = require('./tap4nodeunit.js');
19 | var test = tap4nodeunit.test;
20 |
21 |
22 | // ---- Tests
23 |
24 | var log1 = bunyan.createLogger({
25 | name: 'log1',
26 | streams: [
27 | {
28 | path: __dirname + '/level.test.log1.log',
29 | level: 'info'
30 | }
31 | ]
32 | });
33 |
34 |
35 | test('default stream log level', function (t) {
36 | var log = bunyan.createLogger({
37 | name: 'foo'
38 | });
39 | t.equal(log.level(), bunyan.INFO);
40 | t.equal(log.streams[0].level, bunyan.INFO);
41 | t.end();
42 | });
43 |
44 | test('default stream, level=DEBUG specified', function (t) {
45 | var log = bunyan.createLogger({
46 | name: 'foo',
47 | level: bunyan.DEBUG
48 | });
49 | t.equal(log.level(), bunyan.DEBUG);
50 | t.equal(log.streams[0].level, bunyan.DEBUG);
51 | t.end();
52 | });
53 |
54 | test('default stream, level="trace" specified', function (t) {
55 | var log = bunyan.createLogger({
56 | name: 'foo',
57 | level: 'trace'
58 | });
59 | t.equal(log.level(), bunyan.TRACE);
60 | t.equal(log.streams[0].level, bunyan.TRACE);
61 | t.end();
62 | });
63 |
64 | test('stream & level="trace" specified', function (t) {
65 | var log = bunyan.createLogger({
66 | name: 'foo',
67 | stream: process.stderr,
68 | level: 'trace'
69 | });
70 | t.equal(log.level(), bunyan.TRACE);
71 | t.equal(log.streams[0].level, bunyan.TRACE);
72 | t.end();
73 | });
74 |
75 | test('one stream, default level', function (t) {
76 | var log = bunyan.createLogger({
77 | name: 'foo',
78 | streams: [
79 | {
80 | stream: process.stderr
81 | }
82 | ]
83 | });
84 | t.equal(log.level(), bunyan.INFO);
85 | t.equal(log.streams[0].level, bunyan.INFO);
86 | t.end();
87 | });
88 |
89 | test('one stream, top-"level" specified', function (t) {
90 | var log = bunyan.createLogger({
91 | name: 'foo',
92 | level: 'error',
93 | streams: [
94 | {
95 | stream: process.stderr
96 | }
97 | ]
98 | });
99 | t.equal(log.level(), bunyan.ERROR);
100 | t.equal(log.streams[0].level, bunyan.ERROR);
101 | t.end();
102 | });
103 |
104 | test('one stream, stream-"level" specified', function (t) {
105 | var log = bunyan.createLogger({
106 | name: 'foo',
107 | streams: [
108 | {
109 | stream: process.stderr,
110 | level: 'error'
111 | }
112 | ]
113 | });
114 | t.equal(log.level(), bunyan.ERROR);
115 | t.equal(log.streams[0].level, bunyan.ERROR);
116 | t.end();
117 | });
118 |
119 | test('one stream, both-"level" specified', function (t) {
120 | var log = bunyan.createLogger({
121 | name: 'foo',
122 | level: 'debug',
123 | streams: [
124 | {
125 | stream: process.stderr,
126 | level: 'error'
127 | }
128 | ]
129 | });
130 | t.equal(log.level(), bunyan.ERROR);
131 | t.equal(log.streams[0].level, bunyan.ERROR);
132 | t.end();
133 | });
134 |
135 | test('two streams, both-"level" specified', function (t) {
136 | var log = bunyan.createLogger({
137 | name: 'foo',
138 | level: 'debug',
139 | streams: [
140 | {
141 | stream: process.stdout,
142 | level: 'trace'
143 | },
144 | {
145 | stream: process.stderr,
146 | level: 'fatal'
147 | }
148 | ]
149 | });
150 | t.equal(log.level(), bunyan.TRACE, 'log.level()');
151 | t.equal(log.streams[0].level, bunyan.TRACE);
152 | t.equal(log.streams[1].level, bunyan.FATAL);
153 | t.end();
154 | });
155 |
156 | test('two streams, one with "level" specified', function (t) {
157 | var log = bunyan.createLogger({
158 | name: 'foo',
159 | streams: [
160 | {
161 | stream: process.stdout,
162 | },
163 | {
164 | stream: process.stderr,
165 | level: 'fatal'
166 | }
167 | ]
168 | });
169 | t.equal(log.level(), bunyan.INFO);
170 | t.equal(log.streams[0].level, bunyan.INFO);
171 | t.equal(log.streams[1].level, bunyan.FATAL);
172 | t.end();
173 | });
174 |
175 | // Issue #335
176 | test('log level 0 to turn on all logging', function (t) {
177 | var log = bunyan.createLogger({
178 | name: 'foo',
179 | level: 0
180 | });
181 | t.equal(log.level(), 0);
182 | t.equal(log.streams[0].level, 0);
183 | t.end();
184 | });
185 |
--------------------------------------------------------------------------------
/test/ctor.test.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012 Trent Mick. All rights reserved.
3 | *
4 | * Test type checking on creation of the Logger.
5 | */
6 |
7 | var bunyan = require('../lib/bunyan'),
8 | Logger = bunyan;
9 |
10 | // node-tap API
11 | if (require.cache[__dirname + '/tap4nodeunit.js'])
12 | delete require.cache[__dirname + '/tap4nodeunit.js'];
13 | var tap4nodeunit = require('./tap4nodeunit.js');
14 | var after = tap4nodeunit.after;
15 | var before = tap4nodeunit.before;
16 | var test = tap4nodeunit.test;
17 |
18 |
19 |
20 | test('ensure Logger creation options', function (t) {
21 | t.throws(function () { new Logger(); },
22 | /options \(object\) is required/,
23 | 'no options should throw');
24 |
25 | t.throws(function () { new Logger({}); },
26 | /options\.name \(string\) is required/,
27 | 'no options.name should throw');
28 |
29 | t.doesNotThrow(function () { new Logger({name: 'foo'}); },
30 | 'just options.name should be sufficient');
31 |
32 | var options = {name: 'foo', stream: process.stdout, streams: []};
33 | t.throws(function () { new Logger(options); },
34 | /* JSSTYLED */
35 | /cannot mix "streams" and "stream" options/,
36 | 'cannot use "stream" and "streams"');
37 |
38 | // https://github.com/trentm/node-bunyan/issues/3
39 | options = {name: 'foo', streams: {}};
40 | t.throws(function () { new Logger(options); },
41 | /invalid options.streams: must be an array/,
42 | '"streams" must be an array');
43 |
44 | options = {name: 'foo', serializers: 'a string'};
45 | t.throws(function () { new Logger(options); },
46 | /invalid options.serializers: must be an object/,
47 | '"serializers" cannot be a string');
48 |
49 | options = {name: 'foo', serializers: [1, 2, 3]};
50 | t.throws(function () { new Logger(options); },
51 | /invalid options.serializers: must be an object/,
52 | '"serializers" cannot be an array');
53 |
54 | t.end();
55 | });
56 |
57 |
58 | test('ensure Logger constructor is safe without new', function (t) {
59 | t.doesNotThrow(function () { Logger({name: 'foo'}); },
60 | 'constructor should call self with new if necessary');
61 |
62 | t.end();
63 | });
64 |
65 |
66 | test('ensure Logger creation options (createLogger)', function (t) {
67 | t.throws(function () { bunyan.createLogger(); },
68 | /options \(object\) is required/,
69 | 'no options should throw');
70 |
71 | t.throws(function () { bunyan.createLogger({}); },
72 | /options\.name \(string\) is required/,
73 | 'no options.name should throw');
74 |
75 | t.doesNotThrow(function () { bunyan.createLogger({name: 'foo'}); },
76 | 'just options.name should be sufficient');
77 |
78 | var options = {name: 'foo', stream: process.stdout, streams: []};
79 | t.throws(function () { bunyan.createLogger(options); },
80 | /* JSSTYLED */
81 | /cannot mix "streams" and "stream" options/,
82 | 'cannot use "stream" and "streams"');
83 |
84 | // https://github.com/trentm/node-bunyan/issues/3
85 | options = {name: 'foo', streams: {}};
86 | t.throws(function () { bunyan.createLogger(options); },
87 | /invalid options.streams: must be an array/,
88 | '"streams" must be an array');
89 |
90 | options = {name: 'foo', serializers: 'a string'};
91 | t.throws(function () { bunyan.createLogger(options); },
92 | /invalid options.serializers: must be an object/,
93 | '"serializers" cannot be a string');
94 |
95 | options = {name: 'foo', serializers: [1, 2, 3]};
96 | t.throws(function () { bunyan.createLogger(options); },
97 | /invalid options.serializers: must be an object/,
98 | '"serializers" cannot be an array');
99 |
100 | t.end();
101 | });
102 |
103 |
104 | test('ensure Logger child() options', function (t) {
105 | var log = new Logger({name: 'foo'});
106 |
107 | t.doesNotThrow(function () { log.child(); },
108 | 'no options should be fine');
109 |
110 | t.doesNotThrow(function () { log.child({}); },
111 | 'empty options should be fine too');
112 |
113 | t.throws(function () { log.child({name: 'foo'}); },
114 | /invalid options.name: child cannot set logger name/,
115 | 'child cannot change name');
116 |
117 | var options = {stream: process.stdout, streams: []};
118 | t.throws(function () { log.child(options); },
119 | /* JSSTYLED */
120 | /cannot mix "streams" and "stream" options/,
121 | 'cannot use "stream" and "streams"');
122 |
123 | // https://github.com/trentm/node-bunyan/issues/3
124 | options = {streams: {}};
125 | t.throws(function () { log.child(options); },
126 | /invalid options.streams: must be an array/,
127 | '"streams" must be an array');
128 |
129 | options = {serializers: 'a string'};
130 | t.throws(function () { log.child(options); },
131 | /invalid options.serializers: must be an object/,
132 | '"serializers" cannot be a string');
133 |
134 | options = {serializers: [1, 2, 3]};
135 | t.throws(function () { log.child(options); },
136 | /invalid options.serializers: must be an object/,
137 | '"serializers" cannot be an array');
138 |
139 | t.end();
140 | });
141 |
142 |
143 | test('ensure Logger() rejects non-Logger parents', function (t) {
144 | var dad = new Logger({name: 'dad', streams: []});
145 |
146 | t.throws(function () { new Logger({}, {}); },
147 | /invalid Logger creation: do not pass a second arg/,
148 | 'Logger arguments must be valid');
149 |
150 | t.doesNotThrow(function () { new Logger(dad, {}); },
151 | 'Logger allows Logger instance as parent');
152 |
153 | t.end();
154 | });
155 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 |
2 | #---- Tools
3 |
4 | NODEUNIT := ./node_modules/.bin/nodeunit
5 | SUDO := sudo
6 | ifeq ($(shell uname -s),SunOS)
7 | # On SunOS (e.g. SmartOS) we expect to run the test suite as the
8 | # root user -- necessary to run dtrace. Therefore `pfexec` isn't
9 | # necessary.
10 | SUDO :=
11 | endif
12 | DTRACE_UP_IN_HERE=
13 | ifeq ($(shell uname -s),SunOS)
14 | DTRACE_UP_IN_HERE=1
15 | endif
16 | ifeq ($(shell uname -s),Darwin)
17 | DTRACE_UP_IN_HERE=1
18 | endif
19 | NODEOPT ?= $(HOME)/opt
20 |
21 |
22 | #---- Files
23 |
24 | JSSTYLE_FILES := $(shell find lib test tools examples -name "*.js") bin/bunyan
25 | # All test files *except* dtrace.test.js.
26 | NON_DTRACE_TEST_FILES := $(shell ls -1 test/*.test.js | grep -v dtrace | xargs)
27 |
28 |
29 | #---- Targets
30 |
31 | all $(NODEUNIT):
32 | npm install $(NPM_INSTALL_FLAGS)
33 |
34 | # Ensure all version-carrying files have the same version.
35 | .PHONY: versioncheck
36 | versioncheck:
37 | @echo version is: $(shell cat package.json | json version)
38 | [[ `cat package.json | json version` == `grep '^## ' CHANGES.md | head -2 | tail -1 | awk '{print $$2}'` ]]
39 | @echo Version check ok.
40 |
41 | .PHONY: cutarelease
42 | cutarelease: check
43 | [[ -z `git status --short` ]] # If this fails, the working dir is dirty.
44 | @which json 2>/dev/null 1>/dev/null && \
45 | ver=$(shell json -f package.json version) && \
46 | name=$(shell json -f package.json name) && \
47 | publishedVer=$(shell npm view -j $(shell json -f package.json name)@$(shell json -f package.json version) version 2>/dev/null) && \
48 | if [[ -n "$$publishedVer" ]]; then \
49 | echo "error: $$name@$$ver is already published to npm"; \
50 | exit 1; \
51 | fi && \
52 | echo "** Are you sure you want to tag and publish $$name@$$ver to npm?" && \
53 | echo "** Enter to continue, Ctrl+C to abort." && \
54 | read
55 | ver=$(shell cat package.json | json version) && \
56 | date=$(shell date -u "+%Y-%m-%d") && \
57 | git tag -a "$$ver" -m "version $$ver ($$date) beta" && \
58 | git push --tags origin && \
59 | npm publish --tag beta
60 |
61 | .PHONY: docs
62 | docs: toc
63 | @[[ `which ronn` ]] || (echo "No 'ronn' on your PATH. Install with 'gem install ronn'" && exit 2)
64 | mkdir -p man/man1
65 | ronn --style=toc --manual="bunyan manual" --date=$(shell git log -1 --pretty=format:%cd --date=short) --roff --html docs/bunyan.1.ronn
66 | python -c 'import sys; h = open("docs/bunyan.1.html").read(); h = h.replace(".mp dt.flush {float:left;width:8ex}", ""); open("docs/bunyan.1.html", "w").write(h)'
67 | python -c 'import sys; h = open("docs/bunyan.1.html").read(); h = h.replace("