open source / metrics4s / dashboards
Dashboards
metrics4s-grafana builds the dashboard JSON from the definitions:
scala
val dashboard = Dashboard("processor", "Line / processor", "line", ServiceFilter.regex("([a-z]+-)?processor.*"))(
Stats(Stat.podsUp, Stat.rate(ProcessorMetrics.processed), Stat.p95(ProcessorMetrics.processTime)),
Row("Processing")(
Panel.rate(ProcessorMetrics.processed).byAll,
Panel.latency(ProcessorMetrics.processTime),
Panel.gauge(ProcessorMetrics.cacheSize, Pods.Max),
),
Rows.catsEffect,
Rows.jvm,
)
dashboard.render // Either[GrafanaError, String]
dashboard.writeTo(Paths.get("metrics"))- The generator applies the guide's rules itself:
- no
__inputs, and adatasourcevariable; namespace,serviceandpodvariables read fromup, and every query filtered by all three;$__rate_intervaleverywhere, andsum by (le)in every quantile;- the unit taken from the definition;
- a fixed
uid, unique ids, and a 24-wide grid with nothing overlapping; - the group tag and links.
A custom query that breaks one of these rules fails
render.
- no
.by(_.outcome)groups by a label field and does not compile for a field that does not exist..byAllgroups by every label, and.byLabel("outcome")takes the name as a string..where(_.outcome =!= Outcome.Ok)keeps the series whose label matches. The values are rendered through the label's ownLabelValue, so a renamed case cannot silently match nothing.===,=!=,.in(a, b)and.notIn(a, b)compare one label field with values of its type, and anything else does not compile. Stats take the same filters:Stat.rate(processed).where(_.source === Source.Kafka)is the rate of one source.Panel.quantile(histogram, 0.95)draws one quantile, by any labels, wherePanel.latencydraws p50, p95 and p99.Stat.quantile(histogram, 0.95)is the stat, andStat.p95its shorthand.Panel.share(counter)(_.result === CacheResult.Hit)andStat.share(counter)(...)draw the share of the counter's rate that matches, inpercentunit..withThresholds(Color.Green, 0.01 -> Color.Orange, 0.05 -> Color.Red)colours a stat by its value: green below 1%, orange from 1%, red from 5%. The values must be finite and increasing, orrenderfails. A stat is green otherwise, andStat.podsUpis red below one pod.dashboard.missingSeries(definitions)lists the series the dashboard's own panels and stats read that none of the definitions serves, such as a panel left behind by a renamed metric. One line in a test gives every service the check. The shared rows are left out: the library checks them against its own definitions.Rows.catsEffect,Rows.zio,Rows.kyo,Rows.jvm,Rows.httpServer,Rows.httpClientandRows.mongoare the shared rows. The cats-effect and JVM rows query exactly what the hand-written merger and descriptions dashboards query.- Keep the rendered JSON in the repository and compare it in a test, so a metric changed without its dashboard fails the build.
MetricsDoc.markdown(definitions*)writes theMetric | Type | Labels | Whattable formetrics/README.md, with the label values listed.
The integration tests import a generated dashboard into Grafana 12. Every query of every panel then has to return data through Grafana's Prometheus datasource, scraped by Prometheus 3.5 from a running service.
built from README.md at v0.2.0
edit this page on GitHub