From fd5ff1cfe0ac522c669cd8af92cf009d500d6caf Mon Sep 17 00:00:00 2001 From: Timm Stelzer Date: Sat, 6 Jan 2024 11:31:26 +0100 Subject: [PATCH] feat: respect pruneAgedBuckets, update README --- README.md | 1 + spec/index.spec.js | 40 +++++++++++++++++++++++++++++++++++++++- src/index.js | 3 ++- types/index.d.ts | 29 ++++++++++++++++++----------- 4 files changed, 60 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index a491d0d..24226f4 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,7 @@ Additional options for **summary**: * **percentiles**: percentiles used for `http_request_duration_seconds` summary * **ageBuckets**: ageBuckets configures how many buckets we have in our sliding window for the summary * **maxAgeSeconds**: the maxAgeSeconds will tell how old a bucket can be before it is reset +* **pruneAgedBuckets**: When enabled, timed out buckets will be removed entirely. By default, buckets are reset to 0. ### Transformation callbacks ### diff --git a/spec/index.spec.js b/spec/index.spec.js index 193e4b9..429e709 100644 --- a/spec/index.spec.js +++ b/spec/index.spec.js @@ -20,6 +20,10 @@ function extractBucket (instance, key) { } } +function getMetricCount (s) { + return s.replace(/^#.*$\n|^$\n/gm, '').trim().split('\n').length; +} + describe('index', () => { beforeEach(() => { promClient.register.clear(); @@ -342,7 +346,7 @@ describe('index', () => { describe('usage of normalizePath()', () => { - it('normalizePath can be replaced gloablly', done => { + it('normalizePath can be replaced globally', done => { const app = express(); const original = bundle.normalizePath; bundle.normalizePath = () => 'dummy'; @@ -366,6 +370,40 @@ describe('index', () => { }); }); + it('respects pruneAgedBuckets', done => { + const app = express(); + const metricsApp = express(); + const bundled = bundle({ + metricsApp, + metricType: 'summary', + includePath: true, + maxAgeSeconds: 1, + percentiles: [0.5], + ageBuckets: 1, + pruneAgedBuckets: true, + }); + + app.use(bundled); + + const agent = supertest(app); + const metricsAgent = supertest(metricsApp); + agent.get('/foo') + .then(() => metricsAgent.get('/metrics')) + .then(response => { + expect(response.status).toBe(200); + // up + bucket, sum, count + expect(getMetricCount(response.text)).toBe(4); + }) + .then(() => new Promise(r => setTimeout(r, 1010))) + .then(() => metricsAgent.get('/metrics')) + .then(response => { + expect(response.status).toBe(200); + // only up + expect(getMetricCount(response.text)).toBe(1); + }) + .finally(done); + }); + it('normalizePath function can be overridden', done => { const app = express(); const instance = bundle({ diff --git a/src/index.js b/src/index.js index 187de0d..5cf81d0 100644 --- a/src/index.js +++ b/src/index.js @@ -112,7 +112,8 @@ function main(opts) { percentiles: opts.percentiles || [0.5, 0.75, 0.95, 0.98, 0.99, 0.999], maxAgeSeconds: opts.maxAgeSeconds, ageBuckets: opts.ageBuckets, - registers: [opts.promRegistry] + registers: [opts.promRegistry], + pruneAgedBuckets: opts.pruneAgedBuckets }); } else if (opts.metricType === 'histogram' || !opts.metricType) { return new promClient.Histogram({ diff --git a/types/index.d.ts b/types/index.d.ts index 1ab68bc..af88a47 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -17,7 +17,7 @@ declare namespace express_prom_bundle { type NormalizeStatusCodeFn = (res: Response) => number | string; type TransformLabelsFn = (labels: Labels, req: Request, res: Response) => void; - interface Opts { + interface BaseOptions { autoregister?: boolean; customLabels?: { [key: string]: any }; @@ -36,16 +36,6 @@ declare namespace express_prom_bundle { excludeRoutes?: Array; - metricType?: 'summary' | 'histogram'; - - // https://github.com/siimon/prom-client#histogram - buckets?: number[]; - - // https://github.com/siimon/prom-client#summary - percentiles?: number[]; - maxAgeSeconds?: number; - ageBuckets?: number; - metricsPath?: string; httpDurationMetricName?: string; promClient?: { collectDefaultMetrics?: DefaultMetricsCollectorConfiguration }; @@ -65,6 +55,23 @@ declare namespace express_prom_bundle { }; } + /** @see https://github.com/siimon/prom-client#summary */ + type SummaryOptions = BaseOptions & { + metricType?: 'summary'; + percentiles?: number[]; + maxAgeSeconds?: number; + ageBuckets?: number; + pruneAgedBuckets?: boolean; + } + + /** @see https://github.com/siimon/prom-client#histogram */ + type HistogramOptions = BaseOptions & { + metricType?: 'histogram'; + buckets?: number[]; + } + + type Opts = SummaryOptions | HistogramOptions; + interface Middleware extends RequestHandler { metricsMiddleware: RequestHandler; }