Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Improve AzureMonitorScraper Error Handling for Time Series Missing Requested Dimension Value #2345

Merged
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog/content/experimental/unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ version:

#### Scraper

- {{% tag fixed %}} Improve handling of time series with missing dimensions that are requested ([#2331](https:/tomkerkhove/promitor/issues/2331))
- {{% tag changed %}} Switch to Mariner distroless base images
- {{% tag security %}} Patch for [CVE-2023-29331](https:/advisories/GHSA-555c-2p6r-68mm) (High)

Expand Down
4 changes: 3 additions & 1 deletion src/Promitor.Core.Scraping/AzureMonitorScraper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,9 @@ protected override async Task<ScrapeResult> ScrapeResourceAsync(string subscript
{
Logger.LogWarning("No metric information found for metric {MetricName} with dimension {MetricDimension}. Details: {Details}", metricsNotFoundException.Name, metricsNotFoundException.Dimension, metricsNotFoundException.Details);

var measuredMetric = string.IsNullOrWhiteSpace(dimensionName) ? MeasuredMetric.CreateWithoutDimension(null) : MeasuredMetric.CreateForDimension(null, dimensionName, "unknown");
var measuredMetric = string.IsNullOrWhiteSpace(dimensionName)
? MeasuredMetric.CreateWithoutDimension(null)
: MeasuredMetric.CreateForDimension(null, dimensionName, "unknown");
measuredMetrics.Add(measuredMetric);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using System;
using GuardNet;

namespace Promitor.Core.Metrics.Exceptions
{
public class MissingDimensionException : Exception
{
/// <summary>
/// Constructor
/// </summary>
/// <param name="metricName">Name of the dimension</param>
public MissingDimensionException(string dimensionName) : base($"No value found for dimension '{dimensionName}'")
{
Guard.NotNullOrWhitespace(dimensionName, nameof(dimensionName));

DimensionName = dimensionName;
}

/// <summary>
/// Name of the dimension
/// </summary>
public string DimensionName { get; }
}
}
10 changes: 8 additions & 2 deletions src/Promitor.Core/Metrics/MeasuredMetric.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Linq;
using GuardNet;
using Microsoft.Azure.Management.Monitor.Fluent.Models;
using Promitor.Core.Metrics.Exceptions;

namespace Promitor.Core.Metrics
{
Expand Down Expand Up @@ -63,9 +64,14 @@ public static MeasuredMetric CreateForDimension(double? value, string dimensionN
{
Guard.NotNullOrWhitespace(dimensionName, nameof(dimensionName));
Guard.NotNull(timeseries, nameof(timeseries));
Guard.For<ArgumentException>(() => timeseries.Metadatavalues.Any() == false);

var dimensionMetadataValue = timeseries.Metadatavalues.Where(metadataValue => metadataValue.Name?.Value.Equals(dimensionName, StringComparison.InvariantCultureIgnoreCase) == true);
if(!dimensionMetadataValue.Any())
{
throw new MissingDimensionException(dimensionName);
tomkerkhove marked this conversation as resolved.
Show resolved Hide resolved
}

var dimensionValue = timeseries.Metadatavalues.Single(metadataValue => metadataValue.Name?.Value.Equals(dimensionName, StringComparison.InvariantCultureIgnoreCase) == true);
var dimensionValue = dimensionMetadataValue.First();
hkfgo marked this conversation as resolved.
Show resolved Hide resolved
return CreateForDimension(value, dimensionName, dimensionValue.Value);
}

Expand Down
17 changes: 14 additions & 3 deletions src/Promitor.Integrations.AzureMonitor/AzureMonitorClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Microsoft.Azure.Management.Monitor.Fluent.Models;
using Microsoft.Azure.Management.ResourceManager.Fluent;
using Microsoft.Azure.Management.ResourceManager.Fluent.Authentication;
using Newtonsoft.Json;
tomkerkhove marked this conversation as resolved.
Show resolved Hide resolved
using GuardNet;
using Microsoft.Azure.Management.ResourceManager.Fluent.Core;
using Microsoft.Extensions.Caching.Memory;
Expand All @@ -22,6 +23,7 @@
using Promitor.Integrations.AzureMonitor.Logging;
using Promitor.Integrations.AzureMonitor.RequestHandlers;
using Promitor.Integrations.Azure.Authentication;
using Promitor.Core.Metrics.Exceptions;
tomkerkhove marked this conversation as resolved.
Show resolved Hide resolved

namespace Promitor.Integrations.AzureMonitor
{
Expand Down Expand Up @@ -108,9 +110,18 @@ public async Task<List<MeasuredMetric>> QueryMetricAsync(string metricName, stri

// Get the metric value according to the requested aggregation type
var requestedMetricAggregate = InterpretMetricValue(aggregationType, mostRecentMetricValue);

var measuredMetric = string.IsNullOrWhiteSpace(metricDimension) ? MeasuredMetric.CreateWithoutDimension(requestedMetricAggregate) : MeasuredMetric.CreateForDimension(requestedMetricAggregate, metricDimension, timeseries);
measuredMetrics.Add(measuredMetric);
try
{
var measuredMetric = string.IsNullOrWhiteSpace(metricDimension)
? MeasuredMetric.CreateWithoutDimension(requestedMetricAggregate)
: MeasuredMetric.CreateForDimension(requestedMetricAggregate, metricDimension, timeseries);
measuredMetrics.Add(measuredMetric);
}
catch (MissingDimensionException)
{
_logger.LogWarning("{MetricName} has return a time series with empty value for {Dimension} and the measurements will be dropped", metricName, metricDimension);
tomkerkhove marked this conversation as resolved.
Show resolved Hide resolved
_logger.LogDebug("The violating time series has content {Details}", JsonConvert.SerializeObject(timeseries));
}
}

return measuredMetrics;
Expand Down
35 changes: 35 additions & 0 deletions src/Promitor.Tests.Unit/Metrics/MeasuredMetricTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using System.Collections.Generic;
using System.ComponentModel;
using Promitor.Core.Metrics;
using Microsoft.Azure.Management.Monitor.Fluent.Models;
using Xunit;
using Promitor.Core.Metrics.Exceptions;

namespace Promitor.Tests.Unit.Metrics
{
[Category("Unit")]
public class MeasuredMeticTest : UnitTest
{
[Fact]
public void Create_MeasuredMetric_With_Dimension_HappyPath_Succeeds()
{
var dimensionName = "dimTest";
var dimensionValue = "dimTest1";
var timeSeries = new TimeSeriesElement(new List<MetadataValue> { new(name: new LocalizableString(dimensionName), value: dimensionValue)});
var measuredMetric = MeasuredMetric.CreateForDimension(1, dimensionName, timeSeries);
Assert.Equal(measuredMetric.DimensionName, dimensionName);
Assert.Equal(measuredMetric.DimensionValue, dimensionValue);
Assert.Equal(measuredMetric.Value, 1);
}

[Fact]
public void Create_MeasuredMetric_Missing_Dimension_Throws_Targeted_Exception()
{
var dimensionName = "dimTest";
var timeSeries = new TimeSeriesElement(new List<MetadataValue> {});
tomkerkhove marked this conversation as resolved.
Show resolved Hide resolved
MissingDimensionException ex = Assert.Throws<MissingDimensionException>(() => MeasuredMetric.CreateForDimension(1, dimensionName, timeSeries));
Assert.Equal(ex.DimensionName, dimensionName)

Check failure on line 31 in src/Promitor.Tests.Unit/Metrics/MeasuredMetricTests.cs

View workflow job for this annotation

GitHub Actions / Verify Codebase

; expected

Check failure on line 31 in src/Promitor.Tests.Unit/Metrics/MeasuredMetricTests.cs

View workflow job for this annotation

GitHub Actions / Verify Codebase

; expected

Check failure on line 31 in src/Promitor.Tests.Unit/Metrics/MeasuredMetricTests.cs

View workflow job for this annotation

GitHub Actions / Analyse

; expected

Check failure on line 31 in src/Promitor.Tests.Unit/Metrics/MeasuredMetricTests.cs

View workflow job for this annotation

GitHub Actions / Analyse

; expected
}
}

}
Loading