mirror of
https://github.com/BeamMP/BeamMP-Server.git
synced 2025-07-02 07:45:26 +00:00
Sentry: add multiple more logging mechanisms, add [CHAT]
This commit is contained in:
parent
5330013dc3
commit
8fada3ac04
@ -1,6 +1,7 @@
|
|||||||
# v2.3.0
|
# v2.3.0
|
||||||
|
|
||||||
- ADDED logging of errors to the backend
|
- ADDED logging of various errors, crashes and exceptions to the backend
|
||||||
|
- ADDED `[CHAT]` messages to server console
|
||||||
|
|
||||||
# v2.2.0
|
# v2.2.0
|
||||||
|
|
||||||
|
@ -126,6 +126,8 @@ void RegisterThread(const std::string str);
|
|||||||
} \
|
} \
|
||||||
} while (false)
|
} while (false)
|
||||||
|
|
||||||
|
void LogChatMessage(const std::string& name, int id, const std::string& msg);
|
||||||
|
|
||||||
#define Biggest 30000
|
#define Biggest 30000
|
||||||
std::string Comp(std::string Data);
|
std::string Comp(std::string Data);
|
||||||
std::string DeComp(std::string Compressed);
|
std::string DeComp(std::string Compressed);
|
||||||
|
@ -59,10 +59,18 @@ inline void _assert([[maybe_unused]] const char* file, [[maybe_unused]] const ch
|
|||||||
#define AssertNotReachable() _assert(__FILE__, __func__, __LINE__, "reached unreachable code", false)
|
#define AssertNotReachable() _assert(__FILE__, __func__, __LINE__, "reached unreachable code", false)
|
||||||
#else
|
#else
|
||||||
// In release build, these macros turn into NOPs. The compiler will optimize these out.
|
// In release build, these macros turn into NOPs. The compiler will optimize these out.
|
||||||
#define Assert(x) \
|
#define Assert(cond) \
|
||||||
do { \
|
do { \
|
||||||
} while (false)
|
if (!result) { \
|
||||||
#define AssertNotReachable() \
|
Sentry.LogAssert(#cond, _file_basename, _line, __func__); \
|
||||||
do { \
|
}
|
||||||
} while (false)
|
}
|
||||||
|
while (false)
|
||||||
|
#define AssertNotReachable() \
|
||||||
|
do { \
|
||||||
|
if (!result) { \
|
||||||
|
Sentry.LogAssert("code is unreachable", _file_basename, _line, __func__); \
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (false)
|
||||||
#endif // DEBUG
|
#endif // DEBUG
|
||||||
|
@ -3,8 +3,9 @@
|
|||||||
|
|
||||||
#include <sentry.h>
|
#include <sentry.h>
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
// TODO possibly use attach_stacktrace
|
// TODO possibly use attach_stacktrace
|
||||||
|
|
||||||
@ -18,9 +19,9 @@ public:
|
|||||||
void SetupUser();
|
void SetupUser();
|
||||||
void Log(sentry_level_t level, const std::string& logger, const std::string& text);
|
void Log(sentry_level_t level, const std::string& logger, const std::string& text);
|
||||||
void LogError(const std::string& text, const std::string& file, const std::string& line);
|
void LogError(const std::string& text, const std::string& file, const std::string& line);
|
||||||
void SetExtra(const std::string& key, const sentry_value_t& value);
|
void SetContext(const std::string& context_name, const std::unordered_map<std::string, std::string>& map);
|
||||||
void SetExtra(const std::string& key, const std::string& value);
|
|
||||||
void LogException(const std::exception& e, const std::string& file, const std::string& line);
|
void LogException(const std::exception& e, const std::string& file, const std::string& line);
|
||||||
|
void LogAssert(const std::string& condition_string, const std::string& file, const std::string& line, const std::string& function);
|
||||||
void AddErrorBreadcrumb(const std::string& msg, const std::string& file, const std::string& line);
|
void AddErrorBreadcrumb(const std::string& msg, const std::string& file, const std::string& line);
|
||||||
// cleared when Logged
|
// cleared when Logged
|
||||||
void SetTransaction(const std::string& id);
|
void SetTransaction(const std::string& id);
|
||||||
@ -29,6 +30,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
bool mValid { true };
|
bool mValid { true };
|
||||||
std::mutex mMutex;
|
std::mutex mMutex;
|
||||||
|
sentry_value_t mContext;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SENTRY_H
|
#endif // SENTRY_H
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#include <array>
|
#include <array>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <sstream>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <zlib.h>
|
#include <zlib.h>
|
||||||
|
|
||||||
@ -86,3 +87,15 @@ std::string ThreadName() {
|
|||||||
void RegisterThread(const std::string str) {
|
void RegisterThread(const std::string str) {
|
||||||
threadNameMap[std::this_thread::get_id()] = str;
|
threadNameMap[std::this_thread::get_id()] = str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LogChatMessage(const std::string& name, int id, const std::string& msg) {
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "[CHAT] ";
|
||||||
|
if (id != -1) {
|
||||||
|
ss << "(" << id << ") <" << name << ">";
|
||||||
|
} else {
|
||||||
|
ss << name << "";
|
||||||
|
}
|
||||||
|
ss << msg;
|
||||||
|
Application::Console().Write(ss.str());
|
||||||
|
}
|
||||||
|
@ -124,15 +124,16 @@ std::string Http::POST(const std::string& host, const std::string& target, const
|
|||||||
|
|
||||||
http::read(stream, buffer, response);
|
http::read(stream, buffer, response);
|
||||||
|
|
||||||
Sentry.SetExtra("reponse-code", std::to_string(response.result_int()));
|
std::unordered_map<std::string, std::string> response_data;
|
||||||
|
response_data["reponse-code"] = std::to_string(response.result_int());
|
||||||
for (const auto& header : response.base()) {
|
for (const auto& header : response.base()) {
|
||||||
// need to do explicit casts to convert string_view to string
|
// need to do explicit casts to convert string_view to string
|
||||||
// since string_view may not be null-terminated (and in fact isn't, here)
|
// since string_view may not be null-terminated (and in fact isn't, here)
|
||||||
std::string KeyString(header.name_string());
|
std::string KeyString(header.name_string());
|
||||||
std::string ValueString(header.value());
|
std::string ValueString(header.value());
|
||||||
Sentry.SetExtra(KeyString, ValueString);
|
response_data[KeyString] = ValueString;
|
||||||
}
|
}
|
||||||
|
Sentry.SetContext("https-post-data", response_data);
|
||||||
|
|
||||||
std::stringstream result;
|
std::stringstream result;
|
||||||
result << response;
|
result << response;
|
||||||
|
@ -43,8 +43,9 @@ void THeartbeatThread::operator()() {
|
|||||||
if (T.size() > std::string("YOU_SHALL_NOT_PASS").size()
|
if (T.size() > std::string("YOU_SHALL_NOT_PASS").size()
|
||||||
&& Application::Settings.Key.size() == 36) {
|
&& Application::Settings.Key.size() == 36) {
|
||||||
auto Lock = Sentry.CreateExclusiveContext();
|
auto Lock = Sentry.CreateExclusiveContext();
|
||||||
Sentry.SetExtra("response-body", T);
|
Sentry.SetContext("heartbeat",
|
||||||
Sentry.SetExtra("request-body", Body);
|
{ { "response-body", T },
|
||||||
|
{ "request-body", Body } });
|
||||||
Sentry.SetTransaction(transaction);
|
Sentry.SetTransaction(transaction);
|
||||||
Sentry.Log(SENTRY_LEVEL_ERROR, "default", "wrong backend response format");
|
Sentry.Log(SENTRY_LEVEL_ERROR, "default", "wrong backend response format");
|
||||||
}
|
}
|
||||||
|
@ -118,7 +118,7 @@ bool CheckLua(lua_State* L, int r) {
|
|||||||
warn(a + " | " + msg);
|
warn(a + " | " + msg);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// What the fuck, what do we do?!
|
// This should never happen since it's not directly called from "userspace" Lua.
|
||||||
AssertNotReachable();
|
AssertNotReachable();
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -127,7 +127,10 @@ bool CheckLua(lua_State* L, int r) {
|
|||||||
int lua_RegisterEvent(lua_State* L) {
|
int lua_RegisterEvent(lua_State* L) {
|
||||||
int Args = lua_gettop(L);
|
int Args = lua_gettop(L);
|
||||||
auto MaybeScript = Engine().GetScript(L);
|
auto MaybeScript = Engine().GetScript(L);
|
||||||
Assert(MaybeScript.has_value());
|
if (!MaybeScript.has_value()) {
|
||||||
|
error("RegisterEvent: There is no script associated with this lua_State.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
TLuaFile& Script = MaybeScript.value();
|
TLuaFile& Script = MaybeScript.value();
|
||||||
if (Args == 2 && lua_isstring(L, 1) && lua_isstring(L, 2)) {
|
if (Args == 2 && lua_isstring(L, 1) && lua_isstring(L, 2)) {
|
||||||
Script.RegisterEvent(lua_tostring(L, 1), lua_tostring(L, 2));
|
Script.RegisterEvent(lua_tostring(L, 1), lua_tostring(L, 2));
|
||||||
@ -139,7 +142,10 @@ int lua_RegisterEvent(lua_State* L) {
|
|||||||
int lua_TriggerEventL(lua_State* L) {
|
int lua_TriggerEventL(lua_State* L) {
|
||||||
int Args = lua_gettop(L);
|
int Args = lua_gettop(L);
|
||||||
auto MaybeScript = Engine().GetScript(L);
|
auto MaybeScript = Engine().GetScript(L);
|
||||||
Assert(MaybeScript.has_value());
|
if (!MaybeScript.has_value()) {
|
||||||
|
error("TriggerEvent: There is no script associated with this lua_State.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
TLuaFile& Script = MaybeScript.value();
|
TLuaFile& Script = MaybeScript.value();
|
||||||
if (Args > 0) {
|
if (Args > 0) {
|
||||||
if (lua_isstring(L, 1)) {
|
if (lua_isstring(L, 1)) {
|
||||||
@ -155,7 +161,10 @@ int lua_TriggerEventL(lua_State* L) {
|
|||||||
int lua_TriggerEventG(lua_State* L) {
|
int lua_TriggerEventG(lua_State* L) {
|
||||||
int Args = lua_gettop(L);
|
int Args = lua_gettop(L);
|
||||||
auto MaybeScript = Engine().GetScript(L);
|
auto MaybeScript = Engine().GetScript(L);
|
||||||
Assert(MaybeScript.has_value());
|
if (!MaybeScript.has_value()) {
|
||||||
|
error("TriggerGlobalEvent: There is no script associated with this lua_State.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
TLuaFile& Script = MaybeScript.value();
|
TLuaFile& Script = MaybeScript.value();
|
||||||
if (Args > 0) {
|
if (Args > 0) {
|
||||||
if (lua_isstring(L, 1)) {
|
if (lua_isstring(L, 1)) {
|
||||||
@ -194,8 +203,11 @@ void CallAsync(TLuaFile* lua, const std::string& Func, int U) {
|
|||||||
|
|
||||||
int lua_StopThread(lua_State* L) {
|
int lua_StopThread(lua_State* L) {
|
||||||
auto MaybeScript = Engine().GetScript(L);
|
auto MaybeScript = Engine().GetScript(L);
|
||||||
Assert(MaybeScript.has_value());
|
if (!MaybeScript.has_value()) {
|
||||||
// ugly, but whatever, this is safe as fuck
|
error("StopThread: There is no script associated with this lua_State.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// ugly, but whatever, this is very safe
|
||||||
MaybeScript.value().get().SetStopThread(true);
|
MaybeScript.value().get().SetStopThread(true);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -209,7 +221,10 @@ int lua_CreateThread(lua_State* L) {
|
|||||||
int U = int(lua_tointeger(L, 2));
|
int U = int(lua_tointeger(L, 2));
|
||||||
if (U > 0 && U < 501) {
|
if (U > 0 && U < 501) {
|
||||||
auto MaybeScript = Engine().GetScript(L);
|
auto MaybeScript = Engine().GetScript(L);
|
||||||
Assert(MaybeScript.has_value());
|
if (!MaybeScript.has_value()) {
|
||||||
|
error("CreateThread: There is no script associated with this lua_State.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
TLuaFile& Script = MaybeScript.value();
|
TLuaFile& Script = MaybeScript.value();
|
||||||
std::thread t1(CallAsync, &Script, STR, U);
|
std::thread t1(CallAsync, &Script, STR, U);
|
||||||
t1.detach();
|
t1.detach();
|
||||||
@ -397,7 +412,9 @@ int lua_sendChat(lua_State* L) {
|
|||||||
if (lua_isstring(L, 2)) {
|
if (lua_isstring(L, 2)) {
|
||||||
int ID = int(lua_tointeger(L, 1));
|
int ID = int(lua_tointeger(L, 1));
|
||||||
if (ID == -1) {
|
if (ID == -1) {
|
||||||
std::string Packet = "C:Server: " + std::string(lua_tostring(L, 2));
|
auto msg = std::string(lua_tostring(L, 2));
|
||||||
|
LogChatMessage("<Server> (to everyone) ", -1, msg);
|
||||||
|
std::string Packet = "C:Server: " + msg;
|
||||||
Engine().Network().SendToAll(nullptr, Packet, true, true);
|
Engine().Network().SendToAll(nullptr, Packet, true, true);
|
||||||
} else {
|
} else {
|
||||||
auto MaybeClient = GetClient(Engine().Server(), ID);
|
auto MaybeClient = GetClient(Engine().Server(), ID);
|
||||||
@ -405,7 +422,9 @@ int lua_sendChat(lua_State* L) {
|
|||||||
auto c = MaybeClient.value().lock();
|
auto c = MaybeClient.value().lock();
|
||||||
if (!c->IsSynced())
|
if (!c->IsSynced())
|
||||||
return 0;
|
return 0;
|
||||||
std::string Packet = "C:Server: " + std::string(lua_tostring(L, 2));
|
auto msg = std::string(lua_tostring(L, 2));
|
||||||
|
LogChatMessage("<Server> (to \"" + c->GetName() + "\")", -1, msg);
|
||||||
|
std::string Packet = "C:Server: " + msg;
|
||||||
Engine().Network().Respond(*c, Packet, true);
|
Engine().Network().Respond(*c, Packet, true);
|
||||||
} else
|
} else
|
||||||
SendError(Engine(), L, ("SendChatMessage invalid argument [1] invalid ID"));
|
SendError(Engine(), L, ("SendChatMessage invalid argument [1] invalid ID"));
|
||||||
|
@ -298,15 +298,17 @@ void TNetwork::Authentication(SOCKET TCPSock) {
|
|||||||
ClientKick(*Client, "Backend returned invalid auth response format.");
|
ClientKick(*Client, "Backend returned invalid auth response format.");
|
||||||
error("Backend returned invalid auth response format. This should never happen.");
|
error("Backend returned invalid auth response format. This should never happen.");
|
||||||
auto Lock = Sentry.CreateExclusiveContext();
|
auto Lock = Sentry.CreateExclusiveContext();
|
||||||
Sentry.SetExtra("response-body", Rc);
|
Sentry.SetContext("auth",
|
||||||
Sentry.SetExtra("key", RequestString);
|
{ { "response-body", Rc },
|
||||||
|
{ "key", RequestString } });
|
||||||
Sentry.SetTransaction(Application::GetBackendUrlForAuth() + Target);
|
Sentry.SetTransaction(Application::GetBackendUrlForAuth() + Target);
|
||||||
Sentry.Log(SENTRY_LEVEL_ERROR, "default", "auth: wrong backend response format");
|
Sentry.Log(SENTRY_LEVEL_ERROR, "default", "auth: wrong backend response format");
|
||||||
return;
|
return;
|
||||||
} else if (Rc == "0") {
|
} else if (Rc == "0") {
|
||||||
auto Lock = Sentry.CreateExclusiveContext();
|
auto Lock = Sentry.CreateExclusiveContext();
|
||||||
Sentry.SetExtra("response-body", Rc);
|
Sentry.SetContext("auth",
|
||||||
Sentry.SetExtra("key", RequestString);
|
{ { "response-body", Rc },
|
||||||
|
{ "key", RequestString } });
|
||||||
Sentry.SetTransaction(Application::GetBackendUrlForAuth() + Target);
|
Sentry.SetTransaction(Application::GetBackendUrlForAuth() + Target);
|
||||||
Sentry.Log(SENTRY_LEVEL_INFO, "default", "backend returned 0 instead of json");
|
Sentry.Log(SENTRY_LEVEL_INFO, "default", "backend returned 0 instead of json");
|
||||||
}
|
}
|
||||||
|
@ -49,18 +49,20 @@ void TSentry::LogError(const std::string& text, const std::string& file, const s
|
|||||||
Log(SENTRY_LEVEL_ERROR, "default", file + ": " + text);
|
Log(SENTRY_LEVEL_ERROR, "default", file + ": " + text);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TSentry::SetExtra(const std::string& key, const sentry_value_t& value) {
|
void TSentry::SetContext(const std::string& context_name, const std::unordered_map<std::string, std::string>& map) {
|
||||||
if (!mValid) {
|
if (!mValid) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
sentry_set_extra(key.c_str(), value);
|
mContext = sentry_value_new_object();
|
||||||
}
|
for (const auto& pair : map) {
|
||||||
|
std::string key = pair.first;
|
||||||
void TSentry::SetExtra(const std::string& key, const std::string& value) {
|
if (key == "type") {
|
||||||
if (!mValid) {
|
// `type` is reserved
|
||||||
return;
|
key = "_type";
|
||||||
|
}
|
||||||
|
sentry_value_set_by_key(mContext, key.c_str(), sentry_value_new_string(pair.second.c_str()));
|
||||||
}
|
}
|
||||||
SetExtra(key.c_str(), sentry_value_new_string(value.c_str()));
|
sentry_set_context(context_name.c_str(), mContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TSentry::LogException(const std::exception& e, const std::string& file, const std::string& line) {
|
void TSentry::LogException(const std::exception& e, const std::string& file, const std::string& line) {
|
||||||
@ -68,7 +70,17 @@ void TSentry::LogException(const std::exception& e, const std::string& file, con
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
SetTransaction(file + ":" + line);
|
SetTransaction(file + ":" + line);
|
||||||
Log(SENTRY_LEVEL_ERROR, "exceptions", std::string(e.what()) + " @ " + file + ":" + line);
|
Log(SENTRY_LEVEL_FATAL, "exceptions", std::string(e.what()) + " @ " + file + ":" + line);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSentry::LogAssert(const std::string& condition_string, const std::string& file, const std::string& line, const std::string& function) {
|
||||||
|
if (!mValid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SetTransaction(file + ":" + line + ":" + function);
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "\"" << condition_string << "\" failed @ " << file << ":" << line;
|
||||||
|
Log(SENTRY_LEVEL_FATAL, "asserts", ss.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void TSentry::AddErrorBreadcrumb(const std::string& msg, const std::string& file, const std::string& line) {
|
void TSentry::AddErrorBreadcrumb(const std::string& msg, const std::string& file, const std::string& line) {
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
#include <any>
|
#include <any>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
#undef GetObject //Fixes Windows
|
#undef GetObject // Fixes Windows
|
||||||
|
|
||||||
#include "Json.h"
|
#include "Json.h"
|
||||||
|
|
||||||
@ -126,6 +126,7 @@ void TServer::GlobalParser(const std::weak_ptr<TClient>& Client, std::string Pac
|
|||||||
if (Packet.length() < 4 || Packet.find(':', 3) == std::string::npos)
|
if (Packet.length() < 4 || Packet.find(':', 3) == std::string::npos)
|
||||||
break;
|
break;
|
||||||
Res = TriggerLuaEvent("onChatMessage", false, nullptr, std::make_unique<TLuaArg>(TLuaArg { { LockedClient->GetID(), LockedClient->GetName(), Packet.substr(Packet.find(':', 3) + 1) } }), true);
|
Res = TriggerLuaEvent("onChatMessage", false, nullptr, std::make_unique<TLuaArg>(TLuaArg { { LockedClient->GetID(), LockedClient->GetName(), Packet.substr(Packet.find(':', 3) + 1) } }), true);
|
||||||
|
LogChatMessage(LockedClient->GetName(), LockedClient->GetID(), Packet.substr(Packet.find(':', 3) + 1)); // FIXME: this needs to be adjusted once lua is merged
|
||||||
if (std::any_cast<int>(Res))
|
if (std::any_cast<int>(Res))
|
||||||
break;
|
break;
|
||||||
Network.SendToAll(nullptr, Packet, true, true);
|
Network.SendToAll(nullptr, Packet, true, true);
|
||||||
@ -324,9 +325,10 @@ void TServer::Apply(TClient& c, int VID, const std::string& pckt) {
|
|||||||
if (VD.empty()) {
|
if (VD.empty()) {
|
||||||
error("Tried to apply change to vehicle that does not exist");
|
error("Tried to apply change to vehicle that does not exist");
|
||||||
auto Lock = Sentry.CreateExclusiveContext();
|
auto Lock = Sentry.CreateExclusiveContext();
|
||||||
Sentry.SetExtra("packet", Packet);
|
Sentry.SetContext("vehicle-change",
|
||||||
Sentry.SetExtra("vehicle-id", std::to_string(VID));
|
{ { "packet", Packet },
|
||||||
Sentry.SetExtra("client-car-count", std::to_string(c.GetCarCount()));
|
{ "vehicle-id", std::to_string(VID) },
|
||||||
|
{ "client-car-count", std::to_string(c.GetCarCount()) } });
|
||||||
Sentry.LogError("attempt to apply change to nonexistent vehicle", _file_basename, _line);
|
Sentry.LogError("attempt to apply change to nonexistent vehicle", _file_basename, _line);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -335,7 +337,8 @@ void TServer::Apply(TClient& c, int VID, const std::string& pckt) {
|
|||||||
FoundPos = VD.find('{');
|
FoundPos = VD.find('{');
|
||||||
if (FoundPos == std::string::npos) {
|
if (FoundPos == std::string::npos) {
|
||||||
auto Lock = Sentry.CreateExclusiveContext();
|
auto Lock = Sentry.CreateExclusiveContext();
|
||||||
Sentry.SetExtra("packet", VD);
|
Sentry.SetContext("vehicle-change-packet",
|
||||||
|
{ { "packet", VD } });
|
||||||
Sentry.LogError("malformed packet", _file_basename, _line);
|
Sentry.LogError("malformed packet", _file_basename, _line);
|
||||||
error("Malformed packet received, no '{' found");
|
error("Malformed packet received, no '{' found");
|
||||||
return;
|
return;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user