dd-trace
Version:
Datadog APM tracing client for JavaScript
113 lines (86 loc) • 2.61 kB
JavaScript
const log = require('../../../log')
const { Namespace } = require('../../../telemetry/metrics')
const { IAST_TRACE_METRIC_PREFIX } = require('../tags')
const { addMetricsToSpan } = require('./span-tags')
const DD_IAST_METRICS_NAMESPACE = Symbol('_dd.iast.request.metrics.namespace')
function initRequestNamespace (context) {
if (!context) return
const namespace = new IastNamespace()
context[DD_IAST_METRICS_NAMESPACE] = namespace
return namespace
}
function getNamespaceFromContext (context) {
return context?.[DD_IAST_METRICS_NAMESPACE]
}
function finalizeRequestNamespace (context, rootSpan) {
try {
const namespace = getNamespaceFromContext(context)
if (!namespace) return
addMetricsToSpan(rootSpan, [...namespace.metrics.values()], IAST_TRACE_METRIC_PREFIX)
merge(namespace)
namespace.clear()
} catch (e) {
log.error('[ASM] Error merging request metrics', e)
} finally {
if (context) {
delete context[DD_IAST_METRICS_NAMESPACE]
}
}
}
function merge (namespace) {
for (const [metricName, metricsByTagMap] of namespace.iastMetrics) {
for (const [tags, metric] of metricsByTagMap) {
const { type, points } = metric
if (points?.length && type === 'count') {
const gMetric = globalNamespace.getMetric(metricName, tags)
points.forEach(point => gMetric.inc(point[1]))
}
}
}
}
class IastNamespace extends Namespace {
constructor (maxMetricTagsSize = 100) {
super('iast')
this.maxMetricTagsSize = maxMetricTagsSize
this.iastMetrics = new Map()
}
getIastMetrics (name) {
let metrics = this.iastMetrics.get(name)
if (!metrics) {
metrics = new Map()
this.iastMetrics.set(name, metrics)
}
return metrics
}
getMetric (name, tags, type = 'count') {
const metrics = this.getIastMetrics(name)
let metric = metrics.get(tags)
if (!metric) {
metric = super[type](name, Array.isArray(tags) ? [...tags] : tags)
if (metrics.size === this.maxMetricTagsSize) {
metrics.clear()
log.error('[ASM] Tags cache max size reached for metric %s', name)
}
metrics.set(tags, metric)
}
return metric
}
count (name, tags) {
return this.getMetric(name, tags, 'count')
}
clear () {
this.iastMetrics.clear()
this.distributions.clear()
this.metrics.clear()
}
}
const globalNamespace = new IastNamespace()
module.exports = {
initRequestNamespace,
getNamespaceFromContext,
finalizeRequestNamespace,
globalNamespace,
DD_IAST_METRICS_NAMESPACE,
IastNamespace
}