Skip to content

Commit 84c0094

Browse files
committed
[#3520] specific metrics for unknown messages
Signed-off-by: Bob Claerhout <[email protected]>
1 parent 4a8911a commit 84c0094

File tree

42 files changed

+46025
-42
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+46025
-42
lines changed

adapter-base/src/main/java/org/eclipse/hono/adapter/AbstractProtocolAdapterBase.java

+1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import org.eclipse.hono.client.telemetry.TelemetrySender;
4343
import org.eclipse.hono.client.util.ServiceClient;
4444
import org.eclipse.hono.service.AbstractServiceBase;
45+
import org.eclipse.hono.service.AdapterDisabledException;
4546
import org.eclipse.hono.service.auth.ValidityBasedTrustOptions;
4647
import org.eclipse.hono.service.metric.MetricsTags.ConnectionAttemptOutcome;
4748
import org.eclipse.hono.service.util.ServiceBaseUtils;

adapters/amqp/src/main/java/org/eclipse/hono/adapter/amqp/VertxBasedAmqpProtocolAdapter.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
import org.apache.qpid.proton.message.Message;
4242
import org.eclipse.hono.adapter.AbstractProtocolAdapterBase;
4343
import org.eclipse.hono.adapter.AdapterConnectionsExceededException;
44-
import org.eclipse.hono.adapter.AdapterDisabledException;
44+
import org.eclipse.hono.service.AdapterDisabledException;
4545
import org.eclipse.hono.adapter.AuthorizationException;
4646
import org.eclipse.hono.adapter.auth.device.CredentialsApiAuthProvider;
4747
import org.eclipse.hono.adapter.auth.device.DeviceCredentials;
@@ -69,6 +69,7 @@
6969
import org.eclipse.hono.notification.deviceregistry.TenantChangeNotification;
7070
import org.eclipse.hono.service.auth.DeviceUser;
7171
import org.eclipse.hono.service.http.HttpUtils;
72+
import org.eclipse.hono.service.metric.MetricsTags;
7273
import org.eclipse.hono.service.metric.MetricsTags.ConnectionAttemptOutcome;
7374
import org.eclipse.hono.service.metric.MetricsTags.Direction;
7475
import org.eclipse.hono.service.metric.MetricsTags.EndpointType;
@@ -1325,7 +1326,8 @@ private Future<Void> doUploadMessage(
13251326
ProcessingOutcome.from(t),
13261327
context.isRemotelySettled() ? QoS.AT_MOST_ONCE : QoS.AT_LEAST_ONCE,
13271328
context.getPayloadSize(),
1328-
context.getTimer());
1329+
context.getTimer(),
1330+
MetricsTags.Reason.from(t));
13291331
return Future.failedFuture(t);
13301332

13311333
}).map(ok -> {

adapters/amqp/src/test/java/org/eclipse/hono/adapter/amqp/VertxBasedAmqpProtocolAdapterTest.java

+6-3
Original file line numberDiff line numberDiff line change
@@ -388,7 +388,8 @@ public void testUploadTelemetryMessageFailsForDisabledAdapter(final VertxTestCon
388388
eq(ProcessingOutcome.UNPROCESSABLE),
389389
eq(MetricsTags.QoS.AT_LEAST_ONCE),
390390
eq(payload.length()),
391-
any());
391+
any(),
392+
eq(MetricsTags.Reason.TENANT_DISABLED_FOR_ADAPTER));
392393
});
393394
ctx.completeNow();
394395
}));
@@ -1000,7 +1001,8 @@ public void testMessageLimitExceededForATelemetryMessage(final VertxTestContext
10001001
eq(ProcessingOutcome.UNPROCESSABLE),
10011002
eq(MetricsTags.QoS.AT_LEAST_ONCE),
10021003
eq(payload.length()),
1003-
any());
1004+
any(),
1005+
eq(MetricsTags.Reason.MESSAGE_LIMIT_EXCEEDED));
10041006
});
10051007
}
10061008

@@ -1027,7 +1029,8 @@ public void testMessageLimitExceededForAnEventMessage(final VertxTestContext ctx
10271029
eq(ProcessingOutcome.UNPROCESSABLE),
10281030
eq(MetricsTags.QoS.AT_LEAST_ONCE),
10291031
eq(payload.length()),
1030-
any());
1032+
any(),
1033+
eq(MetricsTags.Reason.MESSAGE_LIMIT_EXCEEDED));
10311034
});
10321035
}
10331036

