Skip to content

Commit

Permalink
Support multiple values per metric (#189)
Browse files Browse the repository at this point in the history
  • Loading branch information
awegrzyn authored Mar 31, 2020
1 parent 8c37f47 commit f0cb0e5
Show file tree
Hide file tree
Showing 29 changed files with 314 additions and 522 deletions.
3 changes: 0 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@ message(STATUS " Compiling InfluxDB backend with Unix socket and UDP transport"
add_library(Monitoring SHARED
src/Monitoring.cxx
src/Metric.cxx
src/ComplexMetric.cxx
src/Backends/InfluxDB.cxx
src/Backends/StdOut.cxx
src/DerivedMetrics.cxx
Expand Down Expand Up @@ -174,8 +173,6 @@ set(EXAMPLES
examples/5-Benchmark.cxx
examples/6-Increment.cxx
examples/7-InternalBenchamrk.cxx
examples/8-Multiple.cxx
examples/9-AutoUpdate.cxx
examples/10-Buffering.cxx
)

Expand Down
86 changes: 42 additions & 44 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,77 +43,75 @@ See the table below to find `URI`s for supported backends:
| Backend name | Transport | URI backend[-protocol] | URI query | Default verbosity |
| ------------ |:-----------:|:----------------------:|:-----------:| -----------------:|
| No-op | - | `no-op` | | - |
| No-op | - | `no-op` | - | - |
| InfluxDB | UDP | `influxdb-udp` | - | `info` |
| InfluxDB | Unix socket | `influxdb-unix` | - | `info` |
| InfluxDB | StdOut | `influxdb-stdout` | - | `info` |
| InfluxDB | Kafka | `influxdb-kafka` | Kafka topic | `info` |
| ApMon | UDP | `apmon` | - | `info` |
| StdOut | - | `stdout`, `infologger` | [Prefix] | `debug` |
##### StdCout output format
```
[METRIC] <name>,<type> <value> <timestamp> <tags>
```
The prefix (`[METRIC]`) can be changed using query component.

### Metrics
A metric consist of 5 parameters: name, value, timestamp, verbosity and tags.
A metric consist of 5 parameters:
- name - metric name
- values - vector of value and value name pairs
- timestamp - time of creation
- verbosity - metric "severity"
- tags - metric metadata represented as map
| Parameter name | Type | Required | Default |
| -------------- |:--------------------------------:|:--------:| -----------------------:|
| name | string | yes | - |
| value | int / double / string / uint64_t | yes | - |
| values | map&lt;string, int/double/string/uint64_t&gt; | no/1 | - |
| timestamp | time_point&lt;system_clock&gt; | no | current time |
| verbosity | Enum (Debug/Info/Prod) | no | Verbosity::Info |
| tags | vector<unsigned int> | no | host and process names |
| tags | map | no | host and process names |
A metric can be constructed by providing required parameters (value and name):
A metric can be constructed by providing required parameters (value and metric name, value name is set to `value`):
```cpp
Metric{10, "name"}
```
#### Verbosity
There are 3 verbosity levels (the same as for backends): Debug, Info, Prod. By default it is set to `Verbosity::Info`. The default value can be overwritten using: `Metric::setDefaultVerbosity(verbosity)`.
To overwrite verbosity on per metric basis use third, optional parameter to metric constructor:
#### Values
By default metric can be created with zero or one value (in such case value name is set to `value`). Any additional value may be added using `.addValue` method, therefore the following two metrics are identical:
```cpp
Metric{10, "name", Verbosity::Prod}
Metric{10, "name"}
Metric{"name"}.addValue(10, "value")
```
Metrics need to match backends verbosity in order to be sent, eg. backend with `/info` verbosity will accept `Info` and `Prod` metrics only.

#### Tags
Each metric can be tagged with any number of [predefined tags](include/Monitoring/Tags.h).
In order to do so use `addTag(tags::Key, tags::Value)` or `addTag(tags::Key, unsigned short)` methods. The latter method allows assigning numeric value to a tag.
```cpp
Metric{10, "name"}.addTag(tags::Key::Subsystem, tags::Value::QC)
```

See the example: [examples/2-TaggedMetrics.cxx](examples/2-TaggedMetrics.cxx).

### Sending metric
Pass metric object to send method and l-value reference:
Pass metric object to `send` method as l-value reference:
```cpp
send({10, "name"})
send(Metric{20, "name"}.addTag(tags::Key::CRU, 123))
send(Metric{"throughput"}.addValue(100, "tx").addValue(200, "rx"))
```
See how it works in the example: [examples/1-Basic.cxx](examples/1-Basic.cxx).
## Advanced features
### Sending more than one metric
In order to send more than one metric in a packet group them into vector:
```cpp
monitoring->send(std::vector<Metric>&& metrics);
```

It's also possible to send multiple, grouped values (`InfluxDB` backends are supported); For example `cpu` metric can be composed of `cpuUser`, `cpuSystem` values.
### Metric verbosity
There are 3 verbosity levels (the same as for backends): Debug, Info, Prod. By default it is set to `Verbosity::Info`. The default value can be overwritten using: `Metric::setDefaultVerbosity(verbosity)`.
To overwrite verbosity on per metric basis use third, optional parameter to metric constructor:
```cpp
void sendGroupped(std::string name, std::vector<Metric>&& metrics)
Metric{10, "name", Verbosity::Prod}
```

See how it works in the example: [examples/8-Multiple.cxx](examples/8-Multiple.cxx)
Metrics need to match backends verbosity in order to be sent, eg. backend with `/info` verbosity will accept `Info` and `Prod` metrics only.

### Buffering metrics
In order to avoid sending each metric separately, metrics can be temporary stored in the buffer and flushed at the most convenient moment.
This feature can be operated with following two methods:
This feature can be controlled with following two methods:
```cpp
monitoring->enableBuffering(const std::size_t maxSize)
...
Expand All @@ -124,27 +122,29 @@ monitoring->flushBuffer();
See how it works in the example: [examples/10-Buffering.cxx](examples/10-Buffering.cxx).
### Calculating derived metrics
The module can calculate derived metrics. To do so, use optional `DerivedMetricMode mode` parameter of `send` method:
### Calculating derived values
This feature can calculate derived values. To do so, use optional `DerivedMetricMode mode` parameter of `send` method:
```
send(Metric&& metric, [DerivedMetricMode mode])
```
Three modes are available:
+ `DerivedMetricMode::NONE` - no action,
+ `DerivedMetricMode::RATE` - rate between two following metrics,
+ `DerivedMetricMode::AVERAGE` - average value of all metrics stored in cache.
Two modes are available:
+ `DerivedMetricMode::RATE` - rate between two following values,
+ `DerivedMetricMode::INCREMENT` - sum of all passed values.
Derived metrics are generated each time as new value is passed to the module. Their names are suffixed with derived mode name.
The derived value is generated only from the first value of the metric and it is added to the same metric with the value name suffixed with `_rate`, `_increment` accordingly.
See how it works in the example: [examples/4-RateDerivedMetric.cxx](examples/4-RateDerivedMetric.cxx).
### Global tags
Global tags are added to each metric. Two tags: `hostname` and `name` (process name) are set as global by the library.
Global tags are added to each metric sent using given monitoring instance. Two tags: `hostname` and `name` (process name) are set as global by default.
You can add your own global tag by calling `addGlobalTag(std::string_view key, std::string_view value)` or `addGlobalTag(tags::Key, tags::Value)`.
### Process monitoring
This feature provides basic performance status of the process. Note that is runs in separate thread (without mutex).
```cpp
enableProcessMonitoring([interval in seconds]);
```
Expand All @@ -153,16 +153,14 @@ The following metrics are generated every interval:
+ **involuntaryContextSwitches** - involuntary context switches over time interval
+ **memoryUsagePercentage** - ratio of the process's resident set size to the physical memory on the machine, expressed as a percentage (Linux only)

### Automatic metric updates
Sometimes it's necessary to provide value every exact interval of time (even though value does not change). This can be done using `AutoPushMetric`.
```cpp
ComplexMetric& metric = monitoring->getAutoPushMetric("exampleMetric");
metric = 10;
### StdOut backend output format
```
[METRIC] <name>,<type> <values> <timestamp> <tags>
```
See how it works in the example: [examples/11-AutoUpdate.cxx](examples/11-AutoUpdate.cxx).
The prefix (`[METRIC]`) can be changed using query component.

### Regex verbosity policy
Overwrite metric verbosities using regex expression:
Overwrite metric verbosity using regex expression:
```
Metric::setVerbosityPolicy(Verbosity verbosity, const std::regex& regex)
```
Expand Down
12 changes: 8 additions & 4 deletions examples/1-Basic.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,15 @@ int main()
{
// Configure monitoring
// Pass string with list of URLs as parameter
auto monitoring = MonitoringFactory::Get("stdout://");
auto monitoring = MonitoringFactory::Get("influxdb-stdout://");

// now send an application specific metric
// send a metric using one of two equivalent methods
// 10 is the value
// myMetric is the name of the metric by creating and moving Metric object
monitoring->send({10, "myMetricInt"});
monitoring->send({10.10, "myMetricFloat"});
monitoring->send({10, "myMetricInt"}); // default name is "value"
monitoring->send(Metric{"myMetricInt"}.addValue(10, "value"));

// now send a metric with multiple values
monitoring->send(Metric{10, "myMetricInt"}.addValue(10.10, "value_float"));
monitoring->send(Metric{"myMetricInt"}.addValue(10, "value").addValue(10.10, "value_float"));
}
4 changes: 2 additions & 2 deletions examples/3-Verbosity.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ int main()
monitoring->send({10, "myMetricInt", Verbosity::Debug}, DerivedMetricMode::INCREMENT);
monitoring->send({10.10, "myMetricFloat", Verbosity::Prod}, DerivedMetricMode::INCREMENT);

monitoring->sendGrouped("measurementName", {{20, "myMetricIntMultiple"}, {20.30, "myMetricFloatMultple"}}, Verbosity::Debug);
monitoring->sendGrouped("measurementName", {{20, "myMetricIntMultiple"}, {20.30, "myMetricFloatMultple"}}, Verbosity::Prod);
//monitoring->sendGrouped("measurementName", {{20, "myMetricIntMultiple"}, {20.30, "myMetricFloatMultple"}}, Verbosity::Debug);
//monitoring->sendGrouped("measurementName", {{20, "myMetricIntMultiple"}, {20.30, "myMetricFloatMultple"}}, Verbosity::Prod);

monitoring->send({10, "myMetricInt", Verbosity::Debug}, DerivedMetricMode::INCREMENT);
monitoring->send({10.10, "myMetricFloat", Verbosity::Prod}, DerivedMetricMode::INCREMENT);
Expand Down
8 changes: 5 additions & 3 deletions examples/5-Benchmark.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,11 @@ int main(int argc, char* argv[])
if (vm["multiple"].as<bool>()) {
for (int j = 1; j <= count; j++) {
for (int i = 1; i <= measurements; i++) {
monitoring->sendGrouped("measurement" + std::to_string(i), {{doubleDist(mt), "doubleMetric" + std::to_string(i)},
{intDist(mt), "intMetric" + std::to_string(i)},
{std::rand() % 2, "onOffMetric" + std::to_string(i)}});
monitoring->send(Metric{"measurement" + std::to_string(i)}
.addValue(doubleDist(mt), "doubleMetric")
.addValue(intDist(mt), "intMetric")
.addValue(std::rand() % 2, "onOffMetric")
);
std::this_thread::sleep_for(std::chrono::microseconds(sleep));
}
if (!vm.count("count"))
Expand Down
8 changes: 4 additions & 4 deletions examples/7-InternalBenchamrk.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,17 @@ using namespace std::chrono;
void test(std::unique_ptr<Monitoring>& monitoring)
{
for (int i = 0; i < 100000; i++) {
monitoring->send({10, "myMetricInt"});
monitoring->send({10.10, "myMetricFloat"});
monitoring->send(Metric{"myMetricInt"}.addValue(10, "value"));
monitoring->send(Metric{"myMetricFloat"}.addValue(10.10, "value"));
}
}

void testWithTags(std::unique_ptr<Monitoring>& monitoring)
{
monitoring->addGlobalTag("name", "benchmark");
for (int i = 0; i < 100000; i++) {
monitoring->send(Metric{10, "myMetricInt"}.addTag(tags::Key::Detector, tags::Value::TPC));
monitoring->send(Metric{10.10, "myMetricFloat"}.addTag(tags::Key::Subsystem, tags::Value::QC));
monitoring->send(Metric{"myMetricInt"}.addValue(10, "value").addTag(tags::Key::Detector, tags::Value::TPC));
monitoring->send(Metric{"myMetricFloat"}.addValue(10.10, "value").addTag(tags::Key::Subsystem, tags::Value::QC));
}
}

Expand Down
17 changes: 0 additions & 17 deletions examples/8-Multiple.cxx

This file was deleted.

23 changes: 0 additions & 23 deletions examples/9-AutoUpdate.cxx

This file was deleted.

4 changes: 0 additions & 4 deletions include/Monitoring/Backend.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,6 @@ class Backend
/// Sends multiple metrics not related to each other
virtual void send(std::vector<Metric>&& metrics) = 0;

/// Sends multiple related to each other metrics under a common name
/// If not supported by backend, falls back into sending single metrics
virtual void sendMultiple(std::string measurement, std::vector<Metric>&& metrics) = 0;

/// Sets a tag
virtual void addGlobalTag(std::string_view name, std::string_view value) = 0;
};
Expand Down
70 changes: 0 additions & 70 deletions include/Monitoring/ComplexMetric.h

This file was deleted.

2 changes: 1 addition & 1 deletion include/Monitoring/DerivedMetrics.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ class DerivedMetrics

/// Entry method to DerivedMetrics
/// Switches over processing modes: rate and increment
Metric process(Metric& metric, DerivedMetricMode mode);
void process(Metric& metric, DerivedMetricMode mode);
};

} // namespace monitoring
Expand Down
Loading

0 comments on commit f0cb0e5

Please sign in to comment.