Skip to content
This repository was archived by the owner on Mar 16, 2025. It is now read-only.

Commit d912746

Browse files
authored
Fix prometheus label ordering (#268)
Ordering of the dimensions was non consistent when server dimensions were set
1 parent e5fde01 commit d912746

File tree

3 files changed

+69
-60
lines changed

3 files changed

+69
-60
lines changed

core/src/main/scala/fr/davit/akka/http/metrics/core/HttpMetricsRegistry.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,11 @@ abstract class HttpMetricsRegistry(settings: HttpMetricsSettings) extends HttpMe
4545
builder.result()
4646
}
4747

48-
protected def requestDimensions(request: HttpRequest): Seq[Dimension] = {
48+
private def requestDimensions(request: HttpRequest): Seq[Dimension] = {
4949
requestLabelers.map(_.dimension(request))
5050
}
5151

52-
protected def responseDimensions(response: HttpResponse): Seq[Dimension] = {
52+
private def responseDimensions(response: HttpResponse): Seq[Dimension] = {
5353
responseLabelers.map(_.dimension(response))
5454
}
5555

prometheus/src/main/scala/fr/davit/akka/http/metrics/prometheus/PrometheusRegistry.scala

Lines changed: 16 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616

1717
package fr.davit.akka.http.metrics.prometheus
1818

19-
import akka.http.scaladsl.model.{HttpRequest, HttpResponse}
2019
import fr.davit.akka.http.metrics.core._
2120
import fr.davit.akka.http.metrics.prometheus.Quantiles.Quantile
2221
import io.prometheus.client.CollectorRegistry
@@ -58,43 +57,36 @@ class PrometheusRegistry(settings: PrometheusSettings, val underlying: Collector
5857
private val pathDimension = if (settings.includePathDimension) Some(PathLabeler.name) else None
5958
private val statusDimension = if (settings.includeStatusDimension) Some(StatusGroupLabeler.name) else None
6059

61-
private val serverDimensions = settings.serverDimensions.map(_.name)
62-
private val customRequestDimensions = settings.customDimensions.collect { case l: HttpRequestLabeler => l.name }
63-
private val customResponseDimensions = settings.customDimensions.collect { case l: HttpResponseLabeler => l.name }
64-
65-
private val requestsDimensions =
66-
(serverDimensions ++ methodDimension ++ customRequestDimensions).sorted
67-
private val responsesDimensions =
68-
(requestsDimensions ++ pathDimension ++ statusDimension ++ customResponseDimensions).sorted
60+
private[prometheus] val serverDimensions = settings.serverDimensions.map(_.name)
6961

70-
override protected def requestDimensions(request: HttpRequest): Seq[Dimension] =
71-
super.requestDimensions(request).sorted
62+
private val customRequestDimensions = settings.customDimensions.collect { case l: HttpRequestLabeler => l.name }
63+
private[prometheus] val requestsDimensions = (methodDimension ++ customRequestDimensions).toSeq
7264

73-
override protected def responseDimensions(response: HttpResponse): Seq[Dimension] =
74-
super.responseDimensions(response).sorted
65+
private val customResponseDimensions = settings.customDimensions.collect { case l: HttpResponseLabeler => l.name }
66+
private[prometheus] val responsesDimensions = (statusDimension ++ pathDimension ++ customResponseDimensions).toSeq
7567

7668
lazy val requests: Counter = io.prometheus.client.Counter
7769
.build()
7870
.namespace(settings.namespace)
7971
.name(settings.metricsNames.requests)
8072
.help("Total HTTP requests")
81-
.labelNames(requestsDimensions: _*)
73+
.labelNames(serverDimensions ++ requestsDimensions: _*)
8274
.register(underlying)
8375

8476
lazy val requestsActive: Gauge = io.prometheus.client.Gauge
8577
.build()
8678
.namespace(settings.namespace)
8779
.name(settings.metricsNames.requestsActive)
8880
.help("Active HTTP requests")
89-
.labelNames(requestsDimensions: _*)
81+
.labelNames(serverDimensions ++ requestsDimensions: _*)
9082
.register(underlying)
9183

9284
lazy val requestsFailures: Counter = io.prometheus.client.Counter
9385
.build()
9486
.namespace(settings.namespace)
9587
.name(settings.metricsNames.requestsFailures)
9688
.help("Total unserved requests")
97-
.labelNames(requestsDimensions: _*)
89+
.labelNames(serverDimensions ++ requestsDimensions: _*)
9890
.register(underlying)
9991

10092
lazy val requestsSize: Histogram = {
@@ -106,7 +98,7 @@ class PrometheusRegistry(settings: PrometheusSettings, val underlying: Collector
10698
.namespace(settings.namespace)
10799
.name(settings.metricsNames.requestsSize)
108100
.help(help)
109-
.labelNames(requestsDimensions: _*)
101+
.labelNames(serverDimensions ++ requestsDimensions: _*)
110102
.quantiles(qs: _*)
111103
.maxAgeSeconds(maxAge.toSeconds)
112104
.ageBuckets(ageBuckets)
@@ -118,7 +110,7 @@ class PrometheusRegistry(settings: PrometheusSettings, val underlying: Collector
118110
.namespace(settings.namespace)
119111
.name(settings.metricsNames.requestsSize)
120112
.help(help)
121-
.labelNames(requestsDimensions: _*)
113+
.labelNames(serverDimensions ++ requestsDimensions: _*)
122114
.buckets(bs: _*)
123115
.register(underlying)
124116
}
@@ -129,15 +121,15 @@ class PrometheusRegistry(settings: PrometheusSettings, val underlying: Collector
129121
.namespace(settings.namespace)
130122
.name(settings.metricsNames.responses)
131123
.help("HTTP responses")
132-
.labelNames(responsesDimensions: _*)
124+
.labelNames(serverDimensions ++ requestsDimensions ++ responsesDimensions: _*)
133125
.register(underlying)
134126

135127
lazy val responsesErrors: Counter = io.prometheus.client.Counter
136128
.build()
137129
.namespace(settings.namespace)
138130
.name(settings.metricsNames.responsesErrors)
139131
.help("Total HTTP errors")
140-
.labelNames(responsesDimensions: _*)
132+
.labelNames(serverDimensions ++ requestsDimensions ++ responsesDimensions: _*)
141133
.register(underlying)
142134

143135
lazy val responsesDuration: Timer = {
@@ -150,7 +142,7 @@ class PrometheusRegistry(settings: PrometheusSettings, val underlying: Collector
150142
.namespace(settings.namespace)
151143
.name(settings.metricsNames.responsesDuration)
152144
.help(help)
153-
.labelNames(responsesDimensions: _*)
145+
.labelNames(serverDimensions ++ requestsDimensions ++ responsesDimensions: _*)
154146
.quantiles(qs: _*)
155147
.maxAgeSeconds(maxAge.toSeconds)
156148
.ageBuckets(ageBuckets)
@@ -161,7 +153,7 @@ class PrometheusRegistry(settings: PrometheusSettings, val underlying: Collector
161153
.namespace(settings.namespace)
162154
.name(settings.metricsNames.responsesDuration)
163155
.help(help)
164-
.labelNames(responsesDimensions: _*)
156+
.labelNames(serverDimensions ++ requestsDimensions ++ responsesDimensions: _*)
165157
.buckets(bs: _*)
166158
.register(underlying)
167159
}
@@ -177,7 +169,7 @@ class PrometheusRegistry(settings: PrometheusSettings, val underlying: Collector
177169
.namespace(settings.namespace)
178170
.name(settings.metricsNames.responsesSize)
179171
.help(help)
180-
.labelNames(responsesDimensions: _*)
172+
.labelNames(serverDimensions ++ requestsDimensions ++ responsesDimensions: _*)
181173
.quantiles(qs: _*)
182174
.maxAgeSeconds(maxAge.toSeconds)
183175
.ageBuckets(ageBuckets)
@@ -189,7 +181,7 @@ class PrometheusRegistry(settings: PrometheusSettings, val underlying: Collector
189181
.namespace(settings.namespace)
190182
.name(settings.metricsNames.responsesSize)
191183
.help(help)
192-
.labelNames(responsesDimensions: _*)
184+
.labelNames(serverDimensions ++ requestsDimensions ++ responsesDimensions: _*)
193185
.buckets(bs: _*)
194186
.register(underlying)
195187
}

prometheus/src/test/scala/fr/davit/akka/http/metrics/prometheus/PrometheusRegistrySpec.scala

Lines changed: 51 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,6 @@ import scala.concurrent.duration._
2626

2727
class PrometheusRegistrySpec extends AnyFlatSpec with Matchers {
2828

29-
import PrometheusRegistry._
30-
3129
object CustomRequestLabeler extends HttpRequestLabeler {
3230
override def name = "custom_request_dim"
3331
def label = "custom_request_label"
@@ -40,20 +38,18 @@ class PrometheusRegistrySpec extends AnyFlatSpec with Matchers {
4038
override def label(response: HttpResponse): String = label
4139
}
4240

43-
val serverDimensions = List(Dimension("server_dim", "server_label"))
44-
val customRequestDimensions = List(Dimension(CustomRequestLabeler.name, CustomRequestLabeler.label))
45-
val customResponseDimensions = List(Dimension(CustomResponseLabeler.name, CustomResponseLabeler.label))
46-
47-
val requestsDimensions = (
48-
serverDimensions ++
49-
customRequestDimensions ++
50-
List(Dimension(MethodLabeler.name, "GET"))
51-
).sorted
52-
val responsesDimensions = (
53-
requestsDimensions ++
54-
customResponseDimensions ++
55-
List(Dimension(PathLabeler.name, "/api"), Dimension(StatusGroupLabeler.name, "2xx"))
56-
).sorted
41+
val serverDimensions = List(
42+
Dimension("server_dim", "server_label")
43+
)
44+
val requestsDimensions = List(
45+
Dimension(MethodLabeler.name, "GET"),
46+
Dimension(CustomRequestLabeler.name, CustomRequestLabeler.label)
47+
)
48+
val responsesDimensions = List(
49+
Dimension(StatusGroupLabeler.name, "2xx"),
50+
Dimension(PathLabeler.name, "/api"),
51+
Dimension(CustomResponseLabeler.name, CustomResponseLabeler.label)
52+
)
5753

5854
trait Fixture {
5955

@@ -105,6 +101,18 @@ class PrometheusRegistrySpec extends AnyFlatSpec with Matchers {
105101
)
106102
}
107103

104+
it should "not have any dimensions by default" in new Fixture {
105+
registry.serverDimensions shouldBe empty
106+
registry.requestsDimensions shouldBe empty
107+
registry.responsesDimensions shouldBe empty
108+
}
109+
110+
it should "add proper dimensions when configured" in new DimensionFixture {
111+
registry.serverDimensions should contain theSameElementsInOrderAs serverDimensions.map(_.name)
112+
registry.requestsDimensions should contain theSameElementsInOrderAs requestsDimensions.map(_.name)
113+
registry.responsesDimensions should contain theSameElementsInOrderAs responsesDimensions.map(_.name)
114+
}
115+
108116
it should "set requestsActive metrics in the underlying registry" in new Fixture {
109117
registry.requestsActive.inc()
110118
underlyingCounterValue("akka_http_requests_active") shouldBe 1L
@@ -116,8 +124,9 @@ class PrometheusRegistrySpec extends AnyFlatSpec with Matchers {
116124
}
117125

118126
it should "set requestsActive metrics in the underlying registry with dimensions" in new DimensionFixture {
119-
registry.requestsActive.inc(requestsDimensions)
120-
underlyingCounterValue("akka_http_requests_active", requestsDimensions) shouldBe 1L
127+
val dim = serverDimensions ++ requestsDimensions
128+
registry.requestsActive.inc(dim)
129+
underlyingCounterValue("akka_http_requests_active", dim) shouldBe 1L
121130
}
122131

123132
it should "set requests metrics in the underlying registry" in new Fixture {
@@ -131,8 +140,9 @@ class PrometheusRegistrySpec extends AnyFlatSpec with Matchers {
131140
}
132141

133142
it should "set requests metrics in the underlying registry with dimensions" in new DimensionFixture {
134-
registry.requests.inc(requestsDimensions)
135-
underlyingCounterValue("akka_http_requests_total", requestsDimensions) shouldBe 1L
143+
val dims = serverDimensions ++ requestsDimensions
144+
registry.requests.inc(dims)
145+
underlyingCounterValue("akka_http_requests_total", dims) shouldBe 1L
136146
}
137147

138148
it should "set requestsSize metrics in the underlying registry" in new Fixture {
@@ -146,8 +156,9 @@ class PrometheusRegistrySpec extends AnyFlatSpec with Matchers {
146156
}
147157

148158
it should "set requestsSize metrics in the underlying registry with dimensions" in new DimensionFixture {
149-
registry.requestsSize.update(3, requestsDimensions)
150-
underlyingHistogramValue("akka_http_requests_size_bytes", requestsDimensions) shouldBe 3L
159+
val dims = serverDimensions ++ requestsDimensions
160+
registry.requestsSize.update(3, dims)
161+
underlyingHistogramValue("akka_http_requests_size_bytes", dims) shouldBe 3L
151162
}
152163

153164
it should "set responses metrics in the underlying registry" in new Fixture {
@@ -161,8 +172,9 @@ class PrometheusRegistrySpec extends AnyFlatSpec with Matchers {
161172
}
162173

163174
it should "set responses metrics in the underlying registry with dimensions" in new DimensionFixture {
164-
registry.responses.inc(responsesDimensions)
165-
underlyingCounterValue("akka_http_responses_total", responsesDimensions) shouldBe 1L
175+
val dims = serverDimensions ++ requestsDimensions ++ responsesDimensions
176+
registry.responses.inc(dims)
177+
underlyingCounterValue("akka_http_responses_total", dims) shouldBe 1L
166178
}
167179

168180
it should "set responsesErrors metrics in the underlying registry" in new Fixture {
@@ -176,8 +188,9 @@ class PrometheusRegistrySpec extends AnyFlatSpec with Matchers {
176188
}
177189

178190
it should "set responsesErrors metrics in the underlying registry with dimensions" in new DimensionFixture {
179-
registry.responsesErrors.inc(responsesDimensions)
180-
underlyingCounterValue("akka_http_responses_errors_total", responsesDimensions) shouldBe 1L
191+
val dims = serverDimensions ++ requestsDimensions ++ responsesDimensions
192+
registry.responsesErrors.inc(dims)
193+
underlyingCounterValue("akka_http_responses_errors_total", dims) shouldBe 1L
181194
}
182195

183196
it should "set responsesDuration metrics in the underlying registry" in new Fixture {
@@ -191,8 +204,9 @@ class PrometheusRegistrySpec extends AnyFlatSpec with Matchers {
191204
}
192205

193206
it should "set responsesDuration metrics in the underlying registry with dimension" in new DimensionFixture {
194-
registry.responsesDuration.observe(3.seconds, responsesDimensions)
195-
underlyingHistogramValue("akka_http_responses_duration_seconds", responsesDimensions) shouldBe 3.0
207+
val dims = serverDimensions ++ requestsDimensions ++ responsesDimensions
208+
registry.responsesDuration.observe(3.seconds, dims)
209+
underlyingHistogramValue("akka_http_responses_duration_seconds", dims) shouldBe 3.0
196210
}
197211

198212
it should "set responsesSize metrics in the underlying registry" in new Fixture {
@@ -206,8 +220,9 @@ class PrometheusRegistrySpec extends AnyFlatSpec with Matchers {
206220
}
207221

208222
it should "set responsesSize metrics in the underlying registry with dimensions" in new DimensionFixture {
209-
registry.responsesSize.update(3, responsesDimensions)
210-
underlyingHistogramValue("akka_http_responses_size_bytes", responsesDimensions) shouldBe 3L
223+
val dims = serverDimensions ++ requestsDimensions ++ responsesDimensions
224+
registry.responsesSize.update(3, dims)
225+
underlyingHistogramValue("akka_http_responses_size_bytes", dims) shouldBe 3L
211226
}
212227

213228
it should "set connectionsActive metrics in the underlying registry" in new Fixture {
@@ -221,8 +236,9 @@ class PrometheusRegistrySpec extends AnyFlatSpec with Matchers {
221236
}
222237

223238
it should "set connectionsActive metrics in the underlying registry with dimensions" in new DimensionFixture {
224-
registry.connectionsActive.inc(serverDimensions)
225-
underlyingCounterValue("akka_http_connections_active", serverDimensions) shouldBe 1L
239+
val dims = serverDimensions
240+
registry.connectionsActive.inc(dims)
241+
underlyingCounterValue("akka_http_connections_active", dims) shouldBe 1L
226242
}
227243

228244
it should "set connections metrics in the underlying registry" in new Fixture {
@@ -236,7 +252,8 @@ class PrometheusRegistrySpec extends AnyFlatSpec with Matchers {
236252
}
237253

238254
it should "set connections metrics in the underlying registry with dimensions" in new DimensionFixture {
239-
registry.connections.inc(serverDimensions)
240-
underlyingCounterValue("akka_http_connections_total", serverDimensions) shouldBe 1L
255+
val dims = serverDimensions
256+
registry.connections.inc(dims)
257+
underlyingCounterValue("akka_http_connections_total", dims) shouldBe 1L
241258
}
242259
}

0 commit comments

Comments
 (0)