newrelic
Version:
New Relic agent
135 lines (104 loc) • 3.03 kB
JavaScript
/*
* Copyright 2020 New Relic Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
module.exports = parse
module.exports.fromString = fromString
module.exports.fromMap = fromMap
// this creates a copy of trim that can be used with map
const trim = Function.prototype.call.bind(String.prototype.trim)
const stringify = require('json-stringify-safe')
// pass in parent logger to avoid circular deps
function parse(labels, parentLogger) {
const logger = parentLogger.child({ component: 'label-parser' })
let results
if (!labels) {
return []
} else if (typeof labels === 'string') {
results = fromString(labels)
} else if (labels) {
results = fromMap(labels)
}
results.warnings.forEach(function logWarnings(message) {
logger.warn(message)
})
return results.labels
}
function fromString(raw) {
const map = Object.create(null)
if (!raw) {
return { labels: [], warnings: [] }
}
const pairs = raw.split(';').map(trim)
let parts
while (!pairs[pairs.length - 1]) {
pairs.pop()
}
while (!pairs[0]) {
pairs.shift()
}
for (let i = 0, l = pairs.length; i < l; ++i) {
parts = pairs[i].split(':').map(trim)
if (parts.length !== 2) {
return warn('Could not create a Label pair from ' + parts[i])
} else if (!parts[0]) {
return warn('Label key can not be empty')
} else if (!parts[1]) {
return warn('Label value can not be empty')
}
map[parts[0]] = parts[1]
}
return fromMap(map)
function warn(message) {
return { labels: [], warnings: ['Invalid Label String: ' + raw, message] }
}
}
function fromMap(map) {
const warnings = []
let labels = []
Object.keys(map).forEach(function processKeys(key) {
const type = truncate(key, 255)
if (!map[key] || typeof map[key] !== 'string') {
return warnings.push(
'Label value for ' + type + 'should be a string with a length between 1 and 255 characters'
)
}
const value = truncate(map[key], 255)
if (type !== key) {
warnings.push('Label key too long: ' + type)
}
if (value !== map[key]) {
warnings.push('Label value too long: ' + value)
}
labels.push({ label_type: type, label_value: value })
})
if (labels.length > 64) {
warnings.push('Too many Labels, list truncated to 64')
labels = labels.slice(0, 64)
}
if (warnings.length) {
try {
warnings.unshift('Partially Invalid Label Setting: ' + stringify(map))
} catch (err) {
warnings.unshift('Failed to stringify labels: ' + err.message)
}
}
return { labels, warnings }
}
function truncate(str, max) {
let len = 0
let i
for (i = 0; i < str.length; ++i) {
const chr = str.charCodeAt(i)
if (chr >= 0xd800 && chr <= 0xdbff && i !== str.length) {
// Handle UTF-16 surrogate pairs.
// eslint-disable-next-line sonarjs/updated-loop-counter
i += 1
}
if (++len === max) {
break
}
}
return str.slice(0, i + 1)
}