/* * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . */ #ifndef METRIC_H__ #define METRIC_H__ #include "Define.h" #include "Duration.h" #include "MPSCQueue.h" #include "Optional.h" #include #include #include #include #include #include #include namespace Trinity { namespace Asio { class IoContext; class DeadlineTimer; } } enum MetricDataType { METRIC_DATA_VALUE, METRIC_DATA_EVENT }; using MetricTag = std::pair; using MetricTagsVector = boost::container::small_vector; struct MetricData { std::string Category; SystemTimePoint Timestamp; MetricDataType Type; // LogValue-specific fields MetricTagsVector Tags; // LogEvent-specific fields std::string Title; std::string ValueOrEventText; // intrusive queue link std::atomic QueueLink; }; class TC_COMMON_API Metric { private: std::iostream& GetDataStream() { return *_dataStream; } std::unique_ptr _dataStream; MPSCQueue _queuedData; std::unique_ptr _batchTimer; std::unique_ptr _overallStatusTimer; int32 _updateInterval = 0; int32 _overallStatusTimerInterval = 0; bool _enabled = false; bool _overallStatusTimerTriggered = false; std::string _hostname; std::string _port; std::string _databaseName; std::function _overallStatusLogger; std::string _realmName; std::unordered_map _thresholds; bool Connect(); void SendBatch(); void ScheduleSend(); void ScheduleOverallStatusLog(); static std::string FormatInfluxDBValue(bool value); template 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); // ToDo: should format TagKey and FieldKey too in the same way as TagValue public: Metric(); ~Metric(); static Metric* instance(); void Initialize(std::string const& realmName, Trinity::Asio::IoContext& ioContext, std::function overallStatusLogger); void LoadFromConfigs(); void Update(); bool ShouldLog(std::string const& category, int64 value) const; template void LogValue(std::string category, T value, Tags&&... tags) { using namespace std::chrono; 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) (data->Tags.emplace_back(std::move(tags)), ...); _queuedData.Enqueue(data); } void LogEvent(std::string category, std::string title, std::string description); void Unload(); bool IsEnabled() const { return _enabled; } }; #define sMetric Metric::instance() template class MetricStopWatch { public: MetricStopWatch(LoggerType&& loggerFunc) : _logger(std::forward(loggerFunc)), _startTime(std::chrono::steady_clock::now()) { } ~MetricStopWatch() { _logger(_startTime); } private: LoggerType _logger; TimePoint _startTime; }; template Optional> MakeMetricStopWatch(LoggerType&& loggerFunc) { if (!sMetric->IsEnabled()) return {}; return Optional>(std::in_place, std::forward(loggerFunc)); } #define TC_METRIC_TAG(name, value) MetricTag(name, value) #define TC_METRIC_DO_CONCAT(a, b) a ## b #define TC_METRIC_CONCAT(a, b) TC_METRIC_DO_CONCAT(a, b) #define TC_METRIC_UNIQUE_NAME(name) TC_METRIC_CONCAT(name, __LINE__) #if defined PERFORMANCE_PROFILING || defined WITHOUT_METRICS #define TC_METRIC_EVENT(category, title, description) ((void)0) #define TC_METRIC_VALUE(category, value, ...) ((void)0) #define TC_METRIC_TIMER(category, ...) ((void)0) #define TC_METRIC_DETAILED_EVENT(category, title, description) ((void)0) #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) #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 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(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_EVENT(category, title, description) TC_METRIC_EVENT(category, title, description) # else #define TC_METRIC_DETAILED_EVENT(category, title, description) ((void)0) #define TC_METRIC_DETAILED_TIMER(category, ...) ((void)0) #define TC_METRIC_DETAILED_NO_THRESHOLD_TIMER(category, ...) ((void)0) # endif #endif #endif // METRIC_H__