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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
|
# Prometheus Client Library for Modern C++
[![CI Status](https://github.com/jupp0r/prometheus-cpp/workflows/Continuous%20Integration/badge.svg)](https://github.com/jupp0r/prometheus-cpp/actions?workflow=Continuous+Integration)
[![Travis Status](https://travis-ci.org/jupp0r/prometheus-cpp.svg?branch=master)](https://travis-ci.org/jupp0r/prometheus-cpp)
[![Coverage Status](https://coveralls.io/repos/github/jupp0r/prometheus-cpp/badge.svg?branch=master)](https://coveralls.io/github/jupp0r/prometheus-cpp?branch=master)
[![Coverity Scan](https://scan.coverity.com/projects/10567/badge.svg)](https://scan.coverity.com/projects/jupp0r-prometheus-cpp)
This library aims to enable
[Metrics-Driven Development](https://sookocheff.com/post/mdd/mdd/) for
C++ services. It implements the
[Prometheus Data Model](https://prometheus.io/docs/concepts/data_model/),
a powerful abstraction on which to collect and expose metrics. We
offer the possibility for metrics to be collected by Prometheus, but
other push/pull collections can be added as plugins.
## Usage
See https://jupp0r.github.io/prometheus-cpp for more detailed interface documentation.
``` c++
#include <prometheus/counter.h>
#include <prometheus/exposer.h>
#include <prometheus/registry.h>
#include <array>
#include <chrono>
#include <cstdlib>
#include <memory>
#include <string>
#include <thread>
int main() {
using namespace prometheus;
// create an http server running on port 8080
Exposer exposer{"127.0.0.1:8080"};
// create a metrics registry
// @note it's the users responsibility to keep the object alive
auto registry = std::make_shared<Registry>();
// add a new counter family to the registry (families combine values with the
// same name, but distinct label dimensions)
//
// @note please follow the metric-naming best-practices:
// https://prometheus.io/docs/practices/naming/
auto& packet_counter = BuildCounter()
.Name("observed_packets_total")
.Help("Number of observed packets")
.Register(*registry);
// add and remember dimensional data, incrementing those is very cheap
auto& tcp_rx_counter =
packet_counter.Add({{"protocol", "tcp"}, {"direction", "rx"}});
auto& tcp_tx_counter =
packet_counter.Add({{"protocol", "tcp"}, {"direction", "tx"}});
auto& udp_rx_counter =
packet_counter.Add({{"protocol", "udp"}, {"direction", "rx"}});
auto& udp_tx_counter =
packet_counter.Add({{"protocol", "udp"}, {"direction", "tx"}});
// add a counter whose dimensional data is not known at compile time
// nevertheless dimensional values should only occur in low cardinality:
// https://prometheus.io/docs/practices/naming/#labels
auto& http_requests_counter = BuildCounter()
.Name("http_requests_total")
.Help("Number of HTTP requests")
.Register(*registry);
// ask the exposer to scrape the registry on incoming HTTP requests
exposer.RegisterCollectable(registry);
for (;;) {
std::this_thread::sleep_for(std::chrono::seconds(1));
const auto random_value = std::rand();
if (random_value & 1) tcp_rx_counter.Increment();
if (random_value & 2) tcp_tx_counter.Increment();
if (random_value & 4) udp_rx_counter.Increment();
if (random_value & 8) udp_tx_counter.Increment();
const std::array<std::string, 4> methods = {"GET", "PUT", "POST", "HEAD"};
auto method = methods.at(random_value % methods.size());
// dynamically calling Family<T>.Add() works but is slow and should be
// avoided
http_requests_counter.Add({{"method", method}}).Increment();
}
return 0;
}
```
## Requirements
Using `prometheus-cpp` requires a C++11 compliant compiler. It has been successfully tested with GNU GCC 7.4 on Ubuntu Bionic (18.04) and Visual Studio 2017 (but Visual Studio 2015 should work, too).
## Building
There are two supported ways to build
`prometheus-cpp` - [CMake](https://cmake.org)
and [bazel](https://bazel.io). Both are tested in CI and should work
on master and for all releases.
In case these instructions don't work for you, looking at
the [GitHub Workflows](.github/workflows) might help.
### via CMake
For CMake builds don't forget to fetch the submodules first. Please note that
[zlib](https://zlib.net/) and [libcurl](https://curl.se/) are not provided by
the included submodules. In the example below their usage is disabled.
Then build as usual.
``` shell
# fetch third-party dependencies
git submodule init
git submodule update
mkdir _build
cd _build
# run cmake
cmake .. -DBUILD_SHARED_LIBS=ON -DENABLE_PUSH=OFF -DENABLE_COMPRESSION=OFF
# build
cmake --build . --parallel 4
# run tests
ctest -V
# install the libraries and headers
cmake --install .
```
### via Bazel
Install [bazel](https://www.bazel.io). Bazel makes it easy to add
this repo to your project as a dependency. Just add the following
to your `WORKSPACE`:
```python
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive", "http_file")
http_archive(
name = "com_github_jupp0r_prometheus_cpp",
strip_prefix = "prometheus-cpp-master",
urls = ["https://github.com/jupp0r/prometheus-cpp/archive/master.zip"],
)
load("@com_github_jupp0r_prometheus_cpp//bazel:repositories.bzl", "prometheus_cpp_repositories")
prometheus_cpp_repositories()
```
Then, you can reference this library in your own `BUILD` file, as
demonstrated with the sample server included in this repository:
```python
cc_binary(
name = "sample_server",
srcs = ["sample_server.cc"],
deps = ["@com_github_jupp0r_prometheus_cpp//pull"],
)
```
When you call `prometheus_cpp_repositories()` in your `WORKSPACE` file,
you load the following dependencies, if they do not exist yet, into your project:
* `civetweb` for [Civetweb](https://github.com/civetweb/civetweb)
* `com_google_googletest` for [Google Test](https://github.com/google/googletest)
* `com_github_google_benchmark` for [Google Benchmark](https://github.com/google/benchmark)
* `com_github_curl` for [curl](https://curl.haxx.se/)
* `net_zlib_zlib` for [zlib](http://www.zlib.net/)
The list of dependencies is also available from file [repositories.bzl](bazel/repositories.bzl).
## Packaging
By configuring CPack you can generate an installer like a
Debian package (.deb) or RPM (.rpm) for the static or dynamic
libraries so they can be easily installed on
other systems.
Please refer to the [CPack](https://cmake.org/cmake/help/latest/module/CPack.html)
documentation for all available generators and their
configuration options.
To generate a Debian package you could follow these steps:
``` shell
# fetch third-party dependencies
git submodule update --init
# run cmake
cmake -B_build -DCPACK_GENERATOR=DEB -DBUILD_SHARED_LIBS=ON # or OFF for static libraries
# build and package
cmake --build _build --target package --parallel $(nproc)
```
This will place an appropriately named .deb in the
`_build` folder. To build a RPM package set the `CPACK_GENERATOR`
variable to `RPM`.
## Consuming the installed project
### CMake
Consuming prometheus-cpp via CMake is the preferred way because all the dependencies
between the three prometheus-cpp libraries are handled correctly.
The `cmake/project-import` directory contains an
example project and minimal [CMakeLists.txt](cmake/project-import-cmake/CMakeLists.txt).
### vcpkg
The [vcpkg](https://github.com/microsoft/vcpkg) package manager contains a
prometheus-cpp port which has been tested on Linux, macOS, and Windows.
### Conan
[Conan](https://conan.io/) package manager contains prometheus-cpp package as well
in [ConanCenter](https://conan.io/center/prometheus-cpp) repository
### Plain Makefiles
When manually linking prometheus-cpp the library order matters. The needed
libraries depend on the individual use case but the following should work for the pull metrics approach:
```
-lprometheus-cpp-pull -lprometheus-cpp-core -lz
```
For the push-workflow please try:
```
-lprometheus-cpp-push -lprometheus-cpp-core -lcurl -lz
```
## Contributing
Please adhere to the [Google C++ Style
Guide](https://google.github.io/styleguide/cppguide.html). Make sure
to clang-format your patches before opening a PR. Also make sure to
adhere to [these commit message
guidelines](https://chris.beams.io/posts/git-commit/).
You can check out this repo and build the library using
``` bash
bazel build //... # build everything
bazel build //core //pull # build just the libraries
```
Run the unit tests using
```
bazel test //...
```
There is also an integration test that
uses [telegraf](https://github.com/influxdata/telegraf) to scrape a
sample server. With telegraf installed, it can be run using
```
bazel test //pull/tests/integration:scrape-test
```
## Benchmarks
There's a benchmark suite you can run:
```
bazel run -c opt //core/benchmarks
INFO: Analysed target //core/benchmarks:benchmarks (0 packages loaded, 0 targets configured).
INFO: Found 1 target...
Target //core/benchmarks:benchmarks up-to-date:
bazel-bin/core/benchmarks/benchmarks
INFO: Elapsed time: 0.356s, Critical Path: 0.01s, Remote (0.00% of the time): [queue: 0.00%, setup: 0.00%, process: 0.00%]
INFO: 0 processes.
INFO: Build completed successfully, 1 total action
INFO: Build completed successfully, 1 total action
2018-11-30 15:13:14
Run on (4 X 2200 MHz CPU s)
CPU Caches:
L1 Data 32K (x2)
L1 Instruction 32K (x2)
L2 Unified 262K (x2)
L3 Unified 4194K (x1)
-----------------------------------------------------------------------------------
Benchmark Time CPU Iterations
-----------------------------------------------------------------------------------
BM_Counter_Increment 13 ns 12 ns 55616469
BM_Counter_Collect 7 ns 7 ns 99823170
BM_Gauge_Increment 12 ns 12 ns 51511873
BM_Gauge_Decrement 12 ns 12 ns 56831098
BM_Gauge_SetToCurrentTime 184 ns 183 ns 3928964
BM_Gauge_Collect 6 ns 6 ns 117223478
BM_Histogram_Observe/0 134 ns 124 ns 5665310
BM_Histogram_Observe/1 122 ns 120 ns 5937185
BM_Histogram_Observe/8 137 ns 135 ns 4652863
BM_Histogram_Observe/64 143 ns 143 ns 4835957
BM_Histogram_Observe/512 259 ns 257 ns 2334750
BM_Histogram_Observe/4096 1545 ns 1393 ns 620754
BM_Histogram_Collect/0 103 ns 102 ns 5654829
BM_Histogram_Collect/1 100 ns 100 ns 7015153
BM_Histogram_Collect/8 608 ns 601 ns 1149652
BM_Histogram_Collect/64 1438 ns 1427 ns 515236
BM_Histogram_Collect/512 5178 ns 5159 ns 114619
BM_Histogram_Collect/4096 33527 ns 33280 ns 20785
BM_Registry_CreateFamily 320 ns 316 ns 2021567
BM_Registry_CreateCounter/0 128 ns 128 ns 5487140
BM_Registry_CreateCounter/1 2066 ns 2058 ns 386002
BM_Registry_CreateCounter/8 7672 ns 7634 ns 91328
BM_Registry_CreateCounter/64 63270 ns 62761 ns 10780
BM_Registry_CreateCounter/512 560714 ns 558328 ns 1176
BM_Registry_CreateCounter/4096 18672798 ns 18383000 ns 35
BM_Summary_Observe/0/iterations:262144 9351 ns 9305 ns 262144
BM_Summary_Observe/1/iterations:262144 9242 ns 9169 ns 262144
BM_Summary_Observe/8/iterations:262144 14344 ns 14195 ns 262144
BM_Summary_Observe/64/iterations:262144 19176 ns 18950 ns 262144
BM_Summary_Collect/0/0 31 ns 30 ns 24873766
BM_Summary_Collect/1/0 166 ns 166 ns 4266706
BM_Summary_Collect/8/0 1040 ns 1036 ns 660527
BM_Summary_Collect/64/0 4529 ns 4489 ns 155600
BM_Summary_Collect/0/1 28 ns 28 ns 24866697
BM_Summary_Collect/1/1 190 ns 188 ns 3930354
BM_Summary_Collect/8/1 1372 ns 1355 ns 535779
BM_Summary_Collect/64/1 9901 ns 9822 ns 64632
BM_Summary_Collect/0/8 29 ns 29 ns 24922651
BM_Summary_Collect/1/8 217 ns 215 ns 3278381
BM_Summary_Collect/8/8 2275 ns 2256 ns 282503
BM_Summary_Collect/64/8 56790 ns 55804 ns 13878
BM_Summary_Collect/0/64 32 ns 31 ns 22548350
BM_Summary_Collect/1/64 395 ns 389 ns 1817073
BM_Summary_Collect/8/64 10187 ns 10064 ns 71928
BM_Summary_Collect/64/64 374835 ns 373560 ns 1812
BM_Summary_Collect/0/512 28 ns 28 ns 25234228
BM_Summary_Collect/1/512 1710 ns 1639 ns 802285
BM_Summary_Collect/8/512 50355 ns 49335 ns 15975
BM_Summary_Collect/64/512 2520972 ns 2493417 ns 295
BM_Summary_Collect/0/4096 31 ns 31 ns 24059034
BM_Summary_Collect/1/4096 2719 ns 2698 ns 286186
BM_Summary_Collect/8/4096 121689 ns 119995 ns 5647
BM_Summary_Collect/64/4096 5660131 ns 5587634 ns 134
BM_Summary_Collect/0/32768 29 ns 29 ns 22217567
BM_Summary_Collect/1/32768 4344 ns 4294 ns 138135
BM_Summary_Collect/8/32768 331563 ns 326403 ns 2017
BM_Summary_Collect/64/32768 16363553 ns 16038182 ns 44
BM_Summary_Collect/0/262144 27 ns 27 ns 23923036
BM_Summary_Collect/1/262144 10457 ns 10332 ns 67690
BM_Summary_Collect/8/262144 930434 ns 869234 ns 792
BM_Summary_Collect/64/262144 39217069 ns 39054846 ns 13
BM_Summary_Observe_Common/iterations:262144 5587 ns 5557 ns 262144
BM_Summary_Collect_Common/0 676 ns 673 ns 1054630
BM_Summary_Collect_Common/1 709 ns 705 ns 990659
BM_Summary_Collect_Common/8 1030 ns 1025 ns 685649
BM_Summary_Collect_Common/64 2066 ns 2055 ns 339969
BM_Summary_Collect_Common/512 5754 ns 5248 ns 156895
BM_Summary_Collect_Common/4096 23894 ns 23292 ns 31096
BM_Summary_Collect_Common/32768 49831 ns 49292 ns 13492
BM_Summary_Collect_Common/262144 128723 ns 126987 ns 5579
```
## Project Status
Stable and used in production.
Parts of the library are instrumented by itself
(bytes scraped, number of scrapes, scrape request latencies). There
is a working [example](pull/tests/integration/sample_server.cc) that's
scraped by telegraf as part of integration tests.
## FAQ
### What scrape formats do you support
Only the [Prometheus Text Exposition
Format](https://github.com/prometheus/docs/blob/master/content/docs/instrumenting/exposition_formats.md#text-format-details).
Support for the protobuf format was removed because it's been removed
from Prometheus 2.0.
## License
MIT
|