Skip to content

Commit de9bc01

Browse files
committed
Add log option
closes #9
1 parent fb53836 commit de9bc01

File tree

4 files changed

+258
-10
lines changed

4 files changed

+258
-10
lines changed

HISTORY.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
unreleased
2+
==========
3+
4+
* Add `log` option
5+
16
1.2.3 / 2014-11-21
27
==================
38

README.md

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,37 @@ $ npm install errorhandler
2020
var errorhandler = require('errorhandler')
2121
```
2222

23-
### errorhandler()
23+
### errorhandler([options])
2424

2525
Create new middleware to handle errors and respond with content negotiation.
2626
This middleware is only intended to be used in a development environment, as
2727
the full error stack traces will be sent back to the client when an error
2828
occurs.
2929

30-
## Example
30+
#### options.log
31+
32+
Provide a function to be called with the error and a string representation of
33+
the error. Can be used to write the error to any desired location, or set to
34+
`false` to only send the error back in the response. Called as
35+
`log(err, str, req, res)` where `err` is the `Error` object, `str` is a string
36+
representation of the error, `req` is the request object and `res` is the
37+
response object (note, this function is invoked _after_ the response has been
38+
written).
39+
40+
The default value for this option is `true` unless `process.env.NODE_ENV === 'test'`.
41+
42+
Possible values:
43+
44+
* `true`: Log errors using `console.error(str)`.
45+
* `false`: Only send the error back in the response.
46+
* A function: pass the error to a function for handling.
47+
48+
## Examples
49+
50+
### Simple example
51+
52+
Basic example of adding this middleware as the error handler only in development
53+
with `connect` (`express` also can be used in this example).
3154

3255
```js
3356
var connect = require('connect')
@@ -41,6 +64,33 @@ if (process.env.NODE_ENV === 'development') {
4164
}
4265
```
4366

67+
### Custom output location
68+
69+
Sometimes you may want to output the errors to a different location than STDERR
70+
during development, like a system notification, for example.
71+
72+
```js
73+
var connect = require('connect')
74+
var errorhandler = require('errorhandler')
75+
var notifier = require('node-notifier')
76+
77+
var app = connect()
78+
79+
if (process.env.NODE_ENV === 'development') {
80+
// only use in development
81+
app.use(errorhandler({log: errorNotification}))
82+
}
83+
84+
function errorNotification(err, str, req) {
85+
var title = 'Error in ' + req.method + ' ' + req.url
86+
87+
notifier.notify({
88+
title: title,
89+
message: str
90+
})
91+
}
92+
```
93+
4494
## License
4595

4696
[MIT](LICENSE)

index.js

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@
33
* Copyright(c) 2010 Sencha Inc.
44
* Copyright(c) 2011 TJ Holowaychuk
55
* Copyright(c) 2014 Jonathan Ong
6+
* Copyright(c) 2014 Douglas Christopher Wilson
67
* MIT Licensed
78
*/
89

910
/**
1011
* Module dependencies.
12+
* @private
1113
*/
1214

1315
var accepts = require('accepts')
@@ -17,11 +19,17 @@ var util = require('util')
1719

1820
/**
1921
* Module variables.
22+
* @private
2023
*/
2124

2225
var inspect = util.inspect
2326
var toString = Object.prototype.toString
2427

28+
/* istanbul ignore next */
29+
var defer = typeof setImmediate === 'function'
30+
? setImmediate
31+
: function(fn){ process.nextTick(fn.bind.apply(fn, arguments)) }
32+
2533
/**
2634
* Error handler:
2735
*
@@ -47,10 +55,29 @@ var toString = Object.prototype.toString
4755
* @api public
4856
*/
4957

50-
exports = module.exports = function errorHandler(){
58+
exports = module.exports = function errorHandler(options) {
5159
// get environment
5260
var env = process.env.NODE_ENV || 'development'
5361

62+
// get options
63+
var opts = options || {}
64+
65+
// get log option
66+
var log = opts.log === undefined
67+
? env !== 'test'
68+
: opts.log
69+
70+
if (typeof log !== 'function' && typeof log !== 'boolean') {
71+
throw new TypeError('option log must be function or boolean')
72+
}
73+
74+
// default logging using console.error
75+
if (log === true) {
76+
log = function logerror(err, str) {
77+
console.error(str)
78+
}
79+
}
80+
5481
return function errorHandler(err, req, res, next){
5582
// respect err.status
5683
if (err.status) {
@@ -62,9 +89,10 @@ exports = module.exports = function errorHandler(){
6289
res.statusCode = 500
6390
}
6491

65-
// write error to console
66-
if (env !== 'test') {
67-
console.error(stringify(err))
92+
// log the error
93+
var str = stringify(err)
94+
if (log) {
95+
defer(log, err, str, req, res)
6896
}
6997

7098
// cannot actually respond
@@ -93,7 +121,7 @@ exports = module.exports = function errorHandler(){
93121
.replace('{stack}', stack)
94122
.replace('{title}', escapeHtml(exports.title))
95123
.replace('{statusCode}', res.statusCode)
96-
.replace(/\{error\}/g, escapeHtml(stringify(err)).replace(/ /g, ' &nbsp;').replace(/\n/g, '<br>'));
124+
.replace(/\{error\}/g, escapeHtml(str).replace(/ /g, ' &nbsp;').replace(/\n/g, '<br>'))
97125
res.setHeader('Content-Type', 'text/html; charset=utf-8');
98126
res.end(html);
99127
});
@@ -108,7 +136,7 @@ exports = module.exports = function errorHandler(){
108136
// plain text
109137
} else {
110138
res.setHeader('Content-Type', 'text/plain');
111-
res.end(stringify(err));
139+
res.end(str)
112140
}
113141
};
114142
};

test/test.js

Lines changed: 167 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -211,8 +211,161 @@ describe('errorHandler()', function () {
211211
})
212212
})
213213

214-
function createServer(error) {
215-
var _errorHandler = errorHandler()
214+
describe('errorHandler(options)', function () {
215+
describe('log', function () {
216+
it('should reject a string', function () {
217+
assert.throws(errorHandler.bind(null, {log: 'yes, please'}), /option log must be/)
218+
})
219+
220+
describe('when "undefined"', function () {
221+
var _consoleerror
222+
223+
before(function () {
224+
_consoleerror = console.error
225+
})
226+
afterEach(function () {
227+
console.error = _consoleerror
228+
})
229+
230+
describe('when NODE_ENV == test', function () {
231+
alterEnvironment('NODE_ENV', 'test')
232+
233+
it('should produce no output', function (done) {
234+
var error = new Error('boom!')
235+
var server = createServer(error)
236+
237+
console.error = function () {
238+
var log = util.format.apply(null, arguments)
239+
240+
if (log !== error.stack.toString()) {
241+
return _consoleerror.apply(this, arguments)
242+
}
243+
244+
done(new Error('console.error written to'))
245+
}
246+
247+
request(server)
248+
.get('/')
249+
.set('Accept', 'text/plain')
250+
.expect(500, error.stack.toString(), done)
251+
})
252+
})
253+
254+
describe('when NODE_ENV != test', function () {
255+
alterEnvironment('NODE_ENV', '')
256+
257+
it('should write to console', function (done) {
258+
var cb = after(2, done)
259+
var error = new Error('boom!')
260+
var server = createServer(error)
261+
262+
console.error = function () {
263+
var log = util.format.apply(null, arguments)
264+
265+
if (log !== error.stack.toString()) {
266+
return _consoleerror.apply(this, arguments)
267+
}
268+
269+
cb()
270+
}
271+
272+
request(server)
273+
.get('/')
274+
.set('Accept', 'text/plain')
275+
.expect(500, error.stack.toString(), cb)
276+
})
277+
})
278+
})
279+
280+
describe('when "true"', function () {
281+
var _consoleerror
282+
283+
before(function () {
284+
_consoleerror = console.error
285+
})
286+
afterEach(function () {
287+
console.error = _consoleerror
288+
})
289+
290+
it('should write to console', function (done) {
291+
var cb = after(2, done)
292+
var error = new Error('boom!')
293+
var server = createServer(error, {log: true})
294+
295+
console.error = function () {
296+
var log = util.format.apply(null, arguments)
297+
298+
if (log !== error.stack.toString()) {
299+
return _consoleerror.apply(this, arguments)
300+
}
301+
302+
cb()
303+
}
304+
305+
request(server)
306+
.get('/')
307+
.set('Accept', 'text/plain')
308+
.expect(500, error.stack.toString(), cb)
309+
})
310+
})
311+
312+
describe('when "false"', function () {
313+
var _consoleerror
314+
315+
alterEnvironment('NODE_ENV', '')
316+
before(function () {
317+
_consoleerror = console.error
318+
})
319+
afterEach(function () {
320+
console.error = _consoleerror
321+
})
322+
323+
it('should not write to console', function (done) {
324+
var error = new Error('boom!')
325+
var server = createServer(error, {log: false})
326+
327+
console.error = function () {
328+
var log = util.format.apply(null, arguments)
329+
330+
if (log !== error.stack.toString()) {
331+
return _consoleerror.apply(this, arguments)
332+
}
333+
334+
done(new Error('console.error written to'))
335+
}
336+
337+
request(server)
338+
.get('/')
339+
.set('Accept', 'text/plain')
340+
.expect(500, error.stack.toString(), done)
341+
})
342+
})
343+
344+
describe('when a function', function () {
345+
it('should call function', function (done) {
346+
var cb = after(2, done)
347+
var error = new Error('boom!')
348+
var server = createServer(error, {log: log})
349+
350+
function log(err, str, req, res) {
351+
assert.equal(err, error)
352+
assert.equal(str, error.stack.toString())
353+
assert.equal(req.url, '/')
354+
assert.equal(res.statusCode, 500)
355+
cb()
356+
}
357+
358+
request(server)
359+
.get('/')
360+
.set('Accept', 'text/plain')
361+
.expect(500, error.stack.toString(), cb)
362+
})
363+
})
364+
})
365+
})
366+
367+
function createServer(error, options) {
368+
var _errorHandler = errorHandler(options)
216369

217370
return http.createServer(function (req, res) {
218371
_errorHandler(error, req, res, function (err) {
@@ -221,3 +374,15 @@ function createServer(error) {
221374
})
222375
})
223376
}
377+
378+
function alterEnvironment(key, value) {
379+
var prev
380+
381+
before(function () {
382+
prev = process.env[key]
383+
process.env[key] = value
384+
})
385+
after(function () {
386+
process.env[key] = prev
387+
})
388+
}

0 commit comments

Comments
 (0)