Skip to content

Commit

Permalink
Add histogram support for Prometheus exporter (#2282)
Browse files Browse the repository at this point in the history
  • Loading branch information
cijothomas authored Aug 25, 2021
1 parent 899777e commit f1e745d
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 41 deletions.
8 changes: 7 additions & 1 deletion examples/Console/TestPrometheusExporter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ namespace Examples.Console
internal class TestPrometheusExporter
{
private static readonly Meter MyMeter = new Meter("TestMeter", "0.0.1");
private static readonly Counter<long> Counter = MyMeter.CreateCounter<long>("counter");
private static readonly Counter<long> Counter = MyMeter.CreateCounter<long>("myCounter");
private static readonly Histogram<long> MyHistogram = MyMeter.CreateHistogram<long>("myHistogram");
private static readonly Random RandomGenerator = new Random();

internal static object Run(int port, int totalDurationInMins)
Expand Down Expand Up @@ -79,6 +80,11 @@ internal static object Run(int port, int totalDurationInMins)
new KeyValuePair<string, object>("tag1", "anothervalue"),
new KeyValuePair<string, object>("tag2", "somethingelse"));

MyHistogram.Record(
RandomGenerator.Next(1, 1500),
new KeyValuePair<string, object>("tag1", "value1"),
new KeyValuePair<string, object>("tag2", "value2"));

Task.Delay(10).Wait();
}
});
Expand Down
104 changes: 64 additions & 40 deletions src/OpenTelemetry.Exporter.Prometheus/PrometheusExporterExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
// </copyright>

using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text;
using OpenTelemetry.Exporter.Prometheus.Implementation;
Expand All @@ -29,12 +30,12 @@ public static class PrometheusExporterExtensions
{
private const string PrometheusCounterType = "counter";
private const string PrometheusGaugeType = "gauge";
private const string PrometheusSummaryType = "summary";
private const string PrometheusSummarySumPostFix = "_sum";
private const string PrometheusSummaryCountPostFix = "_count";
private const string PrometheusSummaryQuantileLabelName = "quantile";
private const string PrometheusSummaryQuantileLabelValueForMin = "0";
private const string PrometheusSummaryQuantileLabelValueForMax = "1";
private const string PrometheusHistogramType = "histogram";
private const string PrometheusHistogramSumPostFix = "_sum";
private const string PrometheusHistogramCountPostFix = "_count";
private const string PrometheusHistogramBucketPostFix = "_bucket";
private const string PrometheusHistogramBucketLabelPositiveInfinity = "+Inf";
private const string PrometheusHistogramBucketLabelLessThan = "le";

/// <summary>
/// Serialize to Prometheus Format.
Expand All @@ -49,7 +50,7 @@ public static void WriteMetricsCollection(this PrometheusExporter exporter, Stre
{
var builder = new PrometheusMetricBuilder()
.WithName(metric.Name)
.WithDescription(metric.Name);
.WithDescription(metric.Description);

// TODO: Use switch case for higher perf.
if (metric.MetricType == MetricType.LongSum)
Expand All @@ -70,8 +71,15 @@ public static void WriteMetricsCollection(this PrometheusExporter exporter, Stre
{
var gaugeMetric = metric as IGaugeMetric;
var longValue = (long)gaugeMetric.LastValue.Value;

// TODO: Prometheus only supports float/double
WriteGauge(writer, builder, metric.Attributes, longValue);
}
else if (metric.MetricType == MetricType.Histogram)
{
var histogramMetric = metric as IHistogramMetric;
WriteHistogram(writer, builder, metric.Attributes, metric.Name, histogramMetric.PopulationSum, histogramMetric.PopulationCount, histogramMetric.Buckets);
}
}
}
}
Expand Down Expand Up @@ -121,48 +129,64 @@ private static void WriteGauge(StreamWriter writer, PrometheusMetricBuilder buil
builder.Write(writer);
}

private static void WriteSummary(
private static void WriteHistogram(
StreamWriter writer,
PrometheusMetricBuilder builder,
IEnumerable<KeyValuePair<string, string>> labels,
IEnumerable<KeyValuePair<string, object>> labels,
string metricName,
double sum,
long count,
double min,
double max)
IEnumerable<HistogramBucket> buckets)
{
builder = builder.WithType(PrometheusSummaryType);
/* For Histogram we emit one row for Sum, Count and as
* many rows as number of buckets.
* myHistogram_sum{tag1="value1",tag2="value2"} 258330 1629860660991
* myHistogram_count{tag1="value1",tag2="value2"} 355 1629860660991
* myHistogram_bucket{tag1="value1",tag2="value2",le="0"} 0 1629860660991
* myHistogram_bucket{tag1="value1",tag2="value2",le="5"} 2 1629860660991
* myHistogram_bucket{tag1="value1",tag2="value2",le="10"} 4 1629860660991
* myHistogram_bucket{tag1="value1",tag2="value2",le="25"} 6 1629860660991
* myHistogram_bucket{tag1="value1",tag2="value2",le="50"} 12 1629860660991
* myHistogram_bucket{tag1="value1",tag2="value2",le="75"} 19 1629860660991
* myHistogram_bucket{tag1="value1",tag2="value2",le="100"} 26 1629860660991
* myHistogram_bucket{tag1="value1",tag2="value2",le="250"} 65 1629860660991
* myHistogram_bucket{tag1="value1",tag2="value2",le="500"} 128 1629860660991
* myHistogram_bucket{tag1="value1",tag2="value2",le="1000"} 241 1629860660991
* myHistogram_bucket{tag1="value1",tag2="value2",le="+Inf"} 355 1629860660991
*/

builder = builder.WithType(PrometheusHistogramType);
var metricValueBuilderSum = builder.AddValue();
metricValueBuilderSum.WithName(metricName + PrometheusHistogramSumPostFix);
metricValueBuilderSum = metricValueBuilderSum.WithValue(sum);
foreach (var label in labels)
{
metricValueBuilderSum.WithLabel(label.Key, label.Value.ToString());
}

var metricValueBuilderCount = builder.AddValue();
metricValueBuilderCount.WithName(metricName + PrometheusHistogramCountPostFix);
metricValueBuilderCount = metricValueBuilderCount.WithValue(count);
foreach (var label in labels)
{
/* For Summary we emit one row for Sum, Count, Min, Max.
Min,Max exports as quantile 0 and 1.
In future, when OpenTelemetry implements more aggregation
algorithms, this section will need to be revisited.
Sample output:
MyMeasure_sum{dim1="value1"} 750 1587013352982
MyMeasure_count{dim1="value1"} 5 1587013352982
MyMeasure{dim1="value2",quantile="0"} 150 1587013352982
MyMeasure{dim1="value2",quantile="1"} 150 1587013352982
*/
builder.AddValue()
.WithName(metricName + PrometheusSummarySumPostFix)
.WithLabel(label.Key, label.Value)
.WithValue(sum);
builder.AddValue()
.WithName(metricName + PrometheusSummaryCountPostFix)
.WithLabel(label.Key, label.Value)
.WithValue(count);
builder.AddValue()
.WithName(metricName)
.WithLabel(label.Key, label.Value)
.WithLabel(PrometheusSummaryQuantileLabelName, PrometheusSummaryQuantileLabelValueForMin)
.WithValue(min);
builder.AddValue()
.WithName(metricName)
.WithLabel(label.Key, label.Value)
.WithLabel(PrometheusSummaryQuantileLabelName, PrometheusSummaryQuantileLabelValueForMax)
.WithValue(max);
metricValueBuilderCount.WithLabel(label.Key, label.Value.ToString());
}

long totalCount = 0;
foreach (var bucket in buckets)
{
totalCount += bucket.Count;
var metricValueBuilderBuckets = builder.AddValue();
metricValueBuilderBuckets.WithName(metricName + PrometheusHistogramBucketPostFix);
metricValueBuilderBuckets = metricValueBuilderBuckets.WithValue(totalCount);
foreach (var label in labels)
{
metricValueBuilderBuckets.WithLabel(label.Key, label.Value.ToString());
}

var bucketName = double.IsPositiveInfinity(bucket.HighBoundary) ?
PrometheusHistogramBucketLabelPositiveInfinity : bucket.HighBoundary.ToString(CultureInfo.InvariantCulture);
metricValueBuilderBuckets.WithLabel(PrometheusHistogramBucketLabelLessThan, bucketName);
}

builder.Write(writer);
Expand Down

0 comments on commit f1e745d

Please sign in to comment.