Skip to content

Commit b44005b

Browse files
committed
feat: Add labels support to BooleanMetric.
1 parent 40cb759 commit b44005b

File tree

3 files changed

+88
-9
lines changed

3 files changed

+88
-9
lines changed

jicoco-metrics/src/main/kotlin/org/jitsi/metrics/BooleanMetric.kt

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,29 +30,65 @@ class BooleanMetric @JvmOverloads constructor(
3030
/** the namespace (prefix) of this metric */
3131
namespace: String,
3232
/** an optional initial value for this metric */
33-
internal val initialValue: Boolean = false
33+
internal val initialValue: Boolean = false,
34+
/** Label names for this metric. If non-empty, the initial value must be false and all get/update calls MUST
35+
* specify values for the labels. Calls to simply get() or set() will fail with an exception. */
36+
val labelNames: List<String> = emptyList()
3437
) : Metric<Boolean>() {
35-
private val gauge =
36-
Gauge.build(name, help).namespace(namespace).create().apply { set(if (initialValue) 1.0 else 0.0) }
38+
private val gauge = run {
39+
val builder = Gauge.build(name, help).namespace(namespace)
40+
if (labelNames.isNotEmpty()) {
41+
builder.labelNames(*labelNames.toTypedArray())
42+
if (initialValue) {
43+
throw IllegalArgumentException("Cannot set an initial value for a labeled gauge")
44+
}
45+
}
46+
builder.create().apply {
47+
if (initialValue) {
48+
set(1.0)
49+
}
50+
}
51+
}
3752

53+
override val supportsJson: Boolean = labelNames.isEmpty()
3854
override fun get() = gauge.get() != 0.0
55+
fun get(labels: List<String>) = gauge.labels(*labels.toTypedArray()).get() != 0.0
3956

40-
override fun reset() = set(initialValue)
57+
override fun reset() = synchronized(gauge) {
58+
gauge.clear()
59+
if (initialValue) {
60+
gauge.set(1.0)
61+
}
62+
}
4163

4264
override fun register(registry: CollectorRegistry) = this.also { registry.register(gauge) }
4365

4466
/**
4567
* Atomically sets the gauge to the given value.
4668
*/
47-
fun set(newValue: Boolean): Unit = synchronized(gauge) { gauge.set(if (newValue) 1.0 else 0.0) }
69+
fun set(newValue: Boolean, labels: List<String> = emptyList()): Unit = synchronized(gauge) {
70+
if (labels.isEmpty()) {
71+
gauge.set(if (newValue) 1.0 else 0.0)
72+
} else {
73+
gauge.labels(*labels.toTypedArray()).set(if (newValue) 1.0 else 0.0)
74+
}
75+
}
4876

4977
/**
5078
* Atomically sets the gauge to the given value, returning the updated value.
5179
*
5280
* @return the updated value
5381
*/
54-
fun setAndGet(newValue: Boolean): Boolean = synchronized(gauge) {
55-
gauge.set(if (newValue) 1.0 else 0.0)
82+
fun setAndGet(newValue: Boolean, labels: List<String> = emptyList()): Boolean = synchronized(gauge) {
83+
set(newValue, labels)
5684
return newValue
5785
}
86+
87+
/** Remove the child with the given labels (the metric with those labels will stop being emitted) */
88+
fun remove(labels: List<String> = emptyList()) = synchronized(gauge) {
89+
if (labels.isNotEmpty()) {
90+
gauge.remove(*labels.toTypedArray())
91+
}
92+
}
93+
internal fun collect() = gauge.collect()
5894
}

jicoco-metrics/src/main/kotlin/org/jitsi/metrics/MetricsContainer.kt

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,15 +119,20 @@ open class MetricsContainer @JvmOverloads constructor(
119119
/** the description of the metric */
120120
help: String,
121121
/** the optional initial value of the metric */
122-
initialValue: Boolean = false
122+
initialValue: Boolean = false,
123+
/** Label names for this metric. If non-empty, the initial value must be 0 and all get/update calls MUST
124+
* specify values for the labels. Calls to simply get() or set() will fail with an exception. */
125+
labelNames: List<String> = emptyList()
123126
): BooleanMetric {
124127
if (metrics.containsKey(name)) {
125128
if (checkForNameConflicts) {
126129
throw RuntimeException("Could not register metric '$name'. A metric with that name already exists.")
127130
}
128131
return metrics[name] as BooleanMetric
129132
}
130-
return BooleanMetric(name, help, namespace, initialValue).apply { metrics[name] = register(registry) }
133+
return BooleanMetric(name, help, namespace, initialValue, labelNames).apply {
134+
metrics[name] = register(registry)
135+
}
131136
}
132137

133138
/**

jicoco-metrics/src/test/kotlin/org/jitsi/metrics/MetricTest.kt

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,44 @@ class MetricTest : ShouldSpec() {
6161
should("return true") { get() shouldBe true }
6262
}
6363
}
64+
context("With labels") {
65+
with(BooleanMetric("testBoolean", "Help", namespace, labelNames = listOf("l1", "l2"))) {
66+
val labels = listOf("A", "A")
67+
val labels2 = listOf("A", "B")
68+
val labels3 = listOf("B", "B")
69+
70+
get(labels) shouldBe false
71+
get(labels2) shouldBe false
72+
get(labels3) shouldBe false
73+
74+
set(true, labels)
75+
get(labels) shouldBe true
76+
get(labels2) shouldBe false
77+
get(labels3) shouldBe false
78+
79+
set(true, labels2)
80+
get(labels) shouldBe true
81+
get(labels2) shouldBe true
82+
get(labels3) shouldBe false
83+
84+
setAndGet(true, labels3) shouldBe true
85+
86+
set(false, labels)
87+
get(labels) shouldBe false
88+
get(labels2) shouldBe true
89+
get(labels3) shouldBe true
90+
91+
collect()[0].samples.size shouldBe 3
92+
remove(labels2)
93+
// Down to two sets of labels
94+
collect()[0].samples.size shouldBe 2
95+
get(labels) shouldBe false
96+
get(labels2) shouldBe false
97+
get(labels3) shouldBe true
98+
// Even a get() will summon a child
99+
collect()[0].samples.size shouldBe 3
100+
}
101+
}
64102
}
65103
context("Creating a DoubleGaugeMetric") {
66104
context("with the default initial value") {

0 commit comments

Comments
 (0)