프로젝트

일반

사용자정보

통계
| 개정판:

root / HServer / 00.Server / 00.Program / node_modules / bunyan / README.md

이력 | 보기 | 이력해설 | 다운로드 (44.7 KB)

1 39 HKM
[![npm version](https://img.shields.io/npm/v/bunyan.svg?style=flat)](https://www.npmjs.com/package/bunyan)
2
[![Build Status](https://travis-ci.org/trentm/node-bunyan.svg?branch=master)](https://travis-ci.org/trentm/node-bunyan)
3
4
Bunyan is **a simple and fast JSON logging library** for node.js services:
5
6
```js
7
var bunyan = require('bunyan');
8
var log = bunyan.createLogger({name: "myapp"});
9
log.info("hi");
10
```
11
12
and **a `bunyan` CLI tool** for nicely viewing those logs:
13
14
![bunyan CLI screenshot](https://raw.github.com/trentm/node-bunyan/master/tools/screenshot1.png)
15
16
Manifesto: Server logs should be structured. JSON's a good format. Let's do
17
that. A log record is one line of `JSON.stringify`'d output. Let's also
18
specify some common names for the requisite and common fields for a log
19
record (see below).
20
21
22
## Table of Contents
23
24
<!-- toc -->
25
26
- [Current Status](#current-status)
27
- [Installation](#installation)
28
- [Features](#features)
29
- [Introduction](#introduction)
30
  * [Constructor API](#constructor-api)
31
  * [Log Method API](#log-method-api)
32
  * [CLI Usage](#cli-usage)
33
  * [Streams Introduction](#streams-introduction)
34
  * [log.child](#logchild)
35
  * [Serializers](#serializers)
36
    + [Requirements for serializers functions](#requirements-for-serializers-functions)
37
    + [Standard Serializers](#standard-serializers)
38
  * [src](#src)
39
- [Levels](#levels)
40
  * [Level suggestions](#level-suggestions)
41
- [Log Record Fields](#log-record-fields)
42
  * [Core fields](#core-fields)
43
  * [Recommended/Best Practice Fields](#recommendedbest-practice-fields)
44
  * [Other fields to consider](#other-fields-to-consider)
45
- [Streams](#streams)
46
  * [Adding a Stream](#adding-a-stream)
47
  * [stream errors](#stream-errors)
48
  * [stream type: `stream`](#stream-type-stream)
49
  * [stream type: `file`](#stream-type-file)
50
  * [stream type: `rotating-file`](#stream-type-rotating-file)
51
  * [stream type: `raw`](#stream-type-raw)
52
  * [`raw` + RingBuffer Stream](#raw--ringbuffer-stream)
53
  * [third-party streams](#third-party-streams)
54
- [Runtime log snooping via DTrace](#runtime-log-snooping-via-dtrace)
55
  * [DTrace examples](#dtrace-examples)
56
- [Runtime environments](#runtime-environments)
57
  * [Browserify](#browserify)
58
  * [Webpack](#webpack)
59
- [Versioning](#versioning)
60
- [License](#license)
61
- [See Also](#see-also)
62
63
<!-- tocstop -->
64
65
# Current Status
66
67
Solid core functionality is there. Joyent is using this for a number of
68
production services. Bunyan supports node 0.10 and greater. Follow
69
<a href="https://twitter.com/intent/user?screen_name=trentmick" target="_blank">@trentmick</a>
70
for updates to Bunyan.
71
72
There is an email discussion list
73
[bunyan-logging@googlegroups.com](mailto:bunyan-logging@googlegroups.com),
74
also [as a forum in the
75
browser](https://groups.google.com/forum/?fromgroups#!forum/bunyan-logging).
76
77
78
# Installation
79
80
```sh
81
npm install bunyan
82
```
83
84
**Tip**: The `bunyan` CLI tool is written to be compatible (within reason) with
85
all versions of Bunyan logs. Therefore you might want to `npm install -g bunyan`
86
to get the bunyan CLI on your PATH, then use local bunyan installs for
87
node.js library usage of bunyan in your apps.
88
89
90
# Features
91
92
- elegant [log method API](#log-method-api)
93
- extensible [streams](#streams) system for controlling where log records
94
  go (to a stream, to a file, [log file rotation](#stream-type-rotating-file),
95
  etc.)
96
- [`bunyan` CLI](#cli-usage) for pretty-printing and filtering of Bunyan logs
97
- simple include of log call source location (file, line, function) with
98
  [`src: true`](#src)
99
- lightweight specialization of Logger instances with [`log.child`](#logchild)
100
- custom rendering of logged objects with ["serializers"](#serializers)
101
- [Runtime log snooping via DTrace support](#runtime-log-snooping-via-dtrace)
102
- Support for a few [runtime environments](#runtime-environments): Node.js,
103
  [Browserify](http://browserify.org/), [Webpack](https://webpack.github.io/), [NW.js](http://nwjs.io/).
104
105
106
# Introduction
107
108
Like most logging libraries you create a Logger instance and call methods
109
named after the logging levels:
110
111
```js
112
// hi.js
113
var bunyan = require('bunyan');
114
var log = bunyan.createLogger({name: 'myapp'});
115
log.info('hi');
116
log.warn({lang: 'fr'}, 'au revoir');
117
```
118
119
All loggers must provide a "name". This is somewhat akin to the log4j logger
120
"name", but Bunyan doesn't do hierarchical logger names.
121
122
**Bunyan log records are JSON.** A few fields are added automatically:
123
"pid", "hostname", "time" and "v".
124
125
```sh
126
$ node hi.js
127
{"name":"myapp","hostname":"banana.local","pid":40161,"level":30,"msg":"hi","time":"2013-01-04T18:46:23.851Z","v":0}
128
{"name":"myapp","hostname":"banana.local","pid":40161,"level":40,"lang":"fr","msg":"au revoir","time":"2013-01-04T18:46:23.853Z","v":0}
129
```
130
131
132
## Constructor API
133
134
```js
135
var bunyan = require('bunyan');
136
var log = bunyan.createLogger({
137
    name: <string>,                     // Required
138
    level: <level name or number>,      // Optional, see "Levels" section
139
    stream: <node.js stream>,           // Optional, see "Streams" section
140
    streams: [<bunyan streams>, ...],   // Optional, see "Streams" section
141
    serializers: <serializers mapping>, // Optional, see "Serializers" section
142
    src: <boolean>,                     // Optional, see "src" section
143
144
    // Any other fields are added to all log records as is.
145
    foo: 'bar',
146
    ...
147
});
148
```
149
150
151
## Log Method API
152
153
The example above shows two different ways to call `log.info(...)`. The
154
full API is:
155
156
```js
157
log.info();     // Returns a boolean: is the "info" level enabled?
158
                // This is equivalent to `log.isInfoEnabled()` or
159
                // `log.isEnabledFor(INFO)` in log4j.
160
161
log.info('hi');                     // Log a simple string message (or number).
162
log.info('hi %s', bob, anotherVar); // Uses `util.format` for msg formatting.
163
164
log.info({foo: 'bar'}, 'hi');
165
                // The first field can optionally be a "fields" object, which
166
                // is merged into the log record.
167
168
log.info(err);  // Special case to log an `Error` instance to the record.
169
                // This adds an "err" field with exception details
170
                // (including the stack) and sets "msg" to the exception
171
                // message.
172
log.info(err, 'more on this: %s', more);
173
                // ... or you can specify the "msg".
174
175
log.info({foo: 'bar', err: err}, 'some msg about this error');
176
                // To pass in an Error *and* other fields, use the `err`
177
                // field name for the Error instance.
178
```
179
180
Note that this implies **you cannot blindly pass any object as the first
181
argument to log it** because that object might include fields that collide with
182
Bunyan's [core record fields](#core-fields). In other words,
183
`log.info(mywidget)` may not yield what you expect. Instead of a string
184
representation of `mywidget` that other logging libraries may give you, Bunyan
185
will try to JSON-ify your object. It is a Bunyan best practice to always give a
186
field name to included objects, e.g.:
187
188
```js
189
log.info({widget: mywidget}, ...)
190
```
191
192
This will dove-tail with [Bunyan serializer support](#serializers), discussed
193
later.
194
195
The same goes for all of Bunyan's log levels: `log.trace`, `log.debug`,
196
`log.info`, `log.warn`, `log.error`, and `log.fatal`. See the [levels
197
section](#levels) below for details and suggestions.
198
199
200
## CLI Usage
201
202
Bunyan log output is a stream of JSON objects. This is great for processing,
203
but not for reading directly. A **`bunyan` tool** is provided **for
204
pretty-printing bunyan logs** and for **filtering** (e.g.
205
`| bunyan -c 'this.foo == "bar"'`). Using our example above:
206
207
```sh
208
$ node hi.js | ./node_modules/.bin/bunyan
209
[2013-01-04T19:01:18.241Z]  INFO: myapp/40208 on banana.local: hi
210
[2013-01-04T19:01:18.242Z]  WARN: myapp/40208 on banana.local: au revoir (lang=fr)
211
```
212
213
See the screenshot above for an example of the default coloring of rendered
214
log output. That example also shows the nice formatting automatically done for
215
some well-known log record fields (e.g. `req` is formatted like an HTTP request,
216
`res` like an HTTP response, `err` like an error stack trace).
217
218
One interesting feature is **filtering** of log content, which can be useful
219
for digging through large log files or for analysis. We can filter only
220
records above a certain level:
221
222
```sh
223
$ node hi.js | bunyan -l warn
224
[2013-01-04T19:08:37.182Z]  WARN: myapp/40353 on banana.local: au revoir (lang=fr)
225
```
226
227
Or filter on the JSON fields in the records (e.g. only showing the French
228
records in our contrived example):
229
230
```sh
231
$ node hi.js | bunyan -c 'this.lang == "fr"'
232
[2013-01-04T19:08:26.411Z]  WARN: myapp/40342 on banana.local: au revoir (lang=fr)
233
```
234
235
See `bunyan --help` for other facilities.
236
237
238
## Streams Introduction
239
240
By default, log output is to stdout and at the "info" level. Explicitly that
241
looks like:
242
243
```js
244
var log = bunyan.createLogger({
245
    name: 'myapp',
246
    stream: process.stdout,
247
    level: 'info'
248
});
249
```
250
251
That is an abbreviated form for a single stream. **You can define multiple
252
streams at different levels**.
253
254
```js
255
var log = bunyan.createLogger({
256
  name: 'myapp',
257
  streams: [
258
    {
259
      level: 'info',
260
      stream: process.stdout            // log INFO and above to stdout
261
    },
262
    {
263
      level: 'error',
264
      path: '/var/tmp/myapp-error.log'  // log ERROR and above to a file
265
    }
266
  ]
267
});
268
```
269
270
More on streams in the [Streams section](#streams) below.
271
272
## log.child
273
274
Bunyan has a concept of a child logger to **specialize a logger for a
275
sub-component of your application**, i.e. to create a new logger with
276
additional bound fields that will be included in its log records. A child
277
logger is created with `log.child(...)`.
278
279
In the following example, logging on a "Wuzzle" instance's `this.log` will
280
be exactly as on the parent logger with the addition of the `widget_type`
281
field:
282
283
```js
284
var bunyan = require('bunyan');
285
var log = bunyan.createLogger({name: 'myapp'});
286
287
function Wuzzle(options) {
288
    this.log = options.log.child({widget_type: 'wuzzle'});
289
    this.log.info('creating a wuzzle')
290
}
291
Wuzzle.prototype.woos = function () {
292
    this.log.warn('This wuzzle is woosey.')
293
}
294
295
log.info('start');
296
var wuzzle = new Wuzzle({log: log});
297
wuzzle.woos();
298
log.info('done');
299
```
300
301
Running that looks like (raw):
302
303
```sh
304
$ node myapp.js
305
{"name":"myapp","hostname":"myhost","pid":34572,"level":30,"msg":"start","time":"2013-01-04T07:47:25.814Z","v":0}
306
{"name":"myapp","hostname":"myhost","pid":34572,"widget_type":"wuzzle","level":30,"msg":"creating a wuzzle","time":"2013-01-04T07:47:25.815Z","v":0}
307
{"name":"myapp","hostname":"myhost","pid":34572,"widget_type":"wuzzle","level":40,"msg":"This wuzzle is woosey.","time":"2013-01-04T07:47:25.815Z","v":0}
308
{"name":"myapp","hostname":"myhost","pid":34572,"level":30,"msg":"done","time":"2013-01-04T07:47:25.816Z","v":0}
309
```
310
311
And with the `bunyan` CLI (using the "short" output mode):
312
313
```sh
314
$ node myapp.js  | bunyan -o short
315
07:46:42.707Z  INFO myapp: start
316
07:46:42.709Z  INFO myapp: creating a wuzzle (widget_type=wuzzle)
317
07:46:42.709Z  WARN myapp: This wuzzle is woosey. (widget_type=wuzzle)
318
07:46:42.709Z  INFO myapp: done
319
```
320
321
A more practical example is in the
322
[node-restify](https://github.com/mcavage/node-restify) web framework.
323
Restify uses Bunyan for its logging. One feature of its integration, is that
324
if `server.use(restify.requestLogger())` is used, each restify request handler
325
includes a `req.log` logger that is:
326
327
```js
328
log.child({req_id: <unique request id>}, true)
329
```
330
331
Apps using restify can then use `req.log` and have all such log records
332
include the unique request id (as "req\_id"). Handy.
333
334
335
## Serializers
336
337
Bunyan has a concept of **"serializer" functions to produce a JSON-able object
338
from a JavaScript object**, so you can easily do the following:
339
340
```js
341
log.info({req: <request object>}, 'something about handling this request');
342
```
343
344
and have the `req` entry in the log record be just a reasonable subset of
345
`<request object>` fields (or computed data about those fields).
346
347
348
A logger instance can have a `serializers` mapping of log record field name
349
("req" in this example) to a serializer function. When creating the log
350
record, Bunyan will call the serializer function for fields of that name.
351
An example:
352
353
```js
354
function reqSerializer(req) {
355
    return {
356
        method: req.method,
357
        url: req.url,
358
        headers: req.headers
359
    };
360
}
361
var log = bunyan.createLogger({
362
    name: 'myapp',
363
    serializers: {
364
        req: reqSerializer
365
    }
366
});
367
```
368
369
370
Typically serializers are added to a logger at creation time via
371
`bunyan.createLogger({..., serializers: <serializers>})`. However, serializers
372
can be added after creation via `<logger>.addSerializers(...)`, e.g.:
373
374
```js
375
var log = bunyan.createLogger({name: 'myapp'});
376
log.addSerializers({req: reqSerializer});
377
```
378
379
### Requirements for serializers functions
380
381
A serializer function is passed unprotected objects that are passed to the
382
`log.info`, `log.debug`, etc. call. This means a poorly written serializer
383
function can case side-effects. Logging shouldn't do that. Here are a few
384
rules and best practices for serializer functions:
385
386
- A serializer function *should never throw*. The bunyan library *does*
387
  protect somewhat from this: if the serializer throws an error, then
388
  bunyan will (a) write an ugly message on stderr (along with the traceback),
389
  and (b) the field in the log record will be replace with a short error message.
390
  For example:
391
392
    ```
393
    bunyan: ERROR: Exception thrown from the "foo" Bunyan serializer. This should never happen. This is a bug in that serializer function.
394
    TypeError: Cannot read property 'not' of undefined
395
        at Object.fooSerializer [as foo] (/Users/trentm/tm/node-bunyan/bar.js:8:26)
396
        at /Users/trentm/tm/node-bunyan/lib/bunyan.js:873:50
397
        at Array.forEach (native)
398
        at Logger._applySerializers (/Users/trentm/tm/node-bunyan/lib/bunyan.js:865:35)
399
        at mkRecord (/Users/trentm/tm/node-bunyan/lib/bunyan.js:978:17)
400
        at Logger.info (/Users/trentm/tm/node-bunyan/lib/bunyan.js:1044:19)
401
        at Object.<anonymous> (/Users/trentm/tm/node-bunyan/bar.js:13:5)
402
        at Module._compile (module.js:409:26)
403
        at Object.Module._extensions..js (module.js:416:10)
404
        at Module.load (module.js:343:32)
405
    {"name":"bar","hostname":"danger0.local","pid":47411,"level":30,"foo":"(Error in Bunyan log \"foo\" serializer broke field. See stderr for details.)","msg":"one","time":"2017-03-08T02:53:51.173Z","v":0}
406
    ```
407
408
- A serializer function *should never mutate the given object*. Doing so will
409
  change the object in your application.
410
411
- A serializer function *should be defensive*. In my experience it is common to
412
  set a serializer in an app, say for field name "foo", and then accidentally
413
  have a log line that passes a "foo" that is undefined, or null, or of some
414
  unexpected type. A good start at defensiveness is to start with this:
415
416
    ```javascript
417
    function fooSerializers(foo) {
418
        // Guard against foo be null/undefined. Check that expected fields
419
        // are defined.
420
        if (!foo || !foo.bar)
421
            return foo;
422
        var obj = {
423
            // Create the object to be logged.
424
            bar: foo.bar
425
        }
426
        return obj;
427
    };
428
    ```
429
430
431
### Standard Serializers
432
433
Bunyan includes a small set of "standard serializers", exported as
434
`bunyan.stdSerializers`. Their use is completely optional. Example using
435
all of them:
436
437
```js
438
var log = bunyan.createLogger({
439
    name: 'myapp',
440
    serializers: bunyan.stdSerializers
441
});
442
```
443
444
or particular ones:
445
446
```js
447
var log = bunyan.createLogger({
448
    name: 'myapp',
449
    serializers: {err: bunyan.stdSerializers.err}
450
});
451
```
452
453
Standard serializers are:
454
455
| Field | Description |
456
| ----- | ----------- |
457
| err   | Used for serializing JavaScript error objects, including traversing an error's cause chain for error objects with a `.cause()` -- e.g. as from [verror](https://github.com/davepacheco/node-verror). |
458
| req   | Common fields from a node.js HTTP request object. |
459
| res   | Common fields from a node.js HTTP response object. |
460
461
Note that the `req` and `res` serializers intentionally do not include the
462
request/response *body*, as that can be prohibitively large. If helpful, the
463
[restify framework's audit logger plugin](https://github.com/restify/node-restify/blob/ac13902ad9716dcb20aaa62295403983075b1841/lib/plugins/audit.js#L38-L87)
464
has its own req/res serializers that include more information (optionally
465
including the body).
466
467
468
## src
469
470
The **source file, line and function of the log call site** can be added to
471
log records by using the `src: true` config option:
472
473
```js
474
var log = bunyan.createLogger({src: true, ...});
475
```
476
477
This adds the call source info with the 'src' field, like this:
478
479
```js
480
{
481
  "name": "src-example",
482
  "hostname": "banana.local",
483
  "pid": 123,
484
  "component": "wuzzle",
485
  "level": 4,
486
  "msg": "This wuzzle is woosey.",
487
  "time": "2012-02-06T04:19:35.605Z",
488
  "src": {
489
    "file": "/Users/trentm/tm/node-bunyan/examples/src.js",
490
    "line": 20,
491
    "func": "Wuzzle.woos"
492
  },
493
  "v": 0
494
}
495
```
496
497
**WARNING: Determining the call source info is slow. Never use this option
498
in production.**
499
500
501
# Levels
502
503
The log levels in bunyan are as follows. The level descriptions are best
504
practice *opinions* of the author.
505
506
- "fatal" (60): The service/app is going to stop or become unusable now.
507
  An operator should definitely look into this soon.
508
- "error" (50): Fatal for a particular request, but the service/app continues
509
  servicing other requests. An operator should look at this soon(ish).
510
- "warn" (40): A note on something that should probably be looked at by an
511
  operator eventually.
512
- "info" (30): Detail on regular operation.
513
- "debug" (20): Anything else, i.e. too verbose to be included in "info" level.
514
- "trace" (10): Logging from external libraries used by your app or *very*
515
  detailed application logging.
516
517
Setting a logger instance (or one of its streams) to a particular level implies
518
that all log records *at that level and above* are logged. E.g. a logger set to
519
level "info" will log records at level info and above (warn, error, fatal).
520
521
While using log level *names* is preferred, the actual level values are integers
522
internally (10 for "trace", ..., 60 for "fatal"). Constants are defined for
523
the levels: `bunyan.TRACE` ... `bunyan.FATAL`. The lowercase level names are
524
aliases supported in the API, e.g. `log.level("info")`. There is one exception:
525
DTrace integration uses the level names. The fired DTrace probes are named
526
'bunyan-$levelName'.
527
528
Here is the API for querying and changing levels on an existing logger.
529
Recall that a logger instance has an array of output "streams":
530
531
```js
532
log.level() -> INFO   // gets current level (lowest level of all streams)
533
534
log.level(INFO)       // set all streams to level INFO
535
log.level("info")     // set all streams to level INFO
536
537
log.levels() -> [DEBUG, INFO]   // get array of levels of all streams
538
log.levels(0) -> DEBUG          // get level of stream at index 0
539
log.levels("foo")               // get level of stream with name "foo"
540
541
log.levels(0, INFO)             // set level of stream 0 to INFO
542
log.levels(0, "info")           // can use "info" et al aliases
543
log.levels("foo", WARN)         // set stream named "foo" to WARN
544
```
545
546
547
## Level suggestions
548
549
Trent's biased suggestions for server apps: Use "debug" sparingly. Information
550
that will be useful to debug errors *post mortem* should usually be included in
551
"info" messages if it's generally relevant or else with the corresponding
552
"error" event. Don't rely on spewing mostly irrelevant debug messages all the
553
time and sifting through them when an error occurs.
554
555
Trent's biased suggestions for node.js libraries: IMHO, libraries should only
556
ever log at `trace`-level. Fine control over log output should be up to the
557
app using a library. Having a library that spews log output at higher levels
558
gets in the way of a clear story in the *app* logs.
559
560
561
# Log Record Fields
562
563
This section will describe *rules* for the Bunyan log format: field names,
564
field meanings, required fields, etc. However, a Bunyan library doesn't
565
strictly enforce all these rules while records are being emitted. For example,
566
Bunyan will add a `time` field with the correct format to your log records,
567
but you can specify your own. It is the caller's responsibility to specify
568
the appropriate format.
569
570
The reason for the above leniency is because IMO logging a message should
571
never break your app. This leads to this rule of logging: **a thrown
572
exception from `log.info(...)` or equivalent (other than for calling with the
573
incorrect signature) is always a bug in Bunyan.**
574
575
576
A typical Bunyan log record looks like this:
577
578
```js
579
{"name":"myserver","hostname":"banana.local","pid":123,"req":{"method":"GET","url":"/path?q=1#anchor","headers":{"x-hi":"Mom","connection":"close"}},"level":3,"msg":"start request","time":"2012-02-03T19:02:46.178Z","v":0}
580
```
581
582
Pretty-printed:
583
584
```js
585
{
586
  "name": "myserver",
587
  "hostname": "banana.local",
588
  "pid": 123,
589
  "req": {
590
    "method": "GET",
591
    "url": "/path?q=1#anchor",
592
    "headers": {
593
      "x-hi": "Mom",
594
      "connection": "close"
595
    },
596
    "remoteAddress": "120.0.0.1",
597
    "remotePort": 51244
598
  },
599
  "level": 3,
600
  "msg": "start request",
601
  "time": "2012-02-03T19:02:57.534Z",
602
  "v": 0
603
}
604
```
605
606
## Core fields
607
608
- `v`: Required. Integer. Added by Bunyan. Cannot be overridden.
609
  This is the Bunyan log format version (`require('bunyan').LOG_VERSION`).
610
  The log version is a single integer. `0` is until I release a version
611
  "1.0.0" of node-bunyan. Thereafter, starting with `1`, this will be
612
  incremented if there is any backward incompatible change to the log record
613
  format. Details will be in "CHANGES.md" (the change log).
614
- `level`: Required. Integer. Added by Bunyan. Cannot be overridden.
615
  See the "Levels" section.
616
- `name`: Required. String. Provided at Logger creation.
617
  You must specify a name for your logger when creating it. Typically this
618
  is the name of the service/app using Bunyan for logging.
619
- `hostname`: Required. String. Provided or determined at Logger creation.
620
  You can specify your hostname at Logger creation or it will be retrieved
621
  vi `os.hostname()`.
622
- `pid`: Required. Integer. Filled in automatically at Logger creation.
623
- `time`: Required. String. Added by Bunyan. Can be overridden.
624
  The date and time of the event in [ISO 8601
625
  Extended Format](http://en.wikipedia.org/wiki/ISO_8601) format and in UTC,
626
  as from
627
  [`Date.toISOString()`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Date/toISOString).
628
- `msg`: Required. String.
629
  Every `log.debug(...)` et al call must provide a log message.
630
- `src`: Optional. Object giving log call source info. This is added
631
  automatically by Bunyan if the "src: true" config option is given to the
632
  Logger. Never use in production as this is really slow.
633
634
635
Go ahead and add more fields, and nested ones are fine (and recommended) as
636
well. This is why we're using JSON. Some suggestions and best practices
637
follow (feedback from actual users welcome).
638
639
640
## Recommended/Best Practice Fields
641
642
- `err`: Object. A caught JS exception. Log that thing with `log.info(err)`
643
    to get:
644
645
    ```js
646
    ...
647
    "err": {
648
      "message": "boom",
649
      "name": "TypeError",
650
      "stack": "TypeError: boom\n    at Object.<anonymous> ..."
651
    },
652
    "msg": "boom",
653
    ...
654
    ```
655
656
    Or use the `bunyan.stdSerializers.err` serializer in your Logger and
657
    do this `log.error({err: err}, "oops")`. See "examples/err.js".
658
659
- `req_id`: String. A request identifier. Including this field in all logging
660
  tied to handling a particular request to your server is strongly suggested.
661
  This allows post analysis of logs to easily collate all related logging
662
  for a request. This really shines when you have a SOA with multiple services
663
  and you carry a single request ID from the top API down through all APIs
664
  (as [node-restify](https://github.com/mcavage/node-restify) facilitates
665
  with its 'Request-Id' header).
666
667
- `req`: An HTTP server request. Bunyan provides `bunyan.stdSerializers.req`
668
  to serialize a request with a suggested set of keys. Example:
669
670
    ```js
671
    {
672
      "method": "GET",
673
      "url": "/path?q=1#anchor",
674
      "headers": {
675
        "x-hi": "Mom",
676
        "connection": "close"
677
      },
678
      "remoteAddress": "120.0.0.1",
679
      "remotePort": 51244
680
    }
681
    ```
682
683
- `res`: An HTTP server response. Bunyan provides `bunyan.stdSerializers.res`
684
  to serialize a response with a suggested set of keys. Example:
685
686
    ```js
687
    {
688
      "statusCode": 200,
689
      "header": "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nConnection: keep-alive\r\nTransfer-Encoding: chunked\r\n\r\n"
690
    }
691
    ```
692
693
## Other fields to consider
694
695
- `req.username`: Authenticated user (or for a 401, the user attempting to
696
  auth).
697
- Some mechanism to calculate response latency. "restify" users will have
698
  a "X-Response-Time" header. A `latency` custom field would be fine.
699
- `req.body`: If you know that request bodies are small (common in APIs,
700
  for example), then logging the request body is good.
701
702
703
# Streams
704
705
A "stream" is Bunyan's name for where it outputs log messages (the equivalent
706
to a log4j Appender). Ultimately Bunyan uses a
707
[Writable Stream](https://nodejs.org/docs/latest/api/all.html#writable_Stream)
708
interface, but there are some additional attributes used to create and
709
manage the stream. A Bunyan Logger instance has one or more streams.
710
In general streams are specified with the "streams" option:
711
712
```js
713
var bunyan = require('bunyan');
714
var log = bunyan.createLogger({
715
    name: "foo",
716
    streams: [
717
        {
718
            stream: process.stderr,
719
            level: "debug"
720
        },
721
        ...
722
    ]
723
});
724
```
725
726
For convenience, if there is only one stream, it can specified with the
727
"stream" and "level" options (internally converted to a `Logger.streams`).
728
729
```js
730
var log = bunyan.createLogger({
731
    name: "foo",
732
    stream: process.stderr,
733
    level: "debug"
734
});
735
```
736
737
Note that "file" streams do not support this shortcut (partly for historical
738
reasons and partly to not make it difficult to add a literal "path" field
739
on log records).
740
741
If neither "streams" nor "stream" are specified, the default is a stream of
742
type "stream" emitting to `process.stdout` at the "info" level.
743
744
## Adding a Stream
745
746
After a bunyan instance has been initialized, you may add additional streams by
747
calling the `addStream` function.
748
749
```js
750
var bunyan = require('bunyan');
751
var log = bunyan.createLogger('myLogger');
752
log.addStream({
753
  name: "myNewStream",
754
  stream: process.stderr,
755
  level: "debug"
756
});
757
```
758
759
## stream errors
760
761
A Bunyan logger instance can be made to re-emit "error" events from its
762
streams. Bunyan does so by default for [`type === "file"`
763
streams](#stream-type-file), so you can do this:
764
765
```js
766
var log = bunyan.createLogger({name: 'mylog', streams: [{path: LOG_PATH}]});
767
log.on('error', function (err, stream) {
768
    // Handle stream write or create error here.
769
});
770
```
771
772
As of bunyan@1.7.0, the `reemitErrorEvents` field can be used when adding a
773
stream to control whether "error" events are re-emitted on the Logger. For
774
example:
775
776
    var EventEmitter = require('events').EventEmitter;
777
    var util = require('util');
778
779
    function MyFlakyStream() {}
780
    util.inherits(MyFlakyStream, EventEmitter);
781
782
    MyFlakyStream.prototype.write = function (rec) {
783
        this.emit('error', new Error('boom'));
784
    }
785
786
    var log = bunyan.createLogger({
787
        name: 'this-is-flaky',
788
        streams: [
789
            {
790
                type: 'raw',
791
                stream: new MyFlakyStream(),
792
                reemitErrorEvents: true
793
            }
794
        ]
795
    });
796
    log.info('hi there');
797
798
The behaviour is as follows:
799
800
- `reemitErrorEvents` not specified: `file` streams will re-emit error events
801
  on the Logger instance.
802
- `reemitErrorEvents: true`: error events will be re-emitted on the Logger
803
  for any stream with a `.on()` function -- which includes file streams,
804
  process.stdout/stderr, and any object that inherits from EventEmitter.
805
- `reemitErrorEvents: false`: error events will not be re-emitted for any
806
  streams.
807
808
Note: "error" events are **not** related to log records at the "error" level
809
as produced by `log.error(...)`. See [the node.js docs on error
810
events](https://nodejs.org/api/events.html#events_error_events) for details.
811
812
813
## stream type: `stream`
814
815
A `type === 'stream'` is a plain ol' node.js [Writable
816
Stream](http://nodejs.org/docs/latest/api/all.html#writable_Stream). A
817
"stream" (the writable stream) field is required. E.g.: `process.stdout`,
818
`process.stderr`.
819
820
```js
821
var log = bunyan.createLogger({
822
    name: 'foo',
823
    streams: [{
824
        stream: process.stderr
825
        // `type: 'stream'` is implied
826
    }]
827
});
828
```
829
830
<table>
831
<tr>
832
<th>Field</th>
833
<th>Required?</th>
834
<th>Default</th>
835
<th>Description</th>
836
</tr>
837
<tr>
838
<td>stream</td>
839
<td>Yes</td>
840
<td>-</td>
841
<td>A "Writable Stream", e.g. a std handle or an open file write stream.</td>
842
</tr>
843
<tr>
844
<td>type</td>
845
<td>No</td>
846
<td>n/a</td>
847
<td>`type == 'stream'` is implied if the `stream` field is given.</td>
848
</tr>
849
<tr>
850
<td>level</td>
851
<td>No</td>
852
<td>info</td>
853
<td>The level to which logging to this stream is enabled. If not
854
specified it defaults to "info". If specified this can be one of the
855
level strings ("trace", "debug", ...) or constants (`bunyan.TRACE`,
856
`bunyan.DEBUG`, ...). This serves as a severity threshold for that stream
857
so logs of greater severity will also pass through (i.e. If level="warn",
858
error and fatal will also pass through this stream).</td>
859
</tr>
860
<tr>
861
<td>name</td>
862
<td>No</td>
863
<td>-</td>
864
<td>A name for this stream. This may be useful for usage of `log.level(NAME,
865
LEVEL)`. See the [Levels section](#levels) for details. A stream "name" isn't
866
used for anything else.</td>
867
</tr>
868
</table>
869
870
871
## stream type: `file`
872
873
A `type === 'file'` stream requires a "path" field. Bunyan will open this
874
file for appending. E.g.:
875
876
```js
877
var log = bunyan.createLogger({
878
    name: 'foo',
879
    streams: [{
880
        path: '/var/log/foo.log',
881
        // `type: 'file'` is implied
882
    }]
883
});
884
```
885
886
<table>
887
<tr>
888
<th>Field</th>
889
<th>Required?</th>
890
<th>Default</th>
891
<th>Description</th>
892
</tr>
893
<tr>
894
<td>path</td>
895
<td>Yes</td>
896
<td>-</td>
897
<td>A file path to which to log.</td>
898
</tr>
899
<tr>
900
<td>type</td>
901
<td>No</td>
902
<td>n/a</td>
903
<td>`type == 'file'` is implied if the `path` field is given.</td>
904
</tr>
905
<tr>
906
<td>level</td>
907
<td>No</td>
908
<td>info</td>
909
<td>The level to which logging to this stream is enabled. If not
910
specified it defaults to "info". If specified this can be one of the
911
level strings ("trace", "debug", ...) or constants (`bunyan.TRACE`,
912
`bunyan.DEBUG`, ...). This serves as a severity threshold for that
913
stream so logs of greater severity will also pass through (i.e. If
914
level="warn", error and fatal will also pass through this stream).</td>
915
</tr>
916
<tr>
917
<td>name</td>
918
<td>No</td>
919
<td>-</td>
920
<td>A name for this stream. This may be useful for usage of `log.level(NAME,
921
LEVEL)`. See the [Levels section](#levels) for details. A stream "name" isn't
922
used for anything else.</td>
923
</tr>
924
</table>
925
926
927
## stream type: `rotating-file`
928
929
**WARNING on node 0.8 usage:** Users of Bunyan's `rotating-file` should (a) be
930
using at least bunyan 0.23.1 (with the fix for [this
931
issue](https://github.com/trentm/node-bunyan/pull/97)), and (b) should use at
932
least node 0.10 (node 0.8 does not support the `unref()` method on
933
`setTimeout(...)` needed for the mentioned fix). The symptom is that process
934
termination will hang for up to a full rotation period.
935
936
**WARNING on [cluster](http://nodejs.org/docs/latest/api/all.html#all_cluster)
937
usage:** Using Bunyan's `rotating-file` stream with node.js's "cluster" module
938
can result in unexpected file rotation. You must not have multiple processes
939
in the cluster logging to the same file path. In other words, you must have
940
a separate log file path for the master and each worker in the cluster.
941
Alternatively, consider using a system file rotation facility such as
942
`logrotate` on Linux or `logadm` on SmartOS/Illumos. See
943
[this comment on issue #117](https://github.com/trentm/node-bunyan/issues/117#issuecomment-44804938)
944
for details.
945
946
A `type === 'rotating-file'` is a file stream that handles file automatic
947
rotation.
948
949
```js
950
var log = bunyan.createLogger({
951
    name: 'foo',
952
    streams: [{
953
        type: 'rotating-file',
954
        path: '/var/log/foo.log',
955
        period: '1d',   // daily rotation
956
        count: 3        // keep 3 back copies
957
    }]
958
});
959
```
960
961
This will rotate '/var/log/foo.log' every day (at midnight) to:
962
963
```sh
964
/var/log/foo.log.0     # yesterday
965
/var/log/foo.log.1     # 1 day ago
966
/var/log/foo.log.2     # 2 days ago
967
```
968
969
*Currently*, there is no support for providing a template for the rotated
970
files, or for rotating when the log reaches a threshold size.
971
972
<table>
973
<tr>
974
<th>Field</th>
975
<th>Required?</th>
976
<th>Default</th>
977
<th>Description</th>
978
</tr>
979
<tr>
980
<td>type</td>
981
<td>Yes</td>
982
<td>-</td>
983
<td>"rotating-file"</td>
984
</tr>
985
<tr>
986
<td>path</td>
987
<td>Yes</td>
988
<td>-</td>
989
<td>A file path to which to log. Rotated files will be "$path.0",
990
"$path.1", ...</td>
991
</tr>
992
<tr>
993
<td>period</td>
994
<td>No</td>
995
<td>1d</td>
996
<td>The period at which to rotate. This is a string of the format
997
"$number$scope" where "$scope" is one of "ms" (milliseconds -- only useful for
998
testing), "h" (hours), "d" (days), "w" (weeks), "m" (months), "y" (years). Or
999
one of the following names can be used "hourly" (means 1h), "daily" (1d),
1000
"weekly" (1w), "monthly" (1m), "yearly" (1y). Rotation is done at the start of
1001
the scope: top of the hour (h), midnight (d), start of Sunday (w), start of the
1002
1st of the month (m), start of Jan 1st (y).</td>
1003
</tr>
1004
<tr>
1005
<td>count</td>
1006
<td>No</td>
1007
<td>10</td>
1008
<td>The number of rotated files to keep.</td>
1009
</tr>
1010
<tr>
1011
<td>level</td>
1012
<td>No</td>
1013
<td>info</td>
1014
<td>The level at which logging to this stream is enabled. If not
1015
specified it defaults to "info". If specified this can be one of the
1016
level strings ("trace", "debug", ...) or constants (`bunyan.TRACE`,
1017
`bunyan.DEBUG`, ...).</td>
1018
</tr>
1019
<tr>
1020
<td>name</td>
1021
<td>No</td>
1022
<td>-</td>
1023
<td>A name for this stream. This may be useful for usage of `log.level(NAME,
1024
LEVEL)`. See the [Levels section](#levels) for details. A stream "name" isn't
1025
used for anything else.</td>
1026
</tr>
1027
</table>
1028
1029
1030
**Note on log rotation**: Often you may be using external log rotation utilities
1031
like `logrotate` on Linux or `logadm` on SmartOS/Illumos. In those cases, unless
1032
your are ensuring "copy and truncate" semantics (via `copytruncate` with
1033
logrotate or `-c` with logadm) then the fd for your 'file' stream will change.
1034
You can tell bunyan to reopen the file stream with code like this in your
1035
app:
1036
1037
```js
1038
var log = bunyan.createLogger(...);
1039
...
1040
process.on('SIGUSR2', function () {
1041
    log.reopenFileStreams();
1042
});
1043
```
1044
1045
where you'd configure your log rotation to send SIGUSR2 (or some other signal)
1046
to your process. Any other mechanism to signal your app to run
1047
`log.reopenFileStreams()` would work as well.
1048
1049
1050
## stream type: `raw`
1051
1052
- `raw`: Similar to a "stream" writable stream, except that the write method
1053
  is given raw log record *Object*s instead of a JSON-stringified string.
1054
  This can be useful for hooking on further processing to all Bunyan logging:
1055
  pushing to an external service, a RingBuffer (see below), etc.
1056
1057
1058
1059
## `raw` + RingBuffer Stream
1060
1061
Bunyan comes with a special stream called a RingBuffer which keeps the last N
1062
records in memory and does *not* write the data anywhere else.  One common
1063
strategy is to log 'info' and higher to a normal log file but log all records
1064
(including 'trace') to a ringbuffer that you can access via a debugger, or your
1065
own HTTP interface, or a post-mortem facility like MDB or node-panic.
1066
1067
To use a RingBuffer:
1068
1069
```js
1070
/* Create a ring buffer that stores the last 100 records. */
1071
var bunyan = require('bunyan');
1072
var ringbuffer = new bunyan.RingBuffer({ limit: 100 });
1073
var log = bunyan.createLogger({
1074
    name: 'foo',
1075
    streams: [
1076
        {
1077
            level: 'info',
1078
            stream: process.stdout
1079
        },
1080
        {
1081
            level: 'trace',
1082
            type: 'raw',    // use 'raw' to get raw log record objects
1083
            stream: ringbuffer
1084
        }
1085
    ]
1086
});
1087
1088
log.info('hello world');
1089
console.log(ringbuffer.records);
1090
```
1091
1092
This example emits:
1093
1094
```js
1095
[ { name: 'foo',
1096
    hostname: '912d2b29',
1097
    pid: 50346,
1098
    level: 30,
1099
    msg: 'hello world',
1100
    time: '2012-06-19T21:34:19.906Z',
1101
    v: 0 } ]
1102
```
1103
1104
## third-party streams
1105
1106
See the [user-maintained list in the Bunyan
1107
wiki](https://github.com/trentm/node-bunyan/wiki/Awesome-Bunyan).
1108
1109
1110
# Runtime log snooping via DTrace
1111
1112
On systems that support DTrace (e.g., illumos derivatives like SmartOS and
1113
OmniOS, FreeBSD, Mac), Bunyan will create a DTrace provider (`bunyan`) that
1114
makes available the following probes:
1115
1116
```sh
1117
log-trace
1118
log-debug
1119
log-info
1120
log-warn
1121
log-error
1122
log-fatal
1123
```
1124
1125
Each of these probes has a single argument: the string that would be
1126
written to the log.  Note that when a probe is enabled, it will
1127
fire whenever the corresponding function is called, even if the level of
1128
the log message is less than that of any stream.
1129
1130
1131
## DTrace examples
1132
1133
Trace all log messages coming from any Bunyan module on the system.
1134
(The `-x strsize=4k` is to raise dtrace's default 256 byte buffer size
1135
because log messages are longer than typical dtrace probes.)
1136
1137
```sh
1138
dtrace -x strsize=4k -qn 'bunyan*:::log-*{printf("%d: %s: %s", pid, probefunc, copyinstr(arg0))}'
1139
```
1140
1141
Trace all log messages coming from the "wuzzle" component:
1142
1143
```sh
1144
dtrace -x strsize=4k -qn 'bunyan*:::log-*/strstr(this->str = copyinstr(arg0), "\"component\":\"wuzzle\"") != NULL/{printf("%s", this->str)}'
1145
```
1146
1147
Aggregate debug messages from process 1234, by message:
1148
1149
```sh
1150
dtrace -x strsize=4k -n 'bunyan1234:::log-debug{@[copyinstr(arg0)] = count()}'
1151
```
1152
1153
Have the bunyan CLI pretty-print the traced logs:
1154
1155
```sh
1156
dtrace -x strsize=4k -qn 'bunyan1234:::log-*{printf("%s", copyinstr(arg0))}' | bunyan
1157
```
1158
1159
A convenience handle has been made for this:
1160
1161
```sh
1162
bunyan -p 1234
1163
```
1164
1165
On systems that support the
1166
[`jstack`](http://dtrace.org/blogs/dap/2012/04/25/profiling-node-js/) action
1167
via a node.js helper, get a stack backtrace for any debug message that
1168
includes the string "danger!":
1169
1170
```sh
1171
dtrace -x strsize=4k -qn 'log-debug/strstr(copyinstr(arg0), "danger!") != NULL/{printf("\n%s", copyinstr(arg0)); jstack()}'
1172
```
1173
1174
Output of the above might be:
1175
1176
```
1177
{"name":"foo","hostname":"763bf293-d65c-42d5-872b-4abe25d5c4c7.local","pid":12747,"level":20,"msg":"danger!","time":"2012-10-30T18:28:57.115Z","v":0}
1178
1179
          node`0x87e2010
1180
          DTraceProviderBindings.node`usdt_fire_probe+0x32
1181
          DTraceProviderBindings.node`_ZN4node11DTraceProbe5_fireEN2v85LocalINS1_5ValueEEE+0x32d
1182
          DTraceProviderBindings.node`_ZN4node11DTraceProbe4FireERKN2v89ArgumentsE+0x77
1183
          << internal code >>
1184
          (anon) as (anon) at /root/node-bunyan/lib/bunyan.js position 40484
1185
          << adaptor >>
1186
          (anon) as doit at /root/my-prog.js position 360
1187
          (anon) as list.ontimeout at timers.js position 4960
1188
          << adaptor >>
1189
          << internal >>
1190
          << entry >>
1191
          node`_ZN2v88internalL6InvokeEbNS0_6HandleINS0_10JSFunctionEEENS1_INS0_6ObjectEEEiPS5_Pb+0x101
1192
          node`_ZN2v88internal9Execution4CallENS0_6HandleINS0_6ObjectEEES4_iPS4_Pbb+0xcb
1193
          node`_ZN2v88Function4CallENS_6HandleINS_6ObjectEEEiPNS1_INS_5ValueEEE+0xf0
1194
          node`_ZN4node12MakeCallbackEN2v86HandleINS0_6ObjectEEENS1_INS0_8FunctionEEEiPNS1_INS0_5ValueEEE+0x11f
1195
          node`_ZN4node12MakeCallbackEN2v86HandleINS0_6ObjectEEENS1_INS0_6StringEEEiPNS1_INS0_5ValueEEE+0x66
1196
          node`_ZN4node9TimerWrap9OnTimeoutEP10uv_timer_si+0x63
1197
          node`uv__run_timers+0x66
1198
          node`uv__run+0x1b
1199
          node`uv_run+0x17
1200
          node`_ZN4node5StartEiPPc+0x1d0
1201
          node`main+0x1b
1202
          node`_start+0x83
1203
1204
          node`0x87e2010
1205
          DTraceProviderBindings.node`usdt_fire_probe+0x32
1206
          DTraceProviderBindings.node`_ZN4node11DTraceProbe5_fireEN2v85LocalINS1_5ValueEEE+0x32d
1207
          DTraceProviderBindings.node`_ZN4node11DTraceProbe4FireERKN2v89ArgumentsE+0x77
1208
          << internal code >>
1209
          (anon) as (anon) at /root/node-bunyan/lib/bunyan.js position 40484
1210
          << adaptor >>
1211
          (anon) as doit at /root/my-prog.js position 360
1212
          (anon) as list.ontimeout at timers.js position 4960
1213
          << adaptor >>
1214
          << internal >>
1215
          << entry >>
1216
          node`_ZN2v88internalL6InvokeEbNS0_6HandleINS0_10JSFunctionEEENS1_INS0_6ObjectEEEiPS5_Pb+0x101
1217
          node`_ZN2v88internal9Execution4CallENS0_6HandleINS0_6ObjectEEES4_iPS4_Pbb+0xcb
1218
          node`_ZN2v88Function4CallENS_6HandleINS_6ObjectEEEiPNS1_INS_5ValueEEE+0xf0
1219
          node`_ZN4node12MakeCallbackEN2v86HandleINS0_6ObjectEEENS1_INS0_8FunctionEEEiPNS1_INS0_5ValueEEE+0x11f
1220
          node`_ZN4node12MakeCallbackEN2v86HandleINS0_6ObjectEEENS1_INS0_6StringEEEiPNS1_INS0_5ValueEEE+0x66
1221
          node`_ZN4node9TimerWrap9OnTimeoutEP10uv_timer_si+0x63
1222
          node`uv__run_timers+0x66
1223
          node`uv__run+0x1b
1224
          node`uv_run+0x17
1225
          node`_ZN4node5StartEiPPc+0x1d0
1226
          node`main+0x1b
1227
          node`_start+0x83
1228
```
1229
1230
1231
# Runtime environments
1232
1233
Node-bunyan supports running in a few runtime environments:
1234
1235
- [Node.js](https://nodejs.org/)
1236
- [Browserify](http://browserify.org/): See the
1237
  [Browserify section](#browserify) below.
1238
- [Webpack](https://webpack.github.io/): See the [Webpack section](#webpack) below.
1239
- [NW.js](http://nwjs.io/)
1240
1241
Support for other runtime environments is welcome. If you have suggestions,
1242
fixes, or mentions that node-bunyan already works in some other JavaScript
1243
runtime, please open an [issue](https://github.com/trentm/node-bunyan/issues/new)
1244
or a pull request.
1245
1246
The primary target is Node.js. It is the only environment in which I
1247
regularly test. If you have suggestions for how to automate testing for other
1248
environments, I'd appreciate feedback on [this automated testing
1249
issue](https://github.com/trentm/node-bunyan/issues/342).
1250
1251
## Browserify
1252
1253
As the [Browserify](http://browserify.org/) site says it "lets you
1254
`require('modules')` in the browser by bundling up all of your dependencies."
1255
It is a build tool to run on your node.js script to bundle up your script and
1256
all its node.js dependencies into a single file that is runnable in the
1257
browser via:
1258
1259
```html
1260
<script src="play.browser.js"></script>
1261
```
1262
1263
As of version 1.1.0, node-bunyan supports being run via Browserify. The
1264
default [stream](#streams) when running in the browser is one that emits
1265
raw log records to `console.log/info/warn/error`.
1266
1267
Here is a quick example showing you how you can get this working for your
1268
script.
1269
1270
1. Get browserify and bunyan installed in your module:
1271
1272
    ```sh
1273
    $ npm install browserify bunyan
1274
    ```
1275
1276
2. An example script using Bunyan, "play.js":
1277
1278
    ```js
1279
    var bunyan = require('bunyan');
1280
    var log = bunyan.createLogger({name: 'play', level: 'debug'});
1281
    log.trace('this one does not emit');
1282
    log.debug('hi on debug');   // console.log
1283
    log.info('hi on info');     // console.info
1284
    log.warn('hi on warn');     // console.warn
1285
    log.error('hi on error');   // console.error
1286
    ```
1287
1288
3. Build this into a bundle to run in the browser, "play.browser.js":
1289
1290
    ```sh
1291
    $ ./node_modules/.bin/browserify play.js -o play.browser.js
1292
    ```
1293
1294
4. Put that into an HTML file, "play.html":
1295
1296
    ```html
1297
    <!DOCTYPE html>
1298
    <html>
1299
    <head>
1300
      <meta charset="utf-8">
1301
      <script src="play.browser.js"></script>
1302
    </head>
1303
    <body>
1304
      <div>hi</div>
1305
    </body>
1306
    </html>
1307
    ```
1308
1309
5. Open that in your browser and open your browser console:
1310
1311
    ```sh
1312
    $ open play.html
1313
    ```
1314
1315
Here is what it looks like in Firefox's console: ![Bunyan + Browserify in the
1316
Firefox console](./docs/img/bunyan.browserify.png)
1317
1318
For some, the raw log records might not be desired. To have a rendered log line
1319
you'll want to add your own stream, starting with something like this:
1320
1321
```js
1322
var bunyan = require('./lib/bunyan');
1323
1324
function MyRawStream() {}
1325
MyRawStream.prototype.write = function (rec) {
1326
    console.log('[%s] %s: %s',
1327
        rec.time.toISOString(),
1328
        bunyan.nameFromLevel[rec.level],
1329
        rec.msg);
1330
}
1331
1332
var log = bunyan.createLogger({
1333
    name: 'play',
1334
    streams: [
1335
        {
1336
            level: 'info',
1337
            stream: new MyRawStream(),
1338
            type: 'raw'
1339
        }
1340
    ]
1341
});
1342
1343
log.info('hi on info');
1344
```
1345
1346
## Webpack
1347
Webpack can work with the same example Browserify above. To do this, we need to make webpack ignore optional files:
1348
Create "empty_shim.js":
1349
```javascript
1350
// This is an empty shim for things that should be not be included in webpack
1351
```
1352
Now tell webpack to use this file for
1353
[optional dependencies](https://webpack.github.io/docs/configuration.html#resolve-alias)
1354
in your "webpack.config.js":
1355
```
1356
resolve: {
1357
    // These shims are needed for bunyan
1358
    alias: {
1359
        'dtrace-provider': '/path/to/shim/empty_shim.js',
1360
        fs: '/path/to/shim/empty_shim.js',
1361
        'safe-json-stringify': '/path/to/shim/empty_shim.js',
1362
        mv: '/path/to/shim/empty_shim.js',
1363
        'source-map-support': '/path/to/shim/empty_shim.js'
1364
    }
1365
}
1366
```
1367
Now webpack builds, ignoring these optional dependencies via shimming in an empty JS file!
1368
1369
# Versioning
1370
1371
All versions are `<major>.<minor>.<patch>` which will be incremented for
1372
breaking backward compat and major reworks, new features without breaking
1373
change, and bug fixes, respectively. tl;dr: [Semantic
1374
versioning](http://semver.org/).
1375
1376
# License
1377
1378
[MIT](./LICENSE.txt).
1379
1380
# See Also
1381
1382
See the [user-maintained list of Bunyan-related software in the Bunyan
1383
wiki](https://github.com/trentm/node-bunyan/wiki/Awesome-Bunyan).