honojs-middleware/packages/prometheus/src/index.test.ts

185 lines
5.3 KiB
TypeScript

import { Hono } from 'hono'
import type { Histogram } from 'prom-client'
import { Registry } from 'prom-client'
import { prometheus } from './index'
describe('Prometheus middleware', () => {
const app = new Hono()
const registry = new Registry()
app.use(
'*',
prometheus({
registry,
}).registerMetrics
)
app.get('/', (c) => c.text('hello'))
app.get('/user/:id', (c) => c.text(c.req.param('id')))
beforeEach(() => registry.resetMetrics())
describe('configuration', () => {
it('prefix - adds the provided prefix to the metric names', async () => {
const app = new Hono()
const registry = new Registry()
app.use(
'*',
prometheus({
registry,
prefix: 'myprefix_',
}).registerMetrics
)
expect(await registry.metrics()).toMatchInlineSnapshot(`
"# HELP myprefix_http_request_duration_seconds Duration of HTTP requests in seconds
# TYPE myprefix_http_request_duration_seconds histogram
# HELP myprefix_http_requests_total Total number of HTTP requests
# TYPE myprefix_http_requests_total counter
"
`)
})
it('customLabels - adds custom labels to metrics', async () => {
const app = new Hono()
const registry = new Registry()
app.use(
'*',
prometheus({
registry,
metricOptions: {
requestsTotal: {
customLabels: {
id: (c) => c.req.query('id') ?? 'unknown',
contentType: (c) => c.res.headers.get('content-type') ?? 'unknown',
},
},
},
}).registerMetrics
)
app.get('/', (c) => c.text('hello'))
await app.request('http://localhost/?id=123')
expect(await registry.getSingleMetricAsString('http_requests_total')).toMatchInlineSnapshot(`
"# HELP http_requests_total Total number of HTTP requests
# TYPE http_requests_total counter
http_requests_total{method="GET",route="/",status="200",ok="true",id="123",contentType="text/plain;charset=UTF-8"} 1"
`)
})
})
describe('metrics', () => {
describe('http_requests_total', () => {
it('increments the http_requests_total metric with the correct labels on successful responses', async () => {
await app.request('http://localhost/')
const { values } = await registry.getSingleMetric('http_requests_total')!.get()!
expect(values).toEqual([
{
labels: {
method: 'GET',
route: '/',
status: '200',
ok: 'true',
},
value: 1,
},
])
})
it('increments the http_requests_total metric with the correct labels on errors', async () => {
await app.request('http://localhost/notfound')
const { values } = await registry.getSingleMetric('http_requests_total')!.get()!
expect(values).toEqual([
{
labels: {
method: 'GET',
route: '/*',
status: '404',
ok: 'false',
},
value: 1,
},
])
})
})
describe('http_requests_duration', () => {
it('updates the http_requests_duration metric with the correct labels on successful responses', async () => {
await app.request('http://localhost/')
const { values } = await (registry.getSingleMetric(
'http_request_duration_seconds'
) as Histogram)!.get()!
const countMetric = values.find(
(v) =>
v.metricName === 'http_request_duration_seconds_count' &&
v.labels.method === 'GET' &&
v.labels.route === '/' &&
v.labels.status === '200'
)
expect(countMetric?.value).toBe(1)
})
it('updates the http_requests_duration metric with the correct labels on errors', async () => {
await app.request('http://localhost/notfound')
const { values } = await (registry.getSingleMetric(
'http_request_duration_seconds'
) as Histogram)!.get()!
const countMetric = values.find(
(v) =>
v.metricName === 'http_request_duration_seconds_count' &&
v.labels.method === 'GET' &&
v.labels.route === '/*' &&
v.labels.status === '404'
)
expect(countMetric?.value).toBe(1)
})
})
})
describe('metrics endpoint', () => {
it('returns the metrics in the prometheus string format on the /metrics endpoint', async () => {
const app = new Hono()
const registry = new Registry()
const { printMetrics, registerMetrics } = prometheus({
registry,
metricOptions: {
requestDuration: {
disabled: true, // Disable duration metrics to make the test result more predictable
},
},
})
app.use('*', registerMetrics)
app.get('/', (c) => c.text('hello'))
app.get('/metrics', printMetrics)
await app.request('http://localhost/')
const response = await app.request('http://localhost/metrics')
expect(await response.text()).toMatchInlineSnapshot(`
"# HELP http_requests_total Total number of HTTP requests
# TYPE http_requests_total counter
http_requests_total{method="GET",route="/",status="200",ok="true"} 1
"
`)
})
})
})