Skip to content

Commit

Permalink
refactor(error): improve default error handler
Browse files Browse the repository at this point in the history
Show date, pid, child class name, error addition property.
e.g.:

```log
[Thu Nov 06 2014 11:28:50 GMT+0800 (CST)] ERROR 64170 [sdk-base] Unhandle SomeServiceClientError: mock error 1, stack:
Error: mock error 1
    at null._onTimeout (/Users/mk2/git/sdk-base/test/index.test.js:34:19)
    at Timer.listOnTimeout (timers.js:133:15)
{ [SomeServiceClientError: mock error 1]
  data: { foo: 'bar', url: '/foo' },
  name: 'SomeServiceClientError' }
```
  • Loading branch information
fengmk2 committed Nov 6, 2014
1 parent 2318c44 commit 4794c81
Show file tree
Hide file tree
Showing 9 changed files with 200 additions and 40 deletions.
4 changes: 4 additions & 0 deletions .jshintignore
@@ -0,0 +1,4 @@
node_modules/
coverage/
.tmp/
.git/
95 changes: 95 additions & 0 deletions .jshintrc
@@ -0,0 +1,95 @@
{
// JSHint Default Configuration File (as on JSHint website)
// See http://jshint.com/docs/ for more details

"maxerr" : 50, // {int} Maximum error before stopping

// Enforcing
"bitwise" : true, // true: Prohibit bitwise operators (&, |, ^, etc.)
"camelcase" : false, // true: Identifiers must be in camelCase
"curly" : true, // true: Require {} for every new block or scope
"eqeqeq" : true, // true: Require triple equals (===) for comparison
"forin" : false, // true: Require filtering for..in loops with obj.hasOwnProperty()
"immed" : false, // true: Require immediate invocations to be wrapped in parens e.g. `(function () { } ());`
"indent" : false, // {int} Number of spaces to use for indentation
"latedef" : false, // true: Require variables/functions to be defined before being used
"newcap" : false, // true: Require capitalization of all constructor functions e.g. `new F()`
"noarg" : true, // true: Prohibit use of `arguments.caller` and `arguments.callee`
"noempty" : true, // true: Prohibit use of empty blocks
"nonew" : false, // true: Prohibit use of constructors for side-effects (without assignment)
"plusplus" : false, // true: Prohibit use of `++` & `--`
"quotmark" : false, // Quotation mark consistency:
// false : do nothing (default)
// true : ensure whatever is used is consistent
// "single" : require single quotes
// "double" : require double quotes
"undef" : true, // true: Require all non-global variables to be declared (prevents global leaks)
"unused" : true, // true: Require all defined variables be used
"strict" : true, // true: Requires all functions run in ES5 Strict Mode
"trailing" : false, // true: Prohibit trailing whitespaces
"maxparams" : false, // {int} Max number of formal params allowed per function
"maxdepth" : false, // {int} Max depth of nested blocks (within functions)
"maxstatements" : false, // {int} Max number statements per function
"maxcomplexity" : false, // {int} Max cyclomatic complexity per function
"maxlen" : false, // {int} Max number of characters per line

// Relaxing
"asi" : false, // true: Tolerate Automatic Semicolon Insertion (no semicolons)
"boss" : true, // true: Tolerate assignments where comparisons would be expected
"debug" : false, // true: Allow debugger statements e.g. browser breakpoints.
"eqnull" : false, // true: Tolerate use of `== null`
"es5" : false, // true: Allow ES5 syntax (ex: getters and setters)
"esnext" : true, // true: Allow ES.next (ES6) syntax (ex: `const`)
"moz" : false, // true: Allow Mozilla specific syntax (extends and overrides esnext features)
// (ex: `for each`, multiple try/catch, function expression…)
"evil" : false, // true: Tolerate use of `eval` and `new Function()`
"expr" : true, // true: Tolerate `ExpressionStatement` as Programs
"funcscope" : false, // true: Tolerate defining variables inside control statements"
"globalstrict" : false, // true: Allow global "use strict" (also enables 'strict')
"iterator" : false, // true: Tolerate using the `__iterator__` property
"lastsemic" : false, // true: Tolerate omitting a semicolon for the last statement of a 1-line block
"laxbreak" : true, // true: Tolerate possibly unsafe line breakings
"laxcomma" : false, // true: Tolerate comma-first style coding
"loopfunc" : false, // true: Tolerate functions being defined in loops
"multistr" : true, // true: Tolerate multi-line strings
"proto" : false, // true: Tolerate using the `__proto__` property
"scripturl" : false, // true: Tolerate script-targeted URLs
"smarttabs" : false, // true: Tolerate mixed tabs/spaces when used for alignment
"shadow" : true, // true: Allows re-define variables later in code e.g. `var x=1; x=2;`
"sub" : false, // true: Tolerate using `[]` notation when it can still be expressed in dot notation
"supernew" : false, // true: Tolerate `new function () { ... };` and `new Object;`
"validthis" : false, // true: Tolerate using this in a non-constructor function

// Environments
"browser" : true, // Web Browser (window, document, etc)
"couch" : false, // CouchDB
"devel" : true, // Development/debugging (alert, confirm, etc)
"dojo" : false, // Dojo Toolkit
"jquery" : false, // jQuery
"mootools" : false, // MooTools
"node" : true, // Node.js
"nonstandard" : false, // Widely adopted globals (escape, unescape, etc)
"prototypejs" : false, // Prototype and Scriptaculous
"rhino" : false, // Rhino
"worker" : false, // Web Workers
"wsh" : false, // Windows Scripting Host
"yui" : false, // Yahoo User Interface
"noyield" : true, // allow generators without a yield

// Legacy
"nomen" : false, // true: Prohibit dangling `_` in variables
"onevar" : false, // true: Allow only one `var` statement per function
"passfail" : false, // true: Stop on first error
"white" : false, // true: Check against strict whitespace and indentation rules

// Custom Globals
"globals" : { // additional predefined global variables
// mocha
"describe": true,
"it": true,
"before": true,
"afterEach": true,
"beforeEach": true,
"after": true
}
}
7 changes: 4 additions & 3 deletions .npmignore
@@ -1,5 +1,6 @@
benchmark/
test/
cov/
covrage/
.travis.yml
.editorconfig
Makefile
covrage.html
.jshint*
3 changes: 2 additions & 1 deletion .travis.yml
Expand Up @@ -3,4 +3,5 @@ node_js:
- "0.8"
- "0.10"
- "0.11"
script: "make test"
script: "make test-travis"
after_script: "npm install coveralls@2 && cat ./coverage/lcov.info | coveralls"
6 changes: 3 additions & 3 deletions Makefile
@@ -1,13 +1,13 @@
TESTS = test/*.test.js
REPORTER = tap
TIMEOUT = 3000
REPORTER = spec
TIMEOUT = 1000
MOCHA_OPTS =

install:
@npm install --registry=http://registry.npm.taobao.org

test:
@NODE_ENV=test ./node_modules/mocha/bin/mocha \
@NODE_ENV=test ./node_modules/.bin/mocha \
--reporter $(REPORTER) \
--timeout $(TIMEOUT) \
--require should \
Expand Down
19 changes: 14 additions & 5 deletions README.md
Expand Up @@ -3,19 +3,29 @@ sdk-base

[![NPM version][npm-image]][npm-url]
[![build status][travis-image]][travis-url]
[![node version][node-image]][node-url]
[![Test coverage][coveralls-image]][coveralls-url]
[![Gittip][gittip-image]][gittip-url]
[![David deps][david-image]][david-url]
[![node version][node-image]][node-url]
[![npm download][download-image]][download-url]

[npm-image]: https://img.shields.io/npm/v/sdk-base.svg?style=flat-square
[npm-url]: https://npmjs.org/package/sdk-base
[travis-image]: https://img.shields.io/travis/node-modules/sdk-base.svg?style=flat-square
[travis-url]: https://travis-ci.org/node-modules/sdk-base
[node-image]: https://img.shields.io/badge/node.js-%3E=_0.8-green.svg?style=flat-square
[node-url]: http://nodejs.org/download/
[coveralls-image]: https://img.shields.io/coveralls/node-modules/sdk-base.svg?style=flat-square
[coveralls-url]: https://coveralls.io/r/node-modules/sdk-base?branch=master
[gittip-image]: https://img.shields.io/gittip/dead-horse.svg?style=flat-square
[gittip-url]: https://www.gittip.com/dead-horse/
[david-image]: https://img.shields.io/david/node-modules/sdk-base.svg?style=flat-square
[david-url]: https://david-dm.org/node-modules/sdk-base
[node-image]: https://img.shields.io/badge/node.js-%3E=_0.8-green.svg?style=flat-square
[node-url]: http://nodejs.org/download/
[download-image]: https://img.shields.io/npm/dm/sdk-base.svg?style=flat-square
[download-url]: https://npmjs.org/package/sdk-base

a base class for sdk with default error handler.

A base class for sdk with default error handler.

## Installation

Expand All @@ -34,7 +44,6 @@ function Client() {
}

util.inherits(Client, Base);

```

### License
Expand Down
38 changes: 25 additions & 13 deletions index.js
@@ -1,7 +1,12 @@
/*!
/**!
* sdk-base - index.js
* Copyright(c) 2014 dead_horse <dead_horse@qq.com>
*
* Copyright(c) dead_horse and other contributors.
* MIT Licensed
*
* Authors:
* dead_horse <dead_horse@qq.com>
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/

'use strict';
Expand All @@ -19,12 +24,16 @@ var defer = global.setImmediate
? setImmediate
: process.nextTick;

function Base () {
function Base() {
// defer bind default error handler
var self = this;
defer(function () {
if (!self.listeners('error').length) self.on('error', onerror);
});
if (!this.listeners('error').length) {
/**
* default error handler
*/
this.on('error', this.defaultErrorHandler.bind(this));
}
}.bind(this));
EventEmitter.call(this);
}

