diff --git a/common/crash.cpp b/common/crash.cpp index 8fc7323e8..5d040e1fa 100644 --- a/common/crash.cpp +++ b/common/crash.cpp @@ -3,6 +3,14 @@ #include "crash.h" #include "strings.h" #include "process/process.h" +#include "http/httplib.h" +#include "http/uri.h" +#include "json/json.h" +#include "version.h" +#include "eqemu_config.h" +#include "serverinfo.h" +#include "rulesys.h" +#include "platform.h" #include @@ -10,6 +18,80 @@ #define popen _popen #endif +void SendCrashReport(const std::string &crash_report) +{ + // can configure multiple endpoints if need be + std::vector endpoints = { + "http://spire.akkadius.com/api/v1/server-crash-report", +// "http://localhost:3010/api/v1/server-crash-report", // development + }; + + auto config = EQEmuConfig::get(); + for (auto &e: endpoints) { + uri u(e); + + std::string base_url = fmt::format("{}://{}", u.get_scheme(), u.get_host()); + if (u.get_port()) { + base_url += fmt::format(":{}", u.get_port()); + } + + // client + httplib::Client r(base_url); + r.set_connection_timeout(1, 0); + r.set_read_timeout(1, 0); + r.set_write_timeout(1, 0); + httplib::Headers headers = { + {"Content-Type", "application/json"} + }; + + // os info + auto os = EQ::GetOS(); + auto cpus = EQ::GetCPUs(); + auto process_id = EQ::GetPID(); + auto rss = EQ::GetRSS() / 1048576.0; + auto uptime = static_cast(EQ::GetUptime()); + + // payload + Json::Value p; + p["platform_name"] = GetPlatformName(); + p["crash_report"] = crash_report; + p["server_version"] = CURRENT_VERSION; + p["compile_date"] = COMPILE_DATE; + p["compile_time"] = COMPILE_TIME; + p["server_name"] = config->LongName; + p["server_short_name"] = config->ShortName; + p["uptime"] = uptime; + p["os_machine"] = os.machine; + p["os_release"] = os.release; + p["os_version"] = os.version; + p["os_sysname"] = os.sysname; + p["process_id"] = process_id; + p["rss_memory"] = rss; + p["cpus"] = cpus.size(); + p["origination_info"] = ""; + + if (!LogSys.origination_info.zone_short_name.empty()) { + p["origination_info"] = fmt::format( + "{} ({}) instance_id [{}]", + LogSys.origination_info.zone_short_name, + LogSys.origination_info.zone_long_name, + LogSys.origination_info.instance_id + ); + } + + std::stringstream payload; + payload << p; + + if (auto res = r.Post(e, payload.str(), "application/json")) { + if (res->status == 200) { + LogInfo("Sent crash report"); + } + else { + LogError("Failed to send crash report to [{}]", e); + } + } + } +} #if defined(_WINDOWS) && defined(CRASH_LOGGING) #include "StackWalker.h" @@ -34,6 +116,11 @@ public: } } + if (RuleB(Analytics, CrashReporting)) { + std::string crash_report = buffer; + SendCrashReport(crash_report); + } + Log(Logs::General, Logs::Crash, buffer); StackWalker::OnOutput(szText); } @@ -181,12 +268,18 @@ void print_trace() } std::ifstream input(temp_output_file); + std::string crash_report; for (std::string line; getline(input, line);) { LogCrash("{}", line); + crash_report += fmt::format("{}\n", line); } std::remove(temp_output_file.c_str()); + if (RuleB(Analytics, CrashReporting)) { + SendCrashReport(crash_report); + } + exit(1); } diff --git a/common/ruletypes.h b/common/ruletypes.h index d30c1be98..9283142da 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -766,6 +766,10 @@ RULE_INT(Faction, DubiouslyFactionMinimum, -500, "Minimum faction for dubiously" RULE_INT(Faction, ThreateninglyFactionMinimum, -750, "Minimum faction for threateningly") RULE_CATEGORY_END() +RULE_CATEGORY(Analytics) +RULE_BOOL(Analytics, CrashReporting, true, "Automatic crash reporting analytics for EQEmu Server developers") +RULE_CATEGORY_END() + RULE_CATEGORY(Logging) RULE_BOOL(Logging, PrintFileFunctionAndLine, false, "Ex: [World Server] [net.cpp::main:309] Loading variables...") RULE_BOOL(Logging, WorldGMSayLogging, true, "Relay worldserver logging to zone processes via GM say output") diff --git a/common/version.h b/common/version.h index e6fd74e00..79ad6b81b 100644 --- a/common/version.h +++ b/common/version.h @@ -18,14 +18,22 @@ * */ -#ifndef _EQEMU_VERSION_H -#define _EQEMU_VERSION_H +#ifndef EQEMU_VERSION_H +#define EQEMU_VERSION_H -#define LOGIN_VERSION "0.8.0" #define EQEMU_PROTOCOL_VERSION "0.3.10" -#define CURRENT_VERSION "2.0" - +// Build variables +// these get injected during the build pipeline +#define CURRENT_VERSION "22.1.0-dev" // always append -dev to the current version for custom-builds +#define LOGIN_VERSION "0.8.0" +#define COMPILE_DATE __DATE__ +#define COMPILE_TIME __TIME__ +#ifndef WIN32 +#define LAST_MODIFIED __TIME__ +#else +#define LAST_MODIFIED __TIMESTAMP__ +#endif /** * Every time a Database SQL is added to Github increment CURRENT_BINARY_DATABASE_VERSION @@ -37,13 +45,5 @@ #define CURRENT_BINARY_DATABASE_VERSION 9217 #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9037 -#define COMPILE_DATE __DATE__ -#define COMPILE_TIME __TIME__ -#ifndef WIN32 - #define LAST_MODIFIED __TIME__ -#else - #define LAST_MODIFIED __TIMESTAMP__ -#endif - #endif