/** * @file SentryWrapper.cpp * @brief Sentry crash reporting wrapper implementation for cross-platform support. * * This implementation provides a unified API for Sentry integration. * When SLIC3R_SENTRY is not defined, all functions become no-ops. */ #include "SentryWrapper.hpp" #ifdef SLIC3R_SENTRY #include "sentry.h" #endif #ifdef _WIN32 #include #include #endif #ifdef __APPLE__ #include #include #include #include #endif #include #include #include #include "common_func/common_func.hpp" namespace Slic3r { #ifdef SLIC3R_SENTRY #define SENTRY_EVENT_TRACE "trace" #define SENTRY_EVENT_DEBUG "info" #define SENTRY_EVENT_INFO "debug" #define SENTRY_EVENT_WARNING "warning" #define SENTRY_EVENT_ERROR "error" #define SENTRY_EVENT_FATAL "fatal" #define MACHINE_MODULE "Moonraker_Mqtt" #define SENTRY_KEY_LEVEL "level" static sentry_value_t on_crash_callback(const sentry_ucontext_t* uctx, sentry_value_t event, void* closure) { (void) uctx; (void) closure; // tell the backend to retain the event return event; } static sentry_value_t before_send(sentry_value_t event, void* hint, void* data) { sentry_value_t level_val = sentry_value_get_by_key(event, SENTRY_KEY_LEVEL); std::string levelName = sentry_value_as_string(level_val); std::string eventLevel = sentry_value_as_string(sentry_value_get_by_key(event, SENTRY_KEY_LEVEL)); //module name sentry_value_t moduleValue = sentry_value_get_by_key(event, "logger"); std::string moduleName = sentry_value_as_string(moduleValue); if (MACHINE_MODULE == moduleName) { srand((unsigned int) time(0)); int random_num = rand() % 100; int randNumber = rand() % 100 + 1; if (randNumber < 85) { sentry_value_decref(event); return sentry_value_new_null(); } else { return event; } } if (!get_privacy_policy() && levelName == SENTRY_EVENT_TRACE) { sentry_value_decref(event); return sentry_value_new_null(); } if (SENTRY_EVENT_FATAL == eventLevel || SENTRY_EVENT_ERROR == eventLevel || SENTRY_EVENT_TRACE == eventLevel) { return event; } else if (SENTRY_EVENT_WARNING == eventLevel) { srand((unsigned int) time(0)); int random_num = rand() % 100; int randNumber = rand() % 100 + 1; if (randNumber > 5) { sentry_value_decref(event); return sentry_value_new_null(); } else { return event; } } //info trace debug not report sentry_value_decref(event); return sentry_value_new_null(); } void initSentryEx() { sentry_options_t* options = sentry_options_new(); std::string dsn = std::string("https://c74b617c2aedc291444d3a238d23e780@o4508125599563776.ingest.us.sentry.io/4510425163956224"); { #ifdef __APPLE__ #elif _WIN32 #endif sentry_options_set_dsn(options, dsn.c_str()); std::string handlerDir = ""; std::string dataBaseDir = ""; #ifdef __APPLE__ char exe_path[PATH_MAX] = {0}; uint32_t buf_size = PATH_MAX; if (_NSGetExecutablePath(exe_path, &buf_size) != 0) { throw std::runtime_error("Buffer too small for executable path"); } // Get the directory containing the executable, not the executable path itself // Use dirname() to get parent directory (need to copy string as dirname may modify it) char exe_path_copy[PATH_MAX]; strncpy(exe_path_copy, exe_path, PATH_MAX); char* exe_dir = dirname(exe_path_copy); handlerDir = std::string(exe_dir) + "/crashpad_handler"; const char* home_env = getenv("HOME"); dataBaseDir = home_env; dataBaseDir = dataBaseDir + "/Library/Application Support/Snapmaker_Orca/SentryData"; #elif _WIN32 wchar_t exeDir[MAX_PATH]; ::GetModuleFileNameW(nullptr, exeDir, MAX_PATH); std::wstring wsExeDir(exeDir); int nPos = wsExeDir.find_last_of('\\'); std::wstring wsDmpDir = wsExeDir.substr(0, nPos + 1); std::wstring desDir = wsDmpDir + L"crashpad_handler.exe"; wsDmpDir += L"dump"; auto wstringTostring = [](std::wstring wTmpStr) -> std::string { std::string resStr = std::string(); int len = WideCharToMultiByte(CP_UTF8, 0, wTmpStr.c_str(), -1, nullptr, 0, nullptr, nullptr); if (len <= 0) return std::string(); std::string desStr(len, 0); WideCharToMultiByte(CP_UTF8, 0, wTmpStr.c_str(), -1, &desStr[0], len, nullptr, nullptr); resStr = desStr; return resStr; }; handlerDir = wstringTostring(desDir); wchar_t appDataPath[MAX_PATH] = {0}; auto hr = SHGetFolderPathW(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, appDataPath); char* path = new char[MAX_PATH]; size_t pathLength; wcstombs_s(&pathLength, path, MAX_PATH, appDataPath, MAX_PATH); std::string filePath = path; std::string appName = "\\" + std::string("Snapmaker_Orca\\"); dataBaseDir = filePath + appName; #endif if (!handlerDir.empty()) sentry_options_set_handler_path(options, handlerDir.c_str()); if (!dataBaseDir.empty()) sentry_options_set_database_path(options, dataBaseDir.c_str()); #if defined(_DEBUG) || !defined(NDEBUG) sentry_options_set_debug(options, 1); #else sentry_options_set_debug(options, 0); #endif // sentry_options_set_environment(options, "develop"); sentry_options_set_environment(options, "Release"); sentry_options_set_auto_session_tracking(options, 0); sentry_options_set_symbolize_stacktraces(options, 1); sentry_options_set_on_crash(options, on_crash_callback, NULL); sentry_options_set_before_send(options, before_send, NULL); sentry_options_set_sample_rate(options, 1.0); sentry_options_set_traces_sample_rate(options, 1.0); sentry_init(options); sentry_start_session(); sentry_set_tag("snapmaker_version", Snapmaker_VERSION); std::string flutterVersion = common::get_flutter_version(); if (!flutterVersion.empty()) sentry_set_tag("flutter_version", flutterVersion.c_str()); std::string machineID = common::getMachineId(); if (!machineID.empty()) sentry_set_tag("machine_id", machineID.c_str()); std::string pcName = common::get_pc_name(); if (!pcName.empty()) sentry_set_tag("pc_name", pcName.c_str()); } } void exitSentryEx() { sentry_close(); } void sentryReportLogEx(SENTRY_LOG_LEVEL logLevel, const std::string& logContent, const std::string& funcModule, const std::string& logTagKey, const std::string& logTagValue, const std::string& logTraceId) { sentry_level_t sentry_msg_level; switch (logLevel) { case SENTRY_LOG_TRACE: sentry_msg_level = SENTRY_LEVEL_TRACE; break; case SENTRY_LOG_DEBUG: sentry_msg_level = SENTRY_LEVEL_DEBUG; break; case SENTRY_LOG_INFO: sentry_msg_level = SENTRY_LEVEL_INFO; break; case SENTRY_LOG_WARNING: sentry_msg_level = SENTRY_LEVEL_WARNING; break; case SENTRY_LOG_ERROR: sentry_msg_level = SENTRY_LEVEL_ERROR; break; case SENTRY_LOG_FATAL: sentry_msg_level = SENTRY_LEVEL_FATAL; break; default: return; } sentry_value_t event = sentry_value_new_message_event(sentry_msg_level, funcModule.c_str(), logContent.c_str() ); sentry_value_t tags = sentry_value_new_object(); if (!logTraceId.empty()) sentry_value_set_by_key(tags, "snapmaker_trace_id", sentry_value_new_string(logTraceId.c_str())); if (SENTRY_LEVEL_TRACE == sentry_msg_level) sentry_value_set_by_key(tags, BURY_POINT, sentry_value_new_string("snapmaker_bury_point")); if (!logTagKey.empty()) sentry_value_set_by_key(tags, logTagKey.c_str(), sentry_value_new_string(logTagValue.c_str())); sentry_value_set_by_key(event, "snapmaker_tags", tags); sentry_capture_event(event); } #else // SLIC3R_SENTRY not defined - provide no-op implementations #endif // SLIC3R_SENTRY void initSentry() { #ifdef SLIC3R_SENTRY initSentryEx(); #endif } void exitSentry() { #ifdef SLIC3R_SENTRY exitSentryEx(); #endif } void sentryReportLog(SENTRY_LOG_LEVEL logLevel, const std::string& logContent, const std::string& funcModule, const std::string& logTagKey, const std::string& logTagValue, const std::string& logTraceId) { #ifdef SLIC3R_SENTRY sentryReportLogEx(logLevel, logContent, funcModule, logTagKey, logTagValue, logTraceId); #endif } void set_sentry_tags(const std::string& tag_key, const std::string& tag_value) { #ifdef SLIC3R_SENTRY if (!tag_key.empty()) sentry_set_tag(tag_key.c_str(), tag_value.c_str()); #endif } } // namespace Slic3r