prometheus-net

Форк
0
/
MetricServer.cs 
128 строк · 5.4 Кб
1
using System.Diagnostics;
2
using System.Net;
3

4
namespace Prometheus;
5

6
/// <summary>
7
/// Implementation of a Prometheus exporter that serves metrics using HttpListener.
8
/// This is a stand-alone exporter for apps that do not already have an HTTP server included.
9
/// </summary>
10
public class MetricServer : MetricHandler
11
{
12
    private readonly HttpListener _httpListener = new();
13

14
    /// <summary>
15
    /// Only requests that match this predicate will be served by the metric server. This allows you to add authorization checks.
16
    /// By default (if null), all requests are served.
17
    /// </summary>
18
    public Func<HttpListenerRequest, bool>? RequestPredicate { get; set; }
19

20
    public MetricServer(int port, string url = "metrics/", CollectorRegistry? registry = null, bool useHttps = false) : this("+", port, url, registry, useHttps)
21
    {
22
    }
23

24
    public MetricServer(string hostname, int port, string url = "metrics/", CollectorRegistry? registry = null, bool useHttps = false)
25
    {
26
        var s = useHttps ? "s" : "";
27
        _httpListener.Prefixes.Add($"http{s}://{hostname}:{port}/{url}");
28

29
        _registry = registry ?? Metrics.DefaultRegistry;
30
    }
31

32
    private readonly CollectorRegistry _registry;
33

34
    protected override Task StartServer(CancellationToken cancel)
35
    {
36
        // This will ensure that any failures to start are nicely thrown from StartServerAsync.
37
        _httpListener.Start();
38

39
        // Kick off the actual processing to a new thread and return a Task for the processing thread.
40
        return Task.Factory.StartNew(delegate
41
        {
42
            try
43
            {
44
                Thread.CurrentThread.Name = "Metric Server";     //Max length 16 chars (Linux limitation)
45

46
                while (!cancel.IsCancellationRequested)
47
                {
48
                    // There is no way to give a CancellationToken to GCA() so, we need to hack around it a bit.
49
                    var getContext = _httpListener.GetContextAsync();
50
                    getContext.Wait(cancel);
51
                    var context = getContext.Result;
52

53
                    // Asynchronously process the request.
54
                    _ = Task.Factory.StartNew(async delegate
55
                    {
56
                        var request = context.Request;
57
                        var response = context.Response;
58

59
                        try
60
                        {
61
                            var predicate = RequestPredicate;
62

63
                            if (predicate != null && !predicate(request))
64
                            {
65
                                // Request rejected by predicate.
66
                                response.StatusCode = (int)HttpStatusCode.Forbidden;
67
                                return;
68
                            }
69

70
                            try
71
                            {
72
                                // We first touch the response.OutputStream only in the callback because touching
73
                                // it means we can no longer send headers (the status code).
74
                                var serializer = new TextSerializer(delegate
75
                                {
76
                                    response.ContentType = PrometheusConstants.TextContentTypeWithVersionAndEncoding;
77
                                    response.StatusCode = 200;
78
                                    return response.OutputStream;
79
                                });
80

81
                                await _registry.CollectAndSerializeAsync(serializer, cancel);
82
                                response.OutputStream.Dispose();
83
                            }
84
                            catch (ScrapeFailedException ex)
85
                            {
86
                                // This can only happen before anything is written to the stream, so it
87
                                // should still be safe to update the status code and report an error.
88
                                response.StatusCode = 503;
89

90
                                if (!string.IsNullOrWhiteSpace(ex.Message))
91
                                {
92
                                    using (var writer = new StreamWriter(response.OutputStream))
93
                                        writer.Write(ex.Message);
94
                                }
95
                            }
96
                        }
97
                        catch (Exception ex) when (!(ex is OperationCanceledException))
98
                        {
99
                            if (!_httpListener.IsListening)
100
                                return; // We were shut down.
101

102
                            Trace.WriteLine(string.Format("Error in {0}: {1}", nameof(MetricServer), ex));
103

104
                            try
105
                            {
106
                                response.StatusCode = 500;
107
                            }
108
                            catch
109
                            {
110
                                // Might be too late in request processing to set response code, so just ignore.
111
                            }
112
                        }
113
                        finally
114
                        {
115
                            response.Close();
116
                        }
117
                    });
118
                }
119
            }
120
            finally
121
            {
122
                _httpListener.Stop();
123
                // This should prevent any currently processed requests from finishing.
124
                _httpListener.Close();
125
            }
126
        }, TaskCreationOptions.LongRunning);
127
    }
128
}
129

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.