AzerothCore 3.3.5a
OpenSource WoW Emulator
Loading...
Searching...
No Matches
Metric Class Reference

#include "Metric.h"

Public Member Functions

 Metric ()
 
 ~Metric ()
 
void Initialize (std::string const &realmName, Acore::Asio::IoContext &ioContext, std::function< void()> overallStatusLogger)
 
void LoadFromConfigs ()
 
void Update ()
 
bool ShouldLog (std::string const &category, int64 value) const
 
template<class T >
void LogValue (std::string const &category, T value, std::vector< MetricTag > tags)
 
void LogEvent (std::string const &category, std::string const &title, std::string const &description)
 
void Unload ()
 
bool IsEnabled () const
 

Static Public Member Functions

static Metricinstance ()
 

Private Member Functions

std::iostream & GetDataStream ()
 
bool Connect ()
 
void SendBatch ()
 
void ScheduleSend ()
 
void ScheduleOverallStatusLog ()
 

Static Private Member Functions

static std::string FormatInfluxDBValue (bool value)
 
template<class T >
static std::string FormatInfluxDBValue (T value)
 
static std::string FormatInfluxDBValue (std::string const &value)
 
static std::string FormatInfluxDBValue (char const *value)
 
static std::string FormatInfluxDBValue (double value)
 
static std::string FormatInfluxDBValue (float value)
 
static std::string FormatInfluxDBValue (std::chrono::nanoseconds value)
 
static std::string FormatInfluxDBTagValue (std::string const &value)
 

Private Attributes

std::unique_ptr< std::iostream > _dataStream
 
MPSCQueue< MetricData_queuedData
 
std::unique_ptr< boost::asio::steady_timer > _batchTimer
 
std::unique_ptr< boost::asio::steady_timer > _overallStatusTimer
 
int32 _updateInterval = 0
 
int32 _overallStatusTimerInterval = 0
 
bool _enabled = false
 
bool _overallStatusTimerTriggered = false
 
std::string _hostname
 
std::string _port
 
std::string _databaseName
 
bool _useV2 = false
 
std::string _org
 
std::string _bucket
 
std::string _token
 
std::function< void()> _overallStatusLogger
 
std::string _realmName
 
std::unordered_map< std::string, int64_thresholds
 

Detailed Description

Constructor & Destructor Documentation

◆ Metric()

Metric::Metric ( )
Todo:
: should format TagKey and FieldKey too in the same way as TagValue
28{
29}

◆ ~Metric()

Metric::~Metric ( )
32{
33}

Member Function Documentation

◆ Connect()

bool Metric::Connect ( )
private
52{
53 auto& stream = static_cast<boost::asio::ip::tcp::iostream&>(GetDataStream());
54 stream.connect(_hostname, _port);
55
56 auto error = stream.error();
57 if (error)
58 {
59 LOG_ERROR("metric", "Error connecting to '{}:{}', disabling Metric. Error message: {}",
60 _hostname, _port, error.message());
61
62 _enabled = false;
63 return false;
64 }
65
66 stream.clear();
67 return true;
68}
#define LOG_ERROR(filterType__,...)
Definition Log.h:158
std::string _hostname
Definition Metric.h:71
std::string _port
Definition Metric.h:72
std::iostream & GetDataStream()
Definition Metric.h:62
bool _enabled
Definition Metric.h:69

References _enabled, _hostname, _port, GetDataStream(), and LOG_ERROR.

Referenced by LoadFromConfigs(), and SendBatch().

◆ FormatInfluxDBTagValue()

std::string Metric::FormatInfluxDBTagValue ( std::string const &  value)
staticprivate
Todo:
: should handle '=' and ',' characters too
358{
360 return boost::replace_all_copy(value, " ", "\\ ");
361}

Referenced by Initialize(), and SendBatch().

◆ FormatInfluxDBValue() [1/7]

std::string Metric::FormatInfluxDBValue ( bool  value)
staticprivate
327{
328 return value ? "t" : "f";
329}

Referenced by FormatInfluxDBValue(), FormatInfluxDBValue(), and FormatInfluxDBValue().

◆ FormatInfluxDBValue() [2/7]

std::string Metric::FormatInfluxDBValue ( char const *  value)
staticprivate
343{
344 return FormatInfluxDBValue(std::string(value));
345}
static std::string FormatInfluxDBValue(bool value)
Definition Metric.cpp:326

References FormatInfluxDBValue().

