diff --git a/gradle/plugins/Sentry/src/main/java/org/Mengine/Plugin/Sentry/MengineSentryPlugin.java b/gradle/plugins/Sentry/src/main/java/org/Mengine/Plugin/Sentry/MengineSentryPlugin.java index 682ad5f17a..01609850cf 100644 --- a/gradle/plugins/Sentry/src/main/java/org/Mengine/Plugin/Sentry/MengineSentryPlugin.java +++ b/gradle/plugins/Sentry/src/main/java/org/Mengine/Plugin/Sentry/MengineSentryPlugin.java @@ -6,11 +6,16 @@ import androidx.annotation.NonNull; import androidx.annotation.StringRes; +import org.Mengine.Base.MengineAnalytics; import org.Mengine.Base.MengineApplication; import org.Mengine.Base.MengineListenerApplication; import org.Mengine.Base.MengineListenerEngine; +import org.Mengine.Base.MengineListenerLogger; import org.Mengine.Base.MengineListenerTransparencyConsent; import org.Mengine.Base.MengineListenerUser; +import org.Mengine.Base.MengineLog; +import org.Mengine.Base.MengineParamLoggerException; +import org.Mengine.Base.MengineParamLoggerMessage; import org.Mengine.Base.MengineService; import org.Mengine.Base.MengineServiceInvalidInitializeException; import org.Mengine.Base.MengineParamTransparencyConsent; @@ -18,11 +23,16 @@ import java.util.Map; +import io.sentry.JsonSerializable; import io.sentry.Sentry; +import io.sentry.SentryAttribute; +import io.sentry.SentryAttributes; +import io.sentry.SentryLogLevel; import io.sentry.android.core.SentryAndroid; +import io.sentry.logger.SentryLogParameters; import io.sentry.protocol.User; -public class MengineSentryPlugin extends MengineService implements MengineListenerApplication, MengineListenerEngine, MengineListenerTransparencyConsent, MengineListenerUser { +public class MengineSentryPlugin extends MengineService implements MengineListenerApplication, MengineListenerEngine, MengineListenerTransparencyConsent, MengineListenerLogger, MengineListenerUser { public static final String SERVICE_NAME = "Sentry"; public static final boolean SERVICE_EMBEDDING = true; @@ -162,6 +172,70 @@ public void onAppState(@NonNull MengineApplication application, String name, Obj this.setCustomKey("." + name, value); } + static private SentryLogLevel getSentryLogLevel(@NonNull MengineParamLoggerMessage message) { + switch (message.MESSAGE_LEVEL) { + case MengineLog.LM_DEBUG: + return SentryLogLevel.DEBUG; + case MengineLog.LM_INFO: + return SentryLogLevel.INFO; + case MengineLog.LM_MESSAGE: + case MengineLog.LM_MESSAGE_RELEASE: + return SentryLogLevel.INFO; + case MengineLog.LM_WARNING: + return SentryLogLevel.WARN; + case MengineLog.LM_FATAL: + return SentryLogLevel.FATAL; + } + + return SentryLogLevel.TRACE; + } + + @Override + public void onMengineLog(@NonNull MengineApplication application, @NonNull MengineParamLoggerMessage message) { + if (BuildConfig.DEBUG == false) { + if (message.MESSAGE_LEVEL != MengineLog.LM_WARNING && message.MESSAGE_LEVEL != MengineLog.LM_ERROR && message.MESSAGE_LEVEL != MengineLog.LM_FATAL) { + return; + } + } + + SentryLogLevel level = MengineSentryPlugin.getSentryLogLevel(message); + + SentryAttributes attributes = SentryAttributes.of(null); + attributes.add(SentryAttribute.stringAttribute("log.category", message.MESSAGE_CATEGORY.toString())); + attributes.add(SentryAttribute.stringAttribute("log.thread", message.MESSAGE_THREAD)); + + if (message.MESSAGE_FILE != null) { + attributes.add(SentryAttribute.stringAttribute("log.file", message.MESSAGE_FILE)); + attributes.add(SentryAttribute.integerAttribute("log.line", message.MESSAGE_LINE)); + } + + if (message.MESSAGE_FUNCTION != null) { + attributes.add(SentryAttribute.stringAttribute("log.function", message.MESSAGE_FUNCTION)); + } + + Sentry.logger().log(level, SentryLogParameters.create(attributes), message.MESSAGE_DATA); + } + + @Override + public void onMengineException(@NonNull MengineApplication application, @NonNull MengineParamLoggerException exception) { + Sentry.withScope(scope -> { + scope.setExtra("log.category", exception.EXCEPTION_CATEGORY.toString()); + + for (Map.Entry entry : exception.EXCEPTION_ATTRIBUTES.entrySet()) { + String key = entry.getKey(); + Object value = entry.getValue(); + + if (value == null) { + scope.setExtra(key, "null"); + } else { + scope.setExtra(key, String.valueOf(value)); + } + } + + this.recordException(exception.EXCEPTION_THROWABLE); + }); + } + public void setCustomKey(String key, Object value) { if (value == null) { Sentry.setExtra(key, "null"); @@ -175,8 +249,6 @@ public void recordException(Throwable throwable) { , throwable.getMessage() ); - throwable.printStackTrace(System.err); - Sentry.captureException(throwable); } diff --git a/src/Plugins/AppleSentryPlugin/AppleSentryLogger.h b/src/Plugins/AppleSentryPlugin/AppleSentryLogger.h new file mode 100644 index 0000000000..3af17aad2f --- /dev/null +++ b/src/Plugins/AppleSentryPlugin/AppleSentryLogger.h @@ -0,0 +1,25 @@ +#pragma once + +#include "Kernel/LoggerBase.h" + +namespace Mengine +{ + ////////////////////////////////////////////////////////////////////////// + class AppleSentryLogger + : public LoggerBase + { + public: + AppleSentryLogger(); + ~AppleSentryLogger() override; + + protected: + bool _initializeLogger() override; + void _finalizeLogger() override; + + protected: + void _log( const LoggerRecordInterfacePtr & _record ) override; + }; + ////////////////////////////////////////////////////////////////////////// + typedef IntrusivePtr AppleSentryLoggerPtr; + ////////////////////////////////////////////////////////////////////////// +} diff --git a/src/Plugins/AppleSentryPlugin/AppleSentryLogger.mm b/src/Plugins/AppleSentryPlugin/AppleSentryLogger.mm new file mode 100644 index 0000000000..50cce41d18 --- /dev/null +++ b/src/Plugins/AppleSentryPlugin/AppleSentryLogger.mm @@ -0,0 +1,98 @@ +#include "AppleSentryLogger.h" + +#include "Interface/LoggerRecordInterface.h" +#include "Kernel/LoggerMessage.h" + +#import + +namespace Mengine +{ + ////////////////////////////////////////////////////////////////////////// + AppleSentryLogger::AppleSentryLogger() + { + } + ////////////////////////////////////////////////////////////////////////// + AppleSentryLogger::~AppleSentryLogger() + { + } + ////////////////////////////////////////////////////////////////////////// + bool AppleSentryLogger::_initializeLogger() + { + return true; + } + ////////////////////////////////////////////////////////////////////////// + void AppleSentryLogger::_finalizeLogger() + { + //Empty + } + ////////////////////////////////////////////////////////////////////////// + static SentryLevel s_getSentryLevel( ELoggerLevel _level ) + { + switch( _level ) + { + case LM_FATAL: + return kSentryLevelFatal; + case LM_ERROR: + return kSentryLevelError; + case LM_WARNING: + return kSentryLevelWarning; + case LM_INFO: + case LM_MESSAGE: + case LM_MESSAGE_RELEASE: + return kSentryLevelInfo; + case LM_DEBUG: + case LM_VERBOSE: + return kSentryLevelDebug; + default: + break; + } + + return kSentryLevelInfo; + } + ////////////////////////////////////////////////////////////////////////// + void AppleSentryLogger::_log( const LoggerRecordInterfacePtr & _record ) + { + LoggerMessage message; + _record->getMessage( &message ); + + NSString * nsMessage = [NSString stringWithUTF8String:message.data]; + NSString * nsCategory = [NSString stringWithUTF8String:message.category]; + NSString * nsThread = [NSString stringWithUTF8String:message.thread.c_str()]; + + NSString * nsFile = nil; + if( message.file != nullptr && message.file[0] != '\0' ) + { + nsFile = [NSString stringWithUTF8String:message.file]; + } + + NSString * nsFunction = nil; + if( message.function != nullptr && message.function[0] != '\0' ) + { + nsFunction = [NSString stringWithUTF8String:message.function]; + } + + SentryLevel level = s_getSentryLevel( message.level ); + + [SentrySDK captureMessage:nsMessage withScope:^(SentryScope * _Nonnull scope) { + scope.level = level; + [scope setExtraValue:nsCategory forKey:@"log.category"]; + [scope setExtraValue:nsThread forKey:@"log.thread"]; + + if( nsFile != nil ) + { + [scope setExtraValue:nsFile forKey:@"log.file"]; + + if( message.line != 0 ) + { + [scope setExtraValue:@(message.line) forKey:@"log.line"]; + } + } + + if( nsFunction != nil ) + { + [scope setExtraValue:nsFunction forKey:@"log.function"]; + } + }]; + } + ////////////////////////////////////////////////////////////////////////// +} diff --git a/src/Plugins/AppleSentryPlugin/AppleSentryService.h b/src/Plugins/AppleSentryPlugin/AppleSentryService.h index 94004b31e1..3a722da128 100644 --- a/src/Plugins/AppleSentryPlugin/AppleSentryService.h +++ b/src/Plugins/AppleSentryPlugin/AppleSentryService.h @@ -5,6 +5,7 @@ #include "Kernel/ServiceBase.h" #include "Kernel/AssertionLevel.h" #include "Kernel/ErrorLevel.h" +#include "Interface/LoggerInterface.h" namespace Mengine { @@ -23,6 +24,9 @@ namespace Mengine void notifyCreateApplication_(); void notifyAssertion_( const Char * _category, EAssertionLevel _level, const Char * _test, const Char * _file, int32_t _line, const Char * _message ); void notifyError_( const Char * _category, EErrorLevel _level, const Char * _file, int32_t _line, const Char * _message ); - void notifyEngineStop_(); + void notifyEngineStop_(); + + protected: + LoggerInterfacePtr m_logger; }; } diff --git a/src/Plugins/AppleSentryPlugin/AppleSentryService.mm b/src/Plugins/AppleSentryPlugin/AppleSentryService.mm index 464f51b443..f2372bb725 100644 --- a/src/Plugins/AppleSentryPlugin/AppleSentryService.mm +++ b/src/Plugins/AppleSentryPlugin/AppleSentryService.mm @@ -19,6 +19,8 @@ #include "Kernel/NotificationHelper.h" #include "Kernel/ConfigHelper.h" #include "Kernel/TimestampHelper.h" +#include "Kernel/FactorableUnique.h" +#include "AppleSentryLogger.h" #include "Config/StdString.h" #include "Config/StdIO.h" @@ -45,7 +47,25 @@ NOTIFICATION_ADDOBSERVERMETHOD_THIS( NOTIFICATOR_ASSERTION, &AppleSentryService::notifyAssertion_, MENGINE_DOCUMENT_FACTORABLE ); NOTIFICATION_ADDOBSERVERMETHOD_THIS( NOTIFICATOR_ERROR, &AppleSentryService::notifyError_, MENGINE_DOCUMENT_FACTORABLE ); NOTIFICATION_ADDOBSERVERMETHOD_THIS( NOTIFICATOR_ENGINE_STOP, &AppleSentryService::notifyEngineStop_, MENGINE_DOCUMENT_FACTORABLE ); - + + AppleSentryLoggerPtr loggerSentry = Helper::makeFactorableUnique( MENGINE_DOCUMENT_FACTORABLE ); + + bool debugMode = Helper::isDebugMode(); + + if( debugMode == false ) + { + loggerSentry->setVerboseLevel( LM_WARNING ); + } + + uint32_t loggerFilter = MAKE_LOGGER_FILTER( LFILTER_PROTECTED ); + loggerSentry->setVerboseFilter( loggerFilter ); + + if( LOGGER_SERVICE() + ->registerLogger( loggerSentry ) == true ) + { + m_logger = loggerSentry; + } + return true; } ////////////////////////////////////////////////////////////////////////// @@ -55,7 +75,15 @@ NOTIFICATION_REMOVEOBSERVER_THIS( NOTIFICATOR_ASSERTION ); NOTIFICATION_REMOVEOBSERVER_THIS( NOTIFICATOR_ERROR ); NOTIFICATION_REMOVEOBSERVER_THIS( NOTIFICATOR_ENGINE_STOP ); - + + if( m_logger != nullptr ) + { + LOGGER_SERVICE() + ->unregisterLogger( m_logger ); + + m_logger = nullptr; + } + [SentrySDK close]; } ////////////////////////////////////////////////////////////////////////// diff --git a/src/Plugins/AppleSentryPlugin/CMakeLists.txt b/src/Plugins/AppleSentryPlugin/CMakeLists.txt index 1afe5a627e..7dee647f98 100644 --- a/src/Plugins/AppleSentryPlugin/CMakeLists.txt +++ b/src/Plugins/AppleSentryPlugin/CMakeLists.txt @@ -6,9 +6,11 @@ src AppleSentryPlugin.h AppleSentryPlugin.mm - + AppleSentryService.h AppleSentryService.mm + AppleSentryLogger.h + AppleSentryLogger.mm ) if(MENGINE_TARGET_IOS) diff --git a/src/Plugins/Win32SentryPlugin/CMakeLists.txt b/src/Plugins/Win32SentryPlugin/CMakeLists.txt index 0c8700ca60..899ee65683 100644 --- a/src/Plugins/Win32SentryPlugin/CMakeLists.txt +++ b/src/Plugins/Win32SentryPlugin/CMakeLists.txt @@ -7,9 +7,11 @@ src Win32SentryPlugin.h Win32SentryPlugin.def Win32SentryPlugin.cpp - + Win32SentryService.h Win32SentryService.cpp + Win32SentryLogger.h + Win32SentryLogger.cpp ) add_definitions(-DSENTRY_BUILD_STATIC) @@ -35,4 +37,4 @@ TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${THIRDPARTY_LIB_DIR}/${MENGINE_LIB_PREFIX TARGET_LINK_LIBRARIES(${PROJECT_NAME} Winhttp.lib) TARGET_LINK_LIBRARIES(${PROJECT_NAME} Rpcrt4.lib) TARGET_LINK_LIBRARIES(${PROJECT_NAME} Dbghelp.lib) -TARGET_LINK_LIBRARIES(${PROJECT_NAME} Version.lib) \ No newline at end of file +TARGET_LINK_LIBRARIES(${PROJECT_NAME} Version.lib) diff --git a/src/Plugins/Win32SentryPlugin/Win32SentryLogger.cpp b/src/Plugins/Win32SentryPlugin/Win32SentryLogger.cpp new file mode 100644 index 0000000000..c6f304d2b2 --- /dev/null +++ b/src/Plugins/Win32SentryPlugin/Win32SentryLogger.cpp @@ -0,0 +1,86 @@ +#include "Win32SentryLogger.h" + +#include "Interface/LoggerRecordInterface.h" +#include "Kernel/LoggerMessage.h" + +#include "sentry.h" + +namespace Mengine +{ + ////////////////////////////////////////////////////////////////////////// + Win32SentryLogger::Win32SentryLogger() + { + } + ////////////////////////////////////////////////////////////////////////// + Win32SentryLogger::~Win32SentryLogger() + { + } + ////////////////////////////////////////////////////////////////////////// + bool Win32SentryLogger::_initializeLogger() + { + return true; + } + ////////////////////////////////////////////////////////////////////////// + void Win32SentryLogger::_finalizeLogger() + { + //Empty + } + ////////////////////////////////////////////////////////////////////////// + static sentry_level_t s_getSentryLevel( ELoggerLevel _level ) + { + switch( _level ) + { + case LM_FATAL: + return SENTRY_LEVEL_FATAL; + case LM_ERROR: + return SENTRY_LEVEL_ERROR; + case LM_WARNING: + return SENTRY_LEVEL_WARNING; + case LM_INFO: + case LM_MESSAGE: + case LM_MESSAGE_RELEASE: + return SENTRY_LEVEL_INFO; + case LM_DEBUG: + case LM_VERBOSE: + return SENTRY_LEVEL_DEBUG; + default: + break; + } + + return SENTRY_LEVEL_INFO; + } + ////////////////////////////////////////////////////////////////////////// + void Win32SentryLogger::_log( const LoggerRecordInterfacePtr & _record ) + { + LoggerMessage message; + _record->getMessage( &message ); + + sentry_level_t level = s_getSentryLevel( message.level ); + + sentry_value_t event = sentry_value_new_message_event( level, "log", message.data ); + + sentry_value_t extra = sentry_value_new_object(); + sentry_value_set_by_key( extra, "log.category", sentry_value_new_string( message.category ) ); + sentry_value_set_by_key( extra, "log.thread", sentry_value_new_string( message.thread.c_str() ) ); + + if( message.file != nullptr && message.file[0] != '\0' ) + { + sentry_value_set_by_key( extra, "log.file", sentry_value_new_string( message.file ) ); + + if( message.line != 0 ) + { + sentry_value_set_by_key( extra, "log.line", sentry_value_new_int32( message.line ) ); + } + } + + if( message.function != nullptr && message.function[0] != '\0' ) + { + sentry_value_set_by_key( extra, "log.function", sentry_value_new_string( message.function ) ); + } + + sentry_value_set_by_key( event, "extra", extra ); + + sentry_capture_event( event ); + } + ////////////////////////////////////////////////////////////////////////// +} diff --git a/src/Plugins/Win32SentryPlugin/Win32SentryLogger.h b/src/Plugins/Win32SentryPlugin/Win32SentryLogger.h new file mode 100644 index 0000000000..9bf2761962 --- /dev/null +++ b/src/Plugins/Win32SentryPlugin/Win32SentryLogger.h @@ -0,0 +1,25 @@ +#pragma once + +#include "Kernel/LoggerBase.h" + +namespace Mengine +{ + ////////////////////////////////////////////////////////////////////////// + class Win32SentryLogger + : public LoggerBase + { + public: + Win32SentryLogger(); + ~Win32SentryLogger() override; + + protected: + bool _initializeLogger() override; + void _finalizeLogger() override; + + protected: + void _log( const LoggerRecordInterfacePtr & _record ) override; + }; + ////////////////////////////////////////////////////////////////////////// + typedef IntrusivePtr Win32SentryLoggerPtr; + ////////////////////////////////////////////////////////////////////////// +} diff --git a/src/Plugins/Win32SentryPlugin/Win32SentryService.cpp b/src/Plugins/Win32SentryPlugin/Win32SentryService.cpp index 6c562cd197..e35e27750c 100644 --- a/src/Plugins/Win32SentryPlugin/Win32SentryService.cpp +++ b/src/Plugins/Win32SentryPlugin/Win32SentryService.cpp @@ -25,6 +25,8 @@ #include "Kernel/FileLogger.h" #include "Kernel/FilePathHelper.h" #include "Kernel/ContentHelper.h" +#include "Kernel/FactorableUnique.h" +#include "Win32SentryLogger.h" #include "Config/StdString.h" #include "Config/StdIO.h" @@ -313,6 +315,24 @@ namespace Mengine NOTIFICATION_ADDOBSERVERMETHOD_THIS( NOTIFICATOR_ASSERTION, &Win32SentryService::notifyAssertion_, MENGINE_DOCUMENT_FACTORABLE ); NOTIFICATION_ADDOBSERVERMETHOD_THIS( NOTIFICATOR_ERROR, &Win32SentryService::notifyError_, MENGINE_DOCUMENT_FACTORABLE ); + Win32SentryLoggerPtr loggerSentry = Helper::makeFactorableUnique( MENGINE_DOCUMENT_FACTORABLE ); + + bool debugMode = Helper::isDebugMode(); + + if( debugMode == false ) + { + loggerSentry->setVerboseLevel( LM_WARNING ); + } + + uint32_t loggerFilter = MAKE_LOGGER_FILTER( LFILTER_PROTECTED ); + loggerSentry->setVerboseFilter( loggerFilter ); + + if( LOGGER_SERVICE() + ->registerLogger( loggerSentry ) == true ) + { + m_logger = loggerSentry; + } + return true; } ////////////////////////////////////////////////////////////////////////// @@ -326,6 +346,14 @@ namespace Mengine } #endif + if( m_logger != nullptr ) + { + LOGGER_SERVICE() + ->unregisterLogger( m_logger ); + + m_logger = nullptr; + } + if( m_sentryLogger != nullptr ) { LOGGER_SERVICE() diff --git a/src/Plugins/Win32SentryPlugin/Win32SentryService.h b/src/Plugins/Win32SentryPlugin/Win32SentryService.h index 3bdcfcc980..3a01a21fdd 100644 --- a/src/Plugins/Win32SentryPlugin/Win32SentryService.h +++ b/src/Plugins/Win32SentryPlugin/Win32SentryService.h @@ -28,5 +28,6 @@ namespace Mengine protected: LoggerInterfacePtr m_sentryLogger; + LoggerInterfacePtr m_logger; }; } \ No newline at end of file