com.azure.messaging.eventhubs.implementation.instrumentation.EventHubsTracer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of azure-messaging-eventhubs Show documentation
Show all versions of azure-messaging-eventhubs Show documentation
Libraries built on Microsoft Azure Event Hubs
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.azure.messaging.eventhubs.implementation.instrumentation;
import com.azure.core.amqp.exception.AmqpException;
import com.azure.core.util.Configuration;
import com.azure.core.util.Context;
import com.azure.core.util.logging.ClientLogger;
import com.azure.core.util.tracing.SpanKind;
import com.azure.core.util.tracing.StartSpanOptions;
import com.azure.core.util.tracing.Tracer;
import com.azure.core.util.tracing.TracingLink;
import com.azure.messaging.eventhubs.EventData;
import com.azure.messaging.eventhubs.models.PartitionEvent;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.time.Instant;
import java.time.ZoneOffset;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import static com.azure.core.util.tracing.Tracer.ENTITY_PATH_KEY;
import static com.azure.core.util.tracing.Tracer.HOST_NAME_KEY;
import static com.azure.core.util.tracing.Tracer.SPAN_CONTEXT_KEY;
public class EventHubsTracer {
private static final AutoCloseable NOOP_AUTOCLOSEABLE = () -> {
};
public static final String REACTOR_PARENT_TRACE_CONTEXT_KEY = "otel-context-key";
public static final String TRACEPARENT_KEY = "traceparent";
public static final String DIAGNOSTIC_ID_KEY = "Diagnostic-Id";
public static final String MESSAGE_ENQUEUED_TIME_ATTRIBUTE_NAME = "messaging.eventhubs.message.enqueued_time";
public static final String MESSAGING_BATCH_SIZE_ATTRIBUTE_NAME = "messaging.batch.message_count";
private static final String MESSAGING_SYSTEM_ATTRIBUTE_NAME = "messaging.system";
private static final String MESSAGING_OPERATION_ATTRIBUTE_NAME = "messaging.operation";
private static final TracingLink DUMMY_LINK = new TracingLink(Context.NONE);
private static final ClientLogger LOGGER = new ClientLogger(EventHubsTracer.class);
private static final boolean IS_TRACING_DISABLED = Configuration.getGlobalConfiguration().get(Configuration.PROPERTY_AZURE_TRACING_DISABLED, false);
protected final Tracer tracer;
private final String fullyQualifiedName;
private final String entityName;
public EventHubsTracer(Tracer tracer, String fullyQualifiedName, String entityName) {
this.tracer = IS_TRACING_DISABLED ? null : tracer;
this.fullyQualifiedName = Objects.requireNonNull(fullyQualifiedName, "'fullyQualifiedName' cannot be null");
this.entityName = Objects.requireNonNull(entityName, "'entityPath' cannot be null");
}
public boolean isEnabled() {
return tracer != null && tracer.isEnabled();
}
public Context startSpan(String spanName, StartSpanOptions startOptions, Context context) {
return isEnabled() ? tracer.start(spanName, startOptions, context) : context;
}
public Mono traceMono(Mono publisher, String spanName) {
if (isEnabled()) {
return publisher
.doOnEach(signal -> {
if (signal.isOnComplete() || signal.isOnError()) {
Context span = signal.getContextView().getOrDefault(REACTOR_PARENT_TRACE_CONTEXT_KEY, Context.NONE);
endSpan(signal.getThrowable(), span, null);
}
})
.contextWrite(reactor.util.context.Context.of(REACTOR_PARENT_TRACE_CONTEXT_KEY,
tracer.start(spanName, createStartOption(SpanKind.CLIENT, null), Context.NONE)));
}
return publisher;
}
public void endSpan(Throwable throwable, Context span, AutoCloseable scope) {
if (isEnabled()) {
String errorCondition = null;
if (throwable instanceof AmqpException) {
AmqpException exception = (AmqpException) throwable;
if (exception.getErrorCondition() != null) {
errorCondition = exception.getErrorCondition().getErrorCondition();
}
}
try {
if (scope != null) {
scope.close();
}
} catch (Exception e) {
LOGGER.warning("Can't close scope", e);
} finally {
tracer.end(errorCondition, throwable, span);
}
}
}
/**
* Used in ServiceBusMessageBatch.tryAddMessage() to start tracing for to-be-sent out messages.
*/
public void reportMessageSpan(EventData eventData, Context eventContext) {
if (!isEnabled() || eventContext == null || eventContext.getData(SPAN_CONTEXT_KEY).isPresent()) {
// if message has context (in case of retries), don't start a message span or add a new context
return;
}
String traceparent = EventHubsTracer.getTraceparent(eventData.getProperties());
if (traceparent != null) {
// if message has context (in case of retries) or if user supplied it, don't start a message span or add a new context
return;
}
// Starting the span makes the sampling decision (nothing is logged at this time)
StartSpanOptions startOptions = createStartOption(SpanKind.PRODUCER, null);
Context eventSpanContext = tracer.start("EventHubs.message", startOptions, eventContext);
Exception exception = null;
String error = null;
if (canModifyApplicationProperties(eventData.getProperties())) {
try {
tracer.injectContext((key, value) -> {
eventData.getProperties().put(key, value);
if (TRACEPARENT_KEY.equals(key)) {
eventData.getProperties().put(DIAGNOSTIC_ID_KEY, value);
}
}, eventSpanContext);
} catch (RuntimeException ex) {
// it might happen that unmodifiable map is something that we
// didn't account for in canModifyApplicationProperties method
// if it happens, let's not break everything, but log a warning
LOGGER.warning("Failed to inject context into EventData", ex);
exception = ex;
}
} else {
error = "failed to inject context into EventData";
}
tracer.end(error, exception, eventSpanContext);
Optional
© 2015 - 2024 Weber Informatics LLC | Privacy Policy