◆ FormatInfluxDBValue() [3/7]

std::string Metric::FormatInfluxDBValue ( double  value)
staticprivate
348{
349 return std::to_string(value);
350}

◆ FormatInfluxDBValue() [4/7]

std::string Metric::FormatInfluxDBValue ( float  value)
staticprivate
353{
354 return FormatInfluxDBValue(double(value));
355}

References FormatInfluxDBValue().

◆ FormatInfluxDBValue() [5/7]

std::string Metric::FormatInfluxDBValue ( std::chrono::nanoseconds  value)
staticprivate
364{
365 return FormatInfluxDBValue(std::chrono::duration_cast<Milliseconds>(value).count());
366}

References FormatInfluxDBValue().

◆ FormatInfluxDBValue() [6/7]

std::string Metric::FormatInfluxDBValue ( std::string const &  value)
staticprivate
338{
339 return '"' + boost::replace_all_copy(value, "\"", "\\\"") + '"';
340}

◆ FormatInfluxDBValue() [7/7]

template<class T >
std::string Metric::FormatInfluxDBValue ( value)
staticprivate
333{
334 return std::to_string(value) + 'i';
335}

◆ GetDataStream()

std::iostream & Metric::GetDataStream ( )
inlineprivate
62{ return *_dataStream; }
std::unique_ptr< std::iostream > _dataStream
Definition Metric.h:63

Referenced by Connect(), ScheduleSend(), and SendBatch().

◆ Initialize()

void Metric::Initialize ( std::string const &  realmName,
Acore::Asio::IoContext ioContext,
std::function< void()>  overallStatusLogger 
)
42{
43 _dataStream = std::make_unique<boost::asio::ip::tcp::iostream>();
45 _batchTimer = std::make_unique<boost::asio::steady_timer>(ioContext);
46 _overallStatusTimer = std::make_unique<boost::asio::steady_timer>(ioContext);
47 _overallStatusLogger = overallStatusLogger;
49}
static std::string FormatInfluxDBTagValue(std::string const &value)
Definition Metric.cpp:357
std::string _realmName
Definition Metric.h:79
std::function< void()> _overallStatusLogger
Definition Metric.h:78
std::unique_ptr< boost::asio::steady_timer > _overallStatusTimer
Definition Metric.h:66
void LoadFromConfigs()
Definition Metric.cpp:70
std::unique_ptr< boost::asio::steady_timer > _batchTimer
Definition Metric.h:65

References _batchTimer, _dataStream, _overallStatusLogger, _overallStatusTimer, _realmName, FormatInfluxDBTagValue(), and LoadFromConfigs().

◆ instance()

Metric * Metric::instance ( )
static
36{
37 static Metric instance;
38 return &instance;
39}
Definition Metric.h:60
static Metric * instance()
Definition Metric.cpp:35

References instance().

Referenced by instance().

◆ IsEnabled()

bool Metric::IsEnabled ( ) const
inline
131{ return _enabled; }

◆ LoadFromConfigs()

