Skip to content

Commit

Permalink
feat: support better logger timer in precise milliseconds
Browse files Browse the repository at this point in the history
detail see eggjs/egg-logger#74

How to use:

```js
// config.js

exports.logger = {
  enablePerformanceTimer: true,
};
```
  • Loading branch information
fengmk2 committed Nov 20, 2021
1 parent 277948d commit e3c5905
Show file tree
Hide file tree
Showing 13 changed files with 88 additions and 16 deletions.
9 changes: 6 additions & 3 deletions app/extend/context.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
'use strict';

const { performance } = require('perf_hooks');
const delegate = require('delegates');
const { assign } = require('utility');
const eggUtils = require('egg-core').utils;
Expand Down Expand Up @@ -219,19 +220,21 @@ const proto = module.exports = {
// e.g.: https://github.com/eggjs/egg-mock/pull/78
_runInBackground(scope) {
const ctx = this;
const start = Date.now();
const start = performance.now();
/* istanbul ignore next */
const taskName = scope._name || scope.name || eggUtils.getCalleeFromStack(true);
// use setImmediate to ensure all sync logic will run async
return new Promise(resolve => setImmediate(resolve))
// use app.toAsyncFunction to support both generator function and async function
.then(() => ctx.app.toAsyncFunction(scope)(ctx))
.then(() => {
ctx.coreLogger.info('[egg:background] task:%s success (%dms)', taskName, Date.now() - start);
ctx.coreLogger.info('[egg:background] task:%s success (%dms)',
taskName, Math.floor((performance.now() - start) * 1000) / 1000);
})
.catch(err => {
// background task process log
ctx.coreLogger.info('[egg:background] task:%s fail (%dms)', taskName, Date.now() - start);
ctx.coreLogger.info('[egg:background] task:%s fail (%dms)',
taskName, Math.floor((performance.now() - start) * 1000) / 1000);

// emit error when promise catch, and set err.runInBackground flag
err.runInBackground = true;
Expand Down
3 changes: 3 additions & 0 deletions config/config.default.js
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,8 @@ module.exports = appInfo => {
* @property {String} coreLogName - file name of coreLogger
* @property {String} agentLogName - file name of agent worker log
* @property {Object} coreLogger - custom config of coreLogger
* @property {Boolean} allowDebugAtProd - allow debug log at prod, defaults to false
* @property {Boolean} enablePerformanceTimer - using performance.now() timer instead of Date.now() for more more precise milliseconds, defaults to false. e.g.: logger will set 1.456ms instead of 1ms.
*/
config.logger = {
dir: path.join(appInfo.root, 'logs', appInfo.name),
Expand All @@ -265,6 +267,7 @@ module.exports = appInfo => {
errorLogName: 'common-error.log',
coreLogger: {},
allowDebugAtProd: false,
enablePerformanceTimer: false,
};

/**
Expand Down
12 changes: 10 additions & 2 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -217,10 +217,12 @@ declare module 'egg' {
export interface EggLoggerConfig extends RemoveSpecProp<EggLoggersOptions, 'type'> {
/** custom config of coreLogger */
coreLogger?: Partial<EggLoggerOptions>;
/** allow debug log at prod, defaults to true */
/** allow debug log at prod, defaults to `false` */
allowDebugAtProd?: boolean;
/** disable logger console after app ready. defaults to `false` on local and unittest env, others is `true`. */
disableConsoleAfterReady?: boolean;
/** using performance.now() timer instead of Date.now() for more more precise milliseconds, defaults to `false`. e.g.: logger will set 1.456ms instead of 1ms. */
enablePerformanceTimer?: boolean;
}

/** Custom Loader Configuration */
Expand Down Expand Up @@ -329,7 +331,8 @@ declare module 'egg' {
* @property {String} coreLogName - file name of coreLogger
* @property {String} agentLogName - file name of agent worker log
* @property {Object} coreLogger - custom config of coreLogger
* @property {Boolean} allowDebugAtProd - allow debug log at prod, defaults to true
* @property {Boolean} allowDebugAtProd - allow debug log at prod, defaults to false
* @property {Boolean} enablePerformanceTimer - using performance.now() timer instead of Date.now() for more more precise milliseconds, defaults to false. e.g.: logger will set 1.456ms instead of 1ms.
*/
logger: EggLoggerConfig;

Expand Down Expand Up @@ -924,6 +927,11 @@ declare module 'egg' {
*/
starttime: number;

/**
* Request start timer using `performance.now()`
*/
performanceStartTime?: number;

/**
* http request helper base on httpclient, it will auto save httpclient log.
* Keep the same api with httpclient.request(url, args).
Expand Down
9 changes: 9 additions & 0 deletions lib/egg.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
'use strict';

const { performance } = require('perf_hooks');
const path = require('path');
const fs = require('fs');
const ms = require('ms');
Expand Down Expand Up @@ -547,6 +548,14 @@ class EggApplication extends EggCore {
* @member {Number} Context#starttime
*/
context.starttime = Date.now();

if (this.config.logger.enablePerformanceTimer) {
/**
* Request start timer using `performance.now()`
* @member {Number} Context#performanceStarttime
*/
context.performanceStarttime = performance.now();
}
return context;
}

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@
"url": "https://github.com/eggjs/egg.git"
},
"engines": {
"node": ">= 8.0.0"
"node": ">= 8.5.0"
},
"license": "MIT"
}
16 changes: 8 additions & 8 deletions test/app/extend/context.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -259,10 +259,10 @@ describe('test/app/extend/context.test.js', () => {
assert(/background run result file size: \d+/.test(log));
assert(/background run anonymous result file size: \d+/.test(log));
assert(
/\[egg:background] task:saveUserInfo success \(\d+ms\)/.test(fs.readFileSync(path.join(logdir, 'egg-web.log'), 'utf8'))
/\[egg:background] task:saveUserInfo success \([\d\.]+ms\)/.test(fs.readFileSync(path.join(logdir, 'egg-web.log'), 'utf8'))
);
assert(
/\[egg:background] task:.*?app[\/\\]controller[\/\\]home\.js:\d+:\d+ success \(\d+ms\)/.test(fs.readFileSync(path.join(logdir, 'egg-web.log'), 'utf8'))
/\[egg:background] task:.*?app[\/\\]controller[\/\\]home\.js:\d+:\d+ success \([\d\.]+ms\)/.test(fs.readFileSync(path.join(logdir, 'egg-web.log'), 'utf8'))
);
});

Expand All @@ -277,7 +277,7 @@ describe('test/app/extend/context.test.js', () => {
const log = fs.readFileSync(path.join(logdir, 'ctx-background-web.log'), 'utf8');
assert(/background run result file size: \d+/.test(log));
assert(
/\[egg:background] task:customTaskName success \(\d+ms\)/.test(fs.readFileSync(path.join(logdir, 'egg-web.log'), 'utf8'))
/\[egg:background] task:customTaskName success \([\d\.]+ms\)/.test(fs.readFileSync(path.join(logdir, 'egg-web.log'), 'utf8'))
);
});

Expand All @@ -302,7 +302,7 @@ describe('test/app/extend/context.test.js', () => {
const log = fs.readFileSync(path.join(logdir, 'common-error.log'), 'utf8');
assert(/ENOENT: no such file or directory/.test(log));
assert(
/\[egg:background] task:mockError fail \(\d+ms\)/.test(fs.readFileSync(path.join(logdir, 'egg-web.log'), 'utf8'))
/\[egg:background] task:mockError fail \([\d\.]+ms\)/.test(fs.readFileSync(path.join(logdir, 'egg-web.log'), 'utf8'))
);
});

Expand Down Expand Up @@ -335,10 +335,10 @@ describe('test/app/extend/context.test.js', () => {
assert(/background run result file size: \d+/.test(log));
assert(/background run anonymous result file size: \d+/.test(log));
assert(
/\[egg:background] task:saveUserInfo success \(\d+ms\)/.test(fs.readFileSync(path.join(logdir, 'egg-web.log'), 'utf8'))
/\[egg:background] task:saveUserInfo success \([\d\.]+ms\)/.test(fs.readFileSync(path.join(logdir, 'egg-web.log'), 'utf8'))
);
assert(
/\[egg:background] task:.*?app[\/\\]controller[\/\\]home\.js:\d+:\d+ success \(\d+ms\)/.test(fs.readFileSync(path.join(logdir, 'egg-web.log'), 'utf8'))
/\[egg:background] task:.*?app[\/\\]controller[\/\\]home\.js:\d+:\d+ success \([\d\.]+ms\)/.test(fs.readFileSync(path.join(logdir, 'egg-web.log'), 'utf8'))
);
});

Expand All @@ -352,7 +352,7 @@ describe('test/app/extend/context.test.js', () => {
const log = fs.readFileSync(path.join(logdir, 'ctx-background-web.log'), 'utf8');
assert(/background run result file size: \d+/.test(log));
assert(
/\[egg:background] task:customTaskName success \(\d+ms\)/.test(fs.readFileSync(path.join(logdir, 'egg-web.log'), 'utf8'))
/\[egg:background] task:customTaskName success \([\d\.]+ms\)/.test(fs.readFileSync(path.join(logdir, 'egg-web.log'), 'utf8'))
);
});

Expand All @@ -376,7 +376,7 @@ describe('test/app/extend/context.test.js', () => {
const log = fs.readFileSync(path.join(logdir, 'common-error.log'), 'utf8');
assert(/ENOENT: no such file or directory/.test(log));
assert(
/\[egg:background] task:mockError fail \(\d+ms\)/.test(fs.readFileSync(path.join(logdir, 'egg-web.log'), 'utf8'))
/\[egg:background] task:mockError fail \([\d\.]+ms\)/.test(fs.readFileSync(path.join(logdir, 'egg-web.log'), 'utf8'))
);
});
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = async (ctx) => {
ctx.body = 'hello performanceStarttime: ' + ctx.performanceStarttime;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = app => {
app.get('home', '/', 'home');
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
'use strict';

exports.logger = {
level: 'DEBUG',
enablePerformanceTimer: true,
};

exports.keys = 'foo';
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
'use strict';

exports.logger = {
consoleLevel: 'NONE',
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"name": "app-enablePerformanceTimer-true"
}
4 changes: 2 additions & 2 deletions test/fixtures/apps/tracer-demo/tracer.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ module.exports = app => {
if (!req.ctx.traceId) {
req.ctx.traceId = uuid.v1();
}
req.starttime = Date.now();
req.starttime = performance.now();
req.args.headers = req.args.headers || {};
req.args.headers['x-request-id'] = req.ctx.traceId;
req.args.method = req.args.method || 'GET';
Expand All @@ -26,6 +26,6 @@ module.exports = app => {
const res = response.res;
app.logger.info('[httpclient] [%s] %s %s end, status: %s, use: %s',
req.ctx.traceId, req.args.method, req.url,
res.status, Date.now() - req.starttime);
res.status, Math.floor((performance.now() - starttime) * 1000) / 1000);
});
};
27 changes: 27 additions & 0 deletions test/lib/core/context_performance_starttime.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
'use strict';

const assert = require('assert');
const utils = require('../../utils');

describe('test/lib/core/context_performance_starttime.test.js', () => {
let app;

before(() => {
app = utils.app('apps/app-enablePerformanceTimer-true');
return app.ready();
});

it('should set ctx.performanceStarttime', () => {
const ctx = app.mockContext();
assert(ctx.performanceStarttime);
assert(ctx.performanceStarttime > 0);
assert(typeof ctx.performanceStarttime === 'number');
});

it('should use ctx.performanceStarttime on controller', async () => {
const res = await app.httpRequest()
.get('/');
assert(res.status === 200);
assert(/hello performanceStarttime: \d+\.\d+/.test(res.text));
});
});

0 comments on commit e3c5905

Please sign in to comment.