Skip to content

Commit

Permalink
Sanitize label names to comply with Prometheus requirements (#1253)
Browse files Browse the repository at this point in the history
* Sanitize label names to comply with Prometheus requirements

Signed-off-by: Tom Kerkhove <[email protected]>

* Add new metric

Signed-off-by: Tom Kerkhove <[email protected]>

* Description

Signed-off-by: Tom Kerkhove <[email protected]>
  • Loading branch information
tomkerkhove authored Aug 31, 2020
1 parent 014c89a commit d423406
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 5 deletions.
14 changes: 13 additions & 1 deletion config/promitor/scraper/metrics.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -127,4 +127,16 @@ metrics:
aggregation:
type: Total
resourceDiscoveryGroups:
- name: service-bus-landscape
- 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// ReSharper disable once CheckNamespace

namespace System
{
public static class StringExtensions
{
/// <summary>
/// Ensures label key name is valid according to Prometheus requirements
/// </summary>
/// <param name="labelKey">Current label key value</param>
public static string SanitizeForPrometheusLabelKey(this string labelKey)
{
if (string.IsNullOrWhiteSpace(labelKey))
{
return labelKey;
}

return labelKey.Replace("/", "_").ToLower();
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using GuardNet;
Expand Down Expand Up @@ -85,18 +86,18 @@ private IMetricFamily<IGauge> CreateGauge(string metricName, string metricDescri

private Dictionary<string, string> DetermineLabels(PrometheusMetricDefinition metricDefinition, ScrapeResult scrapeResult, MeasuredMetric measuredMetric)
{
var labels = new Dictionary<string, string>(scrapeResult.Labels.Select(label => new KeyValuePair<string, string>(label.Key.ToLower(), label.Value)));
var labels = new Dictionary<string, string>(scrapeResult.Labels.Select(label => new KeyValuePair<string, string>(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);
Expand Down
51 changes: 51 additions & 0 deletions src/Promitor.Tests.Unit/Prometheus/StringExtensionTests.cs
Original file line number Diff line number Diff line change
@@ -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);
}
}
}

0 comments on commit d423406

Please sign in to comment.