Sentry: add multiple more logging mechanisms, add [CHAT]

This commit is contained in:
Lion Kortlepel 2021-08-11 14:12:47 +02:00 committed by Lion
parent 5330013dc3
commit 8fada3ac04
11 changed files with 106 additions and 42 deletions

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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());
}

View File

@ -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;

View File

@ -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");
} }

View File

@ -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"));

View File

@ -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");
} }

View File

@ -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) {

View File

@ -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;