void Metric::LoadFromConfigs ( )
71{
72 bool previousValue = _enabled;
73 _enabled = sConfigMgr->GetOption<bool>("Metric.Enable", false);
74 _updateInterval = sConfigMgr->GetOption<int32>("Metric.Interval", 1);
75
76 if (_updateInterval < 1)
77 {
78 LOG_ERROR("metric", "'Metric.Interval' config set to {}, overriding to 1.", _updateInterval);
80 }
81
82 _overallStatusTimerInterval = sConfigMgr->GetOption<int32>("Metric.OverallStatusInterval", 1);
84 {
85 LOG_ERROR("metric", "'Metric.OverallStatusInterval' config set to {}, overriding to 1.", _overallStatusTimerInterval);
87 }
88
89 _thresholds.clear();
90 std::vector<std::string> thresholdSettings = sConfigMgr->GetKeysByString("Metric.Threshold.");
91 for (std::string const& thresholdSetting : thresholdSettings)
92 {
93 int64 thresholdValue = sConfigMgr->GetOption<int64>(thresholdSetting, 0);
94 std::string thresholdName = thresholdSetting.substr(strlen("Metric.Threshold."));
95 _thresholds[thresholdName] = thresholdValue;
96 }
97
98 // Schedule a send at this point only if the config changed from Disabled to Enabled.
99 // Cancel any scheduled operation if the config changed from Enabled to Disabled.
100 if (_enabled && !previousValue)
101 {
102 std::string connectionInfo = sConfigMgr->GetOption<std::string>("Metric.InfluxDB.Connection", "");
103 if (connectionInfo.empty())
104 {
105 LOG_ERROR("metric", "Metric.InfluxDB.Connection not specified in configuration file.");
106 return;
107 }
108
109 std::vector<std::string_view> tokens = Acore::Tokenize(connectionInfo, ';', true);
110 _useV2 = sConfigMgr->GetOption<bool>("Metric.InfluxDB.v2", false);
111 if (_useV2)
112 {
113 if (tokens.size() != 2)
114 {
115 LOG_ERROR("metric", "Metric.InfluxDB.Connection specified with wrong format in configuration file. (hostname;port)");
116 return;
117 }
118
119 _hostname.assign(tokens[0]);
120 _port.assign(tokens[1]);
121 _org = sConfigMgr->GetOption<std::string>("Metric.InfluxDB.Org", "");
122 _bucket = sConfigMgr->GetOption<std::string>("Metric.InfluxDB.Bucket", "");
123 _token = sConfigMgr->GetOption<std::string>("Metric.InfluxDB.Token", "");
124
125 if (_org.empty() || _bucket.empty() || _token.empty())
126 {
127 LOG_ERROR("metric", "InfluxDB v2 parameters missing: org, bucket, or token.");
128 return;
129 }
130 }
131 else
132 {
133 if (tokens.size() != 3)
134 {
135 LOG_ERROR("metric", "Metric.InfluxDB.Connection specified with wrong format in configuration file. (hostname;port;database)");
136 return;
137 }
138
139 _hostname.assign(tokens[0]);
140 _port.assign(tokens[1]);
141 _databaseName.assign(tokens[2]);
142 }
143
144 Connect();
145 ScheduleSend();
147 }
148}
#define sConfigMgr
Definition Config.h:74
std::int32_t int32
Definition Define.h:103
std::int64_t int64
Definition Define.h:102
void ScheduleSend()
Definition Metric.cpp:280
std::string _databaseName
Definition Metric.h:73
void ScheduleOverallStatusLog()
Definition Metric.cpp:313
int32 _overallStatusTimerInterval
Definition Metric.h:68
int32 _updateInterval
Definition Metric.h:67
std::string _bucket
Definition Metric.h:76
bool Connect()
Definition Metric.cpp:51
std::string _org
Definition Metric.h:75
std::string _token
Definition Metric.h:77
bool _useV2
Definition Metric.h:74
std::unordered_map< std::string, int64 > _thresholds
Definition Metric.h:80
std::vector< std::string_view > Tokenize(std::string_view str, char sep, bool keepEmpty)
Definition Tokenize.cpp:20

References _bucket, _databaseName, _enabled, _hostname, _org, _overallStatusTimerInterval, _port, _thresholds, _token, _updateInterval, _useV2, Connect(), LOG_ERROR, ScheduleOverallStatusLog(), ScheduleSend(), sConfigMgr, and Acore::Tokenize().

Referenced by Initialize().

◆ LogEvent()

void Metric::LogEvent ( std::string const &  category,
std::string const &  title,
std::string const &  description 
)
172{
173 using namespace std::chrono;
174
175 MetricData* data = new MetricData;
176 data->Category = category;
177 data->Timestamp = system_clock::now();
178 data->Type = METRIC_DATA_EVENT;
179 data->Title = title;
180 data->Text = description;
181
182 _queuedData.Enqueue(data);
183}
@ METRIC_DATA_EVENT
Definition Metric.h:39
MPSCQueue< MetricData > _queuedData
Definition Metric.h:64
Definition Metric.h:45
std::string Category
Definition Metric.h:46
SystemTimePoint Timestamp
Definition Metric.h:47
std::string Title
Definition Metric.h:55
std::string Text
Definition Metric.h:56
MetricDataType Type
Definition Metric.h:48

References _queuedData, MetricData::Category, METRIC_DATA_EVENT, MetricData::Text, MetricData::Timestamp, MetricData::Title, and MetricData::Type.

◆ LogValue()

