From bbbef943c95e6473fee5849a0c0351d8f8e5d796 Mon Sep 17 00:00:00 2001 From: Brian Drupieski Date: Sun, 10 Mar 2019 09:46:25 -0400 Subject: [PATCH] add prometheus push gateway reporter --- .../MetricsPrometheusPushGatewayReporter.cs | 91 +++++++++++++++++++ ...csPrometheusPushGatewayReporterSettings.cs | 19 ++++ 2 files changed, 110 insertions(+) create mode 100644 src/App.Metrics.Formatters.Prometheus/MetricsPrometheusPushGatewayReporter.cs create mode 100644 src/App.Metrics.Formatters.Prometheus/MetricsPrometheusPushGatewayReporterSettings.cs diff --git a/src/App.Metrics.Formatters.Prometheus/MetricsPrometheusPushGatewayReporter.cs b/src/App.Metrics.Formatters.Prometheus/MetricsPrometheusPushGatewayReporter.cs new file mode 100644 index 0000000..4f14705 --- /dev/null +++ b/src/App.Metrics.Formatters.Prometheus/MetricsPrometheusPushGatewayReporter.cs @@ -0,0 +1,91 @@ +// +// Copyright (c) App Metrics Contributors. All rights reserved. +// + +using System; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using App.Metrics.Filters; +using App.Metrics.Internal.NoOp; +using App.Metrics.Reporting; + +namespace App.Metrics.Formatters.Prometheus +{ + public class MetricsPrometheusPushGatewayReporter : IReportMetrics + { + private static readonly HttpClient _httpClient = new HttpClient { Timeout = TimeSpan.FromSeconds(10) }; + + public IFilterMetrics Filter { get; set; } + + public TimeSpan FlushInterval { get; set; } + + public IMetricsOutputFormatter Formatter { get; set; } + + private readonly Uri _targetUrl; + + public MetricsPrometheusPushGatewayReporter(MetricsPrometheusPushGatewayReporterSettings settings) + { + settings = settings ?? throw new ArgumentNullException(nameof(settings)); + + if (string.IsNullOrEmpty(settings.Endpoint)) + { + throw new ArgumentNullException(nameof(settings.Endpoint)); + } + + if (string.IsNullOrEmpty(settings.Job)) + { + throw new ArgumentNullException(nameof(settings.Job)); + } + + var sb = new StringBuilder($"{settings.Endpoint.TrimEnd('/')}/metrics/job/{settings.Job}"); + + if (!string.IsNullOrEmpty(settings.Instance)) + { + sb.AppendFormat("/instance/{0}", settings.Instance); + } + + if (settings.AdditionalLabels != null) + { + foreach (var label in settings.AdditionalLabels.Where(x => !string.IsNullOrWhiteSpace(x.Key) && !string.IsNullOrWhiteSpace(x.Value))) + { + sb.AppendFormat("/{0}/{1}", label.Key, label.Value); + } + } + + if (!Uri.TryCreate(sb.ToString(), UriKind.Absolute, out _targetUrl)) + { + throw new ArgumentException("Endpoint must be a valid url", nameof(settings.Endpoint)); + } + + Formatter = new MetricsPrometheusTextOutputFormatter(); + FlushInterval = TimeSpan.FromSeconds(20); + Filter = new NullMetricsFilter(); + } + + public async Task FlushAsync(MetricsDataValueSource metricsData, CancellationToken cancellationToken) + { + var request = new HttpRequestMessage(HttpMethod.Put, _targetUrl); + request.Content = BuildStreamContent(metricsData, cancellationToken); + + var response = await _httpClient.SendAsync(request, cancellationToken); + + return response.IsSuccessStatusCode; + } + + private HttpContent BuildStreamContent(MetricsDataValueSource metrics, CancellationToken cancellationToken) + { + var memoryStream = new MemoryStream(); + Formatter.WriteAsync(memoryStream, metrics, cancellationToken); + + var content = new ByteArrayContent(memoryStream.ToArray()); + content.Headers.ContentType = new MediaTypeHeaderValue(Formatter.MediaType.ContentType); + + return content; + } + } +} \ No newline at end of file diff --git a/src/App.Metrics.Formatters.Prometheus/MetricsPrometheusPushGatewayReporterSettings.cs b/src/App.Metrics.Formatters.Prometheus/MetricsPrometheusPushGatewayReporterSettings.cs new file mode 100644 index 0000000..66c0410 --- /dev/null +++ b/src/App.Metrics.Formatters.Prometheus/MetricsPrometheusPushGatewayReporterSettings.cs @@ -0,0 +1,19 @@ +// +// Copyright (c) App Metrics Contributors. All rights reserved. +// + +using System.Collections.Generic; + +namespace App.Metrics.Formatters.Prometheus +{ + public class MetricsPrometheusPushGatewayReporterSettings + { + public string Endpoint { get; set; } + + public string Job { get; set; } + + public string Instance { get; set; } + + public IEnumerable> AdditionalLabels { get; set; } + } +} \ No newline at end of file