root / HServer / 00.Server / 00.Program / node_modules / bunyan / README.md
이력 | 보기 | 이력해설 | 다운로드 (44.7 KB)
1 |
[](https://www.npmjs.com/package/bunyan) |
---|---|
2 |
[](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 |
 |
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:  |
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). |