template<class T >
void Metric::LogValue ( std::string const &  category,
value,
std::vector< MetricTag tags 
)
inline
115 {
116 using namespace std::chrono;
117
118 MetricData* data = new MetricData;
119 data->Category = category;
120 data->Timestamp = system_clock::now();
121 data->Type = METRIC_DATA_VALUE;
122 data->Value = FormatInfluxDBValue(value);
123 data->Tags = std::move(tags);
124
125 _queuedData.Enqueue(data);
126 }
@ METRIC_DATA_VALUE
Definition Metric.h:38
std::string Value
Definition Metric.h:52
std::vector< MetricTag > Tags
Definition Metric.h:49

References MetricData::Category, METRIC_DATA_VALUE, MetricData::Tags, MetricData::Timestamp, MetricData::Type, and MetricData::Value.

◆ ScheduleOverallStatusLog()

void Metric::ScheduleOverallStatusLog ( )
private
314{
315 if (_enabled)
316 {
318 _overallStatusTimer->async_wait([this](const boost::system::error_code&)
319 {
322 });
323 }
324}
bool _overallStatusTimerTriggered
Definition Metric.h:70
auto GetExpirationTime(int32 seconds)
Definition SteadyTimer.h:25

References _enabled, _overallStatusTimer, _overallStatusTimerInterval, _overallStatusTimerTriggered, Acore::Asio::SteadyTimer::GetExpirationTime(), and ScheduleOverallStatusLog().

Referenced by LoadFromConfigs(), and ScheduleOverallStatusLog().

◆ ScheduleSend()

void Metric::ScheduleSend ( )
private
281{
282 if (_enabled)
283 {
285 _batchTimer->async_wait(std::bind(&Metric::SendBatch, this));
286 }
287 else
288 {
289 static_cast<boost::asio::ip::tcp::iostream&>(GetDataStream()).close();
290 MetricData* data;
291
292 // Clear the queue
293 while (_queuedData.Dequeue(data))
294 {
295 delete data;
296 }
297 }
298}
void SendBatch()
Definition Metric.cpp:185

References _batchTimer, _enabled, _queuedData, _updateInterval, GetDataStream(), Acore::Asio::SteadyTimer::GetExpirationTime(), and SendBatch().

Referenced by LoadFromConfigs(), and SendBatch().

◆ SendBatch()

void Metric::SendBatch ( )
private
186{
187 using namespace std::chrono;
188
189 std::stringstream batchedData;
190 MetricData* data;
191 bool firstLoop = true;
192
193 while (_queuedData.Dequeue(data))
194 {
195 if (!firstLoop)
196 batchedData << "\n";
197
198 batchedData << data->Category;
199 if (!_realmName.empty())
200 batchedData << ",realm=" << _realmName;
201
202 for (MetricTag const& tag : data->Tags)
203 batchedData << "," << tag.first << "=" << FormatInfluxDBTagValue(tag.second);
204
205 batchedData << " ";
206
207 switch (data->Type)
208 {
210 batchedData << "value=" << data->Value;
211 break;
213 batchedData << "title=\"" << data->Title << "\",text=\"" << data->Text << "\"";
214 break;
215 }
216
217 batchedData << " " << std::to_string(duration_cast<nanoseconds>(data->Timestamp.time_since_epoch()).count());
218
219 firstLoop = false;
220 delete data;
221 }
222
223 // Check if there's any data to send
224 if (batchedData.tellp() == std::streampos(0))
225 {
226 ScheduleSend();
227 return;
228 }
229
230 if (!GetDataStream().good() && !Connect())
231 return;
232
233 if (_useV2)
234 {
235 GetDataStream() << "POST " << "/api/v2/write?bucket=" << _bucket
236 << "&org=" << _org << "&precision=ns HTTP/1.1\r\n";
237 GetDataStream() << "Host: " << _hostname << ":" << _port << "\r\n";
238 GetDataStream() << "Authorization: Token " << _token << "\r\n";
239 }
240 else
241 {
242 GetDataStream() << "POST " << "/write?db=" << _databaseName << " HTTP/1.1\r\n";
243 GetDataStream() << "Host: " << _hostname << ":" << _port << "\r\n";
244 }
245 GetDataStream() << "Accept: */*\r\n";
246 GetDataStream() << "Content-Type: application/octet-stream\r\n";
247 GetDataStream() << "Content-Transfer-Encoding: binary\r\n";
248
249 GetDataStream() << "Content-Length: " << std::to_string(batchedData.tellp()) << "\r\n\r\n";
250 GetDataStream() << batchedData.rdbuf();
251
252 std::string http_version;
253 GetDataStream() >> http_version;
254 unsigned int status_code = 0;
255 GetDataStream() >> status_code;
256
257 if (status_code != 204)
258 {
259 LOG_ERROR("metric", "Error sending data, returned HTTP code: {}", status_code);
260 }
261
262 // Read and ignore the status description
263 std::string status_description;
264 std::getline(GetDataStream(), status_description);
265
266 // Read headers
267 std::string header;
268
269 while (std::getline(GetDataStream(), header) && header != "\r")
270 {
271 if (header == "Connection: close\r")
272 {
273 static_cast<boost::asio::ip::tcp::iostream&>(GetDataStream()).close();
274 }
275 }
276
277 ScheduleSend();
278}
std::pair< std::string, std::string > MetricTag
Definition Metric.h:42

