From 4aa78d336ba7cbdd4fbcc344b788d9eac202beed Mon Sep 17 00:00:00 2001 From: "Harsimar Kaur (from Dev Box)" Date: Mon, 9 Dec 2024 16:38:47 -0800 Subject: [PATCH 01/20] adding projection functionality, rethink automic double --- .../quickpulse/QuickPulseDataCollector.java | 46 ++++--- .../quickpulse/QuickPulseDataFetcher.java | 2 + .../filtering/CustomDimensions.java | 14 +++ .../filtering/DependencyDataColumns.java | 4 + .../filtering/DerivedMetricProjections.java | 113 ++++++++++++++++++ .../filtering/ExceptionDataColumns.java | 4 + .../filtering/FilteringConfiguration.java | 20 ++++ .../filtering/RequestDataColumns.java | 4 + .../filtering/TelemetryColumns.java | 2 + .../filtering/TraceDataColumns.java | 4 + 10 files changed, 194 insertions(+), 19 deletions(-) create mode 100644 sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/DerivedMetricProjections.java diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/QuickPulseDataCollector.java b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/QuickPulseDataCollector.java index 43ef28dc1c7a3..d6cb12ef35d9d 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/QuickPulseDataCollector.java +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/QuickPulseDataCollector.java @@ -12,18 +12,9 @@ import com.azure.monitor.opentelemetry.autoconfigure.implementation.models.TelemetryExceptionDetails; import com.azure.monitor.opentelemetry.autoconfigure.implementation.models.MessageData; import com.azure.monitor.opentelemetry.autoconfigure.implementation.models.ContextTagKeys; -import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.filtering.DependencyDataColumns; -import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.filtering.FilteringConfiguration; -import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.filtering.TelemetryColumns; -import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.filtering.ExceptionDataColumns; -import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.filtering.RequestDataColumns; -import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.RemoteDependency; -import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.Request; -import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.KeyValuePairString; -import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.DocumentIngress; +import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.filtering.*; +import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.*; import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.Exception; -import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.FilterConjunctionGroupInfo; -import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.DerivedMetricInfo; import com.azure.monitor.opentelemetry.autoconfigure.implementation.utils.CpuPerformanceCounterCalculator; import reactor.util.annotation.Nullable; @@ -36,6 +27,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.HashMap; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; @@ -75,7 +67,7 @@ synchronized void disable() { synchronized void enable(Supplier instrumentationKeySupplier) { this.instrumentationKeySupplier = instrumentationKeySupplier; - counters.set(new Counters()); + counters.set(new Counters(configuration.get().getValidProjectionInitInfo())); } synchronized void setQuickPulseStatus(QuickPulseStatus quickPulseStatus) { @@ -89,7 +81,7 @@ synchronized QuickPulseStatus getQuickPulseStatus() { @Nullable synchronized FinalCounters getAndRestart() { - Counters currentCounters = counters.getAndSet(new Counters()); + Counters currentCounters = counters.getAndSet(new Counters(configuration.get().getValidProjectionInitInfo())); if (currentCounters != null) { return new FinalCounters(currentCounters); } @@ -168,9 +160,7 @@ private boolean matchesDocumentFilters(TelemetryColumns columns, String telemetr } private void applyMetricFilters(TelemetryColumns columns, String telemetryType, - FilteringConfiguration currentConfig) { - // TODO (harskaur): In a future PR, use Filter class to check if columns match any filter - // TODO (harskaur): If columns matches a filter, then create/increment a derived metric + FilteringConfiguration currentConfig, Counters currentCounters) { // TODO (harskaur): when this PR is merged, remove logging (it is for manual testing & making sure the build does not complain about useless methods) List metricsConfig = currentConfig.fetchMetricConfigForTelemetryType(telemetryType); try { @@ -180,6 +170,13 @@ private void applyMetricFilters(TelemetryColumns columns, String telemetryType, } catch (IOException e) { logger.error(e.getMessage()); } + for (DerivedMetricInfo derivedMetricInfo : metricsConfig) { + if (Filter.checkMetricFilters(derivedMetricInfo, columns)) { + synchronized (currentCounters.derivedMetrics) { + currentCounters.derivedMetrics.calculateProjection(derivedMetricInfo, columns); + } + } + } } private void addDependency(RemoteDependencyData telemetry, int itemCount, FilteringConfiguration currentConfig) { @@ -195,7 +192,7 @@ private void addDependency(RemoteDependencyData telemetry, int itemCount, Filter } DependencyDataColumns columns = new DependencyDataColumns(telemetry); - applyMetricFilters(columns, "Dependency", currentConfig); + applyMetricFilters(columns, "Dependency", currentConfig, counters); if (matchesDocumentFilters(columns, "Dependency", currentConfig)) { RemoteDependency dependencyDoc = new RemoteDependency(); @@ -223,7 +220,7 @@ private void addException(TelemetryExceptionData exceptionData, int itemCount, counters.exceptions.addAndGet(itemCount); ExceptionDataColumns columns = new ExceptionDataColumns(exceptionData); - applyMetricFilters(columns, "Exception", currentConfig); + applyMetricFilters(columns, "Exception", currentConfig, counters); if (matchesDocumentFilters(columns, "Exception", currentConfig)) { List exceptionList = exceptionData.getExceptions(); @@ -257,7 +254,7 @@ private void addRequest(RequestData requestTelemetry, int itemCount, String oper } RequestDataColumns columns = new RequestDataColumns(requestTelemetry); - applyMetricFilters(columns, "Request", currentConfig); + applyMetricFilters(columns, "Request", currentConfig, counters); if (matchesDocumentFilters(columns, "Request", currentConfig)) { Request requestDoc = new Request(); @@ -382,6 +379,8 @@ class FinalCounters { final double processNormalizedCpuUsage; final List documentList = new ArrayList<>(); + final Map projections; + private FinalCounters(Counters currentCounters) { processPhysicalMemory = getPhysicalMemory(memory); @@ -401,6 +400,9 @@ private FinalCounters(Counters currentCounters) { synchronized (currentCounters.documentList) { this.documentList.addAll(currentCounters.documentList); } + synchronized (currentCounters.derivedMetrics) { + this.projections = currentCounters.derivedMetrics.fetchFinalDerivedMetricValues(); + } } private long getPhysicalMemory(@Nullable MemoryMXBean memory) { @@ -453,6 +455,12 @@ static class Counters { final AtomicInteger unsuccessfulRdds = new AtomicInteger(0); final List documentList = new ArrayList<>(); + final DerivedMetricProjections derivedMetrics; + + Counters(Map projectionInfo) { + derivedMetrics = new DerivedMetricProjections(projectionInfo); + } + static long encodeCountAndDuration(long count, long duration) { if (count > MAX_COUNT || duration > MAX_DURATION) { return 0; diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/QuickPulseDataFetcher.java b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/QuickPulseDataFetcher.java index 5c4effebe9809..cbda612dab17e 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/QuickPulseDataFetcher.java +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/QuickPulseDataFetcher.java @@ -120,6 +120,8 @@ private static List addMetricsToMonitoringDataPoint(QuickPulseDataC metrics.put("\\Processor(_Total)\\% Processor Time", counters.processNormalizedCpuUsage); // TODO: remove old cpu counter name when service side makes the UI change metrics.put("\\% Process\\Processor Time Normalized", counters.processNormalizedCpuUsage); + metrics.putAll(counters.projections); + for (Map.Entry entry : metrics.entrySet()) { MetricPoint point = new MetricPoint(); point.setName(entry.getKey()); diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/CustomDimensions.java b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/CustomDimensions.java index 2ec82d89e36b5..d23ea584fcab6 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/CustomDimensions.java +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/CustomDimensions.java @@ -41,4 +41,18 @@ public boolean matchesCustomDimFilter(FilterInfo filter, String trimmedFieldName } } + public double getCustomDimValueForProjection(String key) { + double result = Double.NaN; + if (customDimensions.containsKey(key)) { + String value = customDimensions.get(key); + try { + result = Double.valueOf(value); + } catch (NumberFormatException e) { + // TODO (harskaur): track this error in the error tracker, as this means a customer asked to project a dimension that did not have a numeric value + return result; + } + } + return result; + } + } diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/DependencyDataColumns.java b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/DependencyDataColumns.java index 2f489c497f68e..896a84f63a23d 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/DependencyDataColumns.java +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/DependencyDataColumns.java @@ -73,4 +73,8 @@ public boolean checkAllCustomDims(FilterInfo filter, TelemetryColumns data) { public boolean checkCustomDimFilter(FilterInfo filter, TelemetryColumns data, String trimmedFieldName) { return customDims.matchesCustomDimFilter(filter, trimmedFieldName); } + + public double getCustomDimValueForProjection(String key) { + return customDims.getCustomDimValueForProjection(key); + } } diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/DerivedMetricProjections.java b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/DerivedMetricProjections.java new file mode 100644 index 0000000000000..22e3dbadd0352 --- /dev/null +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/DerivedMetricProjections.java @@ -0,0 +1,113 @@ +package com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.filtering; + +import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.AggregationType; +import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.DerivedMetricInfo; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicLong; + +public class DerivedMetricProjections { + + public static final String COUNT = "Count()"; + private final Map derivedMetricValues = new HashMap<>(); + + public DerivedMetricProjections(Map projectionInfo) { + for (Map.Entry entry : projectionInfo.entrySet()) { + AggregationType aggregationType = entry.getValue(); + DerivedMetricAggregation value; + if (aggregationType.equals(AggregationType.MIN)) { + value = new DerivedMetricAggregation(Long.MAX_VALUE, aggregationType); + } else if (aggregationType.equals(AggregationType.MAX)) { + value = new DerivedMetricAggregation(Long.MIN_VALUE, aggregationType); + } else if (aggregationType.equals(AggregationType.SUM) || aggregationType.equals(AggregationType.AVG)) { + value = new DerivedMetricAggregation(0, aggregationType); + } else { + value = null; // we should never hit this case - that means the UI gave us an invalid aggregation type + } + derivedMetricValues.put(entry.getKey(), value); + } + } + + // This is intended to be called once for every post request + public Map fetchFinalDerivedMetricValues() { + Map result = new HashMap<>(); + for (Map.Entry entry : derivedMetricValues.entrySet()) { + String id = entry.getKey(); + DerivedMetricAggregation dma = entry.getValue(); + double intermediateValue = dma.aggregation.doubleValue(); + double count = dma.count.doubleValue(); + if (count == 0) { + result.put(id, 0.0); + } else { + if (dma.aggregationType.equals(AggregationType.AVG)) { + result.put(id, intermediateValue / count); + } else { + result.put(id, intermediateValue); + } + } + } + return result; + } + + public void calculateProjection(DerivedMetricInfo derivedMetricInfo, TelemetryColumns columns) { + double incrementBy = Double.NaN; + if (COUNT.equals(derivedMetricInfo.getProjection())) { + incrementBy = 1.0; + } else if (KnownRequestColumns.DURATION.equals(derivedMetricInfo.getProjection())) { + if (columns instanceof RequestDataColumns || columns instanceof DependencyDataColumns) { + long duration = columns.getFieldValue(KnownRequestColumns.DURATION, Long.class); + // in case duration from telemetrycolumns doesn't parse correctly. + incrementBy = duration != -1 ? (double) duration : Double.NaN; + } + // The UI doesn't allow for Trace/Exception metrics charts to selection a projection that is a Duration, + // so letting that case slip though. + } else if (derivedMetricInfo.getProjection().startsWith(Filter.CUSTOM_DIM_FIELDNAME_PREFIX)) { + String customDimKey = derivedMetricInfo.getProjection().substring(Filter.CUSTOM_DIM_FIELDNAME_PREFIX.length()); + incrementBy = columns.getCustomDimValueForProjection(customDimKey); + // It is possible for the custom dim value to not parse to a double, or for the custom dim key to not be present. + // For now, such cases produce Double.Nan and get skipped when calculating projection. + // TODO (harskaur): For future PR, the error tracker should track the errors mentioned in lines above. + } + + if (incrementBy != Double.NaN) { + calculateAggregation(derivedMetricInfo.getAggregation(), derivedMetricInfo.getId(), incrementBy); + } + } + + private void calculateAggregation(AggregationType type, String id, double incrementBy) { + DerivedMetricAggregation dma = derivedMetricValues.get(id); + dma.count.getAndAdd(1); + // TODO (harskaur): Use atomic double?? Long will turn out inaccurate with custom dim projections + if (type.equals(AggregationType.SUM) || type.equals(AggregationType.AVG)) { + dma.aggregation.getAndAdd((long) incrementBy); + } else if (type.equals(AggregationType.MIN)) { + dma.aggregation.getAndAccumulate((long) incrementBy , Math::min); + } else if (type.equals(AggregationType.MAX)) { + dma.aggregation.getAndAccumulate((long) incrementBy , Math::max); + } + } + + static class DerivedMetricAggregation { + // This class represents the intermediate state of a derived metric value. + // It keeps track of the count and the aggregated value so that these two + // fields can be used to determine the final value of a derived metric + // when the data fetcher asks for it. + + // Depending on the aggregationType, aggregation holds different values. + // For min, it is the current minimum value + // For max, it is the current max value + // For sum & avg, this represents the current sum. + // When metric values are retrieved by the data fetcher, the final value will + // be determined based on the count and the aggregation. + + // TODO (harskaur): Use atomic double?? Long will turn out inaccurate with custom dim projections + AtomicLong aggregation; + AtomicLong count = new AtomicLong(0); + AggregationType aggregationType; + DerivedMetricAggregation(long initValue, AggregationType type) { + aggregation = new AtomicLong(initValue); + aggregationType = type; + } + } +} diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/ExceptionDataColumns.java b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/ExceptionDataColumns.java index ae35920528b3a..938524d29d2b4 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/ExceptionDataColumns.java +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/ExceptionDataColumns.java @@ -54,4 +54,8 @@ public List getAllFieldValuesAsString() { } return result; } + + public double getCustomDimValueForProjection(String key) { + return customDims.getCustomDimValueForProjection(key); + } } diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/FilteringConfiguration.java b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/FilteringConfiguration.java index e656ea5869cb2..4e4be13392940 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/FilteringConfiguration.java +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/FilteringConfiguration.java @@ -7,6 +7,8 @@ import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.CollectionConfigurationInfo; import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.DocumentStreamInfo; import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.DocumentFilterConjunctionGroupInfo; +import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.AggregationType; + import java.util.Set; import java.util.List; @@ -26,16 +28,20 @@ public class FilteringConfiguration { private final String etag; + private Map validProjectionInfo; + public FilteringConfiguration() { validDerivedMetricInfos = new HashMap<>(); validDocumentFilterConjunctionGroupInfos = new HashMap<>(); etag = ""; + validProjectionInfo = new HashMap<>(); } public FilteringConfiguration(CollectionConfigurationInfo configuration) { validDerivedMetricInfos = parseMetricFilterConfiguration(configuration); validDocumentFilterConjunctionGroupInfos = parseDocumentFilterConfiguration(configuration); etag = configuration.getETag(); + validProjectionInfo = initValidProjectionInfo(); } public List fetchMetricConfigForTelemetryType(String telemetryType) { @@ -59,6 +65,10 @@ public String getETag() { return etag; } + public Map getValidProjectionInitInfo() { + return new HashMap<>(validProjectionInfo); + } + private Map>> parseDocumentFilterConfiguration(CollectionConfigurationInfo configuration) { Map>> result = new HashMap<>(); @@ -112,4 +122,14 @@ public String getETag() { return result; } + private Map initValidProjectionInfo() { + Map result = new HashMap<>(); + for (List derivedMetricInfoList : validDerivedMetricInfos.values()) { + for (DerivedMetricInfo derivedMetricInfo : derivedMetricInfoList) { + result.put(derivedMetricInfo.getId(), derivedMetricInfo.getAggregation()); + } + } + return result; + } + } diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/RequestDataColumns.java b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/RequestDataColumns.java index 4cc310c9de999..d575d945c1ac1 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/RequestDataColumns.java +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/RequestDataColumns.java @@ -71,4 +71,8 @@ public boolean checkCustomDimFilter(FilterInfo filter, TelemetryColumns data, St return customDims.matchesCustomDimFilter(filter, trimmedFieldName); } + public double getCustomDimValueForProjection(String key) { + return customDims.getCustomDimValueForProjection(key); + } + } diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/TelemetryColumns.java b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/TelemetryColumns.java index 54155fc8fb306..80fc2127d69f6 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/TelemetryColumns.java +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/TelemetryColumns.java @@ -16,4 +16,6 @@ public interface TelemetryColumns { boolean checkCustomDimFilter(FilterInfo filter, TelemetryColumns data, String trimmedFieldName); + double getCustomDimValueForProjection(String key); + } diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/TraceDataColumns.java b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/TraceDataColumns.java index 7a3fcdb86c2a7..2e434336cb582 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/TraceDataColumns.java +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/TraceDataColumns.java @@ -44,4 +44,8 @@ public List getAllFieldValuesAsString() { result.add((String) mapping.get(KnownTraceColumns.MESSAGE)); return result; } + + public double getCustomDimValueForProjection(String key) { + return customDims.getCustomDimValueForProjection(key); + } } From bf15276a9b702155f95a5e7ef997fd9ad2c20ed3 Mon Sep 17 00:00:00 2001 From: "Harsimar Kaur (from Dev Box)" Date: Tue, 10 Dec 2024 16:47:22 -0800 Subject: [PATCH 02/20] atomic double and some other changes --- .../pom.xml | 6 ++++++ .../quickpulse/QuickPulseDataCollector.java | 2 +- .../filtering/CustomDimensions.java | 1 - .../filtering/DerivedMetricProjections.java | 20 +++++++++---------- 4 files changed, 17 insertions(+), 12 deletions(-) diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/pom.xml b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/pom.xml index 909cdd6c5f7e2..42a7ab71e773f 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/pom.xml +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/pom.xml @@ -186,6 +186,12 @@ 3.9.1 test + + com.google.guava + guava + 33.2.1-jre + compile + diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/QuickPulseDataCollector.java b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/QuickPulseDataCollector.java index d6cb12ef35d9d..31b19afce89e1 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/QuickPulseDataCollector.java +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/QuickPulseDataCollector.java @@ -27,7 +27,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.HashMap; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; @@ -173,6 +172,7 @@ private void applyMetricFilters(TelemetryColumns columns, String telemetryType, for (DerivedMetricInfo derivedMetricInfo : metricsConfig) { if (Filter.checkMetricFilters(derivedMetricInfo, columns)) { synchronized (currentCounters.derivedMetrics) { + // TODO (harskaur): In future PR, track any error that comes from calculateProjection currentCounters.derivedMetrics.calculateProjection(derivedMetricInfo, columns); } } diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/CustomDimensions.java b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/CustomDimensions.java index d23ea584fcab6..6385fc0b779bc 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/CustomDimensions.java +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/CustomDimensions.java @@ -48,7 +48,6 @@ public double getCustomDimValueForProjection(String key) { try { result = Double.valueOf(value); } catch (NumberFormatException e) { - // TODO (harskaur): track this error in the error tracker, as this means a customer asked to project a dimension that did not have a numeric value return result; } } diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/DerivedMetricProjections.java b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/DerivedMetricProjections.java index 22e3dbadd0352..5abda4f9ff4d4 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/DerivedMetricProjections.java +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/DerivedMetricProjections.java @@ -2,6 +2,7 @@ import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.AggregationType; import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.DerivedMetricInfo; +import com.google.common.util.concurrent.AtomicDouble; import java.util.HashMap; import java.util.Map; @@ -35,8 +36,8 @@ public Map fetchFinalDerivedMetricValues() { for (Map.Entry entry : derivedMetricValues.entrySet()) { String id = entry.getKey(); DerivedMetricAggregation dma = entry.getValue(); - double intermediateValue = dma.aggregation.doubleValue(); - double count = dma.count.doubleValue(); + double intermediateValue = dma.aggregation.get(); + long count = dma.count.get(); if (count == 0) { result.put(id, 0.0); } else { @@ -50,6 +51,8 @@ public Map fetchFinalDerivedMetricValues() { return result; } + // Once a telemetry item passes a metric chart filter, we use that telemetry item to increment + // a derived metric public void calculateProjection(DerivedMetricInfo derivedMetricInfo, TelemetryColumns columns) { double incrementBy = Double.NaN; if (COUNT.equals(derivedMetricInfo.getProjection())) { @@ -67,7 +70,6 @@ public void calculateProjection(DerivedMetricInfo derivedMetricInfo, TelemetryCo incrementBy = columns.getCustomDimValueForProjection(customDimKey); // It is possible for the custom dim value to not parse to a double, or for the custom dim key to not be present. // For now, such cases produce Double.Nan and get skipped when calculating projection. - // TODO (harskaur): For future PR, the error tracker should track the errors mentioned in lines above. } if (incrementBy != Double.NaN) { @@ -78,13 +80,12 @@ public void calculateProjection(DerivedMetricInfo derivedMetricInfo, TelemetryCo private void calculateAggregation(AggregationType type, String id, double incrementBy) { DerivedMetricAggregation dma = derivedMetricValues.get(id); dma.count.getAndAdd(1); - // TODO (harskaur): Use atomic double?? Long will turn out inaccurate with custom dim projections if (type.equals(AggregationType.SUM) || type.equals(AggregationType.AVG)) { - dma.aggregation.getAndAdd((long) incrementBy); + dma.aggregation.getAndAdd(incrementBy); } else if (type.equals(AggregationType.MIN)) { - dma.aggregation.getAndAccumulate((long) incrementBy , Math::min); + dma.aggregation.getAndAccumulate(incrementBy, Math::min); } else if (type.equals(AggregationType.MAX)) { - dma.aggregation.getAndAccumulate((long) incrementBy , Math::max); + dma.aggregation.getAndAccumulate(incrementBy, Math::max); } } @@ -101,12 +102,11 @@ static class DerivedMetricAggregation { // When metric values are retrieved by the data fetcher, the final value will // be determined based on the count and the aggregation. - // TODO (harskaur): Use atomic double?? Long will turn out inaccurate with custom dim projections - AtomicLong aggregation; + AtomicDouble aggregation; AtomicLong count = new AtomicLong(0); AggregationType aggregationType; DerivedMetricAggregation(long initValue, AggregationType type) { - aggregation = new AtomicLong(initValue); + aggregation = new AtomicDouble(initValue); aggregationType = type; } } From 75718c7d45f2c6920c23c5c90ee86adf5237ff5d Mon Sep 17 00:00:00 2001 From: "Harsimar Kaur (from Dev Box)" Date: Wed, 11 Dec 2024 14:04:50 -0800 Subject: [PATCH 03/20] pr comments and some build stuff --- .../checkstyle-suppressions.xml | 2 ++ .../pom.xml | 3 +- .../quickpulse/QuickPulseDataCollector.java | 2 -- .../filtering/DerivedMetricProjections.java | 31 +++++++++---------- .../filtering/FilteringConfiguration.java | 4 +-- 5 files changed, 21 insertions(+), 21 deletions(-) diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/checkstyle-suppressions.xml b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/checkstyle-suppressions.xml index 7f518b4971c1f..79c361ecf4125 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/checkstyle-suppressions.xml +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/checkstyle-suppressions.xml @@ -248,6 +248,7 @@ + @@ -363,6 +364,7 @@ + diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/pom.xml b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/pom.xml index 42a7ab71e773f..ce6d7b0aca4e9 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/pom.xml +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/pom.xml @@ -189,7 +189,7 @@ com.google.guava guava - 33.2.1-jre + 33.2.1-jre compile @@ -212,6 +212,7 @@ io.opentelemetry:opentelemetry-sdk-extension-autoconfigure:[1.43.0] io.opentelemetry.semconv:opentelemetry-semconv-incubating:[1.26.0-alpha] com.squareup.okio:okio:[3.9.1] + com.google.guava:guava:[33.2.1-jre] diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/QuickPulseDataCollector.java b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/QuickPulseDataCollector.java index 5b3b485899a9d..7adbea0d56810 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/QuickPulseDataCollector.java +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/QuickPulseDataCollector.java @@ -215,7 +215,6 @@ private void addDependency(RemoteDependencyData telemetry, int itemCount, Filter DependencyDataColumns columns = new DependencyDataColumns(telemetry); applyMetricFilters(columns, TelemetryType.DEPENDENCY, currentConfig, counters); - List documentStreamIds = new ArrayList<>(); if (matchesDocumentFilters(columns, TelemetryType.DEPENDENCY, currentConfig, documentStreamIds)) { RemoteDependency dependencyDoc = new RemoteDependency(); @@ -284,7 +283,6 @@ private void addRequest(RequestData requestTelemetry, int itemCount, String oper applyMetricFilters(columns, TelemetryType.REQUEST, currentConfig, counters); - List documentStreamIds = new ArrayList<>(); if (matchesDocumentFilters(columns, TelemetryType.REQUEST, currentConfig, documentStreamIds)) { Request requestDoc = new Request(); diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/DerivedMetricProjections.java b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/DerivedMetricProjections.java index 5abda4f9ff4d4..b36ce060198f2 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/DerivedMetricProjections.java +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/DerivedMetricProjections.java @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + package com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.filtering; import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.AggregationType; @@ -40,12 +43,10 @@ public Map fetchFinalDerivedMetricValues() { long count = dma.count.get(); if (count == 0) { result.put(id, 0.0); + } else if (dma.aggregationType.equals(AggregationType.AVG)) { + result.put(id, intermediateValue / count); } else { - if (dma.aggregationType.equals(AggregationType.AVG)) { - result.put(id, intermediateValue / count); - } else { - result.put(id, intermediateValue); - } + result.put(id, intermediateValue); } } return result; @@ -58,15 +59,12 @@ public void calculateProjection(DerivedMetricInfo derivedMetricInfo, TelemetryCo if (COUNT.equals(derivedMetricInfo.getProjection())) { incrementBy = 1.0; } else if (KnownRequestColumns.DURATION.equals(derivedMetricInfo.getProjection())) { - if (columns instanceof RequestDataColumns || columns instanceof DependencyDataColumns) { - long duration = columns.getFieldValue(KnownRequestColumns.DURATION, Long.class); - // in case duration from telemetrycolumns doesn't parse correctly. - incrementBy = duration != -1 ? (double) duration : Double.NaN; - } - // The UI doesn't allow for Trace/Exception metrics charts to selection a projection that is a Duration, - // so letting that case slip though. + long duration = columns.getFieldValue(KnownRequestColumns.DURATION, Long.class); + // in case duration from telemetrycolumns doesn't parse correctly. + incrementBy = duration != -1 ? (double) duration : Double.NaN; } else if (derivedMetricInfo.getProjection().startsWith(Filter.CUSTOM_DIM_FIELDNAME_PREFIX)) { - String customDimKey = derivedMetricInfo.getProjection().substring(Filter.CUSTOM_DIM_FIELDNAME_PREFIX.length()); + String customDimKey + = derivedMetricInfo.getProjection().substring(Filter.CUSTOM_DIM_FIELDNAME_PREFIX.length()); incrementBy = columns.getCustomDimValueForProjection(customDimKey); // It is possible for the custom dim value to not parse to a double, or for the custom dim key to not be present. // For now, such cases produce Double.Nan and get skipped when calculating projection. @@ -102,9 +100,10 @@ static class DerivedMetricAggregation { // When metric values are retrieved by the data fetcher, the final value will // be determined based on the count and the aggregation. - AtomicDouble aggregation; - AtomicLong count = new AtomicLong(0); - AggregationType aggregationType; + final AtomicDouble aggregation; + final AtomicLong count = new AtomicLong(0); + final AggregationType aggregationType; + DerivedMetricAggregation(long initValue, AggregationType type) { aggregation = new AtomicDouble(initValue); aggregationType = type; diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/FilteringConfiguration.java b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/FilteringConfiguration.java index 59a9a15b02f58..cde93b9b4978e 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/FilteringConfiguration.java +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/FilteringConfiguration.java @@ -29,7 +29,7 @@ public class FilteringConfiguration { private final String etag; // key is the derived metric id - private Map validProjectionInfo; + private final Map validProjectionInfo; public FilteringConfiguration() { validDerivedMetricInfos = new HashMap<>(); @@ -127,7 +127,7 @@ public Map getValidProjectionInitInfo() { private Map initValidProjectionInfo() { Map result = new HashMap<>(); for (List derivedMetricInfoList : validDerivedMetricInfos.values()) { - for (DerivedMetricInfo derivedMetricInfo : derivedMetricInfoList) { + for (DerivedMetricInfo derivedMetricInfo : derivedMetricInfoList) { result.put(derivedMetricInfo.getId(), derivedMetricInfo.getAggregation()); } } From 27d4979e09b4889e0905b17b912e6b6d4935273a Mon Sep 17 00:00:00 2001 From: "Harsimar Kaur (from Dev Box)" Date: Wed, 11 Dec 2024 14:56:28 -0800 Subject: [PATCH 04/20] changing guava version --- sdk/monitor/azure-monitor-opentelemetry-autoconfigure/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/pom.xml b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/pom.xml index ce6d7b0aca4e9..f473890679dd4 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/pom.xml +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/pom.xml @@ -189,7 +189,7 @@ com.google.guava guava - 33.2.1-jre + 33.1.0-jre compile @@ -212,7 +212,7 @@ io.opentelemetry:opentelemetry-sdk-extension-autoconfigure:[1.43.0] io.opentelemetry.semconv:opentelemetry-semconv-incubating:[1.26.0-alpha] com.squareup.okio:okio:[3.9.1] - com.google.guava:guava:[33.2.1-jre] + com.google.guava:guava:[33.1.0-jre] From 619bb47cbb0127a7057625c768b9780f7f4f492f Mon Sep 17 00:00:00 2001 From: "Harsimar Kaur (from Dev Box)" Date: Wed, 11 Dec 2024 15:14:57 -0800 Subject: [PATCH 05/20] spotbug fix --- .../implementation/quickpulse/filtering/CustomDimensions.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/CustomDimensions.java b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/CustomDimensions.java index 6385fc0b779bc..a2d637ed8da19 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/CustomDimensions.java +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/CustomDimensions.java @@ -46,7 +46,7 @@ public double getCustomDimValueForProjection(String key) { if (customDimensions.containsKey(key)) { String value = customDimensions.get(key); try { - result = Double.valueOf(value); + result = Double.parseDouble(value); } catch (NumberFormatException e) { return result; } From 915d57991b1b45d667d14cd6064d410670175f33 Mon Sep 17 00:00:00 2001 From: "Harsimar Kaur (from Dev Box)" Date: Wed, 11 Dec 2024 16:53:17 -0800 Subject: [PATCH 06/20] starting to add tests --- .../filtering/DerivedMetricProjections.java | 3 +- .../DerivedMetricProjectionsTest.java | 147 ++++++++++++++++++ 2 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/test/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filteringTest/DerivedMetricProjectionsTest.java diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/DerivedMetricProjections.java b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/DerivedMetricProjections.java index b36ce060198f2..d3ee28cd11996 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/DerivedMetricProjections.java +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/DerivedMetricProjections.java @@ -61,7 +61,8 @@ public void calculateProjection(DerivedMetricInfo derivedMetricInfo, TelemetryCo } else if (KnownRequestColumns.DURATION.equals(derivedMetricInfo.getProjection())) { long duration = columns.getFieldValue(KnownRequestColumns.DURATION, Long.class); // in case duration from telemetrycolumns doesn't parse correctly. - incrementBy = duration != -1 ? (double) duration : Double.NaN; + // also quickpulse expects duration derived metrics to be reported in ms. + incrementBy = duration != -1 ? (double) duration / 1000.0 : Double.NaN; } else if (derivedMetricInfo.getProjection().startsWith(Filter.CUSTOM_DIM_FIELDNAME_PREFIX)) { String customDimKey = derivedMetricInfo.getProjection().substring(Filter.CUSTOM_DIM_FIELDNAME_PREFIX.length()); diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/test/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filteringTest/DerivedMetricProjectionsTest.java b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/test/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filteringTest/DerivedMetricProjectionsTest.java new file mode 100644 index 0000000000000..c79a9905d9ce7 --- /dev/null +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/test/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filteringTest/DerivedMetricProjectionsTest.java @@ -0,0 +1,147 @@ +package com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.filteringTest; + +import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.filtering.*; +import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.AggregationType; +import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.DerivedMetricInfo; +import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.FilterInfo; +import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.FilterConjunctionGroupInfo; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static java.util.Arrays.asList; +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class DerivedMetricProjectionsTest { + private DerivedMetricInfo createDerivedMetricInfoWithEmptyFilters(String id, String telemetryType, AggregationType agg, + AggregationType backendAgg, String projection) { + DerivedMetricInfo result = new DerivedMetricInfo(); + result.setId(id); + result.setTelemetryType(telemetryType); + result.setAggregation(agg); + result.setBackEndAggregation(backendAgg); + result.setProjection(projection); + + FilterConjunctionGroupInfo filterGroup = new FilterConjunctionGroupInfo(); + filterGroup.setFilters(new ArrayList()); + List filterGroups = asList(filterGroup); + + result.setFilterGroups(filterGroups); + return result; + } + + @Test + void testCountProjection() { + DerivedMetricInfo dmiRequest = createDerivedMetricInfoWithEmptyFilters("id-for-request", "Request", AggregationType.SUM, AggregationType.SUM, DerivedMetricProjections.COUNT); + DerivedMetricInfo dmiDep = createDerivedMetricInfoWithEmptyFilters("id-for-dependency", "Dependency", AggregationType.SUM, AggregationType.SUM, DerivedMetricProjections.COUNT); + DerivedMetricInfo dmiException = createDerivedMetricInfoWithEmptyFilters("id-for-exception", "Exception", AggregationType.SUM, AggregationType.SUM, DerivedMetricProjections.COUNT); + DerivedMetricInfo dmiTrace = createDerivedMetricInfoWithEmptyFilters("id-for-trace", "Trace", AggregationType.SUM, AggregationType.SUM, DerivedMetricProjections.COUNT); + + RequestDataColumns request = new RequestDataColumns("https://test.com/hiThere", 200000L, 200, true, "GET /hiThere", new HashMap<>(), new HashMap<>()); + DependencyDataColumns dependency = new DependencyDataColumns("test.com", 200000L, true, "GET /hiThere", 200, "HTTP", "https://test.com/hiThere?x=y", new HashMap<>(), new HashMap<>()); + ExceptionDataColumns exception = new ExceptionDataColumns("Exception Message", "Stack Trace", new HashMap<>(), new HashMap<>()); + TraceDataColumns trace = new TraceDataColumns("Message", new HashMap<>(), new HashMap<>()); + + Map projectionInfo = new HashMap<>(); + projectionInfo.put("id-for-request", AggregationType.SUM); + projectionInfo.put("id-for-dependency", AggregationType.SUM); + projectionInfo.put("id-for-exception", AggregationType.SUM); + projectionInfo.put("id-for-trace", AggregationType.SUM); + DerivedMetricProjections projections = new DerivedMetricProjections(projectionInfo); + + for (int i = 0; i < 2; i++) { + projections.calculateProjection(dmiRequest, request); + } + + for (int i = 0; i < 3; i++) { + projections.calculateProjection(dmiDep, dependency); + } + + for (int i = 0; i < 4; i++) { + projections.calculateProjection(dmiTrace, trace); + } + + projections.calculateProjection(dmiException, exception); + + Map finalValues = projections.fetchFinalDerivedMetricValues(); + assertEquals(finalValues.get("id-for-request"), 2.0); + assertEquals(finalValues.get("id-for-dependency"), 3.0); + assertEquals(finalValues.get("id-for-exception"), 1.0); + assertEquals(finalValues.get("id-for-trace"), 4.0); + } + + @Test + void testDurationProjection() { + DerivedMetricInfo dmiRequestAvg = createDerivedMetricInfoWithEmptyFilters("request-avg", "Request", AggregationType.AVG, AggregationType.AVG, KnownRequestColumns.DURATION); + DerivedMetricInfo dmiRequestMin = createDerivedMetricInfoWithEmptyFilters("request-min", "Request", AggregationType.MIN, AggregationType.MIN, KnownRequestColumns.DURATION); + DerivedMetricInfo dmiRequestMax = createDerivedMetricInfoWithEmptyFilters("request-max", "Request", AggregationType.MAX, AggregationType.MAX, KnownRequestColumns.DURATION); + DerivedMetricInfo dmiDepAvg = createDerivedMetricInfoWithEmptyFilters("dependency-avg", "Dependency", AggregationType.AVG, AggregationType.AVG, KnownRequestColumns.DURATION); + DerivedMetricInfo dmiDepMin = createDerivedMetricInfoWithEmptyFilters("dependency-min", "Dependency", AggregationType.MIN, AggregationType.MIN, KnownRequestColumns.DURATION); + DerivedMetricInfo dmiDepMax = createDerivedMetricInfoWithEmptyFilters("dependency-max", "Dependency", AggregationType.MAX, AggregationType.MAX, KnownRequestColumns.DURATION); + + // The main dif between these requests/deps is the duration. + RequestDataColumns request1 = new RequestDataColumns("https://test.com/hiThere", 200000L, 200, true, "GET /hiThere", new HashMap<>(), new HashMap<>()); + RequestDataColumns request2 = new RequestDataColumns("https://test.com/hiThere", 400000L, 200, true, "GET /hiThere", new HashMap<>(), new HashMap<>()); + RequestDataColumns request3 = new RequestDataColumns("https://test.com/hiThere", 600000L, 200, true, "GET /hiThere", new HashMap<>(), new HashMap<>()); + RequestDataColumns request4 = new RequestDataColumns("https://test.com/hiThere", 100000L, 200, true, "GET /hiThere", new HashMap<>(), new HashMap<>()); + RequestDataColumns request5 = new RequestDataColumns("https://test.com/hiThere", 500000L, 200, true, "GET /hiThere", new HashMap<>(), new HashMap<>()); + DependencyDataColumns dep1 = new DependencyDataColumns("test.com", 200000L, true, "GET /hiThere", 200, "HTTP", "https://test.com/hiThere?x=y", new HashMap<>(), new HashMap<>()); + DependencyDataColumns dep2 = new DependencyDataColumns("test.com", 400000L, true, "GET /hiThere", 200, "HTTP", "https://test.com/hiThere?x=y", new HashMap<>(), new HashMap<>()); + DependencyDataColumns dep3 = new DependencyDataColumns("test.com", 600000L, true, "GET /hiThere", 200, "HTTP", "https://test.com/hiThere?x=y", new HashMap<>(), new HashMap<>()); + DependencyDataColumns dep4 = new DependencyDataColumns("test.com", 100000L, true, "GET /hiThere", 200, "HTTP", "https://test.com/hiThere?x=y", new HashMap<>(), new HashMap<>()); + DependencyDataColumns dep5 = new DependencyDataColumns("test.com", 500000L, true, "GET /hiThere", 200, "HTTP", "https://test.com/hiThere?x=y", new HashMap<>(), new HashMap<>()); + + Map projectionInfo = new HashMap<>(); + projectionInfo.put("request-avg", AggregationType.AVG); + projectionInfo.put("request-min", AggregationType.MIN); + projectionInfo.put("request-max", AggregationType.MAX); + projectionInfo.put("dependency-avg", AggregationType.AVG); + projectionInfo.put("dependency-min", AggregationType.MIN); + projectionInfo.put("dependency-max", AggregationType.MAX); + DerivedMetricProjections projections = new DerivedMetricProjections(projectionInfo); + + // request duration - avg + projections.calculateProjection(dmiRequestAvg, request1); + projections.calculateProjection(dmiRequestAvg, request2); + projections.calculateProjection(dmiRequestAvg, request3); + + // request duration - min + projections.calculateProjection(dmiRequestMin, request3); + projections.calculateProjection(dmiRequestMin, request4); + projections.calculateProjection(dmiRequestMin, request5); + + // request duration - max + projections.calculateProjection(dmiRequestMax, request5); + projections.calculateProjection(dmiRequestMax, request4); + projections.calculateProjection(dmiRequestMax, request3); + + // dep duration - avg + projections.calculateProjection(dmiDepAvg, dep1); + projections.calculateProjection(dmiDepAvg, dep2); + projections.calculateProjection(dmiDepAvg, dep3); + + // dep duration - min + projections.calculateProjection(dmiDepMin, dep3); + projections.calculateProjection(dmiDepMin, dep4); + projections.calculateProjection(dmiDepMin, dep5); + + // dep duration - max + projections.calculateProjection(dmiDepMax, dep5); + projections.calculateProjection(dmiDepMax, dep4); + projections.calculateProjection(dmiDepMax, dep3); + + Map finalValues = projections.fetchFinalDerivedMetricValues(); + assertEquals(finalValues.get("request-avg"), 400.0); + assertEquals(finalValues.get("request-min"), 100.0); + assertEquals(finalValues.get("request-max"), 600.0); + assertEquals(finalValues.get("dependency-avg"), 400.0); + assertEquals(finalValues.get("dependency-min"), 100.0); + assertEquals(finalValues.get("dependency-max"), 600.0); + } + + // TODO (harskaur): add test for custom dim projection + +} From 9c8fa3756b7b051c03b56a50295b02a08d1b5c8c Mon Sep 17 00:00:00 2001 From: "Harsimar Kaur (from Dev Box)" Date: Thu, 12 Dec 2024 16:06:04 -0800 Subject: [PATCH 07/20] added unit tests for derived metric projection --- .../filtering/DerivedMetricProjections.java | 2 +- .../DerivedMetricProjectionsTest.java | 151 ++++++++++++++---- 2 files changed, 125 insertions(+), 28 deletions(-) diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/DerivedMetricProjections.java b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/DerivedMetricProjections.java index d3ee28cd11996..e7e7ac6a2676b 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/DerivedMetricProjections.java +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/DerivedMetricProjections.java @@ -71,7 +71,7 @@ public void calculateProjection(DerivedMetricInfo derivedMetricInfo, TelemetryCo // For now, such cases produce Double.Nan and get skipped when calculating projection. } - if (incrementBy != Double.NaN) { + if (!Double.isNaN(incrementBy)) { calculateAggregation(derivedMetricInfo.getAggregation(), derivedMetricInfo.getId(), incrementBy); } } diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/test/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filteringTest/DerivedMetricProjectionsTest.java b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/test/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filteringTest/DerivedMetricProjectionsTest.java index c79a9905d9ce7..f6468d96afed3 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/test/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filteringTest/DerivedMetricProjectionsTest.java +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/test/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filteringTest/DerivedMetricProjectionsTest.java @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + package com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.filteringTest; import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.filtering.*; @@ -16,8 +19,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class DerivedMetricProjectionsTest { - private DerivedMetricInfo createDerivedMetricInfoWithEmptyFilters(String id, String telemetryType, AggregationType agg, - AggregationType backendAgg, String projection) { + private DerivedMetricInfo createDerivedMetricInfoWithEmptyFilters(String id, String telemetryType, + AggregationType agg, AggregationType backendAgg, String projection) { DerivedMetricInfo result = new DerivedMetricInfo(); result.setId(id); result.setTelemetryType(telemetryType); @@ -35,14 +38,21 @@ private DerivedMetricInfo createDerivedMetricInfoWithEmptyFilters(String id, Str @Test void testCountProjection() { - DerivedMetricInfo dmiRequest = createDerivedMetricInfoWithEmptyFilters("id-for-request", "Request", AggregationType.SUM, AggregationType.SUM, DerivedMetricProjections.COUNT); - DerivedMetricInfo dmiDep = createDerivedMetricInfoWithEmptyFilters("id-for-dependency", "Dependency", AggregationType.SUM, AggregationType.SUM, DerivedMetricProjections.COUNT); - DerivedMetricInfo dmiException = createDerivedMetricInfoWithEmptyFilters("id-for-exception", "Exception", AggregationType.SUM, AggregationType.SUM, DerivedMetricProjections.COUNT); - DerivedMetricInfo dmiTrace = createDerivedMetricInfoWithEmptyFilters("id-for-trace", "Trace", AggregationType.SUM, AggregationType.SUM, DerivedMetricProjections.COUNT); - - RequestDataColumns request = new RequestDataColumns("https://test.com/hiThere", 200000L, 200, true, "GET /hiThere", new HashMap<>(), new HashMap<>()); - DependencyDataColumns dependency = new DependencyDataColumns("test.com", 200000L, true, "GET /hiThere", 200, "HTTP", "https://test.com/hiThere?x=y", new HashMap<>(), new HashMap<>()); - ExceptionDataColumns exception = new ExceptionDataColumns("Exception Message", "Stack Trace", new HashMap<>(), new HashMap<>()); + DerivedMetricInfo dmiRequest = createDerivedMetricInfoWithEmptyFilters("id-for-request", "Request", + AggregationType.SUM, AggregationType.SUM, DerivedMetricProjections.COUNT); + DerivedMetricInfo dmiDep = createDerivedMetricInfoWithEmptyFilters("id-for-dependency", "Dependency", + AggregationType.SUM, AggregationType.SUM, DerivedMetricProjections.COUNT); + DerivedMetricInfo dmiException = createDerivedMetricInfoWithEmptyFilters("id-for-exception", "Exception", + AggregationType.SUM, AggregationType.SUM, DerivedMetricProjections.COUNT); + DerivedMetricInfo dmiTrace = createDerivedMetricInfoWithEmptyFilters("id-for-trace", "Trace", + AggregationType.SUM, AggregationType.SUM, DerivedMetricProjections.COUNT); + + RequestDataColumns request = new RequestDataColumns("https://test.com/hiThere", 200000L, 200, true, + "GET /hiThere", new HashMap<>(), new HashMap<>()); + DependencyDataColumns dependency = new DependencyDataColumns("test.com", 200000L, true, "GET /hiThere", 200, + "HTTP", "https://test.com/hiThere?x=y", new HashMap<>(), new HashMap<>()); + ExceptionDataColumns exception + = new ExceptionDataColumns("Exception Message", "Stack Trace", new HashMap<>(), new HashMap<>()); TraceDataColumns trace = new TraceDataColumns("Message", new HashMap<>(), new HashMap<>()); Map projectionInfo = new HashMap<>(); @@ -75,24 +85,40 @@ void testCountProjection() { @Test void testDurationProjection() { - DerivedMetricInfo dmiRequestAvg = createDerivedMetricInfoWithEmptyFilters("request-avg", "Request", AggregationType.AVG, AggregationType.AVG, KnownRequestColumns.DURATION); - DerivedMetricInfo dmiRequestMin = createDerivedMetricInfoWithEmptyFilters("request-min", "Request", AggregationType.MIN, AggregationType.MIN, KnownRequestColumns.DURATION); - DerivedMetricInfo dmiRequestMax = createDerivedMetricInfoWithEmptyFilters("request-max", "Request", AggregationType.MAX, AggregationType.MAX, KnownRequestColumns.DURATION); - DerivedMetricInfo dmiDepAvg = createDerivedMetricInfoWithEmptyFilters("dependency-avg", "Dependency", AggregationType.AVG, AggregationType.AVG, KnownRequestColumns.DURATION); - DerivedMetricInfo dmiDepMin = createDerivedMetricInfoWithEmptyFilters("dependency-min", "Dependency", AggregationType.MIN, AggregationType.MIN, KnownRequestColumns.DURATION); - DerivedMetricInfo dmiDepMax = createDerivedMetricInfoWithEmptyFilters("dependency-max", "Dependency", AggregationType.MAX, AggregationType.MAX, KnownRequestColumns.DURATION); + DerivedMetricInfo dmiRequestAvg = createDerivedMetricInfoWithEmptyFilters("request-avg", "Request", + AggregationType.AVG, AggregationType.AVG, KnownRequestColumns.DURATION); + DerivedMetricInfo dmiRequestMin = createDerivedMetricInfoWithEmptyFilters("request-min", "Request", + AggregationType.MIN, AggregationType.MIN, KnownRequestColumns.DURATION); + DerivedMetricInfo dmiRequestMax = createDerivedMetricInfoWithEmptyFilters("request-max", "Request", + AggregationType.MAX, AggregationType.MAX, KnownRequestColumns.DURATION); + DerivedMetricInfo dmiDepAvg = createDerivedMetricInfoWithEmptyFilters("dependency-avg", "Dependency", + AggregationType.AVG, AggregationType.AVG, KnownRequestColumns.DURATION); + DerivedMetricInfo dmiDepMin = createDerivedMetricInfoWithEmptyFilters("dependency-min", "Dependency", + AggregationType.MIN, AggregationType.MIN, KnownRequestColumns.DURATION); + DerivedMetricInfo dmiDepMax = createDerivedMetricInfoWithEmptyFilters("dependency-max", "Dependency", + AggregationType.MAX, AggregationType.MAX, KnownRequestColumns.DURATION); // The main dif between these requests/deps is the duration. - RequestDataColumns request1 = new RequestDataColumns("https://test.com/hiThere", 200000L, 200, true, "GET /hiThere", new HashMap<>(), new HashMap<>()); - RequestDataColumns request2 = new RequestDataColumns("https://test.com/hiThere", 400000L, 200, true, "GET /hiThere", new HashMap<>(), new HashMap<>()); - RequestDataColumns request3 = new RequestDataColumns("https://test.com/hiThere", 600000L, 200, true, "GET /hiThere", new HashMap<>(), new HashMap<>()); - RequestDataColumns request4 = new RequestDataColumns("https://test.com/hiThere", 100000L, 200, true, "GET /hiThere", new HashMap<>(), new HashMap<>()); - RequestDataColumns request5 = new RequestDataColumns("https://test.com/hiThere", 500000L, 200, true, "GET /hiThere", new HashMap<>(), new HashMap<>()); - DependencyDataColumns dep1 = new DependencyDataColumns("test.com", 200000L, true, "GET /hiThere", 200, "HTTP", "https://test.com/hiThere?x=y", new HashMap<>(), new HashMap<>()); - DependencyDataColumns dep2 = new DependencyDataColumns("test.com", 400000L, true, "GET /hiThere", 200, "HTTP", "https://test.com/hiThere?x=y", new HashMap<>(), new HashMap<>()); - DependencyDataColumns dep3 = new DependencyDataColumns("test.com", 600000L, true, "GET /hiThere", 200, "HTTP", "https://test.com/hiThere?x=y", new HashMap<>(), new HashMap<>()); - DependencyDataColumns dep4 = new DependencyDataColumns("test.com", 100000L, true, "GET /hiThere", 200, "HTTP", "https://test.com/hiThere?x=y", new HashMap<>(), new HashMap<>()); - DependencyDataColumns dep5 = new DependencyDataColumns("test.com", 500000L, true, "GET /hiThere", 200, "HTTP", "https://test.com/hiThere?x=y", new HashMap<>(), new HashMap<>()); + RequestDataColumns request1 = new RequestDataColumns("https://test.com/hiThere", 200000L, 200, true, + "GET /hiThere", new HashMap<>(), new HashMap<>()); + RequestDataColumns request2 = new RequestDataColumns("https://test.com/hiThere", 400000L, 200, true, + "GET /hiThere", new HashMap<>(), new HashMap<>()); + RequestDataColumns request3 = new RequestDataColumns("https://test.com/hiThere", 600000L, 200, true, + "GET /hiThere", new HashMap<>(), new HashMap<>()); + RequestDataColumns request4 = new RequestDataColumns("https://test.com/hiThere", 100000L, 200, true, + "GET /hiThere", new HashMap<>(), new HashMap<>()); + RequestDataColumns request5 = new RequestDataColumns("https://test.com/hiThere", 500000L, 200, true, + "GET /hiThere", new HashMap<>(), new HashMap<>()); + DependencyDataColumns dep1 = new DependencyDataColumns("test.com", 200000L, true, "GET /hiThere", 200, "HTTP", + "https://test.com/hiThere?x=y", new HashMap<>(), new HashMap<>()); + DependencyDataColumns dep2 = new DependencyDataColumns("test.com", 400000L, true, "GET /hiThere", 200, "HTTP", + "https://test.com/hiThere?x=y", new HashMap<>(), new HashMap<>()); + DependencyDataColumns dep3 = new DependencyDataColumns("test.com", 600000L, true, "GET /hiThere", 200, "HTTP", + "https://test.com/hiThere?x=y", new HashMap<>(), new HashMap<>()); + DependencyDataColumns dep4 = new DependencyDataColumns("test.com", 100000L, true, "GET /hiThere", 200, "HTTP", + "https://test.com/hiThere?x=y", new HashMap<>(), new HashMap<>()); + DependencyDataColumns dep5 = new DependencyDataColumns("test.com", 500000L, true, "GET /hiThere", 200, "HTTP", + "https://test.com/hiThere?x=y", new HashMap<>(), new HashMap<>()); Map projectionInfo = new HashMap<>(); projectionInfo.put("request-avg", AggregationType.AVG); @@ -142,6 +168,77 @@ void testDurationProjection() { assertEquals(finalValues.get("dependency-max"), 600.0); } - // TODO (harskaur): add test for custom dim projection + @Test + void testCustomProjection() { + DerivedMetricInfo dmiRequestAvg = createDerivedMetricInfoWithEmptyFilters("request-avg", "Request", + AggregationType.AVG, AggregationType.AVG, "CustomDimensions.property"); + DerivedMetricInfo dmiRequestMin = createDerivedMetricInfoWithEmptyFilters("request-min", "Request", + AggregationType.MIN, AggregationType.MIN, "CustomDimensions.property"); + DerivedMetricInfo dmiRequestMax = createDerivedMetricInfoWithEmptyFilters("request-max", "Request", + AggregationType.MAX, AggregationType.MAX, "CustomDimensions.property"); + DerivedMetricInfo dmiRequestSum = createDerivedMetricInfoWithEmptyFilters("request-sum", "Request", + AggregationType.SUM, AggregationType.SUM, "CustomDimensions.property"); + + Map projectionInfo = new HashMap<>(); + projectionInfo.put("request-avg", AggregationType.AVG); + projectionInfo.put("request-min", AggregationType.MIN); + projectionInfo.put("request-max", AggregationType.MAX); + projectionInfo.put("request-sum", AggregationType.SUM); + DerivedMetricProjections projections = new DerivedMetricProjections(projectionInfo); + + Map customDims = new HashMap<>(); + RequestDataColumns request1 = new RequestDataColumns("https://test.com/hiThere", 200000L, 200, true, + "GET /hiThere", customDims, new HashMap<>()); + customDims.put("property", "hi"); + RequestDataColumns request2 = new RequestDataColumns("https://test.com/hiThere", 200000L, 200, true, + "GET /hiThere", customDims, new HashMap<>()); + customDims.put("property", "5"); + RequestDataColumns request3 = new RequestDataColumns("https://test.com/hiThere", 200000L, 200, true, + "GET /hiThere", customDims, new HashMap<>()); + customDims.put("property", "10"); + RequestDataColumns request4 = new RequestDataColumns("https://test.com/hiThere", 200000L, 200, true, + "GET /hiThere", customDims, new HashMap<>()); + customDims.put("property", "15"); + RequestDataColumns request5 = new RequestDataColumns("https://test.com/hiThere", 200000L, 200, true, + "GET /hiThere", customDims, new HashMap<>()); + customDims.put("property", "1"); + RequestDataColumns request6 = new RequestDataColumns("https://test.com/hiThere", 200000L, 200, true, + "GET /hiThere", customDims, new HashMap<>()); + customDims.put("property", "20"); + RequestDataColumns request7 = new RequestDataColumns("https://test.com/hiThere", 200000L, 200, true, + "GET /hiThere", customDims, new HashMap<>()); + + // custom dim does not exist in current request - should not count + projections.calculateProjection(dmiRequestAvg, request1); + + // custom dim exists in current request, but does not have a value that converts to a double - should not count + projections.calculateProjection(dmiRequestAvg, request2); + + // custom dim - avg + projections.calculateProjection(dmiRequestAvg, request3); + projections.calculateProjection(dmiRequestAvg, request4); + projections.calculateProjection(dmiRequestAvg, request5); + + // custom dim - min + projections.calculateProjection(dmiRequestMin, request5); + projections.calculateProjection(dmiRequestMin, request6); + projections.calculateProjection(dmiRequestMin, request7); + + // custom dim - max + projections.calculateProjection(dmiRequestMax, request7); + projections.calculateProjection(dmiRequestMax, request6); + projections.calculateProjection(dmiRequestMax, request5); + + // custom dim - sum + projections.calculateProjection(dmiRequestSum, request5); + projections.calculateProjection(dmiRequestSum, request6); + projections.calculateProjection(dmiRequestSum, request5); + + Map finalValues = projections.fetchFinalDerivedMetricValues(); + assertEquals(finalValues.get("request-avg"), 10.0); + assertEquals(finalValues.get("request-min"), 1.0); + assertEquals(finalValues.get("request-max"), 20.0); + assertEquals(finalValues.get("request-sum"), 31.0); + } } From c599211b466f6c63346b57825b5dc38fda2334cb Mon Sep 17 00:00:00 2001 From: "Harsimar Kaur (from Dev Box)" Date: Fri, 13 Dec 2024 14:16:17 -0800 Subject: [PATCH 08/20] reorganize tests --- .../QuickPulseDataCollectorTests.java | 157 +++++++++-- .../DerivedMetricProjectionsTest.java | 247 ++++++++---------- 2 files changed, 242 insertions(+), 162 deletions(-) diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/test/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/QuickPulseDataCollectorTests.java b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/test/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/QuickPulseDataCollectorTests.java index 46d1b5ad0bd94..e97835cfc13b9 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/test/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/QuickPulseDataCollectorTests.java +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/test/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/QuickPulseDataCollectorTests.java @@ -7,26 +7,16 @@ import com.azure.monitor.opentelemetry.autoconfigure.implementation.builders.MessageTelemetryBuilder; import com.azure.monitor.opentelemetry.autoconfigure.implementation.configuration.ConnectionString; import com.azure.monitor.opentelemetry.autoconfigure.implementation.models.TelemetryItem; +import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.filtering.DerivedMetricProjections; import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.filtering.FilteringConfiguration; +import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.filtering.KnownExceptionColumns; import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.filtering.KnownRequestColumns; -import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.TelemetryType; -import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.DocumentIngress; -import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.CollectionConfigurationInfo; -import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.DocumentFilterConjunctionGroupInfo; -import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.DocumentStreamInfo; -import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.FilterInfo; -import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.FilterConjunctionGroupInfo; -import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.DocumentType; -import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.RemoteDependency; -import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.PredicateType; -import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.Request; +import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.*; import org.junit.jupiter.api.Test; import java.time.Duration; -import java.util.Date; +import java.util.*; import java.util.concurrent.atomic.AtomicReference; -import java.util.List; -import java.util.ArrayList; import static com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.QuickPulseTestBase.createRemoteDependencyTelemetry; import static com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.QuickPulseTestBase.createRequestTelemetry; @@ -260,7 +250,7 @@ void honorDefaultConfig() { collector.setQuickPulseStatus(QuickPulseStatus.QP_IS_ON); collector.enable(FAKE_CONNECTION_STRING::getInstrumentationKey); - createTelemetryItemsForDocsFiltering(collector); + createTelemetryItemsForFiltering(collector); QuickPulseDataCollector.FinalCounters counters = collector.peek(); List documents = counters.documentList; @@ -277,17 +267,7 @@ void honorDefaultConfig() { DocumentIngress traceDoc = counters.documentList.get(3); assertThat(traceDoc.getDocumentType()).isEqualTo(DocumentType.TRACE); - assertThat(counters.rdds).isEqualTo(2); - assertThat(counters.unsuccessfulRdds).isEqualTo(1); - // The below line represents the "\\ApplicationInsights\\Dependency Call Duration" counter, which is meant to be an average in the 1s interval. (500 + 300) / 2 = 400. - // See this same logic used in the QuickPulseDataFetcher when building the monitoring point. - assertThat(counters.rddsDuration / counters.rdds).isEqualTo(400); - assertThat(counters.requests).isEqualTo(2); - assertThat(counters.unsuccessfulRequests).isEqualTo(1); - // The below line represents the "\\ApplicationInsights\\Request Duration" counter, which is meant to be an average in the 1s interval. (500 + 300) / 2 = 400. - // See this same logic used in the QuickPulseDataFetcher when building the monitoring point. - assertThat(counters.requestsDuration / counters.requests).isEqualTo(400); - assertThat(counters.exceptions).isEqualTo(1); + assertDefaultMetrics(counters); counters = collector.getAndRestart(); assertCountersReset(collector.peek()); @@ -303,7 +283,7 @@ void honorDifferentMultipleSessionDocConfig() { collector.setQuickPulseStatus(QuickPulseStatus.QP_IS_ON); collector.enable(FAKE_CONNECTION_STRING::getInstrumentationKey); - createTelemetryItemsForDocsFiltering(collector); + createTelemetryItemsForFiltering(collector); QuickPulseDataCollector.FinalCounters counters = collector.peek(); List documents = counters.documentList; @@ -346,7 +326,7 @@ void honorDuplicateMultipleSessionDocConfig() { QuickPulseDataCollector collector = new QuickPulseDataCollector(configuration); collector.setQuickPulseStatus(QuickPulseStatus.QP_IS_ON); collector.enable(FAKE_CONNECTION_STRING::getInstrumentationKey); - createTelemetryItemsForDocsFiltering(collector); + createTelemetryItemsForFiltering(collector); QuickPulseDataCollector.FinalCounters counters = collector.peek(); List documents = counters.documentList; @@ -375,7 +355,56 @@ void honorDuplicateMultipleSessionDocConfig() { assertCountersReset(collector.peek()); } - private void createTelemetryItemsForDocsFiltering(QuickPulseDataCollector collector) { + @Test + void testMetricChartFiltering() { + CollectionConfigurationInfo derivedMetricsConfig = createDerivedMetricConfig(); + AtomicReference configuration + = new AtomicReference<>(new FilteringConfiguration(derivedMetricsConfig)); + + QuickPulseDataCollector collector = new QuickPulseDataCollector(configuration); + collector.setQuickPulseStatus(QuickPulseStatus.QP_IS_ON); + collector.enable(FAKE_CONNECTION_STRING::getInstrumentationKey); + createTelemetryItemsForFiltering(collector); + + QuickPulseDataCollector.FinalCounters counters = collector.peek(); + // The default metrics should not be impacted by derived metric filters + assertDefaultMetrics(counters); + Map finalDerivedMetricValues = counters.projections; + + // The config asks to take the avg duration of requests that have response code 200. + // Only one such request came through and that request has a duration of 300. + assertThat(finalDerivedMetricValues.get("request-duration")).isEqualTo(300.0); + + // The config asks to count the # of exceptions that contain the message "hi". No + // exceptions contain that message. + assertThat(finalDerivedMetricValues.get("exception-count")).isEqualTo(0.0); + + counters = collector.getAndRestart(); + QuickPulseDataCollector.FinalCounters resetCounters = collector.peek(); + assertCountersReset(resetCounters); + + Map resetProjections = new HashMap<>(); + resetProjections.put("request-duration", 0.0); + resetProjections.put("exception-count", 0.0); + + assertThat(resetCounters.projections).isEqualTo(resetProjections); + } + + private void assertDefaultMetrics(QuickPulseDataCollector.FinalCounters counters) { + assertThat(counters.rdds).isEqualTo(2); + assertThat(counters.unsuccessfulRdds).isEqualTo(1); + // The below line represents the "\\ApplicationInsights\\Dependency Call Duration" counter, which is meant to be an average in the 1s interval. (500 + 300) / 2 = 400. + // See this same logic used in the QuickPulseDataFetcher when building the monitoring point. + assertThat(counters.rddsDuration / counters.rdds).isEqualTo(400); + assertThat(counters.requests).isEqualTo(2); + assertThat(counters.unsuccessfulRequests).isEqualTo(1); + // The below line represents the "\\ApplicationInsights\\Request Duration" counter, which is meant to be an average in the 1s interval. (500 + 300) / 2 = 400. + // See this same logic used in the QuickPulseDataFetcher when building the monitoring point. + assertThat(counters.requestsDuration / counters.requests).isEqualTo(400); + assertThat(counters.exceptions).isEqualTo(1); + } + + private void createTelemetryItemsForFiltering(QuickPulseDataCollector collector) { collector.setQuickPulseStatus(QuickPulseStatus.QP_IS_ON); collector.enable(FAKE_CONNECTION_STRING::getInstrumentationKey); @@ -493,4 +522,74 @@ private DocumentStreamInfo createDocumentStream(boolean isDefault) { return documentStreamInfo; } + + private CollectionConfigurationInfo createDerivedMetricConfig() { + CollectionConfigurationInfo config = new CollectionConfigurationInfo(); + List documentStreams = new ArrayList<>(); + DocumentStreamInfo defaultStream = createDocumentStream(true); + documentStreams.add(defaultStream); + + config.setDocumentStreams(documentStreams); + config.setETag("random-etag"); + + List metrics = new ArrayList<>(); + DerivedMetricInfo requestDuration = createRequestDurationDerivedMetricInfo(); + DerivedMetricInfo exceptionCount = createExceptionCountDerivedMetricInfo(); + metrics.add(requestDuration); + metrics.add(exceptionCount); + + config.setMetrics(metrics); + + return config; + } + + private DerivedMetricInfo createRequestDurationDerivedMetricInfo() { + DerivedMetricInfo dmi = new DerivedMetricInfo(); + dmi.setId("request-duration"); + dmi.setTelemetryType("Request"); + dmi.setAggregation(AggregationType.AVG); + dmi.setBackEndAggregation(AggregationType.AVG); + dmi.setProjection(KnownRequestColumns.DURATION); + + FilterInfo filter = new FilterInfo(); + filter.setFieldName(KnownRequestColumns.RESPONSE_CODE); + filter.setPredicate(PredicateType.EQUAL); + filter.setComparand("200"); + List filters = new ArrayList<>(); + filters.add(filter); + + FilterConjunctionGroupInfo filterGroup = new FilterConjunctionGroupInfo(); + filterGroup.setFilters(filters); + + List filterGroups = new ArrayList<>(); + filterGroups.add(filterGroup); + dmi.setFilterGroups(filterGroups); + + return dmi; + } + + private DerivedMetricInfo createExceptionCountDerivedMetricInfo() { + DerivedMetricInfo dmi = new DerivedMetricInfo(); + dmi.setId("exception-count"); + dmi.setTelemetryType("Exception"); + dmi.setAggregation(AggregationType.SUM); + dmi.setBackEndAggregation(AggregationType.SUM); + dmi.setProjection(DerivedMetricProjections.COUNT); + + FilterInfo filter = new FilterInfo(); + filter.setFieldName(KnownExceptionColumns.MESSAGE); + filter.setPredicate(PredicateType.CONTAINS); + filter.setComparand("hi"); + List filters = new ArrayList<>(); + filters.add(filter); + + FilterConjunctionGroupInfo filterGroup = new FilterConjunctionGroupInfo(); + filterGroup.setFilters(filters); + + List filterGroups = new ArrayList<>(); + filterGroups.add(filterGroup); + dmi.setFilterGroups(filterGroups); + + return dmi; + } } diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/test/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filteringTest/DerivedMetricProjectionsTest.java b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/test/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filteringTest/DerivedMetricProjectionsTest.java index f6468d96afed3..01eb789c646ba 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/test/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filteringTest/DerivedMetricProjectionsTest.java +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/test/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filteringTest/DerivedMetricProjectionsTest.java @@ -4,10 +4,7 @@ package com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.filteringTest; import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.filtering.*; -import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.AggregationType; -import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.DerivedMetricInfo; -import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.FilterInfo; -import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.FilterConjunctionGroupInfo; +import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.*; import org.junit.jupiter.api.Test; import java.util.ArrayList; @@ -84,161 +81,145 @@ void testCountProjection() { } @Test - void testDurationProjection() { - DerivedMetricInfo dmiRequestAvg = createDerivedMetricInfoWithEmptyFilters("request-avg", "Request", - AggregationType.AVG, AggregationType.AVG, KnownRequestColumns.DURATION); - DerivedMetricInfo dmiRequestMin = createDerivedMetricInfoWithEmptyFilters("request-min", "Request", - AggregationType.MIN, AggregationType.MIN, KnownRequestColumns.DURATION); - DerivedMetricInfo dmiRequestMax = createDerivedMetricInfoWithEmptyFilters("request-max", "Request", - AggregationType.MAX, AggregationType.MAX, KnownRequestColumns.DURATION); - DerivedMetricInfo dmiDepAvg = createDerivedMetricInfoWithEmptyFilters("dependency-avg", "Dependency", - AggregationType.AVG, AggregationType.AVG, KnownRequestColumns.DURATION); - DerivedMetricInfo dmiDepMin = createDerivedMetricInfoWithEmptyFilters("dependency-min", "Dependency", - AggregationType.MIN, AggregationType.MIN, KnownRequestColumns.DURATION); - DerivedMetricInfo dmiDepMax = createDerivedMetricInfoWithEmptyFilters("dependency-max", "Dependency", - AggregationType.MAX, AggregationType.MAX, KnownRequestColumns.DURATION); - - // The main dif between these requests/deps is the duration. - RequestDataColumns request1 = new RequestDataColumns("https://test.com/hiThere", 200000L, 200, true, - "GET /hiThere", new HashMap<>(), new HashMap<>()); - RequestDataColumns request2 = new RequestDataColumns("https://test.com/hiThere", 400000L, 200, true, - "GET /hiThere", new HashMap<>(), new HashMap<>()); - RequestDataColumns request3 = new RequestDataColumns("https://test.com/hiThere", 600000L, 200, true, - "GET /hiThere", new HashMap<>(), new HashMap<>()); - RequestDataColumns request4 = new RequestDataColumns("https://test.com/hiThere", 100000L, 200, true, - "GET /hiThere", new HashMap<>(), new HashMap<>()); - RequestDataColumns request5 = new RequestDataColumns("https://test.com/hiThere", 500000L, 200, true, - "GET /hiThere", new HashMap<>(), new HashMap<>()); - DependencyDataColumns dep1 = new DependencyDataColumns("test.com", 200000L, true, "GET /hiThere", 200, "HTTP", - "https://test.com/hiThere?x=y", new HashMap<>(), new HashMap<>()); - DependencyDataColumns dep2 = new DependencyDataColumns("test.com", 400000L, true, "GET /hiThere", 200, "HTTP", - "https://test.com/hiThere?x=y", new HashMap<>(), new HashMap<>()); - DependencyDataColumns dep3 = new DependencyDataColumns("test.com", 600000L, true, "GET /hiThere", 200, "HTTP", - "https://test.com/hiThere?x=y", new HashMap<>(), new HashMap<>()); - DependencyDataColumns dep4 = new DependencyDataColumns("test.com", 100000L, true, "GET /hiThere", 200, "HTTP", - "https://test.com/hiThere?x=y", new HashMap<>(), new HashMap<>()); - DependencyDataColumns dep5 = new DependencyDataColumns("test.com", 500000L, true, "GET /hiThere", 200, "HTTP", - "https://test.com/hiThere?x=y", new HashMap<>(), new HashMap<>()); - - Map projectionInfo = new HashMap<>(); - projectionInfo.put("request-avg", AggregationType.AVG); - projectionInfo.put("request-min", AggregationType.MIN); - projectionInfo.put("request-max", AggregationType.MAX); - projectionInfo.put("dependency-avg", AggregationType.AVG); - projectionInfo.put("dependency-min", AggregationType.MIN); - projectionInfo.put("dependency-max", AggregationType.MAX); - DerivedMetricProjections projections = new DerivedMetricProjections(projectionInfo); - - // request duration - avg - projections.calculateProjection(dmiRequestAvg, request1); - projections.calculateProjection(dmiRequestAvg, request2); - projections.calculateProjection(dmiRequestAvg, request3); + void testDurationAvgProjection() { + testDurationProjectionWith("request-avg", "dependency-avg", AggregationType.AVG, 400.0); + } - // request duration - min - projections.calculateProjection(dmiRequestMin, request3); - projections.calculateProjection(dmiRequestMin, request4); - projections.calculateProjection(dmiRequestMin, request5); + @Test + void testDurationMinProjection() { + testDurationProjectionWith("request-min", "dependency-min", AggregationType.MIN, 200.0); + } - // request duration - max - projections.calculateProjection(dmiRequestMax, request5); - projections.calculateProjection(dmiRequestMax, request4); - projections.calculateProjection(dmiRequestMax, request3); + @Test + void testDurationMaxProjection() { + testDurationProjectionWith("request-max", "dependency-max", AggregationType.MAX, 600.0); + } - // dep duration - avg - projections.calculateProjection(dmiDepAvg, dep1); - projections.calculateProjection(dmiDepAvg, dep2); - projections.calculateProjection(dmiDepAvg, dep3); + @Test + void testCustomDimensionAvgProjection() { + testCustomDimProjectionWith("request-avg", AggregationType.AVG, 8.0); + } - // dep duration - min - projections.calculateProjection(dmiDepMin, dep3); - projections.calculateProjection(dmiDepMin, dep4); - projections.calculateProjection(dmiDepMin, dep5); + @Test + void testCustomDimensionMinProjection() { + testCustomDimProjectionWith("request-min", AggregationType.MIN, 4.0); + } - // dep duration - max - projections.calculateProjection(dmiDepMax, dep5); - projections.calculateProjection(dmiDepMax, dep4); - projections.calculateProjection(dmiDepMax, dep3); + @Test + void testCustomDimensionMaxProjection() { + testCustomDimProjectionWith("request-max", AggregationType.MAX, 15.0); + } - Map finalValues = projections.fetchFinalDerivedMetricValues(); - assertEquals(finalValues.get("request-avg"), 400.0); - assertEquals(finalValues.get("request-min"), 100.0); - assertEquals(finalValues.get("request-max"), 600.0); - assertEquals(finalValues.get("dependency-avg"), 400.0); - assertEquals(finalValues.get("dependency-min"), 100.0); - assertEquals(finalValues.get("dependency-max"), 600.0); + @Test + void testCustomDimensionSumProjection() { + testCustomDimProjectionWith("request-sum", AggregationType.SUM, 24.0); } @Test - void testCustomProjection() { - DerivedMetricInfo dmiRequestAvg = createDerivedMetricInfoWithEmptyFilters("request-avg", "Request", + void testInvalidCustomDimensionProjection() { + DerivedMetricInfo dmiRequest = createDerivedMetricInfoWithEmptyFilters("request-avg", "Request", AggregationType.AVG, AggregationType.AVG, "CustomDimensions.property"); - DerivedMetricInfo dmiRequestMin = createDerivedMetricInfoWithEmptyFilters("request-min", "Request", - AggregationType.MIN, AggregationType.MIN, "CustomDimensions.property"); - DerivedMetricInfo dmiRequestMax = createDerivedMetricInfoWithEmptyFilters("request-max", "Request", - AggregationType.MAX, AggregationType.MAX, "CustomDimensions.property"); - DerivedMetricInfo dmiRequestSum = createDerivedMetricInfoWithEmptyFilters("request-sum", "Request", - AggregationType.SUM, AggregationType.SUM, "CustomDimensions.property"); Map projectionInfo = new HashMap<>(); projectionInfo.put("request-avg", AggregationType.AVG); - projectionInfo.put("request-min", AggregationType.MIN); - projectionInfo.put("request-max", AggregationType.MAX); - projectionInfo.put("request-sum", AggregationType.SUM); DerivedMetricProjections projections = new DerivedMetricProjections(projectionInfo); + // The case where the desired custom dimension property is not in the request + RequestDataColumns notContained = new RequestDataColumns("https://test.com/hiThere", 200000L, 200, true, + "GET /hiThere", new HashMap<>(), new HashMap<>()); + projections.calculateProjection(dmiRequest, notContained); + + // The case where the value of the desired custom dim property can't be parsed to a double Map customDims = new HashMap<>(); - RequestDataColumns request1 = new RequestDataColumns("https://test.com/hiThere", 200000L, 200, true, - "GET /hiThere", customDims, new HashMap<>()); customDims.put("property", "hi"); - RequestDataColumns request2 = new RequestDataColumns("https://test.com/hiThere", 200000L, 200, true, - "GET /hiThere", customDims, new HashMap<>()); - customDims.put("property", "5"); - RequestDataColumns request3 = new RequestDataColumns("https://test.com/hiThere", 200000L, 200, true, - "GET /hiThere", customDims, new HashMap<>()); - customDims.put("property", "10"); - RequestDataColumns request4 = new RequestDataColumns("https://test.com/hiThere", 200000L, 200, true, - "GET /hiThere", customDims, new HashMap<>()); - customDims.put("property", "15"); - RequestDataColumns request5 = new RequestDataColumns("https://test.com/hiThere", 200000L, 200, true, - "GET /hiThere", customDims, new HashMap<>()); - customDims.put("property", "1"); - RequestDataColumns request6 = new RequestDataColumns("https://test.com/hiThere", 200000L, 200, true, - "GET /hiThere", customDims, new HashMap<>()); - customDims.put("property", "20"); - RequestDataColumns request7 = new RequestDataColumns("https://test.com/hiThere", 200000L, 200, true, + RequestDataColumns notDouble = new RequestDataColumns("https://test.com/hiThere", 200000L, 200, true, "GET /hiThere", customDims, new HashMap<>()); + projections.calculateProjection(dmiRequest, notDouble); - // custom dim does not exist in current request - should not count - projections.calculateProjection(dmiRequestAvg, request1); + // invalid values should not be counted. + Map finalValues = projections.fetchFinalDerivedMetricValues(); + assertEquals(finalValues.get("request-avg"), 0.0); + } + + private List createRequestsOfDurations(List durations) { + List result = new ArrayList<>(); + for (int i = 0; i < durations.size(); i++) { + result.add(new RequestDataColumns("https://test.com/hiThere", durations.get(i), 200, true, "GET /hiThere", + new HashMap<>(), new HashMap<>())); + } + return result; + } + + private List createDepsOfDurations(List durations) { + List result = new ArrayList<>(); + for (int i = 0; i < durations.size(); i++) { + result.add(new DependencyDataColumns("test.com", durations.get(i), true, "GET /hiThere", 200, "HTTP", + "https://test.com/hiThere?x=y", new HashMap<>(), new HashMap<>())); + } + return result; + } + + private void testDurationProjectionWith(String requestId, String depedencyId, AggregationType aggregationType, + double expectedValue) { + DerivedMetricInfo dmiRequest = createDerivedMetricInfoWithEmptyFilters(requestId, "Request", aggregationType, + aggregationType, KnownRequestColumns.DURATION); - // custom dim exists in current request, but does not have a value that converts to a double - should not count - projections.calculateProjection(dmiRequestAvg, request2); + DerivedMetricInfo dmiDep = createDerivedMetricInfoWithEmptyFilters(depedencyId, "Dependency", aggregationType, + aggregationType, KnownRequestColumns.DURATION); - // custom dim - avg - projections.calculateProjection(dmiRequestAvg, request3); - projections.calculateProjection(dmiRequestAvg, request4); - projections.calculateProjection(dmiRequestAvg, request5); + List durations = asList(200000L, 400000L, 600000L); + List requests = createRequestsOfDurations(durations); + List depedencies = createDepsOfDurations(durations); + + Map projectionInfo = new HashMap<>(); + projectionInfo.put(requestId, aggregationType); + projectionInfo.put(depedencyId, aggregationType); - // custom dim - min - projections.calculateProjection(dmiRequestMin, request5); - projections.calculateProjection(dmiRequestMin, request6); - projections.calculateProjection(dmiRequestMin, request7); + DerivedMetricProjections projections = new DerivedMetricProjections(projectionInfo); - // custom dim - max - projections.calculateProjection(dmiRequestMax, request7); - projections.calculateProjection(dmiRequestMax, request6); - projections.calculateProjection(dmiRequestMax, request5); + // request duration + for (RequestDataColumns request : requests) { + projections.calculateProjection(dmiRequest, request); + } - // custom dim - sum - projections.calculateProjection(dmiRequestSum, request5); - projections.calculateProjection(dmiRequestSum, request6); - projections.calculateProjection(dmiRequestSum, request5); + // dep duration + for (DependencyDataColumns dep : depedencies) { + projections.calculateProjection(dmiDep, dep); + } Map finalValues = projections.fetchFinalDerivedMetricValues(); - assertEquals(finalValues.get("request-avg"), 10.0); - assertEquals(finalValues.get("request-min"), 1.0); - assertEquals(finalValues.get("request-max"), 20.0); - assertEquals(finalValues.get("request-sum"), 31.0); + assertEquals(finalValues.get(requestId), expectedValue); + assertEquals(finalValues.get(depedencyId), expectedValue); + } + + private void testCustomDimProjectionWith(String requestId, AggregationType aggregationType, double expectedValue) { + DerivedMetricInfo dmiRequest = createDerivedMetricInfoWithEmptyFilters(requestId, "Request", aggregationType, + aggregationType, "CustomDimensions.property"); + + Map projectionInfo = new HashMap<>(); + projectionInfo.put(requestId, aggregationType); + DerivedMetricProjections projections = new DerivedMetricProjections(projectionInfo); + + List customDimValues = asList("5.0", "15.0", "4.0"); + List requests = createRequestsWithCustomDimValues(customDimValues); + + for (RequestDataColumns request : requests) { + projections.calculateProjection(dmiRequest, request); + } + + Map finalValues = projections.fetchFinalDerivedMetricValues(); + assertEquals(finalValues.get(requestId), expectedValue); + } + + private List createRequestsWithCustomDimValues(List customDimValues) { + List result = new ArrayList<>(); + for (String value : customDimValues) { + Map customDims = new HashMap<>(); + customDims.put("property", value); + result.add(new RequestDataColumns("https://test.com/hiThere", 200000L, 200, true, "GET /hiThere", + customDims, new HashMap<>())); + } + return result; } } From a6ab9362acc8dc333fa786a7bc24fe664519a04e Mon Sep 17 00:00:00 2001 From: "Harsimar Kaur (from Dev Box)" Date: Fri, 13 Dec 2024 15:27:37 -0800 Subject: [PATCH 09/20] fixing inconsistency with main re okio --- .../azure-monitor-opentelemetry-autoconfigure/pom.xml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/pom.xml b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/pom.xml index 53244901d1eb8..83b427f50cc65 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/pom.xml +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/pom.xml @@ -180,12 +180,6 @@ 2.9.3 test - - com.squareup.okio - okio - 3.9.1 - test - com.google.guava guava From 494a1b9d111b9de20196c1bb76394d059acbdabd Mon Sep 17 00:00:00 2001 From: "Harsimar Kaur (from Dev Box)" Date: Mon, 16 Dec 2024 11:03:06 -0800 Subject: [PATCH 10/20] pr comments & small refactorings --- .../pom.xml | 11 ++-- .../DerivedMetricProjectionsTest.java | 65 ++++++++++++------- 2 files changed, 45 insertions(+), 31 deletions(-) diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/pom.xml b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/pom.xml index 83b427f50cc65..4ce76e8821a64 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/pom.xml +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/pom.xml @@ -94,6 +94,11 @@ opentelemetry-semconv-incubating 1.26.0-alpha + + com.google.guava + guava + 33.1.0-jre + test - - com.google.guava - guava - 33.1.0-jre - compile - diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/test/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filteringTest/DerivedMetricProjectionsTest.java b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/test/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filteringTest/DerivedMetricProjectionsTest.java index 01eb789c646ba..9d80e004b3d28 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/test/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filteringTest/DerivedMetricProjectionsTest.java +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/test/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filteringTest/DerivedMetricProjectionsTest.java @@ -15,9 +15,16 @@ import static java.util.Arrays.asList; import static org.junit.jupiter.api.Assertions.assertEquals; -public class DerivedMetricProjectionsTest { +class DerivedMetricProjectionsTest { + + public static final String REQUEST_AVG = "request-avg"; + public static final String REQUEST = "Request"; + public static final String DEPENDENCY = "Dependency"; + public static final String EXCEPTION = "Exception"; + public static final String TRACE = "Trace"; + private DerivedMetricInfo createDerivedMetricInfoWithEmptyFilters(String id, String telemetryType, - AggregationType agg, AggregationType backendAgg, String projection) { + AggregationType agg, AggregationType backendAgg, String projection) { DerivedMetricInfo result = new DerivedMetricInfo(); result.setId(id); result.setTelemetryType(telemetryType); @@ -35,13 +42,21 @@ private DerivedMetricInfo createDerivedMetricInfoWithEmptyFilters(String id, Str @Test void testCountProjection() { - DerivedMetricInfo dmiRequest = createDerivedMetricInfoWithEmptyFilters("id-for-request", "Request", + String requestId = "id-for-request"; + String dependencyId = "id-for-dependency"; + String exceptionId = "id-for-exception"; + String traceId = "id-for-trace"; + + DerivedMetricInfo dmiRequest = createDerivedMetricInfoWithEmptyFilters(requestId, REQUEST, AggregationType.SUM, AggregationType.SUM, DerivedMetricProjections.COUNT); - DerivedMetricInfo dmiDep = createDerivedMetricInfoWithEmptyFilters("id-for-dependency", "Dependency", + + DerivedMetricInfo dmiDep = createDerivedMetricInfoWithEmptyFilters(dependencyId, DEPENDENCY, AggregationType.SUM, AggregationType.SUM, DerivedMetricProjections.COUNT); - DerivedMetricInfo dmiException = createDerivedMetricInfoWithEmptyFilters("id-for-exception", "Exception", + + DerivedMetricInfo dmiException = createDerivedMetricInfoWithEmptyFilters(exceptionId, EXCEPTION, AggregationType.SUM, AggregationType.SUM, DerivedMetricProjections.COUNT); - DerivedMetricInfo dmiTrace = createDerivedMetricInfoWithEmptyFilters("id-for-trace", "Trace", + + DerivedMetricInfo dmiTrace = createDerivedMetricInfoWithEmptyFilters(traceId, TRACE, AggregationType.SUM, AggregationType.SUM, DerivedMetricProjections.COUNT); RequestDataColumns request = new RequestDataColumns("https://test.com/hiThere", 200000L, 200, true, @@ -53,10 +68,10 @@ void testCountProjection() { TraceDataColumns trace = new TraceDataColumns("Message", new HashMap<>(), new HashMap<>()); Map projectionInfo = new HashMap<>(); - projectionInfo.put("id-for-request", AggregationType.SUM); - projectionInfo.put("id-for-dependency", AggregationType.SUM); - projectionInfo.put("id-for-exception", AggregationType.SUM); - projectionInfo.put("id-for-trace", AggregationType.SUM); + projectionInfo.put(requestId, AggregationType.SUM); + projectionInfo.put(dependencyId, AggregationType.SUM); + projectionInfo.put(exceptionId, AggregationType.SUM); + projectionInfo.put(traceId, AggregationType.SUM); DerivedMetricProjections projections = new DerivedMetricProjections(projectionInfo); for (int i = 0; i < 2; i++) { @@ -74,15 +89,15 @@ void testCountProjection() { projections.calculateProjection(dmiException, exception); Map finalValues = projections.fetchFinalDerivedMetricValues(); - assertEquals(finalValues.get("id-for-request"), 2.0); - assertEquals(finalValues.get("id-for-dependency"), 3.0); - assertEquals(finalValues.get("id-for-exception"), 1.0); - assertEquals(finalValues.get("id-for-trace"), 4.0); + assertEquals(finalValues.get(requestId), 2.0); + assertEquals(finalValues.get(dependencyId), 3.0); + assertEquals(finalValues.get(exceptionId), 1.0); + assertEquals(finalValues.get(traceId), 4.0); } @Test void testDurationAvgProjection() { - testDurationProjectionWith("request-avg", "dependency-avg", AggregationType.AVG, 400.0); + testDurationProjectionWith(REQUEST_AVG, "dependency-avg", AggregationType.AVG, 400.0); } @Test @@ -97,7 +112,7 @@ void testDurationMaxProjection() { @Test void testCustomDimensionAvgProjection() { - testCustomDimProjectionWith("request-avg", AggregationType.AVG, 8.0); + testCustomDimProjectionWith(REQUEST_AVG, AggregationType.AVG, 8.0); } @Test @@ -117,11 +132,11 @@ void testCustomDimensionSumProjection() { @Test void testInvalidCustomDimensionProjection() { - DerivedMetricInfo dmiRequest = createDerivedMetricInfoWithEmptyFilters("request-avg", "Request", + DerivedMetricInfo dmiRequest = createDerivedMetricInfoWithEmptyFilters(REQUEST_AVG, REQUEST, AggregationType.AVG, AggregationType.AVG, "CustomDimensions.property"); Map projectionInfo = new HashMap<>(); - projectionInfo.put("request-avg", AggregationType.AVG); + projectionInfo.put(REQUEST_AVG, AggregationType.AVG); DerivedMetricProjections projections = new DerivedMetricProjections(projectionInfo); // The case where the desired custom dimension property is not in the request @@ -138,7 +153,7 @@ void testInvalidCustomDimensionProjection() { // invalid values should not be counted. Map finalValues = projections.fetchFinalDerivedMetricValues(); - assertEquals(finalValues.get("request-avg"), 0.0); + assertEquals(finalValues.get(REQUEST_AVG), 0.0); } private List createRequestsOfDurations(List durations) { @@ -159,12 +174,12 @@ private List createDepsOfDurations(List durations) return result; } - private void testDurationProjectionWith(String requestId, String depedencyId, AggregationType aggregationType, + private void testDurationProjectionWith(String requestId, String dependencyId, AggregationType aggregationType, double expectedValue) { - DerivedMetricInfo dmiRequest = createDerivedMetricInfoWithEmptyFilters(requestId, "Request", aggregationType, + DerivedMetricInfo dmiRequest = createDerivedMetricInfoWithEmptyFilters(requestId, REQUEST, aggregationType, aggregationType, KnownRequestColumns.DURATION); - DerivedMetricInfo dmiDep = createDerivedMetricInfoWithEmptyFilters(depedencyId, "Dependency", aggregationType, + DerivedMetricInfo dmiDep = createDerivedMetricInfoWithEmptyFilters(dependencyId, DEPENDENCY, aggregationType, aggregationType, KnownRequestColumns.DURATION); List durations = asList(200000L, 400000L, 600000L); @@ -173,7 +188,7 @@ private void testDurationProjectionWith(String requestId, String depedencyId, Ag Map projectionInfo = new HashMap<>(); projectionInfo.put(requestId, aggregationType); - projectionInfo.put(depedencyId, aggregationType); + projectionInfo.put(dependencyId, aggregationType); DerivedMetricProjections projections = new DerivedMetricProjections(projectionInfo); @@ -189,11 +204,11 @@ private void testDurationProjectionWith(String requestId, String depedencyId, Ag Map finalValues = projections.fetchFinalDerivedMetricValues(); assertEquals(finalValues.get(requestId), expectedValue); - assertEquals(finalValues.get(depedencyId), expectedValue); + assertEquals(finalValues.get(dependencyId), expectedValue); } private void testCustomDimProjectionWith(String requestId, AggregationType aggregationType, double expectedValue) { - DerivedMetricInfo dmiRequest = createDerivedMetricInfoWithEmptyFilters(requestId, "Request", aggregationType, + DerivedMetricInfo dmiRequest = createDerivedMetricInfoWithEmptyFilters(requestId, REQUEST, aggregationType, aggregationType, "CustomDimensions.property"); Map projectionInfo = new HashMap<>(); From 1351d2baf1ba4b4617a9787d0fb8764faafa6e69 Mon Sep 17 00:00:00 2001 From: "Harsimar Kaur (from Dev Box)" Date: Mon, 16 Dec 2024 14:42:30 -0800 Subject: [PATCH 11/20] remove logging, spotless --- .../quickpulse/QuickPulseDataCollector.java | 12 ------------ .../filteringTest/DerivedMetricProjectionsTest.java | 10 +++++----- 2 files changed, 5 insertions(+), 17 deletions(-) diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/QuickPulseDataCollector.java b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/QuickPulseDataCollector.java index 7adbea0d56810..9127b169ce6d0 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/QuickPulseDataCollector.java +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/QuickPulseDataCollector.java @@ -3,7 +3,6 @@ package com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse; -import com.azure.core.util.logging.ClientLogger; import com.azure.monitor.opentelemetry.autoconfigure.implementation.models.MonitorDomain; import com.azure.monitor.opentelemetry.autoconfigure.implementation.models.RemoteDependencyData; import com.azure.monitor.opentelemetry.autoconfigure.implementation.models.RequestData; @@ -33,7 +32,6 @@ import com.azure.monitor.opentelemetry.autoconfigure.implementation.utils.CpuPerformanceCounterCalculator; import reactor.util.annotation.Nullable; -import java.io.IOException; import java.lang.management.ManagementFactory; import java.lang.management.MemoryMXBean; import java.lang.management.MemoryUsage; @@ -61,8 +59,6 @@ final class QuickPulseDataCollector { private volatile Supplier instrumentationKeySupplier; - private static final ClientLogger logger = new ClientLogger(QuickPulseDataCollector.class); - // TODO (harskaur): Track projection (runtime) related errors in future PR private final AtomicReference configuration; @@ -181,15 +177,7 @@ private boolean matchesDocumentFilters(TelemetryColumns columns, TelemetryType t private void applyMetricFilters(TelemetryColumns columns, TelemetryType telemetryType, FilteringConfiguration currentConfig, Counters currentCounters) { - // TODO (harskaur): when this PR is merged, remove logging (it is for manual testing & making sure the build does not complain about useless methods) List metricsConfig = currentConfig.fetchMetricConfigForTelemetryType(telemetryType); - try { - for (DerivedMetricInfo dmi : metricsConfig) { - logger.verbose(dmi.toJsonString()); - } - } catch (IOException e) { - logger.error(e.getMessage()); - } for (DerivedMetricInfo derivedMetricInfo : metricsConfig) { if (Filter.checkMetricFilters(derivedMetricInfo, columns)) { synchronized (currentCounters.derivedMetrics) { diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/test/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filteringTest/DerivedMetricProjectionsTest.java b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/test/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filteringTest/DerivedMetricProjectionsTest.java index 9d80e004b3d28..7b3c164380cb1 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/test/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filteringTest/DerivedMetricProjectionsTest.java +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/test/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filteringTest/DerivedMetricProjectionsTest.java @@ -24,7 +24,7 @@ class DerivedMetricProjectionsTest { public static final String TRACE = "Trace"; private DerivedMetricInfo createDerivedMetricInfoWithEmptyFilters(String id, String telemetryType, - AggregationType agg, AggregationType backendAgg, String projection) { + AggregationType agg, AggregationType backendAgg, String projection) { DerivedMetricInfo result = new DerivedMetricInfo(); result.setId(id); result.setTelemetryType(telemetryType); @@ -47,8 +47,8 @@ void testCountProjection() { String exceptionId = "id-for-exception"; String traceId = "id-for-trace"; - DerivedMetricInfo dmiRequest = createDerivedMetricInfoWithEmptyFilters(requestId, REQUEST, - AggregationType.SUM, AggregationType.SUM, DerivedMetricProjections.COUNT); + DerivedMetricInfo dmiRequest = createDerivedMetricInfoWithEmptyFilters(requestId, REQUEST, AggregationType.SUM, + AggregationType.SUM, DerivedMetricProjections.COUNT); DerivedMetricInfo dmiDep = createDerivedMetricInfoWithEmptyFilters(dependencyId, DEPENDENCY, AggregationType.SUM, AggregationType.SUM, DerivedMetricProjections.COUNT); @@ -56,8 +56,8 @@ void testCountProjection() { DerivedMetricInfo dmiException = createDerivedMetricInfoWithEmptyFilters(exceptionId, EXCEPTION, AggregationType.SUM, AggregationType.SUM, DerivedMetricProjections.COUNT); - DerivedMetricInfo dmiTrace = createDerivedMetricInfoWithEmptyFilters(traceId, TRACE, - AggregationType.SUM, AggregationType.SUM, DerivedMetricProjections.COUNT); + DerivedMetricInfo dmiTrace = createDerivedMetricInfoWithEmptyFilters(traceId, TRACE, AggregationType.SUM, + AggregationType.SUM, DerivedMetricProjections.COUNT); RequestDataColumns request = new RequestDataColumns("https://test.com/hiThere", 200000L, 200, true, "GET /hiThere", new HashMap<>(), new HashMap<>()); From a77adfe0b1ae64467e11345c90dee860e4e84279 Mon Sep 17 00:00:00 2001 From: "Harsimar Kaur (from Dev Box)" Date: Mon, 16 Dec 2024 16:46:52 -0800 Subject: [PATCH 12/20] validator --- .../filtering/DuplicateMetricIdException.java | 9 ++ .../filtering/FilteringConfiguration.java | 114 ++++++++++++++++++ .../MetricFailureToCreateException.java | 9 ++ .../filtering/TelemetryTypeException.java | 9 ++ .../UnexpectedFilterCreateException.java | 9 ++ 5 files changed, 150 insertions(+) create mode 100644 sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/DuplicateMetricIdException.java create mode 100644 sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/MetricFailureToCreateException.java create mode 100644 sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/TelemetryTypeException.java create mode 100644 sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/UnexpectedFilterCreateException.java diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/DuplicateMetricIdException.java b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/DuplicateMetricIdException.java new file mode 100644 index 0000000000000..6c6eb054dba29 --- /dev/null +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/DuplicateMetricIdException.java @@ -0,0 +1,9 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.filtering; + +public class DuplicateMetricIdException extends Exception { + public DuplicateMetricIdException(String message) { + super(message); + } +} diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/FilteringConfiguration.java b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/FilteringConfiguration.java index cde93b9b4978e..30b9177075d7d 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/FilteringConfiguration.java +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/FilteringConfiguration.java @@ -9,6 +9,8 @@ import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.DocumentFilterConjunctionGroupInfo; import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.AggregationType; import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.TelemetryType; +import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.PredicateType; +import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.FilterInfo; import java.util.Set; import java.util.List; @@ -16,6 +18,7 @@ import java.util.Map; import java.util.HashSet; import java.util.HashMap; +import static java.util.Arrays.asList; public class FilteringConfiguration { private final Set seenMetricIds = new HashSet<>(); @@ -134,4 +137,115 @@ private Map initValidProjectionInfo() { return result; } + public static class Validator { + private static final Set knownStringColumns + = new HashSet(asList(KnownRequestColumns.URL, KnownRequestColumns.NAME, KnownDependencyColumns.DATA, + KnownDependencyColumns.TARGET, KnownDependencyColumns.TYPE, KnownTraceColumns.MESSAGE, + KnownExceptionColumns.MESSAGE, KnownExceptionColumns.STACK)); + + private static final Set knownNumericColumns = new HashSet<>(asList(KnownRequestColumns.RESPONSE_CODE, + KnownRequestColumns.DURATION, KnownDependencyColumns.RESULT_CODE)); + + private static final Set validStringPredicates = new HashSet<>(asList(PredicateType.CONTAINS, + PredicateType.DOES_NOT_CONTAIN, PredicateType.EQUAL, PredicateType.NOT_EQUAL)); + + public static void validateTelemetryType(TelemetryType telemetryType) throws TelemetryTypeException { + if (telemetryType.equals(TelemetryType.PERFORMANCE_COUNTER)) { + throw new TelemetryTypeException( + "The telemetry type PerformanceCounter was specified, but the distro does not send performance counters other than CPU/Mem to quickpulse"); + } else if (telemetryType.equals(TelemetryType.EVENT)) { + throw new TelemetryTypeException( + "The telemetry type Event was specified, but the distro does not send events to quickpulse"); + } else if (telemetryType.equals(TelemetryType.METRIC)) { + throw new TelemetryTypeException( + "The telemetry type Metric was specified, but the distro does not support sending open telemetry metrics to quickpulse"); + } + } + + public static void checkCustomMetricProjection(DerivedMetricInfo derivedMetricInfo) + throws UnexpectedFilterCreateException { + if (derivedMetricInfo.getProjection().startsWith("CustomMetrics.")) { + throw new UnexpectedFilterCreateException( + "The projection of a customMetric property is not supported in this distro."); + } + } + + public static void validateMetricFilters(DerivedMetricInfo derivedMetricInfo) + throws UnexpectedFilterCreateException { + for (FilterConjunctionGroupInfo conjunctionGroupInfo : derivedMetricInfo.getFilterGroups()) { + for (FilterInfo filter : conjunctionGroupInfo.getFilters()) { + TelemetryType telemetryType = TelemetryType.fromString(derivedMetricInfo.getTelemetryType()); + validateFieldNames(filter.getFieldName(), telemetryType); + validatePredicateAndComparand(filter); + } + } + } + + public static void + validateDocumentFilters(DocumentFilterConjunctionGroupInfo documentFilterConjunctionGroupInfo) + throws UnexpectedFilterCreateException { + FilterConjunctionGroupInfo conjunctionGroupInfo = documentFilterConjunctionGroupInfo.getFilters(); + for (FilterInfo filter : conjunctionGroupInfo.getFilters()) { + validateFieldNames(filter.getFieldName(), documentFilterConjunctionGroupInfo.getTelemetryType()); + validatePredicateAndComparand(filter); + } + } + + private static boolean isCustomDimOrAnyField(String fieldName) { + return fieldName.startsWith(Filter.CUSTOM_DIM_FIELDNAME_PREFIX) || fieldName.equals(Filter.ANY_FIELD); + } + + private static void validateFieldNames(String fieldName, TelemetryType telemetryType) + throws UnexpectedFilterCreateException { + if (fieldName.isEmpty()) { + throw new UnexpectedFilterCreateException("A filter must have a non-empty field name."); + } + if (fieldName.startsWith("CustomMetrics.")) { + throw new UnexpectedFilterCreateException( + "Filtering of a customMetric property is not supported in this distro."); + } + } + + private static void validatePredicateAndComparand(FilterInfo filter) throws UnexpectedFilterCreateException { + if (filter.getComparand().isEmpty()) { + // It is possible to not type in a comparand and the service side to send us empty string. + throw new UnexpectedFilterCreateException("A filter must have a non-empty comparand. FilterName: " + + filter.getFieldName() + " Predicate: " + filter.getPredicate().getValue()); + } else if (Filter.ANY_FIELD.equals(filter.getFieldName()) + && !(filter.getPredicate().equals(PredicateType.CONTAINS) + || filter.getPredicate().equals(PredicateType.DOES_NOT_CONTAIN))) { + // While the UI allows != and == for the ANY_FIELD fieldName, .net classic code only allows contains/not contains & the spec follows + // .net classic behavior for this particular condition. + throw new UnexpectedFilterCreateException( + "The predicate " + filter.getPredicate().getValue() + " is not supported for the field name *"); + } else if (knownNumericColumns.contains(filter.getFieldName())) { + + // Just in case a strange timestamp value is passed from the service side. The service side should send a duration with a specific + // format ([days].[hours]:[minutes]:[seconds] - the seconds may be a whole number or something like 7.89). + if (KnownDependencyColumns.DURATION.equals(filter.getFieldName())) { + if (Filter.getMicroSecondsFromFilterTimestampString(filter.getComparand()) == Long.MIN_VALUE) { + throw new UnexpectedFilterCreateException( + "The provided duration timestamp can't be converted to microseconds: " + filter.getComparand()); + } + } else { // The service side not does not validate if resultcode or responsecode is a numeric value + try { + Long.parseLong(filter.getComparand()); + } catch (NumberFormatException e) { + throw new UnexpectedFilterCreateException( + "Could not convert the provided result/response code to a numeric value: " + + filter.getComparand()); + } + } + } else if (knownStringColumns.contains(filter.getFieldName()) + || filter.getFieldName().startsWith(Filter.CUSTOM_DIM_FIELDNAME_PREFIX)) { + // While the UI allows a user to select any predicate for a custom dimension filter, .net classic treats all custom dimensions like + // String values. therefore we validate for predicates applicable to String. This is called out in the spec as well. + if (!validStringPredicates.contains(filter.getPredicate())) { + throw new UnexpectedFilterCreateException("The predicate " + filter.getPredicate().getValue() + + " is not supported for the field " + filter.getFieldName()); + } + } + } + } + } diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/MetricFailureToCreateException.java b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/MetricFailureToCreateException.java new file mode 100644 index 0000000000000..df93098fba8f7 --- /dev/null +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/MetricFailureToCreateException.java @@ -0,0 +1,9 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.filtering; + +public class MetricFailureToCreateException extends Exception { + public MetricFailureToCreateException(String message) { + super(message); + } +} diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/TelemetryTypeException.java b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/TelemetryTypeException.java new file mode 100644 index 0000000000000..714a3bb9952dd --- /dev/null +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/TelemetryTypeException.java @@ -0,0 +1,9 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.filtering; + +public class TelemetryTypeException extends Exception { + public TelemetryTypeException(String message) { + super(message); + } +} diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/UnexpectedFilterCreateException.java b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/UnexpectedFilterCreateException.java new file mode 100644 index 0000000000000..f8bc94b4969e0 --- /dev/null +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/UnexpectedFilterCreateException.java @@ -0,0 +1,9 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.filtering; + +public class UnexpectedFilterCreateException extends Exception { + public UnexpectedFilterCreateException(String message) { + super(message); + } +} From 68b9c839354065bbc24d7d5672324a46ba7501fa Mon Sep 17 00:00:00 2001 From: "Harsimar Kaur (from Dev Box)" Date: Tue, 17 Dec 2024 13:26:11 -0800 Subject: [PATCH 13/20] minor --- .../quickpulse/filtering/DerivedMetricProjections.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/DerivedMetricProjections.java b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/DerivedMetricProjections.java index e7e7ac6a2676b..10a57ac34c29f 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/DerivedMetricProjections.java +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/DerivedMetricProjections.java @@ -88,7 +88,7 @@ private void calculateAggregation(AggregationType type, String id, double increm } } - static class DerivedMetricAggregation { + private static class DerivedMetricAggregation { // This class represents the intermediate state of a derived metric value. // It keeps track of the count and the aggregated value so that these two // fields can be used to determine the final value of a derived metric From d80053ec47958d1aabdf00880abd12904b3115e7 Mon Sep 17 00:00:00 2001 From: "Harsimar Kaur (from Dev Box)" Date: Tue, 17 Dec 2024 14:25:15 -0800 Subject: [PATCH 14/20] changes to concurrency handling --- .../checkstyle-suppressions.xml | 1 + .../pom.xml | 6 -- .../quickpulse/QuickPulseDataCollector.java | 11 ++-- .../filtering/DerivedMetricProjections.java | 61 ++++++++++--------- .../src/main/java/module-info.java | 1 - 5 files changed, 37 insertions(+), 43 deletions(-) diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/checkstyle-suppressions.xml b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/checkstyle-suppressions.xml index 79c361ecf4125..e2453089401ab 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/checkstyle-suppressions.xml +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/checkstyle-suppressions.xml @@ -309,6 +309,7 @@ + diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/pom.xml b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/pom.xml index 4ce76e8821a64..ef94452e46463 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/pom.xml +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/pom.xml @@ -94,11 +94,6 @@ opentelemetry-semconv-incubating 1.26.0-alpha - - com.google.guava - guava - 33.1.0-jre - io.opentelemetry.semconv:opentelemetry-semconv-incubating:[1.26.0-alpha] com.squareup.okio:okio:[3.9.1] - com.google.guava:guava:[33.1.0-jre] diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/QuickPulseDataCollector.java b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/QuickPulseDataCollector.java index 9127b169ce6d0..18a5d052368f2 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/QuickPulseDataCollector.java +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/QuickPulseDataCollector.java @@ -180,10 +180,8 @@ private void applyMetricFilters(TelemetryColumns columns, TelemetryType telemetr List metricsConfig = currentConfig.fetchMetricConfigForTelemetryType(telemetryType); for (DerivedMetricInfo derivedMetricInfo : metricsConfig) { if (Filter.checkMetricFilters(derivedMetricInfo, columns)) { - synchronized (currentCounters.derivedMetrics) { - // TODO (harskaur): In future PR, track any error that comes from calculateProjection - currentCounters.derivedMetrics.calculateProjection(derivedMetricInfo, columns); - } + // TODO (harskaur): In future PR, track any error that comes from calculateProjection + currentCounters.derivedMetrics.calculateProjection(derivedMetricInfo, columns); } } } @@ -432,9 +430,8 @@ private FinalCounters(Counters currentCounters) { synchronized (currentCounters.documentList) { this.documentList.addAll(currentCounters.documentList); } - synchronized (currentCounters.derivedMetrics) { - this.projections = currentCounters.derivedMetrics.fetchFinalDerivedMetricValues(); - } + this.projections = currentCounters.derivedMetrics.fetchFinalDerivedMetricValues(); + } private long getPhysicalMemory(@Nullable MemoryMXBean memory) { diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/DerivedMetricProjections.java b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/DerivedMetricProjections.java index e7e7ac6a2676b..f31bb27b02f7d 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/DerivedMetricProjections.java +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/DerivedMetricProjections.java @@ -5,25 +5,24 @@ import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.AggregationType; import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.DerivedMetricInfo; -import com.google.common.util.concurrent.AtomicDouble; import java.util.HashMap; import java.util.Map; -import java.util.concurrent.atomic.AtomicLong; public class DerivedMetricProjections { public static final String COUNT = "Count()"; private final Map derivedMetricValues = new HashMap<>(); + private final Object lock = new Object(); public DerivedMetricProjections(Map projectionInfo) { for (Map.Entry entry : projectionInfo.entrySet()) { AggregationType aggregationType = entry.getValue(); DerivedMetricAggregation value; if (aggregationType.equals(AggregationType.MIN)) { - value = new DerivedMetricAggregation(Long.MAX_VALUE, aggregationType); + value = new DerivedMetricAggregation(Double.MAX_VALUE, aggregationType); } else if (aggregationType.equals(AggregationType.MAX)) { - value = new DerivedMetricAggregation(Long.MIN_VALUE, aggregationType); + value = new DerivedMetricAggregation(Double.MIN_VALUE, aggregationType); } else if (aggregationType.equals(AggregationType.SUM) || aggregationType.equals(AggregationType.AVG)) { value = new DerivedMetricAggregation(0, aggregationType); } else { @@ -36,17 +35,19 @@ public DerivedMetricProjections(Map projectionInfo) { // This is intended to be called once for every post request public Map fetchFinalDerivedMetricValues() { Map result = new HashMap<>(); - for (Map.Entry entry : derivedMetricValues.entrySet()) { - String id = entry.getKey(); - DerivedMetricAggregation dma = entry.getValue(); - double intermediateValue = dma.aggregation.get(); - long count = dma.count.get(); - if (count == 0) { - result.put(id, 0.0); - } else if (dma.aggregationType.equals(AggregationType.AVG)) { - result.put(id, intermediateValue / count); - } else { - result.put(id, intermediateValue); + synchronized (lock) { + for (Map.Entry entry : derivedMetricValues.entrySet()) { + String id = entry.getKey(); + DerivedMetricAggregation dma = entry.getValue(); + double intermediateValue = dma.aggregation; + long count = dma.count; + if (count == 0) { + result.put(id, 0.0); + } else if (dma.aggregationType.equals(AggregationType.AVG)) { + result.put(id, intermediateValue / count); + } else { + result.put(id, intermediateValue); + } } } return result; @@ -61,7 +62,7 @@ public void calculateProjection(DerivedMetricInfo derivedMetricInfo, TelemetryCo } else if (KnownRequestColumns.DURATION.equals(derivedMetricInfo.getProjection())) { long duration = columns.getFieldValue(KnownRequestColumns.DURATION, Long.class); // in case duration from telemetrycolumns doesn't parse correctly. - // also quickpulse expects duration derived metrics to be reported in ms. + // also quickpulse expects duration derived metrics to be reported in millis. incrementBy = duration != -1 ? (double) duration / 1000.0 : Double.NaN; } else if (derivedMetricInfo.getProjection().startsWith(Filter.CUSTOM_DIM_FIELDNAME_PREFIX)) { String customDimKey @@ -77,18 +78,20 @@ public void calculateProjection(DerivedMetricInfo derivedMetricInfo, TelemetryCo } private void calculateAggregation(AggregationType type, String id, double incrementBy) { - DerivedMetricAggregation dma = derivedMetricValues.get(id); - dma.count.getAndAdd(1); - if (type.equals(AggregationType.SUM) || type.equals(AggregationType.AVG)) { - dma.aggregation.getAndAdd(incrementBy); - } else if (type.equals(AggregationType.MIN)) { - dma.aggregation.getAndAccumulate(incrementBy, Math::min); - } else if (type.equals(AggregationType.MAX)) { - dma.aggregation.getAndAccumulate(incrementBy, Math::max); + synchronized (lock) { + DerivedMetricAggregation dma = derivedMetricValues.get(id); + dma.count++; + if (type.equals(AggregationType.SUM) || type.equals(AggregationType.AVG)) { + dma.aggregation += incrementBy; + } else if (type.equals(AggregationType.MIN)) { + dma.aggregation = Math.min(dma.aggregation, incrementBy); + } else if (type.equals(AggregationType.MAX)) { + dma.aggregation = Math.max(dma.aggregation, incrementBy); + } } } - static class DerivedMetricAggregation { + private static class DerivedMetricAggregation { // This class represents the intermediate state of a derived metric value. // It keeps track of the count and the aggregated value so that these two // fields can be used to determine the final value of a derived metric @@ -101,12 +104,12 @@ static class DerivedMetricAggregation { // When metric values are retrieved by the data fetcher, the final value will // be determined based on the count and the aggregation. - final AtomicDouble aggregation; - final AtomicLong count = new AtomicLong(0); + double aggregation; + long count = 0; final AggregationType aggregationType; - DerivedMetricAggregation(long initValue, AggregationType type) { - aggregation = new AtomicDouble(initValue); + DerivedMetricAggregation(double initValue, AggregationType type) { + aggregation = initValue; aggregationType = type; } } diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/module-info.java b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/module-info.java index 3c8bfdba68309..4d249d2d398fa 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/module-info.java +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/module-info.java @@ -19,7 +19,6 @@ requires io.opentelemetry.sdk.trace; requires io.opentelemetry.semconv; requires io.opentelemetry.semconv.incubating; - requires com.google.common; opens com.azure.monitor.opentelemetry.autoconfigure.implementation.models to com.azure.core; opens com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models to com.azure.core; From f83c1169ad19a8dfdfcfda0b5e54095a692c3865 Mon Sep 17 00:00:00 2001 From: "Harsimar Kaur (from Dev Box)" Date: Tue, 17 Dec 2024 15:26:54 -0800 Subject: [PATCH 15/20] moving synchronization to derivedMetricAggregation --- .../checkstyle-suppressions.xml | 1 - .../filtering/DerivedMetricProjections.java | 58 ++++++++++--------- 2 files changed, 32 insertions(+), 27 deletions(-) diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/checkstyle-suppressions.xml b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/checkstyle-suppressions.xml index e2453089401ab..79c361ecf4125 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/checkstyle-suppressions.xml +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/checkstyle-suppressions.xml @@ -309,7 +309,6 @@ - diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/DerivedMetricProjections.java b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/DerivedMetricProjections.java index f31bb27b02f7d..9af7b6a3f7de5 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/DerivedMetricProjections.java +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/DerivedMetricProjections.java @@ -13,7 +13,6 @@ public class DerivedMetricProjections { public static final String COUNT = "Count()"; private final Map derivedMetricValues = new HashMap<>(); - private final Object lock = new Object(); public DerivedMetricProjections(Map projectionInfo) { for (Map.Entry entry : projectionInfo.entrySet()) { @@ -35,20 +34,10 @@ public DerivedMetricProjections(Map projectionInfo) { // This is intended to be called once for every post request public Map fetchFinalDerivedMetricValues() { Map result = new HashMap<>(); - synchronized (lock) { - for (Map.Entry entry : derivedMetricValues.entrySet()) { - String id = entry.getKey(); - DerivedMetricAggregation dma = entry.getValue(); - double intermediateValue = dma.aggregation; - long count = dma.count; - if (count == 0) { - result.put(id, 0.0); - } else if (dma.aggregationType.equals(AggregationType.AVG)) { - result.put(id, intermediateValue / count); - } else { - result.put(id, intermediateValue); - } - } + for (Map.Entry entry : derivedMetricValues.entrySet()) { + String id = entry.getKey(); + DerivedMetricAggregation dma = entry.getValue(); + result.put(id, dma.getFinalValue()); } return result; } @@ -78,17 +67,8 @@ public void calculateProjection(DerivedMetricInfo derivedMetricInfo, TelemetryCo } private void calculateAggregation(AggregationType type, String id, double incrementBy) { - synchronized (lock) { - DerivedMetricAggregation dma = derivedMetricValues.get(id); - dma.count++; - if (type.equals(AggregationType.SUM) || type.equals(AggregationType.AVG)) { - dma.aggregation += incrementBy; - } else if (type.equals(AggregationType.MIN)) { - dma.aggregation = Math.min(dma.aggregation, incrementBy); - } else if (type.equals(AggregationType.MAX)) { - dma.aggregation = Math.max(dma.aggregation, incrementBy); - } - } + DerivedMetricAggregation dma = derivedMetricValues.get(id); + dma.update(incrementBy); } private static class DerivedMetricAggregation { @@ -107,10 +87,36 @@ private static class DerivedMetricAggregation { double aggregation; long count = 0; final AggregationType aggregationType; + private final Object lock = new Object(); DerivedMetricAggregation(double initValue, AggregationType type) { aggregation = initValue; aggregationType = type; } + + void update(double incrementBy) { + synchronized (lock) { + count++; + if (aggregationType.equals(AggregationType.SUM) || aggregationType.equals(AggregationType.AVG)) { + aggregation += incrementBy; + } else if (aggregationType.equals(AggregationType.MIN)) { + aggregation = Math.min(aggregation, incrementBy); + } else if (aggregationType.equals(AggregationType.MAX)) { + aggregation = Math.max(aggregation, incrementBy); + } + } + } + + double getFinalValue() { + synchronized (lock) { + if (count == 0) { + return 0.0; + } else if (aggregationType.equals(AggregationType.AVG)) { + return aggregation / count; + } else { + return aggregation; + } + } + } } } From d8b618fb3766b55b6a750c81aa187627da7671ab Mon Sep 17 00:00:00 2001 From: "Harsimar Kaur (from Dev Box)" Date: Tue, 17 Dec 2024 15:52:53 -0800 Subject: [PATCH 16/20] moving derivedMetricAggregation to its own file --- .../checkstyle-suppressions.xml | 1 + .../filtering/DerivedMetricAggregation.java | 55 +++++++++++++++++++ .../filtering/DerivedMetricProjections.java | 48 ---------------- 3 files changed, 56 insertions(+), 48 deletions(-) create mode 100644 sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/DerivedMetricAggregation.java diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/checkstyle-suppressions.xml b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/checkstyle-suppressions.xml index 79c361ecf4125..b7a08594f7efa 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/checkstyle-suppressions.xml +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/checkstyle-suppressions.xml @@ -249,6 +249,7 @@ + diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/DerivedMetricAggregation.java b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/DerivedMetricAggregation.java new file mode 100644 index 0000000000000..2cdee28f09e33 --- /dev/null +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/DerivedMetricAggregation.java @@ -0,0 +1,55 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.filtering; + +import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.AggregationType; + +public class DerivedMetricAggregation { + // This class represents the intermediate state of a derived metric value. + // It keeps track of the count and the aggregated value so that these two + // fields can be used to determine the final value of a derived metric + // when the data fetcher asks for it. + + // Depending on the aggregationType, aggregation holds different values. + // For min, it is the current minimum value + // For max, it is the current max value + // For sum & avg, this represents the current sum. + // When metric values are retrieved by the data fetcher, the final value will + // be determined based on the count and the aggregation. + + private double aggregation; + private long count = 0; + final AggregationType aggregationType; + private final Object lock = new Object(); + + DerivedMetricAggregation(double initValue, AggregationType type) { + aggregation = initValue; + aggregationType = type; + } + + void update(double incrementBy) { + synchronized (lock) { + count++; + if (aggregationType.equals(AggregationType.SUM) || aggregationType.equals(AggregationType.AVG)) { + aggregation += incrementBy; + } else if (aggregationType.equals(AggregationType.MIN)) { + aggregation = Math.min(aggregation, incrementBy); + } else if (aggregationType.equals(AggregationType.MAX)) { + aggregation = Math.max(aggregation, incrementBy); + } + } + } + + double getFinalValue() { + synchronized (lock) { + if (count == 0) { + return 0.0; + } else if (aggregationType.equals(AggregationType.AVG)) { + return aggregation / count; + } else { + return aggregation; + } + } + } +} diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/DerivedMetricProjections.java b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/DerivedMetricProjections.java index 9af7b6a3f7de5..ea17898dacc73 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/DerivedMetricProjections.java +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/DerivedMetricProjections.java @@ -71,52 +71,4 @@ private void calculateAggregation(AggregationType type, String id, double increm dma.update(incrementBy); } - private static class DerivedMetricAggregation { - // This class represents the intermediate state of a derived metric value. - // It keeps track of the count and the aggregated value so that these two - // fields can be used to determine the final value of a derived metric - // when the data fetcher asks for it. - - // Depending on the aggregationType, aggregation holds different values. - // For min, it is the current minimum value - // For max, it is the current max value - // For sum & avg, this represents the current sum. - // When metric values are retrieved by the data fetcher, the final value will - // be determined based on the count and the aggregation. - - double aggregation; - long count = 0; - final AggregationType aggregationType; - private final Object lock = new Object(); - - DerivedMetricAggregation(double initValue, AggregationType type) { - aggregation = initValue; - aggregationType = type; - } - - void update(double incrementBy) { - synchronized (lock) { - count++; - if (aggregationType.equals(AggregationType.SUM) || aggregationType.equals(AggregationType.AVG)) { - aggregation += incrementBy; - } else if (aggregationType.equals(AggregationType.MIN)) { - aggregation = Math.min(aggregation, incrementBy); - } else if (aggregationType.equals(AggregationType.MAX)) { - aggregation = Math.max(aggregation, incrementBy); - } - } - } - - double getFinalValue() { - synchronized (lock) { - if (count == 0) { - return 0.0; - } else if (aggregationType.equals(AggregationType.AVG)) { - return aggregation / count; - } else { - return aggregation; - } - } - } - } } From 96e705fabd3a2a76c63d5a7402b00b0c0e4ad21c Mon Sep 17 00:00:00 2001 From: "Harsimar Kaur (from Dev Box)" Date: Tue, 17 Dec 2024 16:51:27 -0800 Subject: [PATCH 17/20] validator round 1 - need to refactor for validator to store errors --- .../filtering/FilteringConfiguration.java | 169 ++++-------------- .../quickpulse/filtering/Validator.java | 119 ++++++++++++ 2 files changed, 152 insertions(+), 136 deletions(-) create mode 100644 sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/Validator.java diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/FilteringConfiguration.java b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/FilteringConfiguration.java index 30b9177075d7d..ab06f952986d7 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/FilteringConfiguration.java +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/FilteringConfiguration.java @@ -9,8 +9,6 @@ import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.DocumentFilterConjunctionGroupInfo; import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.AggregationType; import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.TelemetryType; -import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.PredicateType; -import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.FilterInfo; import java.util.Set; import java.util.List; @@ -18,7 +16,7 @@ import java.util.Map; import java.util.HashSet; import java.util.HashMap; -import static java.util.Arrays.asList; + public class FilteringConfiguration { private final Set seenMetricIds = new HashSet<>(); @@ -84,20 +82,24 @@ public Map getValidProjectionInitInfo() { TelemetryType telemetryType = documentFilterGroupInfo.getTelemetryType(); FilterConjunctionGroupInfo filterGroup = documentFilterGroupInfo.getFilters(); - // TODO (harskaur): In later PR, validate input before adding it to newValidDocumentsConfig - // TODO (harskaur): If any validator methods throw an exception, catch the exception and track the error for post request body + try { + Validator.validateTelemetryType(telemetryType); + Validator.validateDocumentFilters(documentFilterGroupInfo); - if (!result.containsKey(telemetryType)) { - result.put(telemetryType, new HashMap<>()); - } + if (!result.containsKey(telemetryType)) { + result.put(telemetryType, new HashMap<>()); + } - Map> innerMap = result.get(telemetryType); - if (innerMap.containsKey(documentStreamId)) { - innerMap.get(documentStreamId).add(filterGroup); - } else { - List filterGroups = new ArrayList<>(); - filterGroups.add(filterGroup); - innerMap.put(documentStreamId, filterGroups); + Map> innerMap = result.get(telemetryType); + if (innerMap.containsKey(documentStreamId)) { + innerMap.get(documentStreamId).add(filterGroup); + } else { + List filterGroups = new ArrayList<>(); + filterGroups.add(filterGroup); + innerMap.put(documentStreamId, filterGroups); + } + } catch (TelemetryTypeException | UnexpectedFilterCreateException e) { + // TODO (harskaur): For ErrorTracker PR: If any validator methods throw an exception, catch the exception and track the error for post request body } } } @@ -110,18 +112,24 @@ public Map getValidProjectionInitInfo() { for (DerivedMetricInfo derivedMetricInfo : configuration.getMetrics()) { TelemetryType telemetryType = TelemetryType.fromString(derivedMetricInfo.getTelemetryType()); String id = derivedMetricInfo.getId(); - if (!seenMetricIds.contains(id)) { - seenMetricIds.add(id); - // TODO (harskaur): In later PR, validate input before adding it to newValidConfig - // TODO (harskaur): If any validator methods throw an exception, catch the exception and track the error for post request body - - if (result.containsKey(telemetryType)) { - result.get(telemetryType).add(derivedMetricInfo); + try { + if (!seenMetricIds.contains(id)) { + seenMetricIds.add(id); + Validator.validateTelemetryType(telemetryType); + Validator.checkCustomMetricProjection(derivedMetricInfo); + + if (result.containsKey(telemetryType)) { + result.get(telemetryType).add(derivedMetricInfo); + } else { + List infos = new ArrayList<>(); + infos.add(derivedMetricInfo); + result.put(telemetryType, infos); + } } else { - List infos = new ArrayList<>(); - infos.add(derivedMetricInfo); - result.put(telemetryType, infos); + throw new DuplicateMetricIdException("Duplicate Metric Id: " + id); } + } catch (TelemetryTypeException | UnexpectedFilterCreateException | DuplicateMetricIdException e) { + // TODO (harskaur): For ErrorTracker PR: If any validator methods throw an exception, catch the exception and track the error for post request body } } return result; @@ -137,115 +145,4 @@ private Map initValidProjectionInfo() { return result; } - public static class Validator { - private static final Set knownStringColumns - = new HashSet(asList(KnownRequestColumns.URL, KnownRequestColumns.NAME, KnownDependencyColumns.DATA, - KnownDependencyColumns.TARGET, KnownDependencyColumns.TYPE, KnownTraceColumns.MESSAGE, - KnownExceptionColumns.MESSAGE, KnownExceptionColumns.STACK)); - - private static final Set knownNumericColumns = new HashSet<>(asList(KnownRequestColumns.RESPONSE_CODE, - KnownRequestColumns.DURATION, KnownDependencyColumns.RESULT_CODE)); - - private static final Set validStringPredicates = new HashSet<>(asList(PredicateType.CONTAINS, - PredicateType.DOES_NOT_CONTAIN, PredicateType.EQUAL, PredicateType.NOT_EQUAL)); - - public static void validateTelemetryType(TelemetryType telemetryType) throws TelemetryTypeException { - if (telemetryType.equals(TelemetryType.PERFORMANCE_COUNTER)) { - throw new TelemetryTypeException( - "The telemetry type PerformanceCounter was specified, but the distro does not send performance counters other than CPU/Mem to quickpulse"); - } else if (telemetryType.equals(TelemetryType.EVENT)) { - throw new TelemetryTypeException( - "The telemetry type Event was specified, but the distro does not send events to quickpulse"); - } else if (telemetryType.equals(TelemetryType.METRIC)) { - throw new TelemetryTypeException( - "The telemetry type Metric was specified, but the distro does not support sending open telemetry metrics to quickpulse"); - } - } - - public static void checkCustomMetricProjection(DerivedMetricInfo derivedMetricInfo) - throws UnexpectedFilterCreateException { - if (derivedMetricInfo.getProjection().startsWith("CustomMetrics.")) { - throw new UnexpectedFilterCreateException( - "The projection of a customMetric property is not supported in this distro."); - } - } - - public static void validateMetricFilters(DerivedMetricInfo derivedMetricInfo) - throws UnexpectedFilterCreateException { - for (FilterConjunctionGroupInfo conjunctionGroupInfo : derivedMetricInfo.getFilterGroups()) { - for (FilterInfo filter : conjunctionGroupInfo.getFilters()) { - TelemetryType telemetryType = TelemetryType.fromString(derivedMetricInfo.getTelemetryType()); - validateFieldNames(filter.getFieldName(), telemetryType); - validatePredicateAndComparand(filter); - } - } - } - - public static void - validateDocumentFilters(DocumentFilterConjunctionGroupInfo documentFilterConjunctionGroupInfo) - throws UnexpectedFilterCreateException { - FilterConjunctionGroupInfo conjunctionGroupInfo = documentFilterConjunctionGroupInfo.getFilters(); - for (FilterInfo filter : conjunctionGroupInfo.getFilters()) { - validateFieldNames(filter.getFieldName(), documentFilterConjunctionGroupInfo.getTelemetryType()); - validatePredicateAndComparand(filter); - } - } - - private static boolean isCustomDimOrAnyField(String fieldName) { - return fieldName.startsWith(Filter.CUSTOM_DIM_FIELDNAME_PREFIX) || fieldName.equals(Filter.ANY_FIELD); - } - - private static void validateFieldNames(String fieldName, TelemetryType telemetryType) - throws UnexpectedFilterCreateException { - if (fieldName.isEmpty()) { - throw new UnexpectedFilterCreateException("A filter must have a non-empty field name."); - } - if (fieldName.startsWith("CustomMetrics.")) { - throw new UnexpectedFilterCreateException( - "Filtering of a customMetric property is not supported in this distro."); - } - } - - private static void validatePredicateAndComparand(FilterInfo filter) throws UnexpectedFilterCreateException { - if (filter.getComparand().isEmpty()) { - // It is possible to not type in a comparand and the service side to send us empty string. - throw new UnexpectedFilterCreateException("A filter must have a non-empty comparand. FilterName: " - + filter.getFieldName() + " Predicate: " + filter.getPredicate().getValue()); - } else if (Filter.ANY_FIELD.equals(filter.getFieldName()) - && !(filter.getPredicate().equals(PredicateType.CONTAINS) - || filter.getPredicate().equals(PredicateType.DOES_NOT_CONTAIN))) { - // While the UI allows != and == for the ANY_FIELD fieldName, .net classic code only allows contains/not contains & the spec follows - // .net classic behavior for this particular condition. - throw new UnexpectedFilterCreateException( - "The predicate " + filter.getPredicate().getValue() + " is not supported for the field name *"); - } else if (knownNumericColumns.contains(filter.getFieldName())) { - - // Just in case a strange timestamp value is passed from the service side. The service side should send a duration with a specific - // format ([days].[hours]:[minutes]:[seconds] - the seconds may be a whole number or something like 7.89). - if (KnownDependencyColumns.DURATION.equals(filter.getFieldName())) { - if (Filter.getMicroSecondsFromFilterTimestampString(filter.getComparand()) == Long.MIN_VALUE) { - throw new UnexpectedFilterCreateException( - "The provided duration timestamp can't be converted to microseconds: " + filter.getComparand()); - } - } else { // The service side not does not validate if resultcode or responsecode is a numeric value - try { - Long.parseLong(filter.getComparand()); - } catch (NumberFormatException e) { - throw new UnexpectedFilterCreateException( - "Could not convert the provided result/response code to a numeric value: " - + filter.getComparand()); - } - } - } else if (knownStringColumns.contains(filter.getFieldName()) - || filter.getFieldName().startsWith(Filter.CUSTOM_DIM_FIELDNAME_PREFIX)) { - // While the UI allows a user to select any predicate for a custom dimension filter, .net classic treats all custom dimensions like - // String values. therefore we validate for predicates applicable to String. This is called out in the spec as well. - if (!validStringPredicates.contains(filter.getPredicate())) { - throw new UnexpectedFilterCreateException("The predicate " + filter.getPredicate().getValue() - + " is not supported for the field " + filter.getFieldName()); - } - } - } - } - } diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/Validator.java b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/Validator.java new file mode 100644 index 0000000000000..43bab6e71a1fc --- /dev/null +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/Validator.java @@ -0,0 +1,119 @@ +package com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.filtering; + +import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.*; + +import java.util.HashSet; +import java.util.Set; + +import static java.util.Arrays.asList; + +public class Validator { + private static final Set knownStringColumns + = new HashSet(asList(KnownRequestColumns.URL, KnownRequestColumns.NAME, KnownDependencyColumns.DATA, + KnownDependencyColumns.TARGET, KnownDependencyColumns.TYPE, KnownTraceColumns.MESSAGE, + KnownExceptionColumns.MESSAGE, KnownExceptionColumns.STACK)); + + private static final Set knownNumericColumns = new HashSet<>(asList(KnownRequestColumns.RESPONSE_CODE, + KnownRequestColumns.DURATION, KnownDependencyColumns.RESULT_CODE)); + + private static final Set validStringPredicates = new HashSet<>(asList(PredicateType.CONTAINS, + PredicateType.DOES_NOT_CONTAIN, PredicateType.EQUAL, PredicateType.NOT_EQUAL)); + + public static void validateTelemetryType(TelemetryType telemetryType) throws TelemetryTypeException { + if (telemetryType.equals(TelemetryType.PERFORMANCE_COUNTER)) { + throw new TelemetryTypeException( + "The telemetry type PerformanceCounter was specified, but the distro does not send performance counters other than CPU/Mem to quickpulse"); + } else if (telemetryType.equals(TelemetryType.EVENT)) { + throw new TelemetryTypeException( + "The telemetry type Event was specified, but the distro does not send events to quickpulse"); + } else if (telemetryType.equals(TelemetryType.METRIC)) { + throw new TelemetryTypeException( + "The telemetry type Metric was specified, but the distro does not support sending open telemetry metrics to quickpulse"); + } + } + + public static void checkCustomMetricProjection(DerivedMetricInfo derivedMetricInfo) + throws UnexpectedFilterCreateException { + if (derivedMetricInfo.getProjection().startsWith("CustomMetrics.")) { + throw new UnexpectedFilterCreateException( + "The projection of a customMetric property is not supported in this distro."); + } + } + + public static void validateMetricFilters(DerivedMetricInfo derivedMetricInfo) + throws UnexpectedFilterCreateException { + for (FilterConjunctionGroupInfo conjunctionGroupInfo : derivedMetricInfo.getFilterGroups()) { + for (FilterInfo filter : conjunctionGroupInfo.getFilters()) { + TelemetryType telemetryType = TelemetryType.fromString(derivedMetricInfo.getTelemetryType()); + validateFieldNames(filter.getFieldName(), telemetryType); + validatePredicateAndComparand(filter); + } + } + } + + public static void + validateDocumentFilters(DocumentFilterConjunctionGroupInfo documentFilterConjunctionGroupInfo) + throws UnexpectedFilterCreateException { + FilterConjunctionGroupInfo conjunctionGroupInfo = documentFilterConjunctionGroupInfo.getFilters(); + for (FilterInfo filter : conjunctionGroupInfo.getFilters()) { + validateFieldNames(filter.getFieldName(), documentFilterConjunctionGroupInfo.getTelemetryType()); + validatePredicateAndComparand(filter); + } + } + + private static boolean isCustomDimOrAnyField(String fieldName) { + return fieldName.startsWith(Filter.CUSTOM_DIM_FIELDNAME_PREFIX) || fieldName.equals(Filter.ANY_FIELD); + } + + private static void validateFieldNames(String fieldName, TelemetryType telemetryType) + throws UnexpectedFilterCreateException { + if (fieldName.isEmpty()) { + throw new UnexpectedFilterCreateException("A filter must have a non-empty field name."); + } + if (fieldName.startsWith("CustomMetrics.")) { + throw new UnexpectedFilterCreateException( + "Filtering of a customMetric property is not supported in this distro."); + } + } + + private static void validatePredicateAndComparand(FilterInfo filter) throws UnexpectedFilterCreateException { + if (filter.getComparand().isEmpty()) { + // It is possible to not type in a comparand and the service side to send us empty string. + throw new UnexpectedFilterCreateException("A filter must have a non-empty comparand. FilterName: " + + filter.getFieldName() + " Predicate: " + filter.getPredicate().getValue()); + } else if (Filter.ANY_FIELD.equals(filter.getFieldName()) + && !(filter.getPredicate().equals(PredicateType.CONTAINS) + || filter.getPredicate().equals(PredicateType.DOES_NOT_CONTAIN))) { + // While the UI allows != and == for the ANY_FIELD fieldName, .net classic code only allows contains/not contains & the spec follows + // .net classic behavior for this particular condition. + throw new UnexpectedFilterCreateException( + "The predicate " + filter.getPredicate().getValue() + " is not supported for the field name *"); + } else if (knownNumericColumns.contains(filter.getFieldName())) { + + // Just in case a strange timestamp value is passed from the service side. The service side should send a duration with a specific + // format ([days].[hours]:[minutes]:[seconds] - the seconds may be a whole number or something like 7.89). + if (KnownDependencyColumns.DURATION.equals(filter.getFieldName())) { + if (Filter.getMicroSecondsFromFilterTimestampString(filter.getComparand()) == Long.MIN_VALUE) { + throw new UnexpectedFilterCreateException( + "The provided duration timestamp can't be converted to microseconds: " + filter.getComparand()); + } + } else { // The service side not does not validate if resultcode or responsecode is a numeric value + try { + Long.parseLong(filter.getComparand()); + } catch (NumberFormatException e) { + throw new UnexpectedFilterCreateException( + "Could not convert the provided result/response code to a numeric value: " + + filter.getComparand()); + } + } + } else if (knownStringColumns.contains(filter.getFieldName()) + || filter.getFieldName().startsWith(Filter.CUSTOM_DIM_FIELDNAME_PREFIX)) { + // While the UI allows a user to select any predicate for a custom dimension filter, .net classic treats all custom dimensions like + // String values. therefore we validate for predicates applicable to String. This is called out in the spec as well. + if (!validStringPredicates.contains(filter.getPredicate())) { + throw new UnexpectedFilterCreateException("The predicate " + filter.getPredicate().getValue() + + " is not supported for the field " + filter.getFieldName()); + } + } + } +} From 6e81b5520be83a40d2e6b5a7840c0f39a41487eb Mon Sep 17 00:00:00 2001 From: "Harsimar Kaur (from Dev Box)" Date: Wed, 18 Dec 2024 17:02:04 -0800 Subject: [PATCH 18/20] finish implementation and starting to add tests --- .../filtering/FilteringConfiguration.java | 37 ++-- .../quickpulse/filtering/Validator.java | 148 +++++++++----- .../filteringTest/ValidatorTests.java | 181 ++++++++++++++++++ 3 files changed, 291 insertions(+), 75 deletions(-) create mode 100644 sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/test/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filteringTest/ValidatorTests.java diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/FilteringConfiguration.java b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/FilteringConfiguration.java index ab06f952986d7..5303dc448f14b 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/FilteringConfiguration.java +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/FilteringConfiguration.java @@ -2,13 +2,7 @@ // Licensed under the MIT License. package com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.filtering; -import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.DerivedMetricInfo; -import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.FilterConjunctionGroupInfo; -import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.CollectionConfigurationInfo; -import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.DocumentStreamInfo; -import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.DocumentFilterConjunctionGroupInfo; -import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.AggregationType; -import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.TelemetryType; +import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.*; import java.util.Set; import java.util.List; @@ -19,7 +13,6 @@ public class FilteringConfiguration { - private final Set seenMetricIds = new HashSet<>(); // key is the telemetry type private final Map> validDerivedMetricInfos; @@ -32,6 +25,8 @@ public class FilteringConfiguration { // key is the derived metric id private final Map validProjectionInfo; + private final Validator validator = new Validator(); + public FilteringConfiguration() { validDerivedMetricInfos = new HashMap<>(); validDocumentFilterConjunctionGroupInfos = new HashMap<>(); @@ -81,11 +76,7 @@ public Map getValidProjectionInitInfo() { .getDocumentFilterGroups()) { TelemetryType telemetryType = documentFilterGroupInfo.getTelemetryType(); FilterConjunctionGroupInfo filterGroup = documentFilterGroupInfo.getFilters(); - - try { - Validator.validateTelemetryType(telemetryType); - Validator.validateDocumentFilters(documentFilterGroupInfo); - + if (validator.isValidDocConjunctionGroupInfo(documentFilterGroupInfo)) { if (!result.containsKey(telemetryType)) { result.put(telemetryType, new HashMap<>()); } @@ -98,8 +89,6 @@ public Map getValidProjectionInitInfo() { filterGroups.add(filterGroup); innerMap.put(documentStreamId, filterGroups); } - } catch (TelemetryTypeException | UnexpectedFilterCreateException e) { - // TODO (harskaur): For ErrorTracker PR: If any validator methods throw an exception, catch the exception and track the error for post request body } } } @@ -108,16 +97,15 @@ public Map getValidProjectionInitInfo() { private Map> parseMetricFilterConfiguration(CollectionConfigurationInfo configuration) { + Set seenMetricIds = new HashSet<>(); Map> result = new HashMap<>(); for (DerivedMetricInfo derivedMetricInfo : configuration.getMetrics()) { TelemetryType telemetryType = TelemetryType.fromString(derivedMetricInfo.getTelemetryType()); String id = derivedMetricInfo.getId(); - try { - if (!seenMetricIds.contains(id)) { - seenMetricIds.add(id); - Validator.validateTelemetryType(telemetryType); - Validator.checkCustomMetricProjection(derivedMetricInfo); + if (!seenMetricIds.contains(id)) { + seenMetricIds.add(id); + if (validator.isValidDerivedMetricInfo(derivedMetricInfo)) { if (result.containsKey(telemetryType)) { result.get(telemetryType).add(derivedMetricInfo); } else { @@ -125,12 +113,13 @@ public Map getValidProjectionInitInfo() { infos.add(derivedMetricInfo); result.put(telemetryType, infos); } - } else { - throw new DuplicateMetricIdException("Duplicate Metric Id: " + id); } - } catch (TelemetryTypeException | UnexpectedFilterCreateException | DuplicateMetricIdException e) { - // TODO (harskaur): For ErrorTracker PR: If any validator methods throw an exception, catch the exception and track the error for post request body + } else { + validator.constructAndTrackCollectionConfigurationError(CollectionConfigurationErrorType.METRIC_DUPLICATE_IDS, + "A duplicate metric id was found in this configuration", + configuration.getETag(), id, true); } + } return result; } diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/Validator.java b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/Validator.java index 43bab6e71a1fc..f0e3092559531 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/Validator.java +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/Validator.java @@ -1,109 +1,155 @@ package com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.filtering; -import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.*; +import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.PredicateType; +import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.TelemetryType; +import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.DerivedMetricInfo; +import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.FilterConjunctionGroupInfo; +import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.DocumentFilterConjunctionGroupInfo; +import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.FilterInfo; +import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.CollectionConfigurationError; +import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.CollectionConfigurationErrorType; +import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.KeyValuePairString; +import java.util.ArrayList; import java.util.HashSet; +import java.util.List; import java.util.Set; import static java.util.Arrays.asList; public class Validator { - private static final Set knownStringColumns + private final Set knownStringColumns = new HashSet(asList(KnownRequestColumns.URL, KnownRequestColumns.NAME, KnownDependencyColumns.DATA, KnownDependencyColumns.TARGET, KnownDependencyColumns.TYPE, KnownTraceColumns.MESSAGE, KnownExceptionColumns.MESSAGE, KnownExceptionColumns.STACK)); - private static final Set knownNumericColumns = new HashSet<>(asList(KnownRequestColumns.RESPONSE_CODE, + private final Set knownNumericColumns = new HashSet<>(asList(KnownRequestColumns.RESPONSE_CODE, KnownRequestColumns.DURATION, KnownDependencyColumns.RESULT_CODE)); - private static final Set validStringPredicates = new HashSet<>(asList(PredicateType.CONTAINS, + private final Set validStringPredicates = new HashSet<>(asList(PredicateType.CONTAINS, PredicateType.DOES_NOT_CONTAIN, PredicateType.EQUAL, PredicateType.NOT_EQUAL)); - public static void validateTelemetryType(TelemetryType telemetryType) throws TelemetryTypeException { - if (telemetryType.equals(TelemetryType.PERFORMANCE_COUNTER)) { - throw new TelemetryTypeException( - "The telemetry type PerformanceCounter was specified, but the distro does not send performance counters other than CPU/Mem to quickpulse"); - } else if (telemetryType.equals(TelemetryType.EVENT)) { - throw new TelemetryTypeException( - "The telemetry type Event was specified, but the distro does not send events to quickpulse"); - } else if (telemetryType.equals(TelemetryType.METRIC)) { - throw new TelemetryTypeException( - "The telemetry type Metric was specified, but the distro does not support sending open telemetry metrics to quickpulse"); + // TODO (harskaur): In ErrorTracker PR, track a list of configuration validation errors here + + public boolean isValidDerivedMetricInfo(DerivedMetricInfo derivedMetricInfo) { + TelemetryType telemetryType = TelemetryType.fromString(derivedMetricInfo.getTelemetryType()); + if (!isValidTelemetryType(telemetryType)) { + return false; } - } - public static void checkCustomMetricProjection(DerivedMetricInfo derivedMetricInfo) - throws UnexpectedFilterCreateException { - if (derivedMetricInfo.getProjection().startsWith("CustomMetrics.")) { - throw new UnexpectedFilterCreateException( - "The projection of a customMetric property is not supported in this distro."); + if (!isNotCustomMetricProjection(derivedMetricInfo.getProjection())) { + return false; } - } - public static void validateMetricFilters(DerivedMetricInfo derivedMetricInfo) - throws UnexpectedFilterCreateException { for (FilterConjunctionGroupInfo conjunctionGroupInfo : derivedMetricInfo.getFilterGroups()) { for (FilterInfo filter : conjunctionGroupInfo.getFilters()) { - TelemetryType telemetryType = TelemetryType.fromString(derivedMetricInfo.getTelemetryType()); - validateFieldNames(filter.getFieldName(), telemetryType); - validatePredicateAndComparand(filter); + if (!isValidFieldName(filter.getFieldName(), telemetryType)) { + return false; + } + if (!isValidPredicateAndComparand(filter)) { + return false; + } } } + return true; } - public static void - validateDocumentFilters(DocumentFilterConjunctionGroupInfo documentFilterConjunctionGroupInfo) - throws UnexpectedFilterCreateException { + public boolean + isValidDocConjunctionGroupInfo(DocumentFilterConjunctionGroupInfo documentFilterConjunctionGroupInfo) { + TelemetryType telemetryType = documentFilterConjunctionGroupInfo.getTelemetryType(); + if (!isValidTelemetryType(telemetryType)) { + return false; + } + FilterConjunctionGroupInfo conjunctionGroupInfo = documentFilterConjunctionGroupInfo.getFilters(); for (FilterInfo filter : conjunctionGroupInfo.getFilters()) { - validateFieldNames(filter.getFieldName(), documentFilterConjunctionGroupInfo.getTelemetryType()); - validatePredicateAndComparand(filter); + if (!isValidFieldName(filter.getFieldName(), telemetryType)) { + return false; + } + if (!isValidPredicateAndComparand(filter)) { + return false; + } } + return true; } - private static boolean isCustomDimOrAnyField(String fieldName) { - return fieldName.startsWith(Filter.CUSTOM_DIM_FIELDNAME_PREFIX) || fieldName.equals(Filter.ANY_FIELD); + public void constructAndTrackCollectionConfigurationError(CollectionConfigurationErrorType errorType, String message, String eTag, String id, boolean isDerivedMetricId) { + CollectionConfigurationError error = new CollectionConfigurationError(); + error.setMessage(message); + error.setCollectionConfigurationErrorType(errorType); + + KeyValuePairString keyValuePair1 = new KeyValuePairString(); + keyValuePair1.setKey("ETag"); + keyValuePair1.setValue(eTag); + + KeyValuePairString keyValuePair2 = new KeyValuePairString(); + keyValuePair2.setKey(isDerivedMetricId ? "DerivedMetricInfoId" : "DocumentStreamInfoId"); + keyValuePair2.setValue(id); + + List data = new ArrayList<>(); + data.add(keyValuePair1); + data.add(keyValuePair2); + + error.setData(data); + + // TODO (harskaur): For ErrorTracker PR, add this error to list of tracked errors } - private static void validateFieldNames(String fieldName, TelemetryType telemetryType) - throws UnexpectedFilterCreateException { + private boolean isValidTelemetryType(TelemetryType telemetryType) { + // TODO (harskaur): In ErrorTracker PR, create an error message & track an error for each false case + if (telemetryType.equals(TelemetryType.PERFORMANCE_COUNTER)) { + return false; + } else if (telemetryType.equals(TelemetryType.EVENT)) { + return false; + } else if (telemetryType.equals(TelemetryType.METRIC)) { + return false; + } else { + return true; + } + } + + private boolean isNotCustomMetricProjection(String projection) { + if (projection.startsWith("CustomMetrics.")) { + // TODO (harskaur): In ErrorTracker PR, create an error message & track an error for this case + return false; + } + return true; + } + + private boolean isValidFieldName(String fieldName, TelemetryType telemetryType) { + // TODO (harskaur): In ErrorTracker PR, create an error message & track an error for each false case if (fieldName.isEmpty()) { - throw new UnexpectedFilterCreateException("A filter must have a non-empty field name."); + return false; } if (fieldName.startsWith("CustomMetrics.")) { - throw new UnexpectedFilterCreateException( - "Filtering of a customMetric property is not supported in this distro."); + return false; } + return true; } - private static void validatePredicateAndComparand(FilterInfo filter) throws UnexpectedFilterCreateException { + private boolean isValidPredicateAndComparand(FilterInfo filter) { + // TODO (harskaur): In ErrorTracker PR, create an error message & track an error for each false case if (filter.getComparand().isEmpty()) { // It is possible to not type in a comparand and the service side to send us empty string. - throw new UnexpectedFilterCreateException("A filter must have a non-empty comparand. FilterName: " - + filter.getFieldName() + " Predicate: " + filter.getPredicate().getValue()); + return false; } else if (Filter.ANY_FIELD.equals(filter.getFieldName()) && !(filter.getPredicate().equals(PredicateType.CONTAINS) || filter.getPredicate().equals(PredicateType.DOES_NOT_CONTAIN))) { // While the UI allows != and == for the ANY_FIELD fieldName, .net classic code only allows contains/not contains & the spec follows // .net classic behavior for this particular condition. - throw new UnexpectedFilterCreateException( - "The predicate " + filter.getPredicate().getValue() + " is not supported for the field name *"); + return false; } else if (knownNumericColumns.contains(filter.getFieldName())) { - // Just in case a strange timestamp value is passed from the service side. The service side should send a duration with a specific // format ([days].[hours]:[minutes]:[seconds] - the seconds may be a whole number or something like 7.89). if (KnownDependencyColumns.DURATION.equals(filter.getFieldName())) { if (Filter.getMicroSecondsFromFilterTimestampString(filter.getComparand()) == Long.MIN_VALUE) { - throw new UnexpectedFilterCreateException( - "The provided duration timestamp can't be converted to microseconds: " + filter.getComparand()); + return false; } } else { // The service side not does not validate if resultcode or responsecode is a numeric value try { Long.parseLong(filter.getComparand()); } catch (NumberFormatException e) { - throw new UnexpectedFilterCreateException( - "Could not convert the provided result/response code to a numeric value: " - + filter.getComparand()); + return false; } } } else if (knownStringColumns.contains(filter.getFieldName()) @@ -111,9 +157,9 @@ private static void validatePredicateAndComparand(FilterInfo filter) throws Unex // While the UI allows a user to select any predicate for a custom dimension filter, .net classic treats all custom dimensions like // String values. therefore we validate for predicates applicable to String. This is called out in the spec as well. if (!validStringPredicates.contains(filter.getPredicate())) { - throw new UnexpectedFilterCreateException("The predicate " + filter.getPredicate().getValue() - + " is not supported for the field " + filter.getFieldName()); + return false; } } + return true; } } diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/test/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filteringTest/ValidatorTests.java b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/test/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filteringTest/ValidatorTests.java new file mode 100644 index 0000000000000..827cdd6a6d773 --- /dev/null +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/test/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filteringTest/ValidatorTests.java @@ -0,0 +1,181 @@ +package com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.filteringTest; + +import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.filtering.DerivedMetricProjections; +import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.filtering.Filter; +import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.filtering.KnownRequestColumns; +import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.filtering.Validator; +import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.*; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static java.util.Arrays.asList; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class ValidatorTests { + + @Test + void rejectInvalidTelemetryTypes() { + List telemetryTypes = asList(TelemetryType.EVENT, TelemetryType.PERFORMANCE_COUNTER, TelemetryType.METRIC); + Validator validator = new Validator(); + for (TelemetryType telemetryType : telemetryTypes) { + List filterGroups = createListWithOneFilterConjunctionGroupAndNoFilters(); + DerivedMetricInfo dmi = createDerivedMetricInfo("random-id", telemetryType.getValue(), AggregationType.SUM, + AggregationType.SUM, DerivedMetricProjections.COUNT, filterGroups); + assertFalse(validator.isValidDerivedMetricInfo(dmi)); + DocumentFilterConjunctionGroupInfo docGroup = createDocGroupWithNoFilters(telemetryType); + assertFalse(validator.isValidDocConjunctionGroupInfo(docGroup)); + } + } + + @Test + void acceptValidTelemetryType() { + List telemetryTypes = asList(TelemetryType.TRACE, TelemetryType.REQUEST, TelemetryType.DEPENDENCY, TelemetryType.EXCEPTION); + Validator validator = new Validator(); + for (TelemetryType telemetryType : telemetryTypes) { + List filterGroups = createListWithOneFilterConjunctionGroupAndNoFilters(); + DerivedMetricInfo dmi = createDerivedMetricInfo("random-id", telemetryType.getValue(), AggregationType.SUM, + AggregationType.SUM, DerivedMetricProjections.COUNT, filterGroups); + assertTrue(validator.isValidDerivedMetricInfo(dmi)); + DocumentFilterConjunctionGroupInfo docGroup = createDocGroupWithNoFilters(telemetryType); + assertTrue(validator.isValidDocConjunctionGroupInfo(docGroup)); + } + } + + @Test + void rejectCustomMetricProjection() { + List filterGroups = createListWithOneFilterConjunctionGroupAndNoFilters(); + Validator validator = new Validator(); + DerivedMetricInfo dmi = createDerivedMetricInfo("random-id", TelemetryType.TRACE.getValue(), AggregationType.SUM, + AggregationType.SUM, "CustomMetrics.property", filterGroups); + assertFalse(validator.isValidDerivedMetricInfo(dmi)); + + } + + @Test + void rejectCustomMetricFieldName() { + FilterInfo filter = createFilterInfoWithParams("CustomMetrics.property", PredicateType.EQUAL, "5"); + List filterGroups = createListWithOneFilterConjunctionGroupAndOneFilter(filter); + Validator validator = new Validator(); + DerivedMetricInfo dmi = createDerivedMetricInfo("random-id", TelemetryType.TRACE.getValue(), AggregationType.SUM, + AggregationType.SUM, DerivedMetricProjections.COUNT, filterGroups); + assertFalse(validator.isValidDerivedMetricInfo(dmi)); + } + + @Test + void rejectInvalidFilters() { + List filtersToTest = Arrays.asList( + createFilterInfoWithParams("", PredicateType.EQUAL, "blah"), + createFilterInfoWithParams(KnownRequestColumns.URL, PredicateType.EQUAL, ""), + createFilterInfoWithParams(Filter.ANY_FIELD, PredicateType.EQUAL, "5"), + createFilterInfoWithParams(Filter.ANY_FIELD, PredicateType.NOT_EQUAL, "5"), + createFilterInfoWithParams(Filter.ANY_FIELD, PredicateType.LESS_THAN, "5"), + createFilterInfoWithParams(Filter.ANY_FIELD, PredicateType.LESS_THAN_OR_EQUAL, "5"), + createFilterInfoWithParams(Filter.ANY_FIELD, PredicateType.GREATER_THAN, "5"), + createFilterInfoWithParams(Filter.ANY_FIELD, PredicateType.GREATER_THAN_OR_EQUAL, "5"), + createFilterInfoWithParams("CustomDimensions.property", PredicateType.LESS_THAN, "blah"), + createFilterInfoWithParams("CustomDimensions.property", PredicateType.LESS_THAN_OR_EQUAL, "blah"), + createFilterInfoWithParams("CustomDimensions.property", PredicateType.GREATER_THAN, "blah"), + createFilterInfoWithParams("CustomDimensions.property", PredicateType.GREATER_THAN_OR_EQUAL, "blah"), + createFilterInfoWithParams(KnownRequestColumns.DURATION, PredicateType.LESS_THAN, "invalid timestamp"), + createFilterInfoWithParams(KnownRequestColumns.DURATION, PredicateType.LESS_THAN, "150"), + createFilterInfoWithParams(KnownRequestColumns.DURATION, PredicateType.LESS_THAN, "0.0:0:0.") + ); + + for (FilterInfo filter: filtersToTest) { + List filterGroups = createListWithOneFilterConjunctionGroupAndOneFilter(filter); + DerivedMetricInfo dmi = createDerivedMetricInfo("random-id", TelemetryType.REQUEST.getValue(), AggregationType.SUM, AggregationType.SUM, DerivedMetricProjections.COUNT, filterGroups); + Validator validator = new Validator(); + assertFalse(validator.isValidDerivedMetricInfo(dmi)); + + DocumentFilterConjunctionGroupInfo docGroup = createDocGroupWithOneFilter(TelemetryType.REQUEST, filter); + assertFalse(validator.isValidDocConjunctionGroupInfo(docGroup)); + } + } + + @Test + void rejectInvalidGroupWithMultipleFilters() { + List filterGroups = new ArrayList<>(); + FilterConjunctionGroupInfo filterGroup = new FilterConjunctionGroupInfo(); + List filters = Arrays.asList( + createFilterInfoWithParams("", PredicateType.EQUAL, "blah"), // invalid + createFilterInfoWithParams(KnownRequestColumns.SUCCESS, PredicateType.EQUAL, "true") // valid + ); + filterGroup.setFilters(filters); + filterGroups.add(filterGroup); + + Validator validator = new Validator(); + + DerivedMetricInfo dmi = createDerivedMetricInfo("random-id", TelemetryType.REQUEST.getValue(), AggregationType.SUM, AggregationType.SUM, DerivedMetricProjections.COUNT, filterGroups); + assertFalse(validator.isValidDerivedMetricInfo(dmi)); + + DocumentFilterConjunctionGroupInfo docGroup = new DocumentFilterConjunctionGroupInfo(); + docGroup.setFilters(filterGroup); + docGroup.setTelemetryType(TelemetryType.REQUEST); + assertFalse(validator.isValidDocConjunctionGroupInfo(docGroup)); + } + private FilterInfo createFilterInfoWithParams(String fieldName, PredicateType predicate, String comparand) { + FilterInfo result = new FilterInfo(); + result.setFieldName(fieldName); + result.setPredicate(predicate); + result.setComparand(comparand); + return result; + } + + + private DerivedMetricInfo createDerivedMetricInfo(String id, String telemetryType, AggregationType agg, + AggregationType backendAgg, String projection, List filterGroups) { + DerivedMetricInfo result = new DerivedMetricInfo(); + result.setId(id); + result.setTelemetryType(telemetryType); + result.setAggregation(agg); + result.setBackEndAggregation(backendAgg); + result.setProjection(projection); + result.setFilterGroups(filterGroups); + return result; + } + + private List createListWithOneFilterConjunctionGroupAndOneFilter(FilterInfo filter) { + List result = new ArrayList<>(); + FilterConjunctionGroupInfo group = new FilterConjunctionGroupInfo(); + List filters = new ArrayList<>(); + filters.add(filter); + group.setFilters(filters); + result.add(group); + return result; + } + + private List createListWithOneFilterConjunctionGroupAndNoFilters() { + List result = new ArrayList<>(); + FilterConjunctionGroupInfo group = new FilterConjunctionGroupInfo(); + List filters = new ArrayList<>(); + group.setFilters(filters); + result.add(group); + return result; + } + + private DocumentFilterConjunctionGroupInfo createDocGroupWithNoFilters(TelemetryType telemetryType) { + DocumentFilterConjunctionGroupInfo docGroup = new DocumentFilterConjunctionGroupInfo(); + FilterConjunctionGroupInfo group = new FilterConjunctionGroupInfo(); + List filters = new ArrayList<>(); + group.setFilters(filters); + docGroup.setFilters(group); + docGroup.setTelemetryType(telemetryType); + return docGroup; + } + + private DocumentFilterConjunctionGroupInfo createDocGroupWithOneFilter(TelemetryType telemetryType, FilterInfo filter) { + DocumentFilterConjunctionGroupInfo docGroup = new DocumentFilterConjunctionGroupInfo(); + FilterConjunctionGroupInfo group = new FilterConjunctionGroupInfo(); + List filters = new ArrayList<>(); + filters.add(filter); + group.setFilters(filters); + docGroup.setFilters(group); + docGroup.setTelemetryType(telemetryType); + return docGroup; + } + +} From de6bd93d916863b82bac4048986d53493acf670d Mon Sep 17 00:00:00 2001 From: "Harsimar Kaur (from Dev Box)" Date: Thu, 19 Dec 2024 16:01:51 -0800 Subject: [PATCH 19/20] spotless --- .../filtering/FilteringConfiguration.java | 7 +- .../quickpulse/filtering/Validator.java | 21 +++--- .../filteringTest/ValidatorTests.java | 73 ++++++++++++++----- 3 files changed, 69 insertions(+), 32 deletions(-) diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/FilteringConfiguration.java b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/FilteringConfiguration.java index 4557d8ac5c022..2b621bb4c398e 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/FilteringConfiguration.java +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/FilteringConfiguration.java @@ -18,7 +18,6 @@ import java.util.HashSet; import java.util.HashMap; - public class FilteringConfiguration { // key is the telemetry type @@ -122,9 +121,9 @@ public Map getValidProjectionInitInfo() { } } } else { - validator.constructAndTrackCollectionConfigurationError(CollectionConfigurationErrorType.METRIC_DUPLICATE_IDS, - "A duplicate metric id was found in this configuration", - configuration.getETag(), id, true); + validator.constructAndTrackCollectionConfigurationError( + CollectionConfigurationErrorType.METRIC_DUPLICATE_IDS, + "A duplicate metric id was found in this configuration", configuration.getETag(), id, true); } } diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/Validator.java b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/Validator.java index f0e3092559531..b081c4a41454f 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/Validator.java +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/Validator.java @@ -20,14 +20,14 @@ public class Validator { private final Set knownStringColumns = new HashSet(asList(KnownRequestColumns.URL, KnownRequestColumns.NAME, KnownDependencyColumns.DATA, - KnownDependencyColumns.TARGET, KnownDependencyColumns.TYPE, KnownTraceColumns.MESSAGE, - KnownExceptionColumns.MESSAGE, KnownExceptionColumns.STACK)); + KnownDependencyColumns.TARGET, KnownDependencyColumns.TYPE, KnownTraceColumns.MESSAGE, + KnownExceptionColumns.MESSAGE, KnownExceptionColumns.STACK)); - private final Set knownNumericColumns = new HashSet<>(asList(KnownRequestColumns.RESPONSE_CODE, - KnownRequestColumns.DURATION, KnownDependencyColumns.RESULT_CODE)); + private final Set knownNumericColumns = new HashSet<>( + asList(KnownRequestColumns.RESPONSE_CODE, KnownRequestColumns.DURATION, KnownDependencyColumns.RESULT_CODE)); - private final Set validStringPredicates = new HashSet<>(asList(PredicateType.CONTAINS, - PredicateType.DOES_NOT_CONTAIN, PredicateType.EQUAL, PredicateType.NOT_EQUAL)); + private final Set validStringPredicates = new HashSet<>( + asList(PredicateType.CONTAINS, PredicateType.DOES_NOT_CONTAIN, PredicateType.EQUAL, PredicateType.NOT_EQUAL)); // TODO (harskaur): In ErrorTracker PR, track a list of configuration validation errors here @@ -55,7 +55,7 @@ public boolean isValidDerivedMetricInfo(DerivedMetricInfo derivedMetricInfo) { } public boolean - isValidDocConjunctionGroupInfo(DocumentFilterConjunctionGroupInfo documentFilterConjunctionGroupInfo) { + isValidDocConjunctionGroupInfo(DocumentFilterConjunctionGroupInfo documentFilterConjunctionGroupInfo) { TelemetryType telemetryType = documentFilterConjunctionGroupInfo.getTelemetryType(); if (!isValidTelemetryType(telemetryType)) { return false; @@ -73,7 +73,8 @@ public boolean isValidDerivedMetricInfo(DerivedMetricInfo derivedMetricInfo) { return true; } - public void constructAndTrackCollectionConfigurationError(CollectionConfigurationErrorType errorType, String message, String eTag, String id, boolean isDerivedMetricId) { + public void constructAndTrackCollectionConfigurationError(CollectionConfigurationErrorType errorType, + String message, String eTag, String id, boolean isDerivedMetricId) { CollectionConfigurationError error = new CollectionConfigurationError(); error.setMessage(message); error.setCollectionConfigurationErrorType(errorType); @@ -134,7 +135,7 @@ private boolean isValidPredicateAndComparand(FilterInfo filter) { return false; } else if (Filter.ANY_FIELD.equals(filter.getFieldName()) && !(filter.getPredicate().equals(PredicateType.CONTAINS) - || filter.getPredicate().equals(PredicateType.DOES_NOT_CONTAIN))) { + || filter.getPredicate().equals(PredicateType.DOES_NOT_CONTAIN))) { // While the UI allows != and == for the ANY_FIELD fieldName, .net classic code only allows contains/not contains & the spec follows // .net classic behavior for this particular condition. return false; @@ -157,7 +158,7 @@ private boolean isValidPredicateAndComparand(FilterInfo filter) { // While the UI allows a user to select any predicate for a custom dimension filter, .net classic treats all custom dimensions like // String values. therefore we validate for predicates applicable to String. This is called out in the spec as well. if (!validStringPredicates.contains(filter.getPredicate())) { - return false; + return false; } } return true; diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/test/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filteringTest/ValidatorTests.java b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/test/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filteringTest/ValidatorTests.java index 827cdd6a6d773..f78d6188d5b1b 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/test/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filteringTest/ValidatorTests.java +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/test/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filteringTest/ValidatorTests.java @@ -19,7 +19,8 @@ public class ValidatorTests { @Test void rejectInvalidTelemetryTypes() { - List telemetryTypes = asList(TelemetryType.EVENT, TelemetryType.PERFORMANCE_COUNTER, TelemetryType.METRIC); + List telemetryTypes + = asList(TelemetryType.EVENT, TelemetryType.PERFORMANCE_COUNTER, TelemetryType.METRIC); Validator validator = new Validator(); for (TelemetryType telemetryType : telemetryTypes) { List filterGroups = createListWithOneFilterConjunctionGroupAndNoFilters(); @@ -33,7 +34,8 @@ void rejectInvalidTelemetryTypes() { @Test void acceptValidTelemetryType() { - List telemetryTypes = asList(TelemetryType.TRACE, TelemetryType.REQUEST, TelemetryType.DEPENDENCY, TelemetryType.EXCEPTION); + List telemetryTypes + = asList(TelemetryType.TRACE, TelemetryType.REQUEST, TelemetryType.DEPENDENCY, TelemetryType.EXCEPTION); Validator validator = new Validator(); for (TelemetryType telemetryType : telemetryTypes) { List filterGroups = createListWithOneFilterConjunctionGroupAndNoFilters(); @@ -49,8 +51,8 @@ void acceptValidTelemetryType() { void rejectCustomMetricProjection() { List filterGroups = createListWithOneFilterConjunctionGroupAndNoFilters(); Validator validator = new Validator(); - DerivedMetricInfo dmi = createDerivedMetricInfo("random-id", TelemetryType.TRACE.getValue(), AggregationType.SUM, - AggregationType.SUM, "CustomMetrics.property", filterGroups); + DerivedMetricInfo dmi = createDerivedMetricInfo("random-id", TelemetryType.TRACE.getValue(), + AggregationType.SUM, AggregationType.SUM, "CustomMetrics.property", filterGroups); assertFalse(validator.isValidDerivedMetricInfo(dmi)); } @@ -60,15 +62,14 @@ void rejectCustomMetricFieldName() { FilterInfo filter = createFilterInfoWithParams("CustomMetrics.property", PredicateType.EQUAL, "5"); List filterGroups = createListWithOneFilterConjunctionGroupAndOneFilter(filter); Validator validator = new Validator(); - DerivedMetricInfo dmi = createDerivedMetricInfo("random-id", TelemetryType.TRACE.getValue(), AggregationType.SUM, - AggregationType.SUM, DerivedMetricProjections.COUNT, filterGroups); + DerivedMetricInfo dmi = createDerivedMetricInfo("random-id", TelemetryType.TRACE.getValue(), + AggregationType.SUM, AggregationType.SUM, DerivedMetricProjections.COUNT, filterGroups); assertFalse(validator.isValidDerivedMetricInfo(dmi)); } @Test void rejectInvalidFilters() { - List filtersToTest = Arrays.asList( - createFilterInfoWithParams("", PredicateType.EQUAL, "blah"), + List filtersToTest = Arrays.asList(createFilterInfoWithParams("", PredicateType.EQUAL, "blah"), createFilterInfoWithParams(KnownRequestColumns.URL, PredicateType.EQUAL, ""), createFilterInfoWithParams(Filter.ANY_FIELD, PredicateType.EQUAL, "5"), createFilterInfoWithParams(Filter.ANY_FIELD, PredicateType.NOT_EQUAL, "5"), @@ -82,12 +83,12 @@ void rejectInvalidFilters() { createFilterInfoWithParams("CustomDimensions.property", PredicateType.GREATER_THAN_OR_EQUAL, "blah"), createFilterInfoWithParams(KnownRequestColumns.DURATION, PredicateType.LESS_THAN, "invalid timestamp"), createFilterInfoWithParams(KnownRequestColumns.DURATION, PredicateType.LESS_THAN, "150"), - createFilterInfoWithParams(KnownRequestColumns.DURATION, PredicateType.LESS_THAN, "0.0:0:0.") - ); + createFilterInfoWithParams(KnownRequestColumns.DURATION, PredicateType.LESS_THAN, "0.0:0:")); - for (FilterInfo filter: filtersToTest) { + for (FilterInfo filter : filtersToTest) { List filterGroups = createListWithOneFilterConjunctionGroupAndOneFilter(filter); - DerivedMetricInfo dmi = createDerivedMetricInfo("random-id", TelemetryType.REQUEST.getValue(), AggregationType.SUM, AggregationType.SUM, DerivedMetricProjections.COUNT, filterGroups); + DerivedMetricInfo dmi = createDerivedMetricInfo("random-id", TelemetryType.REQUEST.getValue(), + AggregationType.SUM, AggregationType.SUM, DerivedMetricProjections.COUNT, filterGroups); Validator validator = new Validator(); assertFalse(validator.isValidDerivedMetricInfo(dmi)); @@ -100,8 +101,7 @@ void rejectInvalidFilters() { void rejectInvalidGroupWithMultipleFilters() { List filterGroups = new ArrayList<>(); FilterConjunctionGroupInfo filterGroup = new FilterConjunctionGroupInfo(); - List filters = Arrays.asList( - createFilterInfoWithParams("", PredicateType.EQUAL, "blah"), // invalid + List filters = Arrays.asList(createFilterInfoWithParams("", PredicateType.EQUAL, "blah"), // invalid createFilterInfoWithParams(KnownRequestColumns.SUCCESS, PredicateType.EQUAL, "true") // valid ); filterGroup.setFilters(filters); @@ -109,7 +109,8 @@ void rejectInvalidGroupWithMultipleFilters() { Validator validator = new Validator(); - DerivedMetricInfo dmi = createDerivedMetricInfo("random-id", TelemetryType.REQUEST.getValue(), AggregationType.SUM, AggregationType.SUM, DerivedMetricProjections.COUNT, filterGroups); + DerivedMetricInfo dmi = createDerivedMetricInfo("random-id", TelemetryType.REQUEST.getValue(), + AggregationType.SUM, AggregationType.SUM, DerivedMetricProjections.COUNT, filterGroups); assertFalse(validator.isValidDerivedMetricInfo(dmi)); DocumentFilterConjunctionGroupInfo docGroup = new DocumentFilterConjunctionGroupInfo(); @@ -117,6 +118,42 @@ void rejectInvalidGroupWithMultipleFilters() { docGroup.setTelemetryType(TelemetryType.REQUEST); assertFalse(validator.isValidDocConjunctionGroupInfo(docGroup)); } + + @Test + void acceptValidFilters() { + List filtersToTest + = Arrays.asList(createFilterInfoWithParams(Filter.ANY_FIELD, PredicateType.CONTAINS, "hi"), + createFilterInfoWithParams(Filter.ANY_FIELD, PredicateType.DOES_NOT_CONTAIN, "hi"), + createFilterInfoWithParams(KnownRequestColumns.URL, PredicateType.NOT_EQUAL, "hi"), + createFilterInfoWithParams(KnownRequestColumns.URL, PredicateType.EQUAL, "hi"), + createFilterInfoWithParams(KnownRequestColumns.URL, PredicateType.CONTAINS, "hi"), + createFilterInfoWithParams(KnownRequestColumns.URL, PredicateType.DOES_NOT_CONTAIN, "hi"), + createFilterInfoWithParams("CustomDimensions.property", PredicateType.NOT_EQUAL, "hi"), + createFilterInfoWithParams("CustomDimensions.property", PredicateType.EQUAL, "hi"), + createFilterInfoWithParams("CustomDimensions.property", PredicateType.CONTAINS, "hi"), + createFilterInfoWithParams("CustomDimensions.property", PredicateType.DOES_NOT_CONTAIN, "hi"), + createFilterInfoWithParams(KnownRequestColumns.RESPONSE_CODE, PredicateType.LESS_THAN, "5"), + createFilterInfoWithParams(KnownRequestColumns.RESPONSE_CODE, PredicateType.LESS_THAN_OR_EQUAL, "5"), + createFilterInfoWithParams(KnownRequestColumns.RESPONSE_CODE, PredicateType.GREATER_THAN, "5"), + createFilterInfoWithParams(KnownRequestColumns.RESPONSE_CODE, PredicateType.GREATER_THAN_OR_EQUAL, "5"), + createFilterInfoWithParams(KnownRequestColumns.RESPONSE_CODE, PredicateType.EQUAL, "5"), + createFilterInfoWithParams(KnownRequestColumns.RESPONSE_CODE, PredicateType.NOT_EQUAL, "5"), + createFilterInfoWithParams(KnownRequestColumns.DURATION, PredicateType.EQUAL, "0.0:0:0.2"), + createFilterInfoWithParams(KnownRequestColumns.SUCCESS, PredicateType.EQUAL, "true"), + createFilterInfoWithParams(KnownRequestColumns.SUCCESS, PredicateType.NOT_EQUAL, "false")); + + for (FilterInfo filter : filtersToTest) { + List filterGroups = createListWithOneFilterConjunctionGroupAndOneFilter(filter); + DerivedMetricInfo dmi = createDerivedMetricInfo("random-id", TelemetryType.REQUEST.getValue(), + AggregationType.SUM, AggregationType.SUM, DerivedMetricProjections.COUNT, filterGroups); + Validator validator = new Validator(); + assertTrue(validator.isValidDerivedMetricInfo(dmi)); + + DocumentFilterConjunctionGroupInfo docGroup = createDocGroupWithOneFilter(TelemetryType.REQUEST, filter); + assertTrue(validator.isValidDocConjunctionGroupInfo(docGroup)); + } + } + private FilterInfo createFilterInfoWithParams(String fieldName, PredicateType predicate, String comparand) { FilterInfo result = new FilterInfo(); result.setFieldName(fieldName); @@ -125,9 +162,8 @@ private FilterInfo createFilterInfoWithParams(String fieldName, PredicateType pr return result; } - private DerivedMetricInfo createDerivedMetricInfo(String id, String telemetryType, AggregationType agg, - AggregationType backendAgg, String projection, List filterGroups) { + AggregationType backendAgg, String projection, List filterGroups) { DerivedMetricInfo result = new DerivedMetricInfo(); result.setId(id); result.setTelemetryType(telemetryType); @@ -167,7 +203,8 @@ private DocumentFilterConjunctionGroupInfo createDocGroupWithNoFilters(Telemetry return docGroup; } - private DocumentFilterConjunctionGroupInfo createDocGroupWithOneFilter(TelemetryType telemetryType, FilterInfo filter) { + private DocumentFilterConjunctionGroupInfo createDocGroupWithOneFilter(TelemetryType telemetryType, + FilterInfo filter) { DocumentFilterConjunctionGroupInfo docGroup = new DocumentFilterConjunctionGroupInfo(); FilterConjunctionGroupInfo group = new FilterConjunctionGroupInfo(); List filters = new ArrayList<>(); From f492237dd3d43bf0cc8711b3358ed682925569db Mon Sep 17 00:00:00 2001 From: "Harsimar Kaur (from Dev Box)" Date: Thu, 19 Dec 2024 16:06:09 -0800 Subject: [PATCH 20/20] remove unused classes --- .../quickpulse/filtering/DuplicateMetricIdException.java | 9 --------- .../filtering/MetricFailureToCreateException.java | 9 --------- .../quickpulse/filtering/TelemetryTypeException.java | 9 --------- .../filtering/UnexpectedFilterCreateException.java | 9 --------- 4 files changed, 36 deletions(-) delete mode 100644 sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/DuplicateMetricIdException.java delete mode 100644 sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/MetricFailureToCreateException.java delete mode 100644 sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/TelemetryTypeException.java delete mode 100644 sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/UnexpectedFilterCreateException.java diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/DuplicateMetricIdException.java b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/DuplicateMetricIdException.java deleted file mode 100644 index 6c6eb054dba29..0000000000000 --- a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/DuplicateMetricIdException.java +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -package com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.filtering; - -public class DuplicateMetricIdException extends Exception { - public DuplicateMetricIdException(String message) { - super(message); - } -} diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/MetricFailureToCreateException.java b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/MetricFailureToCreateException.java deleted file mode 100644 index df93098fba8f7..0000000000000 --- a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/MetricFailureToCreateException.java +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -package com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.filtering; - -public class MetricFailureToCreateException extends Exception { - public MetricFailureToCreateException(String message) { - super(message); - } -} diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/TelemetryTypeException.java b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/TelemetryTypeException.java deleted file mode 100644 index 714a3bb9952dd..0000000000000 --- a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/TelemetryTypeException.java +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -package com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.filtering; - -public class TelemetryTypeException extends Exception { - public TelemetryTypeException(String message) { - super(message); - } -} diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/UnexpectedFilterCreateException.java b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/UnexpectedFilterCreateException.java deleted file mode 100644 index f8bc94b4969e0..0000000000000 --- a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/UnexpectedFilterCreateException.java +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -package com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.filtering; - -public class UnexpectedFilterCreateException extends Exception { - public UnexpectedFilterCreateException(String message) { - super(message); - } -}