dd-trace
Version:
Datadog APM tracing client for JavaScript
127 lines (113 loc) • 4.08 kB
JavaScript
/* eslint n/no-unsupported-features/node-builtins: ['error', { ignores: ['os.availableParallelism'] }] */
const os = require('os')
const perf = require('perf_hooks').performance
const version = require('../../../../../package.json').version
const { availableParallelism, libuvThreadPoolSize } = require('../libuv-size')
const processTags = require('../../process-tags')
/** @typedef {import('../../config/config-base')} TracerConfig */
/**
* Maps the canonical profiling.enabled value to the activation reported in the
* profile event.
*
* @param {string} [enabled] - config.profiling.enabled ('true' | 'false' | 'auto')
* @returns {string}
*/
function getActivation (enabled) {
if (enabled === 'auto') return 'auto'
if (enabled === 'true') return 'manual'
return 'unknown'
}
class EventSerializer {
#activation
#appVersion
#env
#host
#libraryInjected
#service
/** @param {TracerConfig} config */
constructor (config) {
this.#env = config.env
this.#host = config.reportHostname ? os.hostname() : undefined
this.#service = config.service
this.#appVersion = config.version
this.#libraryInjected = !!config.DD_INJECTION_ENABLED
this.#activation = getActivation(config.profiling?.enabled)
}
/**
* Returns the destination URL for the near-OOM export subprocess, or nothing when this exporter
* does not support OOM export. Overridden by {@link AgentExporter} and {@link FileExporter}.
*
* @returns {URL | undefined}
*/
getExportUrl () {}
typeToFile (type) {
return `${type}.pprof`
}
getEventJSON ({ profiles, infos, start, end, tags = {}, endpointCounts, customAttributes }) {
const event = {
attachments: Object.keys(profiles).map(t => this.typeToFile(t)),
start: start.toISOString(),
end: end.toISOString(),
family: 'node',
version: '4',
tags_profiler: [
'language:javascript',
'runtime:nodejs',
`runtime_arch:${process.arch}`,
`runtime_os:${process.platform}`,
`runtime_version:${process.version}`,
`process_id:${process.pid}`,
`profiler_version:${version}`,
'format:pprof',
...Object.entries(tags).map(([key, value]) => `${key}:${value}`),
].join(','),
endpoint_counts: endpointCounts,
info: {
application: {
env: this.#env,
service: this.#service,
start_time: new Date(perf.nodeTiming.nodeStart + perf.timeOrigin).toISOString(),
version: this.#appVersion,
},
platform: {
hostname: this.#host,
kernel_name: os.type(),
kernel_release: os.release(),
kernel_version: os.version(),
},
profiler: {
activation: this.#activation,
ssi: {
mechanism: this.#libraryInjected ? 'injected_agent' : 'none',
},
version,
...infos,
},
runtime: {
available_processors: availableParallelism(),
// Using `nodejs` for consistency with the existing `runtime` tag.
// Note that the event `family` property uses `node`, as that's what's
// proscribed by the Intake API, but that's an internal enum and is
// not customer visible.
engine: 'nodejs',
libuv_threadpool_size: libuvThreadPoolSize,
// strip off leading 'v'. This makes the format consistent with other
// runtimes (e.g. Ruby) but not with the existing `runtime_version` tag.
// We'll keep it like this as we want cross-engine consistency. We
// also aren't changing the format of the existing tag as we don't want
// to break it.
version: process.version.slice(1),
},
},
}
if (customAttributes) {
event.custom_attributes = customAttributes
}
if (processTags.serialized) {
event[processTags.PROFILING_FIELD_NAME] = processTags.serialized
}
return JSON.stringify(event)
}
}
module.exports = { EventSerializer, getActivation }