com.azure.messaging.eventhubs.implementation.instrumentation.EventHubsMetricsProvider 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.util.TelemetryAttributes;
import com.azure.core.util.logging.ClientLogger;
import com.azure.core.util.metrics.DoubleHistogram;
import com.azure.core.util.metrics.LongCounter;
import com.azure.core.util.metrics.Meter;
import com.azure.messaging.eventhubs.models.Checkpoint;
import java.time.Instant;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import static com.azure.messaging.eventhubs.implementation.instrumentation.InstrumentationUtils.ERROR_TYPE;
import static com.azure.messaging.eventhubs.implementation.instrumentation.InstrumentationUtils.MESSAGING_CLIENT_CONSUMED_MESSAGES;
import static com.azure.messaging.eventhubs.implementation.instrumentation.InstrumentationUtils.MESSAGING_CLIENT_OPERATION_DURATION;
import static com.azure.messaging.eventhubs.implementation.instrumentation.InstrumentationUtils.MESSAGING_CLIENT_PUBLISHED_MESSAGES;
import static com.azure.messaging.eventhubs.implementation.instrumentation.InstrumentationUtils.MESSAGING_DESTINATION_NAME;
import static com.azure.messaging.eventhubs.implementation.instrumentation.InstrumentationUtils.MESSAGING_CONSUMER_GROUP_NAME;
import static com.azure.messaging.eventhubs.implementation.instrumentation.InstrumentationUtils.MESSAGING_EVENTHUBS_CONSUMER_LAG;
import static com.azure.messaging.eventhubs.implementation.instrumentation.InstrumentationUtils.MESSAGING_DESTINATION_PARTITION_ID;
import static com.azure.messaging.eventhubs.implementation.instrumentation.InstrumentationUtils.MESSAGING_OPERATION_NAME;
import static com.azure.messaging.eventhubs.implementation.instrumentation.InstrumentationUtils.MESSAGING_OPERATION_TYPE;
import static com.azure.messaging.eventhubs.implementation.instrumentation.InstrumentationUtils.MESSAGING_PROCESS_DURATION;
import static com.azure.messaging.eventhubs.implementation.instrumentation.InstrumentationUtils.MESSAGING_SYSTEM;
import static com.azure.messaging.eventhubs.implementation.instrumentation.InstrumentationUtils.MESSAGING_SYSTEM_VALUE;
import static com.azure.messaging.eventhubs.implementation.instrumentation.InstrumentationUtils.SERVER_ADDRESS;
import static com.azure.messaging.eventhubs.implementation.instrumentation.InstrumentationUtils.getDurationInSeconds;
import static com.azure.messaging.eventhubs.implementation.instrumentation.InstrumentationUtils.getOperationType;
import static com.azure.messaging.eventhubs.implementation.instrumentation.OperationName.CHECKPOINT;
import static com.azure.messaging.eventhubs.implementation.instrumentation.OperationName.PROCESS;
import static com.azure.messaging.eventhubs.implementation.instrumentation.OperationName.RECEIVE;
import static com.azure.messaging.eventhubs.implementation.instrumentation.OperationName.SEND;
public final class EventHubsMetricsProvider {
private final Meter meter;
private final boolean isEnabled;
private static final ClientLogger LOGGER = new ClientLogger(EventHubsMetricsProvider.class);
private Map commonAttributes;
private AttributeCache sendAttributeCacheSuccess;
private AttributeCache receiveAttributeCacheSuccess;
private AttributeCache checkpointAttributeCacheSuccess;
private AttributeCache processAttributeCacheSuccess;
private AttributeCache getPartitionPropertiesAttributeCacheSuccess;
private AttributeCache getEventHubPropertiesAttributeCacheSuccess;
private AttributeCache lagAttributeCache;
private LongCounter publishedEventCounter;
private LongCounter consumedEventCounter;
private DoubleHistogram operationDuration;
private DoubleHistogram processDuration;
private DoubleHistogram consumerLag;
public EventHubsMetricsProvider(Meter meter, String namespace, String entityName, String consumerGroup) {
this.meter = meter;
this.isEnabled = meter != null && meter.isEnabled();
if (this.isEnabled) {
this.commonAttributes = getCommonAttributes(namespace, entityName, consumerGroup);
this.sendAttributeCacheSuccess = AttributeCache.create(meter, SEND, commonAttributes);
this.receiveAttributeCacheSuccess = AttributeCache.create(meter, RECEIVE, commonAttributes);
this.checkpointAttributeCacheSuccess = AttributeCache.create(meter, CHECKPOINT, commonAttributes);
this.processAttributeCacheSuccess = AttributeCache.create(meter, PROCESS, commonAttributes);
this.getPartitionPropertiesAttributeCacheSuccess = AttributeCache.create(meter, OperationName.GET_PARTITION_PROPERTIES, commonAttributes);
this.getEventHubPropertiesAttributeCacheSuccess = AttributeCache.create(meter, OperationName.GET_EVENT_HUB_PROPERTIES, commonAttributes);
this.lagAttributeCache = new AttributeCache(meter, MESSAGING_DESTINATION_PARTITION_ID, commonAttributes);
this.publishedEventCounter = meter.createLongCounter(MESSAGING_CLIENT_PUBLISHED_MESSAGES, "The number of published events", "{event}");
this.consumedEventCounter = meter.createLongCounter(MESSAGING_CLIENT_CONSUMED_MESSAGES, "The number of consumed events", "{event}");
this.operationDuration = meter.createDoubleHistogram(MESSAGING_CLIENT_OPERATION_DURATION, "The duration of client messaging operations involving communication with the Event Hubs namespace", "s");
this.processDuration = meter.createDoubleHistogram(MESSAGING_PROCESS_DURATION, "The duration of the processing callback", "s");
this.consumerLag = meter.createDoubleHistogram(MESSAGING_EVENTHUBS_CONSUMER_LAG, "Difference between local time when event was received and the local time it was enqueued on broker", "s");
}
}
public boolean isEnabled() {
return isEnabled;
}
public void reportBatchSend(int batchSize, String partitionId, InstrumentationScope scope) {
if (isEnabled && (publishedEventCounter.isEnabled() || operationDuration.isEnabled())) {
TelemetryAttributes attributes = getOrCreateAttributes(SEND, partitionId, scope.getErrorType());
publishedEventCounter.add(batchSize, attributes, scope.getSpan());
operationDuration.record(getDurationInSeconds(scope.getStartTime()), attributes, scope.getSpan());
}
}
public void reportProcess(int batchSize, String partitionId, InstrumentationScope scope) {
if (isEnabled && (consumedEventCounter.isEnabled() || processDuration.isEnabled())) {
TelemetryAttributes attributes = getOrCreateAttributes(PROCESS, partitionId, scope.getErrorType());
consumedEventCounter.add(batchSize, attributes, scope.getSpan());
processDuration.record(getDurationInSeconds(scope.getStartTime()), attributes, scope.getSpan());
}
}
public void reportReceive(int receivedCount, String partitionId, InstrumentationScope scope) {
if (isEnabled && (operationDuration.isEnabled() || consumedEventCounter.isEnabled())) {
String errorType = scope.getErrorType();
TelemetryAttributes attributes = getOrCreateAttributes(RECEIVE, partitionId, errorType);
if (receivedCount > 0) {
consumedEventCounter.add(receivedCount,
errorType == null ? attributes : getOrCreateAttributes(RECEIVE, partitionId, null),
scope.getSpan());
}
operationDuration.record(getDurationInSeconds(scope.getStartTime()), attributes, scope.getSpan());
}
}
public void reportLag(Instant enqueuedTime, String partitionId, InstrumentationScope scope) {
if (isEnabled && consumerLag.isEnabled()) {
consumerLag.record(getDurationInSeconds(enqueuedTime), lagAttributeCache.getOrCreate(partitionId), scope.getSpan());
}
}
public void reportCheckpoint(Checkpoint checkpoint, InstrumentationScope scope) {
if (isEnabled && operationDuration.isEnabled()) {
operationDuration.record(getDurationInSeconds(scope.getStartTime()),
getOrCreateAttributes(CHECKPOINT, checkpoint.getPartitionId(), scope.getErrorType()), scope.getSpan());
}
}
public void reportGenericOperationDuration(OperationName operationName, String partitionId, InstrumentationScope scope) {
if (isEnabled && operationDuration.isEnabled()) {
operationDuration.record(getDurationInSeconds(scope.getStartTime()),
getOrCreateAttributes(operationName, partitionId, scope.getErrorType()), scope.getSpan());
}
}
private TelemetryAttributes getOrCreateAttributes(OperationName operationName, String partitionId, String errorType) {
if (errorType == null) {
switch (operationName) {
case SEND:
return sendAttributeCacheSuccess.getOrCreate(partitionId);
case RECEIVE:
return receiveAttributeCacheSuccess.getOrCreate(partitionId);
case CHECKPOINT:
return checkpointAttributeCacheSuccess.getOrCreate(partitionId);
case PROCESS:
return processAttributeCacheSuccess.getOrCreate(partitionId);
case GET_PARTITION_PROPERTIES:
return getPartitionPropertiesAttributeCacheSuccess.getOrCreate(partitionId);
case GET_EVENT_HUB_PROPERTIES:
return getEventHubPropertiesAttributeCacheSuccess.getOrCreate(partitionId);
default:
LOGGER.atVerbose()
.addKeyValue("operationName", operationName)
.log("Unknown operation name");
// this should never happen
return lagAttributeCache.getOrCreate(partitionId);
}
}
// we can potentially cache failure attributes, but the assumption is that
// the cost of creating attributes is negligible comparing to throwing an exception
Map attributes = new HashMap<>(commonAttributes);
if (partitionId != null) {
attributes.put(MESSAGING_DESTINATION_PARTITION_ID, partitionId);
}
setOperation(attributes, operationName);
attributes.put(ERROR_TYPE, errorType);
return meter.createAttributes(attributes);
}
private Map getCommonAttributes(String namespace, String entityName, String consumerGroup) {
Map commonAttributesMap = new HashMap<>(4);
commonAttributesMap.put(MESSAGING_SYSTEM, MESSAGING_SYSTEM_VALUE);
commonAttributesMap.put(SERVER_ADDRESS, namespace);
commonAttributesMap.put(MESSAGING_DESTINATION_NAME, entityName);
if (consumerGroup != null) {
commonAttributesMap.put(MESSAGING_CONSUMER_GROUP_NAME, consumerGroup);
}
return Collections.unmodifiableMap(commonAttributesMap);
}
private static void setOperation(Map attributes, OperationName name) {
String operationType = getOperationType(name);
if (operationType != null) {
attributes.put(MESSAGING_OPERATION_TYPE, operationType);
}
attributes.put(MESSAGING_OPERATION_NAME, name.toString());
}
private static final class AttributeCache {
private final Map attr = new ConcurrentHashMap<>();
private final TelemetryAttributes commonAttr;
private final Map commonMap;
private final String dimensionName;
private final Meter meter;
static AttributeCache create(Meter meter, OperationName operationName, Map commonAttributes) {
Map attributes = new HashMap<>(commonAttributes);
setOperation(attributes, operationName);
return new AttributeCache(meter, MESSAGING_DESTINATION_PARTITION_ID, attributes);
}
private AttributeCache(Meter meter, String dimensionName, Map common) {
this.dimensionName = dimensionName;
this.commonMap = common;
this.meter = meter;
this.commonAttr = meter.createAttributes(commonMap);
}
public TelemetryAttributes getOrCreate(String value) {
if (value == null) {
return commonAttr;
}
return attr.computeIfAbsent(value, this::create);
}
private TelemetryAttributes create(String value) {
Map attributes = new HashMap<>(commonMap);
attributes.put(dimensionName, value);
return meter.createAttributes(attributes);
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy