From 5197f68ffcf69ef814184876781ef1e19eaf6acf Mon Sep 17 00:00:00 2001 From: Tom Kerkhove Date: Sun, 4 Aug 2019 19:32:31 +0200 Subject: [PATCH 1/9] Change scraped metric value to null if not found Signed-off-by: Tom Kerkhove --- src/Promitor.Core.Scraping/ScrapeResult.cs | 8 ++++---- src/Promitor.Core.Scraping/Scraper.cs | 10 ++++++++-- .../AzureMonitorClient.cs | 14 +++++++------- 3 files changed, 19 insertions(+), 13 deletions(-) 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..f32ad2b51 100644 --- a/src/Promitor.Core.Scraping/Scraper.cs +++ b/src/Promitor.Core.Scraping/Scraper.cs @@ -100,7 +100,13 @@ private void ReportMetric(MetricDefinition metricDefinition, ScrapeResult scrape 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); + var metricValue = DetermineMetricMeasurement(scrapedMetricResult); + gauge.WithLabels(labels.Values).Set(metricValue); + } + + private static double DetermineMetricMeasurement(ScrapeResult scrapedMetricResult) + { + return scrapedMetricResult.MetricValue ?? double.NaN; } private void HandleErrorResponseException(ErrorResponseException errorResponseException) @@ -116,7 +122,7 @@ private void HandleErrorResponseException(ErrorResponseException errorResponseEx { try { - var definition = new {error = new {code = "", message = ""}}; + var definition = new { error = new { code = "", message = "" } }; var jsonError = JsonConvert.DeserializeAnonymousType(errorResponseException.Response.Content, definition); if (jsonError != null && jsonError.error != null) 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 Date: Sun, 4 Aug 2019 20:18:04 +0200 Subject: [PATCH 2/9] Introduce runtimeConfiguration.metricsConfiguration.metricUnavailableValue --- src/Promitor.Core.Configuration/Defaults.cs | 1 + .../Model/Metrics/MetricsConfiguration.cs | 1 + .../RuntimeConfigurationUnitTest.cs | 63 +++++++++++++++++-- .../BogusRuntimeConfigurationGenerator.cs | 5 +- .../Config/RuntimeConfigurationGenerator.cs | 36 ++++++++--- 5 files changed, 94 insertions(+), 12 deletions(-) diff --git a/src/Promitor.Core.Configuration/Defaults.cs b/src/Promitor.Core.Configuration/Defaults.cs index ec2b881e6..f0dbdd077 100644 --- a/src/Promitor.Core.Configuration/Defaults.cs +++ b/src/Promitor.Core.Configuration/Defaults.cs @@ -17,6 +17,7 @@ public static class Prometheus public static class MetricsConfiguration { public static string AbsolutePath { get; } = "/config/metrics-declaration.yaml"; + public static double MetricUnavailableValue { get; } = double.NaN; } public class Telemetry diff --git a/src/Promitor.Core.Configuration/Model/Metrics/MetricsConfiguration.cs b/src/Promitor.Core.Configuration/Model/Metrics/MetricsConfiguration.cs index 5cccff231..555ce7350 100644 --- a/src/Promitor.Core.Configuration/Model/Metrics/MetricsConfiguration.cs +++ b/src/Promitor.Core.Configuration/Model/Metrics/MetricsConfiguration.cs @@ -3,5 +3,6 @@ public class MetricsConfiguration { public string AbsolutePath { get; set; } = Defaults.MetricsConfiguration.AbsolutePath; + public double? MetricUnavailableValue { get; set; } = Defaults.MetricsConfiguration.MetricUnavailableValue; } } \ No newline at end of file diff --git a/src/Promitor.Scraper.Tests.Unit/Configuration/RuntimeConfigurationUnitTest.cs b/src/Promitor.Scraper.Tests.Unit/Configuration/RuntimeConfigurationUnitTest.cs index df2219bcc..0a71592af 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,6 +180,25 @@ 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() + .WithMetricsConfiguration(metricUnavailableValue: metricUnavailableValue) + .GenerateAsync(); + + // Act + var runtimeConfiguration = configuration.Get(); + + // Assert + Assert.NotNull(runtimeConfiguration); + Assert.NotNull(runtimeConfiguration.Prometheus); + Assert.NotNull(runtimeConfiguration.Prometheus.ScrapeEndpoint); + Assert.Equal(metricUnavailableValue, runtimeConfiguration.MetricsConfiguration.MetricUnavailableValue); + } + [Fact] public async Task RuntimeConfiguration_HasConfiguredPrometheusScrapeEndpointConfigured_UsesConfigured() { @@ -293,7 +312,7 @@ public async Task RuntimeConfiguration_HasNoMetricsConfigurationBasePathConfigur { // Arrange var configuration = await RuntimeConfigurationGenerator.WithServerConfiguration() - .WithMetricsConfiguration(null) + .WithMetricsConfiguration(absolutePath: null) .GenerateAsync(); // Act @@ -301,9 +320,45 @@ public async Task RuntimeConfiguration_HasNoMetricsConfigurationBasePathConfigur // Assert Assert.NotNull(runtimeConfiguration); - Assert.NotNull(runtimeConfiguration.Prometheus); - Assert.NotNull(runtimeConfiguration.Prometheus.ScrapeEndpoint); + Assert.NotNull(runtimeConfiguration.MetricsConfiguration); + Assert.NotEqual(Defaults.MetricsConfiguration.MetricUnavailableValue, runtimeConfiguration.MetricsConfiguration.MetricUnavailableValue); + Assert.Equal(Defaults.MetricsConfiguration.AbsolutePath, runtimeConfiguration.MetricsConfiguration.AbsolutePath); + } + + [Fact] + public async Task RuntimeConfiguration_HasNoMetricUnavailableValuePathConfigured_UsesDefault() + { + // Arrange + var configuration = await RuntimeConfigurationGenerator.WithServerConfiguration() + .WithMetricsConfiguration(metricUnavailableValue: null) + .GenerateAsync(); + + // Act + var runtimeConfiguration = configuration.Get(); + + // Assert + Assert.NotNull(runtimeConfiguration); + Assert.NotNull(runtimeConfiguration.MetricsConfiguration); + Assert.NotEqual(Defaults.MetricsConfiguration.AbsolutePath, runtimeConfiguration.MetricsConfiguration.AbsolutePath); + Assert.Equal(Defaults.MetricsConfiguration.MetricUnavailableValue, runtimeConfiguration.MetricsConfiguration.MetricUnavailableValue); + } + + [Fact] + public async Task RuntimeConfiguration_HasNoMetricConfigurationConfigured_UsesDefault() + { + // Arrange + var configuration = await RuntimeConfigurationGenerator.WithServerConfiguration() + .WithMetricsConfiguration(metricUnavailableValue: null, absolutePath:null) + .GenerateAsync(); + + // Act + var runtimeConfiguration = configuration.Get(); + + // Assert + Assert.NotNull(runtimeConfiguration); + Assert.NotNull(runtimeConfiguration.MetricsConfiguration); Assert.Equal(Defaults.MetricsConfiguration.AbsolutePath, runtimeConfiguration.MetricsConfiguration.AbsolutePath); + Assert.Equal(Defaults.MetricsConfiguration.MetricUnavailableValue, runtimeConfiguration.MetricsConfiguration.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..0afaa5b31 100644 --- a/src/Promitor.Scraper.Tests.Unit/Generators/Config/BogusRuntimeConfigurationGenerator.cs +++ b/src/Promitor.Scraper.Tests.Unit/Generators/Config/BogusRuntimeConfigurationGenerator.cs @@ -1,4 +1,6 @@ -using Bogus; +using System; +using Bogus; +using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.Extensions.Logging; using Promitor.Core.Configuration.Model; using Promitor.Core.Configuration.Model.FeatureFlags; @@ -25,6 +27,7 @@ internal static RuntimeConfiguration Generate() var metricsConfiguration = new Faker() .StrictMode(true) .RuleFor(metricConfiguration => metricConfiguration.AbsolutePath, faker => faker.System.DirectoryPath()) + .RuleFor(metricConfiguration => metricConfiguration.MetricUnavailableValue, faker => faker.Random.Double(min: 1)) .Generate(); var scrapeEndpointConfiguration = new Faker() .StrictMode(true) diff --git a/src/Promitor.Scraper.Tests.Unit/Generators/Config/RuntimeConfigurationGenerator.cs b/src/Promitor.Scraper.Tests.Unit/Generators/Config/RuntimeConfigurationGenerator.cs index 4d14e2b77..b75244833 100644 --- a/src/Promitor.Scraper.Tests.Unit/Generators/Config/RuntimeConfigurationGenerator.cs +++ b/src/Promitor.Scraper.Tests.Unit/Generators/Config/RuntimeConfigurationGenerator.cs @@ -63,14 +63,28 @@ public RuntimeConfigurationGenerator WithPrometheusConfiguration(string scrapeEn return this; } - public RuntimeConfigurationGenerator WithMetricsConfiguration(string absolutePath = "/metrics-declaration.yaml") + public RuntimeConfigurationGenerator WithMetricsConfiguration(double? metricUnavailableValue = -1, string absolutePath = "/metrics-declaration.yaml") { - var metricsConfiguration = absolutePath == null - ? null - : new MetricsConfiguration + MetricsConfiguration metricsConfiguration; + if (absolutePath == null && metricUnavailableValue == null) + { + metricsConfiguration = null; + } + else + { + metricsConfiguration = new MetricsConfiguration(); + + if (absolutePath != null) { - AbsolutePath = absolutePath - }; + metricsConfiguration.AbsolutePath = absolutePath; + } + + if (metricUnavailableValue != null) + { + metricsConfiguration.MetricUnavailableValue = (double)metricUnavailableValue; + } + + } _runtimeConfiguration.MetricsConfiguration = metricsConfiguration; @@ -169,7 +183,15 @@ public async Task GenerateAsync() 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?.MetricsConfiguration.MetricUnavailableValue != null) + { + configurationBuilder.AppendLine($" metricUnavailableValue: {_runtimeConfiguration?.MetricsConfiguration.MetricUnavailableValue}"); + } } if (_runtimeConfiguration?.Telemetry != null) From e26d4e9bf7d0b09ae78577b169437164da5548cd Mon Sep 17 00:00:00 2001 From: Tom Kerkhove Date: Tue, 6 Aug 2019 21:17:56 +0200 Subject: [PATCH 3/9] Refactor to move config to Prometheus & introduce PrometheusMetricWriter Signed-off-by: Tom Kerkhove --- src/Promitor.Core.Configuration/Defaults.cs | 2 +- .../Model/Metrics/MetricsConfiguration.cs | 1 - .../Prometheus/PrometheusConfiguration.cs | 1 + .../Factories/MetricScraperFactory.cs | 24 ++++---- .../Interfaces/IPrometheusMetricWriter.cs | 9 +++ .../Prometheus/PrometheusMetricWriter.cs | 59 ++++++++++++++++++ .../ResourceTypes/ContainerInstanceScraper.cs | 5 +- .../ResourceTypes/ContainerRegistryScraper.cs | 5 +- .../ResourceTypes/CosmosDbScraper.cs | 5 +- .../ResourceTypes/GenericScraper.cs | 5 +- .../ResourceTypes/NetworkInterfaceScraper.cs | 5 +- .../ResourceTypes/PostgreSqlScraper.cs | 5 +- .../ResourceTypes/RedisCacheScraper.cs | 5 +- .../ResourceTypes/ServiceBusQueueScraper.cs | 5 +- .../ResourceTypes/StorageQueueScraper.cs | 5 +- .../ResourceTypes/VirtualMachineScraper.cs | 5 +- src/Promitor.Core.Scraping/Scraper.cs | 61 ++++--------------- .../IServiceCollectionExtensions.cs | 5 ++ .../Scheduling/MetricScrapingJob.cs | 13 +++- .../RuntimeConfigurationUnitTest.cs | 31 +++++----- .../BogusRuntimeConfigurationGenerator.cs | 4 +- .../Config/RuntimeConfigurationGenerator.cs | 60 +++++++++--------- 22 files changed, 187 insertions(+), 133 deletions(-) create mode 100644 src/Promitor.Core.Scraping/Prometheus/Interfaces/IPrometheusMetricWriter.cs create mode 100644 src/Promitor.Core.Scraping/Prometheus/PrometheusMetricWriter.cs diff --git a/src/Promitor.Core.Configuration/Defaults.cs b/src/Promitor.Core.Configuration/Defaults.cs index f0dbdd077..2fe691fb3 100644 --- a/src/Promitor.Core.Configuration/Defaults.cs +++ b/src/Promitor.Core.Configuration/Defaults.cs @@ -12,12 +12,12 @@ 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 { public static string AbsolutePath { get; } = "/config/metrics-declaration.yaml"; - public static double MetricUnavailableValue { get; } = double.NaN; } public class Telemetry diff --git a/src/Promitor.Core.Configuration/Model/Metrics/MetricsConfiguration.cs b/src/Promitor.Core.Configuration/Model/Metrics/MetricsConfiguration.cs index 555ce7350..5cccff231 100644 --- a/src/Promitor.Core.Configuration/Model/Metrics/MetricsConfiguration.cs +++ b/src/Promitor.Core.Configuration/Model/Metrics/MetricsConfiguration.cs @@ -3,6 +3,5 @@ public class MetricsConfiguration { public string AbsolutePath { get; set; } = Defaults.MetricsConfiguration.AbsolutePath; - public double? MetricUnavailableValue { get; set; } = Defaults.MetricsConfiguration.MetricUnavailableValue; } } \ No newline at end of file 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..61c126310 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,9 +39,10 @@ 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); @@ -48,25 +50,25 @@ public IScraper CreateScraper(ResourceType metricDefinitionRes switch (metricDefinitionResourceType) { case ResourceType.ServiceBusQueue: - return new ServiceBusQueueScraper(scraperConfiguration); + return new ServiceBusQueueScraper(scraperConfiguration, prometheusMetricWriter); case ResourceType.Generic: - return new GenericScraper(scraperConfiguration); + return new GenericScraper(scraperConfiguration, prometheusMetricWriter); case ResourceType.StorageQueue: - return new StorageQueueScraper(scraperConfiguration); + return new StorageQueueScraper(scraperConfiguration, prometheusMetricWriter); case ResourceType.ContainerInstance: - return new ContainerInstanceScraper(scraperConfiguration); + return new ContainerInstanceScraper(scraperConfiguration, prometheusMetricWriter); case ResourceType.VirtualMachine: - return new VirtualMachineScraper(scraperConfiguration); + return new VirtualMachineScraper(scraperConfiguration, prometheusMetricWriter); case ResourceType.NetworkInterface: - return new NetworkInterfaceScraper(scraperConfiguration); + return new NetworkInterfaceScraper(scraperConfiguration, prometheusMetricWriter); case ResourceType.ContainerRegistry: - return new ContainerRegistryScraper(scraperConfiguration); + return new ContainerRegistryScraper(scraperConfiguration, prometheusMetricWriter); case ResourceType.CosmosDb: - return new CosmosDbScraper(scraperConfiguration); + return new CosmosDbScraper(scraperConfiguration, prometheusMetricWriter); case ResourceType.RedisCache: - return new RedisCacheScraper(scraperConfiguration); + return new RedisCacheScraper(scraperConfiguration, prometheusMetricWriter); case ResourceType.PostgreSql: - return new PostgreSqlScraper(scraperConfiguration); + return new PostgreSqlScraper(scraperConfiguration, prometheusMetricWriter); default: throw new ArgumentOutOfRangeException(); } 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..a241cf83f --- /dev/null +++ b/src/Promitor.Core.Scraping/Prometheus/PrometheusMetricWriter.cs @@ -0,0 +1,59 @@ +using System.Collections.Generic; +using System.Linq; +using Microsoft.Extensions.Logging; +using Prometheus.Client; +using Promitor.Core.Configuration.FeatureFlags; +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 ILogger _logger; + + public PrometheusMetricWriter(FeatureToggleClient featureToggleClient, ILogger logger) + { + _featureToggleClient = featureToggleClient; + _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 static double DetermineMetricMeasurement(ScrapeResult scrapedMetricResult) + { + return scrapedMetricResult.MetricValue ?? double.NaN; + } + + 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/ResourceTypes/ContainerInstanceScraper.cs b/src/Promitor.Core.Scraping/ResourceTypes/ContainerInstanceScraper.cs index 0ec1b81a8..53b486dfd 100644 --- a/src/Promitor.Core.Scraping/ResourceTypes/ContainerInstanceScraper.cs +++ b/src/Promitor.Core.Scraping/ResourceTypes/ContainerInstanceScraper.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks; using Microsoft.Azure.Management.Monitor.Fluent.Models; using Promitor.Core.Scraping.Configuration.Model.Metrics.ResourceTypes; +using Promitor.Core.Scraping.Prometheus.Interfaces; namespace Promitor.Core.Scraping.ResourceTypes { @@ -9,8 +10,8 @@ public class ContainerInstanceScraper : Scraper { private const string ResourceUriTemplate = "subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.DocumentDB/databaseAccounts/{2}"; - public CosmosDbScraper(ScraperConfiguration scraperConfiguration) - : base(scraperConfiguration) + public CosmosDbScraper(ScraperConfiguration scraperConfiguration, IPrometheusMetricWriter prometheusMetricWriter) + : base(scraperConfiguration, prometheusMetricWriter) { } diff --git a/src/Promitor.Core.Scraping/ResourceTypes/GenericScraper.cs b/src/Promitor.Core.Scraping/ResourceTypes/GenericScraper.cs index 083163995..e3f84d892 100644 --- a/src/Promitor.Core.Scraping/ResourceTypes/GenericScraper.cs +++ b/src/Promitor.Core.Scraping/ResourceTypes/GenericScraper.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks; using Microsoft.Azure.Management.Monitor.Fluent.Models; using Promitor.Core.Scraping.Configuration.Model.Metrics.ResourceTypes; +using Promitor.Core.Scraping.Prometheus.Interfaces; namespace Promitor.Core.Scraping.ResourceTypes { @@ -9,8 +10,8 @@ internal class GenericScraper : Scraper { private const string ResourceUriTemplate = "subscriptions/{0}/resourceGroups/{1}/providers/{2}"; - public GenericScraper(ScraperConfiguration scraperConfiguration) - : base(scraperConfiguration) + public GenericScraper(ScraperConfiguration scraperConfiguration, IPrometheusMetricWriter prometheusMetricWriter) + : base(scraperConfiguration, prometheusMetricWriter) { } diff --git a/src/Promitor.Core.Scraping/ResourceTypes/NetworkInterfaceScraper.cs b/src/Promitor.Core.Scraping/ResourceTypes/NetworkInterfaceScraper.cs index 29720ce3a..6a7ee2892 100644 --- a/src/Promitor.Core.Scraping/ResourceTypes/NetworkInterfaceScraper.cs +++ b/src/Promitor.Core.Scraping/ResourceTypes/NetworkInterfaceScraper.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks; using Microsoft.Azure.Management.Monitor.Fluent.Models; using Promitor.Core.Scraping.Configuration.Model.Metrics.ResourceTypes; +using Promitor.Core.Scraping.Prometheus.Interfaces; namespace Promitor.Core.Scraping.ResourceTypes { @@ -9,8 +10,8 @@ internal class NetworkInterfaceScraper : Scraper { private const string ResourceUriTemplate = "subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.DBforPostgreSQL/servers/{2}"; - public PostgreSqlScraper(ScraperConfiguration scraperConfiguration) - : base(scraperConfiguration) + public PostgreSqlScraper(ScraperConfiguration scraperConfiguration, IPrometheusMetricWriter prometheusMetricWriter) + : base(scraperConfiguration, prometheusMetricWriter) { } diff --git a/src/Promitor.Core.Scraping/ResourceTypes/RedisCacheScraper.cs b/src/Promitor.Core.Scraping/ResourceTypes/RedisCacheScraper.cs index c31854b47..bb2bb3005 100644 --- a/src/Promitor.Core.Scraping/ResourceTypes/RedisCacheScraper.cs +++ b/src/Promitor.Core.Scraping/ResourceTypes/RedisCacheScraper.cs @@ -2,6 +2,7 @@ using Promitor.Core.Scraping.Configuration.Model.Metrics.ResourceTypes; using System; using System.Threading.Tasks; +using Promitor.Core.Scraping.Prometheus.Interfaces; namespace Promitor.Core.Scraping.ResourceTypes { @@ -9,8 +10,8 @@ public class RedisCacheScraper : Scraper { private const string ResourceUriTemplate = "subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Cache/Redis/{2}"; - public RedisCacheScraper(ScraperConfiguration scraperConfiguration) - : base(scraperConfiguration) + public RedisCacheScraper(ScraperConfiguration scraperConfiguration, IPrometheusMetricWriter prometheusMetricWriter) + : base(scraperConfiguration, prometheusMetricWriter) { } diff --git a/src/Promitor.Core.Scraping/ResourceTypes/ServiceBusQueueScraper.cs b/src/Promitor.Core.Scraping/ResourceTypes/ServiceBusQueueScraper.cs index 9462e587c..340d8d87c 100644 --- a/src/Promitor.Core.Scraping/ResourceTypes/ServiceBusQueueScraper.cs +++ b/src/Promitor.Core.Scraping/ResourceTypes/ServiceBusQueueScraper.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using Microsoft.Azure.Management.Monitor.Fluent.Models; using Promitor.Core.Scraping.Configuration.Model.Metrics.ResourceTypes; +using Promitor.Core.Scraping.Prometheus.Interfaces; namespace Promitor.Core.Scraping.ResourceTypes { @@ -10,8 +11,8 @@ public class ServiceBusQueueScraper : Scraper { private const string ResourceUriTemplate = "subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.ServiceBus/namespaces/{2}"; - public ServiceBusQueueScraper(ScraperConfiguration scraperConfiguration) - : base(scraperConfiguration) + public ServiceBusQueueScraper(ScraperConfiguration scraperConfiguration, IPrometheusMetricWriter prometheusMetricWriter) + : base(scraperConfiguration, prometheusMetricWriter) { } diff --git a/src/Promitor.Core.Scraping/ResourceTypes/StorageQueueScraper.cs b/src/Promitor.Core.Scraping/ResourceTypes/StorageQueueScraper.cs index 488420093..58bc96249 100644 --- a/src/Promitor.Core.Scraping/ResourceTypes/StorageQueueScraper.cs +++ b/src/Promitor.Core.Scraping/ResourceTypes/StorageQueueScraper.cs @@ -4,6 +4,7 @@ using GuardNet; using Microsoft.Azure.Management.Monitor.Fluent.Models; using Promitor.Core.Scraping.Configuration.Model.Metrics.ResourceTypes; +using Promitor.Core.Scraping.Prometheus.Interfaces; using Promitor.Integrations.AzureStorage; namespace Promitor.Core.Scraping.ResourceTypes @@ -12,8 +13,8 @@ public class StorageQueueScraper : Scraper { private const string ResourceUriTemplate = "subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Storage/storageAccounts/{2}/queueServices"; private readonly AzureStorageQueueClient _azureStorageQueueClient; - public StorageQueueScraper(ScraperConfiguration scraperConfiguration) - : base(scraperConfiguration) + public StorageQueueScraper(ScraperConfiguration scraperConfiguration, IPrometheusMetricWriter prometheusMetricWriter) + : base(scraperConfiguration, prometheusMetricWriter) { _azureStorageQueueClient = new AzureStorageQueueClient(scraperConfiguration.Logger); } diff --git a/src/Promitor.Core.Scraping/ResourceTypes/VirtualMachineScraper.cs b/src/Promitor.Core.Scraping/ResourceTypes/VirtualMachineScraper.cs index 06f061272..4a37b715b 100644 --- a/src/Promitor.Core.Scraping/ResourceTypes/VirtualMachineScraper.cs +++ b/src/Promitor.Core.Scraping/ResourceTypes/VirtualMachineScraper.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks; using Microsoft.Azure.Management.Monitor.Fluent.Models; using Promitor.Core.Scraping.Configuration.Model.Metrics.ResourceTypes; +using Promitor.Core.Scraping.Prometheus.Interfaces; namespace Promitor.Core.Scraping.ResourceTypes { @@ -9,8 +10,8 @@ internal class VirtualMachineScraper : Scraper { private const string ResourceUriTemplate = "subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Compute/virtualMachines/{2}"; - public VirtualMachineScraper(ScraperConfiguration scraperConfiguration) - : base(scraperConfiguration) + public VirtualMachineScraper(ScraperConfiguration scraperConfiguration, IPrometheusMetricWriter prometheusMetricWriter) + : base(scraperConfiguration, prometheusMetricWriter) { } diff --git a/src/Promitor.Core.Scraping/Scraper.cs b/src/Promitor.Core.Scraping/Scraper.cs index f32ad2b51..e132c756f 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,24 +20,25 @@ 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 /// - protected Scraper(ScraperConfiguration scraperConfiguration) + protected Scraper(ScraperConfiguration scraperConfiguration, IPrometheusMetricWriter prometheusMetricWriter) { Guard.NotNull(scraperConfiguration, nameof(scraperConfiguration)); + Guard.NotNull(prometheusMetricWriter, nameof(prometheusMetricWriter)); _logger = scraperConfiguration.Logger; - _featureToggleClient = scraperConfiguration.FeatureToggleClient; _exceptionTracker = scraperConfiguration.ExceptionTracker; _scraperConfiguration = scraperConfiguration; + _prometheusMetricWriter = prometheusMetricWriter; AzureMetadata = scraperConfiguration.AzureMetadata; AzureMonitorClient = scraperConfiguration.AzureMonitorClient; @@ -81,7 +80,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,22 +92,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); - var metricValue = DetermineMetricMeasurement(scrapedMetricResult); - gauge.WithLabels(labels.Values).Set(metricValue); - } - - private static double DetermineMetricMeasurement(ScrapeResult scrapedMetricResult) - { - return scrapedMetricResult.MetricValue ?? double.NaN; - } - private void HandleErrorResponseException(ErrorResponseException errorResponseException) { string reason = string.Empty; @@ -122,7 +105,7 @@ private void HandleErrorResponseException(ErrorResponseException errorResponseEx { try { - var definition = new { error = new { code = "", message = "" } }; + var definition = new {error = new {code = "", message = ""}}; var jsonError = JsonConvert.DeserializeAnonymousType(errorResponseException.Response.Content, definition); if (jsonError != null && jsonError.error != null) @@ -147,27 +130,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 /// @@ -176,7 +138,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.Scraper.Host/Extensions/IServiceCollectionExtensions.cs b/src/Promitor.Scraper.Host/Extensions/IServiceCollectionExtensions.cs index e6d8b9a9e..ba502fed0 100644 --- a/src/Promitor.Scraper.Host/Extensions/IServiceCollectionExtensions.cs +++ b/src/Promitor.Scraper.Host/Extensions/IServiceCollectionExtensions.cs @@ -15,7 +15,10 @@ using Promitor.Core.Configuration.Model.Server; using Promitor.Core.Configuration.Model.Telemetry; using Promitor.Core.Configuration.Model.Telemetry.Sinks; +using Promitor.Core.Scraping; using Promitor.Core.Scraping.Factories; +using Promitor.Core.Scraping.Prometheus; +using Promitor.Core.Scraping.Prometheus.Interfaces; using Promitor.Core.Telemetry; using Promitor.Core.Telemetry.Interfaces; using Promitor.Core.Telemetry.Loggers; @@ -46,6 +49,7 @@ public static IServiceCollection ScheduleMetricScraping(this IServiceCollection { builder.AddJob(serviceProvider => new MetricScrapingJob(metric, metricsProvider, + serviceProvider.GetService(), serviceProvider.GetService(), serviceProvider.GetService(), serviceProvider.GetService(), @@ -70,6 +74,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.Tests.Unit/Configuration/RuntimeConfigurationUnitTest.cs b/src/Promitor.Scraper.Tests.Unit/Configuration/RuntimeConfigurationUnitTest.cs index 0a71592af..2626712ef 100644 --- a/src/Promitor.Scraper.Tests.Unit/Configuration/RuntimeConfigurationUnitTest.cs +++ b/src/Promitor.Scraper.Tests.Unit/Configuration/RuntimeConfigurationUnitTest.cs @@ -186,7 +186,7 @@ public async Task RuntimeConfiguration_HasConfiguredMetricUnavailableValue_UsesC // Arrange var metricUnavailableValue = _faker.Random.Double(min: 1); var configuration = await RuntimeConfigurationGenerator.WithServerConfiguration() - .WithMetricsConfiguration(metricUnavailableValue: metricUnavailableValue) + .WithPrometheusConfiguration(metricUnavailableValue: metricUnavailableValue) .GenerateAsync(); // Act @@ -195,8 +195,7 @@ public async Task RuntimeConfiguration_HasConfiguredMetricUnavailableValue_UsesC // Assert Assert.NotNull(runtimeConfiguration); Assert.NotNull(runtimeConfiguration.Prometheus); - Assert.NotNull(runtimeConfiguration.Prometheus.ScrapeEndpoint); - Assert.Equal(metricUnavailableValue, runtimeConfiguration.MetricsConfiguration.MetricUnavailableValue); + Assert.Equal(metricUnavailableValue, runtimeConfiguration.Prometheus.MetricUnavailableValue); } [Fact] @@ -205,7 +204,7 @@ public async Task RuntimeConfiguration_HasConfiguredPrometheusScrapeEndpointConf // Arrange var scrapeEndpointBaseUri = _faker.System.DirectoryPath(); var configuration = await RuntimeConfigurationGenerator.WithServerConfiguration() - .WithPrometheusConfiguration(scrapeEndpointBaseUri) + .WithPrometheusConfiguration(scrapeEndpointBaseUri: scrapeEndpointBaseUri) .GenerateAsync(); // Act @@ -321,7 +320,6 @@ public async Task RuntimeConfiguration_HasNoMetricsConfigurationBasePathConfigur // Assert Assert.NotNull(runtimeConfiguration); Assert.NotNull(runtimeConfiguration.MetricsConfiguration); - Assert.NotEqual(Defaults.MetricsConfiguration.MetricUnavailableValue, runtimeConfiguration.MetricsConfiguration.MetricUnavailableValue); Assert.Equal(Defaults.MetricsConfiguration.AbsolutePath, runtimeConfiguration.MetricsConfiguration.AbsolutePath); } @@ -330,7 +328,7 @@ public async Task RuntimeConfiguration_HasNoMetricUnavailableValuePathConfigured { // Arrange var configuration = await RuntimeConfigurationGenerator.WithServerConfiguration() - .WithMetricsConfiguration(metricUnavailableValue: null) + .WithPrometheusConfiguration(metricUnavailableValue: null) .GenerateAsync(); // Act @@ -338,17 +336,18 @@ public async Task RuntimeConfiguration_HasNoMetricUnavailableValuePathConfigured // Assert Assert.NotNull(runtimeConfiguration); - Assert.NotNull(runtimeConfiguration.MetricsConfiguration); - Assert.NotEqual(Defaults.MetricsConfiguration.AbsolutePath, runtimeConfiguration.MetricsConfiguration.AbsolutePath); - Assert.Equal(Defaults.MetricsConfiguration.MetricUnavailableValue, runtimeConfiguration.MetricsConfiguration.MetricUnavailableValue); + Assert.NotNull(runtimeConfiguration.Prometheus); + Assert.NotNull(runtimeConfiguration.Prometheus.ScrapeEndpoint); + Assert.NotEqual(Defaults.Prometheus.ScrapeEndpointBaseUri, runtimeConfiguration.Prometheus.ScrapeEndpoint.BaseUriPath); + Assert.Equal(Defaults.Prometheus.MetricUnavailableValue, runtimeConfiguration.Prometheus.MetricUnavailableValue); } [Fact] - public async Task RuntimeConfiguration_HasNoMetricConfigurationConfigured_UsesDefault() + public async Task RuntimeConfiguration_HasNoPrometheusConfigurationConfigured_UsesDefault() { // Arrange var configuration = await RuntimeConfigurationGenerator.WithServerConfiguration() - .WithMetricsConfiguration(metricUnavailableValue: null, absolutePath:null) + .WithPrometheusConfiguration(metricUnavailableValue: null, scrapeEndpointBaseUri: null) .GenerateAsync(); // Act @@ -356,9 +355,10 @@ public async Task RuntimeConfiguration_HasNoMetricConfigurationConfigured_UsesDe // Assert Assert.NotNull(runtimeConfiguration); - Assert.NotNull(runtimeConfiguration.MetricsConfiguration); - Assert.Equal(Defaults.MetricsConfiguration.AbsolutePath, runtimeConfiguration.MetricsConfiguration.AbsolutePath); - Assert.Equal(Defaults.MetricsConfiguration.MetricUnavailableValue, runtimeConfiguration.MetricsConfiguration.MetricUnavailableValue); + 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] @@ -366,7 +366,7 @@ public async Task RuntimeConfiguration_HasNoPrometheusScrapeEndpointConfigured_U { // Arrange var configuration = await RuntimeConfigurationGenerator.WithServerConfiguration() - .WithPrometheusConfiguration(null) + .WithPrometheusConfiguration(scrapeEndpointBaseUri:null) .GenerateAsync(); // Act @@ -377,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 0afaa5b31..e520f60d8 100644 --- a/src/Promitor.Scraper.Tests.Unit/Generators/Config/BogusRuntimeConfigurationGenerator.cs +++ b/src/Promitor.Scraper.Tests.Unit/Generators/Config/BogusRuntimeConfigurationGenerator.cs @@ -27,7 +27,6 @@ internal static RuntimeConfiguration Generate() var metricsConfiguration = new Faker() .StrictMode(true) .RuleFor(metricConfiguration => metricConfiguration.AbsolutePath, faker => faker.System.DirectoryPath()) - .RuleFor(metricConfiguration => metricConfiguration.MetricUnavailableValue, faker => faker.Random.Double(min: 1)) .Generate(); var scrapeEndpointConfiguration = new Faker() .StrictMode(true) @@ -35,7 +34,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 b75244833..ad861bfd3 100644 --- a/src/Promitor.Scraper.Tests.Unit/Generators/Config/RuntimeConfigurationGenerator.cs +++ b/src/Promitor.Scraper.Tests.Unit/Generators/Config/RuntimeConfigurationGenerator.cs @@ -46,46 +46,46 @@ 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 - { - ScrapeEndpoint = new ScrapeEndpointConfiguration - { - BaseUriPath = scrapeEndpointBaseUri - } - }; - - _runtimeConfiguration.Prometheus = prometheusConfiguration; - - return this; - } - - public RuntimeConfigurationGenerator WithMetricsConfiguration(double? metricUnavailableValue = -1, string absolutePath = "/metrics-declaration.yaml") - { - MetricsConfiguration metricsConfiguration; - if (absolutePath == null && metricUnavailableValue == null) + PrometheusConfiguration prometheusConfiguration; + if (string.IsNullOrWhiteSpace(scrapeEndpointBaseUri) && metricUnavailableValue == null) { - metricsConfiguration = null; + prometheusConfiguration = null; } else { - metricsConfiguration = new MetricsConfiguration(); + prometheusConfiguration = new PrometheusConfiguration(); - if (absolutePath != null) + if (string.IsNullOrWhiteSpace(scrapeEndpointBaseUri) == false) { - metricsConfiguration.AbsolutePath = absolutePath; + prometheusConfiguration.ScrapeEndpoint = new ScrapeEndpointConfiguration + { + BaseUriPath = scrapeEndpointBaseUri + }; } if (metricUnavailableValue != null) { - metricsConfiguration.MetricUnavailableValue = (double)metricUnavailableValue; + prometheusConfiguration.MetricUnavailableValue = (double)metricUnavailableValue; } } + _runtimeConfiguration.Prometheus = prometheusConfiguration; + + return this; + } + + public RuntimeConfigurationGenerator WithMetricsConfiguration(string absolutePath = "/metrics-declaration.yaml") + { + var metricsConfiguration = absolutePath == null + ? null + : new MetricsConfiguration + { + AbsolutePath = absolutePath + }; + _runtimeConfiguration.MetricsConfiguration = metricsConfiguration; return this; @@ -178,6 +178,11 @@ 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) @@ -187,11 +192,6 @@ public async Task GenerateAsync() { configurationBuilder.AppendLine($" absolutePath: {_runtimeConfiguration?.MetricsConfiguration.AbsolutePath}"); } - - if (_runtimeConfiguration?.MetricsConfiguration.MetricUnavailableValue != null) - { - configurationBuilder.AppendLine($" metricUnavailableValue: {_runtimeConfiguration?.MetricsConfiguration.MetricUnavailableValue}"); - } } if (_runtimeConfiguration?.Telemetry != null) From 1cfd8b68a4033476826fac11a49ad003ef13a334 Mon Sep 17 00:00:00 2001 From: Tom Kerkhove Date: Tue, 6 Aug 2019 21:19:05 +0200 Subject: [PATCH 4/9] Guard for null --- .../Prometheus/PrometheusMetricWriter.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Promitor.Core.Scraping/Prometheus/PrometheusMetricWriter.cs b/src/Promitor.Core.Scraping/Prometheus/PrometheusMetricWriter.cs index a241cf83f..ee1d19407 100644 --- a/src/Promitor.Core.Scraping/Prometheus/PrometheusMetricWriter.cs +++ b/src/Promitor.Core.Scraping/Prometheus/PrometheusMetricWriter.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using GuardNet; using Microsoft.Extensions.Logging; using Prometheus.Client; using Promitor.Core.Configuration.FeatureFlags; @@ -15,6 +16,9 @@ public class PrometheusMetricWriter : IPrometheusMetricWriter public PrometheusMetricWriter(FeatureToggleClient featureToggleClient, ILogger logger) { + Guard.NotNull(featureToggleClient, nameof(featureToggleClient)); + Guard.NotNull(logger, nameof(logger)); + _featureToggleClient = featureToggleClient; _logger = logger; } From f40720580b33a042a55835d77b1bf8fd08b8dadb Mon Sep 17 00:00:00 2001 From: Tom Kerkhove Date: Wed, 7 Aug 2019 14:23:50 +0200 Subject: [PATCH 5/9] Move to ScraperConfiguration --- .../Factories/MetricScraperFactory.cs | 22 +++++++++---------- .../ResourceTypes/ContainerInstanceScraper.cs | 5 ++--- .../ResourceTypes/ContainerRegistryScraper.cs | 5 ++--- .../ResourceTypes/CosmosDbScraper.cs | 5 ++--- .../ResourceTypes/GenericScraper.cs | 5 ++--- .../ResourceTypes/NetworkInterfaceScraper.cs | 5 ++--- .../ResourceTypes/PostgreSqlScraper.cs | 5 ++--- .../ResourceTypes/RedisCacheScraper.cs | 5 ++--- .../ResourceTypes/ServiceBusQueueScraper.cs | 5 ++--- .../ResourceTypes/StorageQueueScraper.cs | 5 ++--- .../ResourceTypes/VirtualMachineScraper.cs | 5 ++--- src/Promitor.Core.Scraping/Scraper.cs | 5 ++--- .../ScraperConfiguration.cs | 11 +++++++++- 13 files changed, 43 insertions(+), 45 deletions(-) diff --git a/src/Promitor.Core.Scraping/Factories/MetricScraperFactory.cs b/src/Promitor.Core.Scraping/Factories/MetricScraperFactory.cs index 61c126310..925d21c40 100644 --- a/src/Promitor.Core.Scraping/Factories/MetricScraperFactory.cs +++ b/src/Promitor.Core.Scraping/Factories/MetricScraperFactory.cs @@ -45,30 +45,30 @@ public IScraper CreateScraper(ResourceType metricDefinitionRes 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) { case ResourceType.ServiceBusQueue: - return new ServiceBusQueueScraper(scraperConfiguration, prometheusMetricWriter); + return new ServiceBusQueueScraper(scraperConfiguration); case ResourceType.Generic: - return new GenericScraper(scraperConfiguration, prometheusMetricWriter); + return new GenericScraper(scraperConfiguration); case ResourceType.StorageQueue: - return new StorageQueueScraper(scraperConfiguration, prometheusMetricWriter); + return new StorageQueueScraper(scraperConfiguration); case ResourceType.ContainerInstance: - return new ContainerInstanceScraper(scraperConfiguration, prometheusMetricWriter); + return new ContainerInstanceScraper(scraperConfiguration); case ResourceType.VirtualMachine: - return new VirtualMachineScraper(scraperConfiguration, prometheusMetricWriter); + return new VirtualMachineScraper(scraperConfiguration); case ResourceType.NetworkInterface: - return new NetworkInterfaceScraper(scraperConfiguration, prometheusMetricWriter); + return new NetworkInterfaceScraper(scraperConfiguration); case ResourceType.ContainerRegistry: - return new ContainerRegistryScraper(scraperConfiguration, prometheusMetricWriter); + return new ContainerRegistryScraper(scraperConfiguration); case ResourceType.CosmosDb: - return new CosmosDbScraper(scraperConfiguration, prometheusMetricWriter); + return new CosmosDbScraper(scraperConfiguration); case ResourceType.RedisCache: - return new RedisCacheScraper(scraperConfiguration, prometheusMetricWriter); + return new RedisCacheScraper(scraperConfiguration); case ResourceType.PostgreSql: - return new PostgreSqlScraper(scraperConfiguration, prometheusMetricWriter); + return new PostgreSqlScraper(scraperConfiguration); default: throw new ArgumentOutOfRangeException(); } diff --git a/src/Promitor.Core.Scraping/ResourceTypes/ContainerInstanceScraper.cs b/src/Promitor.Core.Scraping/ResourceTypes/ContainerInstanceScraper.cs index 53b486dfd..0ec1b81a8 100644 --- a/src/Promitor.Core.Scraping/ResourceTypes/ContainerInstanceScraper.cs +++ b/src/Promitor.Core.Scraping/ResourceTypes/ContainerInstanceScraper.cs @@ -2,7 +2,6 @@ using System.Threading.Tasks; using Microsoft.Azure.Management.Monitor.Fluent.Models; using Promitor.Core.Scraping.Configuration.Model.Metrics.ResourceTypes; -using Promitor.Core.Scraping.Prometheus.Interfaces; namespace Promitor.Core.Scraping.ResourceTypes { @@ -10,8 +9,8 @@ public class ContainerInstanceScraper : Scraper { private const string ResourceUriTemplate = "subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.DocumentDB/databaseAccounts/{2}"; - public CosmosDbScraper(ScraperConfiguration scraperConfiguration, IPrometheusMetricWriter prometheusMetricWriter) - : base(scraperConfiguration, prometheusMetricWriter) + public CosmosDbScraper(ScraperConfiguration scraperConfiguration) + : base(scraperConfiguration) { } diff --git a/src/Promitor.Core.Scraping/ResourceTypes/GenericScraper.cs b/src/Promitor.Core.Scraping/ResourceTypes/GenericScraper.cs index e3f84d892..083163995 100644 --- a/src/Promitor.Core.Scraping/ResourceTypes/GenericScraper.cs +++ b/src/Promitor.Core.Scraping/ResourceTypes/GenericScraper.cs @@ -2,7 +2,6 @@ using System.Threading.Tasks; using Microsoft.Azure.Management.Monitor.Fluent.Models; using Promitor.Core.Scraping.Configuration.Model.Metrics.ResourceTypes; -using Promitor.Core.Scraping.Prometheus.Interfaces; namespace Promitor.Core.Scraping.ResourceTypes { @@ -10,8 +9,8 @@ internal class GenericScraper : Scraper { private const string ResourceUriTemplate = "subscriptions/{0}/resourceGroups/{1}/providers/{2}"; - public GenericScraper(ScraperConfiguration scraperConfiguration, IPrometheusMetricWriter prometheusMetricWriter) - : base(scraperConfiguration, prometheusMetricWriter) + public GenericScraper(ScraperConfiguration scraperConfiguration) + : base(scraperConfiguration) { } diff --git a/src/Promitor.Core.Scraping/ResourceTypes/NetworkInterfaceScraper.cs b/src/Promitor.Core.Scraping/ResourceTypes/NetworkInterfaceScraper.cs index 6a7ee2892..29720ce3a 100644 --- a/src/Promitor.Core.Scraping/ResourceTypes/NetworkInterfaceScraper.cs +++ b/src/Promitor.Core.Scraping/ResourceTypes/NetworkInterfaceScraper.cs @@ -2,7 +2,6 @@ using System.Threading.Tasks; using Microsoft.Azure.Management.Monitor.Fluent.Models; using Promitor.Core.Scraping.Configuration.Model.Metrics.ResourceTypes; -using Promitor.Core.Scraping.Prometheus.Interfaces; namespace Promitor.Core.Scraping.ResourceTypes { @@ -10,8 +9,8 @@ internal class NetworkInterfaceScraper : Scraper { private const string ResourceUriTemplate = "subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.DBforPostgreSQL/servers/{2}"; - public PostgreSqlScraper(ScraperConfiguration scraperConfiguration, IPrometheusMetricWriter prometheusMetricWriter) - : base(scraperConfiguration, prometheusMetricWriter) + public PostgreSqlScraper(ScraperConfiguration scraperConfiguration) + : base(scraperConfiguration) { } diff --git a/src/Promitor.Core.Scraping/ResourceTypes/RedisCacheScraper.cs b/src/Promitor.Core.Scraping/ResourceTypes/RedisCacheScraper.cs index bb2bb3005..c31854b47 100644 --- a/src/Promitor.Core.Scraping/ResourceTypes/RedisCacheScraper.cs +++ b/src/Promitor.Core.Scraping/ResourceTypes/RedisCacheScraper.cs @@ -2,7 +2,6 @@ using Promitor.Core.Scraping.Configuration.Model.Metrics.ResourceTypes; using System; using System.Threading.Tasks; -using Promitor.Core.Scraping.Prometheus.Interfaces; namespace Promitor.Core.Scraping.ResourceTypes { @@ -10,8 +9,8 @@ public class RedisCacheScraper : Scraper { private const string ResourceUriTemplate = "subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Cache/Redis/{2}"; - public RedisCacheScraper(ScraperConfiguration scraperConfiguration, IPrometheusMetricWriter prometheusMetricWriter) - : base(scraperConfiguration, prometheusMetricWriter) + public RedisCacheScraper(ScraperConfiguration scraperConfiguration) + : base(scraperConfiguration) { } diff --git a/src/Promitor.Core.Scraping/ResourceTypes/ServiceBusQueueScraper.cs b/src/Promitor.Core.Scraping/ResourceTypes/ServiceBusQueueScraper.cs index 340d8d87c..9462e587c 100644 --- a/src/Promitor.Core.Scraping/ResourceTypes/ServiceBusQueueScraper.cs +++ b/src/Promitor.Core.Scraping/ResourceTypes/ServiceBusQueueScraper.cs @@ -3,7 +3,6 @@ using System.Threading.Tasks; using Microsoft.Azure.Management.Monitor.Fluent.Models; using Promitor.Core.Scraping.Configuration.Model.Metrics.ResourceTypes; -using Promitor.Core.Scraping.Prometheus.Interfaces; namespace Promitor.Core.Scraping.ResourceTypes { @@ -11,8 +10,8 @@ public class ServiceBusQueueScraper : Scraper { private const string ResourceUriTemplate = "subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.ServiceBus/namespaces/{2}"; - public ServiceBusQueueScraper(ScraperConfiguration scraperConfiguration, IPrometheusMetricWriter prometheusMetricWriter) - : base(scraperConfiguration, prometheusMetricWriter) + public ServiceBusQueueScraper(ScraperConfiguration scraperConfiguration) + : base(scraperConfiguration) { } diff --git a/src/Promitor.Core.Scraping/ResourceTypes/StorageQueueScraper.cs b/src/Promitor.Core.Scraping/ResourceTypes/StorageQueueScraper.cs index 58bc96249..488420093 100644 --- a/src/Promitor.Core.Scraping/ResourceTypes/StorageQueueScraper.cs +++ b/src/Promitor.Core.Scraping/ResourceTypes/StorageQueueScraper.cs @@ -4,7 +4,6 @@ using GuardNet; using Microsoft.Azure.Management.Monitor.Fluent.Models; using Promitor.Core.Scraping.Configuration.Model.Metrics.ResourceTypes; -using Promitor.Core.Scraping.Prometheus.Interfaces; using Promitor.Integrations.AzureStorage; namespace Promitor.Core.Scraping.ResourceTypes @@ -13,8 +12,8 @@ public class StorageQueueScraper : Scraper { private const string ResourceUriTemplate = "subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Storage/storageAccounts/{2}/queueServices"; private readonly AzureStorageQueueClient _azureStorageQueueClient; - public StorageQueueScraper(ScraperConfiguration scraperConfiguration, IPrometheusMetricWriter prometheusMetricWriter) - : base(scraperConfiguration, prometheusMetricWriter) + public StorageQueueScraper(ScraperConfiguration scraperConfiguration) + : base(scraperConfiguration) { _azureStorageQueueClient = new AzureStorageQueueClient(scraperConfiguration.Logger); } diff --git a/src/Promitor.Core.Scraping/ResourceTypes/VirtualMachineScraper.cs b/src/Promitor.Core.Scraping/ResourceTypes/VirtualMachineScraper.cs index 4a37b715b..06f061272 100644 --- a/src/Promitor.Core.Scraping/ResourceTypes/VirtualMachineScraper.cs +++ b/src/Promitor.Core.Scraping/ResourceTypes/VirtualMachineScraper.cs @@ -2,7 +2,6 @@ using System.Threading.Tasks; using Microsoft.Azure.Management.Monitor.Fluent.Models; using Promitor.Core.Scraping.Configuration.Model.Metrics.ResourceTypes; -using Promitor.Core.Scraping.Prometheus.Interfaces; namespace Promitor.Core.Scraping.ResourceTypes { @@ -10,8 +9,8 @@ internal class VirtualMachineScraper : Scraper { private const string ResourceUriTemplate = "subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Compute/virtualMachines/{2}"; - public VirtualMachineScraper(ScraperConfiguration scraperConfiguration, IPrometheusMetricWriter prometheusMetricWriter) - : base(scraperConfiguration, prometheusMetricWriter) + public VirtualMachineScraper(ScraperConfiguration scraperConfiguration) + : base(scraperConfiguration) { } diff --git a/src/Promitor.Core.Scraping/Scraper.cs b/src/Promitor.Core.Scraping/Scraper.cs index e132c756f..457075e05 100644 --- a/src/Promitor.Core.Scraping/Scraper.cs +++ b/src/Promitor.Core.Scraping/Scraper.cs @@ -30,15 +30,14 @@ public abstract class Scraper : IScraper /// /// Constructor /// - protected Scraper(ScraperConfiguration scraperConfiguration, IPrometheusMetricWriter prometheusMetricWriter) + protected Scraper(ScraperConfiguration scraperConfiguration) { Guard.NotNull(scraperConfiguration, nameof(scraperConfiguration)); - Guard.NotNull(prometheusMetricWriter, nameof(prometheusMetricWriter)); _logger = scraperConfiguration.Logger; _exceptionTracker = scraperConfiguration.ExceptionTracker; _scraperConfiguration = scraperConfiguration; - _prometheusMetricWriter = prometheusMetricWriter; + _prometheusMetricWriter = scraperConfiguration.PrometheusMetricWriter; AzureMetadata = scraperConfiguration.AzureMetadata; AzureMonitorClient = scraperConfiguration.AzureMonitorClient; 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; From 2965ffdd969e20f57b4fb694a5f0f5b29f21ccbe Mon Sep 17 00:00:00 2001 From: Tom Kerkhove Date: Wed, 7 Aug 2019 14:27:20 +0200 Subject: [PATCH 6/9] Use configuration to determine value when metric is not available --- .../Prometheus/PrometheusMetricWriter.cs | 13 ++++++++++--- src/runtime-config.yaml | 1 + 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/Promitor.Core.Scraping/Prometheus/PrometheusMetricWriter.cs b/src/Promitor.Core.Scraping/Prometheus/PrometheusMetricWriter.cs index ee1d19407..d64252121 100644 --- a/src/Promitor.Core.Scraping/Prometheus/PrometheusMetricWriter.cs +++ b/src/Promitor.Core.Scraping/Prometheus/PrometheusMetricWriter.cs @@ -2,8 +2,11 @@ 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; @@ -12,14 +15,17 @@ 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, 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; } @@ -34,9 +40,10 @@ public void ReportMetric(MetricDefinition metricDefinition, ScrapeResult scraped gauge.WithLabels(labels.Values).Set(metricValue); } - private static double DetermineMetricMeasurement(ScrapeResult scrapedMetricResult) + private double DetermineMetricMeasurement(ScrapeResult scrapedMetricResult) { - return scrapedMetricResult.MetricValue ?? double.NaN; + var metricUnavailableValue = _prometheusConfiguration.CurrentValue?.MetricUnavailableValue ?? Defaults.Prometheus.MetricUnavailableValue; + return scrapedMetricResult.MetricValue ?? metricUnavailableValue; } private (string[] Names, string[] Values) DetermineLabels(MetricDefinition metricDefinition, ScrapeResult scrapeResult) 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: From 1bbb6a66413d8444da0397cb6ce9f516786b055a Mon Sep 17 00:00:00 2001 From: Tom Kerkhove Date: Wed, 7 Aug 2019 14:31:50 +0200 Subject: [PATCH 7/9] Code quality --- .../Generators/Config/RuntimeConfigurationGenerator.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Promitor.Scraper.Tests.Unit/Generators/Config/RuntimeConfigurationGenerator.cs b/src/Promitor.Scraper.Tests.Unit/Generators/Config/RuntimeConfigurationGenerator.cs index ad861bfd3..552a401a9 100644 --- a/src/Promitor.Scraper.Tests.Unit/Generators/Config/RuntimeConfigurationGenerator.cs +++ b/src/Promitor.Scraper.Tests.Unit/Generators/Config/RuntimeConfigurationGenerator.cs @@ -69,7 +69,6 @@ public RuntimeConfigurationGenerator WithPrometheusConfiguration(double? metricU { prometheusConfiguration.MetricUnavailableValue = (double)metricUnavailableValue; } - } _runtimeConfiguration.Prometheus = prometheusConfiguration; From 74672b9d46194a2dfc03a22a965837982eb35631 Mon Sep 17 00:00:00 2001 From: Tom Kerkhove Date: Wed, 7 Aug 2019 14:45:31 +0200 Subject: [PATCH 8/9] Code quality --- .../Extensions/IServiceCollectionExtensions.cs | 1 - .../Validation/Steps/MetricsDeclarationValidationStep.cs | 3 +-- .../Generators/Config/BogusRuntimeConfigurationGenerator.cs | 4 +--- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/Promitor.Scraper.Host/Extensions/IServiceCollectionExtensions.cs b/src/Promitor.Scraper.Host/Extensions/IServiceCollectionExtensions.cs index ba502fed0..4019944c5 100644 --- a/src/Promitor.Scraper.Host/Extensions/IServiceCollectionExtensions.cs +++ b/src/Promitor.Scraper.Host/Extensions/IServiceCollectionExtensions.cs @@ -15,7 +15,6 @@ using Promitor.Core.Configuration.Model.Server; using Promitor.Core.Configuration.Model.Telemetry; using Promitor.Core.Configuration.Model.Telemetry.Sinks; -using Promitor.Core.Scraping; using Promitor.Core.Scraping.Factories; using Promitor.Core.Scraping.Prometheus; using Promitor.Core.Scraping.Prometheus.Interfaces; 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/Generators/Config/BogusRuntimeConfigurationGenerator.cs b/src/Promitor.Scraper.Tests.Unit/Generators/Config/BogusRuntimeConfigurationGenerator.cs index e520f60d8..053bce1a7 100644 --- a/src/Promitor.Scraper.Tests.Unit/Generators/Config/BogusRuntimeConfigurationGenerator.cs +++ b/src/Promitor.Scraper.Tests.Unit/Generators/Config/BogusRuntimeConfigurationGenerator.cs @@ -1,6 +1,4 @@ -using System; -using Bogus; -using Microsoft.EntityFrameworkCore.Diagnostics; +using Bogus; using Microsoft.Extensions.Logging; using Promitor.Core.Configuration.Model; using Promitor.Core.Configuration.Model.FeatureFlags; From 67478c0328625a5bfefd31c12fbb802b434212b3 Mon Sep 17 00:00:00 2001 From: Tom Kerkhove Date: Fri, 9 Aug 2019 11:48:14 +0200 Subject: [PATCH 9/9] Tweak code styling --- src/Promitor.sln.DotSettings | 2 ++ 1 file changed, 2 insertions(+) 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