Skip to content

Commit 85b0cbd

Browse files
author
Vincent Thiery
committed
Initial commit
0 parents  commit 85b0cbd

File tree

4 files changed

+575
-0
lines changed

4 files changed

+575
-0
lines changed

LICENSE.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
MIT License
2+
3+
Copyright (c) 2017 Vincent Thiery
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6+
7+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8+
9+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

README.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# C++ StatsD Client
2+
3+
A header-only StatsD client implemented in C++.
4+
The client allows:
5+
- batching,
6+
- change of configuration at runtime,
7+
- user-define frequency rate.
8+
9+
## Usage
10+
A simple example of how to use the client:
11+
12+
```
13+
#include "StatsdClient.hpp"
14+
using namespace Statsd;
15+
16+
int main()
17+
{
18+
// Define the client
19+
StatsdClient client{ "127.0.0.1", 5005, "myPrefix_", true };
20+
21+
// Increment "coco"
22+
client.increment("coco");
23+
24+
// Decrement "kiki"
25+
client.decrement("kiki");
26+
27+
// Adjusts "toto" by +3
28+
client.count("toto", 2, 0.1f);
29+
30+
// Record a gauge "titi" to 3
31+
client.gauge("titi", 3);
32+
33+
// Record a timing of 2ms for "myTiming"
34+
client.timing("myTiming", 2, 0.1f);
35+
36+
// Send a metric explicitly
37+
client.send("tutu", 4, "c", 2.0f);
38+
}
39+
```
40+
41+
## License
42+
This extension is under MIT license.