Expand All @@ -34,10 +43,13 @@ function Base () {

inherits(Base, EventEmitter);

/**
* default error handler
*/

function onerror(err) {
if (process.env.NODE_ENV !== 'test') console.error(err.stack);
}
Base.prototype.defaultErrorHandler = function (err) {
if (err.name === 'Error') {
err.name = this.constructor.name + 'Error';
}
console.error('[%s] ERROR %s [sdk-base] Unhandle %s: %s, stack:\n%s',
Date(), process.pid, err.name, err.message, err.stack);
// try to should addition property on the error object
// e.g.: `err.data = {url: '/foo'};`
console.error(err);
};
5 changes: 4 additions & 1 deletion package.json
Expand Up @@ -17,9 +17,12 @@
"url": "git@github.com:node-modules/sdk-base"
},
"license": "MIT",
"dependencies": {},
"dependencies": {

},
"devDependencies": {
"autod": "~0.3.2",
"istanbul": "*",
"mocha": "~1.21.4",
"should": "~4.0.4"
},
Expand Down
63 changes: 49 additions & 14 deletions test/index.test.js
@@ -1,7 +1,12 @@
/*!
* sdk-base - index.test.js
* Copyright(c) 2014 dead_horse <dead_horse@qq.com>
/**!
* sdk-base - test/index.test.js
*
* Copyright(c) dead_horse and other contributors.
* MIT Licensed
*
* Authors:
* dead_horse <dead_horse@qq.com>
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/

'use strict';
Expand All @@ -13,34 +18,64 @@
var inherits = require('util').inherits;
var Base = require('..');

function Child() {
Base.call(this);
}
describe('sdk-base', function () {
function SomeServiceClient() {
Base.call(this);
}

inherits(Child, Base);
inherits(SomeServiceClient, Base);

describe('sdk-base', function () {
describe('default error handler', function () {
it('should ok', function (done) {
var c = new Child();
it('should auto add the default error handler', function (done) {
var c = new SomeServiceClient();
c.listeners('error').length.should.equal(0);
setTimeout(function () {
c.listeners('error').length.should.equal(1);
var err = new Error('mock error 1');
err.data = {foo: 'bar', url: '/foo'};
c.emit('error', err);
// should stderror output
// [Thu Nov 06 2014 11:14:33 GMT+0800 (CST)] ERROR 63189 [sdk-base] Unhandle SomeServiceClientError: mock error 1, stack:
// Error: mock error 1
// at null._onTimeout (/Users/mk2/git/sdk-base/test/index.test.js:29:19)
// at Timer.listOnTimeout (timers.js:133:15)
// { [SomeServiceClientError: mock error 1]
// data: { foo: 'bar', url: '/foo' },
// name: 'SomeServiceClientError' }
done();
}, 10);
});

it('should not change the error name', function (done) {
var c = new SomeServiceClient();
c.listeners('error').length.should.equal(0);
setTimeout(function () {
c.listeners('error').length.should.equal(1);
var err = new Error('mock some error');
err.name = 'SomeApiError';
c.emit('error', err);
// should stderror output
// [Thu Nov 06 2014 11:14:33 GMT+0800 (CST)] ERROR 63189 [sdk-base] Unhandle SomeApiError: mock some error, stack:
// Error: mock some error
// at null._onTimeout (/Users/mk2/git/sdk-base/test/index.test.js:29:19)
// at Timer.listOnTimeout (timers.js:133:15)
// { [SomeApiError: mock some error]
// name: 'SomeApiError' }
done();
}, 10);
});
});

describe('custom error handler', function () {
it('should ok', function (done) {
var c = new Child();
it('should use the exists error handler', function (done) {
var c = new SomeServiceClient();
c.on('error', function (err) {
err.message.should.equal('mock');
err.message.should.equal('mock error 2');
});
c.listeners('error').length.should.equal(1);
setTimeout(function () {
c.listeners('error').length.should.equal(1);
c.emit('error', new Error('mock'));
c.emit('error', new Error('mock error 2'));
done();
}, 10);
});
Expand Down

0 comments on commit 4794c81

Please sign in to comment.