@gati-framework/observability
Version:
Observability stack for Gati framework - Prometheus, Grafana, Loki, and Tracing
93 lines (92 loc) • 3.09 kB
JavaScript
import { Registry, Counter, Histogram, Gauge, collectDefaultMetrics } from 'prom-client';
export class PrometheusMetrics {
registry;
httpRequestDuration;
httpRequestTotal;
activeConnections;
errorTotal;
constructor() {
this.registry = new Registry();
collectDefaultMetrics({ register: this.registry });
this.httpRequestDuration = new Histogram({
name: 'gati_http_request_duration_seconds',
help: 'Duration of HTTP requests in seconds',
labelNames: ['method', 'route', 'status_code'],
buckets: [0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1, 5],
registers: [this.registry],
});
this.httpRequestTotal = new Counter({
name: 'gati_http_requests_total',
help: 'Total number of HTTP requests',
labelNames: ['method', 'route', 'status_code'],
registers: [this.registry],
});
this.activeConnections = new Gauge({
name: 'gati_active_connections',
help: 'Number of active connections',
registers: [this.registry],
});
this.errorTotal = new Counter({
name: 'gati_errors_total',
help: 'Total number of errors',
labelNames: ['type', 'route'],
registers: [this.registry],
});
}
recordRequest(method, route, statusCode, duration) {
this.httpRequestDuration.observe({ method, route, status_code: statusCode.toString() }, duration);
this.httpRequestTotal.inc({ method, route, status_code: statusCode.toString() });
}
incrementConnections() {
this.activeConnections.inc();
}
decrementConnections() {
this.activeConnections.dec();
}
recordError(type, route) {
this.errorTotal.inc({ type, route });
}
createCounter(name, help, labelNames = []) {
return new Counter({
name: `gati_${name}`,
help,
labelNames,
registers: [this.registry],
});
}
createGauge(name, help, labelNames = []) {
return new Gauge({
name: `gati_${name}`,
help,
labelNames,
registers: [this.registry],
});
}
createHistogram(name, help, labelNames = [], buckets) {
return new Histogram({
name: `gati_${name}`,
help,
labelNames,
buckets,
registers: [this.registry],
});
}
async getMetrics() {
return this.registry.metrics();
}
getRegistry() {
return this.registry;
}
}
export function createPrometheusMiddleware(metrics) {
return (req, res, next) => {
metrics.incrementConnections();
const start = Date.now();
res.on('finish', () => {
const duration = (Date.now() - start) / 1000;
metrics.recordRequest(req.method, req.route?.path || req.path || 'unknown', res.statusCode, duration);
metrics.decrementConnections();
});
next();
};
}