Prometheus metrics from logs
The metrics plugin watches your log entries and builds Prometheus metrics from their payload fields. You get request-duration histograms, error counters and gauges without a second instrumentation layer, and no extra dependency: the Prometheus text format is generated by logixia.
import express from 'express';
import { createLogger, createMetricsPlugin } from 'logixia';
const metrics = createMetricsPlugin({
http_request_duration: {
type: 'histogram',
field: 'duration',
labels: ['method', 'statusCode'],
help: 'HTTP request duration in milliseconds',
},
error_count: {
type: 'counter',
levelFilter: 'error',
labels: ['context'],
help: 'Total error log entries',
},
active_connections: { type: 'gauge', field: 'connections' },
});
const logger = createLogger({ appName: 'api' });
logger.use(metrics);
const app = express();
app.get('/metrics', metrics.expressHandler());All metric names get the prefix logixia_, so error_count is exposed as logixia_error_count. Names and label names are sanitized to valid Prometheus identifiers.
Metric types
Counter
Increments once per matching entry.
| Option | Description |
|---|---|
levelFilter | Count only entries at this level. Omit to count every level. |
field, value | Count only entries where payload[field] === value. |
labels | Payload field names to use as label dimensions. 'level' is also valid. |
help | The # HELP text. |
Histogram
Observes a numeric payload field.
| Option | Description |
|---|---|
field | The payload field with the number to observe (required). |
buckets | Upper bounds, sorted automatically. Default: 1, 5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000, 10000. |
labels, help | As for counters. |
Gauge
Keeps the most recent numeric value of field. Takes labels and help.
Exposing the metrics
| Method | Use |
|---|---|
expressHandler() | An Express route handler: app.get('/metrics', metrics.expressHandler()). |
httpHandler() | A raw Node http handler that serves GET /metrics and answers 404 elsewhere. |
render() | The text exposition format as a string, for any other framework. |
reset() | Clear all collected values. |
import http from 'node:http';
import { createMetricsPlugin } from 'logixia';
const metrics = createMetricsPlugin({
checkout_total: { type: 'counter', field: 'event', value: 'checkout' },
});
http.createServer(metrics.httpHandler()).listen(9464);Label cardinality
Labels come from payload values, so a field like userId could create an unbounded number of series. Each metric tracks at most maxLabelSets distinct label combinations (default 1000). Further combinations are folded into one extra series whose label values are all __overflow__ (exported as OVERFLOW_LABEL_VALUE). Label values are cut at 256 characters.
import { createMetricsPlugin } from 'logixia';
const metrics = createMetricsPlugin(
{ requests: { type: 'counter', labels: ['route'] } },
{ maxLabelSets: 200 }
);
console.log(metrics.name);Prefer low-cardinality labels such as method, statusCode and context.
Sampling affects metrics
The plugin sees entries after sampling. A sampled logger undercounts. Keep metric-bearing entries out of sampling or count them elsewhere.
The plugin is an ordinary plugin; MetricsPlugin is also exported if you prefer new MetricsPlugin(map, options).