diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/checkstyle-suppressions.xml b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/checkstyle-suppressions.xml index d505a0d2cd32..dcce54a16b2b 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/checkstyle-suppressions.xml +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/checkstyle-suppressions.xml @@ -252,6 +252,7 @@ + @@ -368,6 +369,7 @@ + diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/QuickPulseCoordinator.java b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/QuickPulseCoordinator.java index 2cb45cdb8d6e..a2be60112d2d 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/QuickPulseCoordinator.java +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/QuickPulseCoordinator.java @@ -85,6 +85,7 @@ private long sendData() { // in an error state (ping once a minute) if the first ping after the failing post also fails. long errorDelayInNs = TimeUnit.SECONDS.toNanos(40); pingSender.resetLastValidRequestTimeNs(dataSender.getLastValidPostRequestTimeNs() - errorDelayInNs); + logger.verbose("Switching to fallback mode."); return waitOnErrorInMillis; case QP_IS_OFF: @@ -95,6 +96,7 @@ private long sendData() { // sender to go into backoff state immediately instead of waiting 60s to go into backoff state like // the spec describes. See: https://github.com/aep-health-and-standards/Telemetry-Collection-Spec/blob/main/ApplicationInsights/livemetrics.md#timings pingSender.resetLastValidRequestTimeNs(dataSender.getLastValidPostRequestTimeNs()); + logger.verbose("Switching to ping mode."); return qpsServicePollingIntervalHintMillis > 0 ? qpsServicePollingIntervalHintMillis : waitBetweenPingsInMillis; @@ -118,10 +120,12 @@ private long ping() { collector.setQuickPulseStatus(qpStatus); switch (qpStatus) { case ERROR: + logger.verbose("In fallback mode"); return waitOnErrorInMillis; case QP_IS_ON: pingMode = false; + logger.verbose("Switching to post mode"); // Below two lines are necessary because there are cases where the last valid request is a ping // before a failing post. This can happen in cases where authentication fails - pings would return // http 200 but posts http 401. 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 18a5d052368f..c11f574997dd 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 @@ -29,6 +29,7 @@ import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.Trace; import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.TelemetryType; import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.AggregationType; +import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.CollectionConfigurationError; import com.azure.monitor.opentelemetry.autoconfigure.implementation.utils.CpuPerformanceCounterCalculator; import reactor.util.annotation.Nullable; @@ -59,7 +60,6 @@ final class QuickPulseDataCollector { private volatile Supplier instrumentationKeySupplier; - // TODO (harskaur): Track projection (runtime) related errors in future PR private final AtomicReference configuration; QuickPulseDataCollector(AtomicReference configuration) { @@ -77,7 +77,8 @@ synchronized void disable() { synchronized void enable(Supplier instrumentationKeySupplier) { this.instrumentationKeySupplier = instrumentationKeySupplier; - counters.set(new Counters(configuration.get().getValidProjectionInitInfo())); + FilteringConfiguration config = configuration.get(); + counters.set(new Counters(config.getValidProjectionInitInfo(), config.getErrors())); } synchronized void setQuickPulseStatus(QuickPulseStatus quickPulseStatus) { @@ -91,7 +92,9 @@ synchronized QuickPulseStatus getQuickPulseStatus() { @Nullable synchronized FinalCounters getAndRestart() { - Counters currentCounters = counters.getAndSet(new Counters(configuration.get().getValidProjectionInitInfo())); + FilteringConfiguration config = configuration.get(); + Counters currentCounters + = counters.getAndSet(new Counters(config.getValidProjectionInitInfo(), config.getErrors())); if (currentCounters != null) { return new FinalCounters(currentCounters); } @@ -180,7 +183,6 @@ private void applyMetricFilters(TelemetryColumns columns, TelemetryType telemetr List metricsConfig = currentConfig.fetchMetricConfigForTelemetryType(telemetryType); for (DerivedMetricInfo derivedMetricInfo : metricsConfig) { if (Filter.checkMetricFilters(derivedMetricInfo, columns)) { - // TODO (harskaur): In future PR, track any error that comes from calculateProjection currentCounters.derivedMetrics.calculateProjection(derivedMetricInfo, columns); } } @@ -411,6 +413,8 @@ class FinalCounters { final Map projections; + final List configErrors; + private FinalCounters(Counters currentCounters) { processPhysicalMemory = getPhysicalMemory(memory); @@ -431,6 +435,7 @@ private FinalCounters(Counters currentCounters) { this.documentList.addAll(currentCounters.documentList); } this.projections = currentCounters.derivedMetrics.fetchFinalDerivedMetricValues(); + this.configErrors = currentCounters.configErrors; } @@ -486,8 +491,11 @@ static class Counters { final DerivedMetricProjections derivedMetrics; - Counters(Map projectionInfo) { + final List configErrors; + + Counters(Map projectionInfo, List errors) { derivedMetrics = new DerivedMetricProjections(projectionInfo); + configErrors = errors; } static long encodeCountAndDuration(long count, long duration) { 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 cbda612dab17..6b86026d2b21 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 @@ -93,6 +93,7 @@ private MonitoringDataPoint buildMonitoringDataPoint(QuickPulseDataCollector.Fin point.setVersion(sdkVersion); point.setTimestamp(OffsetDateTime.now()); point.setMetrics(addMetricsToMonitoringDataPoint(counters)); + point.setCollectionConfigurationErrors(counters.configErrors); return point; } diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/QuickPulseDataSender.java b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/QuickPulseDataSender.java index cee360f43d47..40a666923aea 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/QuickPulseDataSender.java +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/QuickPulseDataSender.java @@ -6,6 +6,7 @@ import com.azure.core.http.rest.Response; import com.azure.core.util.logging.ClientLogger; +import com.azure.core.util.logging.LogLevel; import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.filtering.FilteringConfiguration; import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.LiveMetricsRestAPIsForClientSDKs; import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.CollectionConfigurationInfo; @@ -79,13 +80,17 @@ public void run() { dataPointList.add(point); Date currentDate = new Date(); long transmissionTimeInTicks = currentDate.getTime() * 10000 + TICKS_AT_EPOCH; + String etag = configuration.get().getETag(); + + if (logger.canLogAtLevel(LogLevel.VERBOSE)) { + logger.verbose("Attempting to send data points to quickpulse with etag {}: {}", etag, + printListOfMonitoringPoints(dataPointList)); + } + try { - // TODO (harskaur): remove logging when manual testing done - logger.verbose("Monitoring point: {}", point.toJsonString()); - logger.verbose("etag: {}", configuration.get().getETag()); Response responseMono = liveMetricsRestAPIsForClientSDKs - .publishNoCustomHeadersWithResponseAsync(endpointPrefix, instrumentationKey.get(), - configuration.get().getETag(), transmissionTimeInTicks, dataPointList) + .publishNoCustomHeadersWithResponseAsync(endpointPrefix, instrumentationKey.get(), etag, + transmissionTimeInTicks, dataPointList) .block(); if (responseMono == null) { // this shouldn't happen, the mono should complete with a response or a failure @@ -105,17 +110,17 @@ public void run() { lastValidRequestTimeNs = sendTime; CollectionConfigurationInfo body = responseMono.getValue(); - if (body != null && !configuration.get().getETag().equals(body.getETag())) { + if (body != null && !etag.equals(body.getETag())) { configuration.set(new FilteringConfiguration(body)); - // TODO (harskaur): remove logging when manual testing done try { - logger.verbose("passed in config {}", body.toJsonString()); + logger.verbose("Received a new live metrics filtering configuration from post response: {}", + body.toJsonString()); } catch (IOException e) { - logger.error(e.getMessage()); + logger.verbose(e.getMessage()); } } - } catch (RuntimeException | IOException e) { // this includes ServiceErrorException & RuntimeException thrown from quickpulse post api + } catch (RuntimeException e) { // this includes ServiceErrorException & RuntimeException thrown from quickpulse post api onPostError(sendTime); logger.error( "QuickPulseDataSender received a service error while attempting to send data to quickpulse {}", @@ -136,6 +141,20 @@ private void onPostError(long sendTime) { } } + private String printListOfMonitoringPoints(List points) { + StringBuilder dataPointsPrint = new StringBuilder("["); + for (MonitoringDataPoint p : points) { + try { + dataPointsPrint.append(p.toJsonString()); + dataPointsPrint.append("\n"); + } catch (IOException e) { + logger.verbose(e.getMessage()); + } + } + dataPointsPrint.append("]"); + return dataPointsPrint.toString(); + } + public void setRedirectEndpointPrefix(String endpointPrefix) { this.redirectEndpointPrefix = endpointPrefix; } diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/QuickPulsePingSender.java b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/QuickPulsePingSender.java index b9fca6455f25..92c4afe5ff4b 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/QuickPulsePingSender.java +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/QuickPulsePingSender.java @@ -17,6 +17,7 @@ import com.azure.monitor.opentelemetry.autoconfigure.implementation.utils.Strings; import reactor.util.annotation.Nullable; +import java.io.IOException; import java.net.URL; import java.util.Date; import java.util.concurrent.atomic.AtomicBoolean; @@ -104,6 +105,12 @@ IsSubscribedHeaders ping(String redirectedEndpoint) { CollectionConfigurationInfo body = responseMono.getValue(); if (body != null && !configuration.get().getETag().equals(body.getETag())) { + try { + logger.verbose("Received a new live metrics filtering configuration from ping response: {}", + body.toJsonString()); + } catch (IOException e) { + logger.verbose(e.getMessage()); + } configuration.set(new FilteringConfiguration(body)); } diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/ConfigErrorTracker.java b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/ConfigErrorTracker.java new file mode 100644 index 000000000000..c973dc80778c --- /dev/null +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/ConfigErrorTracker.java @@ -0,0 +1,56 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.filtering; + +import com.azure.core.util.logging.ClientLogger; +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.List; + +public class ConfigErrorTracker { + private final List errors = new ArrayList<>(); + + private static final ClientLogger LOGGER = new ClientLogger(ConfigErrorTracker.class); + + public void addError(String message, String eTag, String id, boolean isDerivedMetricId) { + CollectionConfigurationError error = new CollectionConfigurationError(); + error.setMessage(message); + error.setCollectionConfigurationErrorType(setErrorType(message)); + + 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); + + errors.add(error); + // This message gets logged once for every error we see on config validation. Config validation + // only happens once per config change. + LOGGER.verbose("{}. Due to this misconfiguration the {} rule with id {} will be ignored by the SDK.", message, + isDerivedMetricId ? "derived metric" : "document filter conjunction", id); + } + + private CollectionConfigurationErrorType setErrorType(String message) { + if (message.contains("telemetry type")) { + return CollectionConfigurationErrorType.METRIC_TELEMETRY_TYPE_UNSUPPORTED; + } else if (message.contains("duplicate metric id")) { + return CollectionConfigurationErrorType.METRIC_DUPLICATE_IDS; + } + return CollectionConfigurationErrorType.FILTER_FAILURE_TO_CREATE_UNEXPECTED; + } + + public List getErrors() { + return new ArrayList<>(errors); + } +} 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 6d90d17f1baa..ee5cc5781675 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 @@ -2,6 +2,7 @@ // Licensed under the MIT License. package com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.filtering; +import com.azure.core.util.logging.ClientLogger; import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.FilterInfo; import java.util.HashMap; @@ -9,6 +10,7 @@ public class CustomDimensions { private final Map customDimensions; + private static final ClientLogger LOGGER = new ClientLogger(CustomDimensions.class); public CustomDimensions(Map customDimensions, Map customMeasurements) { Map resultMap = new HashMap<>(); @@ -47,9 +49,14 @@ public double getCustomDimValueForProjection(String key) { try { return Double.parseDouble(value); } catch (NumberFormatException e) { - + LOGGER.verbose( + "The value \"{}\" for the custom dimension \"{}\" could not be converted to a numeric value for a derived metric projection", + value, key); } + return Double.NaN; } + LOGGER.verbose( + "The custom dimension could not be found in this telemetry item when calculating a derived metric."); return Double.NaN; } 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 896a84f63a23..84e22b60f47d 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 @@ -3,6 +3,7 @@ package com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.filtering; +import com.azure.core.util.logging.ClientLogger; import com.azure.monitor.opentelemetry.autoconfigure.implementation.models.RemoteDependencyData; import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.FilterInfo; import com.azure.monitor.opentelemetry.autoconfigure.implementation.utils.FormattedDuration; @@ -15,17 +16,26 @@ public class DependencyDataColumns implements TelemetryColumns { private final CustomDimensions customDims; private final Map mapping = new HashMap<>(); + private static final ClientLogger LOGGER = new ClientLogger(DependencyDataColumns.class); + public DependencyDataColumns(RemoteDependencyData rdData) { customDims = new CustomDimensions(rdData.getProperties(), rdData.getMeasurements()); mapping.put(KnownDependencyColumns.TARGET, rdData.getTarget()); - mapping.put(KnownDependencyColumns.DURATION, - FormattedDuration.getDurationFromTelemetryItemDurationString(rdData.getDuration())); + + long durationMicroSec = FormattedDuration.getDurationFromTelemetryItemDurationString(rdData.getDuration()); + if (durationMicroSec == -1) { + LOGGER.verbose("The provided timestamp {} could not be converted to microseconds", rdData.getDuration()); + } + mapping.put(KnownDependencyColumns.DURATION, durationMicroSec); + mapping.put(KnownDependencyColumns.SUCCESS, rdData.isSuccess()); mapping.put(KnownDependencyColumns.NAME, rdData.getName()); int resultCode; try { resultCode = Integer.parseInt(rdData.getResultCode()); } catch (NumberFormatException e) { + LOGGER.verbose("The provided result code {} could not be converted to a numeric value", + rdData.getResultCode()); resultCode = -1; } mapping.put(KnownDependencyColumns.RESULT_CODE, resultCode); 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 fb9efa6253b9..9c341ce12a6b 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 @@ -3,6 +3,7 @@ package com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.filtering; +import com.azure.core.util.logging.ClientLogger; import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.AggregationType; import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.DerivedMetricInfo; @@ -14,6 +15,8 @@ public class DerivedMetricProjections { public static final String COUNT = "Count()"; private final Map derivedMetricValues = new HashMap<>(); + private static final ClientLogger LOGGER = new ClientLogger(DerivedMetricProjections.class); + public DerivedMetricProjections(Map projectionInfo) { for (Map.Entry entry : projectionInfo.entrySet()) { AggregationType aggregationType = entry.getValue(); @@ -61,7 +64,10 @@ public void calculateProjection(DerivedMetricInfo derivedMetricInfo, TelemetryCo // For now, such cases produce Double.Nan and get skipped when calculating projection. } - if (!Double.isNaN(incrementBy)) { + if (Double.isNaN(incrementBy)) { + LOGGER.verbose( + "This telemetry item will not be counted in derived metric projections because the Duration or a CustomDimension column could not be interpreted as a numeric value."); + } else { calculateAggregation(derivedMetricInfo.getId(), incrementBy); } } 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 2b621bb4c398..1fe643ed1485 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,6 +2,7 @@ // Licensed under the MIT License. package com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.filtering; +import com.azure.core.util.logging.ClientLogger; 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; @@ -9,7 +10,7 @@ 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.CollectionConfigurationErrorType; +import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.CollectionConfigurationError; import java.util.Set; import java.util.List; @@ -17,6 +18,7 @@ import java.util.Map; import java.util.HashSet; import java.util.HashMap; +import java.util.Optional; public class FilteringConfiguration { @@ -33,14 +35,22 @@ public class FilteringConfiguration { private final Validator validator = new Validator(); + private final ConfigErrorTracker errorTracker = new ConfigErrorTracker(); + + private static final ClientLogger logger = new ClientLogger(FilteringConfiguration.class); + public FilteringConfiguration() { validDerivedMetricInfos = new HashMap<>(); validDocumentFilterConjunctionGroupInfos = new HashMap<>(); etag = ""; validProjectionInfo = new HashMap<>(); + logger.verbose( + "Initializing an empty live metrics filtering configuration - did not yet receive a configuration from ping or post."); } public FilteringConfiguration(CollectionConfigurationInfo configuration) { + logger.verbose("About to parse and validate a new live metrics filtering configuration with etag {}", + configuration.getETag()); validDerivedMetricInfos = parseMetricFilterConfiguration(configuration); validDocumentFilterConjunctionGroupInfos = parseDocumentFilterConfiguration(configuration); etag = configuration.getETag(); @@ -73,6 +83,10 @@ public Map getValidProjectionInitInfo() { return new HashMap<>(validProjectionInfo); } + public List getErrors() { + return errorTracker.getErrors(); + } + private Map>> parseDocumentFilterConfiguration(CollectionConfigurationInfo configuration) { Map>> result = new HashMap<>(); @@ -82,7 +96,13 @@ public Map getValidProjectionInitInfo() { .getDocumentFilterGroups()) { TelemetryType telemetryType = documentFilterGroupInfo.getTelemetryType(); FilterConjunctionGroupInfo filterGroup = documentFilterGroupInfo.getFilters(); - if (validator.isValidDocConjunctionGroupInfo(documentFilterGroupInfo)) { + + Optional docFilterGroupError + = validator.validateDocConjunctionGroupInfo(documentFilterGroupInfo); + + if (docFilterGroupError.isPresent()) { + errorTracker.addError(docFilterGroupError.get(), configuration.getETag(), documentStreamId, false); + } else { // passed validation, store valid docFilterGroupInfo if (!result.containsKey(telemetryType)) { result.put(telemetryType, new HashMap<>()); } @@ -96,6 +116,7 @@ public Map getValidProjectionInitInfo() { innerMap.put(documentStreamId, filterGroups); } } + } } return result; @@ -111,7 +132,11 @@ public Map getValidProjectionInitInfo() { if (!seenMetricIds.contains(id)) { seenMetricIds.add(id); - if (validator.isValidDerivedMetricInfo(derivedMetricInfo)) { + Optional dmiError = validator.validateDerivedMetricInfo(derivedMetricInfo); + if (dmiError.isPresent()) { + errorTracker.addError(dmiError.get(), configuration.getETag(), id, true); + + } else { // validation passed, store valid dmi if (result.containsKey(telemetryType)) { result.get(telemetryType).add(derivedMetricInfo); } else { @@ -121,9 +146,8 @@ public Map getValidProjectionInitInfo() { } } } else { - validator.constructAndTrackCollectionConfigurationError( - CollectionConfigurationErrorType.METRIC_DUPLICATE_IDS, - "A duplicate metric id was found in this configuration", configuration.getETag(), id, true); + errorTracker.addError("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/RequestDataColumns.java b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/quickpulse/filtering/RequestDataColumns.java index d575d945c1ac..cfa30b9edd70 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 @@ -3,6 +3,7 @@ package com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.filtering; +import com.azure.core.util.logging.ClientLogger; import com.azure.monitor.opentelemetry.autoconfigure.implementation.models.RequestData; import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.swagger.models.FilterInfo; import com.azure.monitor.opentelemetry.autoconfigure.implementation.utils.FormattedDuration; @@ -16,18 +17,28 @@ public class RequestDataColumns implements TelemetryColumns { private final Map mapping = new HashMap<>(); private final CustomDimensions customDims; + private static final ClientLogger LOGGER = new ClientLogger(RequestDataColumns.class); + public RequestDataColumns(RequestData requestData) { customDims = new CustomDimensions(requestData.getProperties(), requestData.getMeasurements()); mapping.put(KnownRequestColumns.URL, requestData.getUrl()); mapping.put(KnownRequestColumns.SUCCESS, requestData.isSuccess()); - mapping.put(KnownRequestColumns.DURATION, - FormattedDuration.getDurationFromTelemetryItemDurationString(requestData.getDuration())); + + long durationMicroSec = FormattedDuration.getDurationFromTelemetryItemDurationString(requestData.getDuration()); + if (durationMicroSec == -1) { + LOGGER.verbose("The provided timestamp {} could not be converted to microseconds", + requestData.getDuration()); + } + + mapping.put(KnownRequestColumns.DURATION, durationMicroSec); mapping.put(KnownRequestColumns.NAME, requestData.getName()); int responseCode; try { responseCode = Integer.parseInt(requestData.getResponseCode()); } catch (NumberFormatException e) { responseCode = -1; + LOGGER.verbose("The provided response code {} could not be converted to a numeric value", + requestData.getResponseCode()); } mapping.put(KnownRequestColumns.RESPONSE_CODE, responseCode); } 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 c73b0097a52f..b863191d1df0 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 @@ -8,14 +8,10 @@ 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 java.util.Optional; import static java.util.Arrays.asList; @@ -31,77 +27,63 @@ public class Validator { 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 - private final List errors = new ArrayList<>(); - - public boolean isValidDerivedMetricInfo(DerivedMetricInfo derivedMetricInfo) { + // The string in the return Optional represents an error encountered when validating the derived metric info. + // If the Optional is empty, that means validation passed. + public Optional validateDerivedMetricInfo(DerivedMetricInfo derivedMetricInfo) { TelemetryType telemetryType = TelemetryType.fromString(derivedMetricInfo.getTelemetryType()); if (!isValidTelemetryType(telemetryType)) { - return false; + return Optional + .of("The user selected a telemetry type that the SDK does not support for Live Metrics Filtering. " + + "Telemetry type: " + telemetryType); } if (!isNotCustomMetricProjection(derivedMetricInfo.getProjection())) { - return false; + return Optional.of("The user selected a projection of Custom Metric, which this SDK does not support."); } for (FilterConjunctionGroupInfo conjunctionGroupInfo : derivedMetricInfo.getFilterGroups()) { for (FilterInfo filter : conjunctionGroupInfo.getFilters()) { - if (!isValidFieldName(filter.getFieldName(), telemetryType)) { - return false; + Optional error = validateFieldName(filter.getFieldName()); + if (error.isPresent()) { + return error; } - if (!isValidPredicateAndComparand(filter)) { - return false; + + error = validatePredicateAndComparand(filter); + if (error.isPresent()) { + return error; } } } - return true; + return Optional.empty(); } - public boolean - isValidDocConjunctionGroupInfo(DocumentFilterConjunctionGroupInfo documentFilterConjunctionGroupInfo) { + // The string in the return Optional represents an error encountered when validating the DocumentFilterConjunctionGroupInfo. + // If the Optional is empty, that means validation passed. + public Optional + validateDocConjunctionGroupInfo(DocumentFilterConjunctionGroupInfo documentFilterConjunctionGroupInfo) { TelemetryType telemetryType = documentFilterConjunctionGroupInfo.getTelemetryType(); if (!isValidTelemetryType(telemetryType)) { - return false; + return Optional + .of("The user selected a telemetry type that the SDK does not support for Live Metrics Filtering. " + + "Telemetry type: " + telemetryType); } FilterConjunctionGroupInfo conjunctionGroupInfo = documentFilterConjunctionGroupInfo.getFilters(); for (FilterInfo filter : conjunctionGroupInfo.getFilters()) { - if (!isValidFieldName(filter.getFieldName(), telemetryType)) { - return false; + Optional error = validateFieldName(filter.getFieldName()); + if (error.isPresent()) { + return error; } - if (!isValidPredicateAndComparand(filter)) { - return false; + + error = validatePredicateAndComparand(filter); + if (error.isPresent()) { + return error; } } - return true; - } - - 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 - errors.add(error); + return Optional.empty(); } 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)) { @@ -115,46 +97,53 @@ private boolean isValidTelemetryType(TelemetryType telemetryType) { 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 + // The string in the return Optional represents an error encountered when validating the field name. + // If the Optional is empty, that means validation passed. + private Optional validateFieldName(String fieldName) { if (fieldName.isEmpty()) { - return false; + return Optional.of("The user specified an empty field name for a filter."); } if (fieldName.startsWith("CustomMetrics.")) { - return false; + return Optional.of( + "The user selected a custom metric field name, but this SDK does not support filtering of custom metrics."); } - return true; + return Optional.empty(); } - private boolean isValidPredicateAndComparand(FilterInfo filter) { - // TODO (harskaur): In ErrorTracker PR, create an error message & track an error for each false case + // The string in the return Optional represents an error encountered when validating the predicate/comparand. + // If the Optional is empty, that means validation passed. + private Optional validatePredicateAndComparand(FilterInfo filter) { if (filter.getComparand().isEmpty()) { // It is possible to not type in a comparand and the service side to send us empty string. - return false; + return Optional.of("The user specified an empty comparand value for a filter."); } 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. - return false; + return Optional.of("The specified predicate is not supported for the fieldName * (Any field): " + + filter.getPredicate().getValue()); } 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) { - return false; + return Optional + .of("The duration string provided by the user could not be parsed to a numeric value: " + + 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) { - return false; + return Optional + .of("The result/response code specified by the user did not parse to a numeric value: " + + filter.getComparand()); } } } else if (knownStringColumns.contains(filter.getFieldName()) @@ -162,9 +151,10 @@ 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 Optional.of("The user selected a predicate (" + filter.getPredicate().getValue() + + ") that is not supported for the field name " + filter.getFieldName()); } } - return true; + return Optional.empty(); } } 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 e97835cfc13b..aa28c718bdb9 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 @@ -253,6 +253,14 @@ void honorDefaultConfig() { createTelemetryItemsForFiltering(collector); QuickPulseDataCollector.FinalCounters counters = collector.peek(); + + // The default documents config asks to collect documents of the "Event" telemetry type + // As the SDK does not collect the Event telemetry type for live metrics, we consider that part of the config invalid + List errors = counters.configErrors; + assertThat(errors.size()).isEqualTo(1); + assertThat(errors.get(0).getCollectionConfigurationErrorType()) + .isEqualTo(CollectionConfigurationErrorType.METRIC_TELEMETRY_TYPE_UNSUPPORTED); + List documents = counters.documentList; assertThat(documents.size()).isEqualTo(4); 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 d1b0f75adee2..e79a79355264 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 @@ -27,9 +27,7 @@ void rejectInvalidTelemetryTypesForDmi(TelemetryType telemetryType) { 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)); + assertTrue(validator.validateDerivedMetricInfo(dmi).isPresent()); } @ParameterizedTest @@ -37,7 +35,7 @@ void rejectInvalidTelemetryTypesForDmi(TelemetryType telemetryType) { void rejectInvalidTelemetryTypesForDocs(TelemetryType telemetryType) { Validator validator = new Validator(); DocumentFilterConjunctionGroupInfo docGroup = createDocGroupWithNoFilters(telemetryType); - assertFalse(validator.isValidDocConjunctionGroupInfo(docGroup)); + assertTrue(validator.validateDocConjunctionGroupInfo(docGroup).isPresent()); } @ParameterizedTest @@ -47,9 +45,7 @@ void acceptValidTelemetryTypeForDmi(TelemetryType telemetryType) { 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)); + assertFalse(validator.validateDerivedMetricInfo(dmi).isPresent()); } @ParameterizedTest @@ -57,7 +53,7 @@ void acceptValidTelemetryTypeForDmi(TelemetryType telemetryType) { void acceptValidTelemetryTypeForDocs(TelemetryType telemetryType) { Validator validator = new Validator(); DocumentFilterConjunctionGroupInfo docGroup = createDocGroupWithNoFilters(telemetryType); - assertTrue(validator.isValidDocConjunctionGroupInfo(docGroup)); + assertFalse(validator.validateDocConjunctionGroupInfo(docGroup).isPresent()); } @Test @@ -66,7 +62,7 @@ void rejectCustomMetricProjection() { Validator validator = new Validator(); DerivedMetricInfo dmi = createDerivedMetricInfo("random-id", TelemetryType.TRACE.getValue(), AggregationType.SUM, AggregationType.SUM, "CustomMetrics.property", filterGroups); - assertFalse(validator.isValidDerivedMetricInfo(dmi)); + assertTrue(validator.validateDerivedMetricInfo(dmi).isPresent()); } @Test @@ -76,7 +72,7 @@ void rejectCustomMetricFieldName() { Validator validator = new Validator(); DerivedMetricInfo dmi = createDerivedMetricInfo("random-id", TelemetryType.TRACE.getValue(), AggregationType.SUM, AggregationType.SUM, DerivedMetricProjections.COUNT, filterGroups); - assertFalse(validator.isValidDerivedMetricInfo(dmi)); + assertTrue(validator.validateDerivedMetricInfo(dmi).isPresent()); } @ParameterizedTest @@ -86,10 +82,7 @@ void rejectInvalidFiltersForDmi(FilterInfo 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)); + assertTrue(validator.validateDerivedMetricInfo(dmi).isPresent()); } @ParameterizedTest @@ -97,7 +90,7 @@ void rejectInvalidFiltersForDmi(FilterInfo filter) { void rejectInvalidFiltersForDocs(FilterInfo filter) { Validator validator = new Validator(); DocumentFilterConjunctionGroupInfo docGroup = createDocGroupWithOneFilter(TelemetryType.REQUEST, filter); - assertFalse(validator.isValidDocConjunctionGroupInfo(docGroup)); + assertTrue(validator.validateDocConjunctionGroupInfo(docGroup).isPresent()); } @Test @@ -114,12 +107,12 @@ void rejectInvalidGroupWithMultipleFilters() { DerivedMetricInfo dmi = createDerivedMetricInfo("random-id", TelemetryType.REQUEST.getValue(), AggregationType.SUM, AggregationType.SUM, DerivedMetricProjections.COUNT, filterGroups); - assertFalse(validator.isValidDerivedMetricInfo(dmi)); + assertTrue(validator.validateDerivedMetricInfo(dmi).isPresent()); DocumentFilterConjunctionGroupInfo docGroup = new DocumentFilterConjunctionGroupInfo(); docGroup.setFilters(filterGroup); docGroup.setTelemetryType(TelemetryType.REQUEST); - assertFalse(validator.isValidDocConjunctionGroupInfo(docGroup)); + assertTrue(validator.validateDocConjunctionGroupInfo(docGroup).isPresent()); } @ParameterizedTest @@ -129,10 +122,7 @@ void acceptValidFiltersForDmi(FilterInfo 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)); + assertFalse(validator.validateDerivedMetricInfo(dmi).isPresent()); } @ParameterizedTest @@ -140,7 +130,7 @@ void acceptValidFiltersForDmi(FilterInfo filter) { void acceptValidFiltersForDocs(FilterInfo filter) { Validator validator = new Validator(); DocumentFilterConjunctionGroupInfo docGroup = createDocGroupWithOneFilter(TelemetryType.REQUEST, filter); - assertTrue(validator.isValidDocConjunctionGroupInfo(docGroup)); + assertFalse(validator.validateDocConjunctionGroupInfo(docGroup).isPresent()); } private static List invalidTelemetryTypes() {