这几天写了个 koa-yield-breakpoint模块,这个玩意主要是在 yield 表达式前后打点,主要用在 koa@1 的 routes 或 controllers 文件,用来记录每个 yield 表达式前后的现场及 yield 表达式的返回值。
特点:可以记录每个请求的流程(甚至可以查到某个用户某个时间段的请求),及每一次请求的每一步的现场及返回值,当然只针对 yield 表达式。
实现原理:
- 重载 Module.prototype._compile,hack 了 require
- 用 esprima 解析代码,生成 AST
- 遍历找到 YieldExpression,进行以下包装后生成 AST 替换掉原来的节点:
global.logger(
this,
function*(){
return YieldExpression
},
YieldExpressionString,
filename
);
- 用 escodegen 生成代码(支持 soucemap,所以错误栈对应的行数是对的)
有两点需要说明:
- 每个请求到来时,生成一个 requestId{uuid} 挂载到 this 上
- global.logger 记录请求的日志
如 example:
app.js
'use strict';
const koaYieldBreakpoint = require('koa-yield-breakpoint')({
files: ['./routes/*.js'],
// store: new require('koa-yield-breakpoint-mongodb')({
// url: 'mongodb://localhost:27017/test',
// coll: 'koa-yield-breakpoint-loggers'
// })
});
const koa = require('koa');
const routes = require('./routes');
const app = koa();
app.use(koaYieldBreakpoint);
routes(app);
app.listen(3000, () => {
console.log('listening on 3000');
});
routes/index.js
'use strict';
const route = require('koa-route');
const users = require('./users');
module.exports = function (app) {
app.use(route.get('/users', users.getUsers));
};
routes/users.js
'use strict';
const Mongolass = require('mongolass');
const mongolass = new Mongolass();
mongolass.connect('mongodb://localhost:27017/test');
exports.getUsers = function* getUsers() {
yield mongolass.model('users').create({
name: 'xx',
age: 18
});
const users = yield mongolass.model('users').find();
this.body = users;
};
打点之后的代码:
'use strict';
const Mongolass = require('mongolass');
const mongolass = new Mongolass();
mongolass.connect('mongodb://localhost:27017/test');
exports.getUsers = function* getUsers() {
yield global.logger(this, function* () {
return mongolass.model('users').create({
name: 'xx',
age: 18
});
}, 'mongolass.model(\'users\').create({\n name: \'xx\',\n age: 18\n})', '/Users/nswbmw/node/koa-yield-breakpoint/example/routes/users.js:8:2');
const users = yield global.logger(this, function* () {
return mongolass.model('users').find();
}, 'mongolass.model(\'users\').find()', '/Users/nswbmw/node/koa-yield-breakpoint/example/routes/users.js:13:16');
this.body = users;
};
访问 localhost:3000/users
,控制台打印出:
{ requestId: 'dad593c0-c4a1-4640-a00e-9ba0349cfd2f',
timestamp: Wed Oct 26 2016 18:28:49 GMT+0800 (CST),
this:
{ state: {},
request:
{ method: 'GET',
path: '/users',
header: [Object],
query: [Object] },
response: { status: 404, body: undefined } },
type: 'start',
step: 1 }
{ requestId: 'dad593c0-c4a1-4640-a00e-9ba0349cfd2f',
step: 2,
filename: '/Users/nswbmw/node/koa-yield-breakpoint/example/routes/users.js:8:2',
timestamp: Wed Oct 26 2016 18:28:49 GMT+0800 (CST),
this:
{ state: {},
request:
{ method: 'GET',
path: '/users',
header: [Object],
query: [Object] },
response: { status: 404, body: undefined } },
type: 'before',
fn: 'mongolass.model(\'users\').create({\n name: \'xx\',\n age: 18\n})',
result: undefined,
take: '1ms' }
{ requestId: 'dad593c0-c4a1-4640-a00e-9ba0349cfd2f',
step: 3,
filename: '/Users/nswbmw/node/koa-yield-breakpoint/example/routes/users.js:8:2',
timestamp: Wed Oct 26 2016 18:28:49 GMT+0800 (CST),
this:
{ state: {},
request:
{ method: 'GET',
path: '/users',
header: [Object],
query: [Object] },
response: { status: 404, body: undefined } },
type: 'after',
fn: 'mongolass.model(\'users\').create({\n name: \'xx\',\n age: 18\n})',
result:
{ result: { ok: 1, n: 1 },
ops: [ [Object] ],
insertedCount: 1,
insertedIds: [ undefined, 5810856182d0eea2f12030fd ] },
take: '7ms' }
{ requestId: 'dad593c0-c4a1-4640-a00e-9ba0349cfd2f',
step: 4,
filename: '/Users/nswbmw/node/koa-yield-breakpoint/example/routes/users.js:13:16',
timestamp: Wed Oct 26 2016 18:28:49 GMT+0800 (CST),
this:
{ state: {},
request:
{ method: 'GET',
path: '/users',
header: [Object],
query: [Object] },
response: { status: 404, body: undefined } },
type: 'before',
fn: 'mongolass.model(\'users\').find()',
result: undefined,
take: '1ms' }
{ requestId: 'dad593c0-c4a1-4640-a00e-9ba0349cfd2f',
step: 5,
filename: '/Users/nswbmw/node/koa-yield-breakpoint/example/routes/users.js:13:16',
timestamp: Wed Oct 26 2016 18:28:49 GMT+0800 (CST),
this:
{ state: {},
request:
{ method: 'GET',
path: '/users',
header: [Object],
query: [Object] },
response: { status: 404, body: undefined } },
type: 'after',
fn: 'mongolass.model(\'users\').find()',
result: [ { _id: 5810856182d0eea2f12030fd, name: 'xx', age: 18 } ],
take: '4ms' }
{ requestId: 'dad593c0-c4a1-4640-a00e-9ba0349cfd2f',
timestamp: Wed Oct 26 2016 18:28:49 GMT+0800 (CST),
this:
{ state: {},
request:
{ method: 'GET',
path: '/users',
header: [Object],
query: [Object] },
response: { status: 200, body: [Object] } },
type: 'end',
step: 6,
take: '1ms' }