Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: export unit block when unit is set in metric descriptor #3041

Merged
merged 9 commits into from
Jul 29, 2022
1 change: 1 addition & 0 deletions experimental/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ All notable changes to experimental packages in this project will be documented

* feat(opentelemetry-instrumentation-fetch): optionally ignore network events #3028 @gregolsen
* feat(http-instrumentation): record exceptions in http instrumentation #3008 @luismiramirez
* feat(opentelemetry-exporter-prometheus): optionally export unit for metrics with metric defined #3015 @tapico-weyert

### :bug: (Bug Fix)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,9 @@ export class PrometheusSerializer {
const help = `# HELP ${name} ${escapeString(
metricData.descriptor.description || 'description missing'
)}`;
const unit = metricData.descriptor.unit ? `\n# UNIT ${name} ${escapeString(
dyladan marked this conversation as resolved.
Show resolved Hide resolved
metricData.descriptor.unit,
)}` : '';
const type = `# TYPE ${name} ${toPrometheusType(
metricData.descriptor.type,
dataPointType
Expand All @@ -233,7 +236,7 @@ export class PrometheusSerializer {
}
}

return `${help}\n${type}\n${results}`.trim();
return `${help}${unit}\n${type}\n${results}`.trim();
}

serializeSingularDataPoint(name: string, type: InstrumentType, dataPoint: DataPoint<number>): string {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -265,14 +265,14 @@ describe('PrometheusSerializer', () => {
});

describe('validate against metric conventions', () => {
async function getCounterResult(name: string, serializer: PrometheusSerializer) {
async function getCounterResult(name: string, unit: string | undefined = undefined, serializer: PrometheusSerializer, exportAll = false) {
weyert marked this conversation as resolved.
Show resolved Hide resolved
const reader = new TestMetricReader();
const meterProvider = new MeterProvider();
meterProvider.addMetricReader(reader);
meterProvider.addView({ aggregation: new SumAggregation() });
const meter = meterProvider.getMeter('test');

const counter = meter.createCounter(name);
const counter = meter.createCounter(name, { unit: unit });
counter.add(1);

const { resourceMetrics, errors } = await reader.collect();
Expand All @@ -284,20 +284,51 @@ describe('PrometheusSerializer', () => {
const pointData = metric.dataPoints as DataPoint<number>[];
assert.strictEqual(pointData.length, 1);

const result = serializer.serializeSingularDataPoint(metric.descriptor.name, metric.descriptor.type, pointData[0]);
return result;
if (exportAll) {
const result = serializer.serialize(resourceMetrics);
return result;
} else {
const result = serializer.serializeSingularDataPoint(metric.descriptor.name, metric.descriptor.type, pointData[0]);
return result;
}
}

it('should export unit block when unit of metric is given', async () => {
weyert marked this conversation as resolved.
Show resolved Hide resolved
const serializer = new PrometheusSerializer();

const unitOfMetric = 'seconds';
const result = await getCounterResult('test', unitOfMetric, serializer, true);
assert.strictEqual(
result,
'# HELP test_total description missing\n' +
`# UNIT test_total ${unitOfMetric}\n` +
'# TYPE test_total counter\n' +
`test_total 1 ${mockedHrTimeMs}\n`
);
});

it('should not export unit block when unit of metric is missing', async () => {
const serializer = new PrometheusSerializer();

const result = await getCounterResult('test', undefined, serializer, true);
assert.strictEqual(
result,
'# HELP test_total description missing\n' +
'# TYPE test_total counter\n' +
`test_total 1 ${mockedHrTimeMs}\n`
);
});

it('should rename metric of type counter when name misses _total suffix', async () => {
const serializer = new PrometheusSerializer();

const result = await getCounterResult('test', serializer);
const result = await getCounterResult('test', undefined, serializer);
assert.strictEqual(result, `test_total 1 ${mockedHrTimeMs}\n`);
});

it('should not rename metric of type counter when name contains _total suffix', async () => {
const serializer = new PrometheusSerializer();
const result = await getCounterResult('test_total', serializer);
const result = await getCounterResult('test_total', undefined, serializer);

assert.strictEqual(result, `test_total 1 ${mockedHrTimeMs}\n`);
});
Expand Down