adapters/coap/src/main/java/org/eclipse/hono/adapter/coap/AbstractHonoResource.java

+4-1
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,13 @@
2929
import org.eclipse.californium.core.coap.Response;
3030
import org.eclipse.californium.core.server.resources.CoapExchange;
3131
import org.eclipse.hono.adapter.AbstractProtocolAdapterBase;
32+
import org.eclipse.hono.service.AdapterDisabledException;
3233
import org.eclipse.hono.client.ClientErrorException;
3334
import org.eclipse.hono.client.ServerErrorException;
3435
import org.eclipse.hono.client.command.Command;
3536
import org.eclipse.hono.client.command.CommandContext;
3637
import org.eclipse.hono.client.command.ProtocolAdapterCommandConsumer;
38+
import org.eclipse.hono.client.registry.TenantDisabledOrNotRegisteredException;
3739
import org.eclipse.hono.service.auth.DeviceUser;
3840
import org.eclipse.hono.service.metric.MetricsTags;
3941
import org.eclipse.hono.service.metric.MetricsTags.Direction;
@@ -413,7 +415,8 @@ protected final Future<Void> doUploadMessage(
413415
qos,
414416
payload.length(),
415417
getTtdStatus(context),
416-
context.getTimer());
418+
context.getTimer(),
419+
MetricsTags.Reason.from(t));
417420
TracingHelper.logError(currentSpan, t);
418421
commandConsumerClosedTracker.onComplete(res -> currentSpan.finish());
419422
return Future.failedFuture(t);

adapters/coap/src/test/java/org/eclipse/hono/adapter/coap/EventResourceTest.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,8 @@ public void testUploadEventFailsForRejectedOutcome(final VertxTestContext ctx) {
142142
eq(MetricsTags.QoS.AT_LEAST_ONCE),
143143
eq(payload.length()),
144144
eq(TtdStatus.NONE),
145-
any());
145+
any(),
146+
eq(MetricsTags.Reason.UNKNOWN));
146147
});
147148
ctx.completeNow();
148149
}));

adapters/coap/src/test/java/org/eclipse/hono/adapter/coap/TelemetryResourceTest.java

+8-4
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import org.eclipse.californium.core.coap.OptionSet;
3737
import org.eclipse.californium.core.coap.Response;
3838
import org.eclipse.californium.core.server.resources.CoapExchange;
39+
import org.eclipse.hono.service.AdapterDisabledException;
3940
import org.eclipse.hono.client.ClientErrorException;
4041
import org.eclipse.hono.client.ServerErrorException;
4142
import org.eclipse.hono.client.command.CommandContext;
@@ -92,7 +93,7 @@ public void testUploadTelemetryFailsForDisabledTenant(final VertxTestContext ctx
9293
final var resource = givenAResource(adapter);
9394
// which is disabled for tenant "my-tenant"
9495
when(adapter.isAdapterEnabled(any(TenantObject.class)))
95-
.thenReturn(Future.failedFuture(new ClientErrorException(HttpURLConnection.HTTP_FORBIDDEN)));
96+
.thenReturn(Future.failedFuture(new AdapterDisabledException("my-tenant")));
9697

9798
// WHEN a device that belongs to "my-tenant" publishes a telemetry message
9899
final Buffer payload = Buffer.buffer("some payload");
@@ -118,7 +119,8 @@ public void testUploadTelemetryFailsForDisabledTenant(final VertxTestContext ctx
118119
eq(MetricsTags.QoS.AT_MOST_ONCE),
119120
eq(payload.length()),
120121
eq(TtdStatus.NONE),
121-
any());
122+
any(),
123+
eq(MetricsTags.Reason.TENANT_DISABLED_FOR_ADAPTER));
122124
});
123125
ctx.completeNow();
124126
}));
@@ -371,7 +373,8 @@ public void testMessageLimitExceededForATelemetryMessage(final VertxTestContext
371373
eq(MetricsTags.QoS.AT_MOST_ONCE),
372374
eq(payload.length()),
373375
eq(TtdStatus.NONE),
374-
any());
376+
any(),
377+
eq(MetricsTags.Reason.MESSAGE_LIMIT_EXCEEDED));
375378
});
376379
ctx.completeNow();
377380
}));
@@ -560,7 +563,8 @@ public void testUploadTelemetryReleasesCommandForFailedDownstreamSender(final Ve
560563
eq(MetricsTags.QoS.AT_LEAST_ONCE),
561564
eq(payload.length()),
562565
eq(TtdStatus.COMMAND),
563-
any());
566+
any(),
567+
eq(MetricsTags.Reason.UNKNOWN));
564568
// and the command delivery is released
565569
verify(commandContext).release(any(Throwable.class));
566570
});

