Skip to content

Commit

Permalink
fix(span): Do not pass stack frames into promises
Browse files Browse the repository at this point in the history
Passing error stack frames into a promise context makes it
uncollectable by the garbage collector.
  • Loading branch information
Stephen Belanger committed Mar 15, 2018
1 parent 9ce6460 commit 44beee7
Showing 1 changed file with 35 additions and 18 deletions.
53 changes: 35 additions & 18 deletions lib/instrumentation/span.js
Expand Up @@ -5,6 +5,7 @@ var debug = require('debug')('elastic-apm')
var Timer = require('./timer')
var stackman = require('../stackman')
var parsers = require('../parsers')
var Value = require('async-value-promise')

module.exports = Span

Expand Down Expand Up @@ -33,7 +34,9 @@ Span.prototype.start = function (name, type) {
this.name = name || this.name || 'unnamed'
this.type = type || this.type || 'custom'

if (!this._stackObj) this._recordStackTrace()
if (this._agent._conf.captureSpanStackTraces && !this._stackObj) {
this._recordStackTrace()
}

this._timer = new Timer()

Expand Down Expand Up @@ -102,7 +105,33 @@ Span.prototype._recordStackTrace = function (obj) {
obj = {}
Error.captureStackTrace(obj, Span.prototype.start)
}
this._stackObj = obj

var self = this

// NOTE: This uses a promise-like thing and not a *real* promise
// because passing error stacks into a promise context makes it
// uncollectable by the garbage collector.
var stack = new Value()
this._stackObj = stack

// TODO: This is expensive! Consider if there's a way to cache some of this
stackman.callsites(obj, function (err, callsites) {
if (err || !callsites) {
debug('could not capture stack trace for span %o', {id: self.transaction.id, name: self.name, type: self.type, err: err && err.message})
reject(err)
return
}

if (!process.env.ELASTIC_APM_TEST) callsites = callsites.filter(filterCallsite)

var next = afterAll((err, res) => {
err ? stack.reject(err) : stack.resolve(res)
})

callsites.forEach(function (callsite) {
parsers.parseCallsite(callsite, false, self._agent._conf, next())
})
})
}

Span.prototype._encode = function (cb) {
Expand All @@ -112,22 +141,10 @@ Span.prototype._encode = function (cb) {
if (!this.ended) return cb(new Error('cannot encode un-ended span'))

if (this._agent._conf.captureSpanStackTraces) {
// TODO: This is expensive! Consider if there's a way to cache some of this
stackman.callsites(this._stackObj, function (err, callsites) {
if (!callsites) {
debug('could not capture stack trace for span %o', {id: self.transaction.id, name: self.name, type: self.type, err: err && err.message})
done()
return
}

if (!process.env.ELASTIC_APM_TEST) callsites = callsites.filter(filterCallsite)

var next = afterAll(done)

callsites.forEach(function (callsite) {
parsers.parseCallsite(callsite, false, self._agent._conf, next())
})
})
this._stackObj.then(
value => done(null, value),
error => done(error)
)
} else {
process.nextTick(done)
}
Expand Down

0 comments on commit 44beee7

Please sign in to comment.