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

View File

@ -126,6 +126,8 @@ void RegisterThread(const std::string str);
} \
} while (false)
void LogChatMessage(const std::string& name, int id, const std::string& msg);
#define Biggest 30000
std::string Comp(std::string Data);
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)
#else
// In release build, these macros turn into NOPs. The compiler will optimize these out.
#define Assert(x) \
do { \
} while (false)
#define AssertNotReachable() \
do { \
} while (false)
#define Assert(cond) \
do { \
if (!result) { \
Sentry.LogAssert(#cond, _file_basename, _line, __func__); \
}
}
while (false)
#define AssertNotReachable() \
do { \
if (!result) { \
Sentry.LogAssert("code is unreachable", _file_basename, _line, __func__); \
}
}
while (false)
#endif // DEBUG

View File

@ -3,8 +3,9 @@
#include <sentry.h>
#include <string>
#include <mutex>
#include <string>
#include <unordered_map>
// TODO possibly use attach_stacktrace
@ -18,9 +19,9 @@ public:
void SetupUser();
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 SetExtra(const std::string& key, const sentry_value_t& value);
void SetExtra(const std::string& key, const std::string& value);
void SetContext(const std::string& context_name, const std::unordered_map<std::string, std::string>& map);
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);
// cleared when Logged
void SetTransaction(const std::string& id);
@ -29,6 +30,7 @@ public:
private:
bool mValid { true };
std::mutex mMutex;
sentry_value_t mContext;
};
#endif // SENTRY_H

View File

@ -4,6 +4,7 @@
#include <array>
#include <iostream>
#include <map>
#include <sstream>
#include <thread>
#include <zlib.h>
@ -86,3 +87,15 @@ std::string ThreadName() {
void RegisterThread(const std::string 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);
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()) {
// 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)
std::string KeyString(header.name_string());
std::string ValueString(header.value());
Sentry.SetExtra(KeyString, ValueString);
response_data[KeyString] = ValueString;
}
Sentry.SetContext("https-post-data", response_data);
std::stringstream result;
result << response;

View File

@ -43,8 +43,9 @@ void THeartbeatThread::operator()() {
if (T.size() > std::string("YOU_SHALL_NOT_PASS").size()
&& Application::Settings.Key.size() == 36) {
auto Lock = Sentry.CreateExclusiveContext();
Sentry.SetExtra("response-body", T);
Sentry.SetExtra("request-body", Body);
Sentry.SetContext("heartbeat",
{ { "response-body", T },
{ "request-body", Body } });
Sentry.SetTransaction(transaction);
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);
return false;
}
// What the fuck, what do we do?!
// This should never happen since it's not directly called from "userspace" Lua.
AssertNotReachable();
}
return true;
@ -127,7 +127,10 @@ bool CheckLua(lua_State* L, int r) {
int lua_RegisterEvent(lua_State* L) {
int Args = lua_gettop(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();
if (Args == 2 && lua_isstring(L, 1) && lua_isstring(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 Args = lua_gettop(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();
if (Args > 0) {
if (lua_isstring(L, 1)) {
@ -155,7 +161,10 @@ int lua_TriggerEventL(lua_State* L) {
int lua_TriggerEventG(lua_State* L) {
int Args = lua_gettop(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();
if (Args > 0) {
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) {
auto MaybeScript = Engine().GetScript(L);
Assert(MaybeScript.has_value());
// ugly, but whatever, this is safe as fuck
if (!MaybeScript.has_value()) {
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);
return 0;
}
@ -209,7 +221,10 @@ int lua_CreateThread(lua_State* L) {
int U = int(lua_tointeger(L, 2));
if (U > 0 && U < 501) {
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();
std::thread t1(CallAsync, &Script, STR, U);
t1.detach();
@ -397,7 +412,9 @@ int lua_sendChat(lua_State* L) {
if (lua_isstring(L, 2)) {
int ID = int(lua_tointeger(L, 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);
} else {
auto MaybeClient = GetClient(Engine().Server(), ID);
@ -405,7 +422,9 @@ int lua_sendChat(lua_State* L) {
auto c = MaybeClient.value().lock();
if (!c->IsSynced())
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);
} else
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.");
error("Backend returned invalid auth response format. This should never happen.");
auto Lock = Sentry.CreateExclusiveContext();
Sentry.SetExtra("response-body", Rc);
Sentry.SetExtra("key", RequestString);
Sentry.SetContext("auth",
{ { "response-body", Rc },
{ "key", RequestString } });
Sentry.SetTransaction(Application::GetBackendUrlForAuth() + Target);
Sentry.Log(SENTRY_LEVEL_ERROR, "default", "auth: wrong backend response format");
return;
} else if (Rc == "0") {
auto Lock = Sentry.CreateExclusiveContext();
Sentry.SetExtra("response-body", Rc);
Sentry.SetExtra("key", RequestString);
Sentry.SetContext("auth",
{ { "response-body", Rc },
{ "key", RequestString } });
Sentry.SetTransaction(Application::GetBackendUrlForAuth() + Target);
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);
}
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) {
return;
}
sentry_set_extra(key.c_str(), value);
}
void TSentry::SetExtra(const std::string& key, const std::string& value) {
if (!mValid) {
return;
mContext = sentry_value_new_object();
for (const auto& pair : map) {
std::string key = pair.first;
if (key == "type") {
// `type` is reserved
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) {
@ -68,7 +70,17 @@ void TSentry::LogException(const std::exception& e, const std::string& file, con
return;
}
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) {

View File

@ -7,7 +7,7 @@
#include <any>
#include <sstream>
#undef GetObject //Fixes Windows
#undef GetObject // Fixes Windows
#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)
break;
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))
break;
Network.SendToAll(nullptr, Packet, true, true);
@ -324,9 +325,10 @@ void TServer::Apply(TClient& c, int VID, const std::string& pckt) {
if (VD.empty()) {
error("Tried to apply change to vehicle that does not exist");
auto Lock = Sentry.CreateExclusiveContext();
Sentry.SetExtra("packet", Packet);
Sentry.SetExtra("vehicle-id", std::to_string(VID));
Sentry.SetExtra("client-car-count", std::to_string(c.GetCarCount()));
Sentry.SetContext("vehicle-change",
{ { "packet", Packet },
{ "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);
return;
}
@ -335,7 +337,8 @@ void TServer::Apply(TClient& c, int VID, const std::string& pckt) {
FoundPos = VD.find('{');
if (FoundPos == std::string::npos) {
auto Lock = Sentry.CreateExclusiveContext();
Sentry.SetExtra("packet", VD);
Sentry.SetContext("vehicle-change-packet",
{ { "packet", VD } });
Sentry.LogError("malformed packet", _file_basename, _line);
error("Malformed packet received, no '{' found");
return;