adapters/http-base/src/main/java/org/eclipse/hono/adapter/http/AbstractVertxBasedHttpProtocolAdapter.java

+23-6
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
import org.eclipse.hono.util.CommandConstants;
4646
import org.eclipse.hono.util.Constants;
4747
import org.eclipse.hono.util.MessageHelper;
48+
import org.eclipse.hono.util.QoS;
4849
import org.eclipse.hono.util.RegistrationAssertion;
4950
import org.eclipse.hono.util.Strings;
5051
import org.eclipse.hono.util.TenantObject;
@@ -85,7 +86,7 @@ public abstract class AbstractVertxBasedHttpProtocolAdapter<T extends HttpProtoc
8586

8687
private static final String KEY_MATCH_ALL_ROUTE_APPLIED = "matchAllRouteApplied";
8788

88-
private HttpAdapterMetrics metrics = HttpAdapterMetrics.NOOP;
89+
protected HttpAdapterMetrics metrics = HttpAdapterMetrics.NOOP;
8990
private HttpServer server;
9091
private HttpServer insecureServer;
9192

@@ -203,7 +204,15 @@ public final void doStart(final Promise<Void> startPromise) {
203204
.onComplete(startPromise);
204205
}
205206

206-
private Sample getMicrometerSample(final RoutingContext ctx) {
207+
/**
208+
* Gets the timer used to track the processing of a telemetry message.
209+
*
210+
* @param ctx The routing context to extract the sample from.
211+
* @return The sample or {@code null} if the context does not
212+
* contain a sample.
213+
* @throws NullPointerException if ctx is {@code null}.
214+
*/
215+
protected Sample getMicrometerSample(final RoutingContext ctx) {
207216
return ctx.get(KEY_MICROMETER_SAMPLE);
208217
}
209218

@@ -778,7 +787,8 @@ private void doUploadMessage(
778787
qos,
779788
payloadSize,
780789
ctx.getTtdStatus(),
781-
getMicrometerSample(ctx.getRoutingContext()));
790+
getMicrometerSample(ctx.getRoutingContext()),
791+
MetricsTags.Reason.from(t));
782792
TracingHelper.logError(currentSpan, t);
783793
currentSpan.finish();
784794
return Future.failedFuture(t);
@@ -1295,9 +1305,16 @@ public final void uploadCommandResponseMessage(
12951305
});
12961306
}
12971307

1298-
private static MetricsTags.QoS getQoSLevel(
1299-
final EndpointType endpoint,
1300-
final org.eclipse.hono.util.QoS requestedQos) {
1308+
/**
1309+
* Get the QoS based on the endpoint and the requested QoS.
1310+
*
1311+
* @param endpoint The endpoint the message was sent to.
1312+
* @param requestedQos The QoS requested by the sender.
1313+
* @return The resulting QoS.
1314+
*/
1315+
protected static MetricsTags.QoS getQoSLevel(
1316+
final EndpointType endpoint,
1317+
final QoS requestedQos) {
13011318

13021319
if (endpoint == EndpointType.EVENT) {
13031320
return MetricsTags.QoS.AT_LEAST_ONCE;

adapters/http-base/src/test/java/org/eclipse/hono/adapter/http/AbstractVertxBasedHttpProtocolAdapterTest.java

+6-3
Original file line numberDiff line numberDiff line change
@@ -703,7 +703,8 @@ public void testUploadTelemetryWithTtdClosesCommandConsumerIfSendingFails() {
703703
eq(MetricsTags.QoS.AT_MOST_ONCE),
704704
eq(payload.length()),
705705
eq(TtdStatus.NONE),
706-
any());
706+
any(),
707+
eq(MetricsTags.Reason.UNKNOWN));
707708
// and the command consumer is closed
708709
verify(commandConsumer).close(eq(false), any());
709710
}
@@ -786,7 +787,8 @@ public void testMessageLimitExceededForATelemetryMessage() {
786787
eq(MetricsTags.QoS.AT_MOST_ONCE),
787788
eq(payload.length()),
788789
eq(TtdStatus.NONE),
789-
any());
790+
any(),
791+
eq(MetricsTags.Reason.MESSAGE_LIMIT_EXCEEDED));
790792
}
791793

