dd-trace
Version:
Datadog APM tracing client for JavaScript
164 lines (133 loc) • 4.67 kB
JavaScript
const log = require('./log')
const format = require('./format')
const SpanSampler = require('./span_sampler')
const GitMetadataTagger = require('./git_metadata_tagger')
const { getEnvironmentVariable } = require('./config-helper')
const startedSpans = new WeakSet()
const finishedSpans = new WeakSet()
const { channel } = require('dc-polyfill')
const spanProcessCh = channel('dd-trace:span:process')
class SpanProcessor {
constructor (exporter, prioritySampler, config) {
this._exporter = exporter
this._prioritySampler = prioritySampler
this._config = config
this._killAll = false
// TODO: This should already have been calculated in `config.js`.
if (config.stats?.enabled && !config.appsec?.standalone?.enabled) {
const { SpanStatsProcessor } = require('./span_stats')
this._stats = new SpanStatsProcessor(config)
}
this._spanSampler = new SpanSampler(config.sampler)
this._gitMetadataTagger = new GitMetadataTagger(config)
}
process (span) {
const spanContext = span.context()
const active = []
const formatted = []
const trace = spanContext._trace
const { flushMinSpans, tracing } = this._config
const { started, finished } = trace
if (trace.record === false) return
if (tracing === false) {
this._erase(trace, active)
return
}
if (started.length === finished.length || finished.length >= flushMinSpans) {
this._prioritySampler.sample(spanContext)
this._spanSampler.sample(spanContext)
this._gitMetadataTagger.tagGitMetadata(spanContext)
for (const span of started) {
if (span._duration === undefined) {
active.push(span)
} else {
const formattedSpan = format(span)
this._stats?.onSpanFinished(formattedSpan)
formatted.push(formattedSpan)
spanProcessCh.publish({ span })
}
}
if (formatted.length !== 0 && trace.isRecording !== false) {
this._exporter.export(formatted)
}
this._erase(trace, active)
}
if (this._killAll) {
for (const startedSpan of started) {
if (!startedSpan._finished) {
startedSpan.finish()
}
}
}
}
killAll () {
this._killAll = true
}
_erase (trace, active) {
if (getEnvironmentVariable('DD_TRACE_EXPERIMENTAL_STATE_TRACKING') === 'true') {
const started = new Set()
const startedIds = new Set()
const finished = new Set()
const finishedIds = new Set()
for (const span of trace.finished) {
const context = span.context()
const id = context.toSpanId()
if (finished.has(span)) {
log.error('Span was already finished in the same trace: %s', span)
} else {
finished.add(span)
if (finishedIds.has(id)) {
log.error('Another span with the same ID was already finished in the same trace: %s', span)
} else {
finishedIds.add(id)
}
if (context._trace !== trace) {
log.error('A span was finished in the wrong trace: %s', span)
}
if (finishedSpans.has(span)) {
log.error('Span was already finished in a different trace: %s', span)
} else {
finishedSpans.add(span)
}
}
}
for (const span of trace.started) {
const context = span.context()
const id = context.toSpanId()
if (started.has(span)) {
log.error('Span was already started in the same trace: %s', span)
} else {
started.add(span)
if (startedIds.has(id)) {
log.error('Another span with the same ID was already started in the same trace: %s', span)
} else {
startedIds.add(id)
}
if (context._trace !== trace) {
log.error('A span was started in the wrong trace: %s', span)
}
if (startedSpans.has(span)) {
log.error('Span was already started in a different trace: %s', span)
} else {
startedSpans.add(span)
}
}
if (!finished.has(span)) {
log.error('Span started in one trace but was finished in another trace: %s', span)
}
}
for (const span of trace.finished) {
if (!started.has(span)) {
log.error('Span finished in one trace but was started in another trace: %s', span)
}
}
}
for (const span of trace.finished) {
span.context()._tags = {}
}
trace.started = active
trace.finished = []
}
}
module.exports = SpanProcessor