761 lines
24 KiB
C++
761 lines
24 KiB
C++
|
|
// Copyright (c) 2022 Sentry. All Rights Reserved.
|
||
|
|
|
||
|
|
#include "SentrySubsystem.h"
|
||
|
|
|
||
|
|
#include "SentryModule.h"
|
||
|
|
#include "SentrySettings.h"
|
||
|
|
#include "SentryBreadcrumb.h"
|
||
|
|
#include "SentryDefines.h"
|
||
|
|
#include "SentryEvent.h"
|
||
|
|
#include "SentryId.h"
|
||
|
|
#include "SentryUser.h"
|
||
|
|
#include "SentryUserFeedback.h"
|
||
|
|
#include "SentryBeforeSendHandler.h"
|
||
|
|
#include "SentryTraceSampler.h"
|
||
|
|
#include "SentryTransaction.h"
|
||
|
|
#include "SentryTransactionContext.h"
|
||
|
|
#include "SentryOutputDevice.h"
|
||
|
|
#include "SentryOutputDeviceError.h"
|
||
|
|
|
||
|
|
#include "CoreGlobals.h"
|
||
|
|
#include "Engine/World.h"
|
||
|
|
#include "Misc/EngineVersion.h"
|
||
|
|
#include "Misc/CoreDelegates.h"
|
||
|
|
#include "Misc/App.h"
|
||
|
|
#include "Misc/AssertionMacros.h"
|
||
|
|
#include "GenericPlatform/GenericPlatformDriver.h"
|
||
|
|
#include "GenericPlatform/GenericPlatformMisc.h"
|
||
|
|
|
||
|
|
#include "Interface/SentrySubsystemInterface.h"
|
||
|
|
|
||
|
|
#if PLATFORM_ANDROID
|
||
|
|
#include "Android/SentrySubsystemAndroid.h"
|
||
|
|
#elif PLATFORM_IOS || PLATFORM_MAC
|
||
|
|
#include "Apple/SentrySubsystemApple.h"
|
||
|
|
#elif PLATFORM_WINDOWS || PLATFORM_LINUX
|
||
|
|
#include "Desktop/SentrySubsystemDesktop.h"
|
||
|
|
#endif
|
||
|
|
|
||
|
|
void USentrySubsystem::Initialize(FSubsystemCollectionBase& Collection)
|
||
|
|
{
|
||
|
|
Super::Initialize(Collection);
|
||
|
|
|
||
|
|
#if PLATFORM_ANDROID
|
||
|
|
SubsystemNativeImpl = MakeShareable(new SentrySubsystemAndroid());
|
||
|
|
#elif PLATFORM_IOS || PLATFORM_MAC
|
||
|
|
SubsystemNativeImpl = MakeShareable(new SentrySubsystemApple());
|
||
|
|
#elif (PLATFORM_WINDOWS || PLATFORM_LINUX) && USE_SENTRY_NATIVE
|
||
|
|
SubsystemNativeImpl = MakeShareable(new SentrySubsystemDesktop());
|
||
|
|
#endif
|
||
|
|
|
||
|
|
const USentrySettings* Settings = FSentryModule::Get().GetSettings();
|
||
|
|
|
||
|
|
UE_LOG(LogSentrySdk, Log, TEXT("Sentry plugin auto initialization: %s"), Settings->InitAutomatically ? TEXT("true") : TEXT("false"));
|
||
|
|
|
||
|
|
if (Settings->InitAutomatically)
|
||
|
|
{
|
||
|
|
Initialize();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void USentrySubsystem::Deinitialize()
|
||
|
|
{
|
||
|
|
DisableAutomaticBreadcrumbs();
|
||
|
|
|
||
|
|
Close();
|
||
|
|
|
||
|
|
Super::Deinitialize();
|
||
|
|
}
|
||
|
|
|
||
|
|
void USentrySubsystem::Initialize()
|
||
|
|
{
|
||
|
|
if (!SubsystemNativeImpl)
|
||
|
|
{
|
||
|
|
UE_LOG(LogSentrySdk, Warning, TEXT("Sentry subsystem is invalid and can't be initialized."));
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (SubsystemNativeImpl->IsEnabled())
|
||
|
|
{
|
||
|
|
UE_LOG(LogSentrySdk, Warning, TEXT("Sentry is already initialized. It will be shut down automatically before re-init."));
|
||
|
|
Close();
|
||
|
|
}
|
||
|
|
|
||
|
|
const USentrySettings* Settings = FSentryModule::Get().GetSettings();
|
||
|
|
|
||
|
|
if(Settings->Dsn.IsEmpty())
|
||
|
|
{
|
||
|
|
UE_LOG(LogSentrySdk, Warning, TEXT("Sentry requires minimal configuration for its initialization - please provide the DSN in plugin settings."));
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
if(!IsCurrentBuildConfigurationEnabled() || !IsCurrentBuildTargetEnabled() || !IsCurrentPlatformEnabled())
|
||
|
|
{
|
||
|
|
UE_LOG(LogSentrySdk, Warning, TEXT("Sentry initialization skipped since event capturing is disabled for the current configuration/target/platform/build in plugin settings."));
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
if(IsPromotedBuildsOnlyEnabled() && !FApp::GetEngineIsPromotedBuild())
|
||
|
|
{
|
||
|
|
UE_LOG(LogSentrySdk, Warning, TEXT("Sentry initialization skipped since event capturing is disabled for the non-promoted builds in plugin settings."));
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
const UClass* BeforeSendHandlerClass = Settings->BeforeSendHandler != nullptr
|
||
|
|
? static_cast<UClass*>(Settings->BeforeSendHandler)
|
||
|
|
: USentryBeforeSendHandler::StaticClass();
|
||
|
|
|
||
|
|
BeforeSendHandler = NewObject<USentryBeforeSendHandler>(this, BeforeSendHandlerClass);
|
||
|
|
|
||
|
|
const UClass* TraceSamplerClass = Settings->TracesSampler != nullptr
|
||
|
|
? static_cast<UClass*>(Settings->TracesSampler)
|
||
|
|
: USentryTraceSampler::StaticClass();
|
||
|
|
|
||
|
|
TraceSampler = NewObject<USentryTraceSampler>(this, TraceSamplerClass);
|
||
|
|
|
||
|
|
SubsystemNativeImpl->InitWithSettings(Settings, BeforeSendHandler, TraceSampler);
|
||
|
|
|
||
|
|
if(!SubsystemNativeImpl->IsEnabled())
|
||
|
|
{
|
||
|
|
UE_LOG(LogSentrySdk, Error, TEXT("Sentry initialization failed."));
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
AddDefaultContext();
|
||
|
|
|
||
|
|
#if PLATFORM_WINDOWS || PLATFORM_LINUX || PLATFORM_MAC
|
||
|
|
AddGpuContext();
|
||
|
|
AddDeviceContext();
|
||
|
|
#endif
|
||
|
|
|
||
|
|
PromoteTags();
|
||
|
|
ConfigureBreadcrumbs();
|
||
|
|
|
||
|
|
ConfigureOutputDevice();
|
||
|
|
|
||
|
|
#if PLATFORM_WINDOWS
|
||
|
|
if (FEngineVersion::Current().GetMajor() == 5 && FEngineVersion::Current().GetMinor() >= 2)
|
||
|
|
{
|
||
|
|
if (Settings->EnableAutoCrashCapturing)
|
||
|
|
{
|
||
|
|
ConfigureOutputDeviceError();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
#else
|
||
|
|
ConfigureOutputDeviceError();
|
||
|
|
#endif
|
||
|
|
|
||
|
|
OnEnsureDelegate = FCoreDelegates::OnHandleSystemEnsure.AddLambda([this]()
|
||
|
|
{
|
||
|
|
FString EnsureMessage = GErrorHist;
|
||
|
|
SubsystemNativeImpl->CaptureEnsure(TEXT("Ensure failed"), EnsureMessage.TrimStartAndEnd());
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
void USentrySubsystem::InitializeWithSettings(const FConfigureSettingsDelegate& OnConfigureSettings)
|
||
|
|
{
|
||
|
|
USentrySettings* Settings = FSentryModule::Get().GetSettings();
|
||
|
|
|
||
|
|
OnConfigureSettings.ExecuteIfBound(Settings);
|
||
|
|
|
||
|
|
Initialize();
|
||
|
|
}
|
||
|
|
|
||
|
|
void USentrySubsystem::Close()
|
||
|
|
{
|
||
|
|
if(GLog && OutputDevice)
|
||
|
|
{
|
||
|
|
GLog->RemoveOutputDevice(OutputDevice.Get());
|
||
|
|
OutputDevice = nullptr;
|
||
|
|
}
|
||
|
|
|
||
|
|
if(GError && OutputDeviceError)
|
||
|
|
{
|
||
|
|
if(OnAssertDelegate.IsValid())
|
||
|
|
{
|
||
|
|
OutputDeviceError->OnAssert.Remove(OnAssertDelegate);
|
||
|
|
OnAssertDelegate.Reset();
|
||
|
|
}
|
||
|
|
|
||
|
|
GError = OutputDeviceError->GetParentDevice();
|
||
|
|
OutputDeviceError = nullptr;
|
||
|
|
}
|
||
|
|
|
||
|
|
if(OnEnsureDelegate.IsValid())
|
||
|
|
{
|
||
|
|
FCoreDelegates::OnHandleSystemEnsure.Remove(OnEnsureDelegate);
|
||
|
|
OnEnsureDelegate.Reset();
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!SubsystemNativeImpl || !SubsystemNativeImpl->IsEnabled())
|
||
|
|
return;
|
||
|
|
|
||
|
|
SubsystemNativeImpl->Close();
|
||
|
|
}
|
||
|
|
|
||
|
|
bool USentrySubsystem::IsEnabled()
|
||
|
|
{
|
||
|
|
if (!SubsystemNativeImpl)
|
||
|
|
return false;
|
||
|
|
|
||
|
|
return SubsystemNativeImpl->IsEnabled();
|
||
|
|
}
|
||
|
|
|
||
|
|
ESentryCrashedLastRun USentrySubsystem::IsCrashedLastRun()
|
||
|
|
{
|
||
|
|
if (!SubsystemNativeImpl || !SubsystemNativeImpl->IsEnabled())
|
||
|
|
return ESentryCrashedLastRun::NotEvaluated;
|
||
|
|
|
||
|
|
return SubsystemNativeImpl->IsCrashedLastRun();
|
||
|
|
}
|
||
|
|
|
||
|
|
void USentrySubsystem::AddBreadcrumb(USentryBreadcrumb* Breadcrumb)
|
||
|
|
{
|
||
|
|
if (!SubsystemNativeImpl || !SubsystemNativeImpl->IsEnabled())
|
||
|
|
return;
|
||
|
|
|
||
|
|
SubsystemNativeImpl->AddBreadcrumb(Breadcrumb->GetNativeImpl());
|
||
|
|
}
|
||
|
|
|
||
|
|
void USentrySubsystem::AddBreadcrumbWithParams(const FString& Message, const FString& Category, const FString& Type, const TMap<FString, FString>& Data, ESentryLevel Level)
|
||
|
|
{
|
||
|
|
if (!SubsystemNativeImpl || !SubsystemNativeImpl->IsEnabled())
|
||
|
|
return;
|
||
|
|
|
||
|
|
SubsystemNativeImpl->AddBreadcrumbWithParams(Message, Category, Type, Data, Level);
|
||
|
|
}
|
||
|
|
|
||
|
|
void USentrySubsystem::ClearBreadcrumbs()
|
||
|
|
{
|
||
|
|
if (!SubsystemNativeImpl || !SubsystemNativeImpl->IsEnabled())
|
||
|
|
return;
|
||
|
|
|
||
|
|
SubsystemNativeImpl->ClearBreadcrumbs();
|
||
|
|
}
|
||
|
|
|
||
|
|
USentryId* USentrySubsystem::CaptureMessage(const FString& Message, ESentryLevel Level)
|
||
|
|
{
|
||
|
|
if (!SubsystemNativeImpl || !SubsystemNativeImpl->IsEnabled())
|
||
|
|
return nullptr;
|
||
|
|
|
||
|
|
TSharedPtr<ISentryId> idNativeImpl = SubsystemNativeImpl->CaptureMessage(Message, Level);
|
||
|
|
|
||
|
|
USentryId* unrealId = NewObject<USentryId>();
|
||
|
|
unrealId->InitWithNativeImpl(idNativeImpl);
|
||
|
|
|
||
|
|
return unrealId;
|
||
|
|
}
|
||
|
|
|
||
|
|
USentryId* USentrySubsystem::CaptureMessageWithScope(const FString& Message, const FConfigureScopeDelegate& OnConfigureScope, ESentryLevel Level)
|
||
|
|
{
|
||
|
|
return CaptureMessageWithScope(Message, FConfigureScopeNativeDelegate::CreateUFunction(const_cast<UObject*>(OnConfigureScope.GetUObject()), OnConfigureScope.GetFunctionName()), Level);
|
||
|
|
}
|
||
|
|
|
||
|
|
USentryId* USentrySubsystem::CaptureMessageWithScope(const FString& Message, const FConfigureScopeNativeDelegate& OnConfigureScope, ESentryLevel Level)
|
||
|
|
{
|
||
|
|
if (!SubsystemNativeImpl || !SubsystemNativeImpl->IsEnabled())
|
||
|
|
return nullptr;
|
||
|
|
|
||
|
|
TSharedPtr<ISentryId> idNativeImpl = SubsystemNativeImpl->CaptureMessageWithScope(Message, FSentryScopeDelegate::CreateLambda([&](TSharedPtr<ISentryScope> nativeScope)
|
||
|
|
{
|
||
|
|
USentryScope* unrealScope = NewObject<USentryScope>();
|
||
|
|
unrealScope->InitWithNativeImpl(nativeScope);
|
||
|
|
OnConfigureScope.ExecuteIfBound(unrealScope);
|
||
|
|
}), Level);
|
||
|
|
|
||
|
|
USentryId* unrealId = NewObject<USentryId>();
|
||
|
|
unrealId->InitWithNativeImpl(idNativeImpl);
|
||
|
|
|
||
|
|
return unrealId;
|
||
|
|
}
|
||
|
|
|
||
|
|
USentryId* USentrySubsystem::CaptureEvent(USentryEvent* Event)
|
||
|
|
{
|
||
|
|
if (!SubsystemNativeImpl || !SubsystemNativeImpl->IsEnabled())
|
||
|
|
return nullptr;
|
||
|
|
|
||
|
|
TSharedPtr<ISentryId> idNativeImpl = SubsystemNativeImpl->CaptureEvent(Event->GetNativeImpl());
|
||
|
|
|
||
|
|
USentryId* unrealId = NewObject<USentryId>();
|
||
|
|
unrealId->InitWithNativeImpl(idNativeImpl);
|
||
|
|
|
||
|
|
return unrealId;
|
||
|
|
}
|
||
|
|
|
||
|
|
USentryId* USentrySubsystem::CaptureEventWithScope(USentryEvent* Event, const FConfigureScopeDelegate& OnConfigureScope)
|
||
|
|
{
|
||
|
|
return CaptureEventWithScope(Event, FConfigureScopeNativeDelegate::CreateUFunction(const_cast<UObject*>(OnConfigureScope.GetUObject()), OnConfigureScope.GetFunctionName()));
|
||
|
|
}
|
||
|
|
|
||
|
|
USentryId* USentrySubsystem::CaptureEventWithScope(USentryEvent* Event, const FConfigureScopeNativeDelegate& OnConfigureScope)
|
||
|
|
{
|
||
|
|
if (!SubsystemNativeImpl || !SubsystemNativeImpl->IsEnabled())
|
||
|
|
return nullptr;
|
||
|
|
|
||
|
|
TSharedPtr<ISentryId> idNativeImpl = SubsystemNativeImpl->CaptureEventWithScope(Event->GetNativeImpl(), FSentryScopeDelegate::CreateLambda([&](TSharedPtr<ISentryScope> nativeScope)
|
||
|
|
{
|
||
|
|
USentryScope* unrealScope = NewObject<USentryScope>();
|
||
|
|
unrealScope->InitWithNativeImpl(nativeScope);
|
||
|
|
OnConfigureScope.ExecuteIfBound(unrealScope);
|
||
|
|
}));
|
||
|
|
|
||
|
|
USentryId* unrealId = NewObject<USentryId>();
|
||
|
|
unrealId->InitWithNativeImpl(idNativeImpl);
|
||
|
|
|
||
|
|
return unrealId;
|
||
|
|
}
|
||
|
|
|
||
|
|
void USentrySubsystem::CaptureUserFeedback(USentryUserFeedback* UserFeedback)
|
||
|
|
{
|
||
|
|
if (!SubsystemNativeImpl || !SubsystemNativeImpl->IsEnabled())
|
||
|
|
return;
|
||
|
|
|
||
|
|
SubsystemNativeImpl->CaptureUserFeedback(UserFeedback->GetNativeImpl());
|
||
|
|
}
|
||
|
|
|
||
|
|
void USentrySubsystem::CaptureUserFeedbackWithParams(USentryId* EventId, const FString& Email, const FString& Comments, const FString& Name)
|
||
|
|
{
|
||
|
|
USentryUserFeedback* UserFeedback = NewObject<USentryUserFeedback>();
|
||
|
|
UserFeedback->Initialize(EventId);
|
||
|
|
UserFeedback->SetEmail(Email);
|
||
|
|
UserFeedback->SetComment(Comments);
|
||
|
|
UserFeedback->SetName(Name);
|
||
|
|
|
||
|
|
CaptureUserFeedback(UserFeedback);
|
||
|
|
}
|
||
|
|
|
||
|
|
void USentrySubsystem::SetUser(USentryUser* User)
|
||
|
|
{
|
||
|
|
if (!SubsystemNativeImpl || !SubsystemNativeImpl->IsEnabled())
|
||
|
|
return;
|
||
|
|
|
||
|
|
SubsystemNativeImpl->SetUser(User->GetNativeImpl());
|
||
|
|
}
|
||
|
|
|
||
|
|
void USentrySubsystem::RemoveUser()
|
||
|
|
{
|
||
|
|
if (!SubsystemNativeImpl || !SubsystemNativeImpl->IsEnabled())
|
||
|
|
return;
|
||
|
|
|
||
|
|
SubsystemNativeImpl->RemoveUser();
|
||
|
|
}
|
||
|
|
|
||
|
|
void USentrySubsystem::ConfigureScope(const FConfigureScopeDelegate& OnConfigureScope)
|
||
|
|
{
|
||
|
|
ConfigureScope(FConfigureScopeNativeDelegate::CreateUFunction(const_cast<UObject*>(OnConfigureScope.GetUObject()), OnConfigureScope.GetFunctionName()));
|
||
|
|
}
|
||
|
|
|
||
|
|
void USentrySubsystem::ConfigureScope(const FConfigureScopeNativeDelegate& OnConfigureScope)
|
||
|
|
{
|
||
|
|
if (!SubsystemNativeImpl || !SubsystemNativeImpl->IsEnabled())
|
||
|
|
return;
|
||
|
|
|
||
|
|
SubsystemNativeImpl->ConfigureScope(FSentryScopeDelegate::CreateLambda([&](TSharedPtr<ISentryScope> nativeScope)
|
||
|
|
{
|
||
|
|
USentryScope* unrealScope = NewObject<USentryScope>();
|
||
|
|
unrealScope->InitWithNativeImpl(nativeScope);
|
||
|
|
OnConfigureScope.ExecuteIfBound(unrealScope);
|
||
|
|
}));
|
||
|
|
}
|
||
|
|
|
||
|
|
void USentrySubsystem::SetContext(const FString& Key, const TMap<FString, FString>& Values)
|
||
|
|
{
|
||
|
|
if (!SubsystemNativeImpl || !SubsystemNativeImpl->IsEnabled())
|
||
|
|
return;
|
||
|
|
|
||
|
|
SubsystemNativeImpl->SetContext(Key, Values);
|
||
|
|
}
|
||
|
|
|
||
|
|
void USentrySubsystem::SetTag(const FString& Key, const FString& Value)
|
||
|
|
{
|
||
|
|
if (!SubsystemNativeImpl || !SubsystemNativeImpl->IsEnabled())
|
||
|
|
return;
|
||
|
|
|
||
|
|
SubsystemNativeImpl->SetTag(Key, Value);
|
||
|
|
}
|
||
|
|
|
||
|
|
void USentrySubsystem::RemoveTag(const FString& Key)
|
||
|
|
{
|
||
|
|
if (!SubsystemNativeImpl || !SubsystemNativeImpl->IsEnabled())
|
||
|
|
return;
|
||
|
|
|
||
|
|
SubsystemNativeImpl->RemoveTag(Key);
|
||
|
|
}
|
||
|
|
|
||
|
|
void USentrySubsystem::SetLevel(ESentryLevel Level)
|
||
|
|
{
|
||
|
|
if (!SubsystemNativeImpl || !SubsystemNativeImpl->IsEnabled())
|
||
|
|
return;
|
||
|
|
|
||
|
|
SubsystemNativeImpl->SetLevel(Level);
|
||
|
|
}
|
||
|
|
|
||
|
|
void USentrySubsystem::StartSession()
|
||
|
|
{
|
||
|
|
if (!SubsystemNativeImpl || !SubsystemNativeImpl->IsEnabled())
|
||
|
|
return;
|
||
|
|
|
||
|
|
SubsystemNativeImpl->StartSession();
|
||
|
|
}
|
||
|
|
|
||
|
|
void USentrySubsystem::EndSession()
|
||
|
|
{
|
||
|
|
if (!SubsystemNativeImpl || !SubsystemNativeImpl->IsEnabled())
|
||
|
|
return;
|
||
|
|
|
||
|
|
SubsystemNativeImpl->EndSession();
|
||
|
|
}
|
||
|
|
|
||
|
|
USentryTransaction* USentrySubsystem::StartTransaction(const FString& Name, const FString& Operation)
|
||
|
|
{
|
||
|
|
if (!SubsystemNativeImpl || !SubsystemNativeImpl->IsEnabled())
|
||
|
|
return nullptr;
|
||
|
|
|
||
|
|
TSharedPtr<ISentryTransaction> transactionNativeImpl = SubsystemNativeImpl->StartTransaction(Name, Operation);
|
||
|
|
|
||
|
|
USentryTransaction* unrealTransaction = NewObject<USentryTransaction>();
|
||
|
|
unrealTransaction->InitWithNativeImpl(transactionNativeImpl);
|
||
|
|
|
||
|
|
return unrealTransaction;
|
||
|
|
}
|
||
|
|
|
||
|
|
USentryTransaction* USentrySubsystem::StartTransactionWithContext(USentryTransactionContext* Context)
|
||
|
|
{
|
||
|
|
if (!SubsystemNativeImpl || !SubsystemNativeImpl->IsEnabled())
|
||
|
|
return nullptr;
|
||
|
|
|
||
|
|
TSharedPtr<ISentryTransaction> transactionNativeImpl = SubsystemNativeImpl->StartTransactionWithContext(Context->GetNativeImpl());
|
||
|
|
|
||
|
|
USentryTransaction* unrealTransaction = NewObject<USentryTransaction>();
|
||
|
|
unrealTransaction->InitWithNativeImpl(transactionNativeImpl);
|
||
|
|
|
||
|
|
return unrealTransaction;
|
||
|
|
}
|
||
|
|
|
||
|
|
USentryTransaction* USentrySubsystem::StartTransactionWithContextAndTimestamp(USentryTransactionContext* Context, int64 Timestamp)
|
||
|
|
{
|
||
|
|
if (!SubsystemNativeImpl || !SubsystemNativeImpl->IsEnabled())
|
||
|
|
return nullptr;
|
||
|
|
|
||
|
|
TSharedPtr<ISentryTransaction> transactionNativeImpl = SubsystemNativeImpl->StartTransactionWithContextAndTimestamp(Context->GetNativeImpl(), Timestamp);
|
||
|
|
|
||
|
|
USentryTransaction* unrealTransaction = NewObject<USentryTransaction>();
|
||
|
|
unrealTransaction->InitWithNativeImpl(transactionNativeImpl);
|
||
|
|
|
||
|
|
return unrealTransaction;
|
||
|
|
}
|
||
|
|
|
||
|
|
USentryTransaction* USentrySubsystem::StartTransactionWithContextAndOptions(USentryTransactionContext* Context, const TMap<FString, FString>& Options)
|
||
|
|
{
|
||
|
|
if (!SubsystemNativeImpl || !SubsystemNativeImpl->IsEnabled())
|
||
|
|
return nullptr;
|
||
|
|
|
||
|
|
TSharedPtr<ISentryTransaction> transactionNativeImpl = SubsystemNativeImpl->StartTransactionWithContextAndOptions(Context->GetNativeImpl(), Options);
|
||
|
|
|
||
|
|
USentryTransaction* unrealTransaction = NewObject<USentryTransaction>();
|
||
|
|
unrealTransaction->InitWithNativeImpl(transactionNativeImpl);
|
||
|
|
|
||
|
|
return unrealTransaction;
|
||
|
|
}
|
||
|
|
|
||
|
|
USentryTransactionContext* USentrySubsystem::ContinueTrace(const FString& SentryTrace, const TArray<FString>& BaggageHeaders)
|
||
|
|
{
|
||
|
|
if (!SubsystemNativeImpl || !SubsystemNativeImpl->IsEnabled())
|
||
|
|
return nullptr;
|
||
|
|
|
||
|
|
TSharedPtr<ISentryTransactionContext> transactionContextNativeImpl = SubsystemNativeImpl->ContinueTrace(SentryTrace, BaggageHeaders);
|
||
|
|
|
||
|
|
USentryTransactionContext* unrealTransactionContext = NewObject<USentryTransactionContext>();
|
||
|
|
unrealTransactionContext->InitWithNativeImpl(transactionContextNativeImpl);
|
||
|
|
|
||
|
|
return unrealTransactionContext;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool USentrySubsystem::IsSupportedForCurrentSettings()
|
||
|
|
{
|
||
|
|
if(!IsCurrentBuildConfigurationEnabled() || !IsCurrentBuildTargetEnabled() || !IsCurrentPlatformEnabled())
|
||
|
|
{
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
if(IsPromotedBuildsOnlyEnabled() && !FApp::GetEngineIsPromotedBuild())
|
||
|
|
{
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
void USentrySubsystem::AddDefaultContext()
|
||
|
|
{
|
||
|
|
if (!SubsystemNativeImpl || !SubsystemNativeImpl->IsEnabled())
|
||
|
|
return;
|
||
|
|
|
||
|
|
TMap<FString, FString> DefaultContext;
|
||
|
|
DefaultContext.Add(TEXT("Engine version"), FEngineVersion::Current().ToString(EVersionComponent::Changelist));
|
||
|
|
DefaultContext.Add(TEXT("Plugin version"), FSentryModule::Get().GetPluginVersion());
|
||
|
|
DefaultContext.Add(TEXT("Is Marketplace version"), FSentryModule::Get().IsMarketplaceVersion() ? TEXT("True") : TEXT("False"));
|
||
|
|
DefaultContext.Add(TEXT("Configuration"), LexToString(FApp::GetBuildConfiguration()));
|
||
|
|
DefaultContext.Add(TEXT("Target Type"), LexToString(FApp::GetBuildTargetType()));
|
||
|
|
DefaultContext.Add(TEXT("Engine mode"), FGenericPlatformMisc::GetEngineMode());
|
||
|
|
DefaultContext.Add(TEXT("Is game"), FApp::IsGame() ? TEXT("True") : TEXT("False"));
|
||
|
|
DefaultContext.Add(TEXT("Is standalone"), FApp::IsStandalone() ? TEXT("True") : TEXT("False"));
|
||
|
|
DefaultContext.Add(TEXT("Is unattended"), FApp::IsUnattended() ? TEXT("True") : TEXT("False"));
|
||
|
|
DefaultContext.Add(TEXT("Game name"), FApp::GetName());
|
||
|
|
|
||
|
|
SubsystemNativeImpl->SetContext(TEXT("Unreal Engine"), DefaultContext);
|
||
|
|
}
|
||
|
|
|
||
|
|
void USentrySubsystem::AddGpuContext()
|
||
|
|
{
|
||
|
|
if (!SubsystemNativeImpl || !SubsystemNativeImpl->IsEnabled())
|
||
|
|
return;
|
||
|
|
|
||
|
|
FGPUDriverInfo GpuDriverInfo = FPlatformMisc::GetGPUDriverInfo(FPlatformMisc::GetPrimaryGPUBrand());
|
||
|
|
|
||
|
|
TMap<FString, FString> GpuContext;
|
||
|
|
GpuContext.Add(TEXT("name"), GpuDriverInfo.DeviceDescription);
|
||
|
|
GpuContext.Add(TEXT("vendor_name"), GpuDriverInfo.ProviderName);
|
||
|
|
GpuContext.Add(TEXT("driver_version"), GpuDriverInfo.UserDriverVersion);
|
||
|
|
|
||
|
|
SubsystemNativeImpl->SetContext(TEXT("gpu"), GpuContext);
|
||
|
|
}
|
||
|
|
|
||
|
|
void USentrySubsystem::AddDeviceContext()
|
||
|
|
{
|
||
|
|
if (!SubsystemNativeImpl || !SubsystemNativeImpl->IsEnabled())
|
||
|
|
return;
|
||
|
|
|
||
|
|
const FPlatformMemoryConstants& MemoryConstants = FPlatformMemory::GetConstants();
|
||
|
|
|
||
|
|
TMap<FString, FString> DeviceContext;
|
||
|
|
DeviceContext.Add(TEXT("cpu_description"), FPlatformMisc::GetCPUBrand());
|
||
|
|
DeviceContext.Add(TEXT("number_of_cores"), FString::FromInt(FPlatformMisc::NumberOfCores()));
|
||
|
|
DeviceContext.Add(TEXT("number_of_cores_including_hyperthreads"), FString::FromInt(FPlatformMisc::NumberOfCoresIncludingHyperthreads()));
|
||
|
|
DeviceContext.Add(TEXT("physical_memory_size_gb"), FString::FromInt(MemoryConstants.TotalPhysicalGB));
|
||
|
|
|
||
|
|
SubsystemNativeImpl->SetContext(TEXT("device"), DeviceContext);
|
||
|
|
}
|
||
|
|
|
||
|
|
void USentrySubsystem::PromoteTags()
|
||
|
|
{
|
||
|
|
const USentrySettings* Settings = FSentryModule::Get().GetSettings();
|
||
|
|
|
||
|
|
if(Settings->TagsPromotion.bPromoteBuildConfiguration)
|
||
|
|
SubsystemNativeImpl->SetTag(TEXT("Configuration"), LexToString(FApp::GetBuildConfiguration()));
|
||
|
|
if(Settings->TagsPromotion.bPromoteTargetType)
|
||
|
|
SubsystemNativeImpl->SetTag(TEXT("Target Type"), LexToString(FApp::GetBuildTargetType()));
|
||
|
|
if(Settings->TagsPromotion.bPromoteEngineMode)
|
||
|
|
SubsystemNativeImpl->SetTag(TEXT("Engine Mode"), FGenericPlatformMisc::GetEngineMode());
|
||
|
|
if(Settings->TagsPromotion.bPromoteIsGame)
|
||
|
|
SubsystemNativeImpl->SetTag(TEXT("Is game"), FApp::IsGame() ? TEXT("True") : TEXT("False"));
|
||
|
|
if(Settings->TagsPromotion.bPromoteIsStandalone)
|
||
|
|
SubsystemNativeImpl->SetTag(TEXT("Is standalone"), FApp::IsStandalone() ? TEXT("True") : TEXT("False"));
|
||
|
|
if(Settings->TagsPromotion.bPromoteIsUnattended)
|
||
|
|
SubsystemNativeImpl->SetTag(TEXT("Is unattended"), FApp::IsUnattended() ? TEXT("True") : TEXT("False"));
|
||
|
|
}
|
||
|
|
|
||
|
|
void USentrySubsystem::ConfigureBreadcrumbs()
|
||
|
|
{
|
||
|
|
const USentrySettings* Settings = FSentryModule::Get().GetSettings();
|
||
|
|
|
||
|
|
if(Settings->AutomaticBreadcrumbs.bOnMapLoadingStarted)
|
||
|
|
{
|
||
|
|
PreLoadMapDelegate = FCoreUObjectDelegates::PreLoadMap.AddLambda([this](const FString& MapName)
|
||
|
|
{
|
||
|
|
AddBreadcrumbWithParams(TEXT("PreLoadMap"), TEXT("Unreal"), TEXT("Default"),
|
||
|
|
{{TEXT("Map"), MapName}}, ESentryLevel::Info);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
if(Settings->AutomaticBreadcrumbs.bOnMapLoaded)
|
||
|
|
{
|
||
|
|
PostLoadMapDelegate = FCoreUObjectDelegates::PostLoadMapWithWorld.AddLambda([this](UWorld* World)
|
||
|
|
{
|
||
|
|
if (World)
|
||
|
|
{
|
||
|
|
AddBreadcrumbWithParams(TEXT("PostLoadMapWithWorld"), TEXT("Unreal"), TEXT("Default"),
|
||
|
|
{{TEXT("Map"), World->GetMapName()}}, ESentryLevel::Info);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
AddBreadcrumbWithParams(TEXT("PostLoadMapWithWorld"), TEXT("Unreal"), TEXT("Default"),
|
||
|
|
{{TEXT("Error"), TEXT("Map load failed")}}, ESentryLevel::Error);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
if(Settings->AutomaticBreadcrumbs.bOnGameStateClassChanged)
|
||
|
|
{
|
||
|
|
GameStateChangedDelegate = FCoreDelegates::GameStateClassChanged.AddLambda([this](const FString& GameState)
|
||
|
|
{
|
||
|
|
AddBreadcrumbWithParams(TEXT("GameStateClassChanged"), TEXT("Unreal"), TEXT("Default"),
|
||
|
|
{{TEXT("GameState"), GameState}}, ESentryLevel::Info);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
if(Settings->AutomaticBreadcrumbs.bOnUserActivityStringChanged)
|
||
|
|
{
|
||
|
|
UserActivityChangedDelegate = FCoreDelegates::UserActivityStringChanged.AddLambda([this](const FString& Activity)
|
||
|
|
{
|
||
|
|
AddBreadcrumbWithParams(TEXT("UserActivityStringChanged"), TEXT("Unreal"), TEXT("Default"),
|
||
|
|
{{TEXT("Activity"), Activity}}, ESentryLevel::Info);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
if(Settings->AutomaticBreadcrumbs.bOnGameSessionIDChanged)
|
||
|
|
{
|
||
|
|
GameSessionIDChangedDelegate = FCoreDelegates::GameSessionIDChanged.AddLambda([this](const FString& SessionId)
|
||
|
|
{
|
||
|
|
AddBreadcrumbWithParams(TEXT("GameSessionIDChanged"), TEXT("Unreal"), TEXT("Default"),
|
||
|
|
{{TEXT("Session ID"), SessionId}}, ESentryLevel::Info);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void USentrySubsystem::DisableAutomaticBreadcrumbs()
|
||
|
|
{
|
||
|
|
if(PreLoadMapDelegate.IsValid())
|
||
|
|
{
|
||
|
|
FCoreUObjectDelegates::PreLoadMap.Remove(PreLoadMapDelegate);
|
||
|
|
}
|
||
|
|
|
||
|
|
if(PostLoadMapDelegate.IsValid())
|
||
|
|
{
|
||
|
|
FCoreUObjectDelegates::PostLoadMapWithWorld.Remove(PostLoadMapDelegate);
|
||
|
|
}
|
||
|
|
|
||
|
|
if(GameStateChangedDelegate.IsValid())
|
||
|
|
{
|
||
|
|
FCoreDelegates::GameStateClassChanged.Remove(GameStateChangedDelegate);
|
||
|
|
}
|
||
|
|
|
||
|
|
if(UserActivityChangedDelegate.IsValid())
|
||
|
|
{
|
||
|
|
FCoreDelegates::UserActivityStringChanged.Remove(UserActivityChangedDelegate);
|
||
|
|
}
|
||
|
|
|
||
|
|
if(GameSessionIDChangedDelegate.IsValid())
|
||
|
|
{
|
||
|
|
FCoreDelegates::GameSessionIDChanged.Remove(GameSessionIDChangedDelegate);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
bool USentrySubsystem::IsCurrentBuildConfigurationEnabled()
|
||
|
|
{
|
||
|
|
const USentrySettings* Settings = FSentryModule::Get().GetSettings();
|
||
|
|
|
||
|
|
bool IsBuildConfigurationEnabled;
|
||
|
|
|
||
|
|
switch (FApp::GetBuildConfiguration())
|
||
|
|
{
|
||
|
|
case EBuildConfiguration::Debug:
|
||
|
|
IsBuildConfigurationEnabled = Settings->EnableBuildConfigurations.bEnableDebug;
|
||
|
|
break;
|
||
|
|
case EBuildConfiguration::DebugGame:
|
||
|
|
IsBuildConfigurationEnabled = Settings->EnableBuildConfigurations.bEnableDebugGame;
|
||
|
|
break;
|
||
|
|
case EBuildConfiguration::Development:
|
||
|
|
IsBuildConfigurationEnabled = Settings->EnableBuildConfigurations.bEnableDevelopment;
|
||
|
|
break;
|
||
|
|
case EBuildConfiguration::Shipping:
|
||
|
|
IsBuildConfigurationEnabled = Settings->EnableBuildConfigurations.bEnableShipping;
|
||
|
|
break;
|
||
|
|
case EBuildConfiguration::Test:
|
||
|
|
IsBuildConfigurationEnabled = Settings->EnableBuildConfigurations.bEnableTest;
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
IsBuildConfigurationEnabled = false;
|
||
|
|
}
|
||
|
|
|
||
|
|
return IsBuildConfigurationEnabled;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool USentrySubsystem::IsCurrentBuildTargetEnabled()
|
||
|
|
{
|
||
|
|
const USentrySettings* Settings = FSentryModule::Get().GetSettings();
|
||
|
|
|
||
|
|
bool IsBuildTargetTypeEnabled;
|
||
|
|
|
||
|
|
switch (FApp::GetBuildTargetType())
|
||
|
|
{
|
||
|
|
case EBuildTargetType::Game:
|
||
|
|
IsBuildTargetTypeEnabled = Settings->EnableBuildTargets.bEnableGame;
|
||
|
|
break;
|
||
|
|
case EBuildTargetType::Server:
|
||
|
|
IsBuildTargetTypeEnabled = Settings->EnableBuildTargets.bEnableServer;
|
||
|
|
break;
|
||
|
|
case EBuildTargetType::Client:
|
||
|
|
IsBuildTargetTypeEnabled = Settings->EnableBuildTargets.bEnableClient;
|
||
|
|
break;
|
||
|
|
case EBuildTargetType::Editor:
|
||
|
|
IsBuildTargetTypeEnabled = Settings->EnableBuildTargets.bEnableEditor;
|
||
|
|
break;
|
||
|
|
case EBuildTargetType::Program:
|
||
|
|
IsBuildTargetTypeEnabled = Settings->EnableBuildTargets.bEnableProgram;
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
IsBuildTargetTypeEnabled = false;
|
||
|
|
}
|
||
|
|
|
||
|
|
return IsBuildTargetTypeEnabled;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool USentrySubsystem::IsCurrentPlatformEnabled()
|
||
|
|
{
|
||
|
|
const USentrySettings* Settings = FSentryModule::Get().GetSettings();
|
||
|
|
|
||
|
|
bool IsBuildPlatformEnabled = false;
|
||
|
|
|
||
|
|
#if PLATFORM_LINUX
|
||
|
|
IsBuildPlatformEnabled = Settings->EnableBuildPlatforms.bEnableLinux;
|
||
|
|
#elif PLATFORM_IOS
|
||
|
|
IsBuildPlatformEnabled = Settings->EnableBuildPlatforms.bEnableIOS;
|
||
|
|
#elif PLATFORM_WINDOWS
|
||
|
|
IsBuildPlatformEnabled = Settings->EnableBuildPlatforms.bEnableWindows;
|
||
|
|
#elif PLATFORM_ANDROID
|
||
|
|
IsBuildPlatformEnabled = Settings->EnableBuildPlatforms.bEnableAndroid;
|
||
|
|
#elif PLATFORM_MAC
|
||
|
|
IsBuildPlatformEnabled = Settings->EnableBuildPlatforms.bEnableMac;
|
||
|
|
#endif
|
||
|
|
|
||
|
|
return IsBuildPlatformEnabled;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool USentrySubsystem::IsPromotedBuildsOnlyEnabled()
|
||
|
|
{
|
||
|
|
const USentrySettings* Settings = FSentryModule::Get().GetSettings();
|
||
|
|
|
||
|
|
return Settings->EnableForPromotedBuildsOnly;
|
||
|
|
}
|
||
|
|
|
||
|
|
void USentrySubsystem::ConfigureOutputDevice()
|
||
|
|
{
|
||
|
|
OutputDevice = MakeShareable(new FSentryOutputDevice());
|
||
|
|
if (OutputDevice)
|
||
|
|
{
|
||
|
|
GLog->AddOutputDevice(OutputDevice.Get());
|
||
|
|
GLog->SerializeBacklog(OutputDevice.Get());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void USentrySubsystem::ConfigureOutputDeviceError()
|
||
|
|
{
|
||
|
|
OutputDeviceError = MakeShareable(new FSentryOutputDeviceError(GError));
|
||
|
|
if (OutputDeviceError)
|
||
|
|
{
|
||
|
|
OnAssertDelegate = OutputDeviceError->OnAssert.AddLambda([this](const FString& Message)
|
||
|
|
|
||
|
|
{
|
||
|
|
SubsystemNativeImpl->CaptureAssertion(TEXT("Assertion failed"), Message);
|
||
|
|
|
||
|
|
// Shut things down before exiting to ensure all the outgoing events are sent to Sentry
|
||
|
|
Close();
|
||
|
|
|
||
|
|
FPlatformMisc::RequestExit( true);
|
||
|
|
});
|
||
|
|
|
||
|
|
GError = OutputDeviceError.Get();
|
||
|
|
}
|
||
|
|
}
|