diff --git a/experimental/CHANGELOG.md b/experimental/CHANGELOG.md index 8ac2ddd66c..328cc1e9f1 100644 --- a/experimental/CHANGELOG.md +++ b/experimental/CHANGELOG.md @@ -8,6 +8,8 @@ All notable changes to experimental packages in this project will be documented ### :rocket: (Enhancement) +* feature(prometheus-serialiser): export the unit block when unit is set in metric descriptor [#3066](https://github.com/open-telemetry/opentelemetry-js/pull/3041) @weyert + ### :bug: (Bug Fix) ### :books: (Refine Doc) diff --git a/experimental/packages/opentelemetry-exporter-prometheus/src/PrometheusSerializer.ts b/experimental/packages/opentelemetry-exporter-prometheus/src/PrometheusSerializer.ts index af6c624375..3c4c127d31 100644 --- a/experimental/packages/opentelemetry-exporter-prometheus/src/PrometheusSerializer.ts +++ b/experimental/packages/opentelemetry-exporter-prometheus/src/PrometheusSerializer.ts @@ -211,6 +211,9 @@ export class PrometheusSerializer { const help = `# HELP ${name} ${escapeString( metricData.descriptor.description || 'description missing' )}`; + const unit = metricData.descriptor.unit ? `\n# UNIT ${name} ${escapeString( + metricData.descriptor.unit, + )}` : ''; const type = `# TYPE ${name} ${toPrometheusType( metricData )}`; @@ -235,7 +238,7 @@ export class PrometheusSerializer { } } - return `${help}\n${type}\n${results}`.trim(); + return `${help}${unit}\n${type}\n${results}`.trim(); } private _serializeSingularDataPoint(name: string, type: InstrumentType, dataPoint: DataPoint): string { diff --git a/experimental/packages/opentelemetry-exporter-prometheus/test/PrometheusSerializer.test.ts b/experimental/packages/opentelemetry-exporter-prometheus/test/PrometheusSerializer.test.ts index befdf62443..b0fc47ce2d 100644 --- a/experimental/packages/opentelemetry-exporter-prometheus/test/PrometheusSerializer.test.ts +++ b/experimental/packages/opentelemetry-exporter-prometheus/test/PrometheusSerializer.test.ts @@ -427,7 +427,8 @@ describe('PrometheusSerializer', () => { }); describe('validate against metric conventions', () => { - async function getCounterResult(name: string, serializer: PrometheusSerializer) { + + async function getCounterResult(name: string, serializer: PrometheusSerializer, options: Partial<{ unit: string, exportAll: boolean }> = {}) { const reader = new TestMetricReader(); const meterProvider = new MeterProvider({ views: [new View({ aggregation: new SumAggregation(), instrumentName: '*' })] @@ -435,7 +436,8 @@ describe('PrometheusSerializer', () => { meterProvider.addMetricReader(reader); const meter = meterProvider.getMeter('test'); - const counter = meter.createCounter(name); + const { unit, exportAll = false } = options; + const counter = meter.createCounter(name, { unit: unit }); counter.add(1); const { resourceMetrics, errors } = await reader.collect(); @@ -447,10 +449,41 @@ describe('PrometheusSerializer', () => { const pointData = metric.dataPoints as DataPoint[]; 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 () => { + const serializer = new PrometheusSerializer(); + + const unitOfMetric = 'seconds'; + const result = await getCounterResult('test', serializer, { unit: unitOfMetric, exportAll: 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', serializer, { exportAll: 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();