Skip to content

Commit

Permalink
PROF-10497: Add span IDs to timeline DNS and TCP events (#4655)
Browse files Browse the repository at this point in the history
  • Loading branch information
szegedi authored and juan-fernandez committed Sep 30, 2024
1 parent 1cf0454 commit cb81b82
Show file tree
Hide file tree
Showing 11 changed files with 291 additions and 36 deletions.
13 changes: 12 additions & 1 deletion integration-tests/profiler/profiler.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,10 +125,12 @@ async function gatherNetworkTimelineEvents (cwd, scriptFilePath, eventType, args
const addressKey = strings.dedup('address')
const portKey = strings.dedup('port')
const nameKey = strings.dedup('operation')
const spanIdKey = strings.dedup('span id')
const localRootSpanIdKey = strings.dedup('local root span id')
const eventValue = strings.dedup(eventType)
const events = []
for (const sample of profile.sample) {
let ts, event, host, address, port, name
let ts, event, host, address, port, name, spanId, localRootSpanId
for (const label of sample.label) {
switch (label.key) {
case tsKey: ts = label.num; break
Expand All @@ -137,13 +139,22 @@ async function gatherNetworkTimelineEvents (cwd, scriptFilePath, eventType, args
case hostKey: host = label.str; break
case addressKey: address = label.str; break
case portKey: port = label.num; break
case spanIdKey: spanId = label.str; break
case localRootSpanIdKey: localRootSpanId = label.str; break
default: assert.fail(`Unexpected label key ${label.key} ${strings.strings[label.key]} ${encoded}`)
}
}
// Timestamp must be defined and be between process start and end time
assert.isDefined(ts, encoded)
assert.isTrue(ts <= procEnd, encoded)
assert.isTrue(ts >= procStart, encoded)
if (process.platform !== 'win32') {
assert.isDefined(spanId, encoded)
assert.isDefined(localRootSpanId, encoded)
} else {
assert.isUndefined(spanId, encoded)
assert.isUndefined(localRootSpanId, encoded)
}
// Gather only DNS events; ignore sporadic GC events
if (event === eventValue) {
assert.isDefined(name, encoded)
Expand Down
13 changes: 13 additions & 0 deletions packages/dd-trace/src/profiling/profilers/event_plugins/dns.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const EventPlugin = require('./event')

class DNSPlugin extends EventPlugin {
static get id () {
return 'dns'
}

static get entryType () {
return 'dns'
}
}

module.exports = DNSPlugin
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
const DNSPlugin = require('./dns')

class DNSLookupPlugin extends DNSPlugin {
static get operation () {
return 'lookup'
}

extendEvent (event, startEvent) {
event.name = 'lookup'
event.detail = { hostname: startEvent[0] }

return event
}
}

module.exports = DNSLookupPlugin
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
const DNSPlugin = require('./dns')

class DNSLookupServicePlugin extends DNSPlugin {
static get operation () {
return 'lookup_service'
}

extendEvent (event, startEvent) {
event.name = 'lookupService'
event.detail = { host: startEvent[0], port: startEvent[1] }

return event
}
}

module.exports = DNSLookupServicePlugin
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
const DNSPlugin = require('./dns')

const queryNames = new Map()

class DNSResolvePlugin extends DNSPlugin {
static get operation () {
return 'resolve'
}

extendEvent (event, startEvent) {
const rrtype = startEvent[1]
let name = queryNames.get(rrtype)
if (!name) {
name = `query${rrtype}`
queryNames.set(rrtype, name)
}
event.name = name
event.detail = { host: startEvent[0] }

return event
}
}

module.exports = DNSResolvePlugin
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
const DNSPlugin = require('./dns')

class DNSReversePlugin extends DNSPlugin {
static get operation () {
return 'reverse'
}

extendEvent (event, startEvent) {
event.name = 'getHostByAddr'
event.detail = { host: startEvent[0] }

return event
}
}

module.exports = DNSReversePlugin
48 changes: 48 additions & 0 deletions packages/dd-trace/src/profiling/profilers/event_plugins/event.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
const { AsyncLocalStorage } = require('async_hooks')
const TracingPlugin = require('../../../plugins/tracing')
const { performance } = require('perf_hooks')

// We are leveraging the TracingPlugin class for its functionality to bind
// start/error/finish methods to the appropriate diagnostic channels.
class EventPlugin extends TracingPlugin {
constructor (eventHandler) {
super()
this.eventHandler = eventHandler
this.store = new AsyncLocalStorage()
this.entryType = this.constructor.entryType
}

start (startEvent) {
this.store.enterWith({
startEvent,
startTime: performance.now()
})
}

error () {
this.store.getStore().error = true
}

finish () {
const { startEvent, startTime, error } = this.store.getStore()
if (error) {
return // don't emit perf events for failed operations
}
const duration = performance.now() - startTime

const context = this.activeSpan?.context()
const _ddSpanId = context?.toSpanId()
const _ddRootSpanId = context?._trace.started[0]?.context().toSpanId() || _ddSpanId

const event = {
entryType: this.entryType,
startTime,
duration,
_ddSpanId,
_ddRootSpanId
}
this.eventHandler(this.extendEvent(event, startEvent))
}
}

module.exports = EventPlugin
24 changes: 24 additions & 0 deletions packages/dd-trace/src/profiling/profilers/event_plugins/net.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
const EventPlugin = require('./event')

class NetPlugin extends EventPlugin {
static get id () {
return 'net'
}

static get operation () {
return 'tcp'
}

static get entryType () {
return 'net'
}

extendEvent (event, { options }) {
event.name = 'connect'
event.detail = options

return event
}
}

module.exports = NetPlugin
Loading

0 comments on commit cb81b82

Please sign in to comment.