44#include < cpp-statsd-client/UDPSender.hpp>
55#include < cstdint>
66#include < cstdio>
7+ #include < functional>
78#include < iomanip>
89#include < memory>
910#include < random>
1011#include < sstream>
1112#include < string>
13+ #include < utility>
1214#include < vector>
1315
1416namespace Statsd {
1517
18+ namespace detail {
19+ inline std::string sanitizePrefix (std::string prefix) {
20+ // For convenience we provide the dot when generating the stat message
21+ if (!prefix.empty () && prefix.back () == ' .' ) {
22+ prefix.pop_back ();
23+ }
24+ return prefix;
25+ }
26+
27+ inline float rand (unsigned int seed) {
28+ static thread_local std::mt19937 twister (seed);
29+ static thread_local std::uniform_real_distribution<float > dist (0 .f , 1 .f );
30+ return dist (twister);
31+ }
32+
33+ // All supported metric types
34+ constexpr char METRIC_TYPE_COUNT[] = " c" ;
35+ constexpr char METRIC_TYPE_GAUGE[] = " g" ;
36+ constexpr char METRIC_TYPE_TIMING[] = " ms" ;
37+ constexpr char METRIC_TYPE_SET[] = " s" ;
38+ } // namespace detail
39+
1640/* !
1741 *
1842 * Statsd client
@@ -27,8 +51,8 @@ namespace Statsd {
2751 * nor prepend one to the key
2852 *
2953 * The sampling frequency is specified per call and uses a
30- * random number generator to determine whether or not the stat
31- * will be recorded this time or not.
54+ * random number generator (optionally user-specified) to
55+ * determine whether the stat will be recorded this time or not.
3256 *
3357 * The top level configuration includes 2 optional parameters
3458 * that determine how the stats are delivered to statsd. These
@@ -57,6 +81,11 @@ namespace Statsd {
5781 */
5882class StatsdClient {
5983public:
84+
85+ // ! A functor that returns a value between 0 and 1 used
86+ // ! to determine whether a given message is sampled.
87+ using FrequencyFunc = std::function<float ()>;
88+
6089 // !@name Constructor and destructor, non-copyable
6190 // !@{
6291
@@ -67,7 +96,8 @@ class StatsdClient {
6796 const uint64_t batchsize = 0 ,
6897 const uint64_t sendInterval = 1000 ,
6998 const int gaugePrecision = 4 ,
70- unsigned int seed = std::random_device()()) noexcept ;
99+ FrequencyFunc frequencyFunc =
100+ std::bind (detail::rand, std::random_device()())) noexcept ;
71101
72102 StatsdClient (const StatsdClient&) = delete;
73103 StatsdClient& operator =(const StatsdClient&) = delete ;
@@ -142,7 +172,6 @@ class StatsdClient {
142172
143173 // !@}
144174
145- private:
146175 // ! The prefix to be used for metrics
147176 std::string m_prefix;
148177
@@ -151,41 +180,21 @@ class StatsdClient {
151180
152181 // ! Fixed floating point precision of gauges
153182 int m_gaugePrecision;
154- };
155-
156- namespace detail {
157- inline std::string sanitizePrefix (std::string prefix) {
158- // For convenience we provide the dot when generating the stat message
159- if (!prefix.empty () && prefix.back () == ' .' ) {
160- prefix.pop_back ();
161- }
162- return prefix;
163- }
164183
165- inline std::mt19937& rng (unsigned int seed = 0 ) {
166- static thread_local std::mt19937 twister (seed);
167- return twister;
168- }
169-
170- // All supported metric types
171- constexpr char METRIC_TYPE_COUNT[] = " c" ;
172- constexpr char METRIC_TYPE_GAUGE[] = " g" ;
173- constexpr char METRIC_TYPE_TIMING[] = " ms" ;
174- constexpr char METRIC_TYPE_SET[] = " s" ;
175- } // namespace detail
184+ // ! The function used to determine whether a message is sampled
185+ FrequencyFunc m_frequencyFunc;
186+ };
176187
177188inline StatsdClient::StatsdClient (const std::string& host,
178189 const uint16_t port,
179190 const std::string& prefix,
180191 const uint64_t batchsize,
181192 const uint64_t sendInterval,
182193 const int gaugePrecision,
183- const unsigned int seed ) noexcept
194+ FrequencyFunc frequencyFunc ) noexcept
184195 : m_prefix(detail::sanitizePrefix(prefix)),
185196 m_sender(new UDPSender{host, port, batchsize, sendInterval}),
186- m_gaugePrecision(gaugePrecision) {
187- // Initialize the random generator to be used for sampling
188- detail::rng (seed);
197+ m_gaugePrecision(gaugePrecision), m_frequencyFunc(std::move(frequencyFunc)) {
189198}
190199
191200inline const std::string& StatsdClient::errorMessage () const noexcept {
@@ -260,7 +269,7 @@ inline void StatsdClient::send(const std::string& key,
260269 const bool isFrequencyOne = std::fabs (frequency - 1 .0f ) < epsilon;
261270 const bool isFrequencyZero = std::fabs (frequency) < epsilon;
262271 if (isFrequencyZero ||
263- (!isFrequencyOne && (frequency < std::uniform_real_distribution< float >( 0 . f , 1 . f )( detail::rng () )))) {
272+ (!isFrequencyOne && (frequency < m_frequencyFunc ( )))) {
264273 return ;
265274 }
266275
0 commit comments