diff --git a/config/promitor/scraper/metrics.yaml b/config/promitor/scraper/metrics.yaml index e0fb6afb2..a30605b0d 100644 --- a/config/promitor/scraper/metrics.yaml +++ b/config/promitor/scraper/metrics.yaml @@ -127,4 +127,16 @@ metrics: aggregation: type: Total resourceDiscoveryGroups: - - name: service-bus-landscape \ No newline at end of file + - name: service-bus-landscape + - name: promitor_demo_app_insights + description: "Average dependency duration per dependency type" + resourceType: Generic + azureMetricConfiguration: + metricName: dependencies/duration + dimension: + name: dependency/type + aggregation: + type: Average + resources: + - resourceUri: Microsoft.Insights/Components/docker-hub-metrics + resourceGroupName: docker-hub-metrics \ No newline at end of file diff --git a/src/Promitor.Integrations.Sinks.Prometheus/Extensions/StringExtensions.cs b/src/Promitor.Integrations.Sinks.Prometheus/Extensions/StringExtensions.cs new file mode 100644 index 000000000..20e2999c2 --- /dev/null +++ b/src/Promitor.Integrations.Sinks.Prometheus/Extensions/StringExtensions.cs @@ -0,0 +1,21 @@ +// ReSharper disable once CheckNamespace + +namespace System +{ + public static class StringExtensions + { + /// + /// Ensures label key name is valid according to Prometheus requirements + /// + /// Current label key value + public static string SanitizeForPrometheusLabelKey(this string labelKey) + { + if (string.IsNullOrWhiteSpace(labelKey)) + { + return labelKey; + } + + return labelKey.Replace("/", "_").ToLower(); + } + } +} \ No newline at end of file diff --git a/src/Promitor.Integrations.Sinks.Prometheus/PrometheusScrapingEndpointMetricSink.cs b/src/Promitor.Integrations.Sinks.Prometheus/PrometheusScrapingEndpointMetricSink.cs index 320057098..d1b152242 100644 --- a/src/Promitor.Integrations.Sinks.Prometheus/PrometheusScrapingEndpointMetricSink.cs +++ b/src/Promitor.Integrations.Sinks.Prometheus/PrometheusScrapingEndpointMetricSink.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using GuardNet; @@ -85,18 +86,18 @@ private IMetricFamily CreateGauge(string metricName, string metricDescri private Dictionary DetermineLabels(PrometheusMetricDefinition metricDefinition, ScrapeResult scrapeResult, MeasuredMetric measuredMetric) { - var labels = new Dictionary(scrapeResult.Labels.Select(label => new KeyValuePair(label.Key.ToLower(), label.Value))); + var labels = new Dictionary(scrapeResult.Labels.Select(label => new KeyValuePair(label.Key.SanitizeForPrometheusLabelKey(), label.Value))); if (measuredMetric.IsDimensional) { - labels.Add(measuredMetric.DimensionName.ToLower(), measuredMetric.DimensionValue); + labels.Add(measuredMetric.DimensionName.SanitizeForPrometheusLabelKey(), measuredMetric.DimensionValue); } if (metricDefinition?.Labels?.Any() == true) { foreach (var customLabel in metricDefinition.Labels) { - var customLabelKey = customLabel.Key.ToLower(); + var customLabelKey = customLabel.Key.SanitizeForPrometheusLabelKey(); if (labels.ContainsKey(customLabelKey)) { _logger.LogWarning("Custom label {CustomLabelName} was already specified with value 'LabelValue' instead of 'CustomLabelValue'. Ignoring...", customLabel.Key, labels[customLabelKey], customLabel.Value); diff --git a/src/Promitor.Tests.Unit/Prometheus/StringExtensionTests.cs b/src/Promitor.Tests.Unit/Prometheus/StringExtensionTests.cs new file mode 100644 index 000000000..8a38c0be5 --- /dev/null +++ b/src/Promitor.Tests.Unit/Prometheus/StringExtensionTests.cs @@ -0,0 +1,51 @@ +using System; +using System.ComponentModel; +using Xunit; + +namespace Promitor.Tests.Unit.Prometheus +{ + [Category("Unit")] + public class StringExtensionTests + { + [Fact] + public void SanitizeForPrometheusLabelKey_ValidKey_IdenticalOutput() + { + // Arrange + var labelKeyInput = "sample_label"; + + // Act + var sanitizedLabelKey = labelKeyInput.SanitizeForPrometheusLabelKey(); + + // Assert + Assert.Equal(labelKeyInput, sanitizedLabelKey); + } + + [Fact] + public void SanitizeForPrometheusLabelKey_KeyIsUppercase_ConvertedToLowercase() + { + // Arrange + var labelKeyInput = "SAMPLE_LABEL"; + var expectedLabelKey = "sample_label"; + + // Act + var sanitizedLabelKey = labelKeyInput.SanitizeForPrometheusLabelKey(); + + // Assert + Assert.Equal(expectedLabelKey, sanitizedLabelKey); + } + + [Fact] + public void SanitizeForPrometheusLabelKey_KeyWithSlash_SlashIsReplaced() + { + // Arrange + var labelKeyInput = "sample/label"; + var expectedLabelKey = "sample_label"; + + // Act + var sanitizedLabelKey = labelKeyInput.SanitizeForPrometheusLabelKey(); + + // Assert + Assert.Equal(expectedLabelKey, sanitizedLabelKey); + } + } +} \ No newline at end of file