Skip to content

Commit

Permalink
Introduce metric severity (#107)
Browse files Browse the repository at this point in the history
* Drop debug API

* Make send vector method private

* Rename methods passing values to backend to transmit

* Fix influxdb http backend

* Added metric verbosity

* Fix - remove unintended code

* Match metric and backend verbosities

* Add verbosity example

* Fix buffering

* Derived metric should have the same verbosity

* Add verbosity example

* Adapt README
  • Loading branch information
awegrzyn authored Jan 24, 2019
1 parent 975f0d3 commit dee48df
Show file tree
Hide file tree
Showing 18 changed files with 196 additions and 145 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ target_compile_features(Monitoring PUBLIC cxx_std_17)
set(EXAMPLES
examples/1-Basic.cxx
examples/2-TaggedMetrics.cxx
examples/3-Verbosity.cxx
examples/4-RateDerivedMetric.cxx
examples/5-Benchmark.cxx
examples/6-Increment.cxx
Expand Down
81 changes: 38 additions & 43 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ make install

## Getting started
### Monitoring instance
The recommended way of getting (`unique_ptr` to) monitoring instance is `Get`ing it from `MonitoringFactory` by passing backend URI(s) as a parameter (comma seperated if more than one).
The library is accessible from `o2::monitoring` namespace.
The recommended way of getting monitoring instance is `Get`ing it from `MonitoringFactory` by passing backend's URI(s) as a parameter (comma separated if more than one).
The factory is accessible from `o2::monitoring` namespace.

```cpp
#include <MonitoringFactory.h>
Expand All @@ -67,53 +67,62 @@ std::unique_ptr<Monitoring> monitoring = MonitoringFactory::Get("backend[-protoc
See table below to find out how to create `URI` for each backend:
| Backend name | Transport | URI backend[-protocol] | URI query | Default verbosity |
| ------------ |:---------:|:----------------------:|:----------------:| -----------------:|
| InfluxDB | HTTP | `influxdb-http` | `/write?db=<db>` | `prod` |
| InfluxDB | UDP | `influxdb-udp` | - | `prod` |
| ApMon | UDP | `apmon` | - | `prod` |
| StdOut | - | `stdout`, `infologger` | - | `debug` |
| Flume | UDP | `flume` | - | `prod` |
| Backend name | Transport | URI backend[-protocol] | URI query | Default verbosity |
| ------------ |:---------:|:----------------------:|:----------:| -----------------:|
| InfluxDB | HTTP | `influxdb-http` | `?db=<db>` | `info` |
| InfluxDB | UDP | `influxdb-udp` | - | `info` |
| ApMon | UDP | `apmon` | - | `info` |
| StdOut | - | `stdout`, `infologger` | - | `debug` |
| Flume | UDP | `flume` | - | `info` |
Multiple backends may be used at the same time, URLs should be separated by `,` (comma).
#### StdCout output format
##### StdCout output format
```
[METRIC] <name>,<type> <value> <timestamp> <tags>
```

### Sending metric
### Metrics
A metric consist of 5 parameters: name, value, timestamp, verbosity and tags.

| Parameter name | Type | Required | Default |
| -------------- |:---------------------------------------------:|:--------:| --------------:|
| name | string | yes | - |
| value | int / double / string / uint64_t | yes | - |
| timestamp | chrono::time_point&lt;std::chrono::system_clock&gt; | no | current timestamp |
| verbosity | DEBUG / INFO / PROD | no | INFO |
| tags | vector<unsigned int> | no | - |

A metric can be constructed by providing required parameters:
```cpp
send(Metric&& metric, [DerivedMetricMode mode])
Metric{10, "name"}
```
Where metric constructor receives following parameters:
- `T value`
- `std::string& name`
See how it works in the example: [examples/1-Basic.cxx](examples/1-Basic.cxx).
#### Verbosity
There are 3 verbosity levels (the same as for backends): DEBUG, INFO, PROD. The default verbosity is set using: `Metric::setDefaultVerbosity(verbosity)`.
To overwrite verbosity on per metric basis use third, optional parameter to metric constructor:
```cpp
Metric{10, "name", Verbosity::PROD}
```

Metrics need to match backends verbosity in order to be sent, eg. backend with `/info` verbosity will accept `INFO` and `PROD` metrics only.

The `DerivedMetricMode` is optional and described in [Calculating derived metrics](#calculating-derived-metrics) section.

### Taging metric
#### Tags
Each metric can be tagged with any number of [predefined tags](include/Monitoring/Tags.h).
In order to do so use `addTags(std::initializer_list<unsigned int>&& tags)` method.

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

### Debug metrics
Debug metrics can be send by a similar method to above's `send`:
### Sending metric

```cpp
debug(Metric&& metric)
send(Metric&& metric, [DerivedMetricMode mode])
```
The difference is that debug metrics are only passed to backends which verbosity level is set to `debug`.
See how it works in the example: [examples/1-Basic.cxx](examples/1-Basic.cxx).
Each backend has its default verbosity (see backend in [Monitoring instance](#monitoring-instance) section). This can be changed by defining path of a backend URL:
- `/prod` - only `send` metrics are passed to the backend
- `/debug` - all the metrics are passed to the backend
The `DerivedMetricMode` is optional and described in [Calculating derived metrics](#calculating-derived-metrics) section.
## Features and additional information
## Advanced features
### Sending more than one metric
In order to send more than one metric in a packet group them into vector:
Expand Down Expand Up @@ -141,20 +150,6 @@ monitoring->flushBuffer();

See how it works in the example: [examples/10-Buffering.cxx](examples/10-Buffering.cxx).

### Metrics
Metrics consist of 4 parameters: name, value, timestamp and tags.
| Parameter name | Type | Required | Default |
| -------------- |:---------------------------------------------:|:--------:| ----------------:|
| name | string | yes | - |
| value | int / double / string / uint64_t | yes | - |
| timestamp | chrono::time_point&lt;std::chrono::system_clock&gt; | no | current timestamp |
| tags | vector<unsigned int> | no | -** |
**Default tag set is process specific and included in each metric:
+ hostname
+ process name
### Calculating derived metrics
The module can calculate derived metrics. To do so, use optional `DerivedMetricMode mode` parameter of `send` method:
+ `DerivedMetricMode::NONE` - no action,
Expand All @@ -172,7 +167,7 @@ Glabal tags are tags that are added to each metric. The following tags are set t

You can add your own global tag by calling `addGlobalTag(std::string name, std::string value)`.

### Monitoring process
### Process monitoring
```cpp
enableProcessMonitoring([interval in seconds]);
```
Expand Down
30 changes: 30 additions & 0 deletions examples/3-Verbosity.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
///
/// \file 1-Basic.cxx
/// \author Adam Wegrzynek <[email protected]>
///

#include "Monitoring/MonitoringFactory.h"

using namespace o2::monitoring;

int main() {
// Configure monitoring
// Pass string with list of URLs as parameter
auto monitoring = MonitoringFactory::Get("stdout:///info");

// now send an application specific metric
// 10 is the value
// myMetric is the name of the metric by creating and moving Metric object
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->send({10, "myMetricInt", Verbosity::DEBUG}, DerivedMetricMode::INCREMENT);
monitoring->send({10.10, "myMetricFloat", Verbosity::PROD}, DerivedMetricMode::INCREMENT);

monitoring->enableBuffering();
monitoring->send({10, "myMetricInt", Verbosity::DEBUG});
monitoring->send({10.10, "myMetricFloat", Verbosity::PROD});
}
13 changes: 0 additions & 13 deletions examples/5-Benchmark.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ int main(int argc, char *argv[]) {
("id", boost::program_options::value<std::string>(), "Instance ID")
("count", boost::program_options::value<int>(), "Number of loop cycles")
("multiple", boost::program_options::bool_switch()->default_value(false), "Sends multiple metrics per measurement")
("vector", boost::program_options::bool_switch()->default_value(false), "Sends vector of metrics")
("monitor", boost::program_options::bool_switch()->default_value(false), "Enabled process monitor")
("buffer", boost::program_options::value<int>(), "Creates buffr of given size")
("measurements", boost::program_options::value<int>(), "Number of different measurements")
Expand Down Expand Up @@ -64,17 +63,6 @@ int main(int argc, char *argv[]) {
}
if (!vm.count("count")) j--;
}
} else if (vm["vector"].as<bool>()) {
for (int j = 1; j <= count; j++) {
for (int i = 1; i <= measurements; i++) {
monitoring->send({
{doubleDist(mt), "doubleMetric" + std::to_string(i)},
{intDist(mt), "intMetricDebug" + std::to_string(i)}
});
std::this_thread::sleep_for(std::chrono::microseconds(sleep));
}
if (!vm.count("count")) j--;
}
} else {
if (vm.count("buffer")) {
monitoring->enableBuffering(vm["buffer"].as<int>());
Expand All @@ -83,7 +71,6 @@ int main(int argc, char *argv[]) {
for (int i = 1; i <= measurements; i++) {
monitoring->send({doubleDist(mt), "doubleMetric" + std::to_string(i)});
monitoring->send({intDist(mt), "intMetric" + std::to_string(i)});
monitoring->debug({intDist(mt), "intMetricDebug" + std::to_string(i)});
std::this_thread::sleep_for(std::chrono::microseconds(sleep));
}
if (!vm.count("count")) j--;
Expand Down
1 change: 0 additions & 1 deletion examples/8-Multiple.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,4 @@ int main() {
auto monitoring = Monitoring::Get("stdout://");

monitoring->sendGrouped("measurementName", {{20, "myMetricIntMultiple"}, {20.30, "myMetricFloatMultple"}});
monitoring->send({{201, "myMetricIntMultiple"}, {2.34, "myMetricFloatMultple"}});
}
12 changes: 4 additions & 8 deletions include/Monitoring/Backend.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,6 @@ namespace o2
namespace monitoring
{

namespace backend
{
enum class Verbosity { Prod, Debug };
}
/// \brief Backend pure virtual interface
///
/// Interface that allows to send a metric to remote backend.
Expand All @@ -28,20 +24,20 @@ class Backend
{
private:
/// Verbosity level
backend::Verbosity verbosityLevel;
Verbosity verbosityLevel;

public:
/// Default constructor
Backend() { verbosityLevel = backend::Verbosity::Prod; }
Backend() { verbosityLevel = Verbosity::PROD; }

/// Default destructor
virtual ~Backend() = default;

/// Set verbosity level
void setVerbosisty(backend::Verbosity level) { verbosityLevel = level; }
void setVerbosisty(Verbosity level) { verbosityLevel = level; }

/// Get verbosity level
backend::Verbosity getVerbosity() { return verbosityLevel; }
Verbosity getVerbosity() { return verbosityLevel; }

/// Sends metric via backend
virtual void send(const Metric& metric) = 0;
Expand Down
23 changes: 18 additions & 5 deletions include/Monitoring/Metric.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ namespace o2
namespace monitoring
{

enum class Verbosity : short { PROD, INFO, DEBUG };

enum MetricType { INT = 0, STRING = 1, DOUBLE = 2, UINT64_T = 3 };

/// \brief Represents a metric including value, type of the value, name, timestamp and tags
Expand All @@ -27,27 +29,27 @@ class Metric
/// Integer metric construtor
/// \param value metric value (int)
/// \param name metric name
Metric(int value, const std::string& name);
Metric(int value, const std::string& name, Verbosity verbosity = Metric::DEFAULT_VERBOSITY);

/// String metric construtor
/// \param value metric value (string)
/// \param name the metric name
Metric(std::string value, const std::string& name);
Metric(std::string value, const std::string& name, Verbosity verbosity = Metric::DEFAULT_VERBOSITY);

/// Double metric constructor
/// \param value metric value (double)
/// \param name metric name
Metric(double value, const std::string& name);
Metric(double value, const std::string& name, Verbosity verbosity = Metric::DEFAULT_VERBOSITY);

/// uint64_t metric constructor
/// \param value metric value (uint64_t)
/// \param name metric name
Metric(uint64_t value, const std::string& name);
Metric(uint64_t value, const std::string& name, Verbosity verbosity = Metric::DEFAULT_VERBOSITY);

/// boost variant metric constructor, required by derived metrics logic
/// \param value metric value (boost variant)
/// \param name metric name
Metric(boost::variant< int, std::string, double, uint64_t >, const std::string& name);
Metric(boost::variant< int, std::string, double, uint64_t >, const std::string& name, Verbosity verbosity = Metric::DEFAULT_VERBOSITY);

/// Default destructor
~Metric() = default;
Expand Down Expand Up @@ -77,10 +79,18 @@ class Metric
/// \return r-value to "this" - to be able to chain methods
Metric&& addTags(std::vector<unsigned int>&& tags);

/// Verbosity getter
Verbosity getVerbosity();

/// Generetes current timestamp
/// return timestamp as std::chrono::system_clock
static auto getCurrentTimestamp() -> decltype(std::chrono::system_clock::now());

/// Sets default verbosity of metrics
static void setDefaultVerbosity(Verbosity verbosity);

/// Default metric verbosity
static Verbosity DEFAULT_VERBOSITY;
protected:
/// Metric value
boost::variant< int, std::string, double, uint64_t > mValue;
Expand All @@ -93,6 +103,9 @@ class Metric

/// Metric tags
std::vector<unsigned int> mTags;

/// Metric verbosity
Verbosity mVerbosity;
};

} // namespace monitoring
Expand Down
22 changes: 12 additions & 10 deletions include/Monitoring/Monitoring.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,20 +58,12 @@ class Monitoring
/// \param mode Derived metric mode
void send(Metric&& metric, DerivedMetricMode mode = DerivedMetricMode::NONE);

/// Send metrics to debug backends only
/// \param metric r-value to metric object
void debug(Metric&& metric);

/// Sends multiple (not related to each other) metrics
/// \param metrics vector of metrics
void send(std::vector<Metric>&& metrics);

/// Sends multiple realated to each other metric values under a common measurement name
/// You can imagine it as a data point with multiple values
/// If it's not supported by a backend it fallbacks into sending one by one
/// \param name measurement name
/// \param metrics list of metrics
void sendGrouped(std::string name, std::vector<Metric>&& metrics);
void sendGrouped(std::string name, std::vector<Metric>&& metrics, Verbosity verbosity = Metric::DEFAULT_VERBOSITY);

/// Enables process monitoring
/// \param interval refresh interval
Expand Down Expand Up @@ -99,6 +91,16 @@ class Monitoring
ComplexMetric& getAutoPushMetric(std::string name, unsigned int interval = 1);

private:
/// Sends multiple (not related to each other) metrics
/// \param metrics vector of metrics
void transmit(std::vector<Metric>&& metrics);

/// Flush buffer of desired verbosity
void flushBuffer(short index);

/// Matches verbosity of a backend and a metric in order to decide whether metric should be send to the backend
bool matchVerbosity(Verbosity backend, Verbosity metric);

/// Derived metrics handler
/// \see class DerivedMetrics
std::unique_ptr<DerivedMetrics> mDerivedHandler;
Expand All @@ -122,7 +124,7 @@ class Monitoring
void pushLoop();

/// Metric buffer
std::vector<Metric> mStorage;
std::unordered_map<std::underlying_type<Verbosity>::type, std::vector<Metric>> mStorage;

/// Flag stating whether metric buffering is enabled
bool mBuffering;
Expand Down
6 changes: 3 additions & 3 deletions src/Backends/InfluxDB.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ InfluxDB::InfluxDB(const std::string& host, unsigned int port)
<< " ("<< host << ":" << port << ")" << MonLogger::End();
}

InfluxDB::InfluxDB(const std::string& host, unsigned int port, const std::string& path)
InfluxDB::InfluxDB(const std::string& host, unsigned int port, const std::string& search)
{
transport = std::make_unique<transports::HTTP>(
"http://" + host + ":" + std::to_string(port) + "/?" + path
"http://" + host + ":" + std::to_string(port) + "/write?" + search
);
MonLogger::Get() << "InfluxDB/HTTP backend initialized" << " (" << "http://" << host
<< ":" << std::to_string(port) << "/?" << path << ")" << MonLogger::End();
<< ":" << std::to_string(port) << "/write?" << search << ")" << MonLogger::End();
}

inline unsigned long InfluxDB::convertTimestamp(const std::chrono::time_point<std::chrono::system_clock>& timestamp)
Expand Down
4 changes: 2 additions & 2 deletions src/Backends/InfluxDB.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ class InfluxDB final : public Backend
/// Constructor for HTTP transport
/// \param host InfluxDB HTTP endpoint hostname
/// \param port InfluxDB HTTP endpoint port number
/// \param path Query path
InfluxDB(const std::string& host, unsigned int port, const std::string& path);
/// \param path Query search providing database name
InfluxDB(const std::string& host, unsigned int port, const std::string& search);

/// Default destructor
~InfluxDB() = default;
Expand Down
Loading

0 comments on commit dee48df

Please sign in to comment.