diff --git a/src/Promitor.Core.Configuration/Defaults.cs b/src/Promitor.Core.Configuration/Defaults.cs
index ec2b881e6..2fe691fb3 100644
--- a/src/Promitor.Core.Configuration/Defaults.cs
+++ b/src/Promitor.Core.Configuration/Defaults.cs
@@ -12,6 +12,7 @@ public static class Server
public static class Prometheus
{
public static string ScrapeEndpointBaseUri { get; } = "/metrics";
+ public static double MetricUnavailableValue { get; } = double.NaN;
}
public static class MetricsConfiguration
diff --git a/src/Promitor.Core.Configuration/Model/Prometheus/PrometheusConfiguration.cs b/src/Promitor.Core.Configuration/Model/Prometheus/PrometheusConfiguration.cs
index ca9bc6571..a9a85e7a1 100644
--- a/src/Promitor.Core.Configuration/Model/Prometheus/PrometheusConfiguration.cs
+++ b/src/Promitor.Core.Configuration/Model/Prometheus/PrometheusConfiguration.cs
@@ -3,5 +3,6 @@
public class PrometheusConfiguration
{
public ScrapeEndpointConfiguration ScrapeEndpoint { get; set; } = new ScrapeEndpointConfiguration();
+ public double? MetricUnavailableValue { get; set; } = Defaults.Prometheus.MetricUnavailableValue;
}
}
diff --git a/src/Promitor.Core.Scraping/Factories/MetricScraperFactory.cs b/src/Promitor.Core.Scraping/Factories/MetricScraperFactory.cs
index b4716c272..925d21c40 100644
--- a/src/Promitor.Core.Scraping/Factories/MetricScraperFactory.cs
+++ b/src/Promitor.Core.Scraping/Factories/MetricScraperFactory.cs
@@ -6,6 +6,7 @@
using Promitor.Core.Scraping.Configuration.Model;
using Promitor.Core.Scraping.Configuration.Model.Metrics;
using Promitor.Core.Scraping.Interfaces;
+using Promitor.Core.Scraping.Prometheus.Interfaces;
using Promitor.Core.Scraping.ResourceTypes;
using Promitor.Core.Telemetry.Interfaces;
using Promitor.Core.Telemetry.Metrics.Interfaces;
@@ -38,12 +39,13 @@ public MetricScraperFactory(IConfiguration configuration, FeatureToggleClient fe
///
/// Metadata concerning the Azure resources
/// Resource type to scrape
+ /// Metrics collector for our Prometheus scraping endpoint
/// Metrics collector for our runtime
public IScraper CreateScraper(ResourceType metricDefinitionResourceType, AzureMetadata azureMetadata,
- IRuntimeMetricsCollector runtimeMetricsCollector)
+ IPrometheusMetricWriter prometheusMetricWriter, IRuntimeMetricsCollector runtimeMetricsCollector)
{
var azureMonitorClient = CreateAzureMonitorClient(azureMetadata, runtimeMetricsCollector);
- var scraperConfiguration = new ScraperConfiguration(azureMetadata, azureMonitorClient, _featureToggleClient, _logger, _exceptionTracker);
+ var scraperConfiguration = new ScraperConfiguration(azureMetadata, azureMonitorClient, prometheusMetricWriter, _featureToggleClient, _logger, _exceptionTracker);
switch (metricDefinitionResourceType)
{
diff --git a/src/Promitor.Core.Scraping/Prometheus/Interfaces/IPrometheusMetricWriter.cs b/src/Promitor.Core.Scraping/Prometheus/Interfaces/IPrometheusMetricWriter.cs
new file mode 100644
index 000000000..100700965
--- /dev/null
+++ b/src/Promitor.Core.Scraping/Prometheus/Interfaces/IPrometheusMetricWriter.cs
@@ -0,0 +1,9 @@
+using Promitor.Core.Scraping.Configuration.Model.Metrics;
+
+namespace Promitor.Core.Scraping.Prometheus.Interfaces
+{
+ public interface IPrometheusMetricWriter
+ {
+ void ReportMetric(MetricDefinition metricDefinition, ScrapeResult scrapedMetricResult);
+ }
+}
\ No newline at end of file
diff --git a/src/Promitor.Core.Scraping/Prometheus/PrometheusMetricWriter.cs b/src/Promitor.Core.Scraping/Prometheus/PrometheusMetricWriter.cs
new file mode 100644
index 000000000..d64252121
--- /dev/null
+++ b/src/Promitor.Core.Scraping/Prometheus/PrometheusMetricWriter.cs
@@ -0,0 +1,70 @@
+using System.Collections.Generic;
+using System.Linq;
+using GuardNet;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using Prometheus.Client;
+using Promitor.Core.Configuration;
+using Promitor.Core.Configuration.FeatureFlags;
+using Promitor.Core.Configuration.Model.Prometheus;
+using Promitor.Core.Scraping.Configuration.Model.Metrics;
+using Promitor.Core.Scraping.Prometheus.Interfaces;
+
+namespace Promitor.Core.Scraping.Prometheus
+{
+ public class PrometheusMetricWriter : IPrometheusMetricWriter
+ {
+ private readonly FeatureToggleClient _featureToggleClient;
+ private readonly IOptionsMonitor _prometheusConfiguration;
+ private readonly ILogger _logger;
+
+ public PrometheusMetricWriter(FeatureToggleClient featureToggleClient, IOptionsMonitor prometheusConfiguration, ILogger logger)
+ {
+ Guard.NotNull(featureToggleClient, nameof(featureToggleClient));
+ Guard.NotNull(prometheusConfiguration, nameof(prometheusConfiguration));
+ Guard.NotNull(logger, nameof(logger));
+
+ _featureToggleClient = featureToggleClient;
+ _prometheusConfiguration = prometheusConfiguration;
+ _logger = logger;
+ }
+
+ public void ReportMetric(MetricDefinition metricDefinition, ScrapeResult scrapedMetricResult)
+ {
+ var metricsTimestampFeatureFlag = _featureToggleClient.IsActive(ToggleNames.DisableMetricTimestamps, defaultFlagState: true);
+
+ var labels = DetermineLabels(metricDefinition, scrapedMetricResult);
+
+ var gauge = Metrics.CreateGauge(metricDefinition.Name, metricDefinition.Description, includeTimestamp: metricsTimestampFeatureFlag, labelNames: labels.Names);
+ var metricValue = DetermineMetricMeasurement(scrapedMetricResult);
+ gauge.WithLabels(labels.Values).Set(metricValue);
+ }
+
+ private double DetermineMetricMeasurement(ScrapeResult scrapedMetricResult)
+ {
+ var metricUnavailableValue = _prometheusConfiguration.CurrentValue?.MetricUnavailableValue ?? Defaults.Prometheus.MetricUnavailableValue;
+ return scrapedMetricResult.MetricValue ?? metricUnavailableValue;
+ }
+
+ private (string[] Names, string[] Values) DetermineLabels(MetricDefinition metricDefinition, ScrapeResult scrapeResult)
+ {
+ var labels = new Dictionary(scrapeResult.Labels);
+
+ if (metricDefinition?.Labels?.Any() == true)
+ {
+ foreach (var customLabel in metricDefinition.Labels)
+ {
+ if (labels.ContainsKey(customLabel.Key))
+ {
+ _logger.LogWarning("Custom label '{CustomLabelName}' was already specified with value 'LabelValue' instead of 'CustomLabelValue'. Ignoring...", customLabel.Key, labels[customLabel.Key], customLabel.Value);
+ continue;
+ }
+
+ labels.Add(customLabel.Key, customLabel.Value);
+ }
+ }
+
+ return (labels.Keys.ToArray(), labels.Values.ToArray());
+ }
+ }
+}
diff --git a/src/Promitor.Core.Scraping/ScrapeResult.cs b/src/Promitor.Core.Scraping/ScrapeResult.cs
index 371c0e27d..d556e799e 100644
--- a/src/Promitor.Core.Scraping/ScrapeResult.cs
+++ b/src/Promitor.Core.Scraping/ScrapeResult.cs
@@ -12,7 +12,7 @@ public class ScrapeResult
/// Resource group name that contains the resource that was scraped
/// Uri of the resource that was scraped
/// Value of the metric that was found
- public ScrapeResult(string subscriptionId, string resourceGroupName, string resourceUri, double metricValue) : this(subscriptionId, resourceGroupName, string.Empty, resourceUri, metricValue, new Dictionary())
+ public ScrapeResult(string subscriptionId, string resourceGroupName, string resourceUri, double? metricValue) : this(subscriptionId, resourceGroupName, string.Empty, resourceUri, metricValue, new Dictionary())
{
}
@@ -25,7 +25,7 @@ public class ScrapeResult
/// Uri of the resource that was scraped
/// Value of the metric that was found
/// A collection of custom labels to add to the scraping result
- public ScrapeResult(string subscriptionId, string resourceGroupName, string instanceName, string resourceUri, double metricValue, Dictionary customLabels)
+ public ScrapeResult(string subscriptionId, string resourceGroupName, string instanceName, string resourceUri, double? metricValue, Dictionary customLabels)
{
Guard.NotNullOrEmpty(subscriptionId, nameof(subscriptionId));
Guard.NotNullOrEmpty(resourceGroupName, nameof(resourceGroupName));
@@ -61,7 +61,7 @@ public ScrapeResult(string subscriptionId, string resourceGroupName, string inst
/// Name of the resource that is being scraped
/// Uri of the resource that was scraped
/// Value of the metric that was found
- public ScrapeResult(string subscriptionId, string resourceGroupName, string instanceName, string resourceUri, double metricValue) : this(subscriptionId, resourceGroupName, instanceName,resourceUri,metricValue, new Dictionary())
+ public ScrapeResult(string subscriptionId, string resourceGroupName, string instanceName, string resourceUri, double? metricValue) : this(subscriptionId, resourceGroupName, instanceName,resourceUri,metricValue, new Dictionary())
{
}
@@ -88,7 +88,7 @@ public ScrapeResult(string subscriptionId, string resourceGroupName, string inst
///
/// Value of the metric that was found
///
- public double MetricValue { get; }
+ public double? MetricValue { get; }
public Dictionary Labels { get; } = new Dictionary();
}
diff --git a/src/Promitor.Core.Scraping/Scraper.cs b/src/Promitor.Core.Scraping/Scraper.cs
index 03c511f71..457075e05 100644
--- a/src/Promitor.Core.Scraping/Scraper.cs
+++ b/src/Promitor.Core.Scraping/Scraper.cs
@@ -1,18 +1,16 @@
using System;
-using System.Collections.Generic;
-using System.Linq;
using System.Threading.Tasks;
using GuardNet;
using Microsoft.Azure.Management.Monitor.Fluent.Models;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
-using Prometheus.Client;
-using Promitor.Core.Configuration.FeatureFlags;
using Promitor.Core.Scraping.Configuration.Model;
using Promitor.Core.Scraping.Interfaces;
+using Promitor.Core.Scraping.Prometheus.Interfaces;
using Promitor.Core.Telemetry.Interfaces;
using Promitor.Integrations.AzureMonitor;
using MetricDefinition = Promitor.Core.Scraping.Configuration.Model.Metrics.MetricDefinition;
+
// ReSharper disable All
namespace Promitor.Core.Scraping
@@ -22,12 +20,12 @@ namespace Promitor.Core.Scraping
///
/// Type of metric definition that is being used
public abstract class Scraper : IScraper
- where TMetricDefinition : MetricDefinition, new()
+ where TMetricDefinition : MetricDefinition, new()
{
- private readonly ScraperConfiguration _scraperConfiguration;
private readonly IExceptionTracker _exceptionTracker;
- private readonly FeatureToggleClient _featureToggleClient;
private readonly ILogger _logger;
+ private readonly IPrometheusMetricWriter _prometheusMetricWriter;
+ private readonly ScraperConfiguration _scraperConfiguration;
///
/// Constructor
@@ -37,9 +35,9 @@ protected Scraper(ScraperConfiguration scraperConfiguration)
Guard.NotNull(scraperConfiguration, nameof(scraperConfiguration));
_logger = scraperConfiguration.Logger;
- _featureToggleClient = scraperConfiguration.FeatureToggleClient;
_exceptionTracker = scraperConfiguration.ExceptionTracker;
_scraperConfiguration = scraperConfiguration;
+ _prometheusMetricWriter = scraperConfiguration.PrometheusMetricWriter;
AzureMetadata = scraperConfiguration.AzureMetadata;
AzureMonitorClient = scraperConfiguration.AzureMonitorClient;
@@ -81,7 +79,7 @@ public async Task ScrapeAsync(MetricDefinition metricDefinition)
_logger.LogInformation("Found value '{MetricValue}' for metric '{MetricName}' with aggregation interval '{AggregationInterval}'", scrapedMetricResult, metricDefinition.Name, aggregationInterval);
- ReportMetric(metricDefinition, scrapedMetricResult);
+ _prometheusMetricWriter.ReportMetric(metricDefinition, scrapedMetricResult);
}
catch (ErrorResponseException errorResponseException)
{
@@ -93,16 +91,6 @@ public async Task ScrapeAsync(MetricDefinition metricDefinition)
}
}
- private void ReportMetric(MetricDefinition metricDefinition, ScrapeResult scrapedMetricResult)
- {
- var metricsTimestampFeatureFlag = _featureToggleClient.IsActive(ToggleNames.DisableMetricTimestamps, defaultFlagState: true);
-
- var labels = DetermineLabels(metricDefinition, scrapedMetricResult);
-
- var gauge = Metrics.CreateGauge(metricDefinition.Name, metricDefinition.Description, includeTimestamp: metricsTimestampFeatureFlag, labelNames: labels.Names);
- gauge.WithLabels(labels.Values).Set(scrapedMetricResult.MetricValue);
- }
-
private void HandleErrorResponseException(ErrorResponseException errorResponseException)
{
string reason = string.Empty;
@@ -141,27 +129,6 @@ private void HandleErrorResponseException(ErrorResponseException errorResponseEx
_exceptionTracker.Track(new Exception(reason));
}
- private (string[] Names, string[] Values) DetermineLabels(MetricDefinition metricDefinition, ScrapeResult scrapeResult)
- {
- var labels = new Dictionary(scrapeResult.Labels);
-
- if (metricDefinition?.Labels?.Any() == true)
- {
- foreach (var customLabel in metricDefinition.Labels)
- {
- if (labels.ContainsKey(customLabel.Key))
- {
- _logger.LogWarning("Custom label '{CustomLabelName}' was already specified with value 'LabelValue' instead of 'CustomLabelValue'. Ignoring...", customLabel.Key, labels[customLabel.Key], customLabel.Value);
- continue;
- }
-
- labels.Add(customLabel.Key, customLabel.Value);
- }
- }
-
- return (labels.Keys.ToArray(), labels.Values.ToArray());
- }
-
///
/// Scrapes the configured resource
///
@@ -170,7 +137,6 @@ private void HandleErrorResponseException(ErrorResponseException errorResponseEx
/// Definition of the metric to scrape
/// Aggregation for the metric to use
/// Interval that is used to aggregate metrics
- ///
protected abstract Task ScrapeResourceAsync(string subscriptionId, string resourceGroupName, TMetricDefinition metricDefinition, AggregationType aggregationType, TimeSpan aggregationInterval);
}
-}
+}
\ No newline at end of file
diff --git a/src/Promitor.Core.Scraping/ScraperConfiguration.cs b/src/Promitor.Core.Scraping/ScraperConfiguration.cs
index 7dc7b8a0a..2122d59c6 100644
--- a/src/Promitor.Core.Scraping/ScraperConfiguration.cs
+++ b/src/Promitor.Core.Scraping/ScraperConfiguration.cs
@@ -2,6 +2,7 @@
using Microsoft.Extensions.Logging;
using Promitor.Core.Configuration.FeatureFlags;
using Promitor.Core.Scraping.Configuration.Model;
+using Promitor.Core.Scraping.Prometheus.Interfaces;
using Promitor.Core.Telemetry.Interfaces;
using Promitor.Integrations.AzureMonitor;
@@ -19,6 +20,11 @@ public class ScraperConfiguration
///
public AzureMonitorClient AzureMonitorClient { get; }
+ ///
+ /// Metrics collector for our Prometheus scraping endpoint
+ ///
+ public IPrometheusMetricWriter PrometheusMetricWriter { get; }
+
///
/// Interaction with feature flags
///
@@ -39,19 +45,22 @@ public class ScraperConfiguration
///
/// Metadata concerning the Azure resources
/// Client to communicate with Azure Monitor
+ /// Metrics collector for our Prometheus scraping endpoint
/// Interaction with feature flags
/// General logger
/// Exception tracker
- public ScraperConfiguration(AzureMetadata azureMetadata, AzureMonitorClient azureMonitorClient, FeatureToggleClient featureToggleClient, ILogger logger, IExceptionTracker exceptionTracker)
+ public ScraperConfiguration(AzureMetadata azureMetadata, AzureMonitorClient azureMonitorClient, IPrometheusMetricWriter prometheusMetricWriter, FeatureToggleClient featureToggleClient, ILogger logger, IExceptionTracker exceptionTracker)
{
Guard.NotNull(azureMetadata, nameof(azureMetadata));
Guard.NotNull(azureMonitorClient, nameof(azureMonitorClient));
+ Guard.NotNull(prometheusMetricWriter, nameof(prometheusMetricWriter));
Guard.NotNull(featureToggleClient, nameof(featureToggleClient));
Guard.NotNull(logger, nameof(logger));
Guard.NotNull(exceptionTracker, nameof(exceptionTracker));
AzureMetadata = azureMetadata;
AzureMonitorClient = azureMonitorClient;
+ PrometheusMetricWriter = prometheusMetricWriter;
FeatureToggleClient = featureToggleClient;
Logger = logger;
ExceptionTracker = exceptionTracker;
diff --git a/src/Promitor.Integrations.AzureMonitor/AzureMonitorClient.cs b/src/Promitor.Integrations.AzureMonitor/AzureMonitorClient.cs
index 096d37839..3b6fcdebf 100644
--- a/src/Promitor.Integrations.AzureMonitor/AzureMonitorClient.cs
+++ b/src/Promitor.Integrations.AzureMonitor/AzureMonitorClient.cs
@@ -53,7 +53,7 @@ public AzureMonitorClient(string tenantId, string subscriptionId, string applica
/// Id of the resource to query
/// Optional filter to filter out metrics
/// Latest representation of the metric
- public async Task QueryMetricAsync(string metricName, AggregationType aggregationType, TimeSpan aggregationInterval,
+ public async Task QueryMetricAsync(string metricName, AggregationType aggregationType, TimeSpan aggregationInterval,
string resourceId, string metricFilter = null)
{
Guard.NotNullOrWhitespace(metricName, nameof(metricName));
@@ -159,20 +159,20 @@ private MetricValue GetMostRecentMetricValue(string metricName, IReadOnlyList new MetricScrapingJob(metric,
metricsProvider,
+ serviceProvider.GetService(),
serviceProvider.GetService(),
serviceProvider.GetService(),
serviceProvider.GetService(),
@@ -70,6 +73,7 @@ public static IServiceCollection DefineDependencies(this IServiceCollection serv
services.AddTransient();
services.AddTransient();
services.AddTransient();
+ services.AddTransient();
return services;
}
diff --git a/src/Promitor.Scraper.Host/Scheduling/MetricScrapingJob.cs b/src/Promitor.Scraper.Host/Scheduling/MetricScrapingJob.cs
index 7c58f5c2c..25d4e4412 100644
--- a/src/Promitor.Scraper.Host/Scheduling/MetricScrapingJob.cs
+++ b/src/Promitor.Scraper.Host/Scheduling/MetricScrapingJob.cs
@@ -8,6 +8,7 @@
using Promitor.Core.Scraping.Configuration.Model.Metrics;
using Promitor.Core.Scraping.Configuration.Providers.Interfaces;
using Promitor.Core.Scraping.Factories;
+using Promitor.Core.Scraping.Prometheus.Interfaces;
using Promitor.Core.Telemetry.Interfaces;
using Promitor.Core.Telemetry.Metrics.Interfaces;
@@ -17,6 +18,7 @@ public class MetricScrapingJob : IScheduledJob
{
private readonly MetricDefinition _metric;
private readonly IMetricsDeclarationProvider _metricsDeclarationProvider;
+ private readonly IPrometheusMetricWriter _prometheusMetricWriter;
private readonly IRuntimeMetricsCollector _runtimeMetricsCollector;
private readonly IExceptionTracker _exceptionTracker;
private readonly ILogger _logger;
@@ -25,17 +27,22 @@ public class MetricScrapingJob : IScheduledJob
public MetricScrapingJob(MetricDefinition metric,
IMetricsDeclarationProvider metricsDeclarationProvider,
+ IPrometheusMetricWriter prometheusMetricWriter,
IRuntimeMetricsCollector runtimeMetricsCollector,
MetricScraperFactory metricScraperFactory,
ILogger logger, IExceptionTracker exceptionTracker)
{
Guard.NotNull(metric, nameof(metric));
- Guard.NotNull(exceptionTracker, nameof(exceptionTracker));
- Guard.NotNull(logger, nameof(logger));
+ Guard.NotNull(metricsDeclarationProvider, nameof(metricsDeclarationProvider));
+ Guard.NotNull(prometheusMetricWriter, nameof(prometheusMetricWriter));
+ Guard.NotNull(runtimeMetricsCollector, nameof(runtimeMetricsCollector));
+ Guard.NotNull(metricScraperFactory, nameof(metricScraperFactory));
Guard.NotNull(logger, nameof(logger));
+ Guard.NotNull(exceptionTracker, nameof(exceptionTracker));
_metric = metric;
_metricsDeclarationProvider = metricsDeclarationProvider;
+ _prometheusMetricWriter = prometheusMetricWriter;
_runtimeMetricsCollector = runtimeMetricsCollector;
_exceptionTracker = exceptionTracker;
_logger = logger;
@@ -76,7 +83,7 @@ private async Task ScrapeMetric(AzureMetadata azureMetadata, MetricDefinition me
{
_logger.LogInformation("Scraping '{MetricName}' for resource type '{ResourceType}'", metricDefinitionDefinition.Name, metricDefinitionDefinition.ResourceType);
- var scraper = _metricScraperFactory.CreateScraper(metricDefinitionDefinition.ResourceType, azureMetadata, _runtimeMetricsCollector);
+ var scraper = _metricScraperFactory.CreateScraper(metricDefinitionDefinition.ResourceType, azureMetadata, _prometheusMetricWriter, _runtimeMetricsCollector);
await scraper.ScrapeAsync(metricDefinitionDefinition);
}
}
diff --git a/src/Promitor.Scraper.Host/Validation/Steps/MetricsDeclarationValidationStep.cs b/src/Promitor.Scraper.Host/Validation/Steps/MetricsDeclarationValidationStep.cs
index 7c23eb2a1..a3bfc182b 100644
--- a/src/Promitor.Scraper.Host/Validation/Steps/MetricsDeclarationValidationStep.cs
+++ b/src/Promitor.Scraper.Host/Validation/Steps/MetricsDeclarationValidationStep.cs
@@ -61,8 +61,7 @@ private void LogMetricsDeclaration(MetricsDeclaration metricsDeclaration)
private void SanitizeStorageQueueDeclaration(MetricDefinition metricDefinition)
{
- var storageQueueDeclaration = metricDefinition as StorageQueueMetricDefinition;
- if (storageQueueDeclaration != null && string.IsNullOrWhiteSpace(storageQueueDeclaration.SasToken.RawValue) == false)
+ if (metricDefinition is StorageQueueMetricDefinition storageQueueDeclaration && string.IsNullOrWhiteSpace(storageQueueDeclaration.SasToken.RawValue) == false)
{
storageQueueDeclaration.SasToken.RawValue = "***";
}
diff --git a/src/Promitor.Scraper.Tests.Unit/Configuration/RuntimeConfigurationUnitTest.cs b/src/Promitor.Scraper.Tests.Unit/Configuration/RuntimeConfigurationUnitTest.cs
index df2219bcc..2626712ef 100644
--- a/src/Promitor.Scraper.Tests.Unit/Configuration/RuntimeConfigurationUnitTest.cs
+++ b/src/Promitor.Scraper.Tests.Unit/Configuration/RuntimeConfigurationUnitTest.cs
@@ -167,7 +167,7 @@ public async Task RuntimeConfiguration_HasConfiguredMetricsConfigurationBasePath
// Arrange
var metricsDeclarationBasePath = _faker.System.DirectoryPath();
var configuration = await RuntimeConfigurationGenerator.WithServerConfiguration()
- .WithMetricsConfiguration(metricsDeclarationBasePath)
+ .WithMetricsConfiguration(absolutePath: metricsDeclarationBasePath)
.GenerateAsync();
// Act
@@ -180,13 +180,31 @@ public async Task RuntimeConfiguration_HasConfiguredMetricsConfigurationBasePath
Assert.Equal(metricsDeclarationBasePath, runtimeConfiguration.MetricsConfiguration.AbsolutePath);
}
+ [Fact]
+ public async Task RuntimeConfiguration_HasConfiguredMetricUnavailableValue_UsesConfigured()
+ {
+ // Arrange
+ var metricUnavailableValue = _faker.Random.Double(min: 1);
+ var configuration = await RuntimeConfigurationGenerator.WithServerConfiguration()
+ .WithPrometheusConfiguration(metricUnavailableValue: metricUnavailableValue)
+ .GenerateAsync();
+
+ // Act
+ var runtimeConfiguration = configuration.Get();
+
+ // Assert
+ Assert.NotNull(runtimeConfiguration);
+ Assert.NotNull(runtimeConfiguration.Prometheus);
+ Assert.Equal(metricUnavailableValue, runtimeConfiguration.Prometheus.MetricUnavailableValue);
+ }
+
[Fact]
public async Task RuntimeConfiguration_HasConfiguredPrometheusScrapeEndpointConfigured_UsesConfigured()
{
// Arrange
var scrapeEndpointBaseUri = _faker.System.DirectoryPath();
var configuration = await RuntimeConfigurationGenerator.WithServerConfiguration()
- .WithPrometheusConfiguration(scrapeEndpointBaseUri)
+ .WithPrometheusConfiguration(scrapeEndpointBaseUri: scrapeEndpointBaseUri)
.GenerateAsync();
// Act
@@ -293,7 +311,24 @@ public async Task RuntimeConfiguration_HasNoMetricsConfigurationBasePathConfigur
{
// Arrange
var configuration = await RuntimeConfigurationGenerator.WithServerConfiguration()
- .WithMetricsConfiguration(null)
+ .WithMetricsConfiguration(absolutePath: null)
+ .GenerateAsync();
+
+ // Act
+ var runtimeConfiguration = configuration.Get();
+
+ // Assert
+ Assert.NotNull(runtimeConfiguration);
+ Assert.NotNull(runtimeConfiguration.MetricsConfiguration);
+ Assert.Equal(Defaults.MetricsConfiguration.AbsolutePath, runtimeConfiguration.MetricsConfiguration.AbsolutePath);
+ }
+
+ [Fact]
+ public async Task RuntimeConfiguration_HasNoMetricUnavailableValuePathConfigured_UsesDefault()
+ {
+ // Arrange
+ var configuration = await RuntimeConfigurationGenerator.WithServerConfiguration()
+ .WithPrometheusConfiguration(metricUnavailableValue: null)
.GenerateAsync();
// Act
@@ -303,7 +338,27 @@ public async Task RuntimeConfiguration_HasNoMetricsConfigurationBasePathConfigur
Assert.NotNull(runtimeConfiguration);
Assert.NotNull(runtimeConfiguration.Prometheus);
Assert.NotNull(runtimeConfiguration.Prometheus.ScrapeEndpoint);
- Assert.Equal(Defaults.MetricsConfiguration.AbsolutePath, runtimeConfiguration.MetricsConfiguration.AbsolutePath);
+ Assert.NotEqual(Defaults.Prometheus.ScrapeEndpointBaseUri, runtimeConfiguration.Prometheus.ScrapeEndpoint.BaseUriPath);
+ Assert.Equal(Defaults.Prometheus.MetricUnavailableValue, runtimeConfiguration.Prometheus.MetricUnavailableValue);
+ }
+
+ [Fact]
+ public async Task RuntimeConfiguration_HasNoPrometheusConfigurationConfigured_UsesDefault()
+ {
+ // Arrange
+ var configuration = await RuntimeConfigurationGenerator.WithServerConfiguration()
+ .WithPrometheusConfiguration(metricUnavailableValue: null, scrapeEndpointBaseUri: null)
+ .GenerateAsync();
+
+ // Act
+ var runtimeConfiguration = configuration.Get();
+
+ // Assert
+ Assert.NotNull(runtimeConfiguration);
+ Assert.NotNull(runtimeConfiguration.Prometheus);
+ Assert.NotNull(runtimeConfiguration.Prometheus.ScrapeEndpoint);
+ Assert.Equal(Defaults.Prometheus.ScrapeEndpointBaseUri, runtimeConfiguration.Prometheus.ScrapeEndpoint.BaseUriPath);
+ Assert.Equal(Defaults.Prometheus.MetricUnavailableValue, runtimeConfiguration.Prometheus.MetricUnavailableValue);
}
[Fact]
@@ -311,7 +366,7 @@ public async Task RuntimeConfiguration_HasNoPrometheusScrapeEndpointConfigured_U
{
// Arrange
var configuration = await RuntimeConfigurationGenerator.WithServerConfiguration()
- .WithPrometheusConfiguration(null)
+ .WithPrometheusConfiguration(scrapeEndpointBaseUri:null)
.GenerateAsync();
// Act
@@ -322,6 +377,7 @@ public async Task RuntimeConfiguration_HasNoPrometheusScrapeEndpointConfigured_U
Assert.NotNull(runtimeConfiguration.Prometheus);
Assert.NotNull(runtimeConfiguration.Prometheus.ScrapeEndpoint);
Assert.Equal(Defaults.Prometheus.ScrapeEndpointBaseUri, runtimeConfiguration.Prometheus.ScrapeEndpoint.BaseUriPath);
+ Assert.NotEqual(Defaults.Prometheus.MetricUnavailableValue, runtimeConfiguration.Prometheus.MetricUnavailableValue);
}
[Fact]
diff --git a/src/Promitor.Scraper.Tests.Unit/Generators/Config/BogusRuntimeConfigurationGenerator.cs b/src/Promitor.Scraper.Tests.Unit/Generators/Config/BogusRuntimeConfigurationGenerator.cs
index a81a023fe..053bce1a7 100644
--- a/src/Promitor.Scraper.Tests.Unit/Generators/Config/BogusRuntimeConfigurationGenerator.cs
+++ b/src/Promitor.Scraper.Tests.Unit/Generators/Config/BogusRuntimeConfigurationGenerator.cs
@@ -32,7 +32,8 @@ internal static RuntimeConfiguration Generate()
.Generate();
var prometheusConfiguration = new Faker()
.StrictMode(true)
- .RuleFor(flagsConfiguration => flagsConfiguration.ScrapeEndpoint, scrapeEndpointConfiguration)
+ .RuleFor(promConfiguration => promConfiguration.ScrapeEndpoint, scrapeEndpointConfiguration)
+ .RuleFor(promConfiguration => promConfiguration.MetricUnavailableValue, faker => faker.Random.Double(min: 1))
.Generate();
var containerLogConfiguration = new Faker()
diff --git a/src/Promitor.Scraper.Tests.Unit/Generators/Config/RuntimeConfigurationGenerator.cs b/src/Promitor.Scraper.Tests.Unit/Generators/Config/RuntimeConfigurationGenerator.cs
index 4d14e2b77..552a401a9 100644
--- a/src/Promitor.Scraper.Tests.Unit/Generators/Config/RuntimeConfigurationGenerator.cs
+++ b/src/Promitor.Scraper.Tests.Unit/Generators/Config/RuntimeConfigurationGenerator.cs
@@ -46,17 +46,30 @@ public static RuntimeConfigurationGenerator WithRuntimeConfiguration(RuntimeConf
return new RuntimeConfigurationGenerator(runtimeConfiguration);
}
- public RuntimeConfigurationGenerator WithPrometheusConfiguration(string scrapeEndpointBaseUri = "/scrape-endpoint")
+ public RuntimeConfigurationGenerator WithPrometheusConfiguration(double? metricUnavailableValue = -1, string scrapeEndpointBaseUri = "/scrape-endpoint")
{
- var prometheusConfiguration = scrapeEndpointBaseUri == null
- ? null
- : new PrometheusConfiguration
+ PrometheusConfiguration prometheusConfiguration;
+ if (string.IsNullOrWhiteSpace(scrapeEndpointBaseUri) && metricUnavailableValue == null)
+ {
+ prometheusConfiguration = null;
+ }
+ else
+ {
+ prometheusConfiguration = new PrometheusConfiguration();
+
+ if (string.IsNullOrWhiteSpace(scrapeEndpointBaseUri) == false)
{
- ScrapeEndpoint = new ScrapeEndpointConfiguration
+ prometheusConfiguration.ScrapeEndpoint = new ScrapeEndpointConfiguration
{
BaseUriPath = scrapeEndpointBaseUri
- }
- };
+ };
+ }
+
+ if (metricUnavailableValue != null)
+ {
+ prometheusConfiguration.MetricUnavailableValue = (double)metricUnavailableValue;
+ }
+ }
_runtimeConfiguration.Prometheus = prometheusConfiguration;
@@ -164,12 +177,20 @@ public async Task GenerateAsync()
configurationBuilder.AppendLine(" scrapeEndpoint:");
configurationBuilder.AppendLine($" baseUriPath: {_runtimeConfiguration?.Prometheus.ScrapeEndpoint.BaseUriPath}");
}
+
+ if (_runtimeConfiguration?.Prometheus.MetricUnavailableValue != null)
+ {
+ configurationBuilder.AppendLine($" metricUnavailableValue: {_runtimeConfiguration?.Prometheus.MetricUnavailableValue}");
+ }
}
if (_runtimeConfiguration?.MetricsConfiguration != null)
{
configurationBuilder.AppendLine("metricsConfiguration:");
- configurationBuilder.AppendLine($" absolutePath: {_runtimeConfiguration?.MetricsConfiguration.AbsolutePath}");
+ if (_runtimeConfiguration?.MetricsConfiguration.AbsolutePath != null)
+ {
+ configurationBuilder.AppendLine($" absolutePath: {_runtimeConfiguration?.MetricsConfiguration.AbsolutePath}");
+ }
}
if (_runtimeConfiguration?.Telemetry != null)
diff --git a/src/Promitor.sln.DotSettings b/src/Promitor.sln.DotSettings
index 5f5cb44b9..357b73ca6 100644
--- a/src/Promitor.sln.DotSettings
+++ b/src/Promitor.sln.DotSettings
@@ -3,6 +3,8 @@
SUGGESTION
SUGGESTION
SUGGESTION
+ SUGGESTION
+ SUGGESTION
SUGGESTION
SUGGESTION
False
diff --git a/src/runtime-config.yaml b/src/runtime-config.yaml
index c69538af7..006955511 100644
--- a/src/runtime-config.yaml
+++ b/src/runtime-config.yaml
@@ -1,6 +1,7 @@
server:
httpPort: 88
prometheus:
+ metricUnavailableValue: NaN
scrapeEndpoint:
baseUriPath: /scrape
metricsConfiguration: