Metrics
import 'package:gisila/gisila.dart';MetricsConfig
Section titled “MetricsConfig”Turns on HTTP metrics and the Prometheus endpoint when set as AppConfig.metrics.
class MetricsConfig { const MetricsConfig({ String path = '/metrics', String? bearerToken, bool Function(Request request)? allow, SendPort? hub, List<double> buckets = defaultLatencyBuckets, });}When to use: whenever Prometheus (or a compatible agent) scrapes your service.
| Field | Default | Description |
|---|---|---|
path |
'/metrics' |
Endpoint path. Requests to it are not counted. |
bearerToken |
null |
Allow requests sending Authorization: Bearer <token>. Must be at least 16 characters, otherwise ArgumentError. |
allow |
null |
Custom access check; takes precedence over bearerToken. |
hub |
null |
A MetricsHub.sendPort when several isolates serve one port. |
buckets |
defaultLatencyBuckets |
Buckets of the request-duration histogram, in seconds. |
Access: with allow, whoever it accepts; otherwise with bearerToken, requests that send it; otherwise only direct loopback connections without X-Forwarded-For, Forwarded or X-Real-IP (so a reverse proxy on the same host doesn’t expose it). Everyone else gets a 404.
Recorded metrics:
| Metric | Type | Labels |
|---|---|---|
http_server_requests_total |
counter | method, route, status |
http_server_request_duration_seconds |
histogram | method, route |
http_server_requests_in_flight |
gauge | |
process_resident_memory_bytes |
gauge | |
process_start_time_seconds |
gauge |
route is the route template (/users/<id>), never the raw path. Unmatched requests are unmatched; handlers you mounted by hand without a routeId are other. Methods outside the standard seven are OTHER.
AppConfig(metrics: MetricsConfig(bearerToken: Platform.environment['METRICS_TOKEN']))metricsMiddleware
Section titled “metricsMiddleware”Middleware metricsMiddleware(MetricsConfig config, MetricsRegistry registry, {MetricsHubMember? hub})The middleware behind AppConfig.metrics. Use it directly only in a custom Shelf pipeline.
MetricsRegistry
Section titled “MetricsRegistry”Holds an app’s metrics. GisilaApp.metrics is one.
class MetricsRegistry { MetricsRegistry(); Counter counter(String name, {required String help, List<String> labels = const []}); Gauge gauge(String name, {required String help, List<String> labels = const []}); Histogram histogram(String name, {required String help, List<String> labels = const [], List<double> buckets = defaultLatencyBuckets}); Map<String, Object?> snapshot(); String render();}When to use: register business metrics on app.metrics so they are served next to the HTTP metrics. Calling counter/gauge/histogram again with the same name returns the same metric (a different type throws StateError). Invalid metric or label names throw ArgumentError.
Label values must come from a small fixed set (a template name, a status), never from user input: every combination is kept in memory forever.
| Method | Description |
|---|---|
snapshot() |
Plain-data copy of every metric, sendable to another isolate. |
render() |
Prometheus text format, plus process metrics. |
final sent = app.metrics.counter('emails_sent_total', help: 'Emails sent.', labels: ['template']);sent.inc({'template': 'welcome'});
final queueDepth = app.metrics.gauge('jobs_waiting', help: 'Jobs waiting.');queueDepth.set(17);
final render = app.metrics.histogram('pdf_render_seconds', help: 'PDF render time.');final pdf = await render.time(() => renderPdf(order));Counter
Section titled “Counter”class Counter { void inc([Map<String, String> labels = const {}, num by = 1]); double value([Map<String, String> labels = const {}]);}A value that only goes up. inc with a negative by throws ArgumentError. labels must name exactly the declared labels.
class Gauge { void set(num value, [Map<String, String> labels = const {}]); void inc([Map<String, String> labels = const {}, num by = 1]); void dec([Map<String, String> labels = const {}, num by = 1]); double value([Map<String, String> labels = const {}]);}A value that goes up and down (queue depth, open connections).
Histogram
Section titled “Histogram”class Histogram { final List<double> buckets; void observe(num value, [Map<String, String> labels = const {}]); Future<T> time<T>(Future<T> Function() body, [Map<String, String> labels = const {}]);}Distribution of observed values (latencies, sizes). time runs body and observes its duration in seconds, even when it throws.
defaultLatencyBuckets
Section titled “defaultLatencyBuckets”const List<double> defaultLatencyBuckets = [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10];Default histogram buckets, in seconds.
MetricsHub
Section titled “MetricsHub”Merges metrics from every isolate serving the same port, so a scrape on any isolate reports the whole process.
class MetricsHub { MetricsHub({Duration collectTimeout = const Duration(seconds: 2)}); static const int maxMisses = 3; final Duration collectTimeout; SendPort get sendPort; int get memberCount; void close();}When to use: when you start the app in several isolates with start(shared: true). Create the hub in the main isolate and pass sendPort to each isolate’s MetricsConfig(hub: ...). An isolate that misses maxMisses collections in a row is dropped.
Future<void> main() async { final hub = MetricsHub(); for (var i = 1; i < Platform.numberOfProcessors; i++) { await Isolate.spawn(serve, hub.sendPort); } await serve(hub.sendPort);}
Future<void> serve(SendPort hub) async { final app = GisilaApp(config: AppConfig(metrics: MetricsConfig(hub: hub))); // register controllers ... await app.start(port: 8080, shared: true);}MetricsHubMember
Section titled “MetricsHubMember”class MetricsHubMember { MetricsHubMember(SendPort hub, MetricsRegistry registry); final SendPort hub; final MetricsRegistry registry; Future<Map<String, Object?>> scrape({Duration timeout = const Duration(seconds: 5)}); void close();}One isolate’s link to a hub. GisilaApp creates it for you when MetricsConfig.hub is set. scrape returns every isolate’s metrics merged, or this isolate’s alone if the hub doesn’t answer in time.
mergeSnapshots
Section titled “mergeSnapshots”Map<String, Object?> mergeSnapshots(Iterable<Map<String, Object?>> snaps)Sums snapshots from several isolates: metrics with the same name and labels are added. A metric whose type or buckets differ from the first one seen is dropped.
renderSnapshot
Section titled “renderSnapshot”String renderSnapshot(Map<String, Object?> snapshot)Renders a snapshot (one isolate’s or a merged one) in the Prometheus text format, followed by the process metrics.
See also: Deploy your app.