Skip to content

Commit bcdc458

Browse files
watsonStephen Belanger
authored andcommitted
refactor: use new microsecond timestamps (#616)
The start offset on spans have also been completely replaced by a microsecond timestamp that isn't affected by clock drift relative to the transaction timestamp. Closes #592 BREAKING CHANGE: The previously undocumented method `span.offsetTime()` have been removed.
1 parent 6918415 commit bcdc458

File tree

19 files changed

+78
-123
lines changed

19 files changed

+78
-123
lines changed

lib/agent.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ Agent.prototype.captureError = function (err, opts, cb) {
242242

243243
var agent = this
244244
var trans = this.currentTransaction
245-
var timestamp = new Date().toISOString()
245+
var timestamp = Date.now() * 1000
246246
var id = crypto.randomBytes(128 / 8).toString('hex')
247247
var req = opts && opts.request instanceof IncomingMessage
248248
? opts.request

lib/instrumentation/http-shared.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ exports.instrumentRequest = function (agent, moduleName) {
3232
if (!err) return trans.end()
3333

3434
if (agent._conf.errorOnAbortedRequests && !trans.ended) {
35-
var duration = Date.now() - trans._timer.start
35+
var duration = trans._timer.elapsed()
3636
if (duration > agent._conf.abortedErrorThreshold) {
3737
agent.captureError('Socket closed with active HTTP request (>' + (agent._conf.abortedErrorThreshold / 1000) + ' sec)', {
3838
request: req,

lib/instrumentation/span.js

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ function Span (transaction) {
2020
this.ended = false
2121
this.name = null
2222
this.type = null
23+
this.timestamp = null
2324
this._db = null
2425
this._timer = null
2526
this._stackObj = null
@@ -42,7 +43,8 @@ Span.prototype.start = function (name, type) {
4243
this._recordStackTrace()
4344
}
4445

45-
this._timer = new Timer()
46+
this._timer = new Timer(this.transaction._timer)
47+
this.timestamp = this._timer.start
4648

4749
this._agent.logger.debug('start span %o', { id: this.transaction.id, name: name, type: type })
4850
}
@@ -75,16 +77,7 @@ Span.prototype.duration = function () {
7577
return null
7678
}
7779

78-
return this._timer.duration()
79-
}
80-
81-
Span.prototype.offsetTime = function () {
82-
if (!this.started) {
83-
this._agent.logger.debug('tried to call span.offsetTime() for un-started span %o', { id: this.transaction.id, name: this.name, type: this.type })
84-
return null
85-
}
86-
87-
return this._timer.offset(this.transaction._timer)
80+
return this._timer.duration
8881
}
8982

9083
Span.prototype.setDbContext = function (context) {
@@ -151,10 +144,9 @@ Span.prototype._encode = function (cb) {
151144
transaction_id: self.transaction.id,
152145
parent_id: self.transaction.id,
153146
trace_id: self.transaction.traceId,
154-
timestamp: self.transaction.timestamp,
155147
name: self.name,
156148
type: self.type,
157-
start: self.offsetTime(),
149+
timestamp: self.timestamp,
158150
duration: self.duration()
159151
}
160152

lib/instrumentation/timer.js

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,21 @@
11
'use strict'
22

3+
var microtime = require('relative-microtime')
4+
35
module.exports = Timer
46

5-
function Timer () {
6-
this.ended = false
7-
this.start = Date.now()
8-
this._hrtime = process.hrtime()
9-
this._diff = null
7+
function Timer (timer) {
8+
this._timer = timer ? timer._timer : microtime()
9+
this.start = this._timer()
10+
this.duration = null
1011
}
1112

1213
Timer.prototype.end = function () {
13-
if (this.ended) return
14-
this._diff = process.hrtime(this._hrtime)
15-
this.ended = true
16-
}
17-
18-
Timer.prototype.duration = function () {
19-
if (!this.ended) return null
20-
var ns = this._diff[0] * 1e9 + this._diff[1]
21-
return ns / 1e6
14+
if (this.duration !== null) return
15+
this.duration = this.elapsed()
2216
}
2317

24-
Timer.prototype.offset = function (timer) {
25-
var a = timer._hrtime
26-
var b = this._hrtime
27-
var ns = (b[0] - a[0]) * 1e9 + (b[1] - a[1])
28-
return ns / 1e6
18+
Timer.prototype.elapsed = function () {
19+
// durations are in milliseconds ¯\_(ツ)_/¯
20+
return (this._timer() - this.start) / 1000
2921
}

lib/instrumentation/transaction.js

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -50,15 +50,6 @@ function Transaction (agent, name, type) {
5050
}
5151
})
5252

53-
Object.defineProperty(this, 'timestamp', {
54-
get () {
55-
if (!this._timestamp) {
56-
this._timestamp = new Date(this._timer.start).toISOString()
57-
}
58-
return this._timestamp
59-
}
60-
})
61-
6253
this.id = crypto.randomBytes(64 / 8).toString('hex') // TODO: Replace with correct id once OT is ready
6354
this.traceId = crypto.randomBytes(128 / 8).toString('hex') // TODO: Replace with correct id once OT is ready
6455
this._defaultName = name || ''
@@ -75,14 +66,14 @@ function Transaction (agent, name, type) {
7566
this._abortTime = 0
7667
this._agent = agent
7768
this._agent._instrumentation.currentTransaction = this
78-
this._timestamp = null
7969

8070
// Random sampling
8171
this.sampled = Math.random() <= this._agent._conf.transactionSampleRate
8272

8373
agent.logger.debug('start transaction %o', { id: this.id, name: name, type: type })
8474

8575
this._timer = new Timer()
76+
this.timestamp = this._timer.start
8677
}
8778

8879
Transaction.prototype.setUserContext = function (context) {
@@ -195,7 +186,7 @@ Transaction.prototype.duration = function () {
195186
return null
196187
}
197188

198-
return this._timer.duration()
189+
return this._timer.duration
199190
}
200191

201192
Transaction.prototype.setDefaultName = function (name) {

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@
8282
"original-url": "^1.2.1",
8383
"read-pkg-up": "^3.0.0",
8484
"redact-secrets": "^1.0.0",
85+
"relative-microtime": "^2.0.0",
8586
"require-ancestors": "^1.0.0",
8687
"require-in-the-middle": "^3.1.0",
8788
"semver": "^5.5.0",

test/instrumentation/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ test('basic', function (t) {
3939
t.ok(span, 'should have span named ' + name)
4040
t.equal(span.transaction_id, trans.id, 'should belong to correct transaction')
4141
t.equal(span.type, 'type')
42-
t.ok(span.start > 0, 'span start should be >0ms')
43-
t.ok(span.start < 100, 'span start should be <100ms')
42+
t.ok(span.timestamp > trans.timestamp, 'assert span timestamp > transaction timestamp')
43+
t.ok(span.timestamp < trans.timestamp + 100000, 'assert span timestamp < transaction timestamp + 100000')
4444
t.ok(span.duration > 0, 'span duration should be >0ms')
4545
t.ok(span.duration < 100, 'span duration should be <100ms')
4646
t.ok(span.stacktrace.length > 0, 'should have stack trace')

test/instrumentation/modules/elasticsearch.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,8 @@ function done (t, method, path, query) {
107107
t.notOk(span2.context)
108108
}
109109

110-
t.ok(span1.start > span2.start, 'http span should start after elasticsearch span')
111-
t.ok(span1.start + span1.duration < span2.start + span2.duration, 'http span should end before elasticsearch span')
110+
t.ok(span1.timestamp > span2.timestamp, 'http span should start after elasticsearch span')
111+
t.ok(span1.timestamp + span1.duration * 1000 < span2.timestamp + span2.duration * 1000, 'http span should end before elasticsearch span')
112112

113113
t.end()
114114
}

test/instrumentation/modules/express-graphql.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,9 @@ function done (t, query) {
169169
t.equal(trans.type, 'request')
170170
t.equal(span.name, 'GraphQL: ' + query)
171171
t.equal(span.type, 'db.graphql.execute')
172-
t.ok(span.start + span.duration < trans.duration)
172+
173+
var offset = span.timestamp - trans.timestamp
174+
t.ok(offset + span.duration * 1000 < trans.duration * 1000)
173175

174176
t.end()
175177
}

test/instrumentation/modules/express-queue.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,9 @@ function done (t, query) {
8989
const span = findObjInArray(data.spans, 'transaction_id', trans.id)
9090
t.equal(span.name, 'foo', 'span name should be foo')
9191
t.equal(span.type, 'bar', 'span name should be bar')
92-
t.ok(span.start + span.duration < trans.duration, 'span should have valid timings')
92+
93+
var offset = span.timestamp - trans.timestamp
94+
t.ok(offset + span.duration * 1000 < trans.duration * 1000, 'span should have valid timings')
9395
})
9496

9597
t.end()

0 commit comments

Comments
 (0)