Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/DarkWanderer/metrics-cpp
High-performance metrics library
https://github.com/DarkWanderer/metrics-cpp
cpp metrics performance statsd
Last synced: 2 days ago
JSON representation
High-performance metrics library
- Host: GitHub
- URL: https://github.com/DarkWanderer/metrics-cpp
- Owner: DarkWanderer
- License: mit
- Created: 2023-01-16T12:07:37.000Z (almost 2 years ago)
- Default Branch: main
- Last Pushed: 2024-07-28T13:25:10.000Z (4 months ago)
- Last Synced: 2024-08-02T13:19:03.821Z (3 months ago)
- Topics: cpp, metrics, performance, statsd
- Language: C++
- Homepage:
- Size: 206 KB
- Stars: 3
- Watchers: 3
- Forks: 1
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# metrics-cpp
[![Build](https://github.com/DarkWanderer/metrics-cpp/actions/workflows/build.yml/badge.svg)](https://github.com/DarkWanderer/metrics-cpp/actions/workflows/build.yml)
![Readiness](https://img.shields.io/badge/readiness-beta-yellow)A low-footprint, high-performance C++ metrics library implementing commonly used metric classes - Counter, Gauge, Histogram, Summary - in idiomatic and thread-safe faction
## Design goals
The design goals of this library are the following:
* Be as lightweight as possible - all operations on Counter, Gauge, Histogram are lock-free using atomic operations
* Allow to think of instrumenting first and exposition later
* Provide easy to use API## Key features
* Provides commonly used metric classes
* A number of out-the-box optimizations
* all metrics except Summary are lock-free
* Labels are optimized for cache locality (vector instead of std::map; make sure to use a compiler which takes advantage of [SSO](https://pvs-studio.com/en/blog/terms/6658/))
* Minimized locking for operations in Registry
* Various methods of serialization
* Prometheus
* JSON/JSONL
* statsd
* Cross-platform (built for Windows, Ubuntu, MacOS)## Limitations & compromises
Due to limited number of locks employed, there is no strong consistency guarantee between different metrics. If a particular thread changes two counters and serialization happens in the middle, you may see a value for one counter increasing but not for the other - until the next time metrics are collected. Hence, care must be taken when creating alerts based on metrics differential. Another compromise stemming from minimized locking requirements is inability to _remove_ metrics from a `Registry` - however, that is something which is not supported by Prometheus anyway
## Readiness
|Feature|Readiness|
|----|----|
|Core API|![GA](https://img.shields.io/badge/GA-green)|
|Serialization: JSON|![icon](https://img.shields.io/badge/GA-green)|
|Serialization: Prometheus|![icon](https://img.shields.io/badge/GA-green)|
|Sink: Statsd UDP|![icon](https://img.shields.io/badge/beta-yellow)|
|Sink: Statsd TCP|![icon](https://img.shields.io/badge/beta-yellow)|
|Sink: PushGateway|![icon](https://img.shields.io/badge/alpha-red)|
|Sink: Prometheus HTTP|![icon](https://img.shields.io/badge/beta-yellow)|## Performance
Performance of metrics is comparable to `atomic` primitives - even with pointer indirection
```
Run on (24 X 3700 MHz CPU s)
CPU Caches:
L1 Data 32 KiB (x12)
L1 Instruction 32 KiB (x12)
L2 Unified 512 KiB (x12)
L3 Unified 32768 KiB (x2)
---------------------------------------------------------------
Benchmark Time CPU Iterations
---------------------------------------------------------------
BM_AtomicIncrement 1.39 ns 1.40 ns 448000000
BM_CounterIncrement 1.34 ns 1.32 ns 497777778
BM_GaugeSet 1.95 ns 1.95 ns 344615385
BM_HistogramObserve 6.05 ns 6.00 ns 112000000
BM_SummaryObserve 9.19 ns 9.21 ns 74666667
BM_RegistryGet 53.0 ns 53.7 ns 16592593
BM_RegistryGetLabels 131 ns 134 ns 5600000
```## Usage examples
### Quickstart
The library API was designed to provide a low barrier for entry:
```cpp
auto metrics = createRegistry();
metrics->getCounter( "birds", {{ "kind", "pigeon" }} )++;
metrics->getCounter( "birds", {{ "kind", "sparrow" }} )+=10;
metrics->getGauge( "tiredness" ) += 1.5;
cout << "The library supports outputting metrics in Prometheus format:" << endl << serializePrometheus(*metrics) << endl;
cout << "And in JSON format:" << endl << serializeJsonl(*metrics) << endl;
```For further information on using library via CMake, see [this sample](https://github.com/DarkWanderer/metrics-cpp/tree/main/samples/cmake)
### Standalone metrics
```cpp
Counter c1;
auto c2 = c1; // Another object shares same underlying metric
c2++;
cout << c1.value(); // 1
```### Working with registry
`Registry` is a class representing grouping of metrics within the application. Usually you would have a single `registry` per application or application domain. You can create metrics from within the registry:
```cpp
auto registry = createRegistry();
auto gauge = registry->getGauge("my_gauge", {{"some", "label"}});
gauge = 10.0;
```Or add existing metrics with a key:
```cpp
Gauge gauge;
gauge = 5;
auto registry = createRegistry();
registry->add("my_gauge", {{"some", "label"}}, gauge);
cout << registry->getGauge("my_gauge", {{"some", "label"}}).value(); // 5
```The recommended pattern is to instrument low-level code using standalone metrics and then add the needed metrics to a `registry` instance - this way, you can track same metrics under different names in different contexts
### Serialization
```cpp
auto registry = createRegistry();
auto gauge = registry->getGauge("my_gauge", {{"some", "label"}});
auto p = serializePrometheus(registry);
auto j = serializeJson(registry);
auto s = serializeStatsd(registry);
```### Timers
```cpp
Histogram histogram({1., 2., 5., 10.});
for (auto file: files)
{
Timer timer(histogram); // Adds an observation to the histogram on scope exit
process_file(file);
}
```### Sinks
Sinks can be created explicitly by type or from a URL. In latter case, specific sink type is derived from URL schema, e.g. `statsd+udp` or `pushgateway+http`
```cpp
// Set this value in config
std::string url = "statsd+udp://localhost:1234";auto registry = createRegistry();
auto gauge = registry->getGauge("my_gauge", {{"some", "label"}});
gauge = 5.0;
auto sink = createOnDemandSink(url);
if (sink)
sink->send(registry);
```# 3rd-party tools and libraries
This project utilizes following 3rd-party libraries and tools
* [boost](https://github.com/boostorg/boost/)
* [Catch2](https://github.com/catchorg/Catch2) - C++ testing framework
* [PVS-Studio](https://pvs-studio.com/en/pvs-studio/?utm_source=website&utm_medium=github&utm_campaign=open_source) - static analyzer for C, C++, C#, and Java code