src/StatsdClient.hpp

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
#ifndef STATSD_CLIENT_HPP
2+
#define STATSD_CLIENT_HPP
3+
4+
#include <experimental/optional>
5+
#include <string>
6+
#include "UDPSender.hpp"
7+
8+
namespace Statsd
9+
{
10+
/*!
11+
*
12+
* Statsd client
13+
*
14+
* This is the Statsd client, exposing the classic methods
15+
* and relying on a UDP sender for the actual sending.
16+
*
17+
* The sampling frequency can be input, as well as the
18+
* batching size. The latter is optional and shall not be
19+
* set if batching is not desired.
20+
*
21+
*/
22+
class StatsdClient
23+
{
24+
public:
25+
26+
//!@name Constructor and destructor
27+
//!@{
28+
29+
//! Constructor
30+
StatsdClient(
31+
const std::string& host,
32+
const uint16_t port,
33+
const std::string& prefix,
34+
const std::experimental::optional<uint64_t> batchsize = std::experimental::nullopt) noexcept;
35+
36+
//! Default destructor
37+
~StatsdClient() = default;
38+
39+
//!@}
40+
41+
//!@name Methods
42+
//!@{
43+
44+
//! Sets a configuration { host, port, prefix }
45+
inline void setConfig(const std::string& host, const uint16_t port, const std::string& prefix) noexcept;
46+
47+
//! Returns the error message as an optional std::string
48+
inline std::experimental::optional<std::string> errorMessage() const noexcept;
49+
50+
//! Increments the key, at a given frequency rate
51+
inline void increment(const std::string& key, const float frequency = 1.0f) const noexcept;
52+
53+
//! Increments the key, at a given frequency rate
54+
inline void decrement(const std::string& key, const float frequency = 1.0f) const noexcept;
55+
56+
//! Adjusts the specified key by a given delta, at a given frequency rate
57+
inline void count(const std::string& key, const int delta, const float frequency = 1.0f) const noexcept;
58+
59+
//! Records a gauge for the key, with a given value, at a given frequency rate
60+
inline void gauge(const std::string& key, const unsigned int value, const float frequency = 1.0f) const noexcept;
61+
62+
//! Records a timing for a key, at a given frequency
63+
inline void timing(const std::string& key, const unsigned int ms, const float frequency = 1.0f) const noexcept;
64+
65+
//! Send a value for a key, according to its type, at a given frequency
66+
void send(const std::string& key, const int value, const std::string& type, const float frequency = 1.0f) const noexcept;
67+
68+
//!@}
69+
70+
private:
71+
72+
// @name Private methods
73+
// @{
74+
75+
//! Returns a cleaned key
76+
inline std::string clean(const std::string& key) const noexcept;
77+
78+
// @}
79+
80+
private:
81+
82+
//! The prefix to be used for metrics
83+
std::string m_prefix;
84+
85+
//! The UDP sender to be used for actual sending
86+
mutable UDPSender m_sender;
87+
};
88+
89+
StatsdClient::
90+
StatsdClient(
91+
const std::string& host,
92+
const uint16_t port,
93+
const std::string& prefix,
94+
const std::experimental::optional<uint64_t> batchsize) noexcept
95+
: m_prefix(prefix)
96+
, m_sender(host, port, batchsize)
97+
{
98+
// Initialize the randorm generator to be used for sampling
99+
srandom(time(NULL));
100+
}
101+
102+
void
103+
StatsdClient::
104+
setConfig(const std::string& host, const uint16_t port, const std::string& prefix) noexcept
105+
{
106+
m_prefix = prefix;
107+
m_sender.setConfig(host, port);
108+
}
109+
110+
std::experimental::optional<std::string>
111+
StatsdClient::
112+
errorMessage() const noexcept
113+
{
114+
return m_sender.errorMessage();
115+
}
116+
117+
void
118+
StatsdClient::
119+
decrement(const std::string& key, const float frequency) const noexcept
120+
{
121+
return count(key, -1, frequency);
122+
}
123+
124+
void
125+
StatsdClient::
126+
increment(const std::string& key, const float frequency) const noexcept
127+
{
128+
return count(key, 1, frequency);
129+
}
130+
131+
void
132+
StatsdClient::
133+
count(const std::string& key, const int delta, const float frequency) const noexcept
134+
{
135+
return send(key, delta, "c", frequency);
136+
}
137+
138+
void
139+
StatsdClient::
140+
gauge(const std::string& key, const unsigned int value, const float frequency) const noexcept
141+
{
142+
return send(key, value, "g", frequency);
143+
}
144+
145+
void
146+
StatsdClient::
147+
timing(const std::string& key, const unsigned int ms, const float frequency) const noexcept
148+
{
149+
return send(key, ms, "ms", frequency);
150+
}
151+
152+
void
153+
StatsdClient::
154+
send(const std::string& key, const int value, const std::string& type, const float frequency) const noexcept
155+
{
156+
const auto isFrequencyOne = [](const float frequency) noexcept
157+
{
158+
constexpr float epsilon{ 0.0001f };
159+
return std::fabs(frequency - 1.0f) < epsilon;
160+
};
161+
162+
// Test if one should send or not, according to the frequency rate
163+
if (!isFrequencyOne(frequency))
164+
{
165+
if (frequency < (float)random() / RAND_MAX)
166+
{
167+
return;
168+
}
169+
}
170+
171+
// Clean the key
172+
clean(key);
173+
174+
// Prepare the buffer, with a sampling rate if specified different from 1.0f
175+
char buffer[256];
176+
if (isFrequencyOne(frequency))
177+
{
178+
// Sampling rate is 1.0f, no need to specify it
179+
snprintf(buffer, sizeof(buffer), "%s%s:%zd|%s", m_prefix.c_str(), key.c_str(), static_cast<signed size_t>(value), type.c_str());
180+
}
181+
else
182+
{
183+
// Sampling rate is different from 1.0f, hence specify it
184+
snprintf(buffer, sizeof(buffer), "%s%s:%zd|%s|@%.2f", m_prefix.c_str(), key.c_str(), static_cast<signed size_t>(value), type.c_str(), frequency);
185+
}
186+
187+
// Send the message via the UDP sender
188+
m_sender.send(buffer);
189+
}
190+
191+
std::string
192+
StatsdClient::
193+
clean(const std::string& key) const noexcept
194+
{
195+
std::string cleanKey = key;
196+
size_t pos = key.find_first_of(":|@");
197+
198+
// Add the '_' appropriately to the key
199+
while (pos != std::string::npos)
200+
{
201+
cleanKey[pos] = '_';
202+
pos = cleanKey.find_first_of(":|@");
203+
}
204+
return cleanKey;
205+
}
206+
207+
}
208+
209+
#endif

0 commit comments

Comments
 (0)