diff options
| author | Stefan Boberg <[email protected]> | 2021-05-11 13:05:39 +0200 |
|---|---|---|
| committer | Stefan Boberg <[email protected]> | 2021-05-11 13:05:39 +0200 |
| commit | f8d9ac5d13dd37b8b57af0478e77ba1e75c813aa (patch) | |
| tree | 1daf7621e110d48acd5e12e3073ce48ef0dd11b2 /zenserver/diag | |
| download | zen-f8d9ac5d13dd37b8b57af0478e77ba1e75c813aa.tar.xz zen-f8d9ac5d13dd37b8b57af0478e77ba1e75c813aa.zip | |
Adding zenservice code
Diffstat (limited to 'zenserver/diag')
| -rw-r--r-- | zenserver/diag/crashreport.cpp | 85 | ||||
| -rw-r--r-- | zenserver/diag/crashreport.h | 9 | ||||
| -rw-r--r-- | zenserver/diag/diagsvcs.h | 103 | ||||
| -rw-r--r-- | zenserver/diag/logging.cpp | 204 | ||||
| -rw-r--r-- | zenserver/diag/logging.h | 11 |
5 files changed, 412 insertions, 0 deletions
diff --git a/zenserver/diag/crashreport.cpp b/zenserver/diag/crashreport.cpp new file mode 100644 index 000000000..03e74ca5c --- /dev/null +++ b/zenserver/diag/crashreport.cpp @@ -0,0 +1,85 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "crashreport.h" + +#include <zencore/filesystem.h> +#include <zencore/zencore.h> + +#include <client/windows/handler/exception_handler.h> + +#include <filesystem> + +// A callback function to run after the minidump has been written. +// minidump_id is a unique id for the dump, so the minidump +// file is <dump_path>\<minidump_id>.dmp. context is the parameter supplied +// by the user as callback_context when the handler was created. exinfo +// points to the exception record, or NULL if no exception occurred. +// succeeded indicates whether a minidump file was successfully written. +// assertion points to information about an assertion if the handler was +// invoked by an assertion. +// +// If an exception occurred and the callback returns true, Breakpad will treat +// the exception as fully-handled, suppressing any other handlers from being +// notified of the exception. If the callback returns false, Breakpad will +// treat the exception as unhandled, and allow another handler to handle it. +// If there are no other handlers, Breakpad will report the exception to the +// system as unhandled, allowing a debugger or native crash dialog the +// opportunity to handle the exception. Most callback implementations +// should normally return the value of |succeeded|, or when they wish to +// not report an exception of handled, false. Callbacks will rarely want to +// return true directly (unless |succeeded| is true). +// +// For out-of-process dump generation, dump path and minidump ID will always +// be NULL. In case of out-of-process dump generation, the dump path and +// minidump id are controlled by the server process and are not communicated +// back to the crashing process. + +static bool +CrashMinidumpCallback(const wchar_t* dump_path, + const wchar_t* minidump_id, + void* context, + EXCEPTION_POINTERS* exinfo, + MDRawAssertionInfo* assertion, + bool succeeded) +{ + ZEN_UNUSED(dump_path, minidump_id, context, exinfo, assertion, succeeded); + + // TODO! + return succeeded; +} + +// A callback function to run before Breakpad performs any substantial +// processing of an exception. A FilterCallback is called before writing +// a minidump. context is the parameter supplied by the user as +// callback_context when the handler was created. exinfo points to the +// exception record, if any; assertion points to assertion information, +// if any. +// +// If a FilterCallback returns true, Breakpad will continue processing, +// attempting to write a minidump. If a FilterCallback returns false, +// Breakpad will immediately report the exception as unhandled without +// writing a minidump, allowing another handler the opportunity to handle it. + +bool +CrashFilterCallback(void* context, EXCEPTION_POINTERS* exinfo, MDRawAssertionInfo* assertion) +{ + ZEN_UNUSED(context, exinfo, assertion); + + // Yes, write a dump + return false; +} + +void +InitializeCrashReporting(const std::filesystem::path& DumpPath) +{ + // handler_types specifies the types of handlers that should be installed. + + zen::CreateDirectories(DumpPath); + + static google_breakpad::ExceptionHandler _(DumpPath.native().c_str(), // Dump path + CrashFilterCallback, // Filter Callback + CrashMinidumpCallback, // Minidump callback + nullptr, // Callback context + google_breakpad::ExceptionHandler::HANDLER_ALL // Handler Types + ); +} diff --git a/zenserver/diag/crashreport.h b/zenserver/diag/crashreport.h new file mode 100644 index 000000000..6369d1cf5 --- /dev/null +++ b/zenserver/diag/crashreport.h @@ -0,0 +1,9 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +namespace std::filesystem { +class path; +} + +void InitializeCrashReporting(const std::filesystem::path& DumpPath); diff --git a/zenserver/diag/diagsvcs.h b/zenserver/diag/diagsvcs.h new file mode 100644 index 000000000..84f8d22ee --- /dev/null +++ b/zenserver/diag/diagsvcs.h @@ -0,0 +1,103 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include <zencore/httpserver.h> +#include <zencore/iobuffer.h> + +////////////////////////////////////////////////////////////////////////// + +class HttpTestService : public zen::HttpService +{ + uint32_t LogPoint = 0; + +public: + HttpTestService() {} + ~HttpTestService() = default; + + virtual const char* BaseUri() const override { return "/test/"; } + + virtual void HandleRequest(zen::HttpServerRequest& Request) override + { + using namespace std::literals; + + auto Uri = Request.RelativeUri(); + + if (Uri == "hello"sv) + { + Request.WriteResponse(zen::HttpResponse::OK, zen::HttpContentType::kText, u8"hello world!"sv); + + // OutputLogMessageInternal(&LogPoint, 0, 0); + } + else if (Uri == "1K"sv) + { + Request.WriteResponse(zen::HttpResponse::OK, zen::HttpContentType::kBinary, m_1k); + } + else if (Uri == "1M"sv) + { + Request.WriteResponse(zen::HttpResponse::OK, zen::HttpContentType::kBinary, m_1m); + } + else if (Uri == "1M_1k"sv) + { + std::vector<zen::IoBuffer> Buffers; + Buffers.reserve(1024); + + for (int i = 0; i < 1024; ++i) + { + Buffers.push_back(m_1k); + } + + Request.WriteResponse(zen::HttpResponse::OK, zen::HttpContentType::kBinary, Buffers); + } + else if (Uri == "1G"sv) + { + std::vector<zen::IoBuffer> Buffers; + Buffers.reserve(1024); + + for (int i = 0; i < 1024; ++i) + { + Buffers.push_back(m_1m); + } + + Request.WriteResponse(zen::HttpResponse::OK, zen::HttpContentType::kBinary, Buffers); + } + else if (Uri == "1G_1k"sv) + { + std::vector<zen::IoBuffer> Buffers; + Buffers.reserve(1024 * 1024); + + for (int i = 0; i < 1024 * 1024; ++i) + { + Buffers.push_back(m_1k); + } + + Request.WriteResponse(zen::HttpResponse::OK, zen::HttpContentType::kBinary, Buffers); + } + } + +private: + zen::IoBuffer m_1m{1024 * 1024}; + zen::IoBuffer m_1k{m_1m, 0u, 1024}; +}; + +class HttpHealthService : public zen::HttpService +{ +public: + HttpHealthService() = default; + ~HttpHealthService() = default; + + virtual const char* BaseUri() const override { return "/health/"; } + + virtual void HandleRequest(zen::HttpServerRequest& Request) override + { + using namespace std::literals; + + switch (Request.RequestVerb()) + { + case zen::HttpVerb::kGet: + return Request.WriteResponse(zen::HttpResponse::OK, zen::HttpContentType::kText, u8"OK!"sv); + } + } + +private: +}; diff --git a/zenserver/diag/logging.cpp b/zenserver/diag/logging.cpp new file mode 100644 index 000000000..2bf0e50aa --- /dev/null +++ b/zenserver/diag/logging.cpp @@ -0,0 +1,204 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "logging.h" + +#include "config.h" + +#include <spdlog/pattern_formatter.h> +#include <spdlog/sinks/ansicolor_sink.h> +#include <spdlog/sinks/stdout_color_sinks.h> +#include <spdlog/spdlog.h> +#include <memory> + +// Custom logging -- test code, this should be tweaked + +namespace logging { + +using namespace spdlog; +using namespace spdlog::details; +using namespace std::literals; + +class full_formatter final : public spdlog::formatter +{ +public: + full_formatter(std::string_view LogId, std::chrono::time_point<std::chrono::system_clock> Epoch) : m_Epoch(Epoch), m_LogId(LogId) {} + + virtual std::unique_ptr<formatter> clone() const override { return std::make_unique<full_formatter>(m_LogId, m_Epoch); } + + static constexpr bool UseDate = false; + + virtual void format(const details::log_msg& msg, memory_buf_t& dest) override + { + using std::chrono::duration_cast; + using std::chrono::milliseconds; + using std::chrono::seconds; + + if constexpr (UseDate) + { + auto secs = std::chrono::duration_cast<seconds>(msg.time.time_since_epoch()); + if (secs != m_LastLogSecs) + { + m_CachedTm = os::localtime(log_clock::to_time_t(msg.time)); + m_LastLogSecs = secs; + } + } + + const auto& tm_time = m_CachedTm; + + // cache the date/time part for the next second. + auto duration = msg.time - m_Epoch; + auto secs = duration_cast<seconds>(duration); + + if (m_CacheTimestamp != secs || m_CachedDatetime.size() == 0) + { + m_CachedDatetime.clear(); + m_CachedDatetime.push_back('['); + + if constexpr (UseDate) + { + fmt_helper::append_int(tm_time.tm_year + 1900, m_CachedDatetime); + m_CachedDatetime.push_back('-'); + + fmt_helper::pad2(tm_time.tm_mon + 1, m_CachedDatetime); + m_CachedDatetime.push_back('-'); + + fmt_helper::pad2(tm_time.tm_mday, m_CachedDatetime); + m_CachedDatetime.push_back(' '); + + fmt_helper::pad2(tm_time.tm_hour, m_CachedDatetime); + m_CachedDatetime.push_back(':'); + + fmt_helper::pad2(tm_time.tm_min, m_CachedDatetime); + m_CachedDatetime.push_back(':'); + + fmt_helper::pad2(tm_time.tm_sec, m_CachedDatetime); + } + else + { + int Count = int(secs.count()); + + const int LogSecs = Count % 60; + Count /= 60; + + const int LogMins = Count % 60; + Count /= 60; + + const int LogHours = Count; + + fmt_helper::pad2(LogHours, m_CachedDatetime); + m_CachedDatetime.push_back(':'); + fmt_helper::pad2(LogMins, m_CachedDatetime); + m_CachedDatetime.push_back(':'); + fmt_helper::pad2(LogSecs, m_CachedDatetime); + } + + m_CachedDatetime.push_back('.'); + + m_CacheTimestamp = secs; + } + + dest.append(m_CachedDatetime.begin(), m_CachedDatetime.end()); + + auto millis = fmt_helper::time_fraction<milliseconds>(msg.time); + fmt_helper::pad3(static_cast<uint32_t>(millis.count()), dest); + dest.push_back(']'); + dest.push_back(' '); + + if (!m_LogId.empty()) + { + dest.push_back('['); + fmt_helper::append_string_view(m_LogId, dest); + dest.push_back(']'); + dest.push_back(' '); + } + + // append logger name if exists + if (msg.logger_name.size() > 0) + { + dest.push_back('['); + fmt_helper::append_string_view(msg.logger_name, dest); + dest.push_back(']'); + dest.push_back(' '); + } + + dest.push_back('['); + // wrap the level name with color + msg.color_range_start = dest.size(); + fmt_helper::append_string_view(level::to_string_view(msg.level), dest); + msg.color_range_end = dest.size(); + dest.push_back(']'); + dest.push_back(' '); + + // add source location if present + if (!msg.source.empty()) + { + dest.push_back('['); + const char* filename = details::short_filename_formatter<details::null_scoped_padder>::basename(msg.source.filename); + fmt_helper::append_string_view(filename, dest); + dest.push_back(':'); + fmt_helper::append_int(msg.source.line, dest); + dest.push_back(']'); + dest.push_back(' '); + } + + fmt_helper::append_string_view(msg.payload, dest); + fmt_helper::append_string_view("\n"sv, dest); + } + +private: + std::chrono::time_point<std::chrono::system_clock> m_Epoch; + std::tm m_CachedTm; + std::chrono::seconds m_LastLogSecs; + std::chrono::seconds m_CacheTimestamp{0}; + memory_buf_t m_CachedDatetime; + std::string m_LogId; +}; + +} // namespace logging + +bool +EnableVTMode() +{ + // Set output mode to handle virtual terminal sequences + HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); + if (hOut == INVALID_HANDLE_VALUE) + { + return false; + } + + DWORD dwMode = 0; + if (!GetConsoleMode(hOut, &dwMode)) + { + return false; + } + + dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; + if (!SetConsoleMode(hOut, dwMode)) + { + return false; + } + + return true; +} + +void +InitializeLogging(const ZenServerOptions& GlobalOptions) +{ + EnableVTMode(); + + auto& sinks = spdlog::default_logger()->sinks(); + sinks.clear(); + sinks.push_back(std::make_shared<spdlog::sinks::ansicolor_stdout_sink_mt>()); + spdlog::set_level(spdlog::level::debug); + spdlog::set_formatter(std::make_unique<logging::full_formatter>(GlobalOptions.LogId, std::chrono::system_clock::now())); +} + +spdlog::logger& +ConsoleLog() +{ + static auto ConLogger = spdlog::stdout_color_mt("console"); + + ConLogger->set_pattern("%v"); + + return *ConLogger; +} diff --git a/zenserver/diag/logging.h b/zenserver/diag/logging.h new file mode 100644 index 000000000..1b1813913 --- /dev/null +++ b/zenserver/diag/logging.h @@ -0,0 +1,11 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include <spdlog/spdlog.h> + +struct ZenServerOptions; + +void InitializeLogging(const ZenServerOptions& GlobalOptions); + +spdlog::logger& ConsoleLog(); |