diff --git a/CHANGELOG.md b/CHANGELOG.md index 0bb59b93..e40a9550 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ project adheres to [Semantic Versioning](http://semver.org/). ### Changed - Improve the memory usage of histograms when the `enableExemplars` option is disabled +- fix: Avoid updating exemplar values during subsequent metric changes (Fixes [#616](https://github.com/siimon/prom-client/issues/616)) ### Added diff --git a/lib/counter.js b/lib/counter.js index 22a440ec..39a7bd1b 100644 --- a/lib/counter.js +++ b/lib/counter.js @@ -85,6 +85,7 @@ class Counter extends Metric { } updateExemplar(exemplarLabels, value, hash) { + if (exemplarLabels === this.defaultExemplarLabelSet) return; if (!isObject(this.hashMap[hash].exemplar)) { this.hashMap[hash].exemplar = new Exemplar(); } diff --git a/lib/histogram.js b/lib/histogram.js index 22b65340..539620c2 100644 --- a/lib/histogram.js +++ b/lib/histogram.js @@ -84,6 +84,7 @@ class Histogram extends Metric { } updateExemplar(labels, value, exemplarLabels) { + if (Object.keys(exemplarLabels).length === 0) return; const hash = hashObject(labels, this.sortedLabelNames); const bound = findBound(this.upperBounds, value); const { bucketExemplars } = this.hashMap[hash]; diff --git a/test/exemplarsTest.js b/test/exemplarsTest.js index 68a6046e..ff72ffcb 100644 --- a/test/exemplarsTest.js +++ b/test/exemplarsTest.js @@ -172,6 +172,74 @@ describe('Exemplars', () => { jest.useRealTimers(); }); + describe('when the exemplar labels are not provided during subsequent metric updates', () => { + it('does not update the counter metric exemplar value ', async () => { + const counterInstance = new Counter({ + name: 'counter_exemplar_value_test', + help: 'help', + labelNames: ['method', 'code'], + enableExemplars: true, + }); + + counterInstance.inc({ + value: 2, + labels: { method: 'get', code: '200' }, + exemplarLabels: { + traceId: 'trace_id_test', + spanId: 'span_id_test', + }, + }); + + counterInstance.inc({ + value: 4, + labels: { method: 'get', code: '200' }, + }); + + const vals = await counterInstance.get(); + expect(vals.values[0].value).toEqual(6); + expect(vals.values[0].exemplar.value).toEqual(2); + expect(vals.values[0].exemplar.labelSet.traceId).toEqual( + 'trace_id_test', + ); + expect(vals.values[0].exemplar.labelSet.spanId).toEqual( + 'span_id_test', + ); + }); + + it('does not update the histogram metric exemplar value ', async () => { + const histogramInstance = new Histogram({ + name: 'histogram_exemplar_value_test', + help: 'test', + labelNames: ['method', 'code'], + enableExemplars: true, + }); + + histogramInstance.observe({ + value: 0.3, + labels: { method: 'get', code: '200' }, + exemplarLabels: { + traceId: 'trace_id_test_1', + spanId: 'span_id_test_1', + }, + }); + histogramInstance.observe({ + value: 0.4, + labels: { method: 'get', code: '200' }, + }); + + const vals = (await histogramInstance.get()).values; + + expect(getValuesByLabel(0.5, vals)[0].value).toEqual(2); + expect( + getValuesByLabel(0.5, vals)[0].exemplar.labelSet.traceId, + ).toEqual('trace_id_test_1'); + expect( + getValuesByLabel(0.5, vals)[0].exemplar.labelSet.spanId, + ).toEqual('span_id_test_1'); + expect(getValuesByLabel(0.5, vals)[0].exemplar.value).toEqual(0.3); + }); + }); + function getValueByLabel(label, values, key) { return values.reduce((acc, val) => { if (val.labels && val.labels[key || 'le'] === label) {