References _bucket, _databaseName, _hostname, _org, _port, _queuedData, _realmName, _token, _useV2, MetricData::Category, Connect(), FormatInfluxDBTagValue(), GetDataStream(), LOG_ERROR, METRIC_DATA_EVENT, METRIC_DATA_VALUE, ScheduleSend(), MetricData::Tags, MetricData::Text, MetricData::Timestamp, MetricData::Title, MetricData::Type, and MetricData::Value.

Referenced by ScheduleSend(), and Unload().

◆ ShouldLog()

bool Metric::ShouldLog ( std::string const &  category,
int64  value 
) const
160{
161 auto threshold = _thresholds.find(category);
162
163 if (threshold == _thresholds.end())
164 {
165 return false;
166 }
167
168 return value >= threshold->second;
169}

References _thresholds.

◆ Unload()

void Metric::Unload ( )
301{
302 // Send what's queued only if IoContext is stopped (so only on shutdown)
303 if (_enabled && Acore::Asio::get_io_context(*_batchTimer).stopped())
304 {
305 _enabled = false;
306 SendBatch();
307 }
308
309 _batchTimer->cancel();
310 _overallStatusTimer->cancel();
311}
boost::asio::io_context & get_io_context(T &&ioObject)
Definition IoContext.h:54

References _batchTimer, _enabled, _overallStatusTimer, Acore::Asio::get_io_context(), and SendBatch().

◆ Update()

void Metric::Update ( )

Member Data Documentation

◆ _batchTimer

std::unique_ptr<boost::asio::steady_timer> Metric::_batchTimer
private

Referenced by Initialize(), ScheduleSend(), and Unload().

◆ _bucket

std::string Metric::_bucket
private

Referenced by LoadFromConfigs(), and SendBatch().

◆ _databaseName

std::string Metric::_databaseName
private

Referenced by LoadFromConfigs(), and SendBatch().

◆ _dataStream

std::unique_ptr<std::iostream> Metric::_dataStream
private

Referenced by Initialize().

◆ _enabled

bool Metric::_enabled = false
private

◆ _hostname

std::string Metric::_hostname
private

Referenced by Connect(), LoadFromConfigs(), and SendBatch().

◆ _org

std::string Metric::_org
private

Referenced by LoadFromConfigs(), and SendBatch().

◆ _overallStatusLogger

std::function<void()> Metric::_overallStatusLogger
private

Referenced by Initialize(), and Update().

◆ _overallStatusTimer

std::unique_ptr<boost::asio::steady_timer> Metric::_overallStatusTimer
private

◆ _overallStatusTimerInterval

int32 Metric::_overallStatusTimerInterval = 0
private

◆ _overallStatusTimerTriggered

bool Metric::_overallStatusTimerTriggered = false
private

Referenced by ScheduleOverallStatusLog(), and Update().

◆ _port

std::string Metric::_port
private

Referenced by Connect(), LoadFromConfigs(), and SendBatch().

◆ _queuedData

MPSCQueue<MetricData> Metric::_queuedData
private

Referenced by LogEvent(), ScheduleSend(), and SendBatch().

◆ _realmName

std::string Metric::_realmName
private

Referenced by Initialize(), and SendBatch().

◆ _thresholds

std::unordered_map<std::string, int64> Metric::_thresholds
private

Referenced by LoadFromConfigs(), and ShouldLog().

◆ _token

std::string Metric::_token
private

Referenced by LoadFromConfigs(), and SendBatch().

◆ _updateInterval

int32 Metric::_updateInterval = 0
private

Referenced by LoadFromConfigs(), and ScheduleSend().

◆ _useV2

bool Metric::_useV2 = false
private

Referenced by LoadFromConfigs(), and SendBatch().


The documentation for this class was generated from the following files: