Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

elastic-apm-node

Package Overview
Dependencies
Maintainers
2
Versions
162
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

elastic-apm-node - npm Package Compare versions

Comparing version 1.14.3 to 2.0.0

lib/filters/http-headers.js

16

CHANGELOG.md

@@ -0,1 +1,17 @@

# 2.0.0 - 2018/11/14
* Breaking changes:
* chore: remove support for Node.js 4 and 9
* chore: remove deprecated buildSpan function ([#642](https://github.com/elastic/apm-agent-nodejs/pull/642))
* feat: support APM Server intake API version 2 ([#465](https://github.com/elastic/apm-agent-nodejs/pull/465))
* feat: improved filtering function API ([#579](https://github.com/elastic/apm-agent-nodejs/pull/579))
* feat: replace double-quotes with underscores in tag names ([#666](https://github.com/elastic/apm-agent-nodejs/pull/666))
* feat(config): change config order ([#604](https://github.com/elastic/apm-agent-nodejs/pull/604))
* feat(config): support time suffixes ([#602](https://github.com/elastic/apm-agent-nodejs/pull/602))
* feat(config): stricter boolean parsing ([#613](https://github.com/elastic/apm-agent-nodejs/pull/613))
* feat: add support for Distributed Tracing ([#538](https://github.com/elastic/apm-agent-nodejs/pull/538))
* feat(transaction): add transaction.ensureParentId function ([#661](https://github.com/elastic/apm-agent-nodejs/pull/661))
* feat(config): support byte suffixes ([#601](https://github.com/elastic/apm-agent-nodejs/pull/601))
* feat(transaction): restructure span\_count and include total ([#553](https://github.com/elastic/apm-agent-nodejs/pull/553))
* perf: improve Async Hooks implementation ([#679](https://github.com/elastic/apm-agent-nodejs/pull/679))
# 1.14.3 - 2018/11/13

@@ -2,0 +18,0 @@ * fix(async\_hooks): more reliable cleanup ([#674](https://github.com/elastic/apm-agent-nodejs/pull/674))

111

lib/agent.js
'use strict'
var crypto = require('crypto')
var http = require('http')

@@ -8,6 +9,4 @@ var parseUrl = require('url').parse

var afterAll = require('after-all-results')
var ElasticAPMHttpClient = require('elastic-apm-http-client')
var isError = require('core-util-is').isError
var ancestors = require('require-ancestors')
var uuid = require('uuid')

@@ -19,3 +18,2 @@ var config = require('./config')

var parsers = require('./parsers')
var request = require('./request')
var stackman = require('./stackman')

@@ -28,3 +26,2 @@ var symbols = require('./symbols')

var version = require('../package').version
var userAgent = 'elastic-apm-node/' + version

@@ -37,3 +34,6 @@ module.exports = Agent

this._instrumentation = new Instrumentation(this)
this._filters = new Filters()
this._errorFilters = new Filters()
this._transactionFilters = new Filters()
this._spanFilters = new Filters()
this._transport = null

@@ -59,2 +59,12 @@ this._conf = null

Object.defineProperty(Agent.prototype, 'currentSpan', {
get () {
return this._instrumentation.currentSpan
}
})
Agent.prototype.destroy = function () {
if (this._transport) this._transport.destroy()
}
Agent.prototype.startTransaction = function () {

@@ -73,11 +83,5 @@ return this._instrumentation.startTransaction.apply(this._instrumentation, arguments)

Agent.prototype.startSpan = function (name, type) {
var span = this._instrumentation.buildSpan.apply(this._instrumentation)
if (span) span.start(name, type)
return span
return this._instrumentation.startSpan.apply(this._instrumentation, arguments)
}
Agent.prototype.buildSpan = function () {
return this._instrumentation.buildSpan.apply(this._instrumentation, arguments)
}
Agent.prototype._config = function (opts) {

@@ -100,4 +104,7 @@ this._conf = config(opts)

this._config(opts)
this._filters.config(this._conf)
if (this._conf.filterHttpHeaders) {
this.addFilter(require('./filters/http-headers'))
}
if (!this._conf.active) {

@@ -139,12 +146,6 @@ this.logger.info('Elastic APM agent is inactive due to configuration')

this._transport = this._conf.transport(this._conf, this)
this._instrumentation.start()
this._httpClient = new ElasticAPMHttpClient({
secretToken: this._conf.secretToken,
userAgent: userAgent,
serverUrl: this._conf.serverUrl,
rejectUnauthorized: this._conf.verifyServerCert,
serverTimeout: this._conf.serverTimeout * 1000
})
Error.stackTraceLimit = this._conf.stackTraceLimit

@@ -183,2 +184,8 @@ if (this._conf.captureExceptions) this.handleUncaughtExceptions()

Agent.prototype.addFilter = function (fn) {
this.addErrorFilter(fn)
this.addTransactionFilter(fn)
this.addSpanFilter(fn)
}
Agent.prototype.addErrorFilter = function (fn) {
if (typeof fn !== 'function') {

@@ -189,5 +196,23 @@ this.logger.error('Can\'t add filter of type %s', typeof fn)

this._filters.add(fn)
this._errorFilters.add(fn)
}
Agent.prototype.addTransactionFilter = function (fn) {
if (typeof fn !== 'function') {
this.logger.error('Can\'t add filter of type %s', typeof fn)
return
}
this._transactionFilters.add(fn)
}
Agent.prototype.addSpanFilter = function (fn) {
if (typeof fn !== 'function') {
this.logger.error('Can\'t add filter of type %s', typeof fn)
return
}
this._spanFilters.add(fn)
}
Agent.prototype.captureError = function (err, opts, cb) {

@@ -198,4 +223,5 @@ if (typeof opts === 'function') return this.captureError(err, null, opts)

var trans = this.currentTransaction
var timestamp = new Date().toISOString()
var id = uuid.v4()
var span = this.currentSpan
var timestamp = Date.now() * 1000
var context = (span || trans || {}).context || {}
var req = opts && opts.request instanceof IncomingMessage

@@ -228,3 +254,5 @@ ? opts.request

function prepareError (error) {
error.id = id
error.id = crypto.randomBytes(16).toString('hex')
error.parent_id = context.id
error.trace_id = context.traceId
error.timestamp = timestamp

@@ -249,4 +277,5 @@ error.context = {

}
if (trans) error.transaction = { id: trans.id }
if (trans) error.transaction_id = trans.id
if (error.exception) {

@@ -301,6 +330,21 @@ error.exception.handled = !opts || opts.handled

function send (error) {
agent.logger.info('logging error %s with Elastic APM', id)
request.errors(agent, [error], (err) => {
if (cb) cb(err, error.id)
})
error = agent._errorFilters.process(error)
if (!error) {
agent.logger.debug('error ignored by filter %o', { id: error.id })
if (cb) cb(null, error.id)
return
}
if (agent._transport) {
agent.logger.info(`Sending error to Elastic APM`, { id: error.id })
agent._transport.sendError(error, function () {
agent._transport.flush(function (err) {
if (cb) cb(err, error.id)
})
})
} else if (cb) {
// TODO: Swallow this error just as it's done in agent.flush()?
process.nextTick(cb.bind(null, new Error('cannot capture error before agent is started'), error.id))
}
}

@@ -331,3 +375,8 @@ }

Agent.prototype.flush = function (cb) {
this._instrumentation.flush(cb)
if (this._transport) {
this._transport.flush(cb)
} else {
this.logger.warn(new Error('cannot flush agent before it is started'))
process.nextTick(cb)
}
}

@@ -334,0 +383,0 @@

'use strict'
var fs = require('fs')
var os = require('os')
var path = require('path')
var consoleLogLevel = require('console-log-level')
var normalizeBool = require('normalize-bool')
var ElasticAPMHttpClient = require('elastic-apm-http-client')
var readPkgUp = require('read-pkg-up')
var truncate = require('unicode-byte-truncate')
var version = require('../package').version
var userAgent = 'elastic-apm-node/' + version
config.INTAKE_STRING_MAX_SIZE = 1024

@@ -38,3 +40,4 @@ config.CAPTURE_ERROR_LOG_STACK_TRACES_NEVER = 'never'

logLevel: 'info',
hostname: os.hostname(),
apiRequestSize: '750kb',
apiRequestTime: '10s',
stackTraceLimit: 50,

@@ -47,3 +50,3 @@ captureExceptions: true,

errorOnAbortedRequests: false,
abortedErrorThreshold: 25000,
abortedErrorThreshold: '25s',
instrument: true,

@@ -55,8 +58,6 @@ asyncHooks: true,

sourceLinesSpanLibraryFrames: 0,
errorMessageMaxLength: 2048,
flushInterval: 10,
errorMessageMaxLength: '2kb',
transactionMaxSpans: 500,
transactionSampleRate: 1.0,
maxQueueSize: 100,
serverTimeout: 30,
serverTimeout: '30s',
disableInstrumentations: []

@@ -74,2 +75,4 @@ }

hostname: 'ELASTIC_APM_HOSTNAME',
apiRequestSize: 'ELASTIC_APM_API_REQUEST_SIZE',
apiRequestTime: 'ELASTIC_APM_API_REQUEST_TIME',
frameworkName: 'ELASTIC_APM_FRAMEWORK_NAME',

@@ -86,4 +89,2 @@ frameworkVersion: 'ELASTIC_APM_FRAMEWORK_VERSION',

instrument: 'ELASTIC_APM_INSTRUMENT',
flushInterval: 'ELASTIC_APM_FLUSH_INTERVAL',
maxQueueSize: 'ELASTIC_APM_MAX_QUEUE_SIZE',
asyncHooks: 'ELASTIC_APM_ASYNC_HOOKS',

@@ -98,3 +99,4 @@ sourceLinesErrorAppFrames: 'ELASTIC_APM_SOURCE_LINES_ERROR_APP_FRAMES',

serverTimeout: 'ELASTIC_APM_SERVER_TIMEOUT',
disableInstrumentations: 'ELASTIC_APM_DISABLE_INSTRUMENTATIONS'
disableInstrumentations: 'ELASTIC_APM_DISABLE_INSTRUMENTATIONS',
payloadLogFile: 'ELASTIC_APM_PAYLOAD_LOG_FILE'
}

@@ -115,5 +117,2 @@

'stackTraceLimit',
'abortedErrorThreshold',
'flushInterval',
'maxQueueSize',
'sourceLinesErrorAppFrames',

@@ -123,10 +122,18 @@ 'sourceLinesErrorLibraryFrames',

'sourceLinesSpanLibraryFrames',
'errorMessageMaxLength',
'transactionMaxSpans',
'transactionSampleRate',
'transactionSampleRate'
]
var TIME_OPTS = [
'apiRequestTime',
'abortedErrorThreshold',
'serverTimeout'
]
var BYTES_OPTS = [
'apiRequestSize',
'errorMessageMaxLength'
]
var MINUS_ONE_EQUAL_INFINITY = [
'maxQueueSize',
'transactionMaxSpans'

@@ -143,5 +150,5 @@ ]

DEFAULTS, // default options
readEnv(), // options read from environment variables
confFile, // options read from elastic-apm-node.js config file
opts // options passed in to agent.start()
opts, // options passed in to agent.start()
readEnv() // options read from environment variables
)

@@ -153,13 +160,82 @@

// NOTE: A logger will already exists if a custom logger was given to start()
if (typeof opts.logger === 'undefined') {
opts.logger = consoleLogLevel({
level: opts.logLevel
})
}
normalizeIgnoreOptions(opts)
normalizeNumbers(opts)
normalizeBytes(opts)
normalizeArrays(opts)
normalizeTime(opts)
normalizeBools(opts)
truncateOptions(opts)
// NOTE: A logger will already exists if a custom logger was given to start()
if (typeof opts.logger !== 'function') {
opts.logger = consoleLogLevel({
level: opts.logLevel
})
if (typeof opts.transport !== 'function') {
opts.transport = function httpTransport (conf, agent) {
var transport = new ElasticAPMHttpClient({
// metadata
agentName: 'nodejs',
agentVersion: version,
serviceName: conf.serviceName,
serviceVersion: conf.serviceVersion,
frameworkName: conf.frameworkName,
frameworkVersion: conf.frameworkVersion,
hostname: conf.hostname,
// Sanitize conf
truncateKeywordsAt: config.INTAKE_STRING_MAX_SIZE,
truncateErrorMessagesAt: conf.errorMessageMaxLength,
// HTTP conf
secretToken: conf.secretToken,
userAgent: userAgent,
serverUrl: conf.serverUrl,
rejectUnauthorized: conf.verifyServerCert,
serverTimeout: conf.serverTimeout * 1000,
// Streaming conf
size: conf.apiRequestSize,
time: conf.apiRequestTime * 1000,
// Debugging
payloadLogFile: conf.payloadLogFile
})
transport.on('error', err => {
const haveAccepted = Number.isFinite(err.accepted)
const haveErrors = Array.isArray(err.errors)
let msg
if (err.code === 404) {
msg = 'APM Server responded with "404 Not Found". ' +
'This might be because you\'re running an incompatible version of the APM Server. ' +
'This agent only supports APM Server v6.5 and above. ' +
'If you\'re using an older version of the APM Server, ' +
'please downgrade this agent to version 1.x or upgrade the APM Server'
} else if (err.code) {
msg = `APM Server transport error (${err.code}): ${err.message}`
} else {
msg = `APM Server transport error: ${err.message}`
}
if (haveAccepted || haveErrors) {
if (haveAccepted) msg += `\nAPM Server accepted ${err.accepted} events in the last request`
if (haveErrors) {
err.errors.forEach(error => {
msg += `\nError: ${error.message}`
if (error.document) msg += `\n Document: ${error.document}`
})
}
} else if (err.response) {
msg += `\n${err.response}`
}
agent.logger.error(msg)
})
return transport
}
}

@@ -214,2 +290,14 @@

function normalizeBytes (opts) {
BYTES_OPTS.forEach(function (key) {
if (key in opts) opts[key] = bytes(String(opts[key]))
})
}
function normalizeTime (opts) {
TIME_OPTS.forEach(function (key) {
if (key in opts) opts[key] = toSeconds(String(opts[key]))
})
}
function maybeSplit (value) {

@@ -227,3 +315,3 @@ return typeof value === 'string' ? value.split(',') : value

BOOL_OPTS.forEach(function (key) {
if (key in opts) opts[key] = normalizeBool(opts[key])
if (key in opts) opts[key] = strictBool(opts.logger, key, opts[key])
})

@@ -236,1 +324,60 @@ }

}
function bytes (input) {
const matches = input.match(/^(\d+)(b|kb|mb|gb)$/i)
if (!matches) return Number(input)
const suffix = matches[2].toLowerCase()
let value = Number(matches[1])
if (!suffix || suffix === 'b') {
return value
}
value *= 1024
if (suffix === 'kb') {
return value
}
value *= 1024
if (suffix === 'mb') {
return value
}
value *= 1024
if (suffix === 'gb') {
return value
}
}
function toSeconds (value) {
var matches = /^(-)?(\d+)(m|ms|s)?$/.exec(value)
if (!matches) return null
var negate = matches[1]
var amount = Number(matches[2])
if (negate) amount = -amount
var scale = matches[3]
if (scale === 'm') {
amount *= 60
} else if (scale === 'ms') {
amount /= 1000
}
return amount
}
function strictBool (logger, key, value) {
if (typeof value === 'boolean') {
return value
}
// This will return undefined for unknown inputs, resulting in them being skipped.
switch (value) {
case 'false': return false
case 'true': return true
default: {
logger.warn('unrecognized boolean value "%s" for "%s"', value, key)
}
}
}

@@ -7,31 +7,52 @@ 'use strict'

module.exports = function (ins) {
const asyncHook = asyncHooks.createHook({ init, destroy })
const transactions = new Map()
const asyncHook = asyncHooks.createHook({ init, before, destroy })
const contexts = new WeakMap()
shimmer.wrap(ins, 'addEndedTransaction', function (addEndedTransaction) {
return function wrappedAddEndedTransaction (transaction) {
if (contexts.has(transaction)) {
for (let asyncId of contexts.get(transaction)) {
if (transactions.has(asyncId)) {
transactions.delete(asyncId)
}
}
contexts.delete(transaction)
const activeTransactions = new Map()
Object.defineProperty(ins, 'currentTransaction', {
get () {
const asyncId = asyncHooks.executionAsyncId()
return activeTransactions.get(asyncId) || null
},
set (trans) {
const asyncId = asyncHooks.executionAsyncId()
if (trans) {
activeTransactions.set(asyncId, trans)
} else {
activeTransactions.delete(asyncId)
}
return addEndedTransaction.call(this, transaction)
}
})
Object.defineProperty(ins, 'currentTransaction', {
const activeSpans = new Map()
Object.defineProperty(ins, 'activeSpan', {
get () {
const asyncId = asyncHooks.executionAsyncId()
return transactions.has(asyncId) ? transactions.get(asyncId) : null
return activeSpans.get(asyncId) || null
},
set (trans) {
set (span) {
const asyncId = asyncHooks.executionAsyncId()
transactions.set(asyncId, trans)
if (span) {
activeSpans.set(asyncId, span)
} else {
activeSpans.delete(asyncId)
}
}
})
shimmer.wrap(ins, 'addEndedTransaction', function (addEndedTransaction) {
return function wrappedAddEndedTransaction (transaction) {
const asyncIds = contexts.get(transaction)
if (asyncIds) {
for (const asyncId of asyncIds) {
activeTransactions.delete(asyncId)
activeSpans.delete(asyncId)
}
contexts.delete(transaction)
}
return addEndedTransaction.call(this, transaction)
}
})
asyncHook.enable()

@@ -45,27 +66,38 @@

var transaction = ins.currentTransaction
const transaction = ins.currentTransaction
if (!transaction) return
transactions.set(asyncId, transaction)
activeTransactions.set(asyncId, transaction)
// Track the context by the transaction
if (!contexts.has(transaction)) {
contexts.set(transaction, [])
let asyncIds = contexts.get(transaction)
if (!asyncIds) {
asyncIds = []
contexts.set(transaction, asyncIds)
}
contexts.get(transaction).push(asyncId)
asyncIds.push(asyncId)
const span = ins.bindingSpan || ins.activeSpan
if (span) activeSpans.set(asyncId, span)
}
function before (asyncId) {
ins.bindingSpan = null
}
function destroy (asyncId) {
if (!transactions.has(asyncId)) return // in case type === TIMERWRAP
const span = activeSpans.get(asyncId)
const transaction = span ? span.transaction : activeTransactions.get(asyncId)
var transaction = transactions.get(asyncId)
if (contexts.get(transaction)) {
var list = contexts.get(transaction)
var index = list.indexOf(asyncId)
list.splice(index, 1)
if (transaction) {
const asyncIds = contexts.get(transaction)
if (asyncIds) {
const index = asyncIds.indexOf(asyncId)
asyncIds.splice(index, 1)
}
}
transactions.delete(asyncId)
activeTransactions.delete(asyncId)
activeSpans.delete(asyncId)
}
}

@@ -22,3 +22,4 @@ 'use strict'

} else {
var trans = agent.startTransaction()
var traceparent = req.headers['elastic-apm-traceparent']
var trans = agent.startTransaction(null, null, traceparent)
trans.type = 'request'

@@ -37,5 +38,5 @@ trans.req = req

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

@@ -84,2 +85,12 @@ extra: { abortTime: duration }

// NOTE: This will also stringify and parse URL instances
// to a format which can be mixed into the options object.
function ensureUrl (v) {
if (typeof v === 'string' || v instanceof url.URL) {
return url.parse(String(v))
} else {
return v
}
}
exports.traceOutgoingRequest = function (agent, moduleName) {

@@ -90,4 +101,4 @@ var spanType = 'ext.' + moduleName + '.http'

return function (orig) {
return function () {
var span = agent.buildSpan()
return function (...args) {
var span = agent.startSpan(null, spanType)
var id = span && span.transaction.id

@@ -97,4 +108,27 @@

var req = orig.apply(this, arguments)
var options = {}
var newArgs = [ options ]
for (let arg of args) {
if (typeof arg === 'function') {
newArgs.push(arg)
} else {
Object.assign(options, ensureUrl(arg))
}
}
if (!options.headers) options.headers = {}
// Attempt to use the span context as a traceparent header.
// If the transaction is unsampled the span will not exist,
// however a traceparent header must still be propagated
// to indicate requested services should not be sampled.
// Use the transaction context as the parent, in this case.
var parent = span || agent.currentTransaction
if (parent && parent.context) {
options.headers['elastic-apm-traceparent'] = parent.context.toString()
}
var req = orig.apply(this, newArgs)
if (!span) return req
if (req._headers.host === agent._conf.serverHost) {

@@ -110,4 +144,3 @@ agent.logger.debug('ignore %s request to intake API %o', moduleName, { id: id })

var name = req.method + ' ' + req._headers.host + url.parse(req.path).pathname
span.start(name, spanType)
span.name = req.method + ' ' + req._headers.host + url.parse(req.path).pathname
req.on('response', onresponse)

@@ -114,0 +147,0 @@

@@ -6,8 +6,5 @@ 'use strict'

var AsyncValuePromise = require('async-value-promise')
var hook = require('require-in-the-middle')
var semver = require('semver')
var Queue = require('../queue')
var request = require('../request')
var Transaction = require('./transaction')

@@ -50,6 +47,17 @@ var shimmer = require('./shimmer')

this._agent = agent
this._queue = null
this._hook = null // this._hook is only exposed for testing purposes
this._started = false
this.currentTransaction = null
// Span for binding callbacks
this.bindingSpan = null
// Span which is actively bound
this.activeSpan = null
Object.defineProperty(this, 'currentSpan', {
get () {
return this.bindingSpan || this.activeSpan
}
})
}

@@ -65,17 +73,2 @@

var qopts = {
flushInterval: this._agent._conf.flushInterval,
maxQueueSize: this._agent._conf.maxQueueSize,
logger: this._agent.logger
}
this._queue = new Queue(qopts, function onFlush (transactions, done) {
AsyncValuePromise.all(transactions).then(function (transactions) {
if (self._agent._conf.active && transactions.length > 0) {
request.transactions(self._agent, transactions, done)
} else {
done()
}
}, done)
})
if (this._agent._conf.asyncHooks && semver.gte(process.version, '8.2.0')) {

@@ -117,28 +110,42 @@ require('./async-hooks')(this)

Instrumentation.prototype.addEndedTransaction = function (transaction) {
var agent = this._agent
if (this._started) {
var queue = this._queue
var payload = agent._transactionFilters.process(transaction._encode())
if (!payload) return agent.logger.debug('transaction ignored by filter %o', { trans: transaction.id, trace: transaction.traceId })
agent.logger.debug('sending transaction %o', { trans: transaction.id, trace: transaction.traceId })
agent._transport.sendTransaction(payload)
} else {
agent.logger.debug('ignoring transaction %o', { trans: transaction.id, trace: transaction.traceId })
}
}
this._agent.logger.debug('adding transaction to queue %o', { id: transaction.id })
Instrumentation.prototype.addEndedSpan = function (span) {
var agent = this._agent
var payload = new AsyncValuePromise()
if (this._started) {
agent.logger.debug('encoding span %o', { span: span.id, parent: span.parentId, trace: span.traceId, name: span.name, type: span.type })
span._encode(function (err, payload) {
if (err) {
agent.logger.error('error encoding span %o', { span: span.id, parent: span.parentId, trace: span.traceId, name: span.name, type: span.type, error: err.message })
return
}
payload.catch(function (err) {
this._agent.logger.error('error encoding transaction %s: %s', transaction.id, err.message)
})
payload = agent._spanFilters.process(payload)
// Add the transaction payload to the queue instead of the transation
// object it self to free up the transaction for garbage collection
transaction._encode(function (err, _payload) {
if (err) payload.reject(err)
else payload.resolve(_payload)
if (!payload) {
agent.logger.debug('span ignored by filter %o', { span: span.id, parent: span.parentId, trace: span.traceId, name: span.name, type: span.type })
return
}
agent.logger.debug('sending span %o', { span: span.id, parent: span.parentId, trace: span.traceId, name: span.name, type: span.type })
if (agent._transport) agent._transport.sendSpan(payload)
})
queue.add(payload)
} else {
this._agent.logger.debug('ignoring transaction %o', { id: transaction.id })
agent.logger.debug('ignoring span %o', { span: span.id, parent: span.parentId, trace: span.traceId, name: span.name, type: span.type })
}
}
Instrumentation.prototype.startTransaction = function (name, type) {
return new Transaction(this._agent, name, type)
Instrumentation.prototype.startTransaction = function (name, type, traceparent) {
return new Transaction(this._agent, name, type, traceparent)
}

@@ -172,3 +179,3 @@

Instrumentation.prototype.buildSpan = function () {
Instrumentation.prototype.startSpan = function (name, type) {
if (!this.currentTransaction) {

@@ -179,3 +186,3 @@ this._agent.logger.debug('no active transaction found - cannot build new span')

return this.currentTransaction.buildSpan()
return this.currentTransaction.startSpan(name, type)
}

@@ -188,2 +195,3 @@

var trans = this.currentTransaction
var span = this.currentSpan
if (trans && !trans.sampled) {

@@ -196,6 +204,8 @@ return original

function elasticAPMCallbackWrapper () {
var prev = ins.currentTransaction
var prevTrans = ins.currentTransaction
ins.currentTransaction = trans
ins.bindingSpan = null
ins.activeSpan = span
var result = original.apply(this, arguments)
ins.currentTransaction = prev
ins.currentTransaction = prevTrans
return result

@@ -227,3 +237,4 @@ }

wrong: this.currentTransaction ? this.currentTransaction.id : undefined,
correct: trans.id
correct: trans.id,
trace: trans.traceId
})

@@ -233,9 +244,1 @@

}
Instrumentation.prototype.flush = function (cb) {
if (this._queue) {
this._queue.flush(cb)
} else {
process.nextTick(cb)
}
}

@@ -26,3 +26,3 @@ 'use strict'

return function wrappedConnect (callback) {
const span = agent.buildSpan()
const span = agent.startSpan('Cassandra: Connect', 'db.cassandra.connect')
if (!span) {

@@ -32,4 +32,2 @@ return original.apply(this, arguments)

span.start('Cassandra: Connect', 'db.cassandra.connect')
function resolve () {

@@ -68,3 +66,3 @@ span.end()

return function wrappedBatch (queries, options, callback) {
const span = agent.buildSpan()
const span = agent.startSpan('Cassandra: Batch query', 'db.cassandra.query')
if (!span) {

@@ -77,4 +75,6 @@ return original.apply(this, arguments)

span.setDbContext({ statement: query, type: 'cassandra' })
span.start('Cassandra: Batch query', 'db.cassandra.query')
span.setDbContext({
statement: query,
type: 'cassandra'
})

@@ -112,3 +112,3 @@ function resolve () {

return function wrappedExecute (query, params, options, callback) {
const span = agent.buildSpan()
const span = agent.startSpan(null, 'db.cassandra.query')
if (!span) {

@@ -118,6 +118,4 @@ return original.apply(this, arguments)

span.type = 'db.cassandra.query'
span.setDbContext({ statement: query, type: 'cassandra' })
span.name = sqlSummary(query)
span.start()

@@ -155,3 +153,3 @@ function resolve () {

return function wrappedEachRow (query, params, options, rowCallback, callback) {
const span = agent.buildSpan()
const span = agent.startSpan(null, 'db.cassandra.query')
if (!span) {

@@ -161,6 +159,4 @@ return original.apply(this, arguments)

span.type = 'db.cassandra.query'
span.setDbContext({ statement: query, type: 'cassandra' })
span.name = sqlSummary(query)
span.start()

@@ -167,0 +163,0 @@ // Wrap the callback

@@ -17,3 +17,3 @@ 'use strict'

return function wrappedRequest (params, cb) {
var span = agent.buildSpan()
var span = agent.startSpan(null, 'db.elasticsearch.request')
var id = span && span.transaction.id

@@ -27,3 +27,3 @@ var method = params && params.method

if (span && method && path) {
span.start('Elasticsearch: ' + method + ' ' + path, 'db.elasticsearch.request')
span.name = `Elasticsearch: ${method} ${path}`

@@ -30,0 +30,0 @@ if (query && searchRegexp.test(path)) {

@@ -59,5 +59,4 @@ 'use strict'

var trans = agent._instrumentation.currentTransaction
var span = agent.buildSpan()
var span = agent.startSpan('GraphQL: Unkown Query', 'db.graphql.execute')
var id = span && span.transaction.id
var spanName = 'GraphQL: Unkown Query'
agent.logger.debug('intercepted call to graphql.graphql %o', { id: id })

@@ -80,3 +79,3 @@

var queries = extractDetails(documentAST, operationName).queries
if (queries.length > 0) spanName = 'GraphQL: ' + queries.join(', ')
if (queries.length > 0) span.name = 'GraphQL: ' + queries.join(', ')
}

@@ -90,3 +89,2 @@ } else {

span.start(spanName, 'db.graphql.execute')
var p = orig.apply(this, arguments)

@@ -103,5 +101,4 @@ p.then(function () {

var trans = agent._instrumentation.currentTransaction
var span = agent.buildSpan()
var span = agent.startSpan('GraphQL: Unkown Query', 'db.graphql.execute')
var id = span && span.transaction.id
var spanName = 'GraphQL: Unkown Query'
agent.logger.debug('intercepted call to graphql.execute %o', { id: id })

@@ -120,3 +117,3 @@

operationName = operationName || (details.operation && details.operation.name && details.operation.name.value)
if (queries.length > 0) spanName = 'GraphQL: ' + (operationName ? operationName + ' ' : '') + queries.join(', ')
if (queries.length > 0) span.name = 'GraphQL: ' + (operationName ? operationName + ' ' : '') + queries.join(', ')

@@ -132,3 +129,2 @@ if (trans._graphqlRoute) {

span.start(spanName, 'db.graphql.execute')
var p = orig.apply(this, arguments)

@@ -135,0 +131,0 @@ if (typeof p.then === 'function') {

@@ -14,3 +14,3 @@ 'use strict'

return function wrappedTemplate (data) {
var span = agent.buildSpan()
var span = agent.startSpan('handlebars', 'template.handlebars.render')
var id = span && span.transaction.id

@@ -23,3 +23,2 @@

if (span) span.start('handlebars', 'template.handlebars.render')
var ret = original.apply(this, arguments)

@@ -34,3 +33,3 @@ if (span) span.end()

return function wrappedCompile (input) {
var span = agent.buildSpan()
var span = agent.startSpan('handlebars', 'template.handlebars.compile')
var id = span && span.transaction.id

@@ -43,3 +42,2 @@

if (span) span.start('handlebars', 'template.handlebars.compile')
var ret = original.apply(this, arguments)

@@ -46,0 +44,0 @@ if (span) span.end()

@@ -148,3 +148,3 @@ 'use strict'

return function (headers) {
var span = agent.buildSpan()
var span = agent.startSpan(null, 'ext.http2')
var id = span && span.transaction.id

@@ -160,4 +160,3 @@

var path = url.parse(headers[':path']).pathname
var name = headers[':method'] + ' ' + host + path
span.start(name, 'ext.http2')
span.name = headers[':method'] + ' ' + host + path

@@ -164,0 +163,0 @@ req.on('end', () => {

@@ -51,3 +51,3 @@ 'use strict'

return function wrappedSendCommand (command) {
var span = agent.buildSpan()
var span = agent.startSpan(null, 'cache.redis')
var id = span && span.transaction.id

@@ -80,3 +80,3 @@

span.start(String(command.name).toUpperCase(), 'cache.redis')
span.name = String(command.name).toUpperCase()
}

@@ -83,0 +83,0 @@

@@ -42,3 +42,3 @@ 'use strict'

var cb = arguments[index]
if (typeof cb === 'function' && (span = agent.buildSpan())) {
if (typeof cb === 'function') {
var type

@@ -51,4 +51,6 @@ if (cmd.findAndModify) type = 'findAndModify'

arguments[index] = wrappedCallback
span.start(ns + '.' + type, 'db.mongodb.query')
span = agent.startSpan(ns + '.' + type, 'db.mongodb.query')
if (span) {
arguments[index] = wrappedCallback
}
}

@@ -78,5 +80,7 @@ }

var cb = arguments[index]
if (typeof cb === 'function' && (span = agent.buildSpan())) {
arguments[index] = wrappedCallback
span.start(ns + '.' + name, 'db.mongodb.query')
if (typeof cb === 'function') {
span = agent.startSpan(ns + '.' + name, 'db.mongodb.query')
if (span) {
arguments[index] = wrappedCallback
}
}

@@ -105,5 +109,8 @@ }

var cb = arguments[0]
if (typeof cb === 'function' && (span = agent.buildSpan())) {
arguments[0] = wrappedCallback
span.start(this.ns + '.' + (this.cmd.find ? 'find' : name), 'db.mongodb.query')
if (typeof cb === 'function') {
var spanName = `${this.ns}.${this.cmd.find ? 'find' : name}`
span = agent.startSpan(spanName, 'db.mongodb.query')
if (span) {
arguments[0] = wrappedCallback
}
}

@@ -110,0 +117,0 @@ }

@@ -91,3 +91,3 @@ 'use strict'

return function wrappedQuery (sql, values, cb) {
var span = agent.buildSpan()
var span = agent.startSpan(null, 'db.mysql.query')
var id = span && span.transaction.id

@@ -100,4 +100,2 @@ var hasCallback = false

if (span) {
span.type = 'db.mysql.query'
if (this[symbols.knexStackObj]) {

@@ -141,3 +139,2 @@ span.customStackTrace(this[symbols.knexStackObj])

return function (event) {
span.start()
switch (event) {

@@ -157,3 +154,2 @@ case 'error':

hasCallback = true
span.start()
return function wrappedCallback () {

@@ -160,0 +156,0 @@ span.end()

@@ -23,3 +23,3 @@ 'use strict'

return function wrappedQuery (sql, values, cb) {
var span = enabled && agent.buildSpan()
var span = enabled && agent.startSpan(null, 'db.mysql.query')
var id = span && span.transaction.id

@@ -30,4 +30,2 @@ var hasCallback = false

if (span) {
span.type = 'db.mysql.query'
if (this[symbols.knexStackObj]) {

@@ -70,9 +68,4 @@ span.customStackTrace(this[symbols.knexStackObj])

if (span) {
var started = false
shimmer.wrap(result, 'emit', function (original) {
return function (event) {
if (!started) {
started = true
span.start()
}
switch (event) {

@@ -94,3 +87,2 @@ case 'error':

hasCallback = true
if (span) span.start()
return agent._instrumentation.bindFunction(span ? wrappedCallback : cb)

@@ -97,0 +89,0 @@ function wrappedCallback () {

@@ -47,3 +47,3 @@ 'use strict'

return function wrappedFunction (sql) {
var span = agent.buildSpan()
var span = agent.startSpan('SQL', 'db.postgresql.query')
var id = span && span.transaction.id

@@ -72,6 +72,5 @@

span.setDbContext({ statement: sql, type: 'sql' })
span.start(sqlSummary(sql), 'db.postgresql.query')
span.name = sqlSummary(sql)
} else {
agent.logger.debug('unable to parse sql form pg module (type: %s)', typeof sql)
span.start('SQL', 'db.postgresql.query')
}

@@ -78,0 +77,0 @@

@@ -35,3 +35,3 @@ 'use strict'

return function wrappedInternalSendCommand (commandObj) {
var span = enabled && agent.buildSpan()
var span = enabled && agent.startSpan(null, 'cache.redis')
var id = span && span.transaction.id

@@ -44,3 +44,3 @@ var command = commandObj && commandObj.command

commandObj.callback = makeWrappedCallback(span, commandObj.callback)
if (span) span.start(String(command).toUpperCase(), 'cache.redis')
if (span) span.name = String(command).toUpperCase()
}

@@ -54,3 +54,3 @@

return function wrappedSendCommand (command) {
var span = enabled && agent.buildSpan()
var span = enabled && agent.startSpan(null, 'cache.redis')
var id = span && span.transaction.id

@@ -76,3 +76,3 @@ var args = Array.prototype.slice.call(arguments)

}
if (span) span.start(String(command).toUpperCase(), 'cache.redis')
if (span) span.name = String(command).toUpperCase()
}

@@ -79,0 +79,0 @@

@@ -42,3 +42,3 @@ 'use strict'

Connection.prototype.makeRequest = function makeRequest (request) {
const span = agent.buildSpan()
const span = agent.startSpan(null, 'db.mssql.query')
if (!span) {

@@ -51,5 +51,4 @@ return originalMakeRequest.apply(this, arguments)

const sql = (params.statement || params.stmt || {}).value
const name = sqlSummary(sql) + (preparing ? ' (prepare)' : '')
span.name = sqlSummary(sql) + (preparing ? ' (prepare)' : '')
span.setDbContext({ statement: sql, type: 'sql' })
span.start(name, 'db.mssql.query')

@@ -56,0 +55,0 @@ request.userCallback = wrapCallback(request.userCallback)

@@ -21,3 +21,3 @@ 'use strict'

return function wrappedSend () {
var span = agent.buildSpan()
var span = agent.startSpan('Send WebSocket Message', 'websocket.send')
var id = span && span.transaction.id

@@ -38,4 +38,2 @@

span.start('Send WebSocket Message', 'websocket.send')
return orig.apply(this, args)

@@ -42,0 +40,0 @@

@@ -14,60 +14,42 @@ 'use strict'

function Span (transaction) {
function Span (transaction, name, type) {
this.transaction = transaction
this.started = false
this.truncated = false
this.ended = false
this.name = null
this.type = null
this._db = null
this._timer = null
this._stackObj = null
this._agent = transaction._agent
this._agent.logger.debug('init span %o', { id: this.transaction.id })
}
var current = this._agent._instrumentation.activeSpan || transaction
this.context = current.context.child()
Span.prototype.start = function (name, type) {
if (this.started) {
this._agent.logger.debug('tried to call span.start() on already started span %o', { id: this.transaction.id, name: this.name, type: this.type })
return
}
this.name = name || 'unnamed'
this.type = type || 'custom'
this.started = true
this.name = name || this.name || 'unnamed'
this.type = type || this.type || 'custom'
this._agent._instrumentation.bindingSpan = this
if (this._agent._conf.captureSpanStackTraces && !this._stackObj) {
if (this._agent._conf.captureSpanStackTraces) {
this._recordStackTrace()
}
this._timer = new Timer()
this._timer = new Timer(this.transaction._timer)
this.timestamp = this._timer.start
this._agent.logger.debug('start span %o', { id: this.transaction.id, name: name, type: type })
this._agent.logger.debug('start span %o', { span: this.id, parent: this.parentId, trace: this.traceId, name: name, type: type })
}
Object.defineProperty(Span.prototype, 'id', {
get () {
return this.context.id
}
})
Span.prototype.customStackTrace = function (stackObj) {
this._agent.logger.debug('applying custom stack trace to span %o', { id: this.transaction.id })
this._agent.logger.debug('applying custom stack trace to span %o', { span: this.id, parent: this.parentId, trace: this.traceId })
this._recordStackTrace(stackObj)
}
Span.prototype.truncate = function () {
if (!this.started) {
this._agent.logger.debug('tried to truncate non-started span - ignoring %o', { id: this.transaction.id, name: this.name, type: this.type })
return
} else if (this.ended) {
this._agent.logger.debug('tried to truncate already ended span - ignoring %o', { id: this.transaction.id, name: this.name, type: this.type })
return
}
this.truncated = true
this.end()
}
Span.prototype.end = function () {
if (!this.started) {
this._agent.logger.debug('tried to call span.end() on un-started span %o', { id: this.transaction.id, name: this.name, type: this.type })
if (this.ended) {
this._agent.logger.debug('tried to call span.end() on already ended span %o', { span: this.id, parent: this.parentId, trace: this.traceId, name: this.name, type: this.type })
return
} else if (this.ended) {
this._agent.logger.debug('tried to call span.end() on already ended span %o', { id: this.transaction.id, name: this.name, type: this.type })
return
}

@@ -79,4 +61,4 @@

this.ended = true
this._agent.logger.debug('ended span %o', { id: this.transaction.id, name: this.name, type: this.type, truncated: this.truncated })
this.transaction._recordEndedSpan(this)
this._agent.logger.debug('ended span %o', { span: this.id, parent: this.parentId, trace: this.traceId, name: this.name, type: this.type })
this._agent._instrumentation.addEndedSpan(this)
}

@@ -86,18 +68,9 @@

if (!this.ended) {
this._agent.logger.debug('tried to call span.duration() on un-ended span %o', { id: this.transaction.id, name: this.name, type: this.type })
this._agent.logger.debug('tried to call span.duration() on un-ended span %o', { span: this.id, parent: this.parentId, trace: this.traceId, name: this.name, type: this.type })
return null
}
return this._timer.duration()
return this._timer.duration
}
Span.prototype.offsetTime = function () {
if (!this.started) {
this._agent.logger.debug('tried to call span.offsetTime() for un-started span %o', { id: this.transaction.id, name: this.name, type: this.type })
return null
}
return this._timer.offset(this.transaction._timer)
}
Span.prototype.setDbContext = function (context) {

@@ -111,3 +84,3 @@ if (!context) return

obj = {}
Error.captureStackTrace(obj, Span.prototype.start)
Error.captureStackTrace(obj, Span)
}

@@ -126,3 +99,3 @@

if (err || !callsites) {
self._agent.logger.debug('could not capture stack trace for span %o', { id: self.transaction.id, name: self.name, type: self.type, err: err && err.message })
self._agent.logger.debug('could not capture stack trace for span %o', { span: self.id, parent: self.parentId, trace: self.traceId, name: self.name, type: self.type, err: err && err.message })
stack.reject(err)

@@ -147,3 +120,2 @@ return

if (!this.started) return cb(new Error('cannot encode un-started span'))
if (!this.ended) return cb(new Error('cannot encode un-ended span'))

@@ -162,9 +134,13 @@

if (err) {
self._agent.logger.warn('could not capture stack trace for span %o', { id: self.transaction.id, name: self.name, type: self.type, err: err.message })
self._agent.logger.warn('could not capture stack trace for span %o', { span: self.id, parent: self.parentId, trace: self.traceId, name: self.name, type: self.type, err: err.message })
}
var payload = {
id: self.context.id,
transaction_id: self.transaction.id,
parent_id: self.context.parentId,
trace_id: self.context.traceId,
name: self.name,
type: self.truncated ? self.type + '.truncated' : self.type,
start: self.offsetTime(),
type: self.type,
timestamp: self.timestamp,
duration: self.duration()

@@ -171,0 +147,0 @@ }

'use strict'
var microtime = require('relative-microtime')
module.exports = Timer
function Timer () {
this.ended = false
this.start = Date.now()
this._hrtime = process.hrtime()
this._diff = null
function Timer (timer) {
this._timer = timer ? timer._timer : microtime()
this.start = this._timer()
this.duration = null
}
Timer.prototype.end = function () {
if (this.ended) return
this._diff = process.hrtime(this._hrtime)
this.ended = true
if (this.duration !== null) return
this.duration = this.elapsed()
}
Timer.prototype.duration = function () {
if (!this.ended) return null
var ns = this._diff[0] * 1e9 + this._diff[1]
return ns / 1e6
Timer.prototype.elapsed = function () {
// durations are in milliseconds ¯\_(ツ)_/¯
return (this._timer() - this.start) / 1000
}
Timer.prototype.offset = function (timer) {
var a = timer._hrtime
var b = this._hrtime
var ns = (b[0] - a[0]) * 1e9 + (b[1] - a[1])
return ns / 1e6
}
'use strict'
var afterAll = require('after-all-results')
var truncate = require('unicode-byte-truncate')
var uuid = require('uuid')
var config = require('../config')
var TraceContext = require('./trace-context')
var getPathFromRequest = require('./express-utils').getPathFromRequest

@@ -16,40 +15,11 @@ var parsers = require('../parsers')

function Transaction (agent, name, type) {
Object.defineProperty(this, 'name', {
configurable: true,
enumerable: true,
get () {
// Fall back to a somewhat useful name in case no _defaultName is set.
// This might happen if res.writeHead wasn't called.
return this._customName ||
this._defaultName ||
(this.req ? this.req.method + ' unknown route (unnamed)' : 'unnamed')
},
set (name) {
if (this.ended) {
agent.logger.debug('tried to set transaction.name on already ended transaction %o', { id: this.id })
return
}
agent.logger.debug('setting transaction name %o', { id: this.id, name: name })
this._customName = name
}
})
function Transaction (agent, name, type, traceparent) {
this.context = TraceContext.startOrResume(traceparent, agent._conf)
const verb = this.context.parentId ? 'continue' : 'start'
agent.logger.debug('%s trace %o', verb, { trans: this.id, parent: this.parentId, trace: this.traceId, name: name, type: type })
Object.defineProperty(this, 'result', {
configurable: true,
enumerable: true,
get () {
return this._result
},
set (result) {
if (this.ended) {
agent.logger.debug('tried to set transaction.result on already ended transaction %o', { id: this.id })
return
}
agent.logger.debug('setting transaction result %o', { id: this.id, result: result })
this._result = result
}
})
this._agent = agent
this._agent._instrumentation.currentTransaction = this
this._agent._instrumentation.activeSpan = null
this.id = uuid.v4()
this._defaultName = name || ''

@@ -61,19 +31,76 @@ this._customName = ''

this.type = type || 'custom'
this.result = 'success'
this.spans = []
this._builtSpans = []
this._result = 'success'
this._builtSpans = 0
this._droppedSpans = 0
this._contextLost = false // TODO: Send this up to the server some how
this.ended = false
this._abortTime = 0
this._agent = agent
this._agent._instrumentation.currentTransaction = this
this._timer = new Timer()
this.timestamp = this._timer.start
}
// Random sampling
this.sampled = Math.random() <= this._agent._conf.transactionSampleRate
Object.defineProperty(Transaction.prototype, 'name', {
configurable: true,
enumerable: true,
get () {
// Fall back to a somewhat useful name in case no _defaultName is set.
// This might happen if res.writeHead wasn't called.
return this._customName ||
this._defaultName ||
(this.req ? this.req.method + ' unknown route (unnamed)' : 'unnamed')
},
set (name) {
if (this.ended) {
this._agent.logger.debug('tried to set transaction.name on already ended transaction %o', { trans: this.id, parent: this.parentId, trace: this.traceId })
return
}
this._agent.logger.debug('setting transaction name %o', { trans: this.id, parent: this.parentId, trace: this.traceId, name: name })
this._customName = name
}
})
agent.logger.debug('start transaction %o', { id: this.id, name: name, type: type })
Object.defineProperty(Transaction.prototype, 'result', {
configurable: true,
enumerable: true,
get () {
return this._result
},
set (result) {
if (this.ended) {
this._agent.logger.debug('tried to set transaction.result on already ended transaction %o', { trans: this.id, parent: this.parentId, trace: this.traceId })
return
}
this._agent.logger.debug('setting transaction result %o', { trans: this.id, parent: this.parentId, trace: this.traceId, result: result })
this._result = result
}
})
this._timer = new Timer()
}
Object.defineProperty(Transaction.prototype, 'id', {
enumerable: true,
get () {
return this.context.id
}
})
Object.defineProperty(Transaction.prototype, 'traceId', {
enumerable: true,
get () {
return this.context.traceId
}
})
Object.defineProperty(Transaction.prototype, 'parentId', {
enumerable: true,
get () {
return this.context.parentId
}
})
Object.defineProperty(Transaction.prototype, 'sampled', {
enumerable: true,
get () {
return this.context.sampled
}
})
Transaction.prototype.setUserContext = function (context) {

@@ -92,3 +119,3 @@ if (!context) return

if (!this._tags) this._tags = {}
var skey = key.replace(/[.*]/g, '_')
var skey = key.replace(/[.*"]/g, '_')
if (key !== skey) {

@@ -112,3 +139,3 @@ this._agent.logger.warn('Illegal characters used in tag key: %s', key)

Transaction.prototype.buildSpan = function () {
Transaction.prototype.startSpan = function (name, type) {
if (!this.sampled) {

@@ -119,13 +146,12 @@ return null

if (this.ended) {
this._agent.logger.debug('transaction already ended - cannot build new span %o', { id: this.id })
this._agent.logger.debug('transaction already ended - cannot build new span %o', { trans: this.id, parent: this.parentId, trace: this.traceId }) // TODO: Should this be supported in the new API?
return null
}
if (this._builtSpans.length >= this._agent._conf.transactionMaxSpans) {
if (this._builtSpans >= this._agent._conf.transactionMaxSpans) {
this._droppedSpans++
return null
}
this._builtSpans++
var span = new Span(this)
this._builtSpans.push(span)
return span
return new Span(this, name, type)
}

@@ -136,10 +162,14 @@

id: this.id,
trace_id: this.traceId,
parent_id: this.parentId,
name: this.name,
type: this.type,
duration: this.duration(),
timestamp: new Date(this._timer.start).toISOString(),
timestamp: this.timestamp,
result: String(this.result),
sampled: this.sampled,
context: null,
spans: null
context: undefined,
span_count: {
started: this._builtSpans
}
}

@@ -160,7 +190,3 @@

if (this._droppedSpans > 0) {
payload.span_count = {
dropped: {
total: this._droppedSpans
}
}
payload.span_count.dropped = this._droppedSpans
}

@@ -181,21 +207,9 @@

Transaction.prototype._encode = function (cb) {
var self = this
Transaction.prototype._encode = function () {
if (!this.ended) {
this._agent.logger.error('cannot encode un-ended transaction: %o', { trans: this.id, parent: this.parentId, trace: this.traceId })
return null
}
if (!this.ended) return cb(new Error('cannot encode un-ended transaction'))
var payload = this.toJSON()
var next = afterAll(function (err, spans) {
if (err) return cb(err)
if (self.sampled) {
payload.spans = spans
}
cb(null, payload)
})
this.spans.forEach(function (span) {
span._encode(next())
})
return this.toJSON()
}

@@ -205,11 +219,11 @@

if (!this.ended) {
this._agent.logger.debug('tried to call duration() on un-ended transaction %o', { id: this.id, name: this.name, type: this.type })
this._agent.logger.debug('tried to call duration() on un-ended transaction %o', { trans: this.id, parent: this.parentId, trace: this.traceId, name: this.name, type: this.type })
return null
}
return this._timer.duration()
return this._timer.duration
}
Transaction.prototype.setDefaultName = function (name) {
this._agent.logger.debug('setting default transaction name: %s %o', name, { id: this.id })
this._agent.logger.debug('setting default transaction name: %s %o', name, { trans: this.id, parent: this.parentId, trace: this.traceId })
this._defaultName = name

@@ -230,3 +244,5 @@ }

mountstack: req[symbols.expressMountStack] ? req[symbols.expressMountStack].length : false,
id: this.id
trans: this.id,
parent: this.parentId,
trace: this.traceId
})

@@ -239,5 +255,9 @@ path = 'unknown route'

Transaction.prototype.ensureParentId = function () {
return this.context.ensureParentId()
}
Transaction.prototype.end = function (result) {
if (this.ended) {
this._agent.logger.debug('tried to call transaction.end() on already ended transaction %o', { id: this.id })
this._agent.logger.debug('tried to call transaction.end() on already ended transaction %o', { trans: this.id, parent: this.parentId, trace: this.traceId })
return

@@ -252,7 +272,2 @@ }

this._builtSpans.forEach(function (span) {
if (span.ended || !span.started) return
span.truncate()
})
this._timer.end()

@@ -269,20 +284,11 @@ this.ended = true

if (!trans) {
this._agent.logger.debug('WARNING: no currentTransaction found %o', { current: trans, spans: this.spans.length, id: this.id })
this.spans = []
this._agent.logger.debug('WARNING: no currentTransaction found %o', { current: trans, spans: this._builtSpans, trans: this.id, parent: this.parentId, trace: this.traceId })
this._contextLost = true
} else if (trans !== this) {
this._agent.logger.debug('WARNING: transaction is out of sync %o', { spans: this.spans.length, id: this.id, other: trans.id })
this.spans = []
this._agent.logger.debug('WARNING: transaction is out of sync %o', { other: trans.id, spans: this._builtSpans, trans: this.id, parent: this.parentId, trace: this.traceId })
this._contextLost = true
}
this._agent._instrumentation.addEndedTransaction(this)
this._agent.logger.debug('ended transaction %o', { id: this.id, type: this.type, result: this.result, name: this.name })
this._agent.logger.debug('ended transaction %o', { trans: this.id, parent: this.parentId, trace: this.traceId, type: this.type, result: this.result, name: this.name })
}
Transaction.prototype._recordEndedSpan = function (span) {
if (this.ended) {
this._agent.logger.debug('Can\'t record ended span after parent transaction have ended - ignoring %o', { id: this.id, span: span.name })
return
}
this.spans.push(span)
}
{
"name": "elastic-apm-node",
"version": "1.14.3",
"version": "2.0.0",
"description": "The official Elastic APM agent for Node.js",

@@ -72,76 +72,76 @@ "main": "index.js",

"async-value-promise": "^1.1.1",
"basic-auth": "^2.0.0",
"basic-auth": "^2.0.1",
"console-log-level": "^1.4.0",
"cookie": "^0.3.1",
"core-util-is": "^1.0.2",
"elastic-apm-http-client": "^5.2.1",
"elastic-apm-http-client": "^6.0.1",
"end-of-stream": "^1.4.1",
"fast-safe-stringify": "^2.0.4",
"fast-safe-stringify": "^2.0.6",
"http-headers": "^3.0.2",
"is-native": "^1.0.1",
"normalize-bool": "^1.0.0",
"original-url": "^1.2.1",
"read-pkg-up": "^3.0.0",
"original-url": "^1.2.2",
"read-pkg-up": "^4.0.0",
"redact-secrets": "^1.0.0",
"relative-microtime": "^2.0.0",
"require-ancestors": "^1.0.0",
"require-in-the-middle": "^3.1.0",
"semver": "^5.5.0",
"semver": "^5.6.0",
"set-cookie-serde": "^1.0.0",
"sql-summary": "^1.0.1",
"stackman": "^3.0.2",
"unicode-byte-truncate": "^1.0.0",
"uuid": "^3.2.1"
"unicode-byte-truncate": "^1.0.0"
},
"devDependencies": {
"@commitlint/cli": "^7.0.0",
"@commitlint/config-conventional": "^7.0.1",
"@commitlint/travis-cli": "^7.0.0",
"apollo-server-express": "^2.0.2",
"bluebird": "^3.4.6",
"@commitlint/cli": "^7.2.1",
"@commitlint/config-conventional": "^7.1.2",
"@commitlint/travis-cli": "^7.2.1",
"apollo-server-express": "^2.1.0",
"bluebird": "^3.5.2",
"cassandra-driver": "^3.5.0",
"connect": "^3.6.3",
"dependency-check": "^2.10.1",
"elasticsearch": "^15.0.0",
"express": "^4.14.0",
"connect": "^3.6.6",
"dependency-check": "^3.2.1",
"elasticsearch": "^15.1.1",
"express": "^4.16.4",
"express-graphql": "^0.6.12",
"express-queue": "0.0.12",
"express-queue": "^0.0.12",
"finalhandler": "^1.1.1",
"generic-pool": "^3.1.5",
"get-port": "^3.2.0",
"got": "^9.0.0",
"generic-pool": "^3.4.2",
"get-port": "^4.0.0",
"got": "^9.2.2",
"graphql": "^0.13.2",
"handlebars": "^4.0.11",
"hapi": "^17.2.2",
"handlebars": "^4.0.12",
"hapi": "^17.6.0",
"https-pem": "^2.0.0",
"inquirer": "^0.12.0",
"ioredis": "^3.0.0",
"is-my-json-valid": "^2.17.2",
"json-schema-ref-parser": "^5.0.3",
"knex": "^0.15.0",
"koa": "^2.2.0",
"koa-router": "^7.1.1",
"mimic-response": "^1.0.0",
"mkdirp": "^0.5.0",
"mongodb-core": "^3.0.2",
"mysql": "^2.14.1",
"is-my-json-valid": "^2.19.0",
"json-schema-ref-parser": "^6.0.1",
"knex": "^0.15.2",
"koa": "^2.5.3",
"koa-router": "^7.4.0",
"mimic-response": "^1.0.1",
"mkdirp": "^0.5.1",
"mongodb-core": "^3.1.7",
"mysql": "^2.16.0",
"mysql2": "^1.6.3",
"nyc": "^12.0.2",
"ndjson": "^1.5.0",
"nyc": "^13.1.0",
"once": "^1.4.0",
"p-finally": "^1.0.0",
"pg": "^7.1.0",
"redis": "^2.6.3",
"request": "^2.86.0",
"pg": "^7.5.0",
"redis": "^2.8.0",
"request": "^2.88.0",
"restify": "^7.2.1",
"restify-clients": "^2.0.2",
"restify-clients": "^2.6.2",
"rimraf": "^2.6.2",
"send": "^0.16.1",
"send": "^0.16.2",
"standard": "^12.0.1",
"tape": "^4.8.0",
"tape": "^4.9.1",
"tedious": "^2.6.1",
"test-all-versions": "^3.3.3",
"thunky": "^1.0.2",
"untildify": "^3.0.2",
"thunky": "^1.0.3",
"untildify": "^3.0.3",
"util.promisify": "^1.0.0",
"wait-on": "^2.1.2",
"ws": "^6.0.0"
"wait-on": "^3.1.0",
"ws": "^6.1.0"
},

@@ -164,5 +164,5 @@ "greenkeeper": {

"coordinates": [
55.778238,
12.593177
55.778239,
12.593173
]
}

@@ -9,3 +9,3 @@ # Elastic APM Node.js Agent

[![npm](https://img.shields.io/npm/v/elastic-apm-node.svg)](https://www.npmjs.com/package/elastic-apm-node)
[![Build status](https://travis-ci.org/elastic/apm-agent-nodejs.svg?branch=1.x)](https://travis-ci.org/elastic/apm-agent-nodejs)
[![Build status](https://travis-ci.org/elastic/apm-agent-nodejs.svg?branch=2.x)](https://travis-ci.org/elastic/apm-agent-nodejs)
[![Standard - JavaScript Style Guide](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat)](https://github.com/standard/standard)

@@ -23,3 +23,5 @@

make sure you have the prerequisites in place first.
This agent is compatible with [APM Server](https://github.com/elastic/apm-server) v6.2 and above.
This agent is compatible with [APM Server](https://github.com/elastic/apm-server) v6.5 and above.
For support for previous releases of the APM Server,
use version [1.x](https://github.com/elastic/apm-agent-nodejs/tree/1.x) of the agent.
For details see [Getting Started with Elastic APM](https://www.elastic.co/guide/en/apm/get-started)

@@ -54,5 +56,10 @@

To ease development,
set the environment variable `DEBUG_PAYLOAD=1` to have the agent dump the JSON payload sent to the APM Server to a temporary file on your local harddrive.
To see what data is being sent to the APM Server,
use the environment variable `ELASTIC_APM_PAYLOAD_LOG_FILE` (or the config option `payloadLogFile`) to speicfy a log file,
e.g:
```
ELASTIC_APM_PAYLOAD_LOG_FILE=/tmp/payload.ndjson
```
Please see [TESTING.md](TESTING.md) for instructions on how to run the test suite.

@@ -59,0 +66,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc