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< Acore::Asio::DeadlineTimer_batchTimer
 
std::unique_ptr< Acore::Asio::DeadlineTimer_overallStatusTimer
 
int32 _updateInterval = 0
 
int32 _overallStatusTimerInterval = 0
 
bool _enabled = false
 
bool _overallStatusTimerTriggered = false
 
std::string _hostname
 
std::string _port
 
std::string _databaseName
 
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:157
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
324{
326 return boost::replace_all_copy(value, " ", "\\ ");
327}

Referenced by Initialize(), and SendBatch().

◆ FormatInfluxDBValue() [1/7]

std::string Metric::FormatInfluxDBValue ( bool  value)
staticprivate
293{
294 return value ? "t" : "f";
295}

Referenced by FormatInfluxDBValue().

◆ FormatInfluxDBValue() [2/7]

std::string Metric::FormatInfluxDBValue ( char const *  value)
staticprivate
309{
310 return FormatInfluxDBValue(std::string(value));
311}
static std::string FormatInfluxDBValue(bool value)
Definition: Metric.cpp:292

References FormatInfluxDBValue().

◆ FormatInfluxDBValue() [3/7]

std::string Metric::FormatInfluxDBValue ( double  value)
staticprivate
314{
315 return std::to_string(value);
316}

◆ FormatInfluxDBValue() [4/7]

std::string Metric::FormatInfluxDBValue ( float  value)
staticprivate
319{
320 return FormatInfluxDBValue(double(value));
321}

References FormatInfluxDBValue().

◆ FormatInfluxDBValue() [5/7]

std::string Metric::FormatInfluxDBValue ( std::chrono::nanoseconds  value)
staticprivate
330{
331 return FormatInfluxDBValue(std::chrono::duration_cast<Milliseconds>(value).count());
332}

References FormatInfluxDBValue().

◆ FormatInfluxDBValue() [6/7]

std::string Metric::FormatInfluxDBValue ( std::string const &  value)
staticprivate
304{
305 return '"' + boost::replace_all_copy(value, "\"", "\\\"") + '"';
306}

◆ FormatInfluxDBValue() [7/7]

template<class T >
std::string Metric::FormatInfluxDBValue ( value)
staticprivate
299{
300 return std::to_string(value) + 'i';
301}

◆ 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<Acore::Asio::DeadlineTimer>(ioContext);
46 _overallStatusTimer = std::make_unique<Acore::Asio::DeadlineTimer>(ioContext);
47 _overallStatusLogger = overallStatusLogger;
49}
static std::string FormatInfluxDBTagValue(std::string const &value)
Definition: Metric.cpp:323
std::unique_ptr< Acore::Asio::DeadlineTimer > _overallStatusTimer
Definition: Metric.h:66
std::string _realmName
Definition: Metric.h:75
std::function< void()> _overallStatusLogger
Definition: Metric.h:74
std::unique_ptr< Acore::Asio::DeadlineTimer > _batchTimer
Definition: Metric.h:65
void LoadFromConfigs()
Definition: Metric.cpp:70

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
127{ 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.ConnectionInfo", "");
103 if (connectionInfo.empty())
104 {
105 LOG_ERROR("metric", "'Metric.ConnectionInfo' not specified in configuration file.");
106 return;
107 }
108
109 std::vector<std::string_view> tokens = Acore::Tokenize(connectionInfo, ';', true);
110 if (tokens.size() != 3)
111 {
112 LOG_ERROR("metric", "'Metric.ConnectionInfo' specified with wrong format in configuration file.");
113 return;
114 }
115
116 _hostname.assign(tokens[0]);
117 _port.assign(tokens[1]);
118 _databaseName.assign(tokens[2]);
119 Connect();
120
121 ScheduleSend();
123 }
124}
#define sConfigMgr
Definition: Config.h:74
std::int32_t int32
Definition: Define.h:103
std::int64_t int64
Definition: Define.h:102
std::vector< std::string_view > Tokenize(std::string_view str, char sep, bool keepEmpty)
Definition: Tokenize.cpp:20
void ScheduleSend()
Definition: Metric.cpp:246
std::string _databaseName
Definition: Metric.h:73
void ScheduleOverallStatusLog()
Definition: Metric.cpp:279
int32 _overallStatusTimerInterval
Definition: Metric.h:68
int32 _updateInterval
Definition: Metric.h:67
bool Connect()
Definition: Metric.cpp:51
std::unordered_map< std::string, int64 > _thresholds
Definition: Metric.h:76

References _databaseName, _enabled, _hostname, _overallStatusTimerInterval, _port, _thresholds, _updateInterval, 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 
)
148{
149 using namespace std::chrono;
150
151 MetricData* data = new MetricData;
152 data->Category = category;
153 data->Timestamp = system_clock::now();
154 data->Type = METRIC_DATA_EVENT;
155 data->Title = title;
156 data->Text = description;
157
158 _queuedData.Enqueue(data);
159}
@ METRIC_DATA_EVENT
Definition: Metric.h:39
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
MPSCQueue< MetricData > _queuedData
Definition: Metric.h:64

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
111 {
112 using namespace std::chrono;
113
114 MetricData* data = new MetricData;
115 data->Category = category;
116 data->Timestamp = system_clock::now();
117 data->Type = METRIC_DATA_VALUE;
118 data->Value = FormatInfluxDBValue(value);
119 data->Tags = std::move(tags);
120
121 _queuedData.Enqueue(data);
122 }
@ 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
280{
281 if (_enabled)
282 {
283 _overallStatusTimer->expires_from_now(boost::posix_time::seconds(_overallStatusTimerInterval));
284 _overallStatusTimer->async_wait([this](const boost::system::error_code&)
285 {
288 });
289 }
290}
bool _overallStatusTimerTriggered
Definition: Metric.h:70

References _enabled, _overallStatusTimer, _overallStatusTimerInterval, _overallStatusTimerTriggered, and ScheduleOverallStatusLog().

Referenced by LoadFromConfigs(), and ScheduleOverallStatusLog().

◆ ScheduleSend()

void Metric::ScheduleSend ( )
private
247{
248 if (_enabled)
249 {
250 _batchTimer->expires_from_now(boost::posix_time::seconds(_updateInterval));
251 _batchTimer->async_wait(std::bind(&Metric::SendBatch, this));
252 }
253 else
254 {
255 static_cast<boost::asio::ip::tcp::iostream&>(GetDataStream()).close();
256 MetricData* data;
257
258 // Clear the queue
259 while (_queuedData.Dequeue(data))
260 {
261 delete data;
262 }
263 }
264}
void SendBatch()
Definition: Metric.cpp:161

References _batchTimer, _enabled, _queuedData, _updateInterval, GetDataStream(), and SendBatch().

Referenced by LoadFromConfigs(), and SendBatch().

◆ SendBatch()

void Metric::SendBatch ( )
private
162{
163 using namespace std::chrono;
164
165 std::stringstream batchedData;
166 MetricData* data;
167 bool firstLoop = true;
168
169 while (_queuedData.Dequeue(data))
170 {
171 if (!firstLoop)
172 batchedData << "\n";
173
174 batchedData << data->Category;
175 if (!_realmName.empty())
176 batchedData << ",realm=" << _realmName;
177
178 for (MetricTag const& tag : data->Tags)
179 batchedData << "," << tag.first << "=" << FormatInfluxDBTagValue(tag.second);
180
181 batchedData << " ";
182
183 switch (data->Type)
184 {
186 batchedData << "value=" << data->Value;
187 break;
189 batchedData << "title=\"" << data->Title << "\",text=\"" << data->Text << "\"";
190 break;
191 }
192
193 batchedData << " " << std::to_string(duration_cast<nanoseconds>(data->Timestamp.time_since_epoch()).count());
194
195 firstLoop = false;
196 delete data;
197 }
198
199 // Check if there's any data to send
200 if (batchedData.tellp() == std::streampos(0))
201 {
202 ScheduleSend();
203 return;
204 }
205
206 if (!GetDataStream().good() && !Connect())
207 return;
208
209 GetDataStream() << "POST " << "/write?db=" << _databaseName << " HTTP/1.1\r\n";
210 GetDataStream() << "Host: " << _hostname << ":" << _port << "\r\n";
211 GetDataStream() << "Accept: */*\r\n";
212 GetDataStream() << "Content-Type: application/octet-stream\r\n";
213 GetDataStream() << "Content-Transfer-Encoding: binary\r\n";
214
215 GetDataStream() << "Content-Length: " << std::to_string(batchedData.tellp()) << "\r\n\r\n";
216 GetDataStream() << batchedData.rdbuf();
217
218 std::string http_version;
219 GetDataStream() >> http_version;
220 unsigned int status_code = 0;
221 GetDataStream() >> status_code;
222
223 if (status_code != 204)
224 {
225 LOG_ERROR("metric", "Error sending data, returned HTTP code: {}", status_code);
226 }
227
228 // Read and ignore the status description
229 std::string status_description;
230 std::getline(GetDataStream(), status_description);
231
232 // Read headers
233 std::string header;
234
235 while (std::getline(GetDataStream(), header) && header != "\r")
236 {
237 if (header == "Connection: close\r")
238 {
239 static_cast<boost::asio::ip::tcp::iostream&>(GetDataStream()).close();
240 }
241 }
242
243 ScheduleSend();
244}
std::pair< std::string, std::string > MetricTag
Definition: Metric.h:42

References _databaseName, _hostname, _port, _queuedData, _realmName, 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
136{
137 auto threshold = _thresholds.find(category);
138
139 if (threshold == _thresholds.end())
140 {
141 return false;
142 }
143
144 return value >= threshold->second;
145}

References _thresholds.

◆ Unload()

void Metric::Unload ( )
267{
268 // Send what's queued only if IoContext is stopped (so only on shutdown)
269 if (_enabled && Acore::Asio::get_io_context(*_batchTimer).stopped())
270 {
271 _enabled = false;
272 SendBatch();
273 }
274
275 _batchTimer->cancel();
276 _overallStatusTimer->cancel();
277}
decltype(auto) get_io_context(T &&ioObject)
Definition: IoContext.h:55

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

◆ Update()

void Metric::Update ( )

Member Data Documentation

◆ _batchTimer

std::unique_ptr<Acore::Asio::DeadlineTimer> Metric::_batchTimer
private

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

◆ _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().

◆ _overallStatusLogger

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

Referenced by Initialize(), and Update().

◆ _overallStatusTimer

std::unique_ptr<Acore::Asio::DeadlineTimer> 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().

◆ _updateInterval

int32 Metric::_updateInterval = 0
private

Referenced by LoadFromConfigs(), and ScheduleSend().