Compare commits
48 Commits
Author | SHA1 | Date |
---|---|---|
Jonatan Nilsson | 035af486a2 | |
Jonatan Nilsson | e28bfc0d1c | |
Jonatan Nilsson | 2ce531c0e8 | |
Jonatan Nilsson | a945e7e9b9 | |
Jonatan Nilsson | 1090cdc21a | |
Jonatan Nilsson | 5937469265 | |
Jonatan Nilsson | 97765f09a5 | |
Jonatan Nilsson | 89429a34c1 | |
Jonatan Nilsson | 1260b89e48 | |
Jonatan Nilsson | a119d46dab | |
Jonatan Nilsson | d01ea1c740 | |
Jonatan Nilsson | 9c404a6cf5 | |
Jonatan Nilsson | 49b80e4722 | |
Jonatan Nilsson | 6b4f927f4f | |
Jonatan Nilsson | 29019b3145 | |
Jonatan Nilsson | 4a50ed39c2 | |
Colder Xihk | fe31b83e42 | |
Trent Mick | 14609dfb69 | |
Trent Mick | c0932196dd | |
Trent Mick | 11e26ecf55 | |
Trent Mick | 39aa5d1f6e | |
Trent Mick | 2d17313c4a | |
Li Kai | 3e085f2fdb | |
Trent Mick | e540a76424 | |
Trent Mick | 368c94583d | |
Trent Mick | 9d12604653 | |
Trent Mick | 05a49c34c9 | |
Trent Mick | 40ee2503cc | |
Trent Mick | 0d62f8a890 | |
Trent Mick | 2a5bdd231a | |
Trent Mick | f812975f0f | |
Trent Mick | 13c86f1d15 | |
Trent Mick | 1d511793f0 | |
Trent Mick | a75df963f6 | |
Trent Mick | 58846afa89 | |
Greg Walden | 994f90eb83 | |
Trent Mick | 34f4469a25 | |
Zach Bjornson | aff022bb2e | |
Trent Mick | f4fff7b7fa | |
Trent Mick | b33b7e523d | |
Trent Mick | b66571f113 | |
Trent Mick | a778ea5697 | |
Trent Mick | 4d4663758e | |
Samuel Tribehou | 2d535a6395 | |
Thomas Lee | 027cf5aab1 | |
Trent Mick | 5c51dc2182 | |
Paweł Sroka | 4a500c4536 | |
Paweł Sroka | b74180cf14 |
|
@ -1,7 +0,0 @@
|
|||
/tmp
|
||||
/node_modules
|
||||
*.log
|
||||
/examples
|
||||
/test
|
||||
/*.tgz
|
||||
/tools
|
1
AUTHORS
1
AUTHORS
|
@ -39,3 +39,4 @@ Paul Milham (https://github.com/domrein)
|
|||
Frankie O'Rourke (https://github.com/psfrankie)
|
||||
Cody Mello (https://github.com/melloc)
|
||||
Todd Whiteman (https://github.com/twhiteman)
|
||||
Zach Bjornson (https://github.com/zbjornson)
|
||||
|
|
38
CHANGES.md
38
CHANGES.md
|
@ -7,6 +7,44 @@ Known issues:
|
|||
|
||||
## not yet released
|
||||
|
||||
(nothing yet)
|
||||
|
||||
|
||||
## 2.0.2 (beta)
|
||||
|
||||
- [issue #444] Fix the `bunyan` CLI to not duplicate the "HTTP/1.1 ..." status
|
||||
line when serializing a "res" field.
|
||||
|
||||
|
||||
## 2.0.1 (beta)
|
||||
|
||||
- [issue #504] **Backward incompatible change to the `bunyan` CLI:**
|
||||
The `bunyan` CLI no longer adds a `Host` header when rendering a `client_req`
|
||||
field in a log record. In 1.x it used to do this (using `client_req.address`
|
||||
and `client_req.port`), on the *guess* that Node.js' `http.ClientRequest`
|
||||
handling would add it. However, the guess can be wrong and misleading. It is
|
||||
better not to guess.
|
||||
|
||||
|
||||
## 2.0.0 (beta)
|
||||
|
||||
- [issue #499] Fix some `bunyan` CLI exit handling problems.
|
||||
|
||||
|
||||
## 1.8.10
|
||||
|
||||
- Ensure that `bunyan` errors out if attempting to use `-p PID` and
|
||||
file args at the same time.
|
||||
|
||||
|
||||
## 1.8.9
|
||||
|
||||
- [pull #409, issue #246] Revert a change added to the `bunyan` CLI version
|
||||
1.0.1 where `SIGINT` was ignored, such that Ctrl+C could not be used to
|
||||
terminate bunyan. (By @zbjornson and @davepacheco.)
|
||||
- [pull #469] Fix a strict mode (`"use strict;"`) error in some versions of
|
||||
Safari.
|
||||
|
||||
|
||||
## 1.8.8
|
||||
|
||||
|
|
149
Makefile
149
Makefile
|
@ -1,149 +0,0 @@
|
|||
|
||||
#---- Tools
|
||||
|
||||
NODEUNIT := ./node_modules/.bin/nodeunit
|
||||
SUDO := sudo
|
||||
ifeq ($(shell uname -s),SunOS)
|
||||
# On SunOS (e.g. SmartOS) we expect to run the test suite as the
|
||||
# root user -- necessary to run dtrace. Therefore `pfexec` isn't
|
||||
# necessary.
|
||||
SUDO :=
|
||||
endif
|
||||
DTRACE_UP_IN_HERE=
|
||||
ifeq ($(shell uname -s),SunOS)
|
||||
DTRACE_UP_IN_HERE=1
|
||||
endif
|
||||
ifeq ($(shell uname -s),Darwin)
|
||||
DTRACE_UP_IN_HERE=1
|
||||
endif
|
||||
NODEOPT ?= $(HOME)/opt
|
||||
|
||||
|
||||
#---- Files
|
||||
|
||||
JSSTYLE_FILES := $(shell find lib test tools examples -name "*.js") bin/bunyan
|
||||
# All test files *except* dtrace.test.js.
|
||||
NON_DTRACE_TEST_FILES := $(shell ls -1 test/*.test.js | grep -v dtrace | xargs)
|
||||
|
||||
|
||||
#---- Targets
|
||||
|
||||
all $(NODEUNIT):
|
||||
npm install $(NPM_INSTALL_FLAGS)
|
||||
|
||||
# Ensure all version-carrying files have the same version.
|
||||
.PHONY: versioncheck
|
||||
versioncheck:
|
||||
@echo version is: $(shell cat package.json | json version)
|
||||
[[ `cat package.json | json version` == `grep '^## ' CHANGES.md | head -2 | tail -1 | awk '{print $$2}'` ]]
|
||||
[[ `cat package.json | json version` == `grep '^var VERSION' bin/bunyan | awk -F"'" '{print $$2}'` ]]
|
||||
[[ `cat package.json | json version` == `grep '^var VERSION' lib/bunyan.js | awk -F"'" '{print $$2}'` ]]
|
||||
@echo Version check ok.
|
||||
|
||||
.PHONY: cutarelease
|
||||
cutarelease: check
|
||||
[[ -z `git status --short` ]] # If this fails, the working dir is dirty.
|
||||
@which json 2>/dev/null 1>/dev/null && \
|
||||
ver=$(shell json -f package.json version) && \
|
||||
name=$(shell json -f package.json name) && \
|
||||
publishedVer=$(shell npm view -j $(shell json -f package.json name)@$(shell json -f package.json version) version 2>/dev/null) && \
|
||||
if [[ -n "$$publishedVer" ]]; then \
|
||||
echo "error: $$name@$$ver is already published to npm"; \
|
||||
exit 1; \
|
||||
fi && \
|
||||
echo "** Are you sure you want to tag and publish $$name@$$ver to npm?" && \
|
||||
echo "** Enter to continue, Ctrl+C to abort." && \
|
||||
read
|
||||
ver=$(shell cat package.json | json version) && \
|
||||
date=$(shell date -u "+%Y-%m-%d") && \
|
||||
git tag -a "$$ver" -m "version $$ver ($$date)" && \
|
||||
git push --tags origin && \
|
||||
npm publish
|
||||
|
||||
.PHONY: docs
|
||||
docs: toc
|
||||
@[[ `which ronn` ]] || (echo "No 'ronn' on your PATH. Install with 'gem install ronn'" && exit 2)
|
||||
mkdir -p man/man1
|
||||
ronn --style=toc --manual="bunyan manual" --date=$(shell git log -1 --pretty=format:%cd --date=short) --roff --html docs/bunyan.1.ronn
|
||||
python -c 'import sys; h = open("docs/bunyan.1.html").read(); h = h.replace(".mp dt.flush {float:left;width:8ex}", ""); open("docs/bunyan.1.html", "w").write(h)'
|
||||
python -c 'import sys; h = open("docs/bunyan.1.html").read(); h = h.replace("</body>", """<a href="https://github.com/trentm/node-bunyan"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on GitHub"></a></body>"""); open("docs/bunyan.1.html", "w").write(h)'
|
||||
@echo "# test with 'man ./docs/bunyan.1' and 'open ./docs/bunyan.1.html'"
|
||||
|
||||
# Re-generate the README.md table of contents.
|
||||
toc:
|
||||
./node_modules/.bin/markdown-toc -i README.md
|
||||
|
||||
|
||||
.PHONY: publish
|
||||
publish:
|
||||
mkdir -p tmp
|
||||
[[ -d tmp/bunyan-gh-pages ]] || git clone git@github.com:trentm/node-bunyan.git tmp/bunyan-gh-pages
|
||||
cd tmp/bunyan-gh-pages && git checkout gh-pages && git pull --rebase origin gh-pages
|
||||
cp docs/index.html tmp/bunyan-gh-pages/index.html
|
||||
cp docs/bunyan.1.html tmp/bunyan-gh-pages/bunyan.1.html
|
||||
(cd tmp/bunyan-gh-pages \
|
||||
&& git commit -a -m "publish latest docs" \
|
||||
&& git push origin gh-pages || true)
|
||||
|
||||
.PHONY: distclean
|
||||
distclean:
|
||||
rm -rf node_modules
|
||||
|
||||
|
||||
#---- test
|
||||
|
||||
.PHONY: test
|
||||
test: $(NODEUNIT)
|
||||
test -z "$(DTRACE_UP_IN_HERE)" || test -n "$(SKIP_DTRACE)" || \
|
||||
(node -e 'require("dtrace-provider").createDTraceProvider("isthisthingon")' && \
|
||||
echo "\nNote: Use 'SKIP_DTRACE=1 make test' to skip parts of the test suite that require root." && \
|
||||
$(SUDO) $(NODEUNIT) test/dtrace.test.js)
|
||||
$(NODEUNIT) $(NON_DTRACE_TEST_FILES)
|
||||
|
||||
# Test will all node supported versions (presumes install locations I use on
|
||||
# my machine -- "~/opt/node-VER"):
|
||||
# Note: 'test4' is last so (if all is well) I end up with a binary
|
||||
# dtrace-provider build for my current default node version.
|
||||
.PHONY: testall
|
||||
testall: test7 test6 test012 test010 test4
|
||||
|
||||
.PHONY: test7
|
||||
test7:
|
||||
@echo "# Test node 7.x (with node `$(NODEOPT)/node-7/bin/node --version`)"
|
||||
@$(NODEOPT)/node-7/bin/node --version | grep '^v7\.'
|
||||
PATH="$(NODEOPT)/node-7/bin:$(PATH)" make distclean all test
|
||||
.PHONY: test6
|
||||
test6:
|
||||
@echo "# Test node 6.x (with node `$(NODEOPT)/node-6/bin/node --version`)"
|
||||
@$(NODEOPT)/node-6/bin/node --version | grep '^v6\.'
|
||||
PATH="$(NODEOPT)/node-6/bin:$(PATH)" make distclean all test
|
||||
.PHONY: test4
|
||||
test4:
|
||||
@echo "# Test node 4.x (with node `$(NODEOPT)/node-4/bin/node --version`)"
|
||||
@$(NODEOPT)/node-4/bin/node --version | grep '^v4\.'
|
||||
PATH="$(NODEOPT)/node-4/bin:$(PATH)" make distclean all test
|
||||
.PHONY: test012
|
||||
test012:
|
||||
@echo "# Test node 0.12.x (with node `$(NODEOPT)/node-0.12/bin/node --version`)"
|
||||
@$(NODEOPT)/node-0.12/bin/node --version | grep '^v0\.12\.'
|
||||
PATH="$(NODEOPT)/node-0.12/bin:$(PATH)" make distclean all test
|
||||
.PHONY: test010
|
||||
test010:
|
||||
@echo "# Test node 0.10.x (with node `$(NODEOPT)/node-0.10/bin/node --version`)"
|
||||
@$(NODEOPT)/node-0.10/bin/node --version | grep '^v0\.10\.'
|
||||
PATH="$(NODEOPT)/node-0.10/bin:$(PATH)" make distclean all test
|
||||
|
||||
|
||||
#---- check
|
||||
|
||||
.PHONY: check-jsstyle
|
||||
check-jsstyle: $(JSSTYLE_FILES)
|
||||
./tools/jsstyle -o indent=4,doxygen,unparenthesized-return=0,blank-after-start-comment=0,leading-right-paren-ok=1 $(JSSTYLE_FILES)
|
||||
|
||||
.PHONY: check
|
||||
check: check-jsstyle versioncheck
|
||||
@echo "Check ok."
|
||||
|
||||
.PHONY: prepush
|
||||
prepush: check testall
|
||||
@echo "Okay to push."
|
168
README.md
168
README.md
|
@ -1,9 +1,7 @@
|
|||
[![npm version](https://img.shields.io/npm/v/bunyan.svg?style=flat)](https://www.npmjs.com/package/bunyan)
|
||||
|
||||
Bunyan is **a simple and fast JSON logging library** for node.js services:
|
||||
Bunyan-lite is **a simple and fast JSON logging library** for node.js services that contains zero dependencies:
|
||||
|
||||
```js
|
||||
var bunyan = require('bunyan');
|
||||
var bunyan = require('bunyan-lite');
|
||||
var log = bunyan.createLogger({name: "myapp"});
|
||||
log.info("hi");
|
||||
```
|
||||
|
@ -22,7 +20,6 @@ record (see below).
|
|||
|
||||
<!-- toc -->
|
||||
|
||||
- [Current Status](#current-status)
|
||||
- [Installation](#installation)
|
||||
- [Features](#features)
|
||||
- [Introduction](#introduction)
|
||||
|
@ -32,6 +29,7 @@ record (see below).
|
|||
* [Streams Introduction](#streams-introduction)
|
||||
* [log.child](#logchild)
|
||||
* [Serializers](#serializers)
|
||||
+ [Requirements for serializers functions](#requirements-for-serializers-functions)
|
||||
+ [Standard Serializers](#standard-serializers)
|
||||
* [src](#src)
|
||||
- [Levels](#levels)
|
||||
|
@ -41,6 +39,7 @@ record (see below).
|
|||
* [Recommended/Best Practice Fields](#recommendedbest-practice-fields)
|
||||
* [Other fields to consider](#other-fields-to-consider)
|
||||
- [Streams](#streams)
|
||||
* [Adding a Stream](#adding-a-stream)
|
||||
* [stream errors](#stream-errors)
|
||||
* [stream type: `stream`](#stream-type-stream)
|
||||
* [stream type: `file`](#stream-type-file)
|
||||
|
@ -59,23 +58,10 @@ record (see below).
|
|||
|
||||
<!-- tocstop -->
|
||||
|
||||
# Current Status
|
||||
|
||||
Solid core functionality is there. Joyent is using this for a number of
|
||||
production services. Bunyan supports node 0.10 and greater. Follow
|
||||
<a href="https://twitter.com/intent/user?screen_name=trentmick" target="_blank">@trentmick</a>
|
||||
for updates to Bunyan.
|
||||
|
||||
There is an email discussion list
|
||||
[bunyan-logging@googlegroups.com](mailto:bunyan-logging@googlegroups.com),
|
||||
also [as a forum in the
|
||||
browser](https://groups.google.com/forum/?fromgroups#!forum/bunyan-logging).
|
||||
|
||||
|
||||
# Installation
|
||||
|
||||
```sh
|
||||
npm install bunyan
|
||||
npm install bunyan-lite
|
||||
```
|
||||
|
||||
**Tip**: The `bunyan` CLI tool is written to be compatible (within reason) with
|
||||
|
@ -107,7 +93,7 @@ named after the logging levels:
|
|||
|
||||
```js
|
||||
// hi.js
|
||||
var bunyan = require('bunyan');
|
||||
var bunyan = require('bunyan-lite');
|
||||
var log = bunyan.createLogger({name: 'myapp'});
|
||||
log.info('hi');
|
||||
log.warn({lang: 'fr'}, 'au revoir');
|
||||
|
@ -129,7 +115,7 @@ $ node hi.js
|
|||
## Constructor API
|
||||
|
||||
```js
|
||||
var bunyan = require('bunyan');
|
||||
var bunyan = require('bunyan-lite');
|
||||
var log = bunyan.createLogger({
|
||||
name: <string>, // Required
|
||||
level: <level name or number>, // Optional, see "Levels" section
|
||||
|
@ -171,7 +157,13 @@ log.info(err, 'more on this: %s', more);
|
|||
|
||||
log.info({foo: 'bar', err: err}, 'some msg about this error');
|
||||
// To pass in an Error *and* other fields, use the `err`
|
||||
// field name for the Error instance.
|
||||
// field name for the Error instance **and ensure your logger
|
||||
// has a `err` serializer.** One way to ensure the latter is:
|
||||
// var log = bunyan.createLogger({
|
||||
// ...,
|
||||
// serializers: bunyan.stdSerializers
|
||||
// });
|
||||
// See the "Serializers" section below for details.
|
||||
```
|
||||
|
||||
Note that this implies **you cannot blindly pass any object as the first
|
||||
|
@ -278,7 +270,7 @@ be exactly as on the parent logger with the addition of the `widget_type`
|
|||
field:
|
||||
|
||||
```js
|
||||
var bunyan = require('bunyan');
|
||||
var bunyan = require('bunyan-lite');
|
||||
var log = bunyan.createLogger({name: 'myapp'});
|
||||
|
||||
function Wuzzle(options) {
|
||||
|
@ -343,9 +335,9 @@ and have the `req` entry in the log record be just a reasonable subset of
|
|||
|
||||
|
||||
A logger instance can have a `serializers` mapping of log record field name
|
||||
("req" in this example) to a serializer function. When creating the log
|
||||
record, Bunyan will call the serializer function for fields of that name.
|
||||
An example:
|
||||
("req" in this example) to a serializer function. When creating the log record,
|
||||
Bunyan will call the serializer function for *top-level* fields of that name. An
|
||||
example:
|
||||
|
||||
```js
|
||||
function reqSerializer(req) {
|
||||
|
@ -364,25 +356,88 @@ var log = bunyan.createLogger({
|
|||
```
|
||||
|
||||
|
||||
Typically serializers are added to a logger at creation time via
|
||||
`bunyan.createLogger({..., serializers: <serializers>})`. However, serializers
|
||||
can be added after creation via `<logger>.addSerializers(...)`, e.g.:
|
||||
Typically serializers are added to a logger at creation time via:
|
||||
|
||||
```js
|
||||
var log = bunyan.createLogger({
|
||||
name: 'myapp',
|
||||
serializers: {
|
||||
foo: function fooSerializer(foo) { ... },
|
||||
...
|
||||
}
|
||||
});
|
||||
|
||||
// or
|
||||
var log = bunyan.createLogger({
|
||||
name: 'myapp',
|
||||
serializers: bunyan.stdSerializers
|
||||
});
|
||||
```
|
||||
|
||||
Serializers can also be added after creation via `<logger>.addSerializers(...)`,
|
||||
e.g.:
|
||||
|
||||
```js
|
||||
var log = bunyan.createLogger({name: 'myapp'});
|
||||
log.addSerializers({req: reqSerializer});
|
||||
```
|
||||
|
||||
### Requirements for serializers functions
|
||||
|
||||
**Note**: Your own serializers should never throw, otherwise you'll get an
|
||||
ugly message on stderr from Bunyan (along with the traceback) and the field
|
||||
in your log record will be replaced with a short error message.
|
||||
A serializer function is passed unprotected objects that are passed to the
|
||||
`log.info`, `log.debug`, etc. call. This means a poorly written serializer
|
||||
function can case side-effects. Logging shouldn't do that. Here are a few
|
||||
rules and best practices for serializer functions:
|
||||
|
||||
- A serializer function *should never throw*. The bunyan library *does*
|
||||
protect somewhat from this: if the serializer throws an error, then
|
||||
bunyan will (a) write an ugly message on stderr (along with the traceback),
|
||||
and (b) the field in the log record will be replace with a short error message.
|
||||
For example:
|
||||
|
||||
```
|
||||
bunyan: ERROR: Exception thrown from the "foo" Bunyan serializer. This should never happen. This is a bug in that serializer function.
|
||||
TypeError: Cannot read property 'not' of undefined
|
||||
at Object.fooSerializer [as foo] (/Users/trentm/tm/node-bunyan/bar.js:8:26)
|
||||
at /Users/trentm/tm/node-bunyan/lib/bunyan.js:873:50
|
||||
at Array.forEach (native)
|
||||
at Logger._applySerializers (/Users/trentm/tm/node-bunyan/lib/bunyan.js:865:35)
|
||||
at mkRecord (/Users/trentm/tm/node-bunyan/lib/bunyan.js:978:17)
|
||||
at Logger.info (/Users/trentm/tm/node-bunyan/lib/bunyan.js:1044:19)
|
||||
at Object.<anonymous> (/Users/trentm/tm/node-bunyan/bar.js:13:5)
|
||||
at Module._compile (module.js:409:26)
|
||||
at Object.Module._extensions..js (module.js:416:10)
|
||||
at Module.load (module.js:343:32)
|
||||
{"name":"bar","hostname":"danger0.local","pid":47411,"level":30,"foo":"(Error in Bunyan log \"foo\" serializer broke field. See stderr for details.)","msg":"one","time":"2017-03-08T02:53:51.173Z","v":0}
|
||||
```
|
||||
|
||||
- A serializer function *should never mutate the given object*. Doing so will
|
||||
change the object in your application.
|
||||
|
||||
- A serializer function *should be defensive*. In my experience it is common to
|
||||
set a serializer in an app, say for field name "foo", and then accidentally
|
||||
have a log line that passes a "foo" that is undefined, or null, or of some
|
||||
unexpected type. A good start at defensiveness is to start with this:
|
||||
|
||||
```javascript
|
||||
function fooSerializer(foo) {
|
||||
// Guard against foo be null/undefined. Check that expected fields
|
||||
// are defined.
|
||||
if (!foo || !foo.bar)
|
||||
return foo;
|
||||
var obj = {
|
||||
// Create the object to be logged.
|
||||
bar: foo.bar
|
||||
}
|
||||
return obj;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
### Standard Serializers
|
||||
|
||||
Bunyan includes a small set of "standard serializers", exported as
|
||||
`bunyan.stdSerializers`. Their use is completely optional. Example using
|
||||
`bunyan.stdSerializers`. Their use is completely optional. An example using
|
||||
all of them:
|
||||
|
||||
```js
|
||||
|
@ -405,7 +460,7 @@ Standard serializers are:
|
|||
|
||||
| Field | Description |
|
||||
| ----- | ----------- |
|
||||
| err | Used for serializing JavaScript error objects, including traversing an error's cause chain for error objects with a `.cause()` -- e.g. as from [verror](https://github.com/davepacheco/node-verror). |
|
||||
| err | Used for serializing JavaScript error objects, including traversing an error's cause chain for error objects with a `.cause()` -- e.g. as from [verror](https://github.com/joyent/node-verror). |
|
||||
| req | Common fields from a node.js HTTP request object. |
|
||||
| res | Common fields from a node.js HTTP response object. |
|
||||
|
||||
|
@ -557,7 +612,7 @@ Pretty-printed:
|
|||
## Core fields
|
||||
|
||||
- `v`: Required. Integer. Added by Bunyan. Cannot be overridden.
|
||||
This is the Bunyan log format version (`require('bunyan').LOG_VERSION`).
|
||||
This is the Bunyan log format version (`require('bunyan-lite').LOG_VERSION`).
|
||||
The log version is a single integer. `0` is until I release a version
|
||||
"1.0.0" of node-bunyan. Thereafter, starting with `1`, this will be
|
||||
incremented if there is any backward incompatible change to the log record
|
||||
|
@ -661,7 +716,7 @@ manage the stream. A Bunyan Logger instance has one or more streams.
|
|||
In general streams are specified with the "streams" option:
|
||||
|
||||
```js
|
||||
var bunyan = require('bunyan');
|
||||
var bunyan = require('bunyan-lite');
|
||||
var log = bunyan.createLogger({
|
||||
name: "foo",
|
||||
streams: [
|
||||
|
@ -692,6 +747,20 @@ on log records).
|
|||
If neither "streams" nor "stream" are specified, the default is a stream of
|
||||
type "stream" emitting to `process.stdout` at the "info" level.
|
||||
|
||||
## Adding a Stream
|
||||
|
||||
After a bunyan instance has been initialized, you may add additional streams by
|
||||
calling the `addStream` function.
|
||||
|
||||
```js
|
||||
var bunyan = require('bunyan-lite');
|
||||
var log = bunyan.createLogger('myLogger');
|
||||
log.addStream({
|
||||
name: "myNewStream",
|
||||
stream: process.stderr,
|
||||
level: "debug"
|
||||
});
|
||||
```
|
||||
|
||||
## stream errors
|
||||
|
||||
|
@ -1005,7 +1074,7 @@ To use a RingBuffer:
|
|||
|
||||
```js
|
||||
/* Create a ring buffer that stores the last 100 records. */
|
||||
var bunyan = require('bunyan');
|
||||
var bunyan = require('bunyan-lite');
|
||||
var ringbuffer = new bunyan.RingBuffer({ limit: 100 });
|
||||
var log = bunyan.createLogger({
|
||||
name: 'foo',
|
||||
|
@ -1213,7 +1282,7 @@ script.
|
|||
2. An example script using Bunyan, "play.js":
|
||||
|
||||
```js
|
||||
var bunyan = require('bunyan');
|
||||
var bunyan = require('bunyan-lite');
|
||||
var log = bunyan.createLogger({name: 'play', level: 'debug'});
|
||||
log.trace('this one does not emit');
|
||||
log.debug('hi on debug'); // console.log
|
||||
|
@ -1282,26 +1351,17 @@ log.info('hi on info');
|
|||
|
||||
## Webpack
|
||||
Webpack can work with the same example Browserify above. To do this, we need to make webpack ignore optional files:
|
||||
Create "empty-shim.js":
|
||||
```javascript
|
||||
// This is an empty shim for things that should be not be included in webpack
|
||||
```
|
||||
Now tell webpack to use this file for
|
||||
[optional dependencies](https://webpack.github.io/docs/configuration.html#resolve-alias)
|
||||
|
||||
Now tell webpack to ignore files for
|
||||
[optional dependencies](https://webpack.js.org/configuration/module/#module-noparse)
|
||||
in your "webpack.config.js":
|
||||
```
|
||||
resolve: {
|
||||
// These shims are needed for bunyan
|
||||
alias: {
|
||||
'dtrace-provider': '/path/to/shim/empty_shim.js',
|
||||
fs: '/path/to/shim/empty_shim.js',
|
||||
'safe-json-stringify': '/path/to/shim/empty_shim.js',
|
||||
mv: '/path/to/shim/empty_shim.js',
|
||||
'source-map-support': '/path/to/shim/empty_shim.js'
|
||||
}
|
||||
module: {
|
||||
noParse: [/dtrace-provider$/, /safe-json-stringify$/, /mv/],
|
||||
...
|
||||
}
|
||||
```
|
||||
Now webpack builds, ignoring these optional dependencies via shimming in an empty JS file!
|
||||
Now webpack builds.
|
||||
|
||||
# Versioning
|
||||
|
||||
|
|
72
TODO.md
72
TODO.md
|
@ -1,7 +1,71 @@
|
|||
- `bunyan` (without redir) ^C should stop, doesn't since recent change
|
||||
# v2
|
||||
|
||||
- `createLogger(<config-and-fields>, <fields>)` changes (#460)
|
||||
- see section below
|
||||
- the dtrace-provider thing (#487)
|
||||
TODO: answer Cody email
|
||||
- use package.json version for VERSION
|
||||
- use deps
|
||||
- dashdash
|
||||
- assert-plus?
|
||||
- verror?
|
||||
- break out to multiple files
|
||||
- want to work through PRs before that, so don't just break them all
|
||||
- TODO: a quick pass through tickets and pulls for other things to include
|
||||
- get ticket refs for the above, if any
|
||||
- formatters: read up again on `glp master..1.x`
|
||||
- support for customer formatters
|
||||
- for the CLI as well? How? ~/.bunyanrc?
|
||||
- if doing ~/.bunyanrc, then consider color schemes
|
||||
|
||||
|
||||
|
||||
# changes to ctor and log.child to separate fields from config
|
||||
|
||||
<https://github.com/trentm/node-bunyan/issues/460>
|
||||
|
||||
Current:
|
||||
|
||||
createLogger(<config-and-fields>)
|
||||
log.child(<config-and-fields>, <just-fields-bool>)
|
||||
|
||||
Could be:
|
||||
|
||||
createLogger(<config-and-fields>, <fields>)
|
||||
log.child(<config-and-fields>, <fields>)
|
||||
# Still support: log.child(<config-and-fields>, <just-fields-bool>)
|
||||
|
||||
Pros: Compat issues are minimal: a change is only required if there is a
|
||||
collision with used field and a new config var name.
|
||||
Cons: A *slight* con is that my guess is the common usage of child is
|
||||
`log.child(<fields>)`, so the more future-proof common usage becomes:
|
||||
|
||||
log.child(null, <fields>)
|
||||
|
||||
That's not too bad. It is clearer at least than:
|
||||
|
||||
log.child(<fields>, true)
|
||||
|
||||
TODO:
|
||||
|
||||
- is there a ticket for this work already? #460
|
||||
- make the change
|
||||
- do a migration guide? i.e. provide the grep commands to find all
|
||||
possible calls to inspect. E.g. if don't have `rg logUndefined` in your
|
||||
code, then you are fine. And one time future-proofing via changing
|
||||
to fields in the *second* arg.
|
||||
- list of issues/pulls that wanted to add new config fields
|
||||
|
||||
|
||||
# higher prio
|
||||
|
||||
- published organized advice for
|
||||
https://github.com/trentm/node-bunyan/issues/37 so can close that out.
|
||||
Perhaps a wiki page with examples and strategies.
|
||||
- man page for the bunyan CLI (refer to it in the readme)
|
||||
- `tail -f`-like support
|
||||
- 2.0 (?) with `v: 1` in log records. Fwd/bwd compat in `bunyan` CLI
|
||||
- perhaps wait for a bunyan new version with deps, and use dashdash
|
||||
with a (vapour) man page generator
|
||||
|
||||
|
||||
# docs
|
||||
|
||||
|
@ -10,6 +74,8 @@
|
|||
|
||||
# someday/maybe
|
||||
|
||||
- 2.0 (?) with `v: 1` in log records. Fwd/bwd compat in `bunyan` CLI
|
||||
- `tail -f`-like support
|
||||
- full-on docs
|
||||
- better examples/
|
||||
- better coloring
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
# version format
|
||||
version: '{build}'
|
||||
deploy: on
|
||||
|
||||
# branches to build
|
||||
branches:
|
||||
# whitelist
|
||||
only:
|
||||
- master
|
||||
|
||||
# Do not build on tags (GitHub, Bitbucket, GitLab, Gitea)
|
||||
skip_tags: true
|
||||
|
||||
# Maximum number of concurrent jobs for the project
|
||||
max_jobs: 1
|
||||
clone_depth: 1
|
||||
|
||||
# Build worker image (VM template)
|
||||
build_cloud: Docker
|
||||
|
||||
environment:
|
||||
APPVEYOR_SSH_KEY: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBRMxhawMlUlQ8l4pOaeHsZl8XDO54WQngkYM1U/XB4m samsyn\jonatan@JonatanAMD
|
||||
docker_image: node:16-alpine
|
||||
npm_config_cache: /appveyor/projects/cache
|
||||
|
||||
test_script:
|
||||
- sh: |
|
||||
chmod -R 777 /appveyor/projects
|
||||
npm install
|
||||
npm run test
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# on successful build
|
||||
on_success:
|
||||
- sh: |
|
||||
apk add curl jq
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "Finished installling curl and jq"
|
||||
else
|
||||
exit 1
|
||||
fi
|
||||
CURR_VER=$(cat package.json | jq -r .version)
|
||||
echo "Checking https://git.nfp.is/api/v1/repos/$APPVEYOR_REPO_NAME/releases for version v${CURR_VER}"
|
||||
curl -s -X GET -H "Authorization: token $deploytoken" https://git.nfp.is/api/v1/repos/$APPVEYOR_REPO_NAME/releases | grep -o "\"name\"\:\"v${CURR_VER}\"" > /dev/null
|
||||
|
||||
if [ $? -eq 0 ] ; then
|
||||
echo "Release already exists, nothing to do.";
|
||||
else
|
||||
echo "Creating release on gitea"
|
||||
RELEASE_RESULT=$(curl \
|
||||
-X POST \
|
||||
-H "Authorization: token $deploytoken" \
|
||||
-H "Content-Type: application/json" \
|
||||
https://git.nfp.is/api/v1/repos/$APPVEYOR_REPO_NAME/releases \
|
||||
-d "{\"tag_name\":\"v${CURR_VER}\",\"name\":\"v${CURR_VER}\",\"body\":\"Automatic release from Appveyor from ${APPVEYOR_REPO_COMMIT} :\n\n${APPVEYOR_REPO_COMMIT_MESSAGE}\"}")
|
||||
RELEASE_ID=$(echo $RELEASE_RESULT | jq -r .id)
|
||||
echo '//registry.npmjs.org/:_authToken=${npmtoken}' > ~/.npmrc
|
||||
echo "Publishing new version to npm"
|
||||
npm publish
|
||||
fi
|
||||
# - sh: |
|
||||
# YELLOW='\033[0;33m'
|
||||
# NC='\033[0m'
|
||||
# LOCK_FILE="${HOME}/build.lock"
|
||||
# export APPVEYOR_SSH_BLOCK=true
|
||||
# touch "${LOCK_FILE}"
|
||||
# echo -e "Build paused. To resume it, open a bash session to run '${YELLOW}rm \"${LOCK_FILE}\"${NC}' command."
|
||||
# while [ -f "${LOCK_FILE}" ]; do
|
||||
# sleep 1
|
||||
# done
|
||||
|
||||
# on build failure
|
||||
on_failure:
|
||||
- sh: echo on_failure
|
1665
bin/bunyan
1665
bin/bunyan
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -102,13 +102,6 @@ Print version of this command and exit\.
|
|||
Don\'t warn if input isn\'t valid JSON\.
|
||||
.
|
||||
.P
|
||||
Dtrace options (only on dtrace\-supporting platforms):
|
||||
.
|
||||
.TP
|
||||
\fB\-p PID\fR, \fB\-p NAME\fR
|
||||
Process bunyan:log\-* probes from the process with the given PID\. Can be used multiple times, or specify all processes with \'*\', or a set of processes whose command & args match a pattern with \'\-p NAME\'\.
|
||||
.
|
||||
.P
|
||||
Filtering options:
|
||||
.
|
||||
.TP
|
||||
|
@ -142,10 +135,6 @@ Specify an output format\. One of \fBlong\fR (the default), \fBshort\fR, \fBjson
|
|||
\fB\-j\fR
|
||||
Shortcut for \fB\-o json\fR\.
|
||||
.
|
||||
.TP
|
||||
\fB\-L\fR, \fB\-\-time local\fR
|
||||
Display the time field in \fIlocal\fR time, rather than the default UTC time\.
|
||||
.
|
||||
.SH "LOG LEVELS"
|
||||
In Bunyan log records, then \fBlevel\fR field is a number\. For the \fB\-l|\-\-level\fR argument the level \fBnames\fR are supported as shortcuts\. In \fB\-c|\-\-condition\fR scripts, uppercase symbols like "DEBUG" are defined for convenience\.
|
||||
.
|
||||
|
|
|
@ -89,8 +89,6 @@
|
|||
|
||||
<p>... | <code>bunyan</code> [OPTIONS]</p>
|
||||
|
||||
<p><code>bunyan</code> [OPTIONS] -p PID</p>
|
||||
|
||||
<h2 id="DESCRIPTION">DESCRIPTION</h2>
|
||||
|
||||
<p>"Bunyan" is <strong>a simple and fast a JSON logging library</strong> for node.js services,
|
||||
|
@ -152,16 +150,6 @@ record data. In the COND code, <code>this</code> refers to the record object:</p
|
|||
</dl>
|
||||
|
||||
|
||||
<p>Dtrace options (only on dtrace-supporting platforms):</p>
|
||||
|
||||
<dl>
|
||||
<dt><code>-p PID</code>, <code>-p NAME</code></dt><dd>Process bunyan:log-* probes from the process with the given PID.
|
||||
Can be used multiple times, or specify all processes with '*',
|
||||
or a set of processes whose command & args match a pattern with
|
||||
'-p NAME'.</dd>
|
||||
</dl>
|
||||
|
||||
|
||||
<p>Filtering options:</p>
|
||||
|
||||
<dl>
|
||||
|
@ -182,8 +170,6 @@ non-Bunyan lines are passed through.</p></dd>
|
|||
<dt><code>-o FORMAT</code>, <code>--output FORMAT</code></dt><dd><p>Specify an output format. One of <code>long</code> (the default), <code>short</code>, <code>json</code>,
|
||||
<code>json-N</code>, <code>bunyan</code> (the native bunyan 0-indent JSON output) or <code>inspect</code>.</p></dd>
|
||||
<dt class="flush"><code>-j</code></dt><dd><p>Shortcut for <code>-o json</code>.</p></dd>
|
||||
<dt><code>-L</code>, <code>--time local</code></dt><dd><p>Display the time field in <em>local</em> time, rather than the default UTC
|
||||
time.</p></dd>
|
||||
</dl>
|
||||
|
||||
|
||||
|
@ -218,34 +204,6 @@ bunyan Alias for "json-0", the Bunyan "native" format.
|
|||
inspect Node.js `util.inspect` output.
|
||||
</code></pre>
|
||||
|
||||
<h2 id="DTRACE-SUPPORT">DTRACE SUPPORT</h2>
|
||||
|
||||
<p>On systems that support DTrace (e.g., MacOS, FreeBSD, illumos derivatives
|
||||
like SmartOS and OmniOS), Bunyan will create a DTrace provider (<code>bunyan</code>)
|
||||
that makes available the following probes:</p>
|
||||
|
||||
<pre><code>log-trace
|
||||
log-debug
|
||||
log-info
|
||||
log-warn
|
||||
log-error
|
||||
log-fatal
|
||||
</code></pre>
|
||||
|
||||
<p>Each of these probes has a single argument: the string that would be
|
||||
written to the log. Note that when a probe is enabled, it will
|
||||
fire whenever the corresponding function is called, even if the level of
|
||||
the log message is less than that of any stream.</p>
|
||||
|
||||
<p>See <a href="https://github.com/trentm/node-bunyan#dtrace-support" data-bare-link="true">https://github.com/trentm/node-bunyan#dtrace-support</a> for more details
|
||||
and the '-p PID' option above for convenience usage.</p>
|
||||
|
||||
<h2 id="ENVIRONMENT">ENVIRONMENT</h2>
|
||||
|
||||
<dl>
|
||||
<dt><code>BUNYAN_NO_COLOR</code></dt><dd>Set to a non-empty value to force no output coloring. See '--no-color'.</dd>
|
||||
</dl>
|
||||
|
||||
|
||||
<h2 id="PROJECT-BUGS">PROJECT & BUGS</h2>
|
||||
|
||||
|
|
|
@ -7,8 +7,6 @@
|
|||
|
||||
... | `bunyan` \[OPTIONS\]
|
||||
|
||||
`bunyan` \[OPTIONS\] -p PID
|
||||
|
||||
|
||||
## DESCRIPTION
|
||||
|
||||
|
@ -72,14 +70,6 @@ record data. In the COND code, `this` refers to the record object:
|
|||
* `-q`, `--quiet`:
|
||||
Don't warn if input isn't valid JSON.
|
||||
|
||||
Dtrace options (only on dtrace-supporting platforms):
|
||||
|
||||
* `-p PID`, `-p NAME`:
|
||||
Process bunyan:log-\* probes from the process with the given PID.
|
||||
Can be used multiple times, or specify all processes with '\*',
|
||||
or a set of processes whose command & args match a pattern with
|
||||
'-p NAME'.
|
||||
|
||||
Filtering options:
|
||||
|
||||
* `-l`, `--level LEVEL`:
|
||||
|
@ -109,10 +99,6 @@ Output options:
|
|||
* `-j`:
|
||||
Shortcut for `-o json`.
|
||||
|
||||
* `-L`, `--time local`:
|
||||
Display the time field in *local* time, rather than the default UTC
|
||||
time.
|
||||
|
||||
|
||||
## LOG LEVELS
|
||||
|
||||
|
@ -145,28 +131,6 @@ scripts, uppercase symbols like "DEBUG" are defined for convenience.
|
|||
inspect Node.js `util.inspect` output.
|
||||
|
||||
|
||||
## DTRACE SUPPORT
|
||||
|
||||
On systems that support DTrace (e.g., MacOS, FreeBSD, illumos derivatives
|
||||
like SmartOS and OmniOS), Bunyan will create a DTrace provider (`bunyan`)
|
||||
that makes available the following probes:
|
||||
|
||||
log-trace
|
||||
log-debug
|
||||
log-info
|
||||
log-warn
|
||||
log-error
|
||||
log-fatal
|
||||
|
||||
Each of these probes has a single argument: the string that would be
|
||||
written to the log. Note that when a probe is enabled, it will
|
||||
fire whenever the corresponding function is called, even if the level of
|
||||
the log message is less than that of any stream.
|
||||
|
||||
See <https://github.com/trentm/node-bunyan#dtrace-support> for more details
|
||||
and the '-p PID' option above for convenience usage.
|
||||
|
||||
|
||||
## ENVIRONMENT
|
||||
|
||||
* `BUNYAN_NO_COLOR`:
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
#!/usr/bin/env node
|
||||
/* BEGIN JSSTYLED */
|
||||
/**
|
||||
* <https://github.com/trentm/node-bunyan/pull/473> is a change to add a
|
||||
* feature to Bunyan's log record stringification to log `undefined` values.
|
||||
* Let's attempt that with a custom raw stream.
|
||||
*
|
||||
* Note that a raw stream here isn't ideal, because using a custom raw stream
|
||||
* means that it is a pain to use some of the other built-in stream types
|
||||
* (file, rotating-file). However, it might be a satisfactory workaround for
|
||||
* some.
|
||||
*
|
||||
* Example:
|
||||
* $ node log-undefined-values.js
|
||||
* {"name":"log-undefined-values","hostname":"danger0.local","pid":28161,"level":30,"anull":null,"aundef":"[Undefined]","anum":42,"astr":"foo","msg":"hi","time":"2017-03-04T20:53:54.331Z","v":0}
|
||||
* $ node log-undefined-values.js | bunyan
|
||||
* [2017-03-04T20:54:41.874Z] INFO: log-undefined-values/28194 on danger0.local: hi (anull=null, aundef=[Undefined], anum=42, astr=foo)
|
||||
*/
|
||||
/* END JSSTYLED */
|
||||
|
||||
var bunyan = require('../lib/bunyan');
|
||||
var fs = require('fs');
|
||||
|
||||
|
||||
function replacer() {
|
||||
// Note: If node > 0.10, then could use Set here (see `safeCyclesSet()`
|
||||
// in bunyan.js) for a performance improvement.
|
||||
var seen = [];
|
||||
return function (key, val) {
|
||||
if (val === undefined) {
|
||||
return '[Undefined]';
|
||||
} else if (!val || typeof (val) !== 'object') {
|
||||
return val;
|
||||
}
|
||||
if (seen.indexOf(val) !== -1) {
|
||||
return '[Circular]';
|
||||
}
|
||||
seen.push(val);
|
||||
return val;
|
||||
};
|
||||
}
|
||||
|
||||
function LogUndefinedValuesStream(stream) {
|
||||
this.stream = stream;
|
||||
}
|
||||
LogUndefinedValuesStream.prototype.write = function (rec) {
|
||||
var str = JSON.stringify(rec, replacer()) + '\n';
|
||||
this.stream.write(str);
|
||||
}
|
||||
|
||||
var log = bunyan.createLogger({
|
||||
name: 'log-undefined-values',
|
||||
streams: [ {
|
||||
level: 'info',
|
||||
type: 'raw',
|
||||
stream: new LogUndefinedValuesStream(process.stdout)
|
||||
} ]
|
||||
});
|
||||
|
||||
log.info({anull: null, aundef: undefined, anum: 42, astr: 'foo'}, 'hi');
|
1627
lib/bunyan.js
1627
lib/bunyan.js
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,69 @@
|
|||
var hasProp = Object.prototype.hasOwnProperty
|
||||
|
||||
function throwsMessage(err) {
|
||||
return '[Throws: ' + (err ? err.message : '?') + ']'
|
||||
}
|
||||
|
||||
function safeGetValueFromPropertyOnObject(obj, property) {
|
||||
if (hasProp.call(obj, property)) {
|
||||
try {
|
||||
return obj[property]
|
||||
}
|
||||
catch (err) {
|
||||
return throwsMessage(err)
|
||||
}
|
||||
}
|
||||
|
||||
return obj[property]
|
||||
}
|
||||
|
||||
function ensureProperties(obj) {
|
||||
var seen = new WeakMap()
|
||||
|
||||
function visit(obj) {
|
||||
if (obj === null || typeof obj !== 'object') {
|
||||
return obj
|
||||
}
|
||||
|
||||
if (seen.has(obj)) {
|
||||
return '[Circular]'
|
||||
}
|
||||
|
||||
seen.set(obj, true)
|
||||
|
||||
if (typeof obj.toJSON === 'function') {
|
||||
try {
|
||||
var fResult = visit(obj.toJSON())
|
||||
seen.delete(obj)
|
||||
return fResult
|
||||
} catch(err) {
|
||||
seen.delete(obj)
|
||||
return throwsMessage(err)
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(obj)) {
|
||||
var aResult = obj.map(visit)
|
||||
seen.delete(obj)
|
||||
return aResult
|
||||
}
|
||||
|
||||
var result = Object.keys(obj).reduce(function(result, prop) {
|
||||
// prevent faulty defined getter properties
|
||||
result[prop] = visit(safeGetValueFromPropertyOnObject(obj, prop))
|
||||
return result
|
||||
}, {})
|
||||
seen.delete(obj)
|
||||
return result
|
||||
}
|
||||
|
||||
return visit(obj)
|
||||
}
|
||||
|
||||
function safeJson(data, replacer, space) {
|
||||
return JSON.stringify(ensureProperties(data), replacer, space)
|
||||
}
|
||||
|
||||
safeJson.ensureProperties = ensureProperties
|
||||
|
||||
export default safeJson
|
52
package.json
52
package.json
|
@ -1,41 +1,35 @@
|
|||
{
|
||||
"name": "bunyan",
|
||||
"version": "1.8.8",
|
||||
"description": "a JSON logging library for node.js services",
|
||||
"author": "Trent Mick <trentm@gmail.com> (http://trentm.com)",
|
||||
"main": "./lib/bunyan.js",
|
||||
"name": "bunyan-lite",
|
||||
"version": "1.2.1",
|
||||
"description": "a lite version of bunyan, a JSON logging library for node.js services with zero dependency",
|
||||
"author": "Jonatan Nilsson <jonatan@nilsson.is> (https://nfp.is)",
|
||||
"main": "./lib/bunyan.mjs",
|
||||
"bin": {
|
||||
"bunyan": "./bin/bunyan"
|
||||
"bunyan": "./bin/bunyan.mjs"
|
||||
},
|
||||
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/trentm/node-bunyan.git"
|
||||
"url": "git://github.com/nfp-projects/node-bunyan-lite.git"
|
||||
},
|
||||
"engines": ["node >=0.10.0"],
|
||||
"keywords": ["log", "logging", "log4j", "json", "bunyan"],
|
||||
"keywords": [
|
||||
"log",
|
||||
"logging",
|
||||
"log4j",
|
||||
"json",
|
||||
"bunyan"
|
||||
],
|
||||
"license": "MIT",
|
||||
|
||||
"// dtrace-provider": "required for dtrace features",
|
||||
"// mv": "required for RotatingFileStream",
|
||||
"// moment": "required for local time with CLI",
|
||||
"optionalDependencies": {
|
||||
"dtrace-provider": "~0.8",
|
||||
"mv": "~2",
|
||||
"safe-json-stringify": "~1",
|
||||
"moment": "^2.10.6"
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"nodeunit": "0.9",
|
||||
"ben": "0.0.0",
|
||||
"markdown-toc": "0.12.x",
|
||||
"verror": "1.3.3",
|
||||
"vasync": "1.4.3"
|
||||
"eltro": "^1.0.2"
|
||||
},
|
||||
|
||||
"scripts": {
|
||||
"test": "make test"
|
||||
"test": "eltro test/**/*.test.mjs -r dot"
|
||||
},
|
||||
"dependencies": {
|
||||
}
|
||||
"files": [
|
||||
"LICENSE.txt",
|
||||
"README.md",
|
||||
"bin",
|
||||
"lib"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2016 Trent Mick. All rights reserved.
|
||||
*
|
||||
* Test stream adding.
|
||||
*/
|
||||
|
||||
var bunyan = require('../lib/bunyan');
|
||||
|
||||
// node-tap API
|
||||
if (require.cache[__dirname + '/tap4nodeunit.js'])
|
||||
delete require.cache[__dirname + '/tap4nodeunit.js'];
|
||||
var tap4nodeunit = require('./tap4nodeunit.js');
|
||||
var test = tap4nodeunit.test;
|
||||
|
||||
|
||||
test('non-writables passed as stream', function (t) {
|
||||
var things = ['process.stdout', {}];
|
||||
things.forEach(function (thing) {
|
||||
function createLogger() {
|
||||
bunyan.createLogger({
|
||||
name: 'foo',
|
||||
stream: thing
|
||||
});
|
||||
}
|
||||
t.throws(createLogger,
|
||||
/stream is not writable/,
|
||||
'"stream" stream is not writable');
|
||||
})
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('proper stream', function (t) {
|
||||
var log = bunyan.createLogger({
|
||||
name: 'foo',
|
||||
stream: process.stdout
|
||||
});
|
||||
t.ok('should not throw');
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Test stream adding.
|
||||
*/
|
||||
import { Eltro as t, assert} from 'eltro'
|
||||
import bunyan from '../lib/bunyan.mjs'
|
||||
|
||||
t.test('non-writables passed as stream', function () {
|
||||
var things = ['process.stdout', {}];
|
||||
things.forEach(function (thing) {
|
||||
function createLogger() {
|
||||
bunyan.createLogger({
|
||||
name: 'foo',
|
||||
stream: thing
|
||||
});
|
||||
}
|
||||
assert.throws(createLogger,
|
||||
/stream is not writable/);
|
||||
})
|
||||
});
|
||||
|
||||
t.test('proper stream', function () {
|
||||
var log = bunyan.createLogger({
|
||||
name: 'foo',
|
||||
stream: process.stdout
|
||||
});
|
||||
});
|
|
@ -1,81 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2012 Trent Mick. All rights reserved.
|
||||
* Copyright (c) 2012 Joyent Inc. All rights reserved.
|
||||
*
|
||||
* Test logging with (accidental) usage of buffers.
|
||||
*/
|
||||
|
||||
var util = require('util'),
|
||||
inspect = util.inspect,
|
||||
format = util.format;
|
||||
var bunyan = require('../lib/bunyan');
|
||||
|
||||
// node-tap API
|
||||
if (require.cache[__dirname + '/tap4nodeunit.js'])
|
||||
delete require.cache[__dirname + '/tap4nodeunit.js'];
|
||||
var tap4nodeunit = require('./tap4nodeunit.js');
|
||||
var after = tap4nodeunit.after;
|
||||
var before = tap4nodeunit.before;
|
||||
var test = tap4nodeunit.test;
|
||||
|
||||
|
||||
|
||||
function Catcher() {
|
||||
this.records = [];
|
||||
}
|
||||
Catcher.prototype.write = function (record) {
|
||||
this.records.push(record);
|
||||
}
|
||||
|
||||
var catcher = new Catcher();
|
||||
var log = new bunyan.createLogger({
|
||||
name: 'buffer.test',
|
||||
streams: [
|
||||
{
|
||||
type: 'raw',
|
||||
stream: catcher,
|
||||
level: 'trace'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
|
||||
test('log.info(BUFFER)', function (t) {
|
||||
var b = new Buffer('foo');
|
||||
|
||||
['trace',
|
||||
'debug',
|
||||
'info',
|
||||
'warn',
|
||||
'error',
|
||||
'fatal'].forEach(function (lvl) {
|
||||
log[lvl].call(log, b);
|
||||
var rec = catcher.records[catcher.records.length - 1];
|
||||
t.equal(rec.msg, inspect(b),
|
||||
format('log.%s msg is inspect(BUFFER)', lvl));
|
||||
t.ok(rec['0'] === undefined,
|
||||
'no "0" array index key in record: ' + inspect(rec['0']));
|
||||
t.ok(rec['parent'] === undefined,
|
||||
'no "parent" array index key in record: ' + inspect(rec['parent']));
|
||||
|
||||
log[lvl].call(log, b, 'bar');
|
||||
var rec = catcher.records[catcher.records.length - 1];
|
||||
t.equal(rec.msg, inspect(b) + ' bar', format(
|
||||
'log.%s(BUFFER, "bar") msg is inspect(BUFFER) + " bar"', lvl));
|
||||
});
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
//test('log.info({buf: BUFFER})', function (t) {
|
||||
// var b = new Buffer('foo');
|
||||
//
|
||||
// // Really there isn't much Bunyan can do here. See
|
||||
// // <https://github.com/joyent/node/issues/3905>. An unwelcome hack would
|
||||
// // be to monkey-patch in Buffer.toJSON. Bletch.
|
||||
// log.info({buf: b}, 'my message');
|
||||
// var rec = catcher.records[catcher.records.length - 1];
|
||||
//
|
||||
// t.end();
|
||||
//});
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Test logging with (accidental) usage of buffers.
|
||||
*/
|
||||
|
||||
import util from 'util'
|
||||
import { Eltro as t, assert} from 'eltro'
|
||||
import bunyan from '../lib/bunyan.mjs'
|
||||
|
||||
const inspect = util.inspect
|
||||
const format = util.format
|
||||
|
||||
|
||||
function Catcher() {
|
||||
this.records = []
|
||||
}
|
||||
Catcher.prototype.write = function (record) {
|
||||
this.records.push(record)
|
||||
}
|
||||
|
||||
let catcher = new Catcher()
|
||||
let log = new bunyan.createLogger({
|
||||
name: 'buffer.test',
|
||||
streams: [
|
||||
{
|
||||
type: 'raw',
|
||||
stream: catcher,
|
||||
level: 'trace'
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
|
||||
t.test('log.info(BUFFER)', function () {
|
||||
let b = Buffer.from('foo')
|
||||
|
||||
let testLevels = ['trace',
|
||||
'debug',
|
||||
'info',
|
||||
'warn',
|
||||
'error',
|
||||
'fatal']
|
||||
|
||||
testLevels.forEach(function (lvl) {
|
||||
log[lvl].call(log, b)
|
||||
let rec = catcher.records[catcher.records.length - 1]
|
||||
assert.strictEqual(rec.msg, inspect(b),
|
||||
format('log.%s msg is inspect(BUFFER)', lvl))
|
||||
assert.ok(rec['0'] === undefined,
|
||||
'no "0" array index key in record: ' + inspect(rec['0']))
|
||||
assert.ok(rec['parent'] === undefined,
|
||||
'no "parent" array index key in record: ' + inspect(rec['parent']))
|
||||
|
||||
log[lvl].call(log, b, 'bar')
|
||||
rec = catcher.records[catcher.records.length - 1]
|
||||
assert.strictEqual(rec.msg, inspect(b) + ' bar', format(
|
||||
'log.%s(BUFFER, "bar") msg is inspect(BUFFER) + " bar"', lvl))
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
//test('log.info({buf: BUFFER})', function (t) {
|
||||
// let b = new Buffer('foo')
|
||||
//
|
||||
// // Really there isn't much Bunyan can do here. See
|
||||
// // <https://github.com/joyent/node/issues/3905>. An unwelcome hack would
|
||||
// // be to monkey-patch in Buffer.toJSON. Bletch.
|
||||
// log.info({buf: b}, 'my message')
|
||||
// let rec = catcher.records[catcher.records.length - 1]
|
||||
//
|
||||
// t.end()
|
||||
//})
|
|
@ -1,161 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2012 Trent Mick. All rights reserved.
|
||||
*
|
||||
* Test some `<Logger>.child(...)` behaviour.
|
||||
*/
|
||||
|
||||
var bunyan = require('../lib/bunyan');
|
||||
|
||||
// node-tap API
|
||||
if (require.cache[__dirname + '/tap4nodeunit.js'])
|
||||
delete require.cache[__dirname + '/tap4nodeunit.js'];
|
||||
var tap4nodeunit = require('./tap4nodeunit.js');
|
||||
var after = tap4nodeunit.after;
|
||||
var before = tap4nodeunit.before;
|
||||
var test = tap4nodeunit.test;
|
||||
|
||||
|
||||
|
||||
function CapturingStream(recs) {
|
||||
this.recs = recs || [];
|
||||
}
|
||||
CapturingStream.prototype.write = function (rec) {
|
||||
this.recs.push(rec);
|
||||
}
|
||||
|
||||
|
||||
|
||||
test('child can add stream', function (t) {
|
||||
var dadStream = new CapturingStream();
|
||||
var dad = bunyan.createLogger({
|
||||
name: 'surname',
|
||||
streams: [ {
|
||||
type: 'raw',
|
||||
stream: dadStream,
|
||||
level: 'info'
|
||||
} ]
|
||||
});
|
||||
|
||||
var sonStream = new CapturingStream();
|
||||
var son = dad.child({
|
||||
component: 'son',
|
||||
streams: [ {
|
||||
type: 'raw',
|
||||
stream: sonStream,
|
||||
level: 'debug'
|
||||
} ]
|
||||
});
|
||||
|
||||
dad.info('info from dad');
|
||||
dad.debug('debug from dad');
|
||||
son.debug('debug from son');
|
||||
|
||||
var rec;
|
||||
t.equal(dadStream.recs.length, 1);
|
||||
rec = dadStream.recs[0];
|
||||
t.equal(rec.msg, 'info from dad');
|
||||
t.equal(sonStream.recs.length, 1);
|
||||
rec = sonStream.recs[0];
|
||||
t.equal(rec.msg, 'debug from son');
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('child can set level of inherited streams', function (t) {
|
||||
var dadStream = new CapturingStream();
|
||||
var dad = bunyan.createLogger({
|
||||
name: 'surname',
|
||||
streams: [ {
|
||||
type: 'raw',
|
||||
stream: dadStream,
|
||||
level: 'info'
|
||||
} ]
|
||||
});
|
||||
|
||||
// Intention here is that the inherited `dadStream` logs at 'debug' level
|
||||
// for the son.
|
||||
var son = dad.child({
|
||||
component: 'son',
|
||||
level: 'debug'
|
||||
});
|
||||
|
||||
dad.info('info from dad');
|
||||
dad.debug('debug from dad');
|
||||
son.debug('debug from son');
|
||||
|
||||
var rec;
|
||||
t.equal(dadStream.recs.length, 2);
|
||||
rec = dadStream.recs[0];
|
||||
t.equal(rec.msg, 'info from dad');
|
||||
rec = dadStream.recs[1];
|
||||
t.equal(rec.msg, 'debug from son');
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('child can set level of inherited streams and add streams', function (t) {
|
||||
var dadStream = new CapturingStream();
|
||||
var dad = bunyan.createLogger({
|
||||
name: 'surname',
|
||||
streams: [ {
|
||||
type: 'raw',
|
||||
stream: dadStream,
|
||||
level: 'info'
|
||||
} ]
|
||||
});
|
||||
|
||||
// Intention here is that the inherited `dadStream` logs at 'debug' level
|
||||
// for the son.
|
||||
var sonStream = new CapturingStream();
|
||||
var son = dad.child({
|
||||
component: 'son',
|
||||
level: 'trace',
|
||||
streams: [ {
|
||||
type: 'raw',
|
||||
stream: sonStream,
|
||||
level: 'debug'
|
||||
} ]
|
||||
});
|
||||
|
||||
dad.info('info from dad');
|
||||
dad.trace('trace from dad');
|
||||
son.trace('trace from son');
|
||||
son.debug('debug from son');
|
||||
|
||||
t.equal(dadStream.recs.length, 3);
|
||||
t.equal(dadStream.recs[0].msg, 'info from dad');
|
||||
t.equal(dadStream.recs[1].msg, 'trace from son');
|
||||
t.equal(dadStream.recs[2].msg, 'debug from son');
|
||||
|
||||
t.equal(sonStream.recs.length, 1);
|
||||
t.equal(sonStream.recs[0].msg, 'debug from son');
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
// issue #291
|
||||
test('child should not lose parent "hostname"', function (t) {
|
||||
var stream = new CapturingStream();
|
||||
var dad = bunyan.createLogger({
|
||||
name: 'hostname-test',
|
||||
hostname: 'bar0',
|
||||
streams: [ {
|
||||
type: 'raw',
|
||||
stream: stream,
|
||||
level: 'info'
|
||||
} ]
|
||||
});
|
||||
var son = dad.child({component: 'son'});
|
||||
|
||||
dad.info('HI');
|
||||
son.info('hi');
|
||||
|
||||
t.equal(stream.recs.length, 2);
|
||||
t.equal(stream.recs[0].hostname, 'bar0');
|
||||
t.equal(stream.recs[1].hostname, 'bar0');
|
||||
t.equal(stream.recs[1].component, 'son');
|
||||
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
* Test some `<Logger>.child(...)` behaviour.
|
||||
*/
|
||||
|
||||
import { Eltro as t, assert} from 'eltro'
|
||||
import bunyan from '../lib/bunyan.mjs'
|
||||
|
||||
|
||||
function CapturingStream(recs) {
|
||||
this.recs = recs || [];
|
||||
}
|
||||
CapturingStream.prototype.write = function (rec) {
|
||||
this.recs.push(rec);
|
||||
}
|
||||
|
||||
|
||||
t.test('child can add stream', function () {
|
||||
let dadStream = new CapturingStream();
|
||||
let dad = bunyan.createLogger({
|
||||
name: 'surname',
|
||||
streams: [ {
|
||||
type: 'raw',
|
||||
stream: dadStream,
|
||||
level: 'info'
|
||||
} ]
|
||||
});
|
||||
|
||||
let sonStream = new CapturingStream();
|
||||
let son = dad.child({
|
||||
component: 'son',
|
||||
streams: [ {
|
||||
type: 'raw',
|
||||
stream: sonStream,
|
||||
level: 'debug'
|
||||
} ]
|
||||
});
|
||||
|
||||
dad.info('info from dad');
|
||||
dad.debug('debug from dad');
|
||||
son.debug('debug from son');
|
||||
|
||||
let rec;
|
||||
assert.equal(dadStream.recs.length, 1);
|
||||
rec = dadStream.recs[0];
|
||||
assert.equal(rec.msg, 'info from dad');
|
||||
assert.equal(sonStream.recs.length, 1);
|
||||
rec = sonStream.recs[0];
|
||||
assert.equal(rec.msg, 'debug from son');
|
||||
|
||||
});
|
||||
|
||||
|
||||
t.test('child can set level of inherited streams', function () {
|
||||
let dadStream = new CapturingStream();
|
||||
let dad = bunyan.createLogger({
|
||||
name: 'surname',
|
||||
streams: [ {
|
||||
type: 'raw',
|
||||
stream: dadStream,
|
||||
level: 'info'
|
||||
} ]
|
||||
});
|
||||
|
||||
// Intention here is that the inherited `dadStream` logs at 'debug' level
|
||||
// for the son.
|
||||
let son = dad.child({
|
||||
component: 'son',
|
||||
level: 'debug'
|
||||
});
|
||||
|
||||
dad.info('info from dad');
|
||||
dad.debug('debug from dad');
|
||||
son.debug('debug from son');
|
||||
|
||||
let rec;
|
||||
assert.equal(dadStream.recs.length, 2);
|
||||
rec = dadStream.recs[0];
|
||||
assert.equal(rec.msg, 'info from dad');
|
||||
rec = dadStream.recs[1];
|
||||
assert.equal(rec.msg, 'debug from son');
|
||||
|
||||
});
|
||||
|
||||
|
||||
t.test('child can set level of inherited streams and add streams', function () {
|
||||
let dadStream = new CapturingStream();
|
||||
let dad = bunyan.createLogger({
|
||||
name: 'surname',
|
||||
streams: [ {
|
||||
type: 'raw',
|
||||
stream: dadStream,
|
||||
level: 'info'
|
||||
} ]
|
||||
});
|
||||
|
||||
// Intention here is that the inherited `dadStream` logs at 'debug' level
|
||||
// for the son.
|
||||
let sonStream = new CapturingStream();
|
||||
let son = dad.child({
|
||||
component: 'son',
|
||||
level: 'trace',
|
||||
streams: [ {
|
||||
type: 'raw',
|
||||
stream: sonStream,
|
||||
level: 'debug'
|
||||
} ]
|
||||
});
|
||||
|
||||
dad.info('info from dad');
|
||||
dad.trace('trace from dad');
|
||||
son.trace('trace from son');
|
||||
son.debug('debug from son');
|
||||
|
||||
assert.equal(dadStream.recs.length, 3);
|
||||
assert.equal(dadStream.recs[0].msg, 'info from dad');
|
||||
assert.equal(dadStream.recs[1].msg, 'trace from son');
|
||||
assert.equal(dadStream.recs[2].msg, 'debug from son');
|
||||
|
||||
assert.equal(sonStream.recs.length, 1);
|
||||
assert.equal(sonStream.recs[0].msg, 'debug from son');
|
||||
|
||||
});
|
||||
|
||||
// issue #291
|
||||
t.test('child should not lose parent "hostname"', function () {
|
||||
let stream = new CapturingStream();
|
||||
let dad = bunyan.createLogger({
|
||||
name: 'hostname-test',
|
||||
hostname: 'bar0',
|
||||
streams: [ {
|
||||
type: 'raw',
|
||||
stream: stream,
|
||||
level: 'info'
|
||||
} ]
|
||||
});
|
||||
let son = dad.child({component: 'son'});
|
||||
|
||||
dad.info('HI');
|
||||
son.info('hi');
|
||||
|
||||
assert.equal(stream.recs.length, 2);
|
||||
assert.equal(stream.recs[0].hostname, 'bar0');
|
||||
assert.equal(stream.recs[1].hostname, 'bar0');
|
||||
assert.equal(stream.recs[1].component, 'son');
|
||||
|
||||
});
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Test the bunyan CLI's handling of the "client_req" field.
|
||||
* "client_req" is a common-ish Bunyan log field from restify-clients. See:
|
||||
* // JSSTYLED
|
||||
* https://github.com/restify/clients/blob/85374f87db9f4469de2605b6b15632b317cc12be/lib/helpers/bunyan.js#L213
|
||||
*/
|
||||
|
||||
import { exec, dirname } from './helper.mjs'
|
||||
import { Eltro as t, assert} from 'eltro'
|
||||
|
||||
// ---- tests
|
||||
|
||||
t.test('client_req extra newlines, client_res={} (pull #252)', async function () {
|
||||
const expected = [
|
||||
/* BEGIN JSSTYLED */
|
||||
'[2016-02-10T07:28:40.510Z] TRACE: aclientreq/23280 on danger0.local: request sent',
|
||||
' GET /--ping HTTP/1.1',
|
||||
'[2016-02-10T07:28:41.419Z] TRACE: aclientreq/23280 on danger0.local: Response received',
|
||||
' HTTP/1.1 200 OK',
|
||||
' request-id: e8a5a700-cfc7-11e5-a3dc-3b85d20f26ef',
|
||||
' content-type: application/json'
|
||||
/* END JSSTYLED */
|
||||
].join('\n') + '\n';
|
||||
|
||||
let res = await exec(dirname('/corpus/clientreqres.log'))
|
||||
assert.strictEqual(res.stdout, expected)
|
||||
});
|
||||
|
||||
|
||||
t.test('client_req.address is not used for Host header in 2.x (issue #504)', async function () {
|
||||
const expected = [
|
||||
// JSSTYLED
|
||||
'[2017-05-12T23:59:15.877Z] TRACE: minfo/66266 on sharptooth.local: request sent (client_req.address=127.0.0.1)',
|
||||
' HEAD /dap/stor HTTP/1.1',
|
||||
' accept: application/json, */*',
|
||||
' host: foo.example.com',
|
||||
' date: Fri, 12 May 2017 23:59:15 GMT',
|
||||
''
|
||||
].join('\n')
|
||||
|
||||
let res = await exec(dirname('/corpus/client-req-with-address.log'))
|
||||
assert.strictEqual(res.stdout, expected)
|
||||
});
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Test the bunyan CLI's handling of the "res" field.
|
||||
*/
|
||||
|
||||
import { exec, dirname } from './helper.mjs'
|
||||
import { Eltro as t, assert} from 'eltro'
|
||||
|
||||
// ---- tests
|
||||
|
||||
t.test('res with "header" string (issue #444)', async function () {
|
||||
const expected = [
|
||||
/* BEGIN JSSTYLED */
|
||||
'[2017-08-02T22:37:34.798Z] INFO: res-header/76488 on danger0.local: response sent',
|
||||
' HTTP/1.1 200 OK',
|
||||
' Foo: bar',
|
||||
' Date: Wed, 02 Aug 2017 22:37:34 GMT',
|
||||
' Connection: keep-alive',
|
||||
' Content-Length: 21'
|
||||
/* END JSSTYLED */
|
||||
].join('\n') + '\n';
|
||||
|
||||
let res = await exec(dirname('/corpus/res-header.log'))
|
||||
assert.strictEqual(res.stdout, expected)
|
||||
});
|
||||
|
||||
t.test('res without "header"', async function () {
|
||||
const expected = [
|
||||
/* BEGIN JSSTYLED */
|
||||
'[2017-08-02T22:37:34.798Z] INFO: res-header/76488 on danger0.local: response sent',
|
||||
' HTTP/1.1 200 OK'
|
||||
/* END JSSTYLED */
|
||||
].join('\n') + '\n';
|
||||
|
||||
let res = await exec(dirname('/corpus/res-without-header.log'))
|
||||
assert.strictEqual(res.stdout, expected)
|
||||
});
|
532
test/cli.test.js
532
test/cli.test.js
|
@ -1,532 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2015 Trent Mick. All rights reserved.
|
||||
*
|
||||
* Test the `bunyan` CLI.
|
||||
*/
|
||||
|
||||
var p = console.warn;
|
||||
var exec = require('child_process').exec;
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var _ = require('util').format;
|
||||
var vasync = require('vasync');
|
||||
|
||||
// node-tap API
|
||||
if (require.cache[__dirname + '/tap4nodeunit.js'])
|
||||
delete require.cache[__dirname + '/tap4nodeunit.js'];
|
||||
var tap4nodeunit = require('./tap4nodeunit.js');
|
||||
var after = tap4nodeunit.after;
|
||||
var before = tap4nodeunit.before;
|
||||
var test = tap4nodeunit.test;
|
||||
|
||||
|
||||
// ---- globals
|
||||
|
||||
var BUNYAN = path.resolve(__dirname, '../bin/bunyan');
|
||||
|
||||
|
||||
// ---- support stuff
|
||||
|
||||
/**
|
||||
* Copies over all keys in `from` to `to`, or
|
||||
* to a new object if `to` is not given.
|
||||
*/
|
||||
function objCopy(from, to) {
|
||||
if (to === undefined) {
|
||||
to = {};
|
||||
}
|
||||
for (var k in from) {
|
||||
to[k] = from[k];
|
||||
}
|
||||
return to;
|
||||
}
|
||||
|
||||
|
||||
// ---- tests
|
||||
|
||||
test('--version', function (t) {
|
||||
var version = require('../package.json').version;
|
||||
exec(BUNYAN + ' --version', function (err, stdout, stderr) {
|
||||
t.ifError(err)
|
||||
t.equal(stdout, 'bunyan ' + version + '\n');
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
test('--help', function (t) {
|
||||
exec(BUNYAN + ' --help', function (err, stdout, stderr) {
|
||||
t.ifError(err)
|
||||
t.ok(stdout.indexOf('General options:') !== -1);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
test('-h', function (t) {
|
||||
exec(BUNYAN + ' -h', function (err, stdout, stderr) {
|
||||
t.ifError(err)
|
||||
t.ok(stdout.indexOf('General options:') !== -1);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
test('--bogus', function (t) {
|
||||
exec(BUNYAN + ' --bogus', function (err, stdout, stderr) {
|
||||
t.ok(err, 'should error out')
|
||||
t.equal(err.code, 1, '... with exit code 1')
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
test('simple.log', function (t) {
|
||||
exec(_('%s %s/corpus/simple.log', BUNYAN, __dirname),
|
||||
function (err, stdout, stderr) {
|
||||
t.ifError(err)
|
||||
t.equal(stdout,
|
||||
'[2012-02-08T22:56:52.856Z] INFO: myservice/123 on example.com: '
|
||||
+ 'My message\n');
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
test('cat simple.log', function (t) {
|
||||
exec(_('cat %s/corpus/simple.log | %s', __dirname, BUNYAN),
|
||||
function (err, stdout, stderr) {
|
||||
t.ifError(err)
|
||||
t.equal(stdout,
|
||||
/* JSSTYLED */
|
||||
'[2012-02-08T22:56:52.856Z] INFO: myservice/123 on example.com: My message\n');
|
||||
t.end();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// A stable 'TZ' for 'local' timezone output.
|
||||
tzEnv = objCopy(process.env);
|
||||
tzEnv.TZ = 'Pacific/Honolulu';
|
||||
|
||||
test('time: simple.log local long', function (t) {
|
||||
exec(_('%s -o long -L %s/corpus/simple.log', BUNYAN, __dirname),
|
||||
{env: tzEnv}, function (err, stdout, stderr) {
|
||||
t.ifError(err)
|
||||
t.equal(stdout,
|
||||
// JSSTYLED
|
||||
'[2012-02-08T12:56:52.856-10:00] INFO: myservice/123 on example.com: '
|
||||
+ 'My message\n');
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
test('time: simple.log utc long', function (t) {
|
||||
exec(_('%s -o long --time utc %s/corpus/simple.log', BUNYAN, __dirname),
|
||||
{env: tzEnv}, function (err, stdout, stderr) {
|
||||
t.ifError(err)
|
||||
t.equal(stdout,
|
||||
'[2012-02-08T22:56:52.856Z] INFO: myservice/123 on example.com: '
|
||||
+ 'My message\n');
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
test('time: simple.log local short', function (t) {
|
||||
exec(_('%s -o short -L %s/corpus/simple.log', BUNYAN, __dirname),
|
||||
{env: tzEnv}, function (err, stdout, stderr) {
|
||||
t.ifError(err)
|
||||
t.equal(stdout,
|
||||
'12:56:52.856 INFO myservice: '
|
||||
+ 'My message\n');
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
test('time: simple.log utc short', function (t) {
|
||||
exec(_('%s -o short %s/corpus/simple.log', BUNYAN, __dirname),
|
||||
{env: tzEnv}, function (err, stdout, stderr) {
|
||||
t.ifError(err)
|
||||
t.equal(stdout,
|
||||
'22:56:52.856Z INFO myservice: '
|
||||
+ 'My message\n');
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
test('simple.log with color', function (t) {
|
||||
exec(_('%s --color %s/corpus/simple.log', BUNYAN, __dirname),
|
||||
function (err, stdout, stderr) {
|
||||
t.ifError(err)
|
||||
t.equal(stdout,
|
||||
/* JSSTYLED */
|
||||
'[2012-02-08T22:56:52.856Z] \u001b[36m INFO\u001b[39m: myservice/123 on example.com: \u001b[36mMy message\u001b[39m\n\u001b[0m');
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
test('extrafield.log', function (t) {
|
||||
exec(_('%s %s/corpus/extrafield.log', BUNYAN, __dirname),
|
||||
function (err, stdout, stderr) {
|
||||
t.ifError(err)
|
||||
t.equal(stdout,
|
||||
'[2012-02-08T22:56:52.856Z] INFO: myservice/123 on example.com: '
|
||||
+ 'My message (extra=field)\n');
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
test('extrafield.log with color', function (t) {
|
||||
exec(_('%s --color %s/corpus/extrafield.log', BUNYAN, __dirname),
|
||||
function (err, stdout, stderr) {
|
||||
t.ifError(err)
|
||||
t.equal(stdout,
|
||||
'[2012-02-08T22:56:52.856Z] \u001b[36m INFO\u001b[39m: '
|
||||
+ 'myservice/123 '
|
||||
+ 'on example.com: \u001b[36mMy message\u001b[39m'
|
||||
+ ' (extra=field)\n\u001b[0m');
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
test('bogus.log', function (t) {
|
||||
exec(_('%s %s/corpus/bogus.log', BUNYAN, __dirname),
|
||||
function (err, stdout, stderr) {
|
||||
t.ifError(err)
|
||||
t.equal(stdout, 'not a JSON line\n{"hi": "there"}\n');
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
test('bogus.log -j', function (t) {
|
||||
exec(_('%s -j %s/corpus/bogus.log', BUNYAN, __dirname),
|
||||
function (err, stdout, stderr) {
|
||||
t.ifError(err)
|
||||
t.equal(stdout, 'not a JSON line\n{"hi": "there"}\n');
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
test('all.log', function (t) {
|
||||
exec(_('%s %s/corpus/all.log', BUNYAN, __dirname),
|
||||
function (err, stdout, stderr) {
|
||||
// Just make sure don't blow up on this.
|
||||
t.ifError(err)
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
test('simple.log doesnotexist1.log doesnotexist2.log', function (t) {
|
||||
exec(_('%s %s/corpus/simple.log doesnotexist1.log doesnotexist2.log',
|
||||
BUNYAN, __dirname),
|
||||
function (err, stdout, stderr) {
|
||||
t.ok(err)
|
||||
t.equal(err.code, 2)
|
||||
t.equal(stdout,
|
||||
/* JSSTYLED */
|
||||
'[2012-02-08T22:56:52.856Z] INFO: myservice/123 on example.com: My message\n');
|
||||
// Note: node v0.6.10:
|
||||
// ENOENT, no such file or directory 'asdf.log'
|
||||
// but node v0.6.14:
|
||||
// ENOENT, open 'asdf.log'
|
||||
// io.js 2.2 (at least):
|
||||
// ENOENT: no such file or directory, open 'doesnotexist1.log'
|
||||
var matches = [
|
||||
/^bunyan: ENOENT.*?, open 'doesnotexist1.log'/m,
|
||||
/^bunyan: ENOENT.*?, open 'doesnotexist2.log'/m,
|
||||
];
|
||||
matches.forEach(function (match) {
|
||||
t.ok(match.test(stderr), 'stderr matches ' + match.toString());
|
||||
});
|
||||
t.end();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
test('multiple logs', function (t) {
|
||||
var cmd = _('%s %s/corpus/log1.log %s/corpus/log2.log',
|
||||
BUNYAN, __dirname, __dirname);
|
||||
exec(cmd, function (err, stdout, stderr) {
|
||||
t.ifError(err);
|
||||
t.equal(stdout, [
|
||||
/* BEGIN JSSTYLED */
|
||||
'[2012-05-08T16:57:55.586Z] INFO: agent1/73267 on headnode: message\n',
|
||||
'[2012-05-08T16:58:55.586Z] INFO: agent2/73267 on headnode: message\n',
|
||||
'[2012-05-08T17:01:49.339Z] INFO: agent2/73267 on headnode: message\n',
|
||||
'[2012-05-08T17:02:47.404Z] INFO: agent2/73267 on headnode: message\n',
|
||||
'[2012-05-08T17:02:49.339Z] INFO: agent1/73267 on headnode: message\n',
|
||||
'[2012-05-08T17:02:49.404Z] INFO: agent1/73267 on headnode: message\n',
|
||||
'[2012-05-08T17:02:49.404Z] INFO: agent1/73267 on headnode: message\n',
|
||||
'[2012-05-08T17:02:57.404Z] INFO: agent2/73267 on headnode: message\n',
|
||||
'[2012-05-08T17:08:01.105Z] INFO: agent2/76156 on headnode: message\n',
|
||||
/* END JSSTYLED */
|
||||
].join(''));
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
test('multiple logs, bunyan format', function (t) {
|
||||
var cmd = _('%s -o bunyan %s/corpus/log1.log %s/corpus/log2.log',
|
||||
BUNYAN, __dirname, __dirname);
|
||||
exec(cmd, function (err, stdout, stderr) {
|
||||
t.ifError(err);
|
||||
t.equal(stdout, [
|
||||
/* BEGIN JSSTYLED */
|
||||
'{"name":"agent1","pid":73267,"hostname":"headnode","level":30,"msg":"message","time":"2012-05-08T16:57:55.586Z","v":0}',
|
||||
'{"name":"agent2","pid":73267,"hostname":"headnode","level":30,"msg":"message","time":"2012-05-08T16:58:55.586Z","v":0}',
|
||||
'{"name":"agent2","pid":73267,"hostname":"headnode","level":30,"msg":"message","time":"2012-05-08T17:01:49.339Z","v":0}',
|
||||
'{"name":"agent2","pid":73267,"hostname":"headnode","level":30,"msg":"message","time":"2012-05-08T17:02:47.404Z","v":0}',
|
||||
'{"name":"agent1","pid":73267,"hostname":"headnode","level":30,"msg":"message","time":"2012-05-08T17:02:49.339Z","v":0}',
|
||||
'{"name":"agent1","pid":73267,"hostname":"headnode","level":30,"msg":"message","time":"2012-05-08T17:02:49.404Z","v":0}',
|
||||
'{"name":"agent1","pid":73267,"hostname":"headnode","level":30,"msg":"message","time":"2012-05-08T17:02:49.404Z","v":0}',
|
||||
'{"name":"agent2","pid":73267,"hostname":"headnode","level":30,"msg":"message","time":"2012-05-08T17:02:57.404Z","v":0}',
|
||||
'{"name":"agent2","pid":76156,"hostname":"headnode","level":30,"msg":"message","time":"2012-05-08T17:08:01.105Z","v":0}',
|
||||
''
|
||||
/* END JSSTYLED */
|
||||
].join('\n'));
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
test('log1.log.gz', function (t) {
|
||||
exec(_('%s %s/corpus/log1.log.gz', BUNYAN, __dirname),
|
||||
function (err, stdout, stderr) {
|
||||
t.ifError(err);
|
||||
t.equal(stdout, [
|
||||
/* BEGIN JSSTYLED */
|
||||
'[2012-05-08T16:57:55.586Z] INFO: agent1/73267 on headnode: message\n',
|
||||
'[2012-05-08T17:02:49.339Z] INFO: agent1/73267 on headnode: message\n',
|
||||
'[2012-05-08T17:02:49.404Z] INFO: agent1/73267 on headnode: message\n',
|
||||
'[2012-05-08T17:02:49.404Z] INFO: agent1/73267 on headnode: message\n',
|
||||
/* END JSSTYLED */
|
||||
].join(''));
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
test('mixed text and gzip logs', function (t) {
|
||||
var cmd = _('%s %s/corpus/log1.log.gz %s/corpus/log2.log',
|
||||
BUNYAN, __dirname, __dirname);
|
||||
exec(cmd, function (err, stdout, stderr) {
|
||||
t.ifError(err);
|
||||
t.equal(stdout, [
|
||||
/* BEGIN JSSTYLED */
|
||||
'[2012-05-08T16:57:55.586Z] INFO: agent1/73267 on headnode: message\n',
|
||||
'[2012-05-08T16:58:55.586Z] INFO: agent2/73267 on headnode: message\n',
|
||||
'[2012-05-08T17:01:49.339Z] INFO: agent2/73267 on headnode: message\n',
|
||||
'[2012-05-08T17:02:47.404Z] INFO: agent2/73267 on headnode: message\n',
|
||||
'[2012-05-08T17:02:49.339Z] INFO: agent1/73267 on headnode: message\n',
|
||||
'[2012-05-08T17:02:49.404Z] INFO: agent1/73267 on headnode: message\n',
|
||||
'[2012-05-08T17:02:49.404Z] INFO: agent1/73267 on headnode: message\n',
|
||||
'[2012-05-08T17:02:57.404Z] INFO: agent2/73267 on headnode: message\n',
|
||||
'[2012-05-08T17:08:01.105Z] INFO: agent2/76156 on headnode: message\n',
|
||||
/* END JSSTYLED */
|
||||
].join(''));
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
test('--level 40', function (t) {
|
||||
expect = [
|
||||
/* BEGIN JSSTYLED */
|
||||
'# levels\n',
|
||||
'[2012-02-08T22:56:53.856Z] WARN: myservice/123 on example.com: My message\n',
|
||||
'[2012-02-08T22:56:54.856Z] ERROR: myservice/123 on example.com: My message\n',
|
||||
'[2012-02-08T22:56:55.856Z] LVL55: myservice/123 on example.com: My message\n',
|
||||
'[2012-02-08T22:56:56.856Z] FATAL: myservice/123 on example.com: My message\n',
|
||||
'\n',
|
||||
'# extra fields\n',
|
||||
'\n',
|
||||
'# bogus\n',
|
||||
'not a JSON line\n',
|
||||
'{"hi": "there"}\n'
|
||||
/* END JSSTYLED */
|
||||
].join('');
|
||||
exec(_('%s -l 40 %s/corpus/all.log', BUNYAN, __dirname),
|
||||
function (err, stdout, stderr) {
|
||||
t.ifError(err);
|
||||
t.equal(stdout, expect);
|
||||
exec(_('%s --level 40 %s/corpus/all.log', BUNYAN, __dirname),
|
||||
function (err, stdout, stderr) {
|
||||
t.ifError(err);
|
||||
t.equal(stdout, expect);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('--condition "this.level === 10 && this.pid === 123"', function (t) {
|
||||
var expect = [
|
||||
'# levels\n',
|
||||
/* JSSTYLED */
|
||||
'[2012-02-08T22:56:50.856Z] TRACE: myservice/123 on example.com: My message\n',
|
||||
'\n',
|
||||
'# extra fields\n',
|
||||
'\n',
|
||||
'# bogus\n',
|
||||
'not a JSON line\n',
|
||||
'{"hi": "there"}\n'
|
||||
].join('');
|
||||
var cmd = _('%s -c "this.level === 10 && this.pid === 123"'
|
||||
+ ' %s/corpus/all.log', BUNYAN, __dirname);
|
||||
exec(cmd, function (err, stdout, stderr) {
|
||||
t.ifError(err);
|
||||
t.equal(stdout, expect);
|
||||
var cmd = _(
|
||||
'%s --condition "this.level === 10 && this.pid === 123"'
|
||||
+ ' %s/corpus/all.log', BUNYAN, __dirname);
|
||||
exec(cmd, function (err, stdout, stderr) {
|
||||
t.ifError(err);
|
||||
t.equal(stdout, expect);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('--condition "this.level === TRACE', function (t) {
|
||||
var expect = [
|
||||
'# levels\n',
|
||||
/* JSSTYLED */
|
||||
'[2012-02-08T22:56:50.856Z] TRACE: myservice/123 on example.com: My message\n',
|
||||
'\n',
|
||||
'# extra fields\n',
|
||||
'\n',
|
||||
'# bogus\n',
|
||||
'not a JSON line\n',
|
||||
'{"hi": "there"}\n'
|
||||
].join('');
|
||||
var cmd = _('%s -c "this.level === TRACE" %s/corpus/all.log',
|
||||
BUNYAN, __dirname);
|
||||
exec(cmd, function (err, stdout, stderr) {
|
||||
t.ifError(err);
|
||||
t.equal(stdout, expect);
|
||||
t.done();
|
||||
});
|
||||
});
|
||||
|
||||
// multiple
|
||||
test('multiple --conditions', function (t) {
|
||||
var expect = [
|
||||
'# levels\n',
|
||||
/* JSSTYLED */
|
||||
'[2012-02-08T22:56:53.856Z] WARN: myservice/123 on example.com: My message\n',
|
||||
'\n',
|
||||
'# extra fields\n',
|
||||
'\n',
|
||||
'# bogus\n',
|
||||
'not a JSON line\n',
|
||||
'{"hi": "there"}\n'
|
||||
].join('');
|
||||
exec(_('%s %s/corpus/all.log -c "this.level === 40" -c "this.pid === 123"',
|
||||
BUNYAN, __dirname), function (err, stdout, stderr) {
|
||||
t.ifError(err);
|
||||
t.equal(stdout, expect);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
// https://github.com/trentm/node-bunyan/issues/30
|
||||
//
|
||||
// One of the records in corpus/withreq.log has a 'req'
|
||||
// field with no 'headers'. Ditto for the 'res' field.
|
||||
test('robust req handling', function (t) {
|
||||
var expect = [
|
||||
/* BEGIN JSSTYLED */
|
||||
'[2012-08-08T10:25:47.636Z] DEBUG: amon-master/12859 on 9724a190-27b6-4fd8-830b-a574f839c67d: headAgentProbes respond (req_id=cce79d15-ffc2-487c-a4e4-e940bdaac31e, route=HeadAgentProbes, contentMD5=11FxOYiYfpMxmANj4kGJzg==)',
|
||||
'[2012-08-08T10:25:47.637Z] INFO: amon-master/12859 on 9724a190-27b6-4fd8-830b-a574f839c67d: HeadAgentProbes handled: 200 (req_id=cce79d15-ffc2-487c-a4e4-e940bdaac31e, audit=true, remoteAddress=10.2.207.2, remotePort=50394, latency=3, secure=false, _audit=true, req.version=*)',
|
||||
' HEAD /agentprobes?agent=ccf92af9-0b24-46b6-ab60-65095fdd3037 HTTP/1.1',
|
||||
' accept: application/json',
|
||||
' content-type: application/json',
|
||||
' host: 10.2.207.16',
|
||||
' connection: keep-alive',
|
||||
' --',
|
||||
' HTTP/1.1 200 OK',
|
||||
' content-md5: 11FxOYiYfpMxmANj4kGJzg==',
|
||||
' access-control-allow-origin: *',
|
||||
' access-control-allow-headers: Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version',
|
||||
' access-control-allow-methods: HEAD',
|
||||
' access-control-expose-headers: X-Api-Version, X-Request-Id, X-Response-Time',
|
||||
' connection: Keep-Alive',
|
||||
' date: Wed, 08 Aug 2012 10:25:47 GMT',
|
||||
' server: Amon Master/1.0.0',
|
||||
' x-request-id: cce79d15-ffc2-487c-a4e4-e940bdaac31e',
|
||||
' x-response-time: 3',
|
||||
' --',
|
||||
' route: {',
|
||||
' "name": "HeadAgentProbes",',
|
||||
' "version": false',
|
||||
' }',
|
||||
'[2012-08-08T10:25:47.637Z] INFO: amon-master/12859 on 9724a190-27b6-4fd8-830b-a574f839c67d: HeadAgentProbes handled: 200 (req_id=cce79d15-ffc2-487c-a4e4-e940bdaac31e, audit=true, remoteAddress=10.2.207.2, remotePort=50394, latency=3, secure=false, _audit=true, req.version=*)',
|
||||
' HEAD /agentprobes?agent=ccf92af9-0b24-46b6-ab60-65095fdd3037 HTTP/1.1',
|
||||
' --',
|
||||
' HTTP/1.1 200 OK',
|
||||
' --',
|
||||
' route: {',
|
||||
' "name": "HeadAgentProbes",',
|
||||
' "version": false',
|
||||
' }'
|
||||
/* END JSSTYLED */
|
||||
].join('\n') + '\n';
|
||||
exec(_('%s %s/corpus/withreq.log', BUNYAN, __dirname),
|
||||
function (err, stdout, stderr) {
|
||||
t.ifError(err);
|
||||
t.equal(stdout, expect);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
// Some past crashes from issues.
|
||||
test('should not crash on corpus/old-crashers/*.log', function (t) {
|
||||
var oldCrashers = fs.readdirSync(
|
||||
path.resolve(__dirname, 'corpus/old-crashers'))
|
||||
.filter(function (f) { return f.slice(-4) === '.log'; });
|
||||
vasync.forEachPipeline({
|
||||
inputs: oldCrashers,
|
||||
func: function (logName, next) {
|
||||
exec(_('%s %s/corpus/old-crashers/%s', BUNYAN, __dirname, logName),
|
||||
function (err, stdout, stderr) {
|
||||
next(err);
|
||||
});
|
||||
}
|
||||
}, function (err, results) {
|
||||
t.ifError(err);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
test('client_req extra newlines, client_res={} (pull #252)', function (t) {
|
||||
var expect = [
|
||||
/* BEGIN JSSTYLED */
|
||||
'[2016-02-10T07:28:40.510Z] TRACE: aclientreq/23280 on danger0.local: request sent',
|
||||
' GET /--ping HTTP/1.1',
|
||||
'[2016-02-10T07:28:41.419Z] TRACE: aclientreq/23280 on danger0.local: Response received',
|
||||
' HTTP/1.1 200 OK',
|
||||
' request-id: e8a5a700-cfc7-11e5-a3dc-3b85d20f26ef',
|
||||
' content-type: application/json'
|
||||
/* END JSSTYLED */
|
||||
].join('\n') + '\n';
|
||||
exec(_('%s %s/corpus/clientreqres.log', BUNYAN, __dirname),
|
||||
function (err, stdout, stderr) {
|
||||
t.ifError(err);
|
||||
t.equal(stdout, expect);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
test('should only show nonempty response bodies', function (t) {
|
||||
var expect = [
|
||||
/* BEGIN JSSTYLED */
|
||||
'[2016-02-10T07:28:41.419Z] INFO: myservice/123 on example.com: UnauthorizedError',
|
||||
' HTTP/1.1 401 Unauthorized',
|
||||
' content-type: text/plain',
|
||||
' date: Sat, 07 Mar 2015 06:58:43 GMT',
|
||||
'[2016-02-10T07:28:41.419Z] INFO: myservice/123 on example.com: hello',
|
||||
' HTTP/1.1 200 OK',
|
||||
' content-type: text/plain',
|
||||
' content-length: 0',
|
||||
' date: Sat, 07 Mar 2015 06:58:43 GMT',
|
||||
' ',
|
||||
' hello',
|
||||
'[2016-02-10T07:28:41.419Z] INFO: myservice/123 on example.com: UnauthorizedError',
|
||||
' HTTP/1.1 401 Unauthorized',
|
||||
' content-type: text/plain',
|
||||
' date: Sat, 07 Mar 2015 06:58:43 GMT'
|
||||
/* END JSSTYLED */
|
||||
].join('\n') + '\n';
|
||||
exec(_('%s %s/corpus/content-length-0-res.log', BUNYAN, __dirname),
|
||||
function (err, stdout, stderr) {
|
||||
t.ifError(err);
|
||||
t.equal(stdout, expect);
|
||||
t.end();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,300 @@
|
|||
/*
|
||||
* Test the `bunyan` CLI.
|
||||
*/
|
||||
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import { exec, dirname } from './helper.mjs'
|
||||
import { Eltro as t, assert} from 'eltro'
|
||||
|
||||
// ---- assertable variables
|
||||
|
||||
const catter = process.platform === 'win32' ? 'type' : 'cat'
|
||||
const assertSimpleLog = '[2012-02-08T22:56:52.856Z] INFO: myservice/123 on example.com: My message\n'
|
||||
|
||||
// ---- tests
|
||||
|
||||
t.test('--version', async function () {
|
||||
let pckg = JSON.parse(fs.readFileSync(dirname('/../package.json')))
|
||||
const version = pckg.version
|
||||
|
||||
let res = await exec('--version')
|
||||
assert.strictEqual(res.stdout, 'bunyan ' + version + '\n')
|
||||
});
|
||||
|
||||
t.test('--help', async function () {
|
||||
let res = await exec('--help')
|
||||
assert.match(res.stdout, /General options:/)
|
||||
});
|
||||
|
||||
t.test('-h', async function () {
|
||||
let res = await exec('-h')
|
||||
assert.match(res.stdout, /General options:/)
|
||||
});
|
||||
|
||||
t.test('--bogus', async function () {
|
||||
let err = await assert.isRejected(exec('--bogus'))
|
||||
assert.strictEqual(err.code, 1)
|
||||
});
|
||||
|
||||
t.test('simple.log', async function () {
|
||||
let res = await exec(dirname('/corpus/simple.log'))
|
||||
assert.strictEqual(res.stdout, assertSimpleLog)
|
||||
});
|
||||
|
||||
t.test(`${catter} simple.log`, async function () {
|
||||
let res = await exec('', `${catter} ${dirname('/corpus/simple.log')} | node `)
|
||||
assert.strictEqual(res.stdout, assertSimpleLog)
|
||||
});
|
||||
|
||||
t.test('time: simple.log utc long', async function () {
|
||||
let res = await exec('-o long --time utc ' + dirname('/corpus/simple.log'))
|
||||
assert.strictEqual(res.stdout, assertSimpleLog)
|
||||
});
|
||||
|
||||
t.test('time: simple.log utc short', async function () {
|
||||
let res = await exec('-o short ' + dirname('/corpus/simple.log'))
|
||||
assert.strictEqual(res.stdout, '22:56:52.856Z INFO myservice: My message\n')
|
||||
});
|
||||
|
||||
t.test('simple.log with color', async function () {
|
||||
let res = await exec(dirname('/corpus/simple.log'))
|
||||
assert.notMatch(res.stdout, /\[2012-02-08T22:56:52.856Z\] [^ ]+ INFO[^:]+:/)
|
||||
res = await exec('--color ' + dirname('/corpus/simple.log'))
|
||||
assert.match(res.stdout, /\[2012-02-08T22:56:52.856Z\] [^ ]+ INFO[^:]+:/)
|
||||
});
|
||||
|
||||
t.test('extrafield.log', async function () {
|
||||
let res = await exec(dirname('/corpus/extrafield.log'))
|
||||
assert.strictEqual(res.stdout, '[2012-02-08T22:56:52.856Z] INFO: myservice/123 on example.com: My message (extra=field)\n')
|
||||
});
|
||||
|
||||
t.test('extrafield.log with color', async function () {
|
||||
let res = await exec(dirname('/corpus/extrafield.log'))
|
||||
assert.notMatch(res.stdout, /My message[^ ]+ \(extra=field\)\n.+/)
|
||||
res = await exec('--color ' + dirname('/corpus/extrafield.log'))
|
||||
assert.match(res.stdout, /My message[^ ]+ \(extra=field\)\n.+/)
|
||||
});
|
||||
|
||||
t.test('bogus.log', async function () {
|
||||
let res = await exec(dirname('/corpus/bogus.log'))
|
||||
assert.strictEqual(res.stdout, 'not a JSON line\n{"hi": "there"}\n')
|
||||
});
|
||||
|
||||
t.test('bogus.log -j', async function () {
|
||||
let res = await exec('-j ' + dirname('/corpus/bogus.log'))
|
||||
assert.strictEqual(res.stdout, 'not a JSON line\n{"hi": "there"}\n')
|
||||
});
|
||||
|
||||
t.test('all.log', async function () {
|
||||
// Just make sure don't blow up on this.
|
||||
await exec(dirname('/corpus/all.log'))
|
||||
});
|
||||
|
||||
t.test('simple.log doesnotexist1.log doesnotexist2.log', async function () {
|
||||
let res = await assert.isRejected(exec(dirname('/corpus/simple.log') + ' doesnotexist1.log doesnotexist2.log'))
|
||||
assert.strictEqual(res.stdout, '[2012-02-08T22:56:52.856Z] INFO: myservice/123 on example.com: My message\n')
|
||||
|
||||
// Note: node v0.6.10:
|
||||
// ENOENT, no such file or directory 'asdf.log'
|
||||
// but node v0.6.14:
|
||||
// ENOENT, open 'asdf.log'
|
||||
// io.js 2.2 (at least):
|
||||
// ENOENT: no such file or directory, open 'doesnotexist1.log'
|
||||
let matches = [
|
||||
/^bunyan: ENOENT.*?, open '[^']*doesnotexist1.log'/m,
|
||||
/^bunyan: ENOENT.*?, open '[^']*doesnotexist2.log'/m,
|
||||
];
|
||||
matches.forEach(function (match) {
|
||||
assert.match(res.stderr, match);
|
||||
});
|
||||
});
|
||||
|
||||
t.test('multiple logs', async function () {
|
||||
let res = await exec(dirname('/corpus/log1.log') + ' ' + dirname('/corpus/log2.log'))
|
||||
assert.strictEqual(res.stdout, [
|
||||
'[2012-05-08T16:57:55.586Z] INFO: agent1/73267 on headnode: message\n',
|
||||
'[2012-05-08T16:58:55.586Z] INFO: agent2/73267 on headnode: message\n',
|
||||
'[2012-05-08T17:01:49.339Z] INFO: agent2/73267 on headnode: message\n',
|
||||
'[2012-05-08T17:02:47.404Z] INFO: agent2/73267 on headnode: message\n',
|
||||
'[2012-05-08T17:02:49.339Z] INFO: agent1/73267 on headnode: message\n',
|
||||
'[2012-05-08T17:02:49.404Z] INFO: agent1/73267 on headnode: message\n',
|
||||
'[2012-05-08T17:02:49.404Z] INFO: agent1/73267 on headnode: message\n',
|
||||
'[2012-05-08T17:02:57.404Z] INFO: agent2/73267 on headnode: message\n',
|
||||
'[2012-05-08T17:08:01.105Z] INFO: agent2/76156 on headnode: message\n',
|
||||
].join(''))
|
||||
});
|
||||
|
||||
t.test('multiple logs, bunyan format', async function () {
|
||||
let res = await exec('-o bunyan ' + dirname('/corpus/log1.log') + ' ' + dirname('/corpus/log2.log'))
|
||||
assert.strictEqual(res.stdout, [
|
||||
'{"name":"agent1","pid":73267,"hostname":"headnode","level":30,"msg":"message","time":"2012-05-08T16:57:55.586Z","v":0}\n',
|
||||
'{"name":"agent2","pid":73267,"hostname":"headnode","level":30,"msg":"message","time":"2012-05-08T16:58:55.586Z","v":0}\n',
|
||||
'{"name":"agent2","pid":73267,"hostname":"headnode","level":30,"msg":"message","time":"2012-05-08T17:01:49.339Z","v":0}\n',
|
||||
'{"name":"agent2","pid":73267,"hostname":"headnode","level":30,"msg":"message","time":"2012-05-08T17:02:47.404Z","v":0}\n',
|
||||
'{"name":"agent1","pid":73267,"hostname":"headnode","level":30,"msg":"message","time":"2012-05-08T17:02:49.339Z","v":0}\n',
|
||||
'{"name":"agent1","pid":73267,"hostname":"headnode","level":30,"msg":"message","time":"2012-05-08T17:02:49.404Z","v":0}\n',
|
||||
'{"name":"agent1","pid":73267,"hostname":"headnode","level":30,"msg":"message","time":"2012-05-08T17:02:49.404Z","v":0}\n',
|
||||
'{"name":"agent2","pid":73267,"hostname":"headnode","level":30,"msg":"message","time":"2012-05-08T17:02:57.404Z","v":0}\n',
|
||||
'{"name":"agent2","pid":76156,"hostname":"headnode","level":30,"msg":"message","time":"2012-05-08T17:08:01.105Z","v":0}\n',
|
||||
].join(''))
|
||||
});
|
||||
|
||||
t.test('log1.log.gz', async function () {
|
||||
let res = await exec(dirname('/corpus/log1.log.gz'))
|
||||
assert.strictEqual(res.stdout, [
|
||||
'[2012-05-08T16:57:55.586Z] INFO: agent1/73267 on headnode: message\n',
|
||||
'[2012-05-08T17:02:49.339Z] INFO: agent1/73267 on headnode: message\n',
|
||||
'[2012-05-08T17:02:49.404Z] INFO: agent1/73267 on headnode: message\n',
|
||||
'[2012-05-08T17:02:49.404Z] INFO: agent1/73267 on headnode: message\n',
|
||||
].join(''))
|
||||
});
|
||||
|
||||
t.test('mixed text and gzip logs', async function () {
|
||||
let res = await exec(dirname('/corpus/log1.log.gz') + ' ' + dirname('/corpus/log2.log'))
|
||||
assert.strictEqual(res.stdout, [
|
||||
'[2012-05-08T16:57:55.586Z] INFO: agent1/73267 on headnode: message\n',
|
||||
'[2012-05-08T16:58:55.586Z] INFO: agent2/73267 on headnode: message\n',
|
||||
'[2012-05-08T17:01:49.339Z] INFO: agent2/73267 on headnode: message\n',
|
||||
'[2012-05-08T17:02:47.404Z] INFO: agent2/73267 on headnode: message\n',
|
||||
'[2012-05-08T17:02:49.339Z] INFO: agent1/73267 on headnode: message\n',
|
||||
'[2012-05-08T17:02:49.404Z] INFO: agent1/73267 on headnode: message\n',
|
||||
'[2012-05-08T17:02:49.404Z] INFO: agent1/73267 on headnode: message\n',
|
||||
'[2012-05-08T17:02:57.404Z] INFO: agent2/73267 on headnode: message\n',
|
||||
'[2012-05-08T17:08:01.105Z] INFO: agent2/76156 on headnode: message\n',
|
||||
].join(''))
|
||||
});
|
||||
|
||||
t.test('--level 40', async function () {
|
||||
let res = await exec('-l 40 ' + dirname('/corpus/all.log'))
|
||||
assert.strictEqual(res.stdout, [
|
||||
'# levels\n',
|
||||
'[2012-02-08T22:56:53.856Z] WARN: myservice/123 on example.com: My message\n',
|
||||
'[2012-02-08T22:56:54.856Z] ERROR: myservice/123 on example.com: My message\n',
|
||||
'[2012-02-08T22:56:55.856Z] LVL55: myservice/123 on example.com: My message\n',
|
||||
'[2012-02-08T22:56:56.856Z] FATAL: myservice/123 on example.com: My message\n',
|
||||
'\n',
|
||||
'# extra fields\n',
|
||||
'\n',
|
||||
'# bogus\n',
|
||||
'not a JSON line\n',
|
||||
'{"hi": "there"}\n'
|
||||
].join(''))
|
||||
});
|
||||
|
||||
t.test('--condition "this.level === 10 && this.pid === 123"', async function () {
|
||||
let res = await exec('-c "this.level === 10 && this.pid === 123" ' + dirname('/corpus/all.log'))
|
||||
assert.strictEqual(res.stdout, [
|
||||
'# levels\n',
|
||||
'[2012-02-08T22:56:50.856Z] TRACE: myservice/123 on example.com: My message\n',
|
||||
'\n',
|
||||
'# extra fields\n',
|
||||
'\n',
|
||||
'# bogus\n',
|
||||
'not a JSON line\n',
|
||||
'{"hi": "there"}\n'
|
||||
].join(''))
|
||||
});
|
||||
|
||||
t.test('--condition "this.level === TRACE', async function () {
|
||||
let res = await exec('-c "this.level === TRACE" ' + dirname('/corpus/all.log'))
|
||||
assert.strictEqual(res.stdout, [
|
||||
'# levels\n',
|
||||
'[2012-02-08T22:56:50.856Z] TRACE: myservice/123 on example.com: My message\n',
|
||||
'\n',
|
||||
'# extra fields\n',
|
||||
'\n',
|
||||
'# bogus\n',
|
||||
'not a JSON line\n',
|
||||
'{"hi": "there"}\n'
|
||||
].join(''))
|
||||
});
|
||||
|
||||
t.test('multiple --conditions', async function () {
|
||||
let res = await exec(dirname('/corpus/all.log') + ' -c "this.level === 40" -c "this.pid === 123"')
|
||||
assert.strictEqual(res.stdout, [
|
||||
'# levels\n',
|
||||
'[2012-02-08T22:56:53.856Z] WARN: myservice/123 on example.com: My message\n',
|
||||
'\n',
|
||||
'# extra fields\n',
|
||||
'\n',
|
||||
'# bogus\n',
|
||||
'not a JSON line\n',
|
||||
'{"hi": "there"}\n'
|
||||
].join(''))
|
||||
});
|
||||
|
||||
// https://github.com/trentm/node-bunyan/issues/30
|
||||
//
|
||||
// One of the records in corpus/withreq.log has a 'req'
|
||||
// field with no 'headers'. Ditto for the 'res' field.
|
||||
t.test('robust req handling', async function () {
|
||||
let res = await exec(dirname('/corpus/withreq.log'))
|
||||
assert.strictEqual(res.stdout, [
|
||||
'[2012-08-08T10:25:47.636Z] DEBUG: amon-master/12859 on 9724a190-27b6-4fd8-830b-a574f839c67d: headAgentProbes respond (req_id=cce79d15-ffc2-487c-a4e4-e940bdaac31e, route=HeadAgentProbes, contentMD5=11FxOYiYfpMxmANj4kGJzg==)',
|
||||
'[2012-08-08T10:25:47.637Z] INFO: amon-master/12859 on 9724a190-27b6-4fd8-830b-a574f839c67d: HeadAgentProbes handled: 200 (req_id=cce79d15-ffc2-487c-a4e4-e940bdaac31e, audit=true, remoteAddress=10.2.207.2, remotePort=50394, latency=3, secure=false, _audit=true, req.version=*)',
|
||||
' HEAD /agentprobes?agent=ccf92af9-0b24-46b6-ab60-65095fdd3037 HTTP/1.1',
|
||||
' accept: application/json',
|
||||
' content-type: application/json',
|
||||
' host: 10.2.207.16',
|
||||
' connection: keep-alive',
|
||||
' --',
|
||||
' HTTP/1.1 200 OK',
|
||||
' content-md5: 11FxOYiYfpMxmANj4kGJzg==',
|
||||
' access-control-allow-origin: *',
|
||||
' access-control-allow-headers: Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version',
|
||||
' access-control-allow-methods: HEAD',
|
||||
' access-control-expose-headers: X-Api-Version, X-Request-Id, X-Response-Time',
|
||||
' connection: Keep-Alive',
|
||||
' date: Wed, 08 Aug 2012 10:25:47 GMT',
|
||||
' server: Amon Master/1.0.0',
|
||||
' x-request-id: cce79d15-ffc2-487c-a4e4-e940bdaac31e',
|
||||
' x-response-time: 3',
|
||||
' --',
|
||||
' route: {',
|
||||
' "name": "HeadAgentProbes",',
|
||||
' "version": false',
|
||||
' }',
|
||||
'[2012-08-08T10:25:47.637Z] INFO: amon-master/12859 on 9724a190-27b6-4fd8-830b-a574f839c67d: HeadAgentProbes handled: 200 (req_id=cce79d15-ffc2-487c-a4e4-e940bdaac31e, audit=true, remoteAddress=10.2.207.2, remotePort=50394, latency=3, secure=false, _audit=true, req.version=*)',
|
||||
' HEAD /agentprobes?agent=ccf92af9-0b24-46b6-ab60-65095fdd3037 HTTP/1.1',
|
||||
' --',
|
||||
' HTTP/1.1 200 OK',
|
||||
' --',
|
||||
' route: {',
|
||||
' "name": "HeadAgentProbes",',
|
||||
' "version": false',
|
||||
' }'
|
||||
].join('\n') + '\n')
|
||||
});
|
||||
|
||||
// Some past crashes from issues.
|
||||
t.test('should not crash on corpus/old-crashers/*.log', async function () {
|
||||
let oldCrashers = fs.readdirSync(
|
||||
path.resolve(dirname('/corpus/old-crashers')))
|
||||
.filter(function (f) { return f.slice(-4) === '.log'; });
|
||||
|
||||
await Promise.all(oldCrashers.map(function(logFile) {
|
||||
return exec(dirname('/corpus/old-crashers/' + logFile))
|
||||
}))
|
||||
});
|
||||
|
||||
t.test('should only show nonempty response bodies', async function () {
|
||||
let res = await exec(dirname('/corpus/content-length-0-res.log'))
|
||||
assert.strictEqual(res.stdout, [
|
||||
'[2016-02-10T07:28:41.419Z] INFO: myservice/123 on example.com: UnauthorizedError',
|
||||
' HTTP/1.1 401 Unauthorized',
|
||||
' content-type: text/plain',
|
||||
' date: Sat, 07 Mar 2015 06:58:43 GMT',
|
||||
'[2016-02-10T07:28:41.419Z] INFO: myservice/123 on example.com: hello',
|
||||
' HTTP/1.1 200 OK',
|
||||
' content-type: text/plain',
|
||||
' content-length: 0',
|
||||
' date: Sat, 07 Mar 2015 06:58:43 GMT',
|
||||
' ',
|
||||
' hello',
|
||||
'[2016-02-10T07:28:41.419Z] INFO: myservice/123 on example.com: UnauthorizedError',
|
||||
' HTTP/1.1 401 Unauthorized',
|
||||
' content-type: text/plain',
|
||||
' date: Sat, 07 Mar 2015 06:58:43 GMT'
|
||||
].join('\n') + '\n');
|
||||
});
|
|
@ -0,0 +1 @@
|
|||
{"name":"minfo","hostname":"sharptooth.local","pid":66266,"level":10,"client_req":{"method":"HEAD","url":"/dap/stor","address":"127.0.0.1","headers":{"accept":"application/json, */*","host":"foo.example.com","date":"Fri, 12 May 2017 23:59:15 GMT"}},"msg":"request sent","time":"2017-05-12T23:59:15.877Z","v":0}
|
|
@ -0,0 +1 @@
|
|||
{"name":"res-header","hostname":"danger0.local","pid":76488,"level":30,"res":{"statusCode":200,"header":"HTTP/1.1 200 OK\r\nFoo: bar\r\nDate: Wed, 02 Aug 2017 22:37:34 GMT\r\nConnection: keep-alive\r\nContent-Length: 21\r\n\r\n"},"msg":"response sent","time":"2017-08-02T22:37:34.798Z","v":0}
|
|
@ -0,0 +1 @@
|
|||
{"name":"res-header","hostname":"danger0.local","pid":76488,"level":30,"res":{"statusCode":200},"msg":"response sent","time":"2017-08-02T22:37:34.798Z","v":0}
|
|
@ -1,154 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2012 Trent Mick. All rights reserved.
|
||||
*
|
||||
* Test type checking on creation of the Logger.
|
||||
*/
|
||||
|
||||
var bunyan = require('../lib/bunyan'),
|
||||
Logger = bunyan;
|
||||
|
||||
// node-tap API
|
||||
if (require.cache[__dirname + '/tap4nodeunit.js'])
|
||||
delete require.cache[__dirname + '/tap4nodeunit.js'];
|
||||
var tap4nodeunit = require('./tap4nodeunit.js');
|
||||
var after = tap4nodeunit.after;
|
||||
var before = tap4nodeunit.before;
|
||||
var test = tap4nodeunit.test;
|
||||
|
||||
|
||||
|
||||
test('ensure Logger creation options', function (t) {
|
||||
t.throws(function () { new Logger(); },
|
||||
/options \(object\) is required/,
|
||||
'no options should throw');
|
||||
|
||||
t.throws(function () { new Logger({}); },
|
||||
/options\.name \(string\) is required/,
|
||||
'no options.name should throw');
|
||||
|
||||
t.doesNotThrow(function () { new Logger({name: 'foo'}); },
|
||||
'just options.name should be sufficient');
|
||||
|
||||
var options = {name: 'foo', stream: process.stdout, streams: []};
|
||||
t.throws(function () { new Logger(options); },
|
||||
/* JSSTYLED */
|
||||
/cannot mix "streams" and "stream" options/,
|
||||
'cannot use "stream" and "streams"');
|
||||
|
||||
// https://github.com/trentm/node-bunyan/issues/3
|
||||
options = {name: 'foo', streams: {}};
|
||||
t.throws(function () { new Logger(options); },
|
||||
/invalid options.streams: must be an array/,
|
||||
'"streams" must be an array');
|
||||
|
||||
options = {name: 'foo', serializers: 'a string'};
|
||||
t.throws(function () { new Logger(options); },
|
||||
/invalid options.serializers: must be an object/,
|
||||
'"serializers" cannot be a string');
|
||||
|
||||
options = {name: 'foo', serializers: [1, 2, 3]};
|
||||
t.throws(function () { new Logger(options); },
|
||||
/invalid options.serializers: must be an object/,
|
||||
'"serializers" cannot be an array');
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('ensure Logger constructor is safe without new', function (t) {
|
||||
t.doesNotThrow(function () { Logger({name: 'foo'}); },
|
||||
'constructor should call self with new if necessary');
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('ensure Logger creation options (createLogger)', function (t) {
|
||||
t.throws(function () { bunyan.createLogger(); },
|
||||
/options \(object\) is required/,
|
||||
'no options should throw');
|
||||
|
||||
t.throws(function () { bunyan.createLogger({}); },
|
||||
/options\.name \(string\) is required/,
|
||||
'no options.name should throw');
|
||||
|
||||
t.doesNotThrow(function () { bunyan.createLogger({name: 'foo'}); },
|
||||
'just options.name should be sufficient');
|
||||
|
||||
var options = {name: 'foo', stream: process.stdout, streams: []};
|
||||
t.throws(function () { bunyan.createLogger(options); },
|
||||
/* JSSTYLED */
|
||||
/cannot mix "streams" and "stream" options/,
|
||||
'cannot use "stream" and "streams"');
|
||||
|
||||
// https://github.com/trentm/node-bunyan/issues/3
|
||||
options = {name: 'foo', streams: {}};
|
||||
t.throws(function () { bunyan.createLogger(options); },
|
||||
/invalid options.streams: must be an array/,
|
||||
'"streams" must be an array');
|
||||
|
||||
options = {name: 'foo', serializers: 'a string'};
|
||||
t.throws(function () { bunyan.createLogger(options); },
|
||||
/invalid options.serializers: must be an object/,
|
||||
'"serializers" cannot be a string');
|
||||
|
||||
options = {name: 'foo', serializers: [1, 2, 3]};
|
||||
t.throws(function () { bunyan.createLogger(options); },
|
||||
/invalid options.serializers: must be an object/,
|
||||
'"serializers" cannot be an array');
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('ensure Logger child() options', function (t) {
|
||||
var log = new Logger({name: 'foo'});
|
||||
|
||||
t.doesNotThrow(function () { log.child(); },
|
||||
'no options should be fine');
|
||||
|
||||
t.doesNotThrow(function () { log.child({}); },
|
||||
'empty options should be fine too');
|
||||
|
||||
t.throws(function () { log.child({name: 'foo'}); },
|
||||
/invalid options.name: child cannot set logger name/,
|
||||
'child cannot change name');
|
||||
|
||||
var options = {stream: process.stdout, streams: []};
|
||||
t.throws(function () { log.child(options); },
|
||||
/* JSSTYLED */
|
||||
/cannot mix "streams" and "stream" options/,
|
||||
'cannot use "stream" and "streams"');
|
||||
|
||||
// https://github.com/trentm/node-bunyan/issues/3
|
||||
options = {streams: {}};
|
||||
t.throws(function () { log.child(options); },
|
||||
/invalid options.streams: must be an array/,
|
||||
'"streams" must be an array');
|
||||
|
||||
options = {serializers: 'a string'};
|
||||
t.throws(function () { log.child(options); },
|
||||
/invalid options.serializers: must be an object/,
|
||||
'"serializers" cannot be a string');
|
||||
|
||||
options = {serializers: [1, 2, 3]};
|
||||
t.throws(function () { log.child(options); },
|
||||
/invalid options.serializers: must be an object/,
|
||||
'"serializers" cannot be an array');
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('ensure Logger() rejects non-Logger parents', function (t) {
|
||||
var dad = new Logger({name: 'dad', streams: []});
|
||||
|
||||
t.throws(function () { new Logger({}, {}); },
|
||||
/invalid Logger creation: do not pass a second arg/,
|
||||
'Logger arguments must be valid');
|
||||
|
||||
t.doesNotThrow(function () { new Logger(dad, {}); },
|
||||
'Logger allows Logger instance as parent');
|
||||
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* Test type checking on creation of the Logger.
|
||||
*/
|
||||
import { Eltro as t, assert} from 'eltro'
|
||||
import bunyan from '../lib/bunyan.mjs'
|
||||
|
||||
t.test('ensure Logger creation options', function () {
|
||||
assert.throws(function () { new bunyan(); },
|
||||
/options \(object\) is required/,
|
||||
'no options should throw');
|
||||
|
||||
assert.throws(function () { new bunyan({}); },
|
||||
/options\.name \(string\) is required/,
|
||||
'no options.name should throw');
|
||||
|
||||
new bunyan({name: 'foo'});
|
||||
|
||||
let options = {name: 'foo', stream: process.stdout, streams: []};
|
||||
assert.throws(function () { new bunyan(options); },
|
||||
/cannot mix "streams" and "stream" options/, // JSSTYLED
|
||||
'cannot use "stream" and "streams"');
|
||||
|
||||
// https://github.com/trentm/node-bunyan/issues/3
|
||||
options = {name: 'foo', streams: {}};
|
||||
assert.throws(function () { new bunyan(options); },
|
||||
/invalid options.streams: must be an array/,
|
||||
'"streams" must be an array');
|
||||
|
||||
options = {name: 'foo', serializers: 'a string'};
|
||||
assert.throws(function () { new bunyan(options); },
|
||||
/invalid options.serializers: must be an object/,
|
||||
'"serializers" cannot be a string');
|
||||
|
||||
options = {name: 'foo', serializers: [1, 2, 3]};
|
||||
assert.throws(function () { new bunyan(options); },
|
||||
/invalid options.serializers: must be an object/,
|
||||
'"serializers" cannot be an array');
|
||||
});
|
||||
|
||||
|
||||
t.test('ensure Logger constructor is safe without new', function () {
|
||||
bunyan({name: 'foo'})
|
||||
});
|
||||
|
||||
|
||||
t.test('ensure Logger creation options (createLogger)', function () {
|
||||
assert.throws(function () { bunyan.createLogger(); },
|
||||
/options \(object\) is required/,
|
||||
'no options should throw');
|
||||
|
||||
assert.throws(function () { bunyan.createLogger({}); },
|
||||
/options\.name \(string\) is required/,
|
||||
'no options.name should throw');
|
||||
|
||||
bunyan.createLogger({name: 'foo'});
|
||||
|
||||
let options = {name: 'foo', stream: process.stdout, streams: []};
|
||||
assert.throws(function () { bunyan.createLogger(options); },
|
||||
/cannot mix "streams" and "stream" options/, // JSSTYLED
|
||||
'cannot use "stream" and "streams"');
|
||||
|
||||
// https://github.com/trentm/node-bunyan/issues/3
|
||||
options = {name: 'foo', streams: {}};
|
||||
assert.throws(function () { bunyan.createLogger(options); },
|
||||
/invalid options.streams: must be an array/,
|
||||
'"streams" must be an array');
|
||||
|
||||
options = {name: 'foo', serializers: 'a string'};
|
||||
assert.throws(function () { bunyan.createLogger(options); },
|
||||
/invalid options.serializers: must be an object/,
|
||||
'"serializers" cannot be a string');
|
||||
|
||||
options = {name: 'foo', serializers: [1, 2, 3]};
|
||||
assert.throws(function () { bunyan.createLogger(options); },
|
||||
/invalid options.serializers: must be an object/,
|
||||
'"serializers" cannot be an array');
|
||||
});
|
||||
|
||||
|
||||
t.test('ensure Logger child() options', function () {
|
||||
let log = new bunyan({name: 'foo'});
|
||||
|
||||
log.child();
|
||||
|
||||
log.child({});
|
||||
|
||||
assert.throws(function () { log.child({name: 'foo'}); },
|
||||
/invalid options.name: child cannot set logger name/,
|
||||
'child cannot change name');
|
||||
|
||||
let options = {stream: process.stdout, streams: []};
|
||||
assert.throws(function () { log.child(options); },
|
||||
/cannot mix "streams" and "stream" options/, // JSSTYLED
|
||||
'cannot use "stream" and "streams"');
|
||||
|
||||
// https://github.com/trentm/node-bunyan/issues/3
|
||||
options = {streams: {}};
|
||||
assert.throws(function () { log.child(options); },
|
||||
/invalid options.streams: must be an array/,
|
||||
'"streams" must be an array');
|
||||
|
||||
options = {serializers: 'a string'};
|
||||
assert.throws(function () { log.child(options); },
|
||||
/invalid options.serializers: must be an object/,
|
||||
'"serializers" cannot be a string');
|
||||
|
||||
options = {serializers: [1, 2, 3]};
|
||||
assert.throws(function () { log.child(options); },
|
||||
/invalid options.serializers: must be an object/,
|
||||
'"serializers" cannot be an array');
|
||||
});
|
||||
|
||||
|
||||
t.test('ensure Logger() rejects non-Logger parents', function () {
|
||||
let dad = new bunyan({name: 'dad', streams: []});
|
||||
|
||||
assert.throws(function () { new bunyan({}, {}); },
|
||||
/invalid Logger creation: do not pass a second arg/,
|
||||
'Logger arguments must be valid');
|
||||
|
||||
new bunyan(dad, {});
|
||||
});
|
|
@ -1,90 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2012 Trent Mick. All rights reserved.
|
||||
*
|
||||
* Make sure cycles are safe.
|
||||
*/
|
||||
|
||||
var Logger = require('../lib/bunyan.js');
|
||||
|
||||
// node-tap API
|
||||
if (require.cache[__dirname + '/tap4nodeunit.js'])
|
||||
delete require.cache[__dirname + '/tap4nodeunit.js'];
|
||||
var tap4nodeunit = require('./tap4nodeunit.js');
|
||||
var after = tap4nodeunit.after;
|
||||
var before = tap4nodeunit.before;
|
||||
var test = tap4nodeunit.test;
|
||||
|
||||
|
||||
var Stream = require('stream').Stream;
|
||||
var outstr = new Stream;
|
||||
outstr.writable = true;
|
||||
var output = [];
|
||||
outstr.write = function (c) {
|
||||
output.push(JSON.parse(c + ''));
|
||||
};
|
||||
outstr.end = function (c) {
|
||||
if (c) this.write(c);
|
||||
this.emit('end');
|
||||
};
|
||||
|
||||
// these are lacking a few fields that will probably never match
|
||||
var expect =
|
||||
[
|
||||
{
|
||||
'name': 'blammo',
|
||||
'level': 30,
|
||||
'msg': 'bango { bang: \'boom\', KABOOM: [Circular] }',
|
||||
'v': 0
|
||||
},
|
||||
{
|
||||
'name': 'blammo',
|
||||
'level': 30,
|
||||
'msg': 'kaboom { bang: \'boom\', KABOOM: [Circular] }',
|
||||
'v': 0
|
||||
},
|
||||
{
|
||||
'name': 'blammo',
|
||||
'level': 30,
|
||||
'bang': 'boom',
|
||||
'KABOOM': {
|
||||
'bang': 'boom',
|
||||
'KABOOM': '[Circular]'
|
||||
},
|
||||
'msg': '',
|
||||
'v': 0
|
||||
}
|
||||
];
|
||||
|
||||
var log = new Logger({
|
||||
name: 'blammo',
|
||||
streams: [
|
||||
{
|
||||
type: 'stream',
|
||||
level: 'info',
|
||||
stream: outstr
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
test('cycles', function (t) {
|
||||
outstr.on('end', function () {
|
||||
output.forEach(function (o, i) {
|
||||
// Drop variable parts for comparison.
|
||||
delete o.hostname;
|
||||
delete o.pid;
|
||||
delete o.time;
|
||||
// Hack object/dict comparison: JSONify.
|
||||
t.equal(JSON.stringify(o), JSON.stringify(expect[i]),
|
||||
'log item ' + i + ' matches');
|
||||
});
|
||||
t.end();
|
||||
});
|
||||
|
||||
var obj = { bang: 'boom' };
|
||||
obj.KABOOM = obj;
|
||||
log.info('bango', obj);
|
||||
log.info('kaboom', obj.KABOOM);
|
||||
log.info(obj);
|
||||
outstr.end();
|
||||
t.ok('did not throw');
|
||||
});
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* Make sure cycles are safe.
|
||||
*/
|
||||
|
||||
import { Stream } from 'stream'
|
||||
import { Eltro as t, assert} from 'eltro'
|
||||
import Logger from '../lib/bunyan.mjs'
|
||||
|
||||
var outstr = new Stream;
|
||||
outstr.writable = true;
|
||||
var output = [];
|
||||
outstr.write = function (c) {
|
||||
output.push(JSON.parse(c + ''));
|
||||
};
|
||||
outstr.end = function (c) {
|
||||
if (c) this.write(c);
|
||||
this.emit('end');
|
||||
};
|
||||
|
||||
// these are lacking a few fields that will probably never match
|
||||
var expect = [
|
||||
{
|
||||
'name': 'blammo',
|
||||
'level': 30,
|
||||
'msg': 'bango <ref *1> { bang: \'boom\', KABOOM: [Circular *1] }',
|
||||
'v': 0
|
||||
},
|
||||
{
|
||||
'name': 'blammo',
|
||||
'level': 30,
|
||||
'msg': 'kaboom <ref *1> { bang: \'boom\', KABOOM: [Circular *1] }',
|
||||
'v': 0
|
||||
},
|
||||
{
|
||||
'name': 'blammo',
|
||||
'level': 30,
|
||||
'bang': 'boom',
|
||||
'KABOOM': {
|
||||
'bang': 'boom',
|
||||
'KABOOM': '[Circular]'
|
||||
},
|
||||
'msg': '',
|
||||
'v': 0
|
||||
}
|
||||
];
|
||||
|
||||
var log = new Logger({
|
||||
name: 'blammo',
|
||||
streams: [
|
||||
{
|
||||
type: 'stream',
|
||||
level: 'info',
|
||||
stream: outstr
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
t.test('cycles', function (cb) {
|
||||
outstr.on('end', function () {
|
||||
output.forEach(function (o, i) {
|
||||
// Drop variable parts for comparison.
|
||||
delete o.hostname;
|
||||
delete o.pid;
|
||||
delete o.time;
|
||||
// Hack object/dict comparison: JSONify.
|
||||
try {
|
||||
assert.strictEqual(JSON.stringify(o), JSON.stringify(expect[i]))
|
||||
} catch (err) {
|
||||
cb(err)
|
||||
}
|
||||
});
|
||||
cb()
|
||||
});
|
||||
|
||||
var obj = { bang: 'boom' };
|
||||
obj.KABOOM = obj;
|
||||
log.info('bango', obj);
|
||||
log.info('kaboom', obj.KABOOM);
|
||||
log.info(obj);
|
||||
outstr.end();
|
||||
});
|
|
@ -1,122 +0,0 @@
|
|||
/*
|
||||
* Copyright 2016 Trent Mick
|
||||
*
|
||||
* If available, test dtrace support.
|
||||
*/
|
||||
|
||||
var spawn = require('child_process').spawn;
|
||||
var format = require('util').format;
|
||||
|
||||
var bunyan = require('../lib/bunyan');
|
||||
|
||||
// node-tap API
|
||||
if (require.cache[__dirname + '/tap4nodeunit.js'])
|
||||
delete require.cache[__dirname + '/tap4nodeunit.js'];
|
||||
var tap4nodeunit = require('./tap4nodeunit.js');
|
||||
var after = tap4nodeunit.after;
|
||||
var before = tap4nodeunit.before;
|
||||
var test = tap4nodeunit.test;
|
||||
|
||||
|
||||
// Determine if we can run the dtrace tests.
|
||||
var dtracePlats = ['sunos', 'darwin', 'freebsd'];
|
||||
var runDtraceTests = true;
|
||||
try {
|
||||
require('dtrace-provider');
|
||||
} catch (e) {
|
||||
console.log('# skip dtrace tests: no dtrace-provider module');
|
||||
runDtraceTests = false;
|
||||
}
|
||||
if (!runDtraceTests) {
|
||||
/* pass through */
|
||||
} else if (dtracePlats.indexOf(process.platform) === -1) {
|
||||
console.log('# skip dtrace tests: not on a platform with dtrace');
|
||||
runDtraceTests = false;
|
||||
} else if (process.env.SKIP_DTRACE) {
|
||||
console.log('# skip dtrace tests: SKIP_DTRACE envvar set');
|
||||
runDtraceTests = false;
|
||||
} else if (process.getgid() !== 0) {
|
||||
console.log('# skip dtrace tests: gid is not 0, run with `sudo`');
|
||||
runDtraceTests = false;
|
||||
}
|
||||
if (runDtraceTests) {
|
||||
|
||||
|
||||
test('basic dtrace', function (t) {
|
||||
var argv = ['dtrace', '-Z', '-x', 'strsize=4k', '-qn',
|
||||
'bunyan$target:::log-*{printf("%s", copyinstr(arg0))}',
|
||||
'-c', format('node %s/log-some.js', __dirname)];
|
||||
var dtrace = spawn(argv[0], argv.slice(1));
|
||||
//console.error('ARGV: %j', argv);
|
||||
//console.error('CMD: %s', argv.join(' '));
|
||||
|
||||
var traces = [];
|
||||
dtrace.stdout.on('data', function (data) {
|
||||
//console.error('DTRACE STDOUT:', data.toString());
|
||||
traces.push(data.toString());
|
||||
});
|
||||
dtrace.stderr.on('data', function (data) {
|
||||
console.error('DTRACE STDERR:', data.toString());
|
||||
});
|
||||
dtrace.on('exit', function (code) {
|
||||
t.notOk(code, 'dtrace exited cleanly');
|
||||
traces = traces.join('').split('\n')
|
||||
.filter(function (t) { return t.trim().length })
|
||||
.map(function (t) { return JSON.parse(t) });
|
||||
t.equal(traces.length, 2, 'got 2 log records');
|
||||
if (traces.length) {
|
||||
t.equal(traces[0].level, bunyan.DEBUG);
|
||||
t.equal(traces[0].foo, 'bar');
|
||||
t.equal(traces[1].level, bunyan.TRACE);
|
||||
t.equal(traces[1].msg, 'hi at trace');
|
||||
}
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
/*
|
||||
* Run a logger that logs a couple records every second.
|
||||
* Then run `bunyan -p PID` to capture.
|
||||
* Let those run for a few seconds to ensure dtrace has time to attach and
|
||||
* capture something.
|
||||
*/
|
||||
test('bunyan -p', function (t) {
|
||||
var p = spawn('node', [__dirname + '/log-some-loop.js']);
|
||||
|
||||
var bunyanP = spawn('node',
|
||||
[__dirname + '/../bin/bunyan', '-p', String(p.pid), '-0']);
|
||||
var traces = [];
|
||||
bunyanP.stdout.on('data', function (data) {
|
||||
//console.error('BUNYAN -P STDOUT:', data.toString());
|
||||
traces.push(data.toString());
|
||||
});
|
||||
bunyanP.stderr.on('data', function (data) {
|
||||
console.error('BUNYAN -P STDERR:', data.toString());
|
||||
});
|
||||
bunyanP.on('exit', function (code) {
|
||||
traces = traces.join('').split('\n')
|
||||
.filter(function (t) { return t.trim().length })
|
||||
.map(function (t) { return JSON.parse(t) });
|
||||
t.ok(traces.length >= 3, 'got >=3 log records: ' + traces.length);
|
||||
if (traces.length >= 3) {
|
||||
if (traces[0].level !== bunyan.DEBUG) {
|
||||
traces.shift();
|
||||
}
|
||||
t.equal(traces[0].level, bunyan.DEBUG);
|
||||
t.equal(traces[0].foo, 'bar');
|
||||
t.equal(traces[1].level, bunyan.TRACE);
|
||||
t.equal(traces[1].msg, 'hi at trace');
|
||||
}
|
||||
t.end();
|
||||
});
|
||||
|
||||
// Give it a few seconds to ensure we get some traces.
|
||||
setTimeout(function () {
|
||||
p.kill();
|
||||
bunyanP.kill();
|
||||
}, 5000);
|
||||
});
|
||||
|
||||
|
||||
} /* end of `if (runDtraceTests)` */
|
|
@ -1,151 +0,0 @@
|
|||
/*
|
||||
* Copyright 2016 Trent Mick
|
||||
*
|
||||
* Test emission and handling of 'error' event in a logger with a 'path'
|
||||
* stream.
|
||||
*/
|
||||
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var util = require('util');
|
||||
|
||||
var bunyan = require('../lib/bunyan');
|
||||
|
||||
// node-tap API
|
||||
if (require.cache[__dirname + '/tap4nodeunit.js'])
|
||||
delete require.cache[__dirname + '/tap4nodeunit.js'];
|
||||
var tap4nodeunit = require('./tap4nodeunit.js');
|
||||
var after = tap4nodeunit.after;
|
||||
var before = tap4nodeunit.before;
|
||||
var test = tap4nodeunit.test;
|
||||
|
||||
|
||||
var BOGUS_PATH = '/this/path/is/bogus.log';
|
||||
|
||||
test('error event on file stream (reemitErrorEvents=undefined)', function (t) {
|
||||
var log = bunyan.createLogger(
|
||||
{name: 'error-event-1', streams: [ {path: BOGUS_PATH} ]});
|
||||
log.on('error', function (err, stream) {
|
||||
t.ok(err, 'got err in error event: ' + err);
|
||||
t.equal(err.code, 'ENOENT', 'error code is ENOENT');
|
||||
t.ok(stream, 'got a stream argument');
|
||||
t.equal(stream.path, BOGUS_PATH);
|
||||
t.equal(stream.type, 'file');
|
||||
t.end();
|
||||
});
|
||||
log.info('info log message');
|
||||
});
|
||||
|
||||
test('error event on file stream (reemitErrorEvents=true)', function (t) {
|
||||
var log = bunyan.createLogger({
|
||||
name: 'error-event-2',
|
||||
streams: [ {
|
||||
path: BOGUS_PATH,
|
||||
reemitErrorEvents: true
|
||||
} ]
|
||||
});
|
||||
log.on('error', function (err, stream) {
|
||||
t.ok(err, 'got err in error event: ' + err);
|
||||
t.equal(err.code, 'ENOENT', 'error code is ENOENT');
|
||||
t.ok(stream, 'got a stream argument');
|
||||
t.equal(stream.path, BOGUS_PATH);
|
||||
t.equal(stream.type, 'file');
|
||||
t.end();
|
||||
});
|
||||
log.info('info log message');
|
||||
});
|
||||
|
||||
test('error event on file stream (reemitErrorEvents=false)',
|
||||
function (t) {
|
||||
var log = bunyan.createLogger({
|
||||
name: 'error-event-3',
|
||||
streams: [ {
|
||||
path: BOGUS_PATH,
|
||||
reemitErrorEvents: false
|
||||
} ]
|
||||
});
|
||||
// Hack into the underlying created file stream to catch the error event.
|
||||
log.streams[0].stream.on('error', function (err) {
|
||||
t.ok(err, 'got error event on the file stream');
|
||||
t.end();
|
||||
});
|
||||
log.on('error', function (err, stream) {
|
||||
t.fail('should not have gotten error event on logger');
|
||||
t.end();
|
||||
});
|
||||
log.info('info log message');
|
||||
});
|
||||
|
||||
|
||||
function MyErroringStream() {}
|
||||
util.inherits(MyErroringStream, EventEmitter);
|
||||
MyErroringStream.prototype.write = function (rec) {
|
||||
this.emit('error', new Error('boom'));
|
||||
}
|
||||
|
||||
test('error event on raw stream (reemitErrorEvents=undefined)', function (t) {
|
||||
var estream = new MyErroringStream();
|
||||
var log = bunyan.createLogger({
|
||||
name: 'error-event-raw',
|
||||
streams: [
|
||||
{
|
||||
stream: estream,
|
||||
type: 'raw'
|
||||
}
|
||||
]
|
||||
});
|
||||
estream.on('error', function (err) {
|
||||
t.ok(err, 'got error event on the raw stream');
|
||||
t.end();
|
||||
});
|
||||
log.on('error', function (err, stream) {
|
||||
t.fail('should not have gotten error event on logger');
|
||||
t.end();
|
||||
});
|
||||
log.info('info log message');
|
||||
});
|
||||
|
||||
test('error event on raw stream (reemitErrorEvents=false)', function (t) {
|
||||
var estream = new MyErroringStream();
|
||||
var log = bunyan.createLogger({
|
||||
name: 'error-event-raw',
|
||||
streams: [
|
||||
{
|
||||
stream: estream,
|
||||
type: 'raw',
|
||||
reemitErrorEvents: false
|
||||
}
|
||||
]
|
||||
});
|
||||
estream.on('error', function (err) {
|
||||
t.ok(err, 'got error event on the raw stream');
|
||||
t.end();
|
||||
});
|
||||
log.on('error', function (err, stream) {
|
||||
t.fail('should not have gotten error event on logger');
|
||||
t.end();
|
||||
});
|
||||
log.info('info log message');
|
||||
});
|
||||
|
||||
test('error event on raw stream (reemitErrorEvents=true)', function (t) {
|
||||
var estream = new MyErroringStream();
|
||||
var log = bunyan.createLogger({
|
||||
name: 'error-event-raw',
|
||||
streams: [
|
||||
{
|
||||
stream: estream,
|
||||
type: 'raw',
|
||||
reemitErrorEvents: true
|
||||
}
|
||||
]
|
||||
});
|
||||
log.on('error', function (err, stream) {
|
||||
t.ok(err, 'got err in error event: ' + err);
|
||||
t.equal(err.message, 'boom');
|
||||
t.ok(stream, 'got a stream argument');
|
||||
t.ok(stream.stream instanceof MyErroringStream);
|
||||
t.equal(stream.type, 'raw');
|
||||
t.end();
|
||||
});
|
||||
log.info('info log message');
|
||||
});
|
|
@ -0,0 +1,162 @@
|
|||
/*
|
||||
* Test emission and handling of 'error' event in a logger with a 'path'
|
||||
* stream.
|
||||
*/
|
||||
|
||||
import { EventEmitter } from 'events'
|
||||
import util from 'util'
|
||||
import { Eltro as t, assert} from 'eltro'
|
||||
import bunyan from '../lib/bunyan.mjs'
|
||||
|
||||
|
||||
var BOGUS_PATH = '/this/path/is/bogus.log';
|
||||
|
||||
t.test('error event on file stream (reemitErrorEvents=undefined)', function (cb) {
|
||||
var log = bunyan.createLogger(
|
||||
{name: 'error-event-1', streams: [ {path: BOGUS_PATH} ]});
|
||||
log.on('error', function (err, stream) {
|
||||
try {
|
||||
assert.ok(err, 'got err in error event: ' + err);
|
||||
assert.strictEqual(err.code, 'ENOENT', 'error code is ENOENT');
|
||||
assert.ok(stream, 'got a stream argument');
|
||||
assert.strictEqual(stream.path, BOGUS_PATH);
|
||||
assert.strictEqual(stream.type, 'file');
|
||||
cb()
|
||||
} catch (err) {
|
||||
cb(err)
|
||||
}
|
||||
});
|
||||
log.info('info log message');
|
||||
});
|
||||
|
||||
t.test('error event on file stream (reemitErrorEvents=true)', function (cb) {
|
||||
var log = bunyan.createLogger({
|
||||
name: 'error-event-2',
|
||||
streams: [ {
|
||||
path: BOGUS_PATH,
|
||||
reemitErrorEvents: true
|
||||
} ]
|
||||
});
|
||||
log.on('error', function (err, stream) {
|
||||
try {
|
||||
assert.ok(err, 'got err in error event: ' + err);
|
||||
assert.strictEqual(err.code, 'ENOENT', 'error code is ENOENT');
|
||||
assert.ok(stream, 'got a stream argument');
|
||||
assert.strictEqual(stream.path, BOGUS_PATH);
|
||||
assert.strictEqual(stream.type, 'file');
|
||||
cb()
|
||||
} catch(err) {
|
||||
cb(err)
|
||||
}
|
||||
});
|
||||
log.info('info log message');
|
||||
});
|
||||
|
||||
t.test('error event on file stream (reemitErrorEvents=false)',
|
||||
function (cb) {
|
||||
var log = bunyan.createLogger({
|
||||
name: 'error-event-3',
|
||||
streams: [ {
|
||||
path: BOGUS_PATH,
|
||||
reemitErrorEvents: false
|
||||
} ]
|
||||
});
|
||||
// Hack into the underlying created file stream to catch the error event.
|
||||
log.streams[0].stream.on('error', function (err) {
|
||||
try {
|
||||
assert.ok(err, 'got error event on the file stream');
|
||||
cb()
|
||||
} catch (err) {
|
||||
cb(err)
|
||||
}
|
||||
});
|
||||
log.on('error', function (err, stream) {
|
||||
cb('should not have gotten error event on logger')
|
||||
});
|
||||
log.info('info log message');
|
||||
});
|
||||
|
||||
|
||||
function MyErroringStream() {}
|
||||
util.inherits(MyErroringStream, EventEmitter);
|
||||
MyErroringStream.prototype.write = function (rec) {
|
||||
this.emit('error', new Error('boom'));
|
||||
}
|
||||
|
||||
t.test('error event on raw stream (reemitErrorEvents=undefined)', function (cb) {
|
||||
var estream = new MyErroringStream();
|
||||
var log = bunyan.createLogger({
|
||||
name: 'error-event-raw',
|
||||
streams: [
|
||||
{
|
||||
stream: estream,
|
||||
type: 'raw'
|
||||
}
|
||||
]
|
||||
});
|
||||
estream.on('error', function (err) {
|
||||
try {
|
||||
assert.ok(err, 'got error event on the raw stream');
|
||||
cb()
|
||||
} catch (err) {
|
||||
cb(err)
|
||||
}
|
||||
});
|
||||
log.on('error', function (err, stream) {
|
||||
cb('should not have gotten error event on logger');
|
||||
});
|
||||
log.info('info log message');
|
||||
});
|
||||
|
||||
t.test('error event on raw stream (reemitErrorEvents=false)', function (cb) {
|
||||
var estream = new MyErroringStream();
|
||||
var log = bunyan.createLogger({
|
||||
name: 'error-event-raw',
|
||||
streams: [
|
||||
{
|
||||
stream: estream,
|
||||
type: 'raw',
|
||||
reemitErrorEvents: false
|
||||
}
|
||||
]
|
||||
});
|
||||
estream.on('error', function (err) {
|
||||
try {
|
||||
assert.ok(err, 'got error event on the raw stream');
|
||||
cb()
|
||||
} catch (err) {
|
||||
cb(err)
|
||||
}
|
||||
});
|
||||
log.on('error', function (err, stream) {
|
||||
cb('should not have gotten error event on logger');
|
||||
});
|
||||
log.info('info log message');
|
||||
});
|
||||
|
||||
t.test('error event on raw stream (reemitErrorEvents=true)', function (cb) {
|
||||
var estream = new MyErroringStream();
|
||||
var log = bunyan.createLogger({
|
||||
name: 'error-event-raw',
|
||||
streams: [
|
||||
{
|
||||
stream: estream,
|
||||
type: 'raw',
|
||||
reemitErrorEvents: true
|
||||
}
|
||||
]
|
||||
});
|
||||
log.on('error', function (err, stream) {
|
||||
try {
|
||||
assert.ok(err, 'got err in error event: ' + err);
|
||||
assert.strictEqual(err.message, 'boom');
|
||||
assert.ok(stream, 'got a stream argument');
|
||||
assert.ok(stream.stream instanceof MyErroringStream);
|
||||
assert.strictEqual(stream.type, 'raw');
|
||||
cb()
|
||||
} catch (err) {
|
||||
cb(err)
|
||||
}
|
||||
});
|
||||
log.info('info log message');
|
||||
});
|
|
@ -0,0 +1,29 @@
|
|||
import { exec as ex } from 'child_process'
|
||||
import path from 'path'
|
||||
import { fileURLToPath } from 'url'
|
||||
|
||||
let __dirname = path.dirname(fileURLToPath(import.meta.url))
|
||||
var BUNYAN = path.resolve(__dirname, '../bin/bunyan.mjs');
|
||||
|
||||
export function exec(parameter, prefix = 'node', bunnyboy = BUNYAN) {
|
||||
let command = `${prefix} ${bunnyboy} ${parameter}`
|
||||
return new Promise(function(res, rej) {
|
||||
ex(command,
|
||||
function (err, stdout, stderr) {
|
||||
if (err) {
|
||||
err.stdout = stdout
|
||||
err.stderr = stderr
|
||||
return rej(err)
|
||||
}
|
||||
res({
|
||||
stdout,
|
||||
stderr,
|
||||
})
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
export function dirname(file) {
|
||||
return path.resolve(__dirname + file)
|
||||
}
|
|
@ -1,98 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2014 Trent Mick. All rights reserved.
|
||||
*
|
||||
* Test the `log.level(...)`.
|
||||
*/
|
||||
|
||||
var util = require('util'),
|
||||
format = util.format,
|
||||
inspect = util.inspect;
|
||||
var p = console.log;
|
||||
|
||||
var bunyan = require('../lib/bunyan');
|
||||
|
||||
// node-tap API
|
||||
if (require.cache[__dirname + '/tap4nodeunit.js'])
|
||||
delete require.cache[__dirname + '/tap4nodeunit.js'];
|
||||
var tap4nodeunit = require('./tap4nodeunit.js');
|
||||
var after = tap4nodeunit.after;
|
||||
var before = tap4nodeunit.before;
|
||||
var test = tap4nodeunit.test;
|
||||
|
||||
|
||||
// ---- test boolean `log.<level>()` calls
|
||||
|
||||
var log1 = bunyan.createLogger({
|
||||
name: 'log1',
|
||||
streams: [
|
||||
{
|
||||
path: __dirname + '/level.test.log1.log',
|
||||
level: 'info'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
|
||||
test('log.level() -> level num', function (t) {
|
||||
t.equal(log1.level(), bunyan.INFO);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('log.level(<const>)', function (t) {
|
||||
log1.level(bunyan.DEBUG);
|
||||
t.equal(log1.level(), bunyan.DEBUG);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('log.level(<num>)', function (t) {
|
||||
log1.level(10);
|
||||
t.equal(log1.level(), bunyan.TRACE);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('log.level(<name>)', function (t) {
|
||||
log1.level('error');
|
||||
t.equal(log1.level(), bunyan.ERROR);
|
||||
t.end();
|
||||
});
|
||||
|
||||
// A trick to turn logging off.
|
||||
// See <https://github.com/trentm/node-bunyan/pull/148#issuecomment-53232979>.
|
||||
test('log.level(FATAL + 1)', function (t) {
|
||||
log1.level(bunyan.FATAL + 1);
|
||||
t.equal(log1.level(), bunyan.FATAL + 1);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('log.level(<weird numbers>)', function (t) {
|
||||
log1.level(0);
|
||||
t.equal(log1.level(), 0);
|
||||
log1.level(Number.MAX_VALUE);
|
||||
t.equal(log1.level(), Number.MAX_VALUE);
|
||||
log1.level(Infinity);
|
||||
t.equal(log1.level(), Infinity);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('log.level(<invalid values>)', function (t) {
|
||||
t.throws(function () {
|
||||
var log = bunyan.createLogger({name: 'invalid', level: 'booga'});
|
||||
// JSSTYLED
|
||||
}, /unknown level name: "booga"/);
|
||||
t.throws(function () {
|
||||
var log = bunyan.createLogger({name: 'invalid', level: []});
|
||||
}, /cannot resolve level: invalid arg \(object\): \[\]/);
|
||||
t.throws(function () {
|
||||
var log = bunyan.createLogger({name: 'invalid', level: true});
|
||||
}, /cannot resolve level: invalid arg \(boolean\): true/);
|
||||
t.throws(function () {
|
||||
var log = bunyan.createLogger({name: 'invalid', level: -1});
|
||||
}, /level is not a positive integer: -1/);
|
||||
t.throws(function () {
|
||||
var log = bunyan.createLogger({name: 'invalid', level: 3.14});
|
||||
}, /level is not a positive integer: 3.14/);
|
||||
t.throws(function () {
|
||||
var log = bunyan.createLogger({name: 'invalid', level: -Infinity});
|
||||
}, /level is not a positive integer: -Infinity/);
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Test the `log.level(...)`.
|
||||
*/
|
||||
|
||||
import { Eltro as t, assert} from 'eltro'
|
||||
import bunyan from '../lib/bunyan.mjs'
|
||||
import { dirname } from './helper.mjs'
|
||||
|
||||
|
||||
// ---- test boolean `log.<level>()` calls
|
||||
|
||||
var log1 = bunyan.createLogger({
|
||||
name: 'log1',
|
||||
streams: [
|
||||
{
|
||||
level: 'info',
|
||||
stream: process.stdout,
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
|
||||
t.test('log.level() -> level num', function () {
|
||||
assert.strictEqual(log1.level(), bunyan.INFO);
|
||||
});
|
||||
|
||||
t.test('log.level(<const>)', function () {
|
||||
log1.level(bunyan.DEBUG);
|
||||
assert.strictEqual(log1.level(), bunyan.DEBUG);
|
||||
});
|
||||
|
||||
t.test('log.level(<num>)', function () {
|
||||
log1.level(10);
|
||||
assert.strictEqual(log1.level(), bunyan.TRACE);
|
||||
});
|
||||
|
||||
t.test('log.level(<name>)', function () {
|
||||
log1.level('error');
|
||||
assert.strictEqual(log1.level(), bunyan.ERROR);
|
||||
});
|
||||
|
||||
// A trick to turn logging off.
|
||||
// See <https://github.com/trentm/node-bunyan/pull/148#issuecomment-53232979>.
|
||||
t.test('log.level(FATAL + 1)', function () {
|
||||
log1.level(bunyan.FATAL + 1);
|
||||
assert.strictEqual(log1.level(), bunyan.FATAL + 1);
|
||||
});
|
||||
|
||||
t.test('log.level(<weird numbers>)', function () {
|
||||
log1.level(0);
|
||||
assert.strictEqual(log1.level(), 0);
|
||||
log1.level(Number.MAX_VALUE);
|
||||
assert.strictEqual(log1.level(), Number.MAX_VALUE);
|
||||
log1.level(Infinity);
|
||||
assert.strictEqual(log1.level(), Infinity);
|
||||
});
|
||||
|
||||
t.test('log.level(<invalid values>)', function () {
|
||||
assert.throws(function () {
|
||||
bunyan.createLogger({name: 'invalid', level: 'booga'});
|
||||
}, /unknown level name: "booga"/);
|
||||
assert.throws(function () {
|
||||
bunyan.createLogger({name: 'invalid', level: []});
|
||||
}, /cannot resolve level: invalid arg \(object\): \[\]/);
|
||||
assert.throws(function () {
|
||||
bunyan.createLogger({name: 'invalid', level: true});
|
||||
}, /cannot resolve level: invalid arg \(boolean\): true/);
|
||||
assert.throws(function () {
|
||||
bunyan.createLogger({name: 'invalid', level: -1});
|
||||
}, /level is not a positive integer: -1/);
|
||||
assert.throws(function () {
|
||||
bunyan.createLogger({name: 'invalid', level: 3.14});
|
||||
}, /level is not a positive integer: 3.14/);
|
||||
assert.throws(function () {
|
||||
bunyan.createLogger({name: 'invalid', level: -Infinity});
|
||||
}, /level is not a positive integer: -Infinity/);
|
||||
});
|
|
@ -1,15 +0,0 @@
|
|||
|
||||
// A helper script to log a few times, pause, repeat. We attempt to NOT emit
|
||||
// to stdout or stderr because this is used for dtrace testing
|
||||
// and we don't want to mix output.
|
||||
|
||||
var bunyan = require('../lib/bunyan');
|
||||
var log = bunyan.createLogger({
|
||||
name: 'play',
|
||||
serializers: bunyan.stdSerializers
|
||||
});
|
||||
|
||||
setInterval(function logSome() {
|
||||
log.debug({foo: 'bar'}, 'hi at debug')
|
||||
log.trace('hi at trace')
|
||||
}, 1000);
|
|
@ -1,12 +0,0 @@
|
|||
|
||||
// A helper script to log a few times. We attempt to NOT emit
|
||||
// to stdout or stderr because this is used for dtrace testing
|
||||
// and we don't want to mix output.
|
||||
|
||||
var bunyan = require('../lib/bunyan');
|
||||
var log = bunyan.createLogger({
|
||||
name: 'play',
|
||||
serializers: bunyan.stdSerializers
|
||||
});
|
||||
log.debug({foo: 'bar'}, 'hi at debug')
|
||||
log.trace('hi at trace')
|
271
test/log.test.js
271
test/log.test.js
|
@ -1,271 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2012 Trent Mick. All rights reserved.
|
||||
*
|
||||
* Test the `log.trace(...)`, `log.debug(...)`, ..., `log.fatal(...)` API.
|
||||
*/
|
||||
|
||||
var util = require('util'),
|
||||
format = util.format,
|
||||
inspect = util.inspect;
|
||||
var p = console.log;
|
||||
|
||||
var bunyan = require('../lib/bunyan');
|
||||
|
||||
// node-tap API
|
||||
if (require.cache[__dirname + '/tap4nodeunit.js'])
|
||||
delete require.cache[__dirname + '/tap4nodeunit.js'];
|
||||
var tap4nodeunit = require('./tap4nodeunit.js');
|
||||
var after = tap4nodeunit.after;
|
||||
var before = tap4nodeunit.before;
|
||||
var test = tap4nodeunit.test;
|
||||
|
||||
|
||||
// ---- test boolean `log.<level>()` calls
|
||||
|
||||
var log1 = bunyan.createLogger({
|
||||
name: 'log1',
|
||||
streams: [
|
||||
{
|
||||
path: __dirname + '/log.test.log1.log',
|
||||
level: 'info'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
var log2 = bunyan.createLogger({
|
||||
name: 'log2',
|
||||
streams: [
|
||||
{
|
||||
path: __dirname + '/log.test.log2a.log',
|
||||
level: 'error'
|
||||
},
|
||||
{
|
||||
path: __dirname + '/log.test.log2b.log',
|
||||
level: 'debug'
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
test('log.LEVEL() -> boolean', function (t) {
|
||||
t.equal(log1.trace(), false, 'log1.trace() is false')
|
||||
t.equal(log1.debug(), false)
|
||||
t.equal(log1.info(), true)
|
||||
t.equal(log1.warn(), true)
|
||||
t.equal(log1.error(), true)
|
||||
t.equal(log1.fatal(), true)
|
||||
|
||||
// Level is the *lowest* level of all streams.
|
||||
t.equal(log2.trace(), false)
|
||||
t.equal(log2.debug(), true)
|
||||
t.equal(log2.info(), true)
|
||||
t.equal(log2.warn(), true)
|
||||
t.equal(log2.error(), true)
|
||||
t.equal(log2.fatal(), true)
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
// ---- test `log.<level>(...)` calls which various input types
|
||||
|
||||
function Catcher() {
|
||||
this.records = [];
|
||||
}
|
||||
Catcher.prototype.write = function (record) {
|
||||
this.records.push(record);
|
||||
}
|
||||
var catcher = new Catcher();
|
||||
var log3 = new bunyan.createLogger({
|
||||
name: 'log3',
|
||||
streams: [
|
||||
{
|
||||
type: 'raw',
|
||||
stream: catcher,
|
||||
level: 'trace'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
var names = ['trace', 'debug', 'info', 'warn', 'error', 'fatal'];
|
||||
var fields = {one: 'un'};
|
||||
|
||||
test('log.info(undefined, <msg>)', function (t) {
|
||||
names.forEach(function (lvl) {
|
||||
log3[lvl].call(log3, undefined, 'some message');
|
||||
var rec = catcher.records[catcher.records.length - 1];
|
||||
t.equal(rec.msg, 'undefined \'some message\'',
|
||||
format('log.%s msg is "some message"', lvl));
|
||||
});
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('log.info(<fields>, undefined)', function (t) {
|
||||
names.forEach(function (lvl) {
|
||||
log3[lvl].call(log3, fields, undefined);
|
||||
var rec = catcher.records[catcher.records.length - 1];
|
||||
t.equal(rec.msg, 'undefined',
|
||||
format('log.%s msg: expect "undefined", got %j', lvl, rec.msg));
|
||||
t.equal(rec.one, 'un');
|
||||
});
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('log.info(null, <msg>)', function (t) {
|
||||
names.forEach(function (lvl) {
|
||||
log3[lvl].call(log3, null, 'some message');
|
||||
var rec = catcher.records[catcher.records.length - 1];
|
||||
t.equal(rec.msg, 'some message',
|
||||
format('log.%s msg is "some message"', lvl));
|
||||
});
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('log.info(<fields>, null)', function (t) {
|
||||
names.forEach(function (lvl) {
|
||||
log3[lvl].call(log3, fields, null);
|
||||
var rec = catcher.records[catcher.records.length - 1];
|
||||
t.equal(rec.msg, 'null',
|
||||
format('log.%s msg: expect "null", got %j', lvl, rec.msg));
|
||||
t.equal(rec.one, 'un');
|
||||
});
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('log.info(<str>)', function (t) {
|
||||
names.forEach(function (lvl) {
|
||||
log3[lvl].call(log3, 'some message');
|
||||
var rec = catcher.records[catcher.records.length - 1];
|
||||
t.equal(rec.msg, 'some message',
|
||||
format('log.%s msg is "some message"', lvl));
|
||||
});
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('log.info(<fields>, <str>)', function (t) {
|
||||
names.forEach(function (lvl) {
|
||||
log3[lvl].call(log3, fields, 'some message');
|
||||
var rec = catcher.records[catcher.records.length - 1];
|
||||
t.equal(rec.msg, 'some message',
|
||||
format('log.%s msg: got %j', lvl, rec.msg));
|
||||
t.equal(rec.one, 'un');
|
||||
});
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('log.info(<bool>)', function (t) {
|
||||
names.forEach(function (lvl) {
|
||||
log3[lvl].call(log3, true);
|
||||
var rec = catcher.records[catcher.records.length - 1];
|
||||
t.equal(rec.msg, 'true',
|
||||
format('log.%s msg is "true"', lvl));
|
||||
});
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('log.info(<fields>, <bool>)', function (t) {
|
||||
names.forEach(function (lvl) {
|
||||
log3[lvl].call(log3, fields, true);
|
||||
var rec = catcher.records[catcher.records.length - 1];
|
||||
t.equal(rec.msg, 'true',
|
||||
format('log.%s msg: got %j', lvl, rec.msg));
|
||||
t.equal(rec.one, 'un');
|
||||
});
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('log.info(<num>)', function (t) {
|
||||
names.forEach(function (lvl) {
|
||||
log3[lvl].call(log3, 3.14);
|
||||
var rec = catcher.records[catcher.records.length - 1];
|
||||
t.equal(rec.msg, '3.14',
|
||||
format('log.%s msg: got %j', lvl, rec.msg));
|
||||
});
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('log.info(<fields>, <num>)', function (t) {
|
||||
names.forEach(function (lvl) {
|
||||
log3[lvl].call(log3, fields, 3.14);
|
||||
var rec = catcher.records[catcher.records.length - 1];
|
||||
t.equal(rec.msg, '3.14',
|
||||
format('log.%s msg: got %j', lvl, rec.msg));
|
||||
t.equal(rec.one, 'un');
|
||||
});
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('log.info(<function>)', function (t) {
|
||||
var func = function func1() {};
|
||||
names.forEach(function (lvl) {
|
||||
log3[lvl].call(log3, func);
|
||||
var rec = catcher.records[catcher.records.length - 1];
|
||||
t.equal(rec.msg, '[Function: func1]',
|
||||
format('log.%s msg: got %j', lvl, rec.msg));
|
||||
});
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('log.info(<fields>, <function>)', function (t) {
|
||||
var func = function func2() {};
|
||||
names.forEach(function (lvl) {
|
||||
log3[lvl].call(log3, fields, func);
|
||||
var rec = catcher.records[catcher.records.length - 1];
|
||||
t.equal(rec.msg, '[Function: func2]',
|
||||
format('log.%s msg: got %j', lvl, rec.msg));
|
||||
t.equal(rec.one, 'un');
|
||||
});
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('log.info(<array>)', function (t) {
|
||||
var arr = ['a', 1, {two: 'deux'}];
|
||||
names.forEach(function (lvl) {
|
||||
log3[lvl].call(log3, arr);
|
||||
var rec = catcher.records[catcher.records.length - 1];
|
||||
t.equal(rec.msg, format(arr),
|
||||
format('log.%s msg: got %j', lvl, rec.msg));
|
||||
});
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('log.info(<fields>, <array>)', function (t) {
|
||||
var arr = ['a', 1, {two: 'deux'}];
|
||||
names.forEach(function (lvl) {
|
||||
log3[lvl].call(log3, fields, arr);
|
||||
var rec = catcher.records[catcher.records.length - 1];
|
||||
t.equal(rec.msg, format(arr),
|
||||
format('log.%s msg: got %j', lvl, rec.msg));
|
||||
t.equal(rec.one, 'un');
|
||||
});
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
/*
|
||||
* By accident (starting with trentm/node-bunyan#85 in bunyan@0.23.0),
|
||||
* log.info(null, ...)
|
||||
* was interpreted as `null` being the object of fields. It is gracefully
|
||||
* handled, which is good. However, had I to do it again, I would have made
|
||||
* that interpret `null` as the *message*, and no fields having been passed.
|
||||
* I think it is baked now. It would take a major bunyan rev to change it,
|
||||
* but I don't think it is worth it: passing `null` as the first arg isn't
|
||||
* really an intended way to call these Bunyan methods for either case.
|
||||
*/
|
||||
|
||||
test('log.info(null)', function (t) {
|
||||
names.forEach(function (lvl) {
|
||||
log3[lvl].call(log3, null);
|
||||
var rec = catcher.records[catcher.records.length - 1];
|
||||
t.equal(rec.msg, '', format('log.%s msg: got %j', lvl, rec.msg));
|
||||
});
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('log.info(null, <msg>)', function (t) {
|
||||
names.forEach(function (lvl) {
|
||||
log3[lvl].call(log3, null, 'my message');
|
||||
var rec = catcher.records[catcher.records.length - 1];
|
||||
t.equal(rec.msg, 'my message',
|
||||
format('log.%s msg: got %j', lvl, rec.msg));
|
||||
});
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,225 @@
|
|||
/*
|
||||
* Test the `log.trace(...)`, `log.debug(...)`, ..., `log.fatal(...)` API.
|
||||
*/
|
||||
|
||||
import { Eltro as t, assert} from 'eltro'
|
||||
import { format } from 'util'
|
||||
import bunyan from '../lib/bunyan.mjs'
|
||||
|
||||
// ---- test boolean `log.<level>()` calls
|
||||
|
||||
var log1 = bunyan.createLogger({
|
||||
name: 'log1',
|
||||
streams: [
|
||||
{
|
||||
stream: process.stdout,
|
||||
level: 'info'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
var log2 = bunyan.createLogger({
|
||||
name: 'log2',
|
||||
streams: [
|
||||
{
|
||||
stream: process.stdout,
|
||||
level: 'error'
|
||||
},
|
||||
{
|
||||
stream: process.stdout,
|
||||
level: 'debug'
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
t.test('log.LEVEL() -> boolean', function () {
|
||||
assert.strictEqual(log1.trace(), false, 'log1.trace() is false')
|
||||
assert.strictEqual(log1.debug(), false)
|
||||
assert.strictEqual(log1.info(), true)
|
||||
assert.strictEqual(log1.warn(), true)
|
||||
assert.strictEqual(log1.error(), true)
|
||||
assert.strictEqual(log1.fatal(), true)
|
||||
|
||||
// Level is the *lowest* level of all streams.
|
||||
assert.strictEqual(log2.trace(), false)
|
||||
assert.strictEqual(log2.debug(), true)
|
||||
assert.strictEqual(log2.info(), true)
|
||||
assert.strictEqual(log2.warn(), true)
|
||||
assert.strictEqual(log2.error(), true)
|
||||
assert.strictEqual(log2.fatal(), true)
|
||||
});
|
||||
|
||||
|
||||
// ---- test `log.<level>(...)` calls which various input types
|
||||
|
||||
function Catcher() {
|
||||
this.records = [];
|
||||
}
|
||||
Catcher.prototype.write = function (record) {
|
||||
this.records.push(record);
|
||||
}
|
||||
var catcher = new Catcher();
|
||||
var log3 = new bunyan.createLogger({
|
||||
name: 'log3',
|
||||
streams: [
|
||||
{
|
||||
type: 'raw',
|
||||
stream: catcher,
|
||||
level: 'trace'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
var names = ['trace', 'debug', 'info', 'warn', 'error', 'fatal'];
|
||||
var fields = {one: 'un'};
|
||||
|
||||
t.test('log.info(undefined, <msg>)', function () {
|
||||
names.forEach(function (lvl) {
|
||||
log3[lvl](undefined, 'some message');
|
||||
var rec = catcher.records[catcher.records.length - 1];
|
||||
assert.strictEqual(rec.msg, 'undefined some message');
|
||||
});
|
||||
});
|
||||
|
||||
t.test('log.info(<fields>, undefined)', function () {
|
||||
names.forEach(function (lvl) {
|
||||
log3[lvl](fields, undefined);
|
||||
var rec = catcher.records[catcher.records.length - 1];
|
||||
assert.strictEqual(rec.msg, 'undefined');
|
||||
assert.strictEqual(rec.one, 'un');
|
||||
});
|
||||
});
|
||||
|
||||
t.test('log.info(null, <msg>)', function () {
|
||||
names.forEach(function (lvl) {
|
||||
log3[lvl](null, 'some message');
|
||||
var rec = catcher.records[catcher.records.length - 1];
|
||||
assert.strictEqual(rec.msg, 'some message');
|
||||
});
|
||||
});
|
||||
|
||||
t.test('log.info(<fields>, null)', function () {
|
||||
names.forEach(function (lvl) {
|
||||
log3[lvl](fields, null);
|
||||
var rec = catcher.records[catcher.records.length - 1];
|
||||
assert.strictEqual(rec.msg, 'null');
|
||||
assert.strictEqual(rec.one, 'un');
|
||||
});
|
||||
});
|
||||
|
||||
t.test('log.info(<str>)', function () {
|
||||
names.forEach(function (lvl) {
|
||||
log3[lvl]('some message');
|
||||
var rec = catcher.records[catcher.records.length - 1];
|
||||
assert.strictEqual(rec.msg, 'some message');
|
||||
});
|
||||
});
|
||||
|
||||
t.test('log.info(<fields>, <str>)', function () {
|
||||
names.forEach(function (lvl) {
|
||||
log3[lvl](fields, 'some message');
|
||||
var rec = catcher.records[catcher.records.length - 1];
|
||||
assert.strictEqual(rec.msg, 'some message');
|
||||
assert.strictEqual(rec.one, 'un');
|
||||
});
|
||||
});
|
||||
|
||||
t.test('log.info(<bool>)', function () {
|
||||
names.forEach(function (lvl) {
|
||||
log3[lvl](true);
|
||||
var rec = catcher.records[catcher.records.length - 1];
|
||||
assert.strictEqual(rec.msg, 'true');
|
||||
});
|
||||
});
|
||||
|
||||
t.test('log.info(<fields>, <bool>)', function () {
|
||||
names.forEach(function (lvl) {
|
||||
log3[lvl](fields, true);
|
||||
var rec = catcher.records[catcher.records.length - 1];
|
||||
assert.strictEqual(rec.msg, 'true');
|
||||
assert.strictEqual(rec.one, 'un');
|
||||
});
|
||||
});
|
||||
|
||||
t.test('log.info(<num>)', function () {
|
||||
names.forEach(function (lvl) {
|
||||
log3[lvl](3.14);
|
||||
var rec = catcher.records[catcher.records.length - 1];
|
||||
assert.strictEqual(rec.msg, '3.14');
|
||||
});
|
||||
});
|
||||
|
||||
t.test('log.info(<fields>, <num>)', function () {
|
||||
names.forEach(function (lvl) {
|
||||
log3[lvl](fields, 3.14);
|
||||
var rec = catcher.records[catcher.records.length - 1];
|
||||
assert.strictEqual(rec.msg, '3.14');
|
||||
assert.strictEqual(rec.one, 'un');
|
||||
});
|
||||
});
|
||||
|
||||
t.test('log.info(<function>)', function () {
|
||||
var func = function func1() {};
|
||||
names.forEach(function (lvl) {
|
||||
log3[lvl](func);
|
||||
var rec = catcher.records[catcher.records.length - 1];
|
||||
assert.strictEqual(rec.msg, '[Function: func1]');
|
||||
});
|
||||
});
|
||||
|
||||
t.test('log.info(<fields>, <function>)', function () {
|
||||
var func = function func2() {};
|
||||
names.forEach(function (lvl) {
|
||||
log3[lvl](fields, func);
|
||||
var rec = catcher.records[catcher.records.length - 1];
|
||||
assert.strictEqual(rec.msg, '[Function: func2]');
|
||||
assert.strictEqual(rec.one, 'un');
|
||||
});
|
||||
});
|
||||
|
||||
t.test('log.info(<array>)', function () {
|
||||
var arr = ['a', 1, {two: 'deux'}];
|
||||
names.forEach(function (lvl) {
|
||||
log3[lvl](arr);
|
||||
var rec = catcher.records[catcher.records.length - 1];
|
||||
assert.strictEqual(rec.msg, format(arr));
|
||||
});
|
||||
});
|
||||
|
||||
t.test('log.info(<fields>, <array>)', function () {
|
||||
var arr = ['a', 1, {two: 'deux'}];
|
||||
names.forEach(function (lvl) {
|
||||
log3[lvl](fields, arr);
|
||||
var rec = catcher.records[catcher.records.length - 1];
|
||||
assert.strictEqual(rec.msg, format(arr));
|
||||
assert.strictEqual(rec.one, 'un');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
/*
|
||||
* By accident (starting with trentm/node-bunyan#85 in bunyan@0.23.0),
|
||||
* log.info(null, ...)
|
||||
* was interpreted as `null` being the object of fields. It is gracefully
|
||||
* handled, which is good. However, had I to do it again, I would have made
|
||||
* that interpret `null` as the *message*, and no fields having been passed.
|
||||
* I think it is baked now. It would take a major bunyan rev to change it,
|
||||
* but I don't think it is worth it: passing `null` as the first arg isn't
|
||||
* really an intended way to call these Bunyan methods for either case.
|
||||
*/
|
||||
|
||||
t.test('log.info(null)', function () {
|
||||
names.forEach(function (lvl) {
|
||||
log3[lvl](null);
|
||||
var rec = catcher.records[catcher.records.length - 1];
|
||||
assert.strictEqual(rec.msg, '', format('log.%s msg: got %j', lvl, rec.msg));
|
||||
});
|
||||
});
|
||||
|
||||
t.test('log.info(null, <msg>)', function () {
|
||||
names.forEach(function (lvl) {
|
||||
log3[lvl](null, 'my message');
|
||||
var rec = catcher.records[catcher.records.length - 1];
|
||||
assert.strictEqual(rec.msg, 'my message');
|
||||
});
|
||||
});
|
|
@ -1,42 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2012 Trent Mick. All rights reserved.
|
||||
*
|
||||
* Test other parts of the exported API.
|
||||
*/
|
||||
|
||||
var bunyan = require('../lib/bunyan');
|
||||
|
||||
// node-tap API
|
||||
if (require.cache[__dirname + '/tap4nodeunit.js'])
|
||||
delete require.cache[__dirname + '/tap4nodeunit.js'];
|
||||
var tap4nodeunit = require('./tap4nodeunit.js');
|
||||
var after = tap4nodeunit.after;
|
||||
var before = tap4nodeunit.before;
|
||||
var test = tap4nodeunit.test;
|
||||
|
||||
|
||||
test('bunyan.<LEVEL>s', function (t) {
|
||||
t.ok(bunyan.TRACE, 'TRACE');
|
||||
t.ok(bunyan.DEBUG, 'DEBUG');
|
||||
t.ok(bunyan.INFO, 'INFO');
|
||||
t.ok(bunyan.WARN, 'WARN');
|
||||
t.ok(bunyan.ERROR, 'ERROR');
|
||||
t.ok(bunyan.FATAL, 'FATAL');
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('bunyan.resolveLevel()', function (t) {
|
||||
t.equal(bunyan.resolveLevel('trace'), bunyan.TRACE, 'TRACE');
|
||||
t.equal(bunyan.resolveLevel('TRACE'), bunyan.TRACE, 'TRACE');
|
||||
t.equal(bunyan.resolveLevel('debug'), bunyan.DEBUG, 'DEBUG');
|
||||
t.equal(bunyan.resolveLevel('DEBUG'), bunyan.DEBUG, 'DEBUG');
|
||||
t.equal(bunyan.resolveLevel('info'), bunyan.INFO, 'INFO');
|
||||
t.equal(bunyan.resolveLevel('INFO'), bunyan.INFO, 'INFO');
|
||||
t.equal(bunyan.resolveLevel('warn'), bunyan.WARN, 'WARN');
|
||||
t.equal(bunyan.resolveLevel('WARN'), bunyan.WARN, 'WARN');
|
||||
t.equal(bunyan.resolveLevel('error'), bunyan.ERROR, 'ERROR');
|
||||
t.equal(bunyan.resolveLevel('ERROR'), bunyan.ERROR, 'ERROR');
|
||||
t.equal(bunyan.resolveLevel('fatal'), bunyan.FATAL, 'FATAL');
|
||||
t.equal(bunyan.resolveLevel('FATAL'), bunyan.FATAL, 'FATAL');
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Test other parts of the exported API.
|
||||
*/
|
||||
|
||||
import { Eltro as t, assert} from 'eltro'
|
||||
import bunyan from '../lib/bunyan.mjs'
|
||||
|
||||
|
||||
t.test('bunyan.<LEVEL>s', function () {
|
||||
assert.ok(bunyan.TRACE, 'TRACE');
|
||||
assert.ok(bunyan.DEBUG, 'DEBUG');
|
||||
assert.ok(bunyan.INFO, 'INFO');
|
||||
assert.ok(bunyan.WARN, 'WARN');
|
||||
assert.ok(bunyan.ERROR, 'ERROR');
|
||||
assert.ok(bunyan.FATAL, 'FATAL');
|
||||
});
|
||||
|
||||
t.test('bunyan.resolveLevel()', function () {
|
||||
assert.strictEqual(bunyan.resolveLevel('trace'), bunyan.TRACE, 'TRACE');
|
||||
assert.strictEqual(bunyan.resolveLevel('TRACE'), bunyan.TRACE, 'TRACE');
|
||||
assert.strictEqual(bunyan.resolveLevel('debug'), bunyan.DEBUG, 'DEBUG');
|
||||
assert.strictEqual(bunyan.resolveLevel('DEBUG'), bunyan.DEBUG, 'DEBUG');
|
||||
assert.strictEqual(bunyan.resolveLevel('info'), bunyan.INFO, 'INFO');
|
||||
assert.strictEqual(bunyan.resolveLevel('INFO'), bunyan.INFO, 'INFO');
|
||||
assert.strictEqual(bunyan.resolveLevel('warn'), bunyan.WARN, 'WARN');
|
||||
assert.strictEqual(bunyan.resolveLevel('WARN'), bunyan.WARN, 'WARN');
|
||||
assert.strictEqual(bunyan.resolveLevel('error'), bunyan.ERROR, 'ERROR');
|
||||
assert.strictEqual(bunyan.resolveLevel('ERROR'), bunyan.ERROR, 'ERROR');
|
||||
assert.strictEqual(bunyan.resolveLevel('fatal'), bunyan.FATAL, 'FATAL');
|
||||
assert.strictEqual(bunyan.resolveLevel('FATAL'), bunyan.FATAL, 'FATAL');
|
||||
});
|
|
@ -1,11 +0,0 @@
|
|||
var bunyan = require('../lib/bunyan');
|
||||
var log = bunyan.createLogger({
|
||||
name: 'default',
|
||||
streams: [ {
|
||||
type: 'rotating-file',
|
||||
path: __dirname + '/log.test.rot.log',
|
||||
period: '1d',
|
||||
count: 7
|
||||
} ]
|
||||
});
|
||||
console.log('done');
|
|
@ -1,32 +0,0 @@
|
|||
'use strict';
|
||||
/*
|
||||
* Test that bunyan process will terminate.
|
||||
*
|
||||
* Note: Currently (bunyan 0.23.1) this fails on node 0.8, because there is
|
||||
* no `unref` in node 0.8 and bunyan doesn't yet have `Logger.prototype.close()`
|
||||
* support.
|
||||
*/
|
||||
|
||||
var exec = require('child_process').exec;
|
||||
|
||||
// node-tap API
|
||||
if (require.cache[__dirname + '/tap4nodeunit.js'])
|
||||
delete require.cache[__dirname + '/tap4nodeunit.js'];
|
||||
var tap4nodeunit = require('./tap4nodeunit.js');
|
||||
var test = tap4nodeunit.test;
|
||||
|
||||
var nodeVer = process.versions.node.split('.').map(Number);
|
||||
|
||||
if (nodeVer[0] <= 0 && nodeVer[1] <= 8) {
|
||||
console.warn('skip test (node <= 0.8)');
|
||||
} else {
|
||||
test('log with rotating file stream will terminate', function (t) {
|
||||
exec('node ' +__dirname + '/process-exit.js', {timeout: 1000},
|
||||
function (err, stdout, stderr) {
|
||||
t.ifError(err);
|
||||
t.equal(stdout, 'done\n');
|
||||
t.equal(stderr, '');
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
}
|
|
@ -1,117 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2012 Trent Mick. All rights reserved.
|
||||
*
|
||||
* Test `type: 'raw'` Logger streams.
|
||||
*/
|
||||
|
||||
var format = require('util').format;
|
||||
var Logger = require('../lib/bunyan');
|
||||
|
||||
// node-tap API
|
||||
if (require.cache[__dirname + '/tap4nodeunit.js'])
|
||||
delete require.cache[__dirname + '/tap4nodeunit.js'];
|
||||
var tap4nodeunit = require('./tap4nodeunit.js');
|
||||
var after = tap4nodeunit.after;
|
||||
var before = tap4nodeunit.before;
|
||||
var test = tap4nodeunit.test;
|
||||
|
||||
|
||||
function CapturingStream(recs) {
|
||||
this.recs = recs;
|
||||
}
|
||||
CapturingStream.prototype.write = function (rec) {
|
||||
this.recs.push(rec);
|
||||
}
|
||||
|
||||
|
||||
test('raw stream', function (t) {
|
||||
var recs = [];
|
||||
|
||||
var log = new Logger({
|
||||
name: 'raw-stream-test',
|
||||
streams: [
|
||||
{
|
||||
stream: new CapturingStream(recs),
|
||||
type: 'raw'
|
||||
}
|
||||
]
|
||||
});
|
||||
log.info('first');
|
||||
log.info({two: 'deux'}, 'second');
|
||||
|
||||
t.equal(recs.length, 2);
|
||||
t.equal(typeof (recs[0]), 'object', 'first rec is an object');
|
||||
t.equal(recs[1].two, 'deux', '"two" field made it through');
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('raw streams and regular streams can mix', function (t) {
|
||||
var rawRecs = [];
|
||||
var nonRawRecs = [];
|
||||
|
||||
var log = new Logger({
|
||||
name: 'raw-stream-test',
|
||||
streams: [
|
||||
{
|
||||
stream: new CapturingStream(rawRecs),
|
||||
type: 'raw'
|
||||
},
|
||||
{
|
||||
stream: new CapturingStream(nonRawRecs)
|
||||
}
|
||||
]
|
||||
});
|
||||
log.info('first');
|
||||
log.info({two: 'deux'}, 'second');
|
||||
|
||||
t.equal(rawRecs.length, 2);
|
||||
t.equal(typeof (rawRecs[0]), 'object', 'first rawRec is an object');
|
||||
t.equal(rawRecs[1].two, 'deux', '"two" field made it through');
|
||||
|
||||
t.equal(nonRawRecs.length, 2);
|
||||
t.equal(typeof (nonRawRecs[0]), 'string', 'first nonRawRec is a string');
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('child adding a non-raw stream works', function (t) {
|
||||
var parentRawRecs = [];
|
||||
var rawRecs = [];
|
||||
var nonRawRecs = [];
|
||||
|
||||
var logParent = new Logger({
|
||||
name: 'raw-stream-test',
|
||||
streams: [
|
||||
{
|
||||
stream: new CapturingStream(parentRawRecs),
|
||||
type: 'raw'
|
||||
}
|
||||
]
|
||||
});
|
||||
var logChild = logParent.child({
|
||||
child: true,
|
||||
streams: [
|
||||
{
|
||||
stream: new CapturingStream(rawRecs),
|
||||
type: 'raw'
|
||||
},
|
||||
{
|
||||
stream: new CapturingStream(nonRawRecs)
|
||||
}
|
||||
]
|
||||
});
|
||||
logParent.info('first');
|
||||
logChild.info({two: 'deux'}, 'second');
|
||||
|
||||
t.equal(rawRecs.length, 1,
|
||||
format('rawRecs length should be 1 (is %d)', rawRecs.length));
|
||||
t.equal(typeof (rawRecs[0]), 'object', 'rawRec entry is an object');
|
||||
t.equal(rawRecs[0].two, 'deux', '"two" field made it through');
|
||||
|
||||
t.equal(nonRawRecs.length, 1);
|
||||
t.equal(typeof (nonRawRecs[0]), 'string', 'first nonRawRec is a string');
|
||||
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* Test `type: 'raw'` Logger streams.
|
||||
*/
|
||||
|
||||
import { Eltro as t, assert} from 'eltro'
|
||||
import { format } from 'util'
|
||||
import bunyan from '../lib/bunyan.mjs'
|
||||
|
||||
|
||||
function CapturingStream(recs) {
|
||||
this.recs = recs;
|
||||
}
|
||||
CapturingStream.prototype.write = function (rec) {
|
||||
this.recs.push(rec);
|
||||
}
|
||||
|
||||
|
||||
t.test('raw stream', function () {
|
||||
var recs = [];
|
||||
|
||||
var log = new bunyan({
|
||||
name: 'raw-stream-test',
|
||||
streams: [
|
||||
{
|
||||
stream: new CapturingStream(recs),
|
||||
type: 'raw'
|
||||
}
|
||||
]
|
||||
});
|
||||
log.info('first');
|
||||
log.info({two: 'deux'}, 'second');
|
||||
|
||||
assert.strictEqual(recs.length, 2);
|
||||
assert.strictEqual(typeof (recs[0]), 'object', 'first rec is an object');
|
||||
assert.strictEqual(recs[1].two, 'deux', '"two" field made it through');
|
||||
});
|
||||
|
||||
|
||||
t.test('raw streams and regular streams can mix', function () {
|
||||
var rawRecs = [];
|
||||
var nonRawRecs = [];
|
||||
|
||||
var log = new bunyan({
|
||||
name: 'raw-stream-test',
|
||||
streams: [
|
||||
{
|
||||
stream: new CapturingStream(rawRecs),
|
||||
type: 'raw'
|
||||
},
|
||||
{
|
||||
stream: new CapturingStream(nonRawRecs)
|
||||
}
|
||||
]
|
||||
});
|
||||
log.info('first');
|
||||
log.info({two: 'deux'}, 'second');
|
||||
|
||||
assert.strictEqual(rawRecs.length, 2);
|
||||
assert.strictEqual(typeof (rawRecs[0]), 'object', 'first rawRec is an object');
|
||||
assert.strictEqual(rawRecs[1].two, 'deux', '"two" field made it through');
|
||||
|
||||
assert.strictEqual(nonRawRecs.length, 2);
|
||||
assert.strictEqual(typeof (nonRawRecs[0]), 'string', 'first nonRawRec is a string');
|
||||
|
||||
});
|
||||
|
||||
|
||||
t.test('child adding a non-raw stream works', function () {
|
||||
var parentRawRecs = [];
|
||||
var rawRecs = [];
|
||||
var nonRawRecs = [];
|
||||
|
||||
var logParent = new bunyan({
|
||||
name: 'raw-stream-test',
|
||||
streams: [
|
||||
{
|
||||
stream: new CapturingStream(parentRawRecs),
|
||||
type: 'raw'
|
||||
}
|
||||
]
|
||||
});
|
||||
var logChild = logParent.child({
|
||||
child: true,
|
||||
streams: [
|
||||
{
|
||||
stream: new CapturingStream(rawRecs),
|
||||
type: 'raw'
|
||||
},
|
||||
{
|
||||
stream: new CapturingStream(nonRawRecs)
|
||||
}
|
||||
]
|
||||
});
|
||||
logParent.info('first');
|
||||
logChild.info({two: 'deux'}, 'second');
|
||||
|
||||
assert.strictEqual(rawRecs.length, 1,
|
||||
format('rawRecs length should be 1 (is %d)', rawRecs.length));
|
||||
assert.strictEqual(typeof (rawRecs[0]), 'object', 'rawRec entry is an object');
|
||||
assert.strictEqual(rawRecs[0].two, 'deux', '"two" field made it through');
|
||||
|
||||
assert.strictEqual(nonRawRecs.length, 1);
|
||||
assert.strictEqual(typeof (nonRawRecs[0]), 'string', 'first nonRawRec is a string');
|
||||
|
||||
});
|
|
@ -1,47 +0,0 @@
|
|||
/*
|
||||
* Test the RingBuffer output stream.
|
||||
*/
|
||||
|
||||
var Logger = require('../lib/bunyan');
|
||||
var ringbuffer = new Logger.RingBuffer({ 'limit': 5 });
|
||||
|
||||
// node-tap API
|
||||
if (require.cache[__dirname + '/tap4nodeunit.js'])
|
||||
delete require.cache[__dirname + '/tap4nodeunit.js'];
|
||||
var tap4nodeunit = require('./tap4nodeunit.js');
|
||||
var after = tap4nodeunit.after;
|
||||
var before = tap4nodeunit.before;
|
||||
var test = tap4nodeunit.test;
|
||||
|
||||
|
||||
var log1 = new Logger({
|
||||
name: 'log1',
|
||||
streams: [
|
||||
{
|
||||
stream: ringbuffer,
|
||||
type: 'raw',
|
||||
level: 'info'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
test('ringbuffer', function (t) {
|
||||
log1.info('hello');
|
||||
log1.trace('there');
|
||||
log1.error('android');
|
||||
t.equal(ringbuffer.records.length, 2);
|
||||
t.equal(ringbuffer.records[0]['msg'], 'hello');
|
||||
t.equal(ringbuffer.records[1]['msg'], 'android');
|
||||
log1.error('one');
|
||||
log1.error('two');
|
||||
log1.error('three');
|
||||
t.equal(ringbuffer.records.length, 5);
|
||||
log1.error('four');
|
||||
t.equal(ringbuffer.records.length, 5);
|
||||
t.equal(ringbuffer.records[0]['msg'], 'android');
|
||||
t.equal(ringbuffer.records[1]['msg'], 'one');
|
||||
t.equal(ringbuffer.records[2]['msg'], 'two');
|
||||
t.equal(ringbuffer.records[3]['msg'], 'three');
|
||||
t.equal(ringbuffer.records[4]['msg'], 'four');
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Test the RingBuffer output stream.
|
||||
*/
|
||||
|
||||
import { Eltro as t, assert} from 'eltro'
|
||||
import bunyan from '../lib/bunyan.mjs'
|
||||
var ringbuffer = new bunyan.RingBuffer({ 'limit': 5 });
|
||||
|
||||
var log1 = new bunyan({
|
||||
name: 'log1',
|
||||
streams: [
|
||||
{
|
||||
stream: ringbuffer,
|
||||
type: 'raw',
|
||||
level: 'info'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
t.test('ringbuffer', function () {
|
||||
log1.info('hello');
|
||||
log1.trace('there');
|
||||
log1.error('android');
|
||||
assert.strictEqual(ringbuffer.records.length, 2);
|
||||
assert.strictEqual(ringbuffer.records[0]['msg'], 'hello');
|
||||
assert.strictEqual(ringbuffer.records[1]['msg'], 'android');
|
||||
log1.error('one');
|
||||
log1.error('two');
|
||||
log1.error('three');
|
||||
assert.strictEqual(ringbuffer.records.length, 5);
|
||||
log1.error('four');
|
||||
assert.strictEqual(ringbuffer.records.length, 5);
|
||||
assert.strictEqual(ringbuffer.records[0]['msg'], 'android');
|
||||
assert.strictEqual(ringbuffer.records[1]['msg'], 'one');
|
||||
assert.strictEqual(ringbuffer.records[2]['msg'], 'two');
|
||||
assert.strictEqual(ringbuffer.records[3]['msg'], 'three');
|
||||
assert.strictEqual(ringbuffer.records[4]['msg'], 'four');
|
||||
});
|
|
@ -1,4 +1,4 @@
|
|||
var bunyan = require('../lib/bunyan');
|
||||
import bunyan from '../lib/bunyan.mjs'
|
||||
|
||||
var log = bunyan.createLogger({
|
||||
name: 'safe-json-stringify-1'
|
|
@ -1,5 +1,5 @@
|
|||
import bunyan from '../lib/bunyan.mjs'
|
||||
process.env.BUNYAN_TEST_NO_SAFE_JSON_STRINGIFY = '1';
|
||||
var bunyan = require('../lib/bunyan');
|
||||
|
||||
var log = bunyan.createLogger({
|
||||
name: 'safe-json-stringify-2'
|
|
@ -1,4 +1,4 @@
|
|||
var bunyan = require('../lib/bunyan');
|
||||
import bunyan from '../lib/bunyan.mjs'
|
||||
|
||||
var log = bunyan.createLogger({
|
||||
name: 'safe-json-stringify-3'
|
|
@ -1,5 +1,5 @@
|
|||
import bunyan from '../lib/bunyan.mjs'
|
||||
process.env.BUNYAN_TEST_NO_SAFE_JSON_STRINGIFY = '1';
|
||||
var bunyan = require('../lib/bunyan');
|
||||
|
||||
var log = bunyan.createLogger({
|
||||
name: 'safe-json-stringify-4'
|
|
@ -1,69 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2015 Trent Mick. All rights reserved.
|
||||
*
|
||||
* If available, use `safe-json-stringfy` as a fallback stringifier.
|
||||
* This covers the case where an enumerable property throws an error
|
||||
* in its getter.
|
||||
*
|
||||
* See <https://github.com/trentm/node-bunyan/pull/182>
|
||||
*/
|
||||
|
||||
var p = console.warn;
|
||||
var exec = require('child_process').exec;
|
||||
|
||||
// node-tap API
|
||||
if (require.cache[__dirname + '/tap4nodeunit.js'])
|
||||
delete require.cache[__dirname + '/tap4nodeunit.js'];
|
||||
var tap4nodeunit = require('./tap4nodeunit.js');
|
||||
var after = tap4nodeunit.after;
|
||||
var before = tap4nodeunit.before;
|
||||
var test = tap4nodeunit.test;
|
||||
|
||||
|
||||
test('__defineGetter__ boom', function (t) {
|
||||
var cmd = process.execPath + ' ' + __dirname + '/safe-json-stringify-1.js';
|
||||
exec(cmd, function (err, stdout, stderr) {
|
||||
t.ifError(err, err);
|
||||
var rec = JSON.parse(stdout.trim());
|
||||
t.equal(rec.obj.boom, '[Throws: __defineGetter__ ouch!]');
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
test('__defineGetter__ boom, without safe-json-stringify', function (t) {
|
||||
var cmd = process.execPath + ' ' + __dirname + '/safe-json-stringify-2.js';
|
||||
exec(cmd, function (err, stdout, stderr) {
|
||||
t.ifError(err, err);
|
||||
t.ok(stdout.indexOf('Exception in JSON.stringify') !== -1);
|
||||
t.ok(stderr.indexOf(
|
||||
'You can install the "safe-json-stringify" module') !== -1);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
test('defineProperty boom', function (t) {
|
||||
var cmd = process.execPath + ' ' + __dirname + '/safe-json-stringify-3.js';
|
||||
exec(cmd, function (err, stdout, stderr) {
|
||||
t.ifError(err, err);
|
||||
var recs = stdout.trim().split(/\n/g);
|
||||
t.equal(recs.length, 2);
|
||||
var rec = JSON.parse(recs[0]);
|
||||
t.equal(rec.obj.boom, '[Throws: defineProperty ouch!]');
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
test('defineProperty boom, without safe-json-stringify', function (t) {
|
||||
var cmd = process.execPath + ' ' + __dirname + '/safe-json-stringify-4.js';
|
||||
exec(cmd, function (err, stdout, stderr) {
|
||||
t.ifError(err, err);
|
||||
t.ok(stdout.indexOf('Exception in JSON.stringify') !== -1);
|
||||
t.equal(stdout.match(/Exception in JSON.stringify/g).length, 2);
|
||||
t.ok(stderr.indexOf(
|
||||
'You can install the "safe-json-stringify" module') !== -1);
|
||||
t.equal(stderr.match(
|
||||
/* JSSTYLED */
|
||||
/You can install the "safe-json-stringify" module/g).length, 1);
|
||||
t.end();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* If available, use `safe-json-stringfy` as a fallback stringifier.
|
||||
* This covers the case where an enumerable property throws an error
|
||||
* in its getter.
|
||||
*
|
||||
* See <https://github.com/trentm/node-bunyan/pull/182>
|
||||
*/
|
||||
|
||||
import { Eltro as t, assert} from 'eltro'
|
||||
import { exec, dirname } from './helper.mjs'
|
||||
|
||||
|
||||
t.test('__defineGetter__ boom', async function () {
|
||||
let res = await exec('', 'node', dirname('/safe-json-stringify-1.mjs'))
|
||||
|
||||
var rec = JSON.parse(res.stdout.trim());
|
||||
assert.strictEqual(rec.obj.boom, '[Throws: __defineGetter__ ouch!]');
|
||||
});
|
||||
|
||||
t.test('__defineGetter__ boom, without safe-json-stringify', async function () {
|
||||
let res = await exec('', 'node', dirname('/safe-json-stringify-2.mjs'))
|
||||
assert.ok(res.stdout.indexOf('Exception in JSON.stringify') !== -1);
|
||||
assert.ok(res.stderr.indexOf(
|
||||
'You can install the "safe-json-stringify" module') !== -1);
|
||||
});
|
||||
|
||||
t.test('defineProperty boom', async function () {
|
||||
let res = await exec('', 'node', dirname('/safe-json-stringify-3.mjs'))
|
||||
var recs = res.stdout.trim().split(/\n/g);
|
||||
assert.strictEqual(recs.length, 2);
|
||||
var rec = JSON.parse(recs[0]);
|
||||
assert.strictEqual(rec.obj.boom, '[Throws: defineProperty ouch!]');
|
||||
});
|
||||
|
||||
t.test('defineProperty boom, without safe-json-stringify', async function () {
|
||||
let res = await exec('', 'node', dirname('/safe-json-stringify-4.mjs'))
|
||||
|
||||
assert.ok(res.stdout.indexOf('Exception in JSON.stringify') !== -1);
|
||||
assert.strictEqual(res.stdout.match(/Exception in JSON.stringify/g).length, 2);
|
||||
assert.ok(res.stderr.indexOf(
|
||||
'You can install the "safe-json-stringify" module') !== -1);
|
||||
assert.strictEqual(res.stderr.match(
|
||||
/You can install the "safe-json-stringify" module/g).length, 1);
|
||||
});
|
|
@ -0,0 +1,175 @@
|
|||
import { Eltro as t, assert} from 'eltro'
|
||||
import safeJson from '../lib/safe-json.mjs'
|
||||
|
||||
t.test('basic stringify', function() {
|
||||
assert.strictEqual('"foo"', safeJson('foo'));
|
||||
assert.strictEqual('{"foo":"bar"}', safeJson({foo: 'bar'}));
|
||||
});
|
||||
|
||||
t.test('object identity', function() {
|
||||
var a = { foo: 'bar' };
|
||||
var b = { one: a, two: a };
|
||||
assert.strictEqual('{"one":{"foo":"bar"},"two":{"foo":"bar"}}',safeJson(b));
|
||||
});
|
||||
|
||||
t.test('circular references', function() {
|
||||
var a = {};
|
||||
a.a = a;
|
||||
a.b = 'c';
|
||||
|
||||
assert.doesNotThrow(
|
||||
function() { safeJson(a); },
|
||||
'should not exceed stack size'
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
'{"a":"[Circular]","b":"c"}',
|
||||
safeJson(a)
|
||||
);
|
||||
});
|
||||
|
||||
t.test('null', function() {
|
||||
assert.strictEqual(
|
||||
'{"x":null}',
|
||||
safeJson({x: null})
|
||||
)
|
||||
});
|
||||
|
||||
t.test('arrays', function() {
|
||||
var arr = [ 2 ];
|
||||
assert.strictEqual(
|
||||
'[2]',
|
||||
safeJson(arr)
|
||||
);
|
||||
|
||||
arr.push(arr);
|
||||
|
||||
assert.strictEqual(
|
||||
'[2,"[Circular]"]',
|
||||
safeJson(arr)
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
'{"x":[2,"[Circular]"]}',
|
||||
safeJson({x: arr})
|
||||
);
|
||||
});
|
||||
|
||||
t.test('throwing toJSON', function() {
|
||||
var obj = {
|
||||
toJSON: function() {
|
||||
throw new Error('Failing');
|
||||
}
|
||||
};
|
||||
|
||||
assert.strictEqual(
|
||||
'"[Throws: Failing]"',
|
||||
safeJson(obj)
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
'{"x":"[Throws: Failing]"}',
|
||||
safeJson({ x: obj })
|
||||
);
|
||||
});
|
||||
|
||||
t.test('properties on Object.create(null)', function() {
|
||||
var obj = Object.create(null, {
|
||||
foo: {
|
||||
get: function() { return 'bar'; },
|
||||
enumerable: true
|
||||
}
|
||||
});
|
||||
assert.strictEqual(
|
||||
'{"foo":"bar"}',
|
||||
safeJson(obj)
|
||||
);
|
||||
|
||||
var obj = Object.create(null, {
|
||||
foo: {
|
||||
get: function() { return 'bar'; },
|
||||
enumerable: true
|
||||
},
|
||||
broken: {
|
||||
get: function() { throw new Error('Broken'); },
|
||||
enumerable: true
|
||||
}
|
||||
});
|
||||
assert.strictEqual(
|
||||
'{"foo":"bar","broken":"[Throws: Broken]"}',
|
||||
safeJson(obj)
|
||||
);
|
||||
});
|
||||
|
||||
t.test('defined getter properties using __defineGetter__', function() {
|
||||
// non throwing
|
||||
var obj = {};
|
||||
obj.__defineGetter__('foo', function() { return 'bar'; });
|
||||
assert.strictEqual(
|
||||
'{"foo":"bar"}',
|
||||
safeJson(obj)
|
||||
);
|
||||
|
||||
// throwing
|
||||
obj = {};
|
||||
obj.__defineGetter__('foo', function() { return undefined['oh my']; });
|
||||
|
||||
assert.doesNotThrow(
|
||||
function(){ safeJson(obj)}
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
'{"foo":"[Throws: Cannot read properties of undefined (reading \'oh my\')]"}',
|
||||
safeJson(obj)
|
||||
);
|
||||
});
|
||||
|
||||
t.test('enumerable defined getter properties using Object.defineProperty', function() {
|
||||
// non throwing
|
||||
var obj = {};
|
||||
Object.defineProperty(obj, 'foo', {get: function() { return 'bar'; }, enumerable: true});
|
||||
assert.strictEqual(
|
||||
'{"foo":"bar"}',
|
||||
safeJson(obj)
|
||||
);
|
||||
|
||||
// throwing
|
||||
obj = {};
|
||||
Object.defineProperty(obj, 'foo', {get: function() { return undefined['oh my']; }, enumerable: true});
|
||||
|
||||
assert.doesNotThrow(
|
||||
function(){ safeJson(obj)}
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
'{"foo":"[Throws: Cannot read properties of undefined (reading \'oh my\')]"}',
|
||||
safeJson(obj)
|
||||
);
|
||||
});
|
||||
|
||||
t.test('formatting', function() {
|
||||
var obj = {a:{b:1, c:[{d: 1}]}}; // some nested object
|
||||
var formatters = [3, "\t", " "];
|
||||
formatters.forEach((formatter) => {
|
||||
assert.strictEqual(
|
||||
JSON.stringify(obj, null, formatter),
|
||||
safeJson(obj, null, formatter)
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
t.test('replacing', function() {
|
||||
var obj = {a:{b:1, c:[{d: 1}]}}; // some nested object
|
||||
var replacers = [
|
||||
["a", "c"],
|
||||
(k, v) => typeof v == 'number' ? "***" : v,
|
||||
() => undefined,
|
||||
[]
|
||||
];
|
||||
replacers.forEach((replacer) => {
|
||||
assert.strictEqual(
|
||||
JSON.stringify(obj, replacer),
|
||||
safeJson(obj, replacer)
|
||||
);
|
||||
});
|
||||
});
|
|
@ -1,334 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2012 Trent Mick. All rights reserved.
|
||||
*
|
||||
* Test the standard serializers in Bunyan.
|
||||
*/
|
||||
|
||||
var http = require('http');
|
||||
|
||||
var bunyan = require('../lib/bunyan');
|
||||
var verror = require('verror');
|
||||
|
||||
// node-tap API
|
||||
if (require.cache[__dirname + '/tap4nodeunit.js'])
|
||||
delete require.cache[__dirname + '/tap4nodeunit.js'];
|
||||
var tap4nodeunit = require('./tap4nodeunit.js');
|
||||
var after = tap4nodeunit.after;
|
||||
var before = tap4nodeunit.before;
|
||||
var test = tap4nodeunit.test;
|
||||
|
||||
|
||||
function CapturingStream(recs) {
|
||||
this.recs = recs;
|
||||
}
|
||||
CapturingStream.prototype.write = function (rec) {
|
||||
this.recs.push(rec);
|
||||
}
|
||||
|
||||
|
||||
test('req serializer', function (t) {
|
||||
var records = [];
|
||||
var log = bunyan.createLogger({
|
||||
name: 'serializer-test',
|
||||
streams: [
|
||||
{
|
||||
stream: new CapturingStream(records),
|
||||
type: 'raw'
|
||||
}
|
||||
],
|
||||
serializers: {
|
||||
req: bunyan.stdSerializers.req
|
||||
}
|
||||
});
|
||||
|
||||
// None of these should blow up.
|
||||
var bogusReqs = [
|
||||
undefined,
|
||||
null,
|
||||
{},
|
||||
1,
|
||||
'string',
|
||||
[1, 2, 3],
|
||||
{'foo':'bar'}
|
||||
];
|
||||
for (var i = 0; i < bogusReqs.length; i++) {
|
||||
log.info({req: bogusReqs[i]}, 'hi');
|
||||
t.equal(records[i].req, bogusReqs[i]);
|
||||
}
|
||||
|
||||
// Get http request and response objects to play with and test.
|
||||
var theReq, theRes;
|
||||
var server = http.createServer(function (req, res) {
|
||||
theReq = req;
|
||||
theRes = res;
|
||||
res.writeHead(200, {'Content-Type': 'text/plain'});
|
||||
res.end('Hello World\n');
|
||||
})
|
||||
server.listen(8765, function () {
|
||||
http.get({host: '127.0.0.1', port: 8765, path: '/'}, function (res) {
|
||||
res.resume();
|
||||
log.info({req: theReq}, 'the request');
|
||||
var lastRecord = records[records.length-1];
|
||||
t.equal(lastRecord.req.method, 'GET');
|
||||
t.equal(lastRecord.req.url, theReq.url);
|
||||
t.equal(lastRecord.req.remoteAddress,
|
||||
theReq.connection.remoteAddress);
|
||||
t.equal(lastRecord.req.remotePort, theReq.connection.remotePort);
|
||||
server.close();
|
||||
t.end();
|
||||
}).on('error', function (err) {
|
||||
t.ok(false, 'error requesting to our test server: ' + err);
|
||||
server.close();
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
test('res serializer', function (t) {
|
||||
var records = [];
|
||||
var log = bunyan.createLogger({
|
||||
name: 'serializer-test',
|
||||
streams: [
|
||||
{
|
||||
stream: new CapturingStream(records),
|
||||
type: 'raw'
|
||||
}
|
||||
],
|
||||
serializers: {
|
||||
res: bunyan.stdSerializers.res
|
||||
}
|
||||
});
|
||||
|
||||
// None of these should blow up.
|
||||
var bogusRess = [
|
||||
undefined,
|
||||
null,
|
||||
{},
|
||||
1,
|
||||
'string',
|
||||
[1, 2, 3],
|
||||
{'foo':'bar'}
|
||||
];
|
||||
for (var i = 0; i < bogusRess.length; i++) {
|
||||
log.info({res: bogusRess[i]}, 'hi');
|
||||
t.equal(records[i].res, bogusRess[i]);
|
||||
}
|
||||
|
||||
// Get http request and response objects to play with and test.
|
||||
var theReq, theRes;
|
||||
var server = http.createServer(function (req, res) {
|
||||
theReq = req;
|
||||
theRes = res;
|
||||
res.writeHead(200, {'Content-Type': 'text/plain'});
|
||||
res.end('Hello World\n');
|
||||
})
|
||||
server.listen(8765, function () {
|
||||
http.get({host: '127.0.0.1', port: 8765, path: '/'}, function (res) {
|
||||
res.resume();
|
||||
log.info({res: theRes}, 'the response');
|
||||
var lastRecord = records[records.length-1];
|
||||
t.equal(lastRecord.res.statusCode, theRes.statusCode);
|
||||
t.equal(lastRecord.res.header, theRes._header);
|
||||
server.close();
|
||||
t.end();
|
||||
}).on('error', function (err) {
|
||||
t.ok(false, 'error requesting to our test server: ' + err);
|
||||
server.close();
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
test('err serializer', function (t) {
|
||||
var records = [];
|
||||
var log = bunyan.createLogger({
|
||||
name: 'serializer-test',
|
||||
streams: [
|
||||
{
|
||||
stream: new CapturingStream(records),
|
||||
type: 'raw'
|
||||
}
|
||||
],
|
||||
serializers: {
|
||||
err: bunyan.stdSerializers.err
|
||||
}
|
||||
});
|
||||
|
||||
// None of these should blow up.
|
||||
var bogusErrs = [
|
||||
undefined,
|
||||
null,
|
||||
{},
|
||||
1,
|
||||
'string',
|
||||
[1, 2, 3],
|
||||
{'foo':'bar'}
|
||||
];
|
||||
for (var i = 0; i < bogusErrs.length; i++) {
|
||||
log.info({err: bogusErrs[i]}, 'hi');
|
||||
t.equal(records[i].err, bogusErrs[i]);
|
||||
}
|
||||
|
||||
var theErr = new TypeError('blah');
|
||||
|
||||
log.info(theErr, 'the error');
|
||||
var lastRecord = records[records.length-1];
|
||||
t.equal(lastRecord.err.message, theErr.message);
|
||||
t.equal(lastRecord.err.name, theErr.name);
|
||||
t.equal(lastRecord.err.stack, theErr.stack);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('err serializer: custom serializer', function (t) {
|
||||
var records = [];
|
||||
|
||||
function customSerializer(err) {
|
||||
return {
|
||||
message: err.message,
|
||||
name: err.name,
|
||||
stack: err.stack,
|
||||
beep: err.beep
|
||||
};
|
||||
}
|
||||
|
||||
var log = bunyan.createLogger({
|
||||
name: 'serializer-test',
|
||||
streams: [
|
||||
{
|
||||
stream: new CapturingStream(records),
|
||||
type: 'raw'
|
||||
}
|
||||
],
|
||||
serializers: {
|
||||
err: customSerializer
|
||||
}
|
||||
});
|
||||
|
||||
var e1 = new Error('message1');
|
||||
e1.beep = 'bop';
|
||||
var e2 = new Error('message2');
|
||||
var errs = [e1, e2];
|
||||
|
||||
for (var i = 0; i < errs.length; i++) {
|
||||
log.info(errs[i]);
|
||||
t.equal(records[i].err.message, errs[i].message);
|
||||
t.equal(records[i].err.beep, errs[i].beep);
|
||||
}
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('err serializer: long stack', function (t) {
|
||||
var records = [];
|
||||
var log = bunyan.createLogger({
|
||||
name: 'serializer-test',
|
||||
streams: [ {
|
||||
stream: new CapturingStream(records),
|
||||
type: 'raw'
|
||||
} ],
|
||||
serializers: {
|
||||
err: bunyan.stdSerializers.err
|
||||
}
|
||||
});
|
||||
|
||||
var topErr, midErr, bottomErr;
|
||||
|
||||
// Just a VError.
|
||||
topErr = new verror.VError('top err');
|
||||
log.info(topErr, 'the error');
|
||||
var lastRecord = records[records.length-1];
|
||||
t.equal(lastRecord.err.message, topErr.message, 'Just a VError');
|
||||
t.equal(lastRecord.err.name, topErr.name, 'Just a VError');
|
||||
t.equal(lastRecord.err.stack, topErr.stack, 'Just a VError');
|
||||
|
||||
// Just a WError.
|
||||
topErr = new verror.WError('top err');
|
||||
log.info(topErr, 'the error');
|
||||
var lastRecord = records[records.length-1];
|
||||
t.equal(lastRecord.err.message, topErr.message, 'Just a WError');
|
||||
t.equal(lastRecord.err.name, topErr.name, 'Just a WError');
|
||||
t.equal(lastRecord.err.stack, topErr.stack, 'Just a WError');
|
||||
|
||||
// WError <- TypeError
|
||||
bottomErr = new TypeError('bottom err');
|
||||
topErr = new verror.WError(bottomErr, 'top err');
|
||||
log.info(topErr, 'the error');
|
||||
var lastRecord = records[records.length-1];
|
||||
t.equal(lastRecord.err.message, topErr.message, 'WError <- TypeError');
|
||||
t.equal(lastRecord.err.name, topErr.name, 'WError <- TypeError');
|
||||
var expectedStack = topErr.stack + '\nCaused by: ' + bottomErr.stack;
|
||||
t.equal(lastRecord.err.stack, expectedStack, 'WError <- TypeError');
|
||||
|
||||
// WError <- WError
|
||||
bottomErr = new verror.WError('bottom err');
|
||||
topErr = new verror.WError(bottomErr, 'top err');
|
||||
log.info(topErr, 'the error');
|
||||
var lastRecord = records[records.length-1];
|
||||
t.equal(lastRecord.err.message, topErr.message, 'WError <- WError');
|
||||
t.equal(lastRecord.err.name, topErr.name, 'WError <- WError');
|
||||
var expectedStack = topErr.stack + '\nCaused by: ' + bottomErr.stack;
|
||||
t.equal(lastRecord.err.stack, expectedStack, 'WError <- WError');
|
||||
|
||||
// WError <- WError <- TypeError
|
||||
bottomErr = new TypeError('bottom err');
|
||||
midErr = new verror.WError(bottomErr, 'mid err');
|
||||
topErr = new verror.WError(midErr, 'top err');
|
||||
log.info(topErr, 'the error');
|
||||
var lastRecord = records[records.length-1];
|
||||
t.equal(lastRecord.err.message, topErr.message,
|
||||
'WError <- WError <- TypeError');
|
||||
t.equal(lastRecord.err.name, topErr.name, 'WError <- WError <- TypeError');
|
||||
var expectedStack = (topErr.stack
|
||||
+ '\nCaused by: ' + midErr.stack
|
||||
+ '\nCaused by: ' + bottomErr.stack);
|
||||
t.equal(lastRecord.err.stack, expectedStack,
|
||||
'WError <- WError <- TypeError');
|
||||
|
||||
// WError <- WError <- WError
|
||||
bottomErr = new verror.WError('bottom err');
|
||||
midErr = new verror.WError(bottomErr, 'mid err');
|
||||
topErr = new verror.WError(midErr, 'top err');
|
||||
log.info(topErr, 'the error');
|
||||
var lastRecord = records[records.length-1];
|
||||
t.equal(lastRecord.err.message, topErr.message,
|
||||
'WError <- WError <- WError');
|
||||
t.equal(lastRecord.err.name, topErr.name, 'WError <- WError <- WError');
|
||||
var expectedStack = (topErr.stack
|
||||
+ '\nCaused by: ' + midErr.stack
|
||||
+ '\nCaused by: ' + bottomErr.stack);
|
||||
t.equal(lastRecord.err.stack, expectedStack, 'WError <- WError <- WError');
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
// Bunyan 0.18.3 introduced a bug where *all* serializers are applied
|
||||
// even if the log record doesn't have the associated key. That means
|
||||
// serializers that don't handle an `undefined` value will blow up.
|
||||
test('do not apply serializers if no record key', function (t) {
|
||||
var records = [];
|
||||
var log = bunyan.createLogger({
|
||||
name: 'serializer-test',
|
||||
streams: [ {
|
||||
stream: new CapturingStream(records),
|
||||
type: 'raw'
|
||||
} ],
|
||||
serializers: {
|
||||
err: bunyan.stdSerializers.err,
|
||||
boom: function (value) {
|
||||
throw new Error('boom');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
log.info({foo: 'bar'}, 'record one');
|
||||
log.info({err: new Error('record two err')}, 'record two');
|
||||
|
||||
t.equal(records[0].boom, undefined);
|
||||
t.equal(records[0].foo, 'bar');
|
||||
t.equal(records[1].boom, undefined);
|
||||
t.equal(records[1].err.message, 'record two err');
|
||||
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,240 @@
|
|||
/*
|
||||
* Test the standard serializers in Bunyan.
|
||||
*/
|
||||
|
||||
import http from 'http'
|
||||
import { Eltro as t, assert} from 'eltro'
|
||||
import bunyan from '../lib/bunyan.mjs'
|
||||
|
||||
function CapturingStream(recs) {
|
||||
this.recs = recs;
|
||||
}
|
||||
CapturingStream.prototype.write = function (rec) {
|
||||
this.recs.push(rec);
|
||||
}
|
||||
|
||||
t.test('req serializer', function (cb) {
|
||||
var records = [];
|
||||
var log = bunyan.createLogger({
|
||||
name: 'serializer-test',
|
||||
streams: [
|
||||
{
|
||||
stream: new CapturingStream(records),
|
||||
type: 'raw'
|
||||
}
|
||||
],
|
||||
serializers: {
|
||||
req: bunyan.stdSerializers.req
|
||||
}
|
||||
});
|
||||
|
||||
// None of these should blow up.
|
||||
var bogusReqs = [
|
||||
undefined,
|
||||
null,
|
||||
{},
|
||||
1,
|
||||
'string',
|
||||
[1, 2, 3],
|
||||
{'foo':'bar'}
|
||||
];
|
||||
for (var i = 0; i < bogusReqs.length; i++) {
|
||||
log.info({req: bogusReqs[i]}, 'hi');
|
||||
assert.strictEqual(records[i].req, bogusReqs[i]);
|
||||
}
|
||||
|
||||
// Get http request and response objects to play with and test.
|
||||
var theReq, theRes;
|
||||
var server = http.createServer(function (req, res) {
|
||||
theReq = req;
|
||||
theRes = res;
|
||||
res.writeHead(200, {'Content-Type': 'text/plain'});
|
||||
res.end('Hello World\n');
|
||||
})
|
||||
server.listen(8765, function () {
|
||||
http.get({host: '127.0.0.1', port: 8765, path: '/'}, function (res) {
|
||||
res.resume();
|
||||
log.info({req: theReq}, 'the request');
|
||||
var lastRecord = records[records.length-1];
|
||||
|
||||
try {
|
||||
assert.strictEqual(lastRecord.req.method, 'GET');
|
||||
assert.strictEqual(lastRecord.req.url, theReq.url);
|
||||
assert.strictEqual(lastRecord.req.remoteAddress,
|
||||
theReq.connection.remoteAddress);
|
||||
assert.strictEqual(lastRecord.req.remotePort, theReq.connection.remotePort);
|
||||
} catch (err) {
|
||||
cb(err)
|
||||
}
|
||||
server.close();
|
||||
cb()
|
||||
}).on('error', function (err) {
|
||||
server.close();
|
||||
cb(err)
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
t.test('res serializer', function (cb) {
|
||||
var records = [];
|
||||
var log = bunyan.createLogger({
|
||||
name: 'serializer-test',
|
||||
streams: [
|
||||
{
|
||||
stream: new CapturingStream(records),
|
||||
type: 'raw'
|
||||
}
|
||||
],
|
||||
serializers: {
|
||||
res: bunyan.stdSerializers.res
|
||||
}
|
||||
});
|
||||
|
||||
// None of these should blow up.
|
||||
var bogusRess = [
|
||||
undefined,
|
||||
null,
|
||||
{},
|
||||
1,
|
||||
'string',
|
||||
[1, 2, 3],
|
||||
{'foo':'bar'}
|
||||
];
|
||||
for (var i = 0; i < bogusRess.length; i++) {
|
||||
log.info({res: bogusRess[i]}, 'hi');
|
||||
assert.strictEqual(records[i].res, bogusRess[i]);
|
||||
}
|
||||
|
||||
// Get http request and response objects to play with and test.
|
||||
var theReq, theRes;
|
||||
var server = http.createServer(function (req, res) {
|
||||
theReq = req;
|
||||
theRes = res;
|
||||
res.writeHead(200, {'Content-Type': 'text/plain'});
|
||||
res.end('Hello World\n');
|
||||
})
|
||||
server.listen(8765, function () {
|
||||
http.get({host: '127.0.0.1', port: 8765, path: '/'}, function (res) {
|
||||
res.resume();
|
||||
log.info({res: theRes}, 'the response');
|
||||
var lastRecord = records[records.length-1];
|
||||
try {
|
||||
assert.strictEqual(lastRecord.res.statusCode, theRes.statusCode);
|
||||
assert.strictEqual(lastRecord.res.header, theRes._header);
|
||||
} catch (err) {
|
||||
cb(err)
|
||||
}
|
||||
server.close();
|
||||
cb()
|
||||
}).on('error', function (err) {
|
||||
server.close();
|
||||
cb(err)
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
t.test('err serializer', function () {
|
||||
var records = [];
|
||||
var log = bunyan.createLogger({
|
||||
name: 'serializer-test',
|
||||
streams: [
|
||||
{
|
||||
stream: new CapturingStream(records),
|
||||
type: 'raw'
|
||||
}
|
||||
],
|
||||
serializers: {
|
||||
err: bunyan.stdSerializers.err
|
||||
}
|
||||
});
|
||||
|
||||
// None of these should blow up.
|
||||
var bogusErrs = [
|
||||
undefined,
|
||||
null,
|
||||
{},
|
||||
1,
|
||||
'string',
|
||||
[1, 2, 3],
|
||||
{'foo':'bar'}
|
||||
];
|
||||
for (var i = 0; i < bogusErrs.length; i++) {
|
||||
log.info({err: bogusErrs[i]}, 'hi');
|
||||
assert.strictEqual(records[i].err, bogusErrs[i]);
|
||||
}
|
||||
|
||||
var theErr = new TypeError('blah');
|
||||
|
||||
log.info(theErr, 'the error');
|
||||
var lastRecord = records[records.length-1];
|
||||
assert.strictEqual(lastRecord.err.message, theErr.message);
|
||||
assert.strictEqual(lastRecord.err.name, theErr.name);
|
||||
assert.strictEqual(lastRecord.err.stack, theErr.stack);
|
||||
});
|
||||
|
||||
t.test('err serializer: custom serializer', function () {
|
||||
var records = [];
|
||||
|
||||
function customSerializer(err) {
|
||||
return {
|
||||
message: err.message,
|
||||
name: err.name,
|
||||
stack: err.stack,
|
||||
beep: err.beep
|
||||
};
|
||||
}
|
||||
|
||||
var log = bunyan.createLogger({
|
||||
name: 'serializer-test',
|
||||
streams: [
|
||||
{
|
||||
stream: new CapturingStream(records),
|
||||
type: 'raw'
|
||||
}
|
||||
],
|
||||
serializers: {
|
||||
err: customSerializer
|
||||
}
|
||||
});
|
||||
|
||||
var e1 = new Error('message1');
|
||||
e1.beep = 'bop';
|
||||
var e2 = new Error('message2');
|
||||
var errs = [e1, e2];
|
||||
|
||||
for (var i = 0; i < errs.length; i++) {
|
||||
log.info(errs[i]);
|
||||
assert.strictEqual(records[i].err.message, errs[i].message);
|
||||
assert.strictEqual(records[i].err.beep, errs[i].beep);
|
||||
}
|
||||
});
|
||||
|
||||
// Bunyan 0.18.3 introduced a bug where *all* serializers are applied
|
||||
// even if the log record doesn't have the associated key. That means
|
||||
// serializers that don't handle an `undefined` value will blow up.
|
||||
t.test('do not apply serializers if no record key', function () {
|
||||
var records = [];
|
||||
var log = bunyan.createLogger({
|
||||
name: 'serializer-test',
|
||||
streams: [ {
|
||||
stream: new CapturingStream(records),
|
||||
type: 'raw'
|
||||
} ],
|
||||
serializers: {
|
||||
err: bunyan.stdSerializers.err,
|
||||
boom: function (value) {
|
||||
throw new Error('boom');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
log.info({foo: 'bar'}, 'record one');
|
||||
log.info({err: new Error('record two err')}, 'record two');
|
||||
|
||||
assert.strictEqual(records[0].boom, undefined);
|
||||
assert.strictEqual(records[0].foo, 'bar');
|
||||
assert.strictEqual(records[1].boom, undefined);
|
||||
assert.strictEqual(records[1].err.message, 'record two err');
|
||||
});
|
|
@ -1,62 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2015 Trent Mick.
|
||||
*
|
||||
* Test `src: true` usage.
|
||||
*/
|
||||
|
||||
// Intentionally on line 8 for tests below:
|
||||
function logSomething(log) { log.info('something'); }
|
||||
|
||||
|
||||
var format = require('util').format;
|
||||
var Logger = require('../lib/bunyan');
|
||||
|
||||
// node-tap API
|
||||
if (require.cache[__dirname + '/tap4nodeunit.js'])
|
||||
delete require.cache[__dirname + '/tap4nodeunit.js'];
|
||||
var tap4nodeunit = require('./tap4nodeunit.js');
|
||||
var after = tap4nodeunit.after;
|
||||
var before = tap4nodeunit.before;
|
||||
var test = tap4nodeunit.test;
|
||||
|
||||
|
||||
function CapturingStream(recs) {
|
||||
this.recs = recs;
|
||||
}
|
||||
CapturingStream.prototype.write = function (rec) {
|
||||
this.recs.push(rec);
|
||||
}
|
||||
|
||||
|
||||
test('src', function (t) {
|
||||
var recs = [];
|
||||
|
||||
var log = new Logger({
|
||||
name: 'src-test',
|
||||
src: true,
|
||||
streams: [
|
||||
{
|
||||
stream: new CapturingStream(recs),
|
||||
type: 'raw'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
log.info('top-level');
|
||||
logSomething(log);
|
||||
|
||||
t.equal(recs.length, 2);
|
||||
recs.forEach(function (rec) {
|
||||
t.ok(rec.src);
|
||||
t.equal(typeof (rec.src), 'object');
|
||||
t.equal(rec.src.file, __filename);
|
||||
t.ok(rec.src.line);
|
||||
t.equal(typeof (rec.src.line), 'number');
|
||||
});
|
||||
var rec = recs[1];
|
||||
t.ok(rec.src.func);
|
||||
t.equal(rec.src.func, 'logSomething');
|
||||
t.equal(rec.src.line, 8);
|
||||
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Test `src: true` usage.
|
||||
*/
|
||||
|
||||
import { Eltro as t, assert} from 'eltro'
|
||||
import bunyan from '../lib/bunyan.mjs'
|
||||
|
||||
// Intentionally on line 11 for tests below:
|
||||
function logSomething(log) { log.info('something'); }
|
||||
|
||||
function CapturingStream(recs) {
|
||||
this.recs = recs;
|
||||
}
|
||||
CapturingStream.prototype.write = function (rec) {
|
||||
this.recs.push(rec);
|
||||
}
|
||||
|
||||
// commented out due to broken implementation of
|
||||
// getting caller line in strict mode (getCaller3Info())
|
||||
|
||||
/* t.only().test('src', function () {
|
||||
var recs = [];
|
||||
|
||||
var log = new bunyan({
|
||||
name: 'src-test',
|
||||
src: true,
|
||||
streams: [
|
||||
{
|
||||
stream: new CapturingStream(recs),
|
||||
type: 'raw'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
log.info('top-level');
|
||||
logSomething(log);
|
||||
|
||||
assert.strictEqual(recs.length, 2);
|
||||
recs.forEach(function (rec) {
|
||||
assert.ok(rec.src);
|
||||
assert.strictEqual(typeof (rec.src), 'object');
|
||||
assert.strictEqual(rec.src.file, __filename);
|
||||
assert.ok(rec.src.line);
|
||||
assert.strictEqual(typeof (rec.src.line), 'number');
|
||||
});
|
||||
var rec = recs[1];
|
||||
assert.ok(rec.src.func);
|
||||
assert.strictEqual(rec.src.func, 'logSomething');
|
||||
assert.strictEqual(rec.src.line, 11);
|
||||
});*/
|
|
@ -1,184 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2015 Trent Mick. All rights reserved.
|
||||
*
|
||||
* Test that streams (the various way they can be added to
|
||||
* a Logger instance) get the appropriate level.
|
||||
*/
|
||||
|
||||
var util = require('util'),
|
||||
format = util.format,
|
||||
inspect = util.inspect;
|
||||
var p = console.log;
|
||||
|
||||
var bunyan = require('../lib/bunyan');
|
||||
|
||||
// node-tap API
|
||||
if (require.cache[__dirname + '/tap4nodeunit.js'])
|
||||
delete require.cache[__dirname + '/tap4nodeunit.js'];
|
||||
var tap4nodeunit = require('./tap4nodeunit.js');
|
||||
var test = tap4nodeunit.test;
|
||||
|
||||
|
||||
// ---- Tests
|
||||
|
||||
var log1 = bunyan.createLogger({
|
||||
name: 'log1',
|
||||
streams: [
|
||||
{
|
||||
path: __dirname + '/level.test.log1.log',
|
||||
level: 'info'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
|
||||
test('default stream log level', function (t) {
|
||||
var log = bunyan.createLogger({
|
||||
name: 'foo'
|
||||
});
|
||||
t.equal(log.level(), bunyan.INFO);
|
||||
t.equal(log.streams[0].level, bunyan.INFO);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('default stream, level=DEBUG specified', function (t) {
|
||||
var log = bunyan.createLogger({
|
||||
name: 'foo',
|
||||
level: bunyan.DEBUG
|
||||
});
|
||||
t.equal(log.level(), bunyan.DEBUG);
|
||||
t.equal(log.streams[0].level, bunyan.DEBUG);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('default stream, level="trace" specified', function (t) {
|
||||
var log = bunyan.createLogger({
|
||||
name: 'foo',
|
||||
level: 'trace'
|
||||
});
|
||||
t.equal(log.level(), bunyan.TRACE);
|
||||
t.equal(log.streams[0].level, bunyan.TRACE);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('stream & level="trace" specified', function (t) {
|
||||
var log = bunyan.createLogger({
|
||||
name: 'foo',
|
||||
stream: process.stderr,
|
||||
level: 'trace'
|
||||
});
|
||||
t.equal(log.level(), bunyan.TRACE);
|
||||
t.equal(log.streams[0].level, bunyan.TRACE);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('one stream, default level', function (t) {
|
||||
var log = bunyan.createLogger({
|
||||
name: 'foo',
|
||||
streams: [
|
||||
{
|
||||
stream: process.stderr
|
||||
}
|
||||
]
|
||||
});
|
||||
t.equal(log.level(), bunyan.INFO);
|
||||
t.equal(log.streams[0].level, bunyan.INFO);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('one stream, top-"level" specified', function (t) {
|
||||
var log = bunyan.createLogger({
|
||||
name: 'foo',
|
||||
level: 'error',
|
||||
streams: [
|
||||
{
|
||||
stream: process.stderr
|
||||
}
|
||||
]
|
||||
});
|
||||
t.equal(log.level(), bunyan.ERROR);
|
||||
t.equal(log.streams[0].level, bunyan.ERROR);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('one stream, stream-"level" specified', function (t) {
|
||||
var log = bunyan.createLogger({
|
||||
name: 'foo',
|
||||
streams: [
|
||||
{
|
||||
stream: process.stderr,
|
||||
level: 'error'
|
||||
}
|
||||
]
|
||||
});
|
||||
t.equal(log.level(), bunyan.ERROR);
|
||||
t.equal(log.streams[0].level, bunyan.ERROR);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('one stream, both-"level" specified', function (t) {
|
||||
var log = bunyan.createLogger({
|
||||
name: 'foo',
|
||||
level: 'debug',
|
||||
streams: [
|
||||
{
|
||||
stream: process.stderr,
|
||||
level: 'error'
|
||||
}
|
||||
]
|
||||
});
|
||||
t.equal(log.level(), bunyan.ERROR);
|
||||
t.equal(log.streams[0].level, bunyan.ERROR);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('two streams, both-"level" specified', function (t) {
|
||||
var log = bunyan.createLogger({
|
||||
name: 'foo',
|
||||
level: 'debug',
|
||||
streams: [
|
||||
{
|
||||
stream: process.stdout,
|
||||
level: 'trace'
|
||||
},
|
||||
{
|
||||
stream: process.stderr,
|
||||
level: 'fatal'
|
||||
}
|
||||
]
|
||||
});
|
||||
t.equal(log.level(), bunyan.TRACE, 'log.level()');
|
||||
t.equal(log.streams[0].level, bunyan.TRACE);
|
||||
t.equal(log.streams[1].level, bunyan.FATAL);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('two streams, one with "level" specified', function (t) {
|
||||
var log = bunyan.createLogger({
|
||||
name: 'foo',
|
||||
streams: [
|
||||
{
|
||||
stream: process.stdout,
|
||||
},
|
||||
{
|
||||
stream: process.stderr,
|
||||
level: 'fatal'
|
||||
}
|
||||
]
|
||||
});
|
||||
t.equal(log.level(), bunyan.INFO);
|
||||
t.equal(log.streams[0].level, bunyan.INFO);
|
||||
t.equal(log.streams[1].level, bunyan.FATAL);
|
||||
t.end();
|
||||
});
|
||||
|
||||
// Issue #335
|
||||
test('log level 0 to turn on all logging', function (t) {
|
||||
var log = bunyan.createLogger({
|
||||
name: 'foo',
|
||||
level: 0
|
||||
});
|
||||
t.equal(log.level(), 0);
|
||||
t.equal(log.streams[0].level, 0);
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,150 @@
|
|||
/*
|
||||
* Test that streams (the various way they can be added to
|
||||
* a Logger instance) get the appropriate level.
|
||||
*/
|
||||
import { Eltro as t, assert} from 'eltro'
|
||||
import bunyan from '../lib/bunyan.mjs'
|
||||
|
||||
|
||||
// ---- Tests
|
||||
|
||||
|
||||
t.test('default stream log level', function () {
|
||||
var log = bunyan.createLogger({
|
||||
name: 'foo'
|
||||
});
|
||||
assert.strictEqual(log.level(), bunyan.INFO);
|
||||
assert.strictEqual(log.streams[0].level, bunyan.INFO);
|
||||
});
|
||||
|
||||
t.test('default stream, level=DEBUG specified', function () {
|
||||
var log = bunyan.createLogger({
|
||||
name: 'foo',
|
||||
level: bunyan.DEBUG
|
||||
});
|
||||
assert.strictEqual(log.level(), bunyan.DEBUG);
|
||||
assert.strictEqual(log.streams[0].level, bunyan.DEBUG);
|
||||
});
|
||||
|
||||
t.test('default stream, level="trace" specified', function () {
|
||||
var log = bunyan.createLogger({
|
||||
name: 'foo',
|
||||
level: 'trace'
|
||||
});
|
||||
assert.strictEqual(log.level(), bunyan.TRACE);
|
||||
assert.strictEqual(log.streams[0].level, bunyan.TRACE);
|
||||
});
|
||||
|
||||
t.test('stream & level="trace" specified', function () {
|
||||
var log = bunyan.createLogger({
|
||||
name: 'foo',
|
||||
stream: process.stderr,
|
||||
level: 'trace'
|
||||
});
|
||||
assert.strictEqual(log.level(), bunyan.TRACE);
|
||||
assert.strictEqual(log.streams[0].level, bunyan.TRACE);
|
||||
});
|
||||
|
||||
t.test('one stream, default level', function () {
|
||||
var log = bunyan.createLogger({
|
||||
name: 'foo',
|
||||
streams: [
|
||||
{
|
||||
stream: process.stderr
|
||||
}
|
||||
]
|
||||
});
|
||||
assert.strictEqual(log.level(), bunyan.INFO);
|
||||
assert.strictEqual(log.streams[0].level, bunyan.INFO);
|
||||
});
|
||||
|
||||
t.test('one stream, top-"level" specified', function () {
|
||||
var log = bunyan.createLogger({
|
||||
name: 'foo',
|
||||
level: 'error',
|
||||
streams: [
|
||||
{
|
||||
stream: process.stderr
|
||||
}
|
||||
]
|
||||
});
|
||||
assert.strictEqual(log.level(), bunyan.ERROR);
|
||||
assert.strictEqual(log.streams[0].level, bunyan.ERROR);
|
||||
});
|
||||
|
||||
t.test('one stream, stream-"level" specified', function () {
|
||||
var log = bunyan.createLogger({
|
||||
name: 'foo',
|
||||
streams: [
|
||||
{
|
||||
stream: process.stderr,
|
||||
level: 'error'
|
||||
}
|
||||
]
|
||||
});
|
||||
assert.strictEqual(log.level(), bunyan.ERROR);
|
||||
assert.strictEqual(log.streams[0].level, bunyan.ERROR);
|
||||
});
|
||||
|
||||
t.test('one stream, both-"level" specified', function () {
|
||||
var log = bunyan.createLogger({
|
||||
name: 'foo',
|
||||
level: 'debug',
|
||||
streams: [
|
||||
{
|
||||
stream: process.stderr,
|
||||
level: 'error'
|
||||
}
|
||||
]
|
||||
});
|
||||
assert.strictEqual(log.level(), bunyan.ERROR);
|
||||
assert.strictEqual(log.streams[0].level, bunyan.ERROR);
|
||||
});
|
||||
|
||||
t.test('two streams, both-"level" specified', function () {
|
||||
var log = bunyan.createLogger({
|
||||
name: 'foo',
|
||||
level: 'debug',
|
||||
streams: [
|
||||
{
|
||||
stream: process.stdout,
|
||||
level: 'trace'
|
||||
},
|
||||
{
|
||||
stream: process.stderr,
|
||||
level: 'fatal'
|
||||
}
|
||||
]
|
||||
});
|
||||
assert.strictEqual(log.level(), bunyan.TRACE, 'log.level()');
|
||||
assert.strictEqual(log.streams[0].level, bunyan.TRACE);
|
||||
assert.strictEqual(log.streams[1].level, bunyan.FATAL);
|
||||
});
|
||||
|
||||
t.test('two streams, one with "level" specified', function () {
|
||||
var log = bunyan.createLogger({
|
||||
name: 'foo',
|
||||
streams: [
|
||||
{
|
||||
stream: process.stdout,
|
||||
},
|
||||
{
|
||||
stream: process.stderr,
|
||||
level: 'fatal'
|
||||
}
|
||||
]
|
||||
});
|
||||
assert.strictEqual(log.level(), bunyan.INFO);
|
||||
assert.strictEqual(log.streams[0].level, bunyan.INFO);
|
||||
assert.strictEqual(log.streams[1].level, bunyan.FATAL);
|
||||
});
|
||||
|
||||
// Issue #335
|
||||
t.test('log level 0 to turn on all logging', function () {
|
||||
var log = bunyan.createLogger({
|
||||
name: 'foo',
|
||||
level: 0
|
||||
});
|
||||
assert.strictEqual(log.level(), 0);
|
||||
assert.strictEqual(log.streams[0].level, 0);
|
||||
});
|
|
@ -1,61 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012 Mark Cavage. All rights reserved.
|
||||
*
|
||||
* Help nodeunit API feel like node-tap's.
|
||||
*
|
||||
* Usage:
|
||||
* if (require.cache[__dirname + '/tap4nodeunit.js'])
|
||||
* delete require.cache[__dirname + '/tap4nodeunit.js'];
|
||||
* var tap4nodeunit = require('./tap4nodeunit.js');
|
||||
* var after = tap4nodeunit.after;
|
||||
* var before = tap4nodeunit.before;
|
||||
* var test = tap4nodeunit.test;
|
||||
*/
|
||||
|
||||
|
||||
|
||||
//---- Exports
|
||||
|
||||
module.exports = {
|
||||
after: function after(teardown) {
|
||||
module.parent.exports.tearDown = function _teardown(callback) {
|
||||
try {
|
||||
teardown.call(this, callback);
|
||||
} catch (e) {
|
||||
console.error('after:\n' + e.stack);
|
||||
process.exit(1);
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
before: function before(setup) {
|
||||
module.parent.exports.setUp = function _setup(callback) {
|
||||
try {
|
||||
setup.call(this, callback);
|
||||
} catch (e) {
|
||||
console.error('before:\n' + e.stack);
|
||||
process.exit(1);
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
test: function test(name, tester) {
|
||||
module.parent.exports[name] = function _(t) {
|
||||
var _done = false;
|
||||
t.end = function end() {
|
||||
if (!_done) {
|
||||
_done = true;
|
||||
t.done();
|
||||
}
|
||||
};
|
||||
t.notOk = function notOk(ok, message) {
|
||||
return (t.ok(!ok, message));
|
||||
};
|
||||
t.error = t.ifError;
|
||||
|
||||
tester.call(this, t);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
};
|
|
@ -0,0 +1,43 @@
|
|||
// Bring ben to local and remove one more dependancy.
|
||||
// The package ben has either way not been updated in over 10 years.
|
||||
// https://github.com/substack/node-ben
|
||||
|
||||
var ben = module.exports = function (times, cb) {
|
||||
if (typeof times === 'function') {
|
||||
cb = times;
|
||||
times = 10000;
|
||||
}
|
||||
|
||||
var t0 = Date.now();
|
||||
for (var i = 0; i < times; i++) {
|
||||
cb();
|
||||
}
|
||||
var elapsed = Date.now() - t0;
|
||||
|
||||
return elapsed / times;
|
||||
};
|
||||
ben.sync = ben;
|
||||
|
||||
ben.async = function (times, cb, resultCb) {
|
||||
if (typeof times === 'function') {
|
||||
resultCb = cb;
|
||||
cb = times;
|
||||
times = 100;
|
||||
}
|
||||
|
||||
var pending = times;
|
||||
var t = Date.now();
|
||||
var elapsed = 0;
|
||||
|
||||
cb(function fn () {
|
||||
elapsed += Date.now() - t;
|
||||
|
||||
if (--pending === 0) {
|
||||
resultCb(elapsed / times);
|
||||
}
|
||||
else {
|
||||
t = Date.now();
|
||||
cb(fn);
|
||||
}
|
||||
});
|
||||
};
|
|
@ -1,611 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2009-2012 Trent Mick
|
||||
|
||||
"""cutarelease -- Cut a release of your project.
|
||||
|
||||
A script that will help cut a release for a git-based project that follows
|
||||
a few conventions. It'll update your changelog (CHANGES.md), add a git
|
||||
tag, push those changes, update your version to the next patch level release
|
||||
and create a new changelog section for that new version.
|
||||
|
||||
Conventions:
|
||||
- XXX
|
||||
"""
|
||||
|
||||
__version_info__ = (1, 0, 7)
|
||||
__version__ = '.'.join(map(str, __version_info__))
|
||||
|
||||
import sys
|
||||
import os
|
||||
from os.path import join, dirname, normpath, abspath, exists, basename, splitext
|
||||
from glob import glob
|
||||
from pprint import pprint
|
||||
import re
|
||||
import codecs
|
||||
import logging
|
||||
import optparse
|
||||
import json
|
||||
import time
|
||||
|
||||
|
||||
|
||||
#---- globals and config
|
||||
|
||||
log = logging.getLogger("cutarelease")
|
||||
|
||||
class Error(Exception):
|
||||
pass
|
||||
|
||||
|
||||
|
||||
#---- main functionality
|
||||
|
||||
def cutarelease(project_name, version_files, dry_run=False):
|
||||
"""Cut a release.
|
||||
|
||||
@param project_name {str}
|
||||
@param version_files {list} List of paths to files holding the version
|
||||
info for this project.
|
||||
|
||||
If none are given it attempts to guess the version file:
|
||||
package.json or VERSION.txt or VERSION or $project_name.py
|
||||
or lib/$project_name.py or $project_name.js or lib/$project_name.js.
|
||||
|
||||
The version file can be in one of the following forms:
|
||||
|
||||
- A .py file, in which case the file is expect to have a top-level
|
||||
global called "__version_info__" as follows. [1]
|
||||
|
||||
__version_info__ = (0, 7, 6)
|
||||
|
||||
Note that I typically follow that with the following to get a
|
||||
string version attribute on my modules:
|
||||
|
||||
__version__ = '.'.join(map(str, __version_info__))
|
||||
|
||||
- A .js file, in which case the file is expected to have a top-level
|
||||
global called "VERSION" as follows:
|
||||
|
||||
ver VERSION = "1.2.3";
|
||||
|
||||
- A "package.json" file, typical of a node.js npm-using project.
|
||||
The package.json file must have a "version" field.
|
||||
|
||||
- TODO: A simple version file whose only content is a "1.2.3"-style version
|
||||
string.
|
||||
|
||||
[1]: This is a convention I tend to follow in my projects.
|
||||
Granted it might not be your cup of tea. I should add support for
|
||||
just `__version__ = "1.2.3"`. I'm open to other suggestions too.
|
||||
"""
|
||||
dry_run_str = dry_run and " (dry-run)" or ""
|
||||
|
||||
if not version_files:
|
||||
log.info("guessing version file")
|
||||
candidates = [
|
||||
"package.json",
|
||||
"VERSION.txt",
|
||||
"VERSION",
|
||||
"%s.py" % project_name,
|
||||
"lib/%s.py" % project_name,
|
||||
"%s.js" % project_name,
|
||||
"lib/%s.js" % project_name,
|
||||
]
|
||||
for candidate in candidates:
|
||||
if exists(candidate):
|
||||
version_files = [candidate]
|
||||
break
|
||||
else:
|
||||
raise Error("could not find a version file: specify its path or "
|
||||
"add one of the following to your project: '%s'"
|
||||
% "', '".join(candidates))
|
||||
log.info("using '%s' as version file", version_files[0])
|
||||
|
||||
parsed_version_files = [_parse_version_file(f) for f in version_files]
|
||||
version_file_type, version_info = parsed_version_files[0]
|
||||
version = _version_from_version_info(version_info)
|
||||
|
||||
# Confirm
|
||||
if not dry_run:
|
||||
answer = query_yes_no("* * *\n"
|
||||
"Are you sure you want cut a %s release?\n"
|
||||
"This will involved commits and a push." % version,
|
||||
default="no")
|
||||
print "* * *"
|
||||
if answer != "yes":
|
||||
log.info("user abort")
|
||||
return
|
||||
log.info("cutting a %s release%s", version, dry_run_str)
|
||||
|
||||
# Checks: Ensure there is a section in changes for this version.
|
||||
|
||||
|
||||
|
||||
changes_path = "CHANGES.md"
|
||||
changes_txt, changes, nyr = parse_changelog(changes_path)
|
||||
#pprint(changes)
|
||||
top_ver = changes[0]["version"]
|
||||
if top_ver != version:
|
||||
raise Error("changelog '%s' top section says "
|
||||
"version %r, expected version %r: aborting"
|
||||
% (changes_path, top_ver, version))
|
||||
top_verline = changes[0]["verline"]
|
||||
if not top_verline.endswith(nyr):
|
||||
answer = query_yes_no("\n* * *\n"
|
||||
"The changelog '%s' top section doesn't have the expected\n"
|
||||
"'%s' marker. Has this been released already?"
|
||||
% (changes_path, nyr), default="yes")
|
||||
print "* * *"
|
||||
if answer != "no":
|
||||
log.info("abort")
|
||||
return
|
||||
top_body = changes[0]["body"]
|
||||
if top_body.strip() == "(nothing yet)":
|
||||
raise Error("top section body is `(nothing yet)': it looks like "
|
||||
"nothing has been added to this release")
|
||||
|
||||
# Commits to prepare release.
|
||||
changes_txt_before = changes_txt
|
||||
changes_txt = changes_txt.replace(" (not yet released)", "", 1)
|
||||
if not dry_run and changes_txt != changes_txt_before:
|
||||
log.info("prepare `%s' for release", changes_path)
|
||||
f = codecs.open(changes_path, 'w', 'utf-8')
|
||||
f.write(changes_txt)
|
||||
f.close()
|
||||
run('git commit %s -m "%s"'
|
||||
% (changes_path, version))
|
||||
|
||||
# Tag version and push.
|
||||
curr_tags = set(t for t in _capture_stdout(["git", "tag", "-l"]).split('\n') if t)
|
||||
if not dry_run and version not in curr_tags:
|
||||
log.info("tag the release")
|
||||
date = time.strftime("%Y-%m-%d")
|
||||
run('git tag -a "%s" -m "version %s (%s)"' % (version, version, date))
|
||||
run('git push --tags')
|
||||
|
||||
# Optionally release.
|
||||
if exists("package.json"):
|
||||
answer = query_yes_no("\n* * *\nPublish to npm?", default="yes")
|
||||
print "* * *"
|
||||
if answer == "yes":
|
||||
if dry_run:
|
||||
log.info("skipping npm publish (dry-run)")
|
||||
else:
|
||||
run('npm publish')
|
||||
elif exists("setup.py"):
|
||||
answer = query_yes_no("\n* * *\nPublish to pypi?", default="yes")
|
||||
print "* * *"
|
||||
if answer == "yes":
|
||||
if dry_run:
|
||||
log.info("skipping pypi publish (dry-run)")
|
||||
else:
|
||||
run("%spython setup.py sdist --formats zip upload"
|
||||
% _setup_command_prefix())
|
||||
|
||||
# Commits to prepare for future dev and push.
|
||||
# - update changelog file
|
||||
next_version_info = _get_next_version_info(version_info)
|
||||
next_version = _version_from_version_info(next_version_info)
|
||||
log.info("prepare for future dev (version %s)", next_version)
|
||||
marker = "## " + changes[0]["verline"]
|
||||
if marker.endswith(nyr):
|
||||
marker = marker[0:-len(nyr)]
|
||||
if marker not in changes_txt:
|
||||
raise Error("couldn't find `%s' marker in `%s' "
|
||||
"content: can't prep for subsequent dev" % (marker, changes_path))
|
||||
next_verline = "%s %s%s" % (marker.rsplit(None, 1)[0], next_version, nyr)
|
||||
changes_txt = changes_txt.replace(marker + '\n',
|
||||
"%s\n\n(nothing yet)\n\n\n%s\n" % (next_verline, marker))
|
||||
if not dry_run:
|
||||
f = codecs.open(changes_path, 'w', 'utf-8')
|
||||
f.write(changes_txt)
|
||||
f.close()
|
||||
|
||||
# - update version file
|
||||
next_version_tuple = _tuple_from_version(next_version)
|
||||
for i, ver_file in enumerate(version_files):
|
||||
ver_content = codecs.open(ver_file, 'r', 'utf-8').read()
|
||||
ver_file_type, ver_info = parsed_version_files[i]
|
||||
if ver_file_type == "json":
|
||||
marker = '"version": "%s"' % version
|
||||
if marker not in ver_content:
|
||||
raise Error("couldn't find `%s' version marker in `%s' "
|
||||
"content: can't prep for subsequent dev" % (marker, ver_file))
|
||||
ver_content = ver_content.replace(marker,
|
||||
'"version": "%s"' % next_version)
|
||||
elif ver_file_type == "javascript":
|
||||
candidates = [
|
||||
("single", "var VERSION = '%s';" % version),
|
||||
("double", 'var VERSION = "%s";' % version),
|
||||
]
|
||||
for quote_type, marker in candidates:
|
||||
if marker in ver_content:
|
||||
break
|
||||
else:
|
||||
raise Error("couldn't find any candidate version marker in "
|
||||
"`%s' content: can't prep for subsequent dev: %r"
|
||||
% (ver_file, candidates))
|
||||
if quote_type == "single":
|
||||
ver_content = ver_content.replace(marker,
|
||||
"var VERSION = '%s';" % next_version)
|
||||
else:
|
||||
ver_content = ver_content.replace(marker,
|
||||
'var VERSION = "%s";' % next_version)
|
||||
elif ver_file_type == "python":
|
||||
marker = "__version_info__ = %r" % (version_info,)
|
||||
if marker not in ver_content:
|
||||
raise Error("couldn't find `%s' version marker in `%s' "
|
||||
"content: can't prep for subsequent dev" % (marker, ver_file))
|
||||
ver_content = ver_content.replace(marker,
|
||||
"__version_info__ = %r" % (next_version_tuple,))
|
||||
elif ver_file_type == "version":
|
||||
ver_content = next_version
|
||||
else:
|
||||
raise Error("unknown ver_file_type: %r" % ver_file_type)
|
||||
if not dry_run:
|
||||
log.info("update version to '%s' in '%s'", next_version, ver_file)
|
||||
f = codecs.open(ver_file, 'w', 'utf-8')
|
||||
f.write(ver_content)
|
||||
f.close()
|
||||
|
||||
if not dry_run:
|
||||
run('git commit %s %s -m "bumpver for subsequent work"' % (
|
||||
changes_path, ' '.join(version_files)))
|
||||
run('git push')
|
||||
|
||||
|
||||
|
||||
#---- internal support routines
|
||||
|
||||
def _indent(s, indent=' '):
|
||||
return indent + indent.join(s.splitlines(True))
|
||||
|
||||
def _tuple_from_version(version):
|
||||
def _intify(s):
|
||||
try:
|
||||
return int(s)
|
||||
except ValueError:
|
||||
return s
|
||||
return tuple(_intify(b) for b in version.split('.'))
|
||||
|
||||
def _get_next_version_info(version_info):
|
||||
next = list(version_info[:])
|
||||
next[-1] += 1
|
||||
return tuple(next)
|
||||
|
||||
def _version_from_version_info(version_info):
|
||||
v = str(version_info[0])
|
||||
state_dot_join = True
|
||||
for i in version_info[1:]:
|
||||
if state_dot_join:
|
||||
try:
|
||||
int(i)
|
||||
except ValueError:
|
||||
state_dot_join = False
|
||||
else:
|
||||
pass
|
||||
if state_dot_join:
|
||||
v += "." + str(i)
|
||||
else:
|
||||
v += str(i)
|
||||
return v
|
||||
|
||||
_version_re = re.compile(r"^(\d+)\.(\d+)(?:\.(\d+)([abc](\d+)?)?)?$")
|
||||
def _version_info_from_version(version):
|
||||
m = _version_re.match(version)
|
||||
if not m:
|
||||
raise Error("could not convert '%s' version to version info" % version)
|
||||
version_info = []
|
||||
for g in m.groups():
|
||||
if g is None:
|
||||
break
|
||||
try:
|
||||
version_info.append(int(g))
|
||||
except ValueError:
|
||||
version_info.append(g)
|
||||
return tuple(version_info)
|
||||
|
||||
def _parse_version_file(version_file):
|
||||
"""Get version info from the given file. It can be any of:
|
||||
|
||||
Supported version file types (i.e. types of files from which we know
|
||||
how to parse the version string/number -- often by some convention):
|
||||
- json: use the "version" key
|
||||
- javascript: look for a `var VERSION = "1.2.3";` or
|
||||
`var VERSION = '1.2.3';`
|
||||
- python: Python script/module with `__version_info__ = (1, 2, 3)`
|
||||
- version: a VERSION.txt or VERSION file where the whole contents are
|
||||
the version string
|
||||
|
||||
@param version_file {str} Can be a path or "type:path", where "type"
|
||||
is one of the supported types.
|
||||
"""
|
||||
# Get version file *type*.
|
||||
version_file_type = None
|
||||
match = re.compile("^([a-z]+):(.*)$").search(version_file)
|
||||
if match:
|
||||
version_file = match.group(2)
|
||||
version_file_type = match.group(1)
|
||||
aliases = {
|
||||
"js": "javascript"
|
||||
}
|
||||
if version_file_type in aliases:
|
||||
version_file_type = aliases[version_file_type]
|
||||
|
||||
f = codecs.open(version_file, 'r', 'utf-8')
|
||||
content = f.read()
|
||||
f.close()
|
||||
|
||||
if not version_file_type:
|
||||
# Guess the type.
|
||||
base = basename(version_file)
|
||||
ext = splitext(base)[1]
|
||||
if ext == ".json":
|
||||
version_file_type = "json"
|
||||
elif ext == ".py":
|
||||
version_file_type = "python"
|
||||
elif ext == ".js":
|
||||
version_file_type = "javascript"
|
||||
elif content.startswith("#!"):
|
||||
shebang = content.splitlines(False)[0]
|
||||
shebang_bits = re.split(r'[/ \t]', shebang)
|
||||
for name, typ in {"python": "python", "node": "javascript"}.items():
|
||||
if name in shebang_bits:
|
||||
version_file_type = typ
|
||||
break
|
||||
elif base in ("VERSION", "VERSION.txt"):
|
||||
version_file_type = "version"
|
||||
if not version_file_type:
|
||||
raise RuntimeError("can't extract version from '%s': no idea "
|
||||
"what type of file it it" % version_file)
|
||||
|
||||
if version_file_type == "json":
|
||||
obj = json.loads(content)
|
||||
version_info = _version_info_from_version(obj["version"])
|
||||
elif version_file_type == "python":
|
||||
m = re.search(r'^__version_info__ = (.*?)$', content, re.M)
|
||||
version_info = eval(m.group(1))
|
||||
elif version_file_type == "javascript":
|
||||
m = re.search(r'^var VERSION = (\'|")(.*?)\1;$', content, re.M)
|
||||
version_info = _version_info_from_version(m.group(2))
|
||||
elif version_file_type == "version":
|
||||
version_info = _version_info_from_version(content.strip())
|
||||
else:
|
||||
raise RuntimeError("unexpected version_file_type: %r"
|
||||
% version_file_type)
|
||||
return version_file_type, version_info
|
||||
|
||||
|
||||
def parse_changelog(changes_path):
|
||||
"""Parse the given changelog path and return `(content, parsed, nyr)`
|
||||
where `nyr` is the ' (not yet released)' marker and `parsed` looks like:
|
||||
|
||||
[{'body': u'\n(nothing yet)\n\n',
|
||||
'verline': u'restify 1.0.1 (not yet released)',
|
||||
'version': u'1.0.1'}, # version is parsed out for top section only
|
||||
{'body': u'...',
|
||||
'verline': u'1.0.0'},
|
||||
{'body': u'...',
|
||||
'verline': u'1.0.0-rc2'},
|
||||
{'body': u'...',
|
||||
'verline': u'1.0.0-rc1'}]
|
||||
|
||||
A changelog (CHANGES.md) is expected to look like this:
|
||||
|
||||
# $project Changelog
|
||||
|
||||
## $next_version (not yet released)
|
||||
|
||||
...
|
||||
|
||||
## $version1
|
||||
|
||||
...
|
||||
|
||||
## $version2
|
||||
|
||||
... and so on
|
||||
|
||||
The version lines are enforced as follows:
|
||||
|
||||
- The top entry should have a " (not yet released)" suffix. "Should"
|
||||
because recovery from half-cutarelease failures is supported.
|
||||
- A version string must be extractable from there, but it tries to
|
||||
be loose (though strict "X.Y.Z" versioning is preferred). Allowed
|
||||
|
||||
## 1.0.0
|
||||
## my project 1.0.1
|
||||
## foo 1.2.3-rc2
|
||||
|
||||
Basically, (a) the " (not yet released)" is stripped, (b) the
|
||||
last token is the version, and (c) that version must start with
|
||||
a digit (sanity check).
|
||||
"""
|
||||
if not exists(changes_path):
|
||||
raise Error("changelog file '%s' not found" % changes_path)
|
||||
content = codecs.open(changes_path, 'r', 'utf-8').read()
|
||||
|
||||
parser = re.compile(
|
||||
r'^##\s*(?P<verline>[^\n]*?)\s*$(?P<body>.*?)(?=^##|\Z)',
|
||||
re.M | re.S)
|
||||
sections = parser.findall(content)
|
||||
|
||||
# Sanity checks on changelog format.
|
||||
if not sections:
|
||||
template = "## 1.0.0 (not yet released)\n\n(nothing yet)\n"
|
||||
raise Error("changelog '%s' must have at least one section, "
|
||||
"suggestion:\n\n%s" % (changes_path, _indent(template)))
|
||||
first_section_verline = sections[0][0]
|
||||
nyr = ' (not yet released)'
|
||||
#if not first_section_verline.endswith(nyr):
|
||||
# eg = "## %s%s" % (first_section_verline, nyr)
|
||||
# raise Error("changelog '%s' top section must end with %r, "
|
||||
# "naive e.g.: '%s'" % (changes_path, nyr, eg))
|
||||
|
||||
items = []
|
||||
for i, section in enumerate(sections):
|
||||
item = {
|
||||
"verline": section[0],
|
||||
"body": section[1]
|
||||
}
|
||||
if i == 0:
|
||||
# We only bother to pull out 'version' for the top section.
|
||||
verline = section[0]
|
||||
if verline.endswith(nyr):
|
||||
verline = verline[0:-len(nyr)]
|
||||
version = verline.split()[-1]
|
||||
try:
|
||||
int(version[0])
|
||||
except ValueError:
|
||||
msg = ''
|
||||
if version.endswith(')'):
|
||||
msg = " (cutarelease is picky about the trailing %r " \
|
||||
"on the top version line. Perhaps you misspelled " \
|
||||
"that?)" % nyr
|
||||
raise Error("changelog '%s' top section version '%s' is "
|
||||
"invalid: first char isn't a number%s"
|
||||
% (changes_path, version, msg))
|
||||
item["version"] = version
|
||||
items.append(item)
|
||||
|
||||
return content, items, nyr
|
||||
|
||||
## {{{ http://code.activestate.com/recipes/577058/ (r2)
|
||||
def query_yes_no(question, default="yes"):
|
||||
"""Ask a yes/no question via raw_input() and return their answer.
|
||||
|
||||
"question" is a string that is presented to the user.
|
||||
"default" is the presumed answer if the user just hits <Enter>.
|
||||
It must be "yes" (the default), "no" or None (meaning
|
||||
an answer is required of the user).
|
||||
|
||||
The "answer" return value is one of "yes" or "no".
|
||||
"""
|
||||
valid = {"yes":"yes", "y":"yes", "ye":"yes",
|
||||
"no":"no", "n":"no"}
|
||||
if default == None:
|
||||
prompt = " [y/n] "
|
||||
elif default == "yes":
|
||||
prompt = " [Y/n] "
|
||||
elif default == "no":
|
||||
prompt = " [y/N] "
|
||||
else:
|
||||
raise ValueError("invalid default answer: '%s'" % default)
|
||||
|
||||
while 1:
|
||||
sys.stdout.write(question + prompt)
|
||||
choice = raw_input().lower()
|
||||
if default is not None and choice == '':
|
||||
return default
|
||||
elif choice in valid.keys():
|
||||
return valid[choice]
|
||||
else:
|
||||
sys.stdout.write("Please respond with 'yes' or 'no' "\
|
||||
"(or 'y' or 'n').\n")
|
||||
## end of http://code.activestate.com/recipes/577058/ }}}
|
||||
|
||||
def _capture_stdout(argv):
|
||||
import subprocess
|
||||
p = subprocess.Popen(argv, stdout=subprocess.PIPE)
|
||||
return p.communicate()[0]
|
||||
|
||||
class _NoReflowFormatter(optparse.IndentedHelpFormatter):
|
||||
"""An optparse formatter that does NOT reflow the description."""
|
||||
def format_description(self, description):
|
||||
return description or ""
|
||||
|
||||
def run(cmd):
|
||||
"""Run the given command.
|
||||
|
||||
Raises OSError is the command returns a non-zero exit status.
|
||||
"""
|
||||
log.debug("running '%s'", cmd)
|
||||
fixed_cmd = cmd
|
||||
if sys.platform == "win32" and cmd.count('"') > 2:
|
||||
fixed_cmd = '"' + cmd + '"'
|
||||
retval = os.system(fixed_cmd)
|
||||
if hasattr(os, "WEXITSTATUS"):
|
||||
status = os.WEXITSTATUS(retval)
|
||||
else:
|
||||
status = retval
|
||||
if status:
|
||||
raise OSError(status, "error running '%s'" % cmd)
|
||||
|
||||
def _setup_command_prefix():
|
||||
prefix = ""
|
||||
if sys.platform == "darwin":
|
||||
# http://forums.macosxhints.com/archive/index.php/t-43243.html
|
||||
# This is an Apple customization to `tar` to avoid creating
|
||||
# '._foo' files for extended-attributes for archived files.
|
||||
prefix = "COPY_EXTENDED_ATTRIBUTES_DISABLE=1 "
|
||||
return prefix
|
||||
|
||||
|
||||
#---- mainline
|
||||
|
||||
def main(argv):
|
||||
logging.basicConfig(format="%(name)s: %(levelname)s: %(message)s")
|
||||
log.setLevel(logging.INFO)
|
||||
|
||||
# Parse options.
|
||||
parser = optparse.OptionParser(prog="cutarelease", usage='',
|
||||
version="%prog " + __version__, description=__doc__,
|
||||
formatter=_NoReflowFormatter())
|
||||
parser.add_option("-v", "--verbose", dest="log_level",
|
||||
action="store_const", const=logging.DEBUG,
|
||||
help="more verbose output")
|
||||
parser.add_option("-q", "--quiet", dest="log_level",
|
||||
action="store_const", const=logging.WARNING,
|
||||
help="quieter output (just warnings and errors)")
|
||||
parser.set_default("log_level", logging.INFO)
|
||||
parser.add_option("--test", action="store_true",
|
||||
help="run self-test and exit (use 'eol.py -v --test' for verbose test output)")
|
||||
parser.add_option("-p", "--project-name", metavar="NAME",
|
||||
help='the name of this project (default is the base dir name)',
|
||||
default=basename(os.getcwd()))
|
||||
parser.add_option("-f", "--version-file", metavar="[TYPE:]PATH",
|
||||
action='append', dest="version_files",
|
||||
help='The path to the project file holding the version info. Can be '
|
||||
'specified multiple times if more than one file should be updated '
|
||||
'with new version info. If excluded, it will be guessed.')
|
||||
parser.add_option("-n", "--dry-run", action="store_true",
|
||||
help='Do a dry-run', default=False)
|
||||
opts, args = parser.parse_args()
|
||||
log.setLevel(opts.log_level)
|
||||
|
||||
cutarelease(opts.project_name, opts.version_files, dry_run=opts.dry_run)
|
||||
|
||||
|
||||
## {{{ http://code.activestate.com/recipes/577258/ (r5+)
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
retval = main(sys.argv)
|
||||
except KeyboardInterrupt:
|
||||
sys.exit(1)
|
||||
except SystemExit:
|
||||
raise
|
||||
except:
|
||||
import traceback, logging
|
||||
if not log.handlers and not logging.root.handlers:
|
||||
logging.basicConfig()
|
||||
skip_it = False
|
||||
exc_info = sys.exc_info()
|
||||
if hasattr(exc_info[0], "__name__"):
|
||||
exc_class, exc, tb = exc_info
|
||||
if isinstance(exc, IOError) and exc.args[0] == 32:
|
||||
# Skip 'IOError: [Errno 32] Broken pipe': often a cancelling of `less`.
|
||||
skip_it = True
|
||||
if not skip_it:
|
||||
tb_path, tb_lineno, tb_func = traceback.extract_tb(tb)[-1][:3]
|
||||
log.error("%s (%s:%s in %s)", exc_info[1], tb_path,
|
||||
tb_lineno, tb_func)
|
||||
else: # string exception
|
||||
log.error(exc_info[0])
|
||||
if not skip_it:
|
||||
if log.isEnabledFor(logging.DEBUG):
|
||||
traceback.print_exception(*exc_info)
|
||||
sys.exit(1)
|
||||
else:
|
||||
sys.exit(retval)
|
||||
## end of http://code.activestate.com/recipes/577258/ }}}
|
953
tools/jsstyle
953
tools/jsstyle
|
@ -1,953 +0,0 @@
|
|||
#!/usr/bin/env perl
|
||||
#
|
||||
# CDDL HEADER START
|
||||
#
|
||||
# The contents of this file are subject to the terms of the
|
||||
# Common Development and Distribution License (the "License").
|
||||
# You may not use this file except in compliance with the License.
|
||||
#
|
||||
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
# or http://www.opensolaris.org/os/licensing.
|
||||
# See the License for the specific language governing permissions
|
||||
# and limitations under the License.
|
||||
#
|
||||
# When distributing Covered Code, include this CDDL HEADER in each
|
||||
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
# If applicable, add the following below this CDDL HEADER, with the
|
||||
# fields enclosed by brackets "[]" replaced with your own identifying
|
||||
# information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
#
|
||||
# CDDL HEADER END
|
||||
#
|
||||
#
|
||||
# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
|
||||
# Use is subject to license terms.
|
||||
#
|
||||
# Copyright 2011 Joyent, Inc. All rights reserved.
|
||||
#
|
||||
# jsstyle - check for some common stylistic errors.
|
||||
#
|
||||
# jsstyle is a sort of "lint" for Javascript coding style. This tool is
|
||||
# derived from the cstyle tool, used to check for the style used in the
|
||||
# Solaris kernel, sometimes known as "Bill Joy Normal Form".
|
||||
#
|
||||
# There's a lot this can't check for, like proper indentation of code
|
||||
# blocks. There's also a lot more this could check for.
|
||||
#
|
||||
# A note to the non perl literate:
|
||||
#
|
||||
# perl regular expressions are pretty much like egrep
|
||||
# regular expressions, with the following special symbols
|
||||
#
|
||||
# \s any space character
|
||||
# \S any non-space character
|
||||
# \w any "word" character [a-zA-Z0-9_]
|
||||
# \W any non-word character
|
||||
# \d a digit [0-9]
|
||||
# \D a non-digit
|
||||
# \b word boundary (between \w and \W)
|
||||
# \B non-word boundary
|
||||
#
|
||||
|
||||
require 5.0;
|
||||
use IO::File;
|
||||
use Getopt::Std;
|
||||
use strict;
|
||||
|
||||
my $usage =
|
||||
"Usage: jsstyle [-h?vcC] [-t <num>] [-f <path>] [-o <config>] file ...
|
||||
|
||||
Check your JavaScript file for style.
|
||||
See <https://github.com/davepacheco/jsstyle> for details on config options.
|
||||
Report bugs to <https://github.com/davepacheco/jsstyle/issues>.
|
||||
|
||||
Options:
|
||||
-h print this help and exit
|
||||
-v verbose
|
||||
|
||||
-c check continuation indentation inside functions
|
||||
-t specify tab width for line length calculation
|
||||
-C don't check anything in header block comments
|
||||
|
||||
-f PATH
|
||||
path to a jsstyle config file
|
||||
-o OPTION1,OPTION2
|
||||
set config options, e.g. '-o doxygen,indent=2'
|
||||
|
||||
";
|
||||
|
||||
my %opts;
|
||||
|
||||
if (!getopts("ch?o:t:f:vC", \%opts)) {
|
||||
print $usage;
|
||||
exit 2;
|
||||
}
|
||||
|
||||
if (defined($opts{'h'}) || defined($opts{'?'})) {
|
||||
print $usage;
|
||||
exit;
|
||||
}
|
||||
|
||||
my $check_continuation = $opts{'c'};
|
||||
my $verbose = $opts{'v'};
|
||||
my $ignore_hdr_comment = $opts{'C'};
|
||||
my $tab_width = $opts{'t'};
|
||||
|
||||
# By default, tabs are 8 characters wide
|
||||
if (! defined($opts{'t'})) {
|
||||
$tab_width = 8;
|
||||
}
|
||||
|
||||
|
||||
# Load config
|
||||
my %config = (
|
||||
indent => "tab",
|
||||
doxygen => 0, # doxygen comments: /** ... */
|
||||
splint => 0, # splint comments. Needed?
|
||||
"unparenthesized-return" => 1,
|
||||
"literal-string-quote" => "single", # 'single' or 'double'
|
||||
"blank-after-start-comment" => 1,
|
||||
"continuation-at-front" => 0,
|
||||
"leading-right-paren-ok" => 0,
|
||||
"strict-indent" => 0
|
||||
);
|
||||
sub add_config_var ($$) {
|
||||
my ($scope, $str) = @_;
|
||||
|
||||
if ($str !~ /^([\w-]+)(?:\s*=\s*(.*?))?$/) {
|
||||
die "$scope: invalid option: '$str'";
|
||||
}
|
||||
my $name = $1;
|
||||
my $value = ($2 eq '' ? 1 : $2);
|
||||
#print "scope: '$scope', str: '$str', name: '$name', value: '$value'\n";
|
||||
|
||||
# Validate config var.
|
||||
if ($name eq "indent") {
|
||||
# A number of spaces or "tab".
|
||||
if ($value !~ /^\d+$/ && $value ne "tab") {
|
||||
die "$scope: invalid '$name': must be a number (of ".
|
||||
"spaces) or 'tab'";
|
||||
}
|
||||
} elsif ($name eq "doxygen" || # boolean vars
|
||||
$name eq "splint" ||
|
||||
$name eq "unparenthesized-return" ||
|
||||
$name eq "continuation-at-front" ||
|
||||
$name eq "leading-right-paren-ok" ||
|
||||
$name eq "leading-comma-ok" ||
|
||||
$name eq "uncuddled-else-ok" ||
|
||||
$name eq "whitespace-after-left-paren-ok" ||
|
||||
$name eq "strict-indent" ||
|
||||
$name eq "blank-after-start-comment") {
|
||||
|
||||
if ($value != 1 && $value != 0) {
|
||||
die "$scope: invalid '$name': don't give a value";
|
||||
}
|
||||
} elsif ($name eq "literal-string-quote") {
|
||||
if ($value !~ /single|double/) {
|
||||
die "$scope: invalid '$name': must be 'single' ".
|
||||
"or 'double'";
|
||||
}
|
||||
} else {
|
||||
die "$scope: unknown config var: $name";
|
||||
}
|
||||
$config{$name} = $value;
|
||||
}
|
||||
|
||||
if (defined($opts{'f'})) {
|
||||
my $path = $opts{'f'};
|
||||
my $fh = new IO::File $path, "r";
|
||||
if (!defined($fh)) {
|
||||
die "cannot open config path '$path'";
|
||||
}
|
||||
my $line = 0;
|
||||
while (<$fh>) {
|
||||
$line++;
|
||||
s/^\s*//; # drop leading space
|
||||
s/\s*$//; # drop trailing space
|
||||
next if ! $_; # skip empty line
|
||||
next if /^#/; # skip comments
|
||||
add_config_var "$path:$line", $_;
|
||||
}
|
||||
}
|
||||
|
||||
if (defined($opts{'o'})) {
|
||||
for my $x (split /,/, $opts{'o'}) {
|
||||
add_config_var "'-o' option", $x;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
my ($filename, $line, $prev); # shared globals
|
||||
|
||||
my $fmt;
|
||||
my $hdr_comment_start;
|
||||
|
||||
if ($verbose) {
|
||||
$fmt = "%s: %d: %s\n%s\n";
|
||||
} else {
|
||||
$fmt = "%s: %d: %s\n";
|
||||
}
|
||||
|
||||
if ($config{"doxygen"}) {
|
||||
# doxygen comments look like "/*!" or "/**"; allow them.
|
||||
$hdr_comment_start = qr/^\s*\/\*[\!\*]?$/;
|
||||
} else {
|
||||
$hdr_comment_start = qr/^\s*\/\*$/;
|
||||
}
|
||||
|
||||
# Note, following must be in single quotes so that \s and \w work right.
|
||||
my $lint_re = qr/\/\*(?:
|
||||
jsl:\w+?|ARGSUSED[0-9]*|NOTREACHED|LINTLIBRARY|VARARGS[0-9]*|
|
||||
CONSTCOND|CONSTANTCOND|CONSTANTCONDITION|EMPTY|
|
||||
FALLTHRU|FALLTHROUGH|LINTED.*?|PRINTFLIKE[0-9]*|
|
||||
PROTOLIB[0-9]*|SCANFLIKE[0-9]*|JSSTYLED.*?
|
||||
)\*\//x;
|
||||
|
||||
my $splint_re = qr/\/\*@.*?@\*\//x;
|
||||
|
||||
my $err_stat = 0; # exit status
|
||||
|
||||
if ($#ARGV >= 0) {
|
||||
foreach my $arg (@ARGV) {
|
||||
my $fh = new IO::File $arg, "r";
|
||||
if (!defined($fh)) {
|
||||
printf "%s: cannot open\n", $arg;
|
||||
} else {
|
||||
&jsstyle($arg, $fh);
|
||||
close $fh;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
&jsstyle("<stdin>", *STDIN);
|
||||
}
|
||||
exit $err_stat;
|
||||
|
||||
my $no_errs = 0; # set for JSSTYLED-protected lines
|
||||
|
||||
sub err($) {
|
||||
my ($error) = @_;
|
||||
unless ($no_errs) {
|
||||
printf $fmt, $filename, $., $error, $line;
|
||||
$err_stat = 1;
|
||||
}
|
||||
}
|
||||
|
||||
sub err_prefix($$) {
|
||||
my ($prevline, $error) = @_;
|
||||
my $out = $prevline."\n".$line;
|
||||
unless ($no_errs) {
|
||||
printf $fmt, $filename, $., $error, $out;
|
||||
$err_stat = 1;
|
||||
}
|
||||
}
|
||||
|
||||
sub err_prev($) {
|
||||
my ($error) = @_;
|
||||
unless ($no_errs) {
|
||||
printf $fmt, $filename, $. - 1, $error, $prev;
|
||||
$err_stat = 1;
|
||||
}
|
||||
}
|
||||
|
||||
sub jsstyle($$) {
|
||||
|
||||
my ($fn, $filehandle) = @_;
|
||||
$filename = $fn; # share it globally
|
||||
|
||||
my $in_cpp = 0;
|
||||
my $next_in_cpp = 0;
|
||||
|
||||
my $in_comment = 0;
|
||||
my $in_header_comment = 0;
|
||||
my $comment_done = 0;
|
||||
my $in_function = 0;
|
||||
my $in_function_header = 0;
|
||||
my $in_declaration = 0;
|
||||
my $note_level = 0;
|
||||
my $nextok = 0;
|
||||
my $nocheck = 0;
|
||||
|
||||
my $in_string = 0;
|
||||
|
||||
my ($okmsg, $comment_prefix);
|
||||
|
||||
$line = '';
|
||||
$prev = '';
|
||||
reset_indent();
|
||||
|
||||
line: while (<$filehandle>) {
|
||||
s/\r?\n$//; # strip return and newline
|
||||
|
||||
# save the original line, then remove all text from within
|
||||
# double or single quotes, we do not want to check such text.
|
||||
|
||||
$line = $_;
|
||||
|
||||
#
|
||||
# C allows strings to be continued with a backslash at the end of
|
||||
# the line. We translate that into a quoted string on the previous
|
||||
# line followed by an initial quote on the next line.
|
||||
#
|
||||
# (we assume that no-one will use backslash-continuation with character
|
||||
# constants)
|
||||
#
|
||||
$_ = '"' . $_ if ($in_string && !$nocheck && !$in_comment);
|
||||
|
||||
#
|
||||
# normal strings and characters
|
||||
#
|
||||
s/'([^\\']|\\.)*'/\'\'/g;
|
||||
s/"([^\\"]|\\.)*"/\"\"/g;
|
||||
|
||||
#
|
||||
# detect string continuation
|
||||
#
|
||||
if ($nocheck || $in_comment) {
|
||||
$in_string = 0;
|
||||
} else {
|
||||
#
|
||||
# Now that all full strings are replaced with "", we check
|
||||
# for unfinished strings continuing onto the next line.
|
||||
#
|
||||
$in_string =
|
||||
(s/([^"](?:"")*)"([^\\"]|\\.)*\\$/$1""/ ||
|
||||
s/^("")*"([^\\"]|\\.)*\\$/""/);
|
||||
}
|
||||
|
||||
#
|
||||
# figure out if we are in a cpp directive
|
||||
#
|
||||
$in_cpp = $next_in_cpp || /^\s*#/; # continued or started
|
||||
$next_in_cpp = $in_cpp && /\\$/; # only if continued
|
||||
|
||||
# strip off trailing backslashes, which appear in long macros
|
||||
s/\s*\\$//;
|
||||
|
||||
# an /* END JSSTYLED */ comment ends a no-check block.
|
||||
if ($nocheck) {
|
||||
if (/\/\* *END *JSSTYLED *\*\//) {
|
||||
$nocheck = 0;
|
||||
} else {
|
||||
reset_indent();
|
||||
next line;
|
||||
}
|
||||
}
|
||||
|
||||
# a /*JSSTYLED*/ comment indicates that the next line is ok.
|
||||
if ($nextok) {
|
||||
if ($okmsg) {
|
||||
err($okmsg);
|
||||
}
|
||||
$nextok = 0;
|
||||
$okmsg = 0;
|
||||
if (/\/\* *JSSTYLED.*\*\//) {
|
||||
/^.*\/\* *JSSTYLED *(.*) *\*\/.*$/;
|
||||
$okmsg = $1;
|
||||
$nextok = 1;
|
||||
}
|
||||
$no_errs = 1;
|
||||
} elsif ($no_errs) {
|
||||
$no_errs = 0;
|
||||
}
|
||||
|
||||
# check length of line.
|
||||
# first, a quick check to see if there is any chance of being too long.
|
||||
if ((($line =~ tr/\t/\t/) * ($tab_width - 1)) + length($line) > 80) {
|
||||
# yes, there is a chance.
|
||||
# replace tabs with spaces and check again.
|
||||
my $eline = $line;
|
||||
1 while $eline =~
|
||||
s/\t+/' ' x
|
||||
(length($&) * $tab_width - length($`) % $tab_width)/e;
|
||||
if (length($eline) > 80) {
|
||||
err("line > 80 characters");
|
||||
}
|
||||
}
|
||||
|
||||
# ignore NOTE(...) annotations (assumes NOTE is on lines by itself).
|
||||
if ($note_level || /\b_?NOTE\s*\(/) { # if in NOTE or this is NOTE
|
||||
s/[^()]//g; # eliminate all non-parens
|
||||
$note_level += s/\(//g - length; # update paren nest level
|
||||
next;
|
||||
}
|
||||
|
||||
# a /* BEGIN JSSTYLED */ comment starts a no-check block.
|
||||
if (/\/\* *BEGIN *JSSTYLED *\*\//) {
|
||||
$nocheck = 1;
|
||||
}
|
||||
|
||||
# a /*JSSTYLED*/ comment indicates that the next line is ok.
|
||||
if (/\/\* *JSSTYLED.*\*\//) {
|
||||
/^.*\/\* *JSSTYLED *(.*) *\*\/.*$/;
|
||||
$okmsg = $1;
|
||||
$nextok = 1;
|
||||
}
|
||||
if (/\/\/ *JSSTYLED/) {
|
||||
/^.*\/\/ *JSSTYLED *(.*)$/;
|
||||
$okmsg = $1;
|
||||
$nextok = 1;
|
||||
}
|
||||
|
||||
# universal checks; apply to everything
|
||||
if (/\t +\t/) {
|
||||
err("spaces between tabs");
|
||||
}
|
||||
if (/ \t+ /) {
|
||||
err("tabs between spaces");
|
||||
}
|
||||
if (/\s$/) {
|
||||
err("space or tab at end of line");
|
||||
}
|
||||
if (/[^ \t(]\/\*/ && !/\w\(\/\*.*\*\/\);/) {
|
||||
err("comment preceded by non-blank");
|
||||
}
|
||||
|
||||
# is this the beginning or ending of a function?
|
||||
# (not if "struct foo\n{\n")
|
||||
if (/^{$/ && $prev =~ /\)\s*(const\s*)?(\/\*.*\*\/\s*)?\\?$/) {
|
||||
$in_function = 1;
|
||||
$in_declaration = 1;
|
||||
$in_function_header = 0;
|
||||
$prev = $line;
|
||||
next line;
|
||||
}
|
||||
if (/^}\s*(\/\*.*\*\/\s*)*$/) {
|
||||
if ($prev =~ /^\s*return\s*;/) {
|
||||
err_prev("unneeded return at end of function");
|
||||
}
|
||||
$in_function = 0;
|
||||
reset_indent(); # we don't check between functions
|
||||
$prev = $line;
|
||||
next line;
|
||||
}
|
||||
if (/^\w*\($/) {
|
||||
$in_function_header = 1;
|
||||
}
|
||||
|
||||
# a blank line terminates the declarations within a function.
|
||||
# XXX - but still a problem in sub-blocks.
|
||||
if ($in_declaration && /^$/) {
|
||||
$in_declaration = 0;
|
||||
}
|
||||
|
||||
if ($comment_done) {
|
||||
$in_comment = 0;
|
||||
$in_header_comment = 0;
|
||||
$comment_done = 0;
|
||||
}
|
||||
# does this looks like the start of a block comment?
|
||||
if (/$hdr_comment_start/) {
|
||||
if ($config{"indent"} eq "tab") {
|
||||
if (!/^\t*\/\*/) {
|
||||
err("block comment not indented by tabs");
|
||||
}
|
||||
} elsif (!/^ *\/\*/) {
|
||||
err("block comment not indented by spaces");
|
||||
}
|
||||
$in_comment = 1;
|
||||
/^(\s*)\//;
|
||||
$comment_prefix = $1;
|
||||
if ($comment_prefix eq "") {
|
||||
$in_header_comment = 1;
|
||||
}
|
||||
$prev = $line;
|
||||
next line;
|
||||
}
|
||||
# are we still in the block comment?
|
||||
if ($in_comment) {
|
||||
if (/^$comment_prefix \*\/$/) {
|
||||
$comment_done = 1;
|
||||
} elsif (/\*\//) {
|
||||
$comment_done = 1;
|
||||
err("improper block comment close")
|
||||
unless ($ignore_hdr_comment && $in_header_comment);
|
||||
} elsif (!/^$comment_prefix \*[ \t]/ &&
|
||||
!/^$comment_prefix \*$/) {
|
||||
err("improper block comment")
|
||||
unless ($ignore_hdr_comment && $in_header_comment);
|
||||
}
|
||||
}
|
||||
|
||||
if ($in_header_comment && $ignore_hdr_comment) {
|
||||
$prev = $line;
|
||||
next line;
|
||||
}
|
||||
|
||||
# check for errors that might occur in comments and in code.
|
||||
|
||||
# allow spaces to be used to draw pictures in header comments.
|
||||
#if (/[^ ] / && !/".* .*"/ && !$in_header_comment) {
|
||||
# err("spaces instead of tabs");
|
||||
#}
|
||||
#if (/^ / && !/^ \*[ \t\/]/ && !/^ \*$/ &&
|
||||
# (!/^ \w/ || $in_function != 0)) {
|
||||
# err("indent by spaces instead of tabs");
|
||||
#}
|
||||
if ($config{"indent"} eq "tab") {
|
||||
if (/^ {2,}/ && !/^ [^ ]/) {
|
||||
err("indent by spaces instead of tabs");
|
||||
}
|
||||
} elsif (/^\t/) {
|
||||
err("indent by tabs instead of spaces")
|
||||
} elsif (/^( +)/ && !$in_comment) {
|
||||
my $indent = $1;
|
||||
if (length($indent) < $config{"indent"}) {
|
||||
err("indent of " . length($indent) .
|
||||
" space(s) instead of " . $config{"indent"});
|
||||
} elsif ($config{"strict-indent"} &&
|
||||
length($indent) % $config{"indent"} != 0) {
|
||||
err("indent is " . length($indent) .
|
||||
" not a multiple of " . $config{'indent'} . " spaces");
|
||||
}
|
||||
}
|
||||
if (/^\t+ [^ \t\*]/ || /^\t+ \S/ || /^\t+ \S/) {
|
||||
err("continuation line not indented by 4 spaces");
|
||||
}
|
||||
|
||||
# A multi-line block comment must not have content on the first line.
|
||||
if (/^\s*\/\*./ && !/^\s*\/\*.*\*\// && !/$hdr_comment_start/) {
|
||||
err("improper first line of block comment");
|
||||
}
|
||||
|
||||
if ($in_comment) { # still in comment, don't do further checks
|
||||
$prev = $line;
|
||||
next line;
|
||||
}
|
||||
|
||||
if ((/[^(]\/\*\S/ || /^\/\*\S/) &&
|
||||
!(/$lint_re/ || ($config{"splint"} && /$splint_re/))) {
|
||||
err("missing blank after open comment");
|
||||
}
|
||||
if (/\S\*\/[^)]|\S\*\/$/ &&
|
||||
!(/$lint_re/ || ($config{"splint"} && /$splint_re/))) {
|
||||
err("missing blank before close comment");
|
||||
}
|
||||
if ($config{"blank-after-start-comment"} && /(?<!\w:)\/\/\S/) { # C++ comments
|
||||
err("missing blank after start comment");
|
||||
}
|
||||
# check for unterminated single line comments, but allow them when
|
||||
# they are used to comment out the argument list of a function
|
||||
# declaration.
|
||||
if (/\S.*\/\*/ && !/\S.*\/\*.*\*\// && !/\(\/\*/) {
|
||||
err("unterminated single line comment");
|
||||
}
|
||||
|
||||
if (/^(#else|#endif|#include)(.*)$/) {
|
||||
$prev = $line;
|
||||
next line;
|
||||
}
|
||||
|
||||
#
|
||||
# delete any comments and check everything else. Note that
|
||||
# ".*?" is a non-greedy match, so that we don't get confused by
|
||||
# multiple comments on the same line.
|
||||
#
|
||||
s/\/\*.*?\*\///g;
|
||||
s/\/\/.*$//; # C++ comments
|
||||
|
||||
# delete any trailing whitespace; we have already checked for that.
|
||||
s/\s*$//;
|
||||
|
||||
# following checks do not apply to text in comments.
|
||||
my $quote = $config{"literal-string-quote"};
|
||||
if ($quote eq "single") {
|
||||
if (/"/) {
|
||||
err("literal string using double-quote instead of single");
|
||||
}
|
||||
} elsif ($quote eq "double") {
|
||||
if (/'/) {
|
||||
err("literal string using single-quote instead of double");
|
||||
}
|
||||
}
|
||||
|
||||
if (/[^=!<>\s][!<>=]=/ || /[^<>!=][!<>=]==?[^\s,=]/ ||
|
||||
(/[^->]>[^,=>\s]/ && !/[^->]>$/) ||
|
||||
(/[^<]<[^,=<\s]/ && !/[^<]<$/) ||
|
||||
/[^<\s]<[^<]/ || /[^->\s]>[^>]/) {
|
||||
err("missing space around relational operator");
|
||||
}
|
||||
if (/\S>>=/ || /\S<<=/ || />>=\S/ || /<<=\S/ || /\S[-+*\/&|^%]=/ ||
|
||||
(/[^-+*\/&|^%!<>=\s]=[^=]/ && !/[^-+*\/&|^%!<>=\s]=$/) ||
|
||||
(/[^!<>=]=[^=\s]/ && !/[^!<>=]=$/)) {
|
||||
# XXX - should only check this for C++ code
|
||||
# XXX - there are probably other forms that should be allowed
|
||||
if (!/\soperator=/) {
|
||||
err("missing space around assignment operator");
|
||||
}
|
||||
}
|
||||
if (/[,;]\S/ && !/\bfor \(;;\)/ &&
|
||||
# Allow a comma in a regex quantifier.
|
||||
!/\/.*?\{\d+,?\d*\}.*?\//) {
|
||||
err("comma or semicolon followed by non-blank");
|
||||
}
|
||||
# check for commas preceded by blanks
|
||||
if ((!$config{"leading-comma-ok"} && /^\s*,/) || (!/^\s*,/ && /\s,/)) {
|
||||
err("comma preceded by blank");
|
||||
}
|
||||
# check for semicolons preceded by blanks
|
||||
# allow "for" statements to have empty "while" clauses
|
||||
if (/\s;/ && !/^[\t]+;$/ && !/^\s*for \([^;]*; ;[^;]*\)/) {
|
||||
err("semicolon preceded by blank");
|
||||
}
|
||||
if (!$config{"continuation-at-front"} && /^\s*(&&|\|\|)/) {
|
||||
err("improper boolean continuation");
|
||||
} elsif ($config{"continuation-at-front"} && /(&&|\|\||\+)$/) {
|
||||
err("improper continuation");
|
||||
}
|
||||
if (/\S *(&&|\|\|)/ || /(&&|\|\|) *\S/) {
|
||||
err("more than one space around boolean operator");
|
||||
}
|
||||
# We allow methods which look like obj.delete() but not keywords without
|
||||
# spaces ala: delete(obj)
|
||||
if (/(?<!\.)\b(delete|typeof|instanceof|throw|with|catch|new|function|in|for|if|while|switch|return|case)\(/) {
|
||||
err("missing space between keyword and paren");
|
||||
}
|
||||
if (/(\b(catch|for|if|with|while|switch|return)\b.*){2,}/) {
|
||||
# multiple "case" and "sizeof" allowed
|
||||
err("more than one keyword on line");
|
||||
}
|
||||
if (/\b(delete|typeof|instanceOf|with|throw|catch|new|function|in|for|if|while|switch|return|case)\s\s+\(/ &&
|
||||
!/^#if\s+\(/) {
|
||||
err("extra space between keyword and paren");
|
||||
}
|
||||
# try to detect "func (x)" but not "if (x)" or
|
||||
# "#define foo (x)" or "int (*func)();"
|
||||
if (/\w\s\(/) {
|
||||
my $s = $_;
|
||||
# strip off all keywords on the line
|
||||
s/\b(delete|typeof|instanceOf|throw|with|catch|new|function|in|for|if|while|switch|return|case)\s\(/XXX(/g;
|
||||
s/#elif\s\(/XXX(/g;
|
||||
s/^#define\s+\w+\s+\(/XXX(/;
|
||||
# do not match things like "void (*f)();"
|
||||
# or "typedef void (func_t)();"
|
||||
s/\w\s\(+\*/XXX(*/g;
|
||||
s/\b(void)\s+\(+/XXX(/og;
|
||||
if (/\w\s\(/) {
|
||||
err("extra space between function name and left paren");
|
||||
}
|
||||
$_ = $s;
|
||||
}
|
||||
|
||||
if ($config{"unparenthesized-return"} &&
|
||||
/^\s*return\W[^;]*;/ && !/^\s*return\s*\(.*\);/) {
|
||||
err("unparenthesized return expression");
|
||||
}
|
||||
if (/\btypeof\b/ && !/\btypeof\s*\(.*\)/) {
|
||||
err("unparenthesized typeof expression");
|
||||
}
|
||||
if (!$config{"whitespace-after-left-paren-ok"} && /\(\s/) {
|
||||
err("whitespace after left paren");
|
||||
}
|
||||
# allow "for" statements to have empty "continue" clauses
|
||||
if (/\s\)/ && !/^\s*for \([^;]*;[^;]*; \)/) {
|
||||
if ($config{"leading-right-paren-ok"} && /^\s+\)/) {
|
||||
# this is allowed
|
||||
} else {
|
||||
err("whitespace before right paren");
|
||||
}
|
||||
}
|
||||
if (/^\s*\(void\)[^ ]/) {
|
||||
err("missing space after (void) cast");
|
||||
}
|
||||
if (/\S{/ && !/({|\(){/ &&
|
||||
# Allow a brace in a regex quantifier.
|
||||
!/\/.*?\{\d+,?\d*\}.*?\//) {
|
||||
err("missing space before left brace");
|
||||
}
|
||||
if ($in_function && /^\s+{/ &&
|
||||
($prev =~ /\)\s*$/ || $prev =~ /\bstruct\s+\w+$/)) {
|
||||
err("left brace starting a line");
|
||||
}
|
||||
if (/}(else|while)/) {
|
||||
err("missing space after right brace");
|
||||
}
|
||||
if (/}\s\s+(else|while)/) {
|
||||
err("extra space after right brace");
|
||||
}
|
||||
if (/^\s+#/) {
|
||||
err("preprocessor statement not in column 1");
|
||||
}
|
||||
if (/^#\s/) {
|
||||
err("blank after preprocessor #");
|
||||
}
|
||||
|
||||
#
|
||||
# We completely ignore, for purposes of indentation:
|
||||
# * lines outside of functions
|
||||
# * preprocessor lines
|
||||
#
|
||||
if ($check_continuation && $in_function && !$in_cpp) {
|
||||
process_indent($_);
|
||||
}
|
||||
|
||||
if (/^\s*else\W/) {
|
||||
if (!$config{"uncuddled-else-ok"} && $prev =~ /^\s*}$/) {
|
||||
err_prefix($prev,
|
||||
"else and right brace should be on same line");
|
||||
}
|
||||
}
|
||||
$prev = $line;
|
||||
}
|
||||
|
||||
if ($prev eq "") {
|
||||
err("last line in file is blank");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#
|
||||
# Continuation-line checking
|
||||
#
|
||||
# The rest of this file contains the code for the continuation checking
|
||||
# engine. It's a pretty simple state machine which tracks the expression
|
||||
# depth (unmatched '('s and '['s).
|
||||
#
|
||||
# Keep in mind that the argument to process_indent() has already been heavily
|
||||
# processed; all comments have been replaced by control-A, and the contents of
|
||||
# strings and character constants have been elided.
|
||||
#
|
||||
|
||||
my $cont_in; # currently inside of a continuation
|
||||
my $cont_off; # skipping an initializer or definition
|
||||
my $cont_noerr; # suppress cascading errors
|
||||
my $cont_start; # the line being continued
|
||||
my $cont_base; # the base indentation
|
||||
my $cont_first; # this is the first line of a statement
|
||||
my $cont_multiseg; # this continuation has multiple segments
|
||||
|
||||
my $cont_special; # this is a C statement (if, for, etc.)
|
||||
my $cont_macro; # this is a macro
|
||||
my $cont_case; # this is a multi-line case
|
||||
|
||||
my @cont_paren; # the stack of unmatched ( and [s we've seen
|
||||
|
||||
sub
|
||||
reset_indent()
|
||||
{
|
||||
$cont_in = 0;
|
||||
$cont_off = 0;
|
||||
}
|
||||
|
||||
sub
|
||||
delabel($)
|
||||
{
|
||||
#
|
||||
# replace labels with tabs. Note that there may be multiple
|
||||
# labels on a line.
|
||||
#
|
||||
local $_ = $_[0];
|
||||
|
||||
while (/^(\t*)( *(?:(?:\w+\s*)|(?:case\b[^:]*)): *)(.*)$/) {
|
||||
my ($pre_tabs, $label, $rest) = ($1, $2, $3);
|
||||
$_ = $pre_tabs;
|
||||
while ($label =~ s/^([^\t]*)(\t+)//) {
|
||||
$_ .= "\t" x (length($2) + length($1) / 8);
|
||||
}
|
||||
$_ .= ("\t" x (length($label) / 8)).$rest;
|
||||
}
|
||||
|
||||
return ($_);
|
||||
}
|
||||
|
||||
sub
|
||||
process_indent($)
|
||||
{
|
||||
require strict;
|
||||
local $_ = $_[0]; # preserve the global $_
|
||||
|
||||
s///g; # No comments
|
||||
s/\s+$//; # Strip trailing whitespace
|
||||
|
||||
return if (/^$/); # skip empty lines
|
||||
|
||||
# regexps used below; keywords taking (), macros, and continued cases
|
||||
my $special = '(?:(?:\}\s*)?else\s+)?(?:if|for|while|switch)\b';
|
||||
my $macro = '[A-Z_][A-Z_0-9]*\(';
|
||||
my $case = 'case\b[^:]*$';
|
||||
|
||||
# skip over enumerations, array definitions, initializers, etc.
|
||||
if ($cont_off <= 0 && !/^\s*$special/ &&
|
||||
(/(?:(?:\b(?:enum|struct|union)\s*[^\{]*)|(?:\s+=\s*)){/ ||
|
||||
(/^\s*{/ && $prev =~ /=\s*(?:\/\*.*\*\/\s*)*$/))) {
|
||||
$cont_in = 0;
|
||||
$cont_off = tr/{/{/ - tr/}/}/;
|
||||
return;
|
||||
}
|
||||
if ($cont_off) {
|
||||
$cont_off += tr/{/{/ - tr/}/}/;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$cont_in) {
|
||||
$cont_start = $line;
|
||||
|
||||
if (/^\t* /) {
|
||||
err("non-continuation indented 4 spaces");
|
||||
$cont_noerr = 1; # stop reporting
|
||||
}
|
||||
$_ = delabel($_); # replace labels with tabs
|
||||
|
||||
# check if the statement is complete
|
||||
return if (/^\s*\}?$/);
|
||||
return if (/^\s*\}?\s*else\s*\{?$/);
|
||||
return if (/^\s*do\s*\{?$/);
|
||||
return if (/{$/);
|
||||
return if (/}[,;]?$/);
|
||||
|
||||
# Allow macros on their own lines
|
||||
return if (/^\s*[A-Z_][A-Z_0-9]*$/);
|
||||
|
||||
# cases we don't deal with, generally non-kosher
|
||||
if (/{/) {
|
||||
err("stuff after {");
|
||||
return;
|
||||
}
|
||||
|
||||
# Get the base line, and set up the state machine
|
||||
/^(\t*)/;
|
||||
$cont_base = $1;
|
||||
$cont_in = 1;
|
||||
@cont_paren = ();
|
||||
$cont_first = 1;
|
||||
$cont_multiseg = 0;
|
||||
|
||||
# certain things need special processing
|
||||
$cont_special = /^\s*$special/? 1 : 0;
|
||||
$cont_macro = /^\s*$macro/? 1 : 0;
|
||||
$cont_case = /^\s*$case/? 1 : 0;
|
||||
} else {
|
||||
$cont_first = 0;
|
||||
|
||||
# Strings may be pulled back to an earlier (half-)tabstop
|
||||
unless ($cont_noerr || /^$cont_base / ||
|
||||
(/^\t*(?: )?(?:gettext\()?\"/ && !/^$cont_base\t/)) {
|
||||
err_prefix($cont_start,
|
||||
"continuation should be indented 4 spaces");
|
||||
}
|
||||
}
|
||||
|
||||
my $rest = $_; # keeps the remainder of the line
|
||||
|
||||
#
|
||||
# The split matches 0 characters, so that each 'special' character
|
||||
# is processed separately. Parens and brackets are pushed and
|
||||
# popped off the @cont_paren stack. For normal processing, we wait
|
||||
# until a ; or { terminates the statement. "special" processing
|
||||
# (if/for/while/switch) is allowed to stop when the stack empties,
|
||||
# as is macro processing. Case statements are terminated with a :
|
||||
# and an empty paren stack.
|
||||
#
|
||||
foreach $_ (split /[^\(\)\[\]\{\}\;\:]*/) {
|
||||
next if (length($_) == 0);
|
||||
|
||||
# rest contains the remainder of the line
|
||||
my $rxp = "[^\Q$_\E]*\Q$_\E";
|
||||
$rest =~ s/^$rxp//;
|
||||
|
||||
if (/\(/ || /\[/) {
|
||||
push @cont_paren, $_;
|
||||
} elsif (/\)/ || /\]/) {
|
||||
my $cur = $_;
|
||||
tr/\)\]/\(\[/;
|
||||
|
||||
my $old = (pop @cont_paren);
|
||||
if (!defined($old)) {
|
||||
err("unexpected '$cur'");
|
||||
$cont_in = 0;
|
||||
last;
|
||||
} elsif ($old ne $_) {
|
||||
err("'$cur' mismatched with '$old'");
|
||||
$cont_in = 0;
|
||||
last;
|
||||
}
|
||||
|
||||
#
|
||||
# If the stack is now empty, do special processing
|
||||
# for if/for/while/switch and macro statements.
|
||||
#
|
||||
next if (@cont_paren != 0);
|
||||
if ($cont_special) {
|
||||
if ($rest =~ /^\s*{?$/) {
|
||||
$cont_in = 0;
|
||||
last;
|
||||
}
|
||||
if ($rest =~ /^\s*;$/) {
|
||||
err("empty if/for/while body ".
|
||||
"not on its own line");
|
||||
$cont_in = 0;
|
||||
last;
|
||||
}
|
||||
if (!$cont_first && $cont_multiseg == 1) {
|
||||
err_prefix($cont_start,
|
||||
"multiple statements continued ".
|
||||
"over multiple lines");
|
||||
$cont_multiseg = 2;
|
||||
} elsif ($cont_multiseg == 0) {
|
||||
$cont_multiseg = 1;
|
||||
}
|
||||
# We've finished this section, start
|
||||
# processing the next.
|
||||
goto section_ended;
|
||||
}
|
||||
if ($cont_macro) {
|
||||
if ($rest =~ /^$/) {
|
||||
$cont_in = 0;
|
||||
last;
|
||||
}
|
||||
}
|
||||
} elsif (/\;/) {
|
||||
if ($cont_case) {
|
||||
err("unexpected ;");
|
||||
} elsif (!$cont_special) {
|
||||
err("unexpected ;") if (@cont_paren != 0);
|
||||
if (!$cont_first && $cont_multiseg == 1) {
|
||||
err_prefix($cont_start,
|
||||
"multiple statements continued ".
|
||||
"over multiple lines");
|
||||
$cont_multiseg = 2;
|
||||
} elsif ($cont_multiseg == 0) {
|
||||
$cont_multiseg = 1;
|
||||
}
|
||||
if ($rest =~ /^$/) {
|
||||
$cont_in = 0;
|
||||
last;
|
||||
}
|
||||
if ($rest =~ /^\s*special/) {
|
||||
err("if/for/while/switch not started ".
|
||||
"on its own line");
|
||||
}
|
||||
goto section_ended;
|
||||
}
|
||||
} elsif (/\{/) {
|
||||
err("{ while in parens/brackets") if (@cont_paren != 0);
|
||||
err("stuff after {") if ($rest =~ /[^\s}]/);
|
||||
$cont_in = 0;
|
||||
last;
|
||||
} elsif (/\}/) {
|
||||
err("} while in parens/brackets") if (@cont_paren != 0);
|
||||
if (!$cont_special && $rest !~ /^\s*(while|else)\b/) {
|
||||
if ($rest =~ /^$/) {
|
||||
err("unexpected }");
|
||||
} else {
|
||||
err("stuff after }");
|
||||
}
|
||||
$cont_in = 0;
|
||||
last;
|
||||
}
|
||||
} elsif (/\:/ && $cont_case && @cont_paren == 0) {
|
||||
err("stuff after multi-line case") if ($rest !~ /$^/);
|
||||
$cont_in = 0;
|
||||
last;
|
||||
}
|
||||
next;
|
||||
section_ended:
|
||||
# End of a statement or if/while/for loop. Reset
|
||||
# cont_special and cont_macro based on the rest of the
|
||||
# line.
|
||||
$cont_special = ($rest =~ /^\s*$special/)? 1 : 0;
|
||||
$cont_macro = ($rest =~ /^\s*$macro/)? 1 : 0;
|
||||
$cont_case = 0;
|
||||
next;
|
||||
}
|
||||
$cont_noerr = 0 if (!$cont_in);
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
|
||||
# building pycairo (needed for graphite)
|
||||
|
||||
wget http://cairographics.org/releases/pycairo-1.10.0.tar.bz2
|
||||
|
||||
hack pycairo wscript:
|
||||
#ctx.check_python_version((3,1,0))
|
||||
|
||||
brew install cairo
|
||||
LDFLAGS -L/usr/local/Cellar/cairo/1.10.2/lib
|
||||
CPPFLAGS -I/usr/local/Cellar/cairo/1.10.2/include
|
||||
|
||||
PKG_CONFIG_PATH=/usr/local/Cellar/cairo/1.10.2/lib/pkgconfig ./waf configure
|
||||
|
||||
FAIL so far.
|
|
@ -13,7 +13,7 @@
|
|||
* Result: Another order of magnitude.
|
||||
*/
|
||||
|
||||
var ben = require('ben'); // npm install ben
|
||||
var ben = require('./ben'); // npm install ben
|
||||
var Logger = require('../lib/bunyan');
|
||||
|
||||
var log = new Logger({
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
console.log('Time JSON.stringify and alternatives in Logger._emit:');
|
||||
|
||||
var ben = require('ben'); // npm install ben
|
||||
var ben = require('./ben'); // npm install ben
|
||||
var bunyan = require('../lib/bunyan');
|
||||
|
||||
function Collector() {}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
console.log('Time log.trace() when log level is "info":');
|
||||
|
||||
var ben = require('ben'); // npm install ben
|
||||
var ben = require('./ben'); // npm install ben
|
||||
var bunyan = require('../lib/bunyan');
|
||||
|
||||
function Collector() {}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
console.log('Time adding "src" field with call source info:');
|
||||
|
||||
var ben = require('ben'); // npm install ben
|
||||
var ben = require('./ben'); // npm install ben
|
||||
var Logger = require('../lib/bunyan');
|
||||
|
||||
var records = [];
|
||||
|
|
Loading…
Reference in New Issue