792794
/**
@@ -825,7 +827,8 @@ public void testMessageLimitExceededForAnEventMessage() {
825827
eq(MetricsTags.QoS.AT_LEAST_ONCE),
826828
eq(payload.length()),
827829
eq(TtdStatus.NONE),
828-
any());
830+
any(),
831+
eq(MetricsTags.Reason.MESSAGE_LIMIT_EXCEEDED));
829832
}
830833

831834
/**

adapters/lora/src/main/java/org/eclipse/hono/adapter/lora/LoraProtocolAdapter.java

+28-6
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import org.eclipse.hono.client.command.Command;
4040
import org.eclipse.hono.client.command.CommandContext;
4141
import org.eclipse.hono.client.util.StatusCodeMapper;
42+
import org.eclipse.hono.service.auth.DeviceUser;
4243
import org.eclipse.hono.service.http.HttpServerSpanHelper;
4344
import org.eclipse.hono.service.http.HttpUtils;
4445
import org.eclipse.hono.service.metric.MetricsTags;
@@ -262,13 +263,11 @@ void handleProviderRoute(final HttpContext ctx, final LoraProvider provider) {
262263
uploadTelemetryMessage(ctx, gatewayDevice.getTenantId(), deviceId, payload, contentType);
263264
registerCommandConsumerIfNeeded(provider, gatewayDevice, currentSpan.context());
264265
break;
266+
case UNKNOWN:
267+
discardMessage(ctx, currentSpan, gatewayDevice, type, deviceId, MetricsTags.Reason.UNKNOWN);
268+
break;
265269
default:
266-
LOG.debug("discarding message of unsupported type [tenant: {}, device-id: {}, type: {}]",
267-
gatewayDevice.getTenantId(), deviceId, type);
268-
currentSpan.log("discarding message of unsupported type");
269-
currentSpan.finish();
270-
// discard the message but return 202 to not cause errors on the LoRa provider side
271-
handle202(ctx.getRoutingContext());
270+
discardMessage(ctx, currentSpan, gatewayDevice, type, deviceId, MetricsTags.Reason.UNSUPPORTED_TYPE);
272271
}
273272
} catch (final LoraProviderMalformedPayloadException e) {
274273
LOG.debug("error processing request from provider [name: {}]", provider.getProviderName(), e);
@@ -278,6 +277,29 @@ void handleProviderRoute(final HttpContext ctx, final LoraProvider provider) {
278277
}
279278
}
280279

280+
private void discardMessage(final HttpContext ctx, final Span currentSpan, final DeviceUser gatewayDevice, final LoraMessageType type, final String deviceId, final MetricsTags.Reason reason) {
281+
LOG.debug("discarding message of unsupported type [tenant: {}, device-id: {}, type: {}]",
282+
gatewayDevice.getTenantId(), deviceId, type);
283+
currentSpan.log("discarding message of unsupported type");
284+
currentSpan.finish();
285+
// discard the message but return 202 to not cause errors on the LoRa provider side
286+
handle202(ctx.getRoutingContext());
287+
288+
final Future<TenantObject> tenantTracker = getTenantConfiguration(gatewayDevice.getTenantId(), currentSpan.context());
289+
final MetricsTags.EndpointType endpoint = MetricsTags.EndpointType.fromString(ctx.getRequestedResource().getEndpoint());
290+
final MetricsTags.QoS qos = getQoSLevel(endpoint, ctx.getRequestedQos());
291+
metrics.reportTelemetry(
292+
endpoint,
293+
gatewayDevice.getTenantId(),
294+
tenantTracker.result(),
295+
MetricsTags.ProcessingOutcome.UNPROCESSABLE,
296+
qos,
297+
ctx.getRoutingContext().body().buffer().length(),
298+
ctx.getTtdStatus(),
299+
getMicrometerSample(ctx.getRoutingContext()),
300+
reason);
301+
}
302+
281303
private void registerCommandConsumerIfNeeded(final LoraProvider provider, final Device gatewayDevice,
282304
final SpanContext context) {
283305
final String tenantId = gatewayDevice.getTenantId();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/**
2+
* Copyright (c) 2023 Contributors to the Eclipse Foundation
3+
*
4+
* See the NOTICE file(s) distributed with this work for additional
5+
* information regarding copyright ownership.
6+
*
7+
* This program and the accompanying materials are made available under the
8+
* terms of the Eclipse Public License 2.0 which is available at
9+
* http://www.eclipse.org/legal/epl-2.0
10+
*
11+
* SPDX-License-Identifier: EPL-2.0
12+
*/
13+
14+
15+
package org.eclipse.hono.adapter.lora;
16+
17+
import io.vertx.core.buffer.Buffer;
18+
19+
20+
/**
21+
* A Lora message that contains unknown data sent from an end-device to a Network Server.
22+
*
23+
*/
24+
public class UnknownLoraMessage implements LoraMessage {
25+
26+
/**
27+
* {@inheritDoc}
28+
*/
29+
@Override
30+
public final byte[] getDevEUI() {
31+
return new byte[0];
32+
}
33+
34+
/**
35+
* {@inheritDoc}
36+
*/
37+
@Override
38+
public final String getDevEUIAsString() {
39+
return "";
40+
}
41+
42+
/**
43+
* {@inheritDoc}
44+
*/
45+
@Override
46+
public final LoraMessageType getType() {
47+
return LoraMessageType.UNKNOWN;
48+
}
49+
50+
/**
51+
* {@inheritDoc}
52+
*/
53+
@Override
54+
public final Buffer getPayload() {
55+
return Buffer.buffer();
56+
}
57+
}

