Skip to content

Commit

Permalink
perf: improve performance of getMetricAsString in next (#548)
Browse files Browse the repository at this point in the history
Co-authored-by: Dan Shappir <dan.s@nextinsurance.com>
  • Loading branch information
2 people authored and SimenB committed Mar 9, 2023
1 parent fd0a4f5 commit 2af0453
Showing 1 changed file with 49 additions and 79 deletions.
128 changes: 49 additions & 79 deletions lib/registry.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,6 @@

const { getValueAsString } = require('./util');

function escapeString(str) {
return str.replace(/\n/g, '\\n').replace(/\\(?!n)/g, '\\\\');
}
function escapeLabelValue(str) {
if (typeof str !== 'string') {
return str;
}
return escapeString(str).replace(/"/g, '\\"');
}
function standardizeCounterName(name) {
if (name.endsWith('_total')) {
return name.replace('_total', '');
}
return name;
}

class Registry {
static get PROMETHEUS_CONTENT_TYPE() {
return 'text/plain; version=0.0.4; charset=utf-8';
Expand All @@ -44,84 +28,51 @@ class Registry {
return Object.values(this._metrics);
}

getLabelSetAsString(metric) {
const defaultLabelNames = Object.keys(this._defaultLabels);
let values = '';

for (const val of metric.values || []) {
val.labels = val.labels || {};
async getMetricsAsString(metrics) {
const metric = await metrics.get();

if (defaultLabelNames.length > 0) {
// Make a copy before mutating
val.labels = Object.assign({}, val.labels);
const name = escapeString(metric.name);
const help = `# HELP ${name} ${escapeString(metric.help)}`;
const type = `# TYPE ${name} ${metric.type}`;
const values = [help, type];

for (const labelName of defaultLabelNames) {
val.labels[labelName] =
val.labels[labelName] || this._defaultLabels[labelName];
}
}
const defaultLabels =
Object.keys(this._defaultLabels).length > 0 ? this._defaultLabels : null;

let metricName = val.metricName || metric.name;
for (const val of metric.values || []) {
let { metricName = name, labels = {} } = val;
if (
this.contentType === Registry.OPENMETRICS_CONTENT_TYPE &&
metric.type === 'counter'
) {
metricName = `${metricName}_total`;
}

const keys = Object.keys(val.labels);
const size = keys.length;
if (size > 0) {
let labels = '';
let i = 0;
for (; i < size - 1; i++) {
labels += `${keys[i]}="${escapeLabelValue(val.labels[keys[i]])}",`;
}
labels += `${keys[i]}="${escapeLabelValue(val.labels[keys[i]])}"`;
metricName += `{${labels}}`;
if (defaultLabels) {
labels = { ...labels, ...defaultLabels, ...labels };
}
values += `${metricName} ${getValueAsString(val.value)}`;
if (
val.exemplar &&
this.contentType === Registry.OPENMETRICS_CONTENT_TYPE
) {
const exemplarKeys = Object.keys(val.exemplar.labelSet);
const exemplarSize = exemplarKeys.length;
if (exemplarSize > 0) {
let labels = '';
let i = 0;
for (; i < exemplarSize - 1; i++) {
labels += `${exemplarKeys[i]}="${escapeLabelValue(
val.exemplar.labelSet[exemplarKeys[i]],
)}",`;
}
labels += `${exemplarKeys[i]}="${escapeLabelValue(
val.exemplar.labelSet[exemplarKeys[i]],
)}"`;
values += ` # {${labels}} ${getValueAsString(val.exemplar.value)} ${
val.exemplar.timestamp
}`;
} else {
values += ` # {} ${getValueAsString(val.exemplar.value)} ${
val.exemplar.timestamp
}`;
}
}
values += '\n';
}

return values;
}
const formattedLabels = formatLabels(labels);
const labelsString = formattedLabels.length
? `{${formattedLabels.join(',')}}`
: '';

async getMetricsAsString(metrics) {
const metric = await metrics.get();
values.push(
`${metricName}${labelsString} ${getValueAsString(val.value)}`,
);

const name = escapeString(metric.name);
const help = `# HELP ${name} ${escapeString(metric.help)}`;
const type = `# TYPE ${name} ${metric.type}`;
const values = this.getLabelSetAsString(metric);
const { exemplar } = val;
if (exemplar && this.contentType === Registry.OPENMETRICS_CONTENT_TYPE) {
const formattedExemplars = formatLabels(exemplar.labelSet);
values.push(
` # {${formattedExemplars.join(',')}} ${getValueAsString(
exemplar.value,
)} ${exemplar.timestamp}`,
);
}
}

return `${help}\n${type}\n${values}`.trim();
return values.join('\n');
}

async metrics() {
Expand Down Expand Up @@ -250,5 +201,24 @@ class Registry {
}
}

function formatLabels(labels) {
return Object.entries(labels).map(
([n, v]) => `${n}="${escapeLabelValue(v)}"`,
);
}

function escapeLabelValue(str) {
if (typeof str !== 'string') {
return str;
}
return escapeString(str).replace(/"/g, '\\"');
}
function escapeString(str) {
return str.replace(/\n/g, '\\n').replace(/\\(?!n)/g, '\\\\');
}
function standardizeCounterName(name) {
return name.replace(/_total$/, '');
}

module.exports = Registry;
module.exports.globalRegistry = new Registry();

0 comments on commit 2af0453

Please sign in to comment.