use of io.opentracing.SpanContext in project hono by eclipse.
the class AbstractVertxBasedHttpProtocolAdapter method createCommandConsumer.
/**
* Creates a consumer for command messages to be sent to a device.
*
* @param ttdSecs The number of seconds the device waits for a command.
* @param tenantObject The tenant configuration object.
* @param deviceId The identifier of the device.
* @param gatewayId The identifier of the gateway that is acting on behalf of the device
* or {@code null} otherwise.
* @param ctx The device's currently executing HTTP request.
* @param responseReady A future to complete once one of the following conditions are met:
* <ul>
* <li>the request did not include a <em>hono-ttd</em> parameter or</li>
* <li>a command has been received and the response ready future has not yet been
* completed or</li>
* <li>the ttd has expired</li>
* </ul>
* @param uploadMessageSpan The OpenTracing Span used for tracking the processing
* of the request.
* @return A future indicating the outcome of the operation.
* <p>
* The future will be completed with the created message consumer or {@code null}, if
* the response can be sent back to the device without waiting for a command.
* <p>
* The future will be failed with a {@code ServiceInvocationException} if the
* message consumer could not be created.
* @throws NullPointerException if any of the parameters other than TTD or gatewayId is {@code null}.
*/
protected final Future<CommandConsumer> createCommandConsumer(final Integer ttdSecs, final TenantObject tenantObject, final String deviceId, final String gatewayId, final RoutingContext ctx, final Handler<AsyncResult<Void>> responseReady, final Span uploadMessageSpan) {
Objects.requireNonNull(tenantObject);
Objects.requireNonNull(deviceId);
Objects.requireNonNull(ctx);
Objects.requireNonNull(responseReady);
Objects.requireNonNull(uploadMessageSpan);
if (ttdSecs == null || ttdSecs <= 0) {
// no need to wait for a command
responseReady.handle(Future.succeededFuture());
return Future.succeededFuture();
}
final AtomicBoolean requestProcessed = new AtomicBoolean(false);
uploadMessageSpan.setTag(MessageHelper.APP_PROPERTY_DEVICE_TTD, ttdSecs);
final Span waitForCommandSpan = TracingHelper.buildChildSpan(tracer, uploadMessageSpan.context(), "create consumer and wait for command", getTypeName()).withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_CLIENT).withTag(TracingHelper.TAG_TENANT_ID, tenantObject.getTenantId()).withTag(TracingHelper.TAG_DEVICE_ID, deviceId).start();
final Handler<CommandContext> commandHandler = commandContext -> {
final Span processCommandSpan = TracingHelper.buildFollowsFromSpan(tracer, waitForCommandSpan.context(), "process received command").withTag(Tags.COMPONENT.getKey(), getTypeName()).withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_CLIENT).withTag(TracingHelper.TAG_TENANT_ID, tenantObject.getTenantId()).withTag(TracingHelper.TAG_DEVICE_ID, deviceId).addReference(References.FOLLOWS_FROM, commandContext.getTracingContext()).start();
Tags.COMPONENT.set(commandContext.getTracingSpan(), getTypeName());
commandContext.logCommandToSpan(processCommandSpan);
final Command command = commandContext.getCommand();
final Sample commandSample = getMetrics().startTimer();
if (isCommandValid(command, processCommandSpan)) {
if (requestProcessed.compareAndSet(false, true)) {
waitForCommandSpan.finish();
checkMessageLimit(tenantObject, command.getPayloadSize(), processCommandSpan.context()).onComplete(result -> {
if (result.succeeded()) {
addMicrometerSample(commandContext, commandSample);
// put command context to routing context and notify
ctx.put(CommandContext.KEY_COMMAND_CONTEXT, commandContext);
} else {
commandContext.reject(result.cause());
TracingHelper.logError(processCommandSpan, "rejected command for device", result.cause());
metrics.reportCommand(command.isOneWay() ? Direction.ONE_WAY : Direction.REQUEST, tenantObject.getTenantId(), tenantObject, ProcessingOutcome.from(result.cause()), command.getPayloadSize(), commandSample);
}
cancelCommandReceptionTimer(ctx);
setTtdStatus(ctx, TtdStatus.COMMAND);
responseReady.handle(Future.succeededFuture());
processCommandSpan.finish();
});
} else {
final String errorMsg = "waiting time for command has elapsed or another command has already been processed";
log.debug("{} [tenantId: {}, deviceId: {}]", errorMsg, tenantObject.getTenantId(), deviceId);
getMetrics().reportCommand(command.isOneWay() ? Direction.ONE_WAY : Direction.REQUEST, tenantObject.getTenantId(), tenantObject, ProcessingOutcome.UNDELIVERABLE, command.getPayloadSize(), commandSample);
commandContext.release(new ServerErrorException(HttpURLConnection.HTTP_UNAVAILABLE, errorMsg));
TracingHelper.logError(processCommandSpan, errorMsg);
processCommandSpan.finish();
}
} else {
getMetrics().reportCommand(command.isOneWay() ? Direction.ONE_WAY : Direction.REQUEST, tenantObject.getTenantId(), tenantObject, ProcessingOutcome.UNPROCESSABLE, command.getPayloadSize(), commandSample);
log.debug("command message is invalid: {}", command);
commandContext.reject("malformed command message");
TracingHelper.logError(processCommandSpan, "malformed command message");
processCommandSpan.finish();
}
};
final Future<CommandConsumer> commandConsumerFuture;
if (gatewayId != null) {
// gateway scenario
commandConsumerFuture = getCommandConsumerFactory().createCommandConsumer(tenantObject.getTenantId(), deviceId, gatewayId, commandHandler, Duration.ofSeconds(ttdSecs), waitForCommandSpan.context());
} else {
commandConsumerFuture = getCommandConsumerFactory().createCommandConsumer(tenantObject.getTenantId(), deviceId, commandHandler, Duration.ofSeconds(ttdSecs), waitForCommandSpan.context());
}
return commandConsumerFuture.onFailure(thr -> {
TracingHelper.logError(waitForCommandSpan, thr);
waitForCommandSpan.finish();
}).map(consumer -> {
if (!requestProcessed.get()) {
// if the request was not responded already, add a timer for triggering an empty response
addCommandReceptionTimer(ctx, requestProcessed, responseReady, ttdSecs, waitForCommandSpan);
}
// for unregistering the command consumer (which is something the parent request span doesn't wait for)
return new CommandConsumer() {
@Override
public Future<Void> close(final SpanContext ignored) {
final Span closeConsumerSpan = TracingHelper.buildFollowsFromSpan(tracer, waitForCommandSpan.context(), "close consumer").withTag(Tags.COMPONENT.getKey(), getTypeName()).withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_CLIENT).withTag(TracingHelper.TAG_TENANT_ID, tenantObject.getTenantId()).withTag(TracingHelper.TAG_DEVICE_ID, deviceId).start();
return consumer.close(closeConsumerSpan.context()).onFailure(thr -> TracingHelper.logError(closeConsumerSpan, thr)).onComplete(ar -> closeConsumerSpan.finish());
}
};
});
}
use of io.opentracing.SpanContext in project hono by eclipse.
the class EventSenderTest method testSendWithTracing.
/**
* Verifies that {@link TraceableEventSender} uses the given SpanContext.
*
* @param ctx The test context to use for running asynchronous tests.
*/
@Test
public void testSendWithTracing(final VertxTestContext ctx) {
// GIVEN a EventSender instance
final TraceableEventSender eventSender = ((TraceableEventSender) createEventSender());
// WHEN sending a message using the API...
final SpanContext spanContext = mock(SpanContext.class);
final Future<ProtonDelivery> deliveryFuture = eventSender.send(DEVICE_ID, PAYLOAD, CONTENT_TYPE, APPLICATION_PROPERTIES, spanContext);
// ...AND WHEN the disposition is updated by the peer
updateDisposition();
deliveryFuture.onComplete(ctx.succeeding(delivery -> {
// THEN the given SpanContext is used
ctx.verify(() -> {
verify(spanBuilder).addReference(any(), eq(spanContext));
assertMessageConformsAmqpAdapterSpec(ADDRESS);
});
ctx.completeNow();
}));
}
use of io.opentracing.SpanContext in project hono by eclipse.
the class CommandResponderTest method testSendCommandResponseWithTracing.
/**
* Verifies that {@link TraceableCommandResponder} uses the given SpanContext.
*
* @param ctx The test context to use for running asynchronous tests.
*/
@Test
public void testSendCommandResponseWithTracing(final VertxTestContext ctx) {
// GIVEN a TraceableCommandResponder instance
final TraceableCommandResponder commandResponder = ((TraceableCommandResponder) createCommandResponder());
// WHEN sending a message using the API...
final SpanContext spanContext = mock(SpanContext.class);
final Future<ProtonDelivery> deliveryFuture = commandResponder.sendCommandResponse(DEVICE_ID, ADDRESS, CORRELATION_ID, STATUS, PAYLOAD, CONTENT_TYPE, APPLICATION_PROPERTIES, spanContext);
// ...AND WHEN the disposition is updated by the peer
updateDisposition();
deliveryFuture.onComplete(ctx.succeeding(delivery -> {
// THEN the given SpanContext is used
ctx.verify(() -> {
verify(spanBuilder).addReference(any(), eq(spanContext));
assertMessageConformsAmqpAdapterSpec();
});
ctx.completeNow();
}));
}
use of io.opentracing.SpanContext in project hono by eclipse.
the class TelemetrySenderTest method testSendWithTracing.
/**
* Verifies that the message created by {@link TelemetrySender#send(String, byte[], String, java.util.Map)} conforms
* to the expectations of the AMQP adapter.
*/
@Test
public void testSendWithTracing() {
// GIVEN a TraceableTelemetrySender instance
final TraceableTelemetrySender telemetrySender = ((TraceableTelemetrySender) createTelemetrySender());
// WHEN sending a message using the API
final SpanContext spanContext = mock(SpanContext.class);
telemetrySender.send(DEVICE_ID, PAYLOAD, CONTENT_TYPE, APPLICATION_PROPERTIES, spanContext);
// THEN the given SpanContext is used
verify(spanBuilder).addReference(any(), eq(spanContext));
assertMessageConformsAmqpAdapterSpec(ADDRESS);
}
use of io.opentracing.SpanContext in project hono by eclipse.
the class TableManagementStore method getDeviceCount.
/**
* Gets the number of devices that are registered for a tenant.
*
* @param tenantId The tenant to count devices for.
* @param spanContext The span to contribute to.
* @return A future tracking the outcome of the operation.
* @throws NullPointerException if tenant is {@code null}.
*/
public Future<Integer> getDeviceCount(final String tenantId, final SpanContext spanContext) {
Objects.requireNonNull(tenantId);
final Span span = TracingHelper.buildChildSpan(this.tracer, spanContext, "get device count", getClass().getSimpleName()).withTag(TracingHelper.TAG_TENANT_ID, tenantId).start();
final var expanded = this.countDevicesOfTenantStatement.expand(params -> {
params.put("tenant_id", tenantId);
});
log.debug("count - statement: {}", expanded);
return expanded.trace(this.tracer, span.context()).query(this.client).map(r -> {
final var entries = r.getRows(true);
switch(entries.size()) {
case 1:
final Integer count = entries.get(0).getInteger("DEVICECOUNT");
log.debug("found {} devices registered for tenant [tenant-id: {}]", count, tenantId);
return count;
default:
throw new IllegalStateException("Could not count devices of tenant");
}
}).onComplete(x -> span.finish());
}
Aggregations