diff options
Diffstat (limited to 'src/zencore/sentryintegration.cpp')
| -rw-r--r-- | src/zencore/sentryintegration.cpp | 327 |
1 files changed, 327 insertions, 0 deletions
diff --git a/src/zencore/sentryintegration.cpp b/src/zencore/sentryintegration.cpp new file mode 100644 index 000000000..d08fb7f1d --- /dev/null +++ b/src/zencore/sentryintegration.cpp @@ -0,0 +1,327 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include <zencore/sentryintegration.h> + +#include <zencore/config.h> +#include <zencore/logging.h> +#include <zencore/session.h> +#include <zencore/uid.h> + +#include <stdarg.h> +#include <stdio.h> + +#if ZEN_PLATFORM_LINUX +# include <pwd.h> +#endif + +#if ZEN_PLATFORM_MAC +# include <pwd.h> +#endif + +ZEN_THIRD_PARTY_INCLUDES_START +#include <spdlog/spdlog.h> +ZEN_THIRD_PARTY_INCLUDES_END + +#if ZEN_USE_SENTRY +# define SENTRY_BUILD_STATIC 1 +ZEN_THIRD_PARTY_INCLUDES_START +# include <sentry.h> +# include <spdlog/sinks/base_sink.h> +ZEN_THIRD_PARTY_INCLUDES_END + +namespace sentry { + +struct SentryAssertImpl : zen::AssertImpl +{ + virtual void ZEN_FORCENOINLINE ZEN_DEBUG_SECTION + OnAssert(const char* Filename, int LineNumber, const char* FunctionName, const char* Msg, zen::CallstackFrames* Callstack) override; +}; + +class sentry_sink final : public spdlog::sinks::base_sink<spdlog::details::null_mutex> +{ +public: + sentry_sink(); + ~sentry_sink(); + +protected: + void sink_it_(const spdlog::details::log_msg& msg) override; + void flush_() override; +}; + +////////////////////////////////////////////////////////////////////////// + +static constexpr sentry_level_t MapToSentryLevel[spdlog::level::level_enum::n_levels] = {SENTRY_LEVEL_DEBUG, + SENTRY_LEVEL_DEBUG, + SENTRY_LEVEL_INFO, + SENTRY_LEVEL_WARNING, + SENTRY_LEVEL_ERROR, + SENTRY_LEVEL_FATAL, + SENTRY_LEVEL_DEBUG}; + +sentry_sink::sentry_sink() +{ +} +sentry_sink::~sentry_sink() +{ +} + +void +sentry_sink::sink_it_(const spdlog::details::log_msg& msg) +{ + if (msg.level != spdlog::level::err && msg.level != spdlog::level::critical) + { + return; + } + try + { + std::string Message = fmt::format("{}\n{}({}) [{}]", msg.payload, msg.source.filename, msg.source.line, msg.source.funcname); + sentry_value_t event = sentry_value_new_message_event( + /* level */ MapToSentryLevel[msg.level], + /* logger */ nullptr, + /* message */ Message.c_str()); + sentry_event_value_add_stacktrace(event, NULL, 0); + sentry_capture_event(event); + } + catch (const std::exception&) + { + // If our logging with Message formatting fails we do a non-allocating version and just post the msg.payload raw + char TmpBuffer[256]; + size_t MaxCopy = zen::Min<size_t>(msg.payload.size(), size_t(255)); + memcpy(TmpBuffer, msg.payload.data(), MaxCopy); + TmpBuffer[MaxCopy] = '\0'; + sentry_value_t event = sentry_value_new_message_event( + /* level */ SENTRY_LEVEL_ERROR, + /* logger */ nullptr, + /* message */ TmpBuffer); + sentry_event_value_add_stacktrace(event, NULL, 0); + sentry_capture_event(event); + } +} +void +sentry_sink::flush_() +{ +} + +void +SentryAssertImpl::OnAssert(const char* Filename, int LineNumber, const char* FunctionName, const char* Msg, zen::CallstackFrames* Callstack) +{ + // Sentry will provide its own callstack + ZEN_UNUSED(Callstack); + try + { + std::string Message = fmt::format("ASSERT {}:({}) [{}]\n\"{}\"", Filename, LineNumber, FunctionName, Msg); + sentry_value_t event = sentry_value_new_message_event( + /* level */ SENTRY_LEVEL_ERROR, + /* logger */ nullptr, + /* message */ Message.c_str()); + sentry_event_value_add_stacktrace(event, NULL, 0); + sentry_capture_event(event); + } + catch (const std::exception&) + { + // If our logging with Message formatting fails we do a non-allocating version and just post the Msg raw + sentry_value_t event = sentry_value_new_message_event( + /* level */ SENTRY_LEVEL_ERROR, + /* logger */ nullptr, + /* message */ Msg); + sentry_event_value_add_stacktrace(event, NULL, 0); + sentry_capture_event(event); + } +} + +} // namespace sentry + +namespace zen { + +# if ZEN_USE_SENTRY +static void +SentryLogFunction(sentry_level_t Level, const char* Message, va_list Args, [[maybe_unused]] void* Userdata) +{ + char LogMessageBuffer[160]; + std::string LogMessage; + const char* MessagePtr = LogMessageBuffer; + + int n = vsnprintf(LogMessageBuffer, sizeof LogMessageBuffer, Message, Args); + + if (n >= int(sizeof LogMessageBuffer)) + { + LogMessage.resize(n + 1); + + n = vsnprintf(LogMessage.data(), LogMessage.size(), Message, Args); + + MessagePtr = LogMessage.c_str(); + } + + switch (Level) + { + case SENTRY_LEVEL_DEBUG: + ZEN_CONSOLE_DEBUG("sentry: {}", MessagePtr); + break; + + case SENTRY_LEVEL_INFO: + ZEN_CONSOLE_INFO("sentry: {}", MessagePtr); + break; + + case SENTRY_LEVEL_WARNING: + ZEN_CONSOLE_WARN("sentry: {}", MessagePtr); + break; + + case SENTRY_LEVEL_ERROR: + ZEN_CONSOLE_ERROR("sentry: {}", MessagePtr); + break; + + case SENTRY_LEVEL_FATAL: + ZEN_CONSOLE_CRITICAL("sentry: {}", MessagePtr); + break; + } +} +# endif + +SentryIntegration::SentryIntegration() +{ +} + +SentryIntegration::~SentryIntegration() +{ + if (m_IsInitialized && m_SentryErrorCode == 0) + { + logging::SetErrorLog(""); + m_SentryAssert.reset(); + sentry_close(); + } +} + +void +SentryIntegration::Initialize(std::string SentryDatabasePath, + std::string SentryAttachmentsPath, + bool AllowPII, + const std::string& CommandLine) +{ + m_AllowPII = AllowPII; + + if (SentryDatabasePath.starts_with("\\\\?\\")) + { + SentryDatabasePath = SentryDatabasePath.substr(4); + } + sentry_options_t* SentryOptions = sentry_options_new(); + sentry_options_set_dsn(SentryOptions, "https://[email protected]/5919284"); + sentry_options_set_database_path(SentryOptions, SentryDatabasePath.c_str()); + sentry_options_set_logger(SentryOptions, SentryLogFunction, this); + if (!SentryAttachmentsPath.empty()) + { + if (SentryAttachmentsPath.starts_with("\\\\?\\")) + { + SentryAttachmentsPath = SentryAttachmentsPath.substr(4); + } + sentry_options_add_attachment(SentryOptions, SentryAttachmentsPath.c_str()); + } + sentry_options_set_release(SentryOptions, ZEN_CFG_VERSION); + + // sentry_options_set_debug(SentryOptions, 1); + + m_SentryErrorCode = sentry_init(SentryOptions); + + if (m_SentryErrorCode == 0) + { + sentry_value_t SentryUserObject = sentry_value_new_object(); + + if (m_AllowPII) + { +# if ZEN_PLATFORM_WINDOWS + CHAR Buffer[511 + 1]; + DWORD BufferLength = sizeof(Buffer) / sizeof(CHAR); + BOOL OK = GetUserNameA(Buffer, &BufferLength); + if (OK && BufferLength) + { + m_SentryUserName = std::string(Buffer, BufferLength - 1); + } + BufferLength = sizeof(Buffer) / sizeof(CHAR); + OK = GetComputerNameA(Buffer, &BufferLength); + if (OK && BufferLength) + { + m_SentryHostName = std::string(Buffer, BufferLength); + } + else + { + m_SentryHostName = "unknown"; + } +# endif // ZEN_PLATFORM_WINDOWS + +# if (ZEN_PLATFORM_LINUX || ZEN_PLATFORM_MAC) + uid_t uid = geteuid(); + struct passwd* pw = getpwuid(uid); + if (pw) + { + m_SentryUserName = std::string(pw->pw_name); + } + else + { + m_SentryUserName = "unknown"; + } + char HostNameBuffer[1023 + 1]; + int err = gethostname(HostNameBuffer, sizeof(HostNameBuffer)); + if (err == 0) + { + m_SentryHostName = std::string(HostNameBuffer); + } + else + { + m_SentryHostName = "unknown"; + } +# endif + m_SentryId = fmt::format("{}@{}", m_SentryUserName, m_SentryHostName); + sentry_value_set_by_key(SentryUserObject, "id", sentry_value_new_string(m_SentryId.c_str())); + sentry_value_set_by_key(SentryUserObject, "username", sentry_value_new_string(m_SentryUserName.c_str())); + sentry_value_set_by_key(SentryUserObject, "ip_address", sentry_value_new_string("{{auto}}")); + } + + sentry_value_set_by_key(SentryUserObject, "cmd", sentry_value_new_string(CommandLine.c_str())); + + const std::string SessionId(GetSessionIdString()); + sentry_value_set_by_key(SentryUserObject, "session", sentry_value_new_string(SessionId.c_str())); + + sentry_set_user(SentryUserObject); + + m_SentryLogger = spdlog::create<sentry::sentry_sink>("sentry"); + logging::SetErrorLog("sentry"); + + m_SentryAssert = std::make_unique<sentry::SentryAssertImpl>(); + } + + m_IsInitialized = true; +} + +void +SentryIntegration::LogStartupInformation() +{ + if (m_IsInitialized) + { + if (m_SentryErrorCode == 0) + { + if (m_AllowPII) + { + ZEN_INFO("sentry initialized, username: '{}', hostname: '{}', id: '{}'", m_SentryUserName, m_SentryHostName, m_SentryId); + } + else + { + ZEN_INFO("sentry initialized with anonymous reports"); + } + } + else + { + ZEN_WARN( + "sentry_init returned failure! (error code: {}) note that sentry expects crashpad_handler to exist alongside the running " + "executable", + m_SentryErrorCode); + } + } +} + +void +SentryIntegration::ClearCaches() +{ + sentry_clear_modulecache(); +} + +} // namespace zen +#endif |