aboutsummaryrefslogtreecommitdiff
path: root/src/common
diff options
context:
space:
mode:
Diffstat (limited to 'src/common')
-rw-r--r--src/common/Collision/DynamicTree.cpp2
-rw-r--r--src/common/Collision/Maps/MapTree.cpp5
-rw-r--r--src/common/Metric/Metric.cpp235
-rw-r--r--src/common/Metric/Metric.h141
4 files changed, 382 insertions, 1 deletions
diff --git a/src/common/Collision/DynamicTree.cpp b/src/common/Collision/DynamicTree.cpp
index 5d2e18b1a2e..111606644de 100644
--- a/src/common/Collision/DynamicTree.cpp
+++ b/src/common/Collision/DynamicTree.cpp
@@ -248,7 +248,7 @@ bool DynamicMapTree::isInLineOfSight(float x1, float y1, float z1, float x2, flo
float DynamicMapTree::getHeight(float x, float y, float z, float maxSearchDist, uint32 phasemask) const
{
- G3D::Vector3 v(x, y, z);
+ G3D::Vector3 v(x, y, z + 0.5f);
G3D::Ray r(v, G3D::Vector3(0, 0, -1));
DynamicTreeIntersectionCallback callback(phasemask);
impl->intersectZAllignedRay(r, callback, maxSearchDist);
diff --git a/src/common/Collision/Maps/MapTree.cpp b/src/common/Collision/Maps/MapTree.cpp
index 4d0996e1961..5907971efb9 100644
--- a/src/common/Collision/Maps/MapTree.cpp
+++ b/src/common/Collision/Maps/MapTree.cpp
@@ -22,6 +22,7 @@
#include "VMapDefinitions.h"
#include "Log.h"
#include "Errors.h"
+#include "Metric.h"
#include <string>
#include <sstream>
@@ -415,6 +416,8 @@ 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));
return result;
}
@@ -473,6 +476,8 @@ 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));
}
void StaticMapTree::getModelInstances(ModelInstance* &models, uint32 &count)
diff --git a/src/common/Metric/Metric.cpp b/src/common/Metric/Metric.cpp
new file mode 100644
index 00000000000..9484cebcc72
--- /dev/null
+++ b/src/common/Metric/Metric.cpp
@@ -0,0 +1,235 @@
+/*
+* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
+*
+* 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "Metric.h"
+#include "Log.h"
+#include "Config.h"
+#include "Util.h"
+
+void Metric::Initialize(std::string const& realmName, boost::asio::io_service& ioService, std::function<void()> overallStatusLogger)
+{
+ _realmName = realmName;
+ _batchTimer = Trinity::make_unique<boost::asio::deadline_timer>(ioService);
+ _overallStatusTimer = Trinity::make_unique<boost::asio::deadline_timer>(ioService);
+ _overallStatusLogger = overallStatusLogger;
+ LoadFromConfigs();
+}
+
+bool Metric::Connect()
+{
+ _dataStream.connect(_hostname, _port);
+ auto error = _dataStream.error();
+ if (error)
+ {
+ TC_LOG_ERROR("metric", "Error connecting to '%s:%s', disabling Metric. Error message : %s",
+ _hostname.c_str(), _port.c_str(), error.message().c_str());
+ _enabled = false;
+ return false;
+ }
+ _dataStream.clear();
+ return true;
+}
+
+void Metric::LoadFromConfigs()
+{
+ bool previousValue = _enabled;
+ _enabled = sConfigMgr->GetBoolDefault("Metric.Enable", false);
+ _updateInterval = sConfigMgr->GetIntDefault("Metric.Interval", 10);
+ if (_updateInterval < 1)
+ {
+ TC_LOG_ERROR("metric", "'Metric.Interval' config set to %d, overriding to 1.", _updateInterval);
+ _updateInterval = 1;
+ }
+
+ _overallStatusTimerInterval = sConfigMgr->GetIntDefault("Metric.OverallStatusInterval", 1);
+ if (_overallStatusTimerInterval < 1)
+ {
+ TC_LOG_ERROR("metric", "'Metric.OverallStatusInterval' config set to %d, overriding to 1.", _overallStatusTimerInterval);
+ _overallStatusTimerInterval = 1;
+ }
+
+ // Schedule a send at this point only if the config changed from Disabled to Enabled.
+ // Cancel any scheduled operation if the config changed from Enabled to Disabled.
+ if (_enabled && !previousValue)
+ {
+ std::string connectionInfo = sConfigMgr->GetStringDefault("Metric.ConnectionInfo", "");
+ if (connectionInfo.empty())
+ {
+ TC_LOG_ERROR("metric", "'Metric.ConnectionInfo' not specified in configuration file.");
+ return;
+ }
+
+ Tokenizer tokens(connectionInfo, ';');
+ if (tokens.size() != 3)
+ {
+ TC_LOG_ERROR("metric", "'Metric.ConnectionInfo' specified with wrong format in configuration file.");
+ return;
+ }
+
+ _hostname.assign(tokens[0]);
+ _port.assign(tokens[1]);
+ _databaseName.assign(tokens[2]);
+ Connect();
+
+ ScheduleSend();
+ ScheduleOverallStatusLog();
+ }
+}
+
+void Metric::Update()
+{
+ if (_overallStatusTimerTriggered)
+ {
+ _overallStatusTimerTriggered = false;
+ _overallStatusLogger();
+ }
+}
+
+void Metric::LogEvent(std::string const& category, std::string const& title, std::string const& description)
+{
+ using namespace std::chrono;
+
+ MetricData* data = new MetricData;
+ data->Category = category;
+ data->Timestamp = system_clock::now();
+ data->Type = METRIC_DATA_EVENT;
+ data->Title = title;
+ data->Text = description;
+
+ _queuedData.Enqueue(data);
+}
+
+void Metric::SendBatch()
+{
+ using namespace std::chrono;
+
+ std::stringstream batchedData;
+ MetricData* data;
+ bool firstLoop = true;
+ while (_queuedData.Dequeue(data))
+ {
+ if (!firstLoop)
+ batchedData << "\n";
+
+ batchedData << data->Category;
+ if (!_realmName.empty())
+ batchedData << ",realm=" << _realmName;
+
+ batchedData << " ";
+
+ switch (data->Type)
+ {
+ case METRIC_DATA_VALUE:
+ batchedData << "value=" << data->Value;
+ break;
+ case METRIC_DATA_EVENT:
+ batchedData << "title=\"" << data->Title << "\",text=\"" << data->Text << "\"";
+ break;
+ }
+
+ batchedData << " ";
+
+ batchedData << std::to_string(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))
+ {
+ ScheduleSend();
+ return;
+ }
+
+ if (!_dataStream.good() && !Connect())
+ return;
+
+ _dataStream << "POST " << "/write?db=" << _databaseName << " HTTP/1.1\r\n";
+ _dataStream << "Host: " << _hostname << ":" << _port << "\r\n";
+ _dataStream << "Accept: */*\r\n";
+ _dataStream << "Content-Type: application/octet-stream\r\n";
+ _dataStream << "Content-Transfer-Encoding: binary\r\n";
+
+ _dataStream << "Content-Length: " << std::to_string(batchedData.tellp()) << "\r\n\r\n";
+ _dataStream << batchedData.rdbuf();
+
+ std::string http_version;
+ _dataStream >> http_version;
+ unsigned int status_code = 0;
+ _dataStream >> status_code;
+ if (status_code != 204)
+ {
+ TC_LOG_ERROR("metric", "Error sending data, returned HTTP code: %u", status_code);
+ }
+
+ // Read and ignore the status description
+ std::string status_description;
+ std::getline(_dataStream, status_description);
+ // Read headers
+ std::string header;
+ while (std::getline(_dataStream, header) && header != "\r")
+ {
+ if (header == "Connection: close\r")
+ _dataStream.close();
+ }
+
+ ScheduleSend();
+}
+
+void Metric::ScheduleSend()
+{
+ if (_enabled)
+ {
+ _batchTimer->expires_from_now(boost::posix_time::seconds(_updateInterval));
+ _batchTimer->async_wait(std::bind(&Metric::SendBatch, this));
+ }
+ else
+ {
+ _dataStream.close();
+ MetricData* data;
+ // Clear the queue
+ while (_queuedData.Dequeue(data))
+ ;
+ }
+}
+
+void Metric::ForceSend()
+{
+ // Send what's queued only if io_service is stopped (so only on shutdown)
+ if (_enabled && _batchTimer->get_io_service().stopped())
+ SendBatch();
+}
+
+void Metric::ScheduleOverallStatusLog()
+{
+ if (_enabled)
+ {
+ _overallStatusTimer->expires_from_now(boost::posix_time::seconds(_overallStatusTimerInterval));
+ _overallStatusTimer->async_wait([this](const boost::system::error_code&)
+ {
+ _overallStatusTimerTriggered = true;
+ ScheduleOverallStatusLog();
+ });
+ }
+}
+
+Metric* Metric::instance()
+{
+ static Metric instance;
+ return &instance;
+}
diff --git a/src/common/Metric/Metric.h b/src/common/Metric/Metric.h
new file mode 100644
index 00000000000..1855e1d0098
--- /dev/null
+++ b/src/common/Metric/Metric.h
@@ -0,0 +1,141 @@
+/*
+* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
+*
+* 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef METRIC_H__
+#define METRIC_H__
+
+#include "Common.h"
+#include "Threading/MPSCQueue.h"
+#include <boost/asio/ip/tcp.hpp>
+#include <boost/algorithm/string.hpp>
+#include <type_traits>
+
+enum MetricDataType
+{
+ METRIC_DATA_VALUE,
+ METRIC_DATA_EVENT
+};
+
+struct MetricData
+{
+ std::string Category;
+ std::chrono::time_point<std::chrono::system_clock> Timestamp;
+ MetricDataType Type;
+
+ // LogValue-specific fields
+ std::string Value;
+
+ // LogEvent-specific fields
+ std::string Title;
+ std::string Text;
+};
+
+class TC_COMMON_API Metric
+{
+private:
+ boost::asio::ip::tcp::iostream _dataStream;
+ MPSCQueue<MetricData> _queuedData;
+ std::unique_ptr<boost::asio::deadline_timer> _batchTimer;
+ std::unique_ptr<boost::asio::deadline_timer> _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;
+
+ bool Connect();
+ void SendBatch();
+ void ScheduleSend();
+ void ScheduleOverallStatusLog();
+
+ template<class T, typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
+ static std::string FormatInfluxDBValue(T value) { return std::to_string(value) + 'i'; }
+
+ static std::string FormatInfluxDBValue(std::string const& value)
+ {
+ return '"' + boost::replace_all_copy(value, "\"", "\\\"") + '"';
+ }
+
+ static std::string FormatInfluxDBValue(bool value) { return value ? "t" : "f"; }
+ static std::string FormatInfluxDBValue(const char* value) { return FormatInfluxDBValue(std::string(value)); }
+ static std::string FormatInfluxDBValue(double value) { return std::to_string(value); }
+ static std::string FormatInfluxDBValue(float value) { return FormatInfluxDBValue(double(value)); }
+
+public:
+ static Metric* instance();
+
+ void Initialize(std::string const& realmName, boost::asio::io_service& ioService, std::function<void()> overallStatusLogger = [](){});
+ void LoadFromConfigs();
+ void Update();
+
+ template<class T>
+ void LogValue(std::string const& category, T value)
+ {
+ using namespace std::chrono;
+
+ MetricData* data = new MetricData;
+ data->Category = category;
+ data->Timestamp = system_clock::now();
+ data->Type = METRIC_DATA_VALUE;
+ data->Value = FormatInfluxDBValue(value);
+
+ _queuedData.Enqueue(data);
+ }
+
+ void LogEvent(std::string const& category, std::string const& title, std::string const& description);
+
+ void ForceSend();
+ bool IsEnabled() const { return _enabled; }
+};
+
+#define sMetric Metric::instance()
+
+#if PLATFORM != 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); \
+ } 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); \
+ } while (0) \
+ __pragma(warning(pop))
+#endif
+
+#endif // METRIC_H__