Skip to content

Commit 59da606

Browse files
author
Kai Hudalla
committed
[#1149] Use all tags when reporting metrics to PrometheusMeterRegistry.
The protocol adapters now always provide values for all tags when reporting telemetry messages. Fixes #1149 Signed-off-by: Kai Hudalla <[email protected]>
1 parent 5d76378 commit 59da606

File tree

5 files changed

+99
-64
lines changed

5 files changed

+99
-64
lines changed

service-base/src/main/java/org/eclipse/hono/service/metric/MetricsTags.java

+2-38
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ public enum TtdStatus {
196196
/**
197197
* Status indicating that the message from the device did not contain a TTD value.
198198
*/
199-
NONE(),
199+
NONE("none"),
200200
/**
201201
* Status indicating that the TTD expired without any pending commands for the device.
202202
*/
@@ -226,24 +226,6 @@ public enum TtdStatus {
226226
public Tag asTag() {
227227
return tag;
228228
}
229-
230-
/**
231-
* Adds a tag for the TTD status to a given set of tags.
232-
* <p>
233-
* The tag is only added if the status is not {@link #NONE}.
234-
*
235-
* @param tags The tags to add to.
236-
* @return The tags.
237-
* @throws NullPointerException if tags is {@code null}.
238-
*/
239-
public Tags add(final Tags tags) {
240-
Objects.requireNonNull(tags);
241-
if (tag == null) {
242-
return tags;
243-
} else {
244-
return tags.and(tag);
245-
}
246-
}
247229
}
248230

249231
/**
@@ -254,7 +236,7 @@ public enum QoS {
254236
/**
255237
* QoS indicating unknown delivery semantics.
256238
*/
257-
UNKNOWN(),
239+
UNKNOWN("unknown"),
258240
/**
259241
* QoS (level 0) indicating at-most-once delivery semantics.
260242
*/
@@ -301,24 +283,6 @@ public static QoS from(final int level) {
301283
public Tag asTag() {
302284
return tag;
303285
}
304-
305-
/**
306-
* Adds a tag for the QoS level to a given set of tags.
307-
* <p>
308-
* The tag is only added if the status is not {@link #UNKNOWN}.
309-
*
310-
* @param tags The tags to add to.
311-
* @return The tags.
312-
* @throws NullPointerException if tags is {@code null}.
313-
*/
314-
public Tags add(final Tags tags) {
315-
Objects.requireNonNull(tags);
316-
if (tag == null) {
317-
return tags;
318-
} else {
319-
return tags.and(tag);
320-
}
321-
}
322286
}
323287

324288
/**

service-base/src/main/java/org/eclipse/hono/service/metric/MicrometerBasedMetrics.java

+9-9
Original file line numberDiff line numberDiff line change
@@ -172,17 +172,19 @@ public final void reportTelemetry(
172172
throw new IllegalArgumentException("payload size must not be negative");
173173
}
174174

175-
final Tags baseTags = Tags.of(type.asTag())
175+
final Tags tags = Tags.of(type.asTag())
176176
.and(MetricsTags.getTenantTag(tenantId))
177-
.and(outcome.asTag());
177+
.and(outcome.asTag())
178+
.and(qos.asTag())
179+
.and(ttdStatus.asTag());
178180

179-
timer.stop(this.registry.timer(METER_MESSAGES_RECEIVED, ttdStatus.add(qos.add(baseTags))));
181+
timer.stop(this.registry.timer(METER_MESSAGES_RECEIVED, tags));
180182

181183
// record payload size
182184
DistributionSummary.builder(METER_MESSAGES_PAYLOAD)
183185
.baseUnit("bytes")
184186
.minimumExpectedValue(0L)
185-
.tags(baseTags)
187+
.tags(tags)
186188
.register(this.registry)
187189
.record(payloadSize);
188190

@@ -233,17 +235,17 @@ public void reportCommand(
233235
throw new IllegalArgumentException("payload size must not be negative");
234236
}
235237

236-
final Tags baseTags = Tags.of(direction.asTag())
238+
final Tags tags = Tags.of(direction.asTag())
237239
.and(MetricsTags.getTenantTag(tenantId))
238240
.and(outcome.asTag());
239241

240-
timer.stop(this.registry.timer(METER_COMMANDS_RECEIVED, baseTags));
242+
timer.stop(this.registry.timer(METER_COMMANDS_RECEIVED, tags));
241243

242244
// record payload size
243245
DistributionSummary.builder(METER_COMMANDS_PAYLOAD)
244246
.baseUnit("bytes")
245247
.minimumExpectedValue(0L)
246-
.tags(baseTags)
248+
.tags(tags)
247249
.register(this.registry)
248250
.record(payloadSize);
249251

@@ -284,7 +286,6 @@ protected <K, V extends Number> V gaugeForKey(final String name, final Map<K, V>
284286
return this.registry.gauge(name, tags, instanceSupplier.get());
285287

286288
});
287-
288289
}
289290

290291
/**
@@ -304,6 +305,5 @@ protected <V extends Number> V gaugeForTenant(final String name, final Map<Strin
304305
final Supplier<V> instanceSupplier) {
305306

306307
return gaugeForKey(name, map, tenant, Tags.of(MetricsTags.getTenantTag(tenant)), instanceSupplier);
307-
308308
}
309309
}

service-base/src/test/java/org/eclipse/hono/service/metric/MicrometerBasedMetricsTest.java

+81-12
Original file line numberDiff line numberDiff line change
@@ -15,44 +15,116 @@
1515
package org.eclipse.hono.service.metric;
1616

1717
import static org.junit.Assert.assertNotNull;
18-
import static org.junit.Assert.assertNull;
19-
import static org.mockito.Mockito.*;
18+
import static org.mockito.ArgumentMatchers.eq;
19+
import static org.mockito.Mockito.mock;
20+
import static org.mockito.Mockito.verify;
21+
22+
import java.util.Arrays;
23+
import java.util.Collection;
2024

2125
import org.eclipse.hono.service.metric.MetricsTags.EndpointType;
2226
import org.eclipse.hono.service.metric.MetricsTags.QoS;
2327
import org.eclipse.hono.service.metric.MetricsTags.TtdStatus;
2428
import org.junit.Before;
2529
import org.junit.Test;
30+
import org.junit.runner.RunWith;
31+
import org.junit.runners.Parameterized;
32+
import org.junit.runners.Parameterized.Parameter;
33+
import org.junit.runners.Parameterized.Parameters;
2634

35+
import io.micrometer.core.instrument.Clock;
2736
import io.micrometer.core.instrument.MeterRegistry;
2837
import io.micrometer.core.instrument.Tags;
29-
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
38+
import io.micrometer.core.instrument.Timer.Sample;
39+
import io.micrometer.graphite.GraphiteConfig;
40+
import io.micrometer.graphite.GraphiteMeterRegistry;
41+
import io.micrometer.prometheus.PrometheusConfig;
42+
import io.micrometer.prometheus.PrometheusMeterRegistry;
3043

3144

3245
/**
3346
* Verifies behavior of {@link MicrometerBasedMetrics}.
3447
*
3548
*/
49+
@RunWith(Parameterized.class)
3650
public class MicrometerBasedMetricsTest {
3751

38-
private MeterRegistry registry;
52+
/**
53+
* The Micrometer registry to run the tests against.
54+
*/
55+
@Parameter
56+
public MeterRegistry registry;
3957
private MicrometerBasedMetrics metrics;
4058

59+
/**
60+
* Gets the Micrometer registries that the tests should be run against.
61+
*
62+
* @return The registries.
63+
*/
64+
@Parameters(name = "{0}")
65+
public static Collection<MeterRegistry> registries() {
66+
return Arrays.asList(new MeterRegistry[] {
67+
new PrometheusMeterRegistry(PrometheusConfig.DEFAULT),
68+
new GraphiteMeterRegistry(GraphiteConfig.DEFAULT, Clock.SYSTEM)
69+
});
70+
}
71+
4172
/**
4273
* Sets up the fixture.
4374
*/
4475
@Before
4576
public void setUp() {
46-
registry = new SimpleMeterRegistry();
4777
metrics = new MicrometerBasedMetrics(registry);
4878
}
4979

80+
/**
81+
* Verifies that arbitrary telemetry messages with or without a QoS
82+
* can be reported successfully.
83+
*/
84+
@Test
85+
public void testReportTelemetryWithOptionalQos() {
86+
87+
// GIVEN a sample
88+
final Sample sample = metrics.startTimer();
89+
90+
// WHEN reporting a telemetry message with a QoS of AT_LEAST_ONCE
91+
// and no TTD
92+
metrics.reportTelemetry(
93+
MetricsTags.EndpointType.TELEMETRY,
94+
"tenant",
95+
MetricsTags.ProcessingOutcome.FORWARDED,
96+
MetricsTags.QoS.AT_LEAST_ONCE,
97+
1024,
98+
MetricsTags.TtdStatus.NONE,
99+
sample);
100+
101+
// THEN the meter can be found in the registry with the tags that have a known value
102+
final Tags expectedTags = Tags.of(MetricsTags.EndpointType.TELEMETRY.asTag())
103+
.and(MetricsTags.getTenantTag("tenant"))
104+
.and(MetricsTags.ProcessingOutcome.FORWARDED.asTag())
105+
.and(MetricsTags.QoS.AT_LEAST_ONCE.asTag());
106+
assertNotNull(registry.find(MicrometerBasedMetrics.METER_MESSAGES_RECEIVED).tags(expectedTags).timer());
107+
108+
// and reporting another telemetry message with no QoS but with a TTD status succeeds
109+
110+
final Sample otherSample = metrics.startTimer();
111+
112+
metrics.reportTelemetry(
113+
MetricsTags.EndpointType.TELEMETRY,
114+
"tenant",
115+
MetricsTags.ProcessingOutcome.FORWARDED,
116+
MetricsTags.QoS.UNKNOWN,
117+
1024,
118+
MetricsTags.TtdStatus.EXPIRED,
119+
otherSample);
120+
}
121+
50122
/**
51123
* Verifies that when reporting a downstream message no tags for
52124
* {@link QoS#UNKNOWN} nor {@link TtdStatus#NONE} are included.
53125
*/
54126
@Test
55-
public void testReportTelemetryOmitsOptionalTags() {
127+
public void testReportTelemetryWithUnknownTagValues() {
56128

57129
metrics.reportTelemetry(
58130
MetricsTags.EndpointType.TELEMETRY,
@@ -65,14 +137,11 @@ public void testReportTelemetryOmitsOptionalTags() {
65137

66138
final Tags expectedTags = Tags.of(MetricsTags.EndpointType.TELEMETRY.asTag())
67139
.and(MetricsTags.getTenantTag("tenant"))
68-
.and(MetricsTags.ProcessingOutcome.FORWARDED.asTag());
140+
.and(MetricsTags.ProcessingOutcome.FORWARDED.asTag())
141+
.and(MetricsTags.QoS.UNKNOWN.asTag())
142+
.and(MetricsTags.TtdStatus.NONE.asTag());
69143

70144
assertNotNull(registry.find(MicrometerBasedMetrics.METER_MESSAGES_RECEIVED).tags(expectedTags).timer());
71-
72-
assertNull(registry.find(MicrometerBasedMetrics.METER_MESSAGES_RECEIVED)
73-
.tags(expectedTags).tagKeys(MetricsTags.QoS.TAG_NAME).timer());
74-
assertNull(registry.find(MicrometerBasedMetrics.METER_MESSAGES_RECEIVED)
75-
.tags(expectedTags).tagKeys(MetricsTags.TtdStatus.TAG_NAME).timer());
76145
}
77146

78147
/**

site/content/api/Metrics.md

+3-5
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,10 @@ Additional tags for protocol adapters are:
5858
| Name | Value | Description |
5959
| ----------- | -------------------------------------------------- | ----------- |
6060
| *direction* | `one-way`, `request`, `response` | The direction in which a Command &amp; Control message is being sent:<br>`one-way` indicates a command sent to a device for which the sending application doesn't expect to receive a response.<br>`request` indicates a command request message sent to a device.<br>`response` indicates a command response received from a device. |
61-
| *qos* | `0`, `1` | The quality of service used for a telemetry or event message.<br>`0` indicates *at most once*,<br>`1` indicates *at least once* delivery semantics.<br>This tag will be omitted if the quality of service cannot be determined. |
61+
| *qos* | `0`, `1`, `unknown` | The quality of service used for a telemetry or event message.<br>`0` indicates *at most once*,<br>`1` indicates *at least once* and<br> `none` indicates unknown delivery semantics. |
6262
| *status* | `forwarded`, `unprocessable`, `undeliverable` | The processing status of a message.<br>`forwarded` indicates that the message has been forwarded to a downstream consumer<br>`unprocessable` indicates that the message has not been processed not forwarded, e.g. because the message was malformed<br>`undeliverable` indicates that the message could not be forwarded, e.g. because there is no downstream consumer or due to an infrastructure problem |
6363
| *tenant* | *string* | The identifier of the tenant that the metric is being reported for |
64-
| *ttd* | `command`, `expired` | A status indicating the outcome of processing a TTD value contained in a message received from a device.<br>`command` indicates that a command for the device has been included in the response to the device's request for uploading the message.<br>`expired` indicates that a response without a command has been sent to the device<br>Note that this tag is only used by protocol adapters which use a request/response based transport protocol like HTTP. The tag will be omitted if the device did not specify a TTD value in its message. |
64+
| *ttd* | `command`, `expired`, `none` | A status indicating the outcome of processing a TTD value contained in a message received from a device.<br>`command` indicates that a command for the device has been included in the response to the device's request for uploading the message.<br>`expired` indicates that a response without a command has been sent to the device.<br>`none` indicates that either no TTD value has been specified by the device or that the protocol adapter does not support it. |
6565
| *type* | `telemetry`, `event` | The type of (downstream) message that the metric is being reported for. |
6666

6767
Metrics provided by the protocol adapters are:
@@ -72,11 +72,9 @@ Metrics provided by the protocol adapters are:
7272
| *hono.commands.payload* | DistributionSummary | *host*, *component-type*, *component-name*, *tenant*, *type*, *status*, *direction* | The number of bytes conveyed in the payload of a command message. |
7373
| *hono.connections.authenticated* | Gauge | *host*, *component-type*, *component-name*, *tenant* | Current number of connected, authenticated devices. <br/> **NB** This metric is only supported by protocol adapters that maintain *connection state* with authenticated devices. In particular, the HTTP adapter does not support this metric. |
7474
| *hono.connections.unauthenticated* | Gauge | *host*, *component-type*, *component-name* | Current number of connected, unauthenticated devices. <br/> **NB** This metric is only supported by protocol adapters that maintain *connection state* with authenticated devices. In particular, the HTTP adapter does not support this metric. |
75-
| *hono.messages.received* | Timer | *host*, *component-type*, *component-name*, *tenant*, *type*, *status*, \[*qos*,\] \[*ttd*\] | The time it took to process a message conveying telemetry data or an event. |
75+
| *hono.messages.received* | Timer | *host*, *component-type*, *component-name*, *tenant*, *type*, *status*, *qos*, *ttd* | The time it took to process a message conveying telemetry data or an event. |
7676
| *hono.messages.payload* | DistributionSummary | *host*, *component-type*, *component-name*, *tenant*, *type*, *status* | The number of bytes conveyed in the payload of a telemetry or event message. |
7777

78-
A tag name in square brackets indicates that the tag may not be used with each reported value.
79-
8078
### Service Metrics
8179

8280
Hono's service components do not report any metrics at the moment.

site/content/release-notes.md

+4
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ title = "Release Notes"
1818
processing of requests at a high level.
1919
* The `hono-client` and `hono-core` artifacts use Java 8 level again so that they
2020
can be used in applications using Java 8.
21+
* The protocol adapters now always specify values for the *ttd* and *qos* tags when
22+
reporting telemetry messages using meter name *hono.messages.received*. This fixes
23+
an issue when using the Prometheus back end where the HTTP adapter failed to report
24+
messages that contained a TTD value and others that didn't.
2125

2226
### API Changes
2327

0 commit comments

Comments
 (0)