From edccef82ff2b3d15310e7efd724c1efdaf246ff2 Mon Sep 17 00:00:00 2001 From: David Sze Date: Tue, 7 Jun 2022 16:30:18 -0400 Subject: [PATCH] feat: support custom metric types Add a `custom()` method where the metric type is passed in by the caller, to allow using statsd backends that support metric types beyond those supported by the original Etsy statsd daemon. --- README.md | 14 ++++++++++++++ include/cpp-statsd-client/StatsdClient.hpp | 17 +++++++++++++++++ tests/testStatsdClient.cpp | 5 +++++ 3 files changed, 36 insertions(+) diff --git a/README.md b/README.md index 2cf77f7..df629e0 100644 --- a/README.md +++ b/README.md @@ -134,6 +134,20 @@ One may also attach tags to a metrics, e.g. client.gauge("titi", 3.2, 0.1f, {"foo", "bar"}); ``` +### Custom metric types + +Some statsd backends (e.g. Datadog, netdata) support metric types beyond those supported by the original Etsy statsd daemon, e.g. + +* Histograms (type "h"), which are like Timers, but with different units +* Dictionaries (type "d"), which are like Sets but also record a count for each unique value + +Non-standard metrics can be sent using the `custom()` method, where the metric type is passed in by the caller. The client performs no validation, under the assumption that the caller will only pass in types, values, and other optional parameters (e.g. frequency, tags) that are supported by their backend. The templated `value` parameter must support serialization to a `std::basic_ostream` using `operator<<`. + +```cpp +// Record a histogram of packet sizes +client.custom("packet_size", 1500, "h"); +``` + ## Advanced Testing A simple mock StatsD server can be found at `tests/StatsdServer.hpp`. This can be used to do simple validation of your application's metrics, typically in the form of unit tests. In fact this is the primary means by which this library is tested. The mock server itself is not distributed with the library so to use it you'd need to vendor this project into your project. Once you have though, you can test your application's use of the client like so: diff --git a/include/cpp-statsd-client/StatsdClient.hpp b/include/cpp-statsd-client/StatsdClient.hpp index 8c19426..7114432 100644 --- a/include/cpp-statsd-client/StatsdClient.hpp +++ b/include/cpp-statsd-client/StatsdClient.hpp @@ -122,6 +122,14 @@ class StatsdClient { float frequency = 1.0f, const std::vector& tags = {}) const noexcept; + //! Records a custom metric type for the key, with a given value, at a given frequency + template + void custom(const std::string& key, + const T value, + const char* type, + float frequency = 1.0f, + const std::vector& tags = {}) const noexcept; + //! Seed the RNG that controls sampling void seed(unsigned int seed = std::random_device()()) noexcept; @@ -248,6 +256,15 @@ inline void StatsdClient::set(const std::string& key, send(key, sum, detail::METRIC_TYPE_SET, frequency, tags); } +template +inline void StatsdClient::custom(const std::string& key, + const T value, + const char* type, + const float frequency, + const std::vector& tags) const noexcept { + send(key, value, type, frequency, tags); +} + template inline void StatsdClient::send(const std::string& key, const T value, diff --git a/tests/testStatsdClient.cpp b/tests/testStatsdClient.cpp index 74db846..22e0d6e 100644 --- a/tests/testStatsdClient.cpp +++ b/tests/testStatsdClient.cpp @@ -139,6 +139,11 @@ void testSendRecv(uint64_t batchSize, uint64_t sendInterval) { client.count("foo", -42, .9f, {"bar", "baz"}); throwOnError(client); expected.emplace_back("sendRecv.foo:-42|c|@0.90|#bar,baz"); + + // Custom metric type should pass through all params + client.custom("custom_metric_type", 5678, "cust", .95f, {"tag1", "tag2"}); + throwOnError(client); + expected.emplace_back("sendRecv.custom_metric_type:5678|cust|@0.95|#tag1,tag2"); } // Signal the mock server we are done