Skip to content

Commit

Permalink
Merge pull request #35 from yosuke-furukawa/add_auto_end_feature
Browse files Browse the repository at this point in the history
feat: add automatic stop if timer is not ended
  • Loading branch information
yosuke-furukawa committed Dec 17, 2019
2 parents 588162c + 737f4ae commit a7e3980
Show file tree
Hide file tree
Showing 8 changed files with 81 additions and 23 deletions.
10 changes: 8 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ app.use((req, res, next) => {
}, 100);
next();
});
app.use((req, res, next) => {
// you can see test end time response
res.startTime('test', 'forget to call endTime');
next();
});
app.use((req, res, next) => {
// All timings should be in milliseconds (s). See issue #9 (https://github.com/yosuke-furukawa/server-timing/issues/9).
res.setMetric('db', 100.0, 'Database metric');
Expand All @@ -46,8 +51,9 @@ app.use((req, res, next) => {

## constructor(options)

- options.total: boolean, default `true`
- options.enabled: boolean, default `true`
- options.total: boolean, default `true`, add total response time
- options.enabled: boolean, default `true`, enable server timing header
- options.autoEnd: boolean, default `true` automatically endTime is called if timer is not finished.

# Result

Expand Down
4 changes: 4 additions & 0 deletions example/express_app.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ app.use((req, res, next) => {
next()
}, 1000)
})
app.use((req, res, next) => {
res.startTime('test', 'endtime is automatically called')
next()
})
app.use((req, res, next) => {
res.send('Open DevTools and See Network tab')
})
Expand Down
26 changes: 17 additions & 9 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
const onHeaders = require('on-headers')
const Timer = require('./timer')

module.exports = function serverTiming(options) {
module.exports = function serverTiming (options) {
const opts = Object.assign({
total: true,
enabled: true
}, options);
enabled: true,
autoEnd: true
}, options)
return (_, res, next) => {
const headers = []
const timer = new Timer()
Expand All @@ -25,12 +26,19 @@ module.exports = function serverTiming(options) {
if (opts.total) {
const diff = process.hrtime(startAt)
const timeSec = (diff[0] * 1E3) + (diff[1] * 1e-6)
if (opts.autoEnd) {
const keys = timer.keys()
for (const key of keys) {
res.endTime(key)
}
}
headers.push(`total; dur=${timeSec}; desc="Total Response Time"`)
}
timer.clear()

if (opts.enabled) {
const existingHeaders = res.getHeader('Server-Timing')

res.setHeader('Server-Timing', [].concat(existingHeaders || []).concat(headers).join(', '))
}
})
Expand All @@ -40,7 +48,7 @@ module.exports = function serverTiming(options) {
}
}

function setMetric(headers) {
function setMetric (headers) {
return (name, value, description) => {
if (typeof name !== 'string') {
return console.warn('1st argument name is not string')
Expand All @@ -49,14 +57,14 @@ function setMetric(headers) {
return console.warn('2nd argument value is not number')
}

const metric = typeof description !== 'string' || !description ?
`${name}; dur=${value}` : `${name}; dur=${value}; desc="${description}"`
const metric = typeof description !== 'string' || !description
? `${name}; dur=${value}` : `${name}; dur=${value}; desc="${description}"`

headers.push(metric)
}
}

function startTime(timer) {
function startTime (timer) {
return (name, description) => {
if (typeof name !== 'string') {
return console.warn('1st argument name is not string')
Expand All @@ -66,7 +74,7 @@ function startTime(timer) {
}
}

function endTime(timer, res) {
function endTime (timer, res) {
return (name) => {
if (typeof name !== 'string') {
return console.warn('1st argument name is not string')
Expand All @@ -78,4 +86,4 @@ function endTime(timer, res) {
}
res.setMetric(obj.name, obj.value, obj.description)
}
}
}
20 changes: 10 additions & 10 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion test/express-test-set-timer.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,3 @@ test('express use startTime/endTime multiple', () => {
http.get(`http://localhost:${server.address().port}/`, mustCall(checkFunc))
})
})

17 changes: 17 additions & 0 deletions test/express-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,20 @@ test('express request twice and check idempotent', () => {
})
})

test('express stop automatic timer', () => {
const app = express()
app.use(serverTiming())
app.use((req, res, next) => {
res.startTime('hello', 'hello')
res.send('hello')
})
const server = app.listen(0, () => {
http.get(`http://localhost:${server.address().port}/`, mustCall((res) => {
const assertStream = new AssertStream()
assertStream.expect('hello')
res.pipe(assertStream)
assert(/hello; dur=.*; desc="hello", total; dur=.*; desc="Total Response Time"/.test(res.headers['server-timing']))
server.close()
}))
})
})
19 changes: 18 additions & 1 deletion test/http-basic-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,4 +114,21 @@ test('success: no response', () => {
server.close()
}))
})
})
})

test('success: stop automatically timer', () => {
const server = http.createServer((req, res) => {
serverTiming({})(req, res)
res.startTime('foo', 'foo')
res.end('hello')
}).listen(0, () => {
http.get(`http://localhost:${server.address().port}/`, mustCall((res) => {
const assertStream = new AssertStream()
assertStream.expect('hello')
res.pipe(assertStream)
assert(res.headers['server-timing'])
console.log(res.headers)
server.close()
}))
})
})
7 changes: 7 additions & 0 deletions timer.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ class Timer {
constructor () {
this._times = new Map()
}

time (name, description) {
this._times.set(name, {
name: name,
description: description,
start: process.hrtime()
})
}

timeEnd (name) {
const timeObj = this._times.get(name)
if (!timeObj) {
Expand All @@ -22,9 +24,14 @@ class Timer {
this._times.delete(name)
return timeObj
}

clear () {
this._times.clear()
}

keys () {
return this._times.keys()
}
}

module.exports = Timer

0 comments on commit a7e3980

Please sign in to comment.