adapters/lora/src/main/java/org/eclipse/hono/adapter/lora/providers/JsonBasedLoraProvider.java

+24-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import org.eclipse.hono.adapter.lora.LoraMessage;
2121
import org.eclipse.hono.adapter.lora.LoraMessageType;
2222
import org.eclipse.hono.adapter.lora.LoraMetaData;
23+
import org.eclipse.hono.adapter.lora.UnknownLoraMessage;
2324
import org.eclipse.hono.adapter.lora.UplinkLoraMessage;
2425
import org.eclipse.hono.util.CommandEndpoint;
2526
import org.eclipse.hono.util.Strings;
@@ -51,7 +52,7 @@ public LoraMessage getMessage(final RoutingContext ctx) {
5152
case UPLINK:
5253
return createUplinkMessage(ctx.request(), message);
5354
default:
54-
throw new LoraProviderMalformedPayloadException(String.format("unsupported message type [%s]", type));
55+
return createUnknownMessage(ctx.request(), message);
5556
}
5657
} catch (final RuntimeException e) {
5758
// catch generic exception in order to also cover any (runtime) exceptions
@@ -184,4 +185,26 @@ protected UplinkLoraMessage createUplinkMessage(final HttpServerRequest request,
184185
message.setAdditionalData(getAdditionalData(requestBody));
185186
return message;
186187
}
188+
189+
/**
190+
* Creates an object representation of a Lora unknown message.
191+
* <p>
192+
* This method uses the {@link #getDevEui(JsonObject)}
193+
* method to extract relevant information from the request body to add
194+
* to the returned message.
195+
*
196+
* @param request The request sent by the provider's Network Server.
197+
* @param requestBody The JSON object contained in the request's body.
198+
* @return The message.
199+
* @throws RuntimeException if the message cannot be parsed.
200+
*/
201+
protected UnknownLoraMessage createUnknownMessage(final HttpServerRequest request, final JsonObject requestBody) {
202+
203+
Objects.requireNonNull(requestBody);
204+
205+
final UnknownLoraMessage message = new UnknownLoraMessage();
206+
return message;
207+
}
208+
209+
187210
}

0 commit comments

Comments
 (0)