Changelog
Notable changes per release. Breaking changes and how to migrate are in COMPATIBILITY.md; what is not covered yet is in ROADMAP.md.
0.2.0
The first public release. It carries 0.1.0, which was only tested inside Sendline.now, and what wiring the library
into the Sendline backend asked for, with its dashboard generated and imported into Grafana.
Added
-
Measured: an operation timed and counted by outcome in one call.Measured(histogram, counter, failed)(labels)givesmeasure(labels)(fa)(outcome). The outcome is read from the value, and an error counts asfailedand is re-raised as the same instance. A cancellation records nothing. When recording fails as well, the operation's error is still the one raised, with the recording failure suppressed in it. -
registerAs[P]registers a named tuple of definitions into a case class. The names must be its fields in their order, and the instruments must fit their types, or the call does not compile. Two instruments of the same type can no longer swap places unnoticed, as they could in a tuple. -
ExporterException.PrometheusExporter.allocatednames the port it cannot listen on, in the message and asport, with the bind failure as the cause. -
The integrations' definitions without an instance.
HttpServerMetrics.requests,.duration,.inFlightand.definitions, the same onHttpClientMetrics, andMongoMetrics.commands,.failures,.connections,.checkOut,.checkOutFailuresand.definitions. A dashboard draws from them without the route or collection list. An instance still defines the same series, with its values pre-created. -
metrics4s-grafana:- typed label filters:
.where(_.outcome =!= Outcome.Ok), with===,.in(a, b)and.notIn(a, b), rendered through the label's ownLabelValue. A value of another type, or anything but one label field compared with values, does not compile. Panels and stats both take it; - one quantile:
Panel.quantile(histogram, 0.95)andStat.quantile(histogram, 0.95); - shares:
Panel.share(counter)(condition)andStat.share(counter)(condition), the matching part of the rate over all of it, inpercentunit; - stat thresholds:
.withThresholds(Color.Green, 0.01 -> Color.Orange, 0.05 -> Color.Red), refused byrenderunless the values are finite and increasing; - the drift check:
dashboard.missingSeries(definitions)lists the series the dashboard's own panels and stats read that no definition serves.dashboard.readsis every series it reads.
- typed label filters:
-
Info metrics.
Info("name", "help").labelled[L]is a constant seriesname_info = 1whose labels carry the facts: the build's version, the stage, a configuration.registergives anInfoOf[F, L], whoseset(labels)replaces the previous labels, so one copy shows exactly one series.- The name is checked at compile time like the others, and a unit suffix is refused: an info carries no unit.
MetricsBackend.infois a new SPI member with a default that refuses, so an existing backend still compiles. The Prometheus backend implements it with the client'sInfo.metrics4s-grafana:Stat.info(definition)(_.label)andStat.infoBy(definition, "label")show a label's value as the stat's text (the version, not a count).MetricsDoclists the series asname_info, typeinfo.
-
Named-tuple label selectors in dashboards.
Panel.rate(x).by(_.route)now compiles when the labels are a named tuple,(route: Route, status: StatusClass), and not only a case class. -
RECIPES.md:
/metricson an internal http4s server, a ZIO service, labels known only at startup, errors as a label, a dashboard kept in the repository and stats with thresholds. The integration tests compile every block and run the recipes that do something.
Fixed
metrics4s-tapircounted a request twice when its body failed after the headers. Tapir calls both the response callback and the exception callback for such a request. It was counted once as its status and once asfailed, andhttp_server_in_flightwent below zero. A request now finishes once, whichever callback comes first.- The README's recording example did not compile. It set a labelled gauge and observed a labelled histogram without labels. Every Scala block in the README is now copied from an example the integration tests compile.
Changed
- Every module is compiled to Java 17 class files against the JDK 17 API, so it runs on JDK 17.
metrics4s-rapidneeds 21 andmetrics4s-kyoneeds 25, like rapid and Kyo themselves. Only the build needs JDK 25. MetricKindhas a new case,Info. A match on it in user code that was exhaustive now warns.GrafanaErrorhas new cases,NotOneLabel,UnknownLabelValue,InvalidQuantileandInvalidThresholds, so an exhaustive match on it now warns. The message ofUnknownLabelno longer says "to group by", because a filter raises it too.PrometheusExporter.allocatedfails withExporterExceptionwhere it raised the bareIOException.metrics4s-mongois built against the MongoDB driver5.14.0, from5.13.0. Drivers5.6.0to5.13.0nest one more callback for every partial read of a reply, so a large reply read in small pieces overflows the IO thread's stack and never completes (JAVA-6313). A service that takes the driver frommetrics4s-mongonow gets the fixed one. The listeners play no part in the bug.Statis a sealed class. The metric stats areMetricStat[L], typed by the labels.wherefilters on, andStat.podsUpandStat.customare aCustomStat.Stat.infoByrefuses a label the info does not carry. A stat'sthresholdsare aThresholdsofColors, no longer a list of strings..gitattributeskeeps*.jsonwith LF endings, so the byte-for-byte dashboard tests pass on a Windows checkout.
0.1.0
Not released publicly: only tested inside Sendline.now.
Added
metrics4s-core, with no dependencies:- metric definitions (
Counter,Gauge,Histogram) whose names, units and help are checked at compile time; - typed labels:
derives LabelValuefor enums,derives Labelsfor case classes, named tuples,Labels.single, bounded values; - series pre-created for enumerated domains, with a limit;
- instruments with an effect-free
unsafeside,registerAllwith rollback; timedandtimedBy;- gauges with
replace; - callbacks read at scrape time:
observe,observeAll, andobserveAllFthroughDispatch; - the effect bridge (
Effect,Dispatch,Allocated) and the backend SPI.
- metric definitions (
metrics4s-prometheus: the backend on the Prometheus Java client 1.10 with classic histograms only, the HTTP exporter, the client's JVM metrics, andmetrics4s_callback_errors_total.- Runtimes:
metrics4s-cats,-zio,-kyoand-rapid, each withEffect,Dispatchand the native resource adapter. The cats-effect, ZIO and Kyo runtime metrics export the same families as commons, names and types unchanged. metrics4s-testkit: a registry per test, typed reads, the raw series map, andmissingSeries(dashboard).metrics4s-grafana: Grafana dashboards generated from the definitions, validated against the metrics guide, with shared rows for cats-effect, ZIO, Kyo, the JVM, HTTP servers, HTTP clients and MongoDB.MetricsDocwrites the README tables.- Integrations:
metrics4s-http, the shared HTTP definitions;metrics4s-http4s, server and client middleware;metrics4s-tapir, a metric for tapir'sMetricsRequestInterceptor;metrics4s-sttp, a listener backend for sttp client 4;metrics4s-mongo, command and connection-pool listeners for the MongoDB driver, whichmongo4stakes through its settings.
Found on the way
- The Kyo
guaranteeCaseofmongo4s5.1.0reports a cancellation asErrored(KyoException("fiber abandoned"))on Kyo 1.0.0-RC7. The instance here records how the body ended and reportsCanceled;mongo4s5.1.1does the same. - commons' ZIO
Dispatchallocates its runner inside an uninterruptible region, so a timed-out callback leaves its fiber running. The runner here runsfa.interruptible.