/
handler.cc
159 lines (126 loc) · 4.55 KB
/
handler.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
#include "handler.h"
#include <cstring>
#include "prometheus/counter.h"
#include "prometheus/summary.h"
#ifdef HAVE_ZLIB
#include <zlib.h>
#endif
#include "metrics_collector.h"
#include "prometheus/serializer.h"
#include "prometheus/text_serializer.h"
namespace prometheus {
namespace detail {
MetricsHandler::MetricsHandler(Registry& registry)
: bytes_transferred_family_(
BuildCounter()
.Name("exposer_transferred_bytes_total")
.Help("Transferred bytes to metrics services")
.Register(registry)),
bytes_transferred_(bytes_transferred_family_.Add({})),
num_scrapes_family_(BuildCounter()
.Name("exposer_scrapes_total")
.Help("Number of times metrics were scraped")
.Register(registry)),
num_scrapes_(num_scrapes_family_.Add({})),
request_latencies_family_(
BuildSummary()
.Name("exposer_request_latencies")
.Help("Latencies of serving scrape requests, in microseconds")
.Register(registry)),
request_latencies_(request_latencies_family_.Add(
{}, Summary::Quantiles{{0.5, 0.05}, {0.9, 0.01}, {0.99, 0.001}})) {}
#ifdef HAVE_ZLIB
static bool IsEncodingAccepted(struct mg_connection* conn,
const char* encoding) {
auto accept_encoding = mg_get_header(conn, "Accept-Encoding");
if (!accept_encoding) {
return false;
}
return std::strstr(accept_encoding, encoding) != nullptr;
}
static std::vector<Byte> GZipCompress(const std::string& input) {
auto zs = z_stream{};
auto windowSize = 16 + MAX_WBITS;
auto memoryLevel = 9;
if (deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, windowSize,
memoryLevel, Z_DEFAULT_STRATEGY) != Z_OK) {
return {};
}
zs.next_in = (Bytef*)input.data();
zs.avail_in = input.size();
int ret;
std::vector<Byte> output;
output.reserve(input.size() / 2u);
do {
static const auto outputBytesPerRound = std::size_t{32768};
zs.avail_out = outputBytesPerRound;
output.resize(zs.total_out + zs.avail_out);
zs.next_out = reinterpret_cast<Bytef*>(output.data() + zs.total_out);
ret = deflate(&zs, Z_FINISH);
output.resize(zs.total_out);
} while (ret == Z_OK);
deflateEnd(&zs);
if (ret != Z_STREAM_END) {
return {};
}
return output;
}
#endif
static std::size_t WriteResponse(struct mg_connection* conn,
const std::string& body) {
mg_printf(conn,
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/plain\r\n");
#ifdef HAVE_ZLIB
auto acceptsGzip = IsEncodingAccepted(conn, "gzip");
if (acceptsGzip) {
auto compressed = GZipCompress(body);
if (!compressed.empty()) {
mg_printf(conn,
"Content-Encoding: gzip\r\n"
"Content-Length: %lu\r\n\r\n",
static_cast<unsigned long>(compressed.size()));
mg_write(conn, compressed.data(), compressed.size());
return compressed.size();
}
}
#endif
mg_printf(conn, "Content-Length: %lu\r\n\r\n",
static_cast<unsigned long>(body.size()));
mg_write(conn, body.data(), body.size());
return body.size();
}
void MetricsHandler::RegisterCollectable(
const std::weak_ptr<Collectable>& collectable) {
std::lock_guard<std::mutex> lock{collectables_mutex_};
collectables_.push_back(collectable);
}
int MetricsHandler::requestHandler(struct mg_connection* conn, void* cbdata) {
auto* handler = reinterpret_cast<MetricsHandler*>(cbdata);
auto* request_info = mg_get_request_info(conn);
if (handler && request_info) {
if (std::strcmp(request_info->request_method, "GET") == 0) {
return handler->handleGet(conn) ? 1 : 0;
}
}
return 0; // No handler found
}
bool MetricsHandler::handleGet(struct mg_connection* conn) {
auto start_time_of_request = std::chrono::steady_clock::now();
std::vector<MetricFamily> metrics;
{
std::lock_guard<std::mutex> lock{collectables_mutex_};
metrics = CollectMetrics(collectables_);
}
auto serializer = std::unique_ptr<Serializer>{new TextSerializer()};
auto bodySize = WriteResponse(conn, serializer->Serialize(metrics));
auto stop_time_of_request = std::chrono::steady_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(
stop_time_of_request - start_time_of_request);
request_latencies_.Observe(duration.count());
bytes_transferred_.Increment(bodySize);
num_scrapes_.Increment();
return true;
}
} // namespace detail
} // namespace prometheus