dd-trace
Version:
Datadog APM tracing client for JavaScript
95 lines (80 loc) • 2.49 kB
JavaScript
const { PAYLOAD_TAGGING_MAX_TAGS } = require('../constants')
const redactedKeys = new Set([
'authorization', 'x-authorization', 'password', 'token'
])
const truncated = 'truncated'
const redacted = 'redacted'
/**
* Escapes dots in keys to preserve hierarchy in flattened tag names.
*
* @param {string} key
* @returns {string}
*/
function escapeKey (key) {
return key.replaceAll('.', String.raw`\.`)
}
/**
* Compute normalized payload tags from any given object.
*
* - Limits total tag count to `PAYLOAD_TAGGING_MAX_TAGS - 1` plus the `_dd.payload_tags_incomplete` flag
* - Truncates values at max depth and for large scalars
* - Redacts known sensitive keys
*
* @param {unknown} object - Input to flatten into tags
* @param {{ maxDepth: number, prefix: string }} opts - Traversal options
* @returns {Record<string, string|boolean>} Map of tag names to values
*/
function tagsFromObject (object, opts) {
const { maxDepth, prefix } = opts
let tagCount = 0
let abort = false
/** @type {Record<string, string|boolean>} */
const result = {}
function tagRec (prefix, object, depth = 0) {
// Off by one: _dd.payload_tags_trimmed counts as 1 tag
if (abort) { return }
if (tagCount >= PAYLOAD_TAGGING_MAX_TAGS - 1) {
abort = true
result['_dd.payload_tags_incomplete'] = true
return
}
if (depth >= maxDepth && object !== null && typeof object === 'object') {
tagCount += 1
result[prefix] = truncated
return
}
if (object === undefined) {
tagCount += 1
result[prefix] = 'undefined'
return
}
if (object === null) {
tagCount += 1
result[prefix] = 'null'
return
}
if (['number', 'boolean'].includes(typeof object) || Buffer.isBuffer(object)) {
tagCount += 1
result[prefix] = object.toString().slice(0, 5000)
return
}
if (typeof object === 'string') {
tagCount += 1
result[prefix] = object.slice(0, 5000)
}
if (typeof object === 'object') { // eslint-disable-line eslint-rules/eslint-safe-typeof-object
for (const [key, value] of Object.entries(object)) {
if (redactedKeys.has(key.toLowerCase())) {
tagCount += 1
result[`${prefix}.${escapeKey(key)}`] = redacted
} else {
tagRec(`${prefix}.${escapeKey(key)}`, value, depth + 1)
}
}
}
}
tagRec(prefix, object)
return result
}
module.exports = { tagsFromObject }