Core/Metric: Reduce number of string copies in metric and eliminate them entirely when metric is disabled in config
Signed-off-by: luis <[email protected]>
This commit is contained in:
@@ -365,8 +365,7 @@ namespace VMAP
|
||||
}
|
||||
else
|
||||
iLoadedTiles[packTileID(tileX, tileY)] = false;
|
||||
TC_METRIC_EVENT("map_events", "LoadMapTile",
|
||||
"Map: " + std::to_string(iMapID) + " TileX: " + std::to_string(tileX) + " TileY: " + std::to_string(tileY));
|
||||
TC_METRIC_EVENT("map_events", "LoadMapTile", Trinity::StringFormat("Map: {} TileX: {} TileY: {}", iMapID, tileX, tileY));
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -432,8 +431,7 @@ namespace VMAP
|
||||
}
|
||||
}
|
||||
iLoadedTiles.erase(tile);
|
||||
TC_METRIC_EVENT("map_events", "UnloadMapTile",
|
||||
"Map: " + std::to_string(iMapID) + " TileX: " + std::to_string(tileX) + " TileY: " + std::to_string(tileY));
|
||||
TC_METRIC_EVENT("map_events", "UnloadMapTile", Trinity::StringFormat("Map: {} TileX: {} TileY: {}", iMapID, tileX, tileY));
|
||||
}
|
||||
|
||||
std::span<ModelInstance const> StaticMapTree::getModelInstances() const
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
#include <span>
|
||||
|
||||
void Metric::Initialize(std::string const& realmName, Trinity::Asio::IoContext& ioContext, std::function<void()> overallStatusLogger)
|
||||
{
|
||||
@@ -29,22 +30,21 @@ void Metric::Initialize(std::string const& realmName, Trinity::Asio::IoContext&
|
||||
_realmName = FormatInfluxDBTagValue(realmName);
|
||||
_batchTimer = std::make_unique<Trinity::Asio::DeadlineTimer>(ioContext);
|
||||
_overallStatusTimer = std::make_unique<Trinity::Asio::DeadlineTimer>(ioContext);
|
||||
_overallStatusLogger = overallStatusLogger;
|
||||
_overallStatusLogger = std::move(overallStatusLogger);
|
||||
LoadFromConfigs();
|
||||
}
|
||||
|
||||
bool Metric::Connect()
|
||||
{
|
||||
auto& stream = static_cast<boost::asio::ip::tcp::iostream&>(GetDataStream());
|
||||
stream.connect(_hostname, _port);
|
||||
if (boost::system::error_code const& error = stream.error())
|
||||
GetDataStream().connect(_hostname, _port);
|
||||
if (boost::system::error_code const& error = GetDataStream().error())
|
||||
{
|
||||
TC_LOG_ERROR("metric", "Error connecting to '{}:{}', disabling Metric. Error message : {}",
|
||||
_hostname, _port, error.message());
|
||||
_enabled = false;
|
||||
return false;
|
||||
}
|
||||
stream.clear();
|
||||
GetDataStream().clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -68,11 +68,11 @@ void Metric::LoadFromConfigs()
|
||||
|
||||
_thresholds.clear();
|
||||
std::vector<std::string> thresholdSettings = sConfigMgr->GetKeysByString("Metric.Threshold.");
|
||||
for (std::string const& thresholdSetting : thresholdSettings)
|
||||
for (std::string& thresholdSetting : thresholdSettings)
|
||||
{
|
||||
int thresholdValue = sConfigMgr->GetIntDefault(thresholdSetting, 0);
|
||||
int64 thresholdValue = sConfigMgr->GetIntDefault(thresholdSetting, 0);
|
||||
std::string thresholdName = thresholdSetting.substr(strlen("Metric.Threshold."));
|
||||
_thresholds[thresholdName] = thresholdValue;
|
||||
_thresholds.emplace(std::move(thresholdName), thresholdValue);
|
||||
}
|
||||
|
||||
// Schedule a send at this point only if the config changed from Disabled to Enabled.
|
||||
@@ -112,7 +112,7 @@ void Metric::Update()
|
||||
}
|
||||
}
|
||||
|
||||
bool Metric::ShouldLog(std::string const& category, int64 value) const
|
||||
bool Metric::ShouldLog(std::string_view category, int64 value) const
|
||||
{
|
||||
auto threshold = _thresholds.find(category);
|
||||
if (threshold == _thresholds.end())
|
||||
@@ -120,16 +120,16 @@ bool Metric::ShouldLog(std::string const& category, int64 value) const
|
||||
return value >= threshold->second;
|
||||
}
|
||||
|
||||
void Metric::LogEvent(std::string category, std::string title, std::string description)
|
||||
void Metric::LogEvent(std::string_view category, std::string_view title, std::string description)
|
||||
{
|
||||
using namespace std::chrono;
|
||||
|
||||
MetricData* data = new MetricData;
|
||||
data->Category = std::move(category);
|
||||
data->Timestamp = system_clock::now();
|
||||
data->Type = METRIC_DATA_EVENT;
|
||||
data->Title = std::move(title);
|
||||
data->ValueOrEventText = std::move(description);
|
||||
MetricData* data = new MetricData
|
||||
{
|
||||
.Category = std::string(category),
|
||||
.Timestamp = std::chrono::system_clock::now(),
|
||||
.Type = METRIC_DATA_EVENT,
|
||||
.Title = std::string(title),
|
||||
.ValueOrEventText = std::move(description)
|
||||
};
|
||||
|
||||
_queuedData.Enqueue(data);
|
||||
}
|
||||
@@ -150,14 +150,21 @@ void Metric::SendBatch()
|
||||
if (!_realmName.empty())
|
||||
batchedData << ",realm=" << _realmName;
|
||||
|
||||
if (data->Tags)
|
||||
auto tags = [](MetricTags const& tags) -> std::span<MetricTag const>
|
||||
{
|
||||
auto begin = std::visit([](auto&& value) { return value.data(); }, *data->Tags);
|
||||
auto end = std::visit([](auto&& value) { return value.data() + value.size(); }, *data->Tags);
|
||||
for (auto itr = begin; itr != end; ++itr)
|
||||
if (!itr->first.empty())
|
||||
batchedData << "," << itr->first << "=" << FormatInfluxDBTagValue(itr->second);
|
||||
}
|
||||
switch (tags.index())
|
||||
{
|
||||
case 1: return std::get<1>(tags);
|
||||
case 2: return std::get<2>(tags);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return {};
|
||||
}(data->Tags);
|
||||
|
||||
for (auto const& [tagName, tagValue] : tags)
|
||||
if (!tagName.empty())
|
||||
batchedData << "," << tagName << "=" << FormatInfluxDBTagValue(tagValue);
|
||||
|
||||
batchedData << " ";
|
||||
|
||||
@@ -173,14 +180,15 @@ void Metric::SendBatch()
|
||||
|
||||
batchedData << " ";
|
||||
|
||||
batchedData << std::to_string(duration_cast<nanoseconds>(data->Timestamp.time_since_epoch()).count());
|
||||
batchedData << duration_cast<nanoseconds>(data->Timestamp.time_since_epoch()).count();
|
||||
|
||||
firstLoop = false;
|
||||
delete data;
|
||||
}
|
||||
|
||||
// Check if there's any data to send
|
||||
if (batchedData.tellp() == std::streampos(0))
|
||||
std::streamoff batchedDataSize = batchedData.tellp();
|
||||
if (batchedDataSize <= 0)
|
||||
{
|
||||
ScheduleSend();
|
||||
return;
|
||||
@@ -195,7 +203,7 @@ void Metric::SendBatch()
|
||||
GetDataStream() << "Content-Type: application/octet-stream\r\n";
|
||||
GetDataStream() << "Content-Transfer-Encoding: binary\r\n";
|
||||
|
||||
GetDataStream() << "Content-Length: " << std::to_string(batchedData.tellp()) << "\r\n\r\n";
|
||||
GetDataStream() << "Content-Length: " << batchedDataSize << "\r\n\r\n";
|
||||
GetDataStream() << batchedData.rdbuf();
|
||||
|
||||
std::string http_version;
|
||||
@@ -214,7 +222,7 @@ void Metric::SendBatch()
|
||||
std::string header;
|
||||
while (std::getline(GetDataStream(), header) && header != "\r")
|
||||
if (header == "Connection: close\r")
|
||||
static_cast<boost::asio::ip::tcp::iostream&>(GetDataStream()).close();
|
||||
GetDataStream().close();
|
||||
|
||||
ScheduleSend();
|
||||
}
|
||||
@@ -228,7 +236,7 @@ void Metric::ScheduleSend()
|
||||
}
|
||||
else
|
||||
{
|
||||
static_cast<boost::asio::ip::tcp::iostream&>(GetDataStream()).close();
|
||||
GetDataStream().close();
|
||||
MetricData* data;
|
||||
// Clear the queue
|
||||
while (_queuedData.Dequeue(data))
|
||||
@@ -267,7 +275,7 @@ std::string Metric::FormatInfluxDBValue(bool value)
|
||||
return std::string(1, value ? 't' : 'f');
|
||||
}
|
||||
|
||||
template<class T>
|
||||
template<class T> requires std::integral<T> && (!std::same_as<T, bool>)
|
||||
std::string Metric::FormatInfluxDBValue(T value)
|
||||
{
|
||||
std::string result = std::to_string(value);
|
||||
|
||||
+92
-97
@@ -15,30 +15,21 @@
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef METRIC_H__
|
||||
#define METRIC_H__
|
||||
#ifndef TRINITYCORE_METRIC_H
|
||||
#define TRINITYCORE_METRIC_H
|
||||
|
||||
#include "AsioHacksFwd.h"
|
||||
#include "Define.h"
|
||||
#include "Duration.h"
|
||||
#include "Hash.h"
|
||||
#include "MPSCQueue.h"
|
||||
#include "Optional.h"
|
||||
#include <functional>
|
||||
#include <iosfwd>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
|
||||
namespace Trinity
|
||||
{
|
||||
namespace Asio
|
||||
{
|
||||
class IoContext;
|
||||
class DeadlineTimer;
|
||||
}
|
||||
}
|
||||
|
||||
enum MetricDataType
|
||||
{
|
||||
METRIC_DATA_VALUE,
|
||||
@@ -46,6 +37,7 @@ enum MetricDataType
|
||||
};
|
||||
|
||||
using MetricTag = std::pair<std::string, std::string>;
|
||||
using MetricTags = std::variant<std::monostate, std::array<MetricTag, 2>, std::vector<MetricTag>>;
|
||||
|
||||
struct MetricData
|
||||
{
|
||||
@@ -54,7 +46,7 @@ struct MetricData
|
||||
MetricDataType Type = METRIC_DATA_VALUE;
|
||||
|
||||
// LogValue-specific fields
|
||||
Optional<std::variant<std::array<MetricTag, 2>, std::vector<MetricTag>>> Tags;
|
||||
MetricTags Tags;
|
||||
|
||||
// LogEvent-specific fields
|
||||
std::string Title;
|
||||
@@ -68,8 +60,10 @@ struct MetricData
|
||||
class TC_COMMON_API Metric
|
||||
{
|
||||
private:
|
||||
std::iostream& GetDataStream() { return *_dataStream; }
|
||||
std::unique_ptr<std::iostream> _dataStream;
|
||||
using iostream = boost::asio::basic_socket_iostream<boost::asio::ip::tcp, std::chrono::steady_clock, boost::asio::wait_traits<std::chrono::steady_clock>>;
|
||||
|
||||
iostream& GetDataStream() { return *_dataStream; }
|
||||
std::unique_ptr<iostream> _dataStream;
|
||||
MPSCQueue<MetricData, &MetricData::QueueLink> _queuedData;
|
||||
std::unique_ptr<Trinity::Asio::DeadlineTimer> _batchTimer;
|
||||
std::unique_ptr<Trinity::Asio::DeadlineTimer> _overallStatusTimer;
|
||||
@@ -82,7 +76,7 @@ private:
|
||||
std::string _databaseName;
|
||||
std::function<void()> _overallStatusLogger;
|
||||
std::string _realmName;
|
||||
std::unordered_map<std::string, int64> _thresholds;
|
||||
std::unordered_map<std::string, int64, Trinity::TransparentHash<std::string_view>, std::equal_to<>> _thresholds;
|
||||
|
||||
bool Connect();
|
||||
void SendBatch();
|
||||
@@ -90,7 +84,7 @@ private:
|
||||
void ScheduleOverallStatusLog();
|
||||
|
||||
static std::string FormatInfluxDBValue(bool value);
|
||||
template <class T>
|
||||
template <class T> requires std::integral<T> && (!std::same_as<T, bool>)
|
||||
static std::string FormatInfluxDBValue(T value);
|
||||
static std::string FormatInfluxDBValue(std::string const& value);
|
||||
static std::string FormatInfluxDBValue(char const* value);
|
||||
@@ -114,37 +108,50 @@ public:
|
||||
void Initialize(std::string const& realmName, Trinity::Asio::IoContext& ioContext, std::function<void()> overallStatusLogger);
|
||||
void LoadFromConfigs();
|
||||
void Update();
|
||||
bool ShouldLog(std::string const& category, int64 value) const;
|
||||
bool ShouldLog(std::string_view category, int64 value) const;
|
||||
|
||||
template<class T, class... TagsList>
|
||||
void LogValue(std::string category, T value, TagsList&&... tags)
|
||||
void LogValue(std::string_view category, T value, TagsList&&... tags) noexcept
|
||||
{
|
||||
using namespace std::chrono;
|
||||
using TagsType = std::conditional_t<(sizeof...(tags) > 2),
|
||||
std::vector<MetricTag>,
|
||||
std::conditional_t<(sizeof...(tags) > 0),
|
||||
std::array<MetricTag, 2>,
|
||||
std::monostate>>;
|
||||
|
||||
MetricData* data = new MetricData;
|
||||
data->Category = std::move(category);
|
||||
data->Timestamp = system_clock::now();
|
||||
data->Type = METRIC_DATA_VALUE;
|
||||
data->ValueOrEventText = FormatInfluxDBValue(value);
|
||||
if constexpr (sizeof...(tags) > 0)
|
||||
MetricData* data = new MetricData
|
||||
{
|
||||
data->Tags.emplace();
|
||||
if constexpr (sizeof...(tags) > 2)
|
||||
.Category = std::string(category),
|
||||
.Timestamp = std::chrono::system_clock::now(),
|
||||
.Type = METRIC_DATA_VALUE,
|
||||
.Tags = MetricTags{ std::in_place_type<TagsType> },
|
||||
.ValueOrEventText = FormatInfluxDBValue(value)
|
||||
};
|
||||
if constexpr (sizeof...(tags) > 2)
|
||||
{
|
||||
TagsType& tagsVector = std::get<TagsType>(data->Tags);
|
||||
(tagsVector.emplace_back(std::forward<TagsList>(tags)), ...);
|
||||
}
|
||||
else if constexpr (sizeof...(tags) == 2)
|
||||
{
|
||||
[](TagsType& tagsArray, auto const& tag1, auto const& tag2)
|
||||
{
|
||||
decltype(auto) tagsVector = data->Tags->emplace<1>();
|
||||
(tagsVector.emplace_back(std::move(tags)), ...);
|
||||
}
|
||||
else
|
||||
tagsArray[0] = tag1;
|
||||
tagsArray[1] = tag2;
|
||||
}(std::get<TagsType>(data->Tags), std::forward<TagsList>(tags)...);
|
||||
}
|
||||
else if constexpr (sizeof...(tags) == 1)
|
||||
{
|
||||
[](TagsType& tagsArray, auto const& tag1)
|
||||
{
|
||||
decltype(auto) tagsArray = data->Tags->emplace<0>();
|
||||
tagsArray = { std::move(tags)... };
|
||||
}
|
||||
tagsArray[0] = tag1;
|
||||
}(std::get<TagsType>(data->Tags), std::forward<TagsList>(tags)...);
|
||||
}
|
||||
|
||||
_queuedData.Enqueue(data);
|
||||
}
|
||||
|
||||
void LogEvent(std::string category, std::string title, std::string description);
|
||||
void LogEvent(std::string_view category, std::string_view title, std::string description);
|
||||
|
||||
void Unload();
|
||||
bool IsEnabled() const { return _enabled; }
|
||||
@@ -152,36 +159,32 @@ public:
|
||||
|
||||
#define sMetric Metric::instance()
|
||||
|
||||
template<typename LoggerType>
|
||||
class MetricStopWatch
|
||||
struct MetricStopWatchBase
|
||||
{
|
||||
public:
|
||||
MetricStopWatch(LoggerType&& loggerFunc) :
|
||||
_logger(std::forward<LoggerType>(loggerFunc)),
|
||||
_startTime(std::chrono::steady_clock::now())
|
||||
void Enable(Metric* metric) noexcept
|
||||
{
|
||||
MetricInstance = metric;
|
||||
StartTime = std::chrono::steady_clock::now();
|
||||
}
|
||||
|
||||
~MetricStopWatch()
|
||||
{
|
||||
_logger(_startTime);
|
||||
}
|
||||
|
||||
private:
|
||||
LoggerType _logger;
|
||||
TimePoint _startTime;
|
||||
Metric* MetricInstance = nullptr;
|
||||
TimePoint StartTime = { };
|
||||
};
|
||||
|
||||
template<typename LoggerType>
|
||||
Optional<MetricStopWatch<LoggerType>> MakeMetricStopWatch(LoggerType&& loggerFunc)
|
||||
struct MetricStopWatch : public LoggerType, public MetricStopWatchBase
|
||||
{
|
||||
if (!sMetric->IsEnabled())
|
||||
return {};
|
||||
~MetricStopWatch()
|
||||
{
|
||||
if (MetricInstance)
|
||||
LoggerType::operator()(MetricInstance, StartTime);
|
||||
}
|
||||
};
|
||||
|
||||
return Optional<MetricStopWatch<LoggerType>>(std::in_place, std::forward<LoggerType>(loggerFunc));
|
||||
}
|
||||
template<typename LoggerType>
|
||||
MetricStopWatch(LoggerType) -> MetricStopWatch<LoggerType>;
|
||||
|
||||
#define TC_METRIC_TAG(name, value) MetricTag(name, value)
|
||||
#define TC_METRIC_TAG(name, value) std::pair<std::string_view, std::string_view>(name, value)
|
||||
|
||||
#define TC_METRIC_DO_CONCAT(a, b) a ## b
|
||||
#define TC_METRIC_CONCAT(a, b) TC_METRIC_DO_CONCAT(a, b)
|
||||
@@ -195,50 +198,42 @@ Optional<MetricStopWatch<LoggerType>> MakeMetricStopWatch(LoggerType&& loggerFun
|
||||
#define TC_METRIC_DETAILED_TIMER(category, ...) ((void)0)
|
||||
#define TC_METRIC_DETAILED_NO_THRESHOLD_TIMER(category, ...) ((void)0)
|
||||
#else
|
||||
# if TRINITY_PLATFORM != TRINITY_PLATFORM_WINDOWS
|
||||
#define TC_METRIC_EVENT(category, title, description) \
|
||||
do { \
|
||||
if (sMetric->IsEnabled()) \
|
||||
sMetric->LogEvent(category, title, description); \
|
||||
} while (0)
|
||||
if (Metric* metric = sMetric; metric->IsEnabled()) \
|
||||
metric->LogEvent(category, title, description); \
|
||||
} while (false)
|
||||
#define TC_METRIC_VALUE(category, value, ...) \
|
||||
do { \
|
||||
if (sMetric->IsEnabled()) \
|
||||
sMetric->LogValue(category, value, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
# else
|
||||
#define TC_METRIC_EVENT(category, title, description) \
|
||||
__pragma(warning(push)) \
|
||||
__pragma(warning(disable:4127)) \
|
||||
do { \
|
||||
if (sMetric->IsEnabled()) \
|
||||
sMetric->LogEvent(category, title, description); \
|
||||
} while (0) \
|
||||
__pragma(warning(pop))
|
||||
#define TC_METRIC_VALUE(category, value, ...) \
|
||||
__pragma(warning(push)) \
|
||||
__pragma(warning(disable:4127)) \
|
||||
do { \
|
||||
if (sMetric->IsEnabled()) \
|
||||
sMetric->LogValue(category, value, ##__VA_ARGS__); \
|
||||
} while (0) \
|
||||
__pragma(warning(pop))
|
||||
# endif
|
||||
#define TC_METRIC_TIMER(category, ...) \
|
||||
auto TC_METRIC_UNIQUE_NAME(__tc_metric_stop_watch) = MakeMetricStopWatch([&](TimePoint start) \
|
||||
{ \
|
||||
sMetric->LogValue(category, std::chrono::steady_clock::now() - start, ##__VA_ARGS__); \
|
||||
});
|
||||
if (Metric* metric = sMetric; metric->IsEnabled()) \
|
||||
metric->LogValue(category, value, ## __VA_ARGS__); \
|
||||
} while (false)
|
||||
#define TC_METRIC_TIMER_IMPL(variable, category, ...) \
|
||||
MetricStopWatch variable{ [&](Metric* metric, TimePoint start) \
|
||||
{ \
|
||||
metric->LogValue(category, std::chrono::steady_clock::now() - start, ## __VA_ARGS__); \
|
||||
} }; \
|
||||
do \
|
||||
{ \
|
||||
if (Metric* metric = sMetric; metric->IsEnabled()) \
|
||||
variable.Enable(metric); \
|
||||
} while (false)
|
||||
#define TC_METRIC_TIMER(category, ...) TC_METRIC_TIMER_IMPL(TC_METRIC_UNIQUE_NAME(tc_metric_stop_watch_), category, ## __VA_ARGS__)
|
||||
# if defined WITH_DETAILED_METRICS
|
||||
#define TC_METRIC_DETAILED_TIMER(category, ...) \
|
||||
auto TC_METRIC_UNIQUE_NAME(__tc_metric_stop_watch) = MakeMetricStopWatch([&](TimePoint start) \
|
||||
{ \
|
||||
int64 duration = int64(std::chrono::duration_cast<Milliseconds>(std::chrono::steady_clock::now() - start).count()); \
|
||||
std::string category2 = category; \
|
||||
if (sMetric->ShouldLog(category2, duration)) \
|
||||
sMetric->LogValue(std::move(category2), duration, ##__VA_ARGS__); \
|
||||
});
|
||||
#define TC_METRIC_DETAILED_NO_THRESHOLD_TIMER(category, ...) TC_METRIC_TIMER(category, ##__VA_ARGS__)
|
||||
#define TC_METRIC_DETAILED_TIMER_IMPL(variable, category, ...) \
|
||||
MetricStopWatch variable{ [&](Metric* metric, TimePoint start) \
|
||||
{ \
|
||||
int64 duration = int64(duration_cast<Milliseconds>(std::chrono::steady_clock::now() - start).count()); \
|
||||
if (metric->ShouldLog(category, duration)) \
|
||||
metric->LogValue(category, duration, ## __VA_ARGS__); \
|
||||
} }; \
|
||||
do \
|
||||
{ \
|
||||
if (Metric* metric = sMetric; metric->IsEnabled()) \
|
||||
variable.Enable(metric); \
|
||||
} while (false)
|
||||
#define TC_METRIC_DETAILED_TIMER(category, ...) TC_METRIC_DETAILED_TIMER_IMPL(TC_METRIC_UNIQUE_NAME(tc_metric_stop_watch_), category, ## __VA_ARGS__)
|
||||
#define TC_METRIC_DETAILED_NO_THRESHOLD_TIMER(category, ...) TC_METRIC_TIMER(category, ## __VA_ARGS__)
|
||||
#define TC_METRIC_DETAILED_EVENT(category, title, description) TC_METRIC_EVENT(category, title, description)
|
||||
# else
|
||||
#define TC_METRIC_DETAILED_EVENT(category, title, description) ((void)0)
|
||||
@@ -248,4 +243,4 @@ Optional<MetricStopWatch<LoggerType>> MakeMetricStopWatch(LoggerType&& loggerFun
|
||||
|
||||
#endif
|
||||
|
||||
#endif // METRIC_H__
|
||||
#endif // TRINITYCORE_METRIC_H
|
||||
|
||||
Reference in New Issue
Block a user