Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
- Remove `EnableTargetPlatforms` from plugin settings ([#809](https://github.com/getsentry/sentry-unreal/pull/809))
- Remove Native SDK build from source in Unreal ([#808](https://github.com/getsentry/sentry-unreal/pull/808))

### Features

- Add callback to pre-process breadcrumbs before adding ([#814](https://github.com/getsentry/sentry-unreal/pull/814))

### Dependencies

- Bump Cocoa SDK (iOS and Mac) from v8.45.0 to v8.46.0 ([#810](https://github.com/getsentry/sentry-unreal/pull/810))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
#include "Dom/JsonObject.h"
#include "Serialization/JsonSerializer.h"

void FAndroidSentrySubsystem::InitWithSettings(const USentrySettings* settings, USentryBeforeSendHandler* beforeSendHandler, USentryTraceSampler* traceSampler)
void FAndroidSentrySubsystem::InitWithSettings(const USentrySettings* settings, USentryBeforeSendHandler* beforeSendHandler, USentryBeforeBreadcrumbHandler* beforeBreadcrumbHandler, USentryTraceSampler* traceSampler)
{
TSharedPtr<FJsonObject> SettingsJson = MakeShareable(new FJsonObject);
SettingsJson->SetStringField(TEXT("dsn"), settings->Dsn);
Expand All @@ -55,6 +55,10 @@ void FAndroidSentrySubsystem::InitWithSettings(const USentrySettings* settings,
{
SettingsJson->SetNumberField(TEXT("tracesSampler"), (jlong)traceSampler);
}
if(beforeBreadcrumbHandler != nullptr)
{
SettingsJson->SetNumberField(TEXT("beforeBreadcrumb"), (jlong)beforeBreadcrumbHandler);
}

FString SettingsJsonStr;
TSharedRef<TJsonWriter<>> JsonWriter = TJsonWriterFactory<>::Create(&SettingsJsonStr);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
class FAndroidSentrySubsystem : public ISentrySubsystem
{
public:
virtual void InitWithSettings(const USentrySettings* settings, USentryBeforeSendHandler* beforeSendHandler, USentryTraceSampler* traceSampler) override;
virtual void InitWithSettings(const USentrySettings* settings, USentryBeforeSendHandler* beforeSendHandler, USentryBeforeBreadcrumbHandler* beforeBreadcrumbHandler, USentryTraceSampler* traceSampler) override;
virtual void Close() override;
virtual bool IsEnabled() override;
virtual ESentryCrashedLastRun IsCrashedLastRun() override;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
public class SentryBridgeJava {
public static native void onConfigureScope(long callbackAddr, IScope scope);
public static native SentryEvent onBeforeSend(long handlerAddr, SentryEvent event, Hint hint);
public static native Breadcrumb onBeforeBreadcrumb(long handlerAddr, Breadcrumb breadcrumb, Hint hint);
public static native float onTracesSampler(long samplerAddr, SamplingContext samplingContext);

public static void init(Activity activity, final String settingsJsonStr, final long beforeSendHandler) {
Expand Down Expand Up @@ -89,6 +90,15 @@ public Double sample(SamplingContext samplingContext) {
}
});
}
if(settingJson.has("beforeBreadcrumb")) {
final long beforeBreadcrumbAddr = settingJson.getLong("beforeBreadcrumb");
options.setBeforeBreadcrumb(new SentryOptions.BeforeBreadcrumbCallback() {
@Override
public Breadcrumb execute(Breadcrumb breadcrumb, Hint hint) {
return onBeforeBreadcrumb(beforeBreadcrumbAddr, breadcrumb, hint);
}
});
}
} catch (JSONException e) {
throw new RuntimeException(e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,19 @@
#include "Android/Infrastructure/SentryJavaClasses.h"
#include "Android/AndroidSentrySubsystem.h"
#include "Android/SentryScopeAndroid.h"
#include "Android/SentryBreadcrumbAndroid.h"
#include "Android/SentryEventAndroid.h"
#include "Android/SentryHintAndroid.h"
#include "Android/SentrySamplingContextAndroid.h"

#include "Android/AndroidJNI.h"

#include "SentryDefines.h"
#include "SentryBreadcrumb.h"
#include "SentryEvent.h"
#include "SentryHint.h"
#include "SentryBeforeSendHandler.h"
#include "SentryBeforeBreadcrumbHandler.h"
#include "SentryTraceSampler.h"
#include "SentrySamplingContext.h"
#include "UObject/GarbageCollection.h"
Expand Down Expand Up @@ -51,6 +54,34 @@ JNI_METHOD jobject Java_io_sentry_unreal_SentryBridgeJava_onBeforeSend(JNIEnv* e
return ProcessedEvent ? event : nullptr;
}

JNI_METHOD jobject Java_io_sentry_unreal_SentryBridgeJava_onBeforeBreadcrumb(JNIEnv* env, jclass clazz, jlong objAddr, jobject breadcrumb, jobject hint)
{
if (!FTaskTagScope::IsCurrentTag(ETaskTag::EGameThread))
{
// Executing `onBeforeBreadcrumb` handler is not allowed when called from Android main thread.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

running on the main thread will be quite common. Why is it not allowed?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This causes issues when wrapping Android jobject instances in Unreal since USentryBreadcrumb and USentryHint (both derived from UObject) can only be safely created on the GameThread.

Despite certain breadcrumb types (i.e. Android activity and app lifecycle callbacks) originating from the main thread and lacking pre-processing support they will still be included in captured events.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's the behavior a user can expect? some crumbs will go through the callback and some won't?
it just seems like a surprising behavior.

"why can't I drop these breadcrumbs through beforeBreadcrumb?"

Should we avoid adding a callback altogether then?
Or how do we avoid surprises/disapointments?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bruno-garcia The original error message I've seen while testing this led me to believe that calling the beforeBreadcrumb hook outside the game thread on Android was the issue but it turned out to be misleading. Similar to beforeSend (#860) the real limitation is that we can't call it during GC otherwise it works as expected.

I've added this limitation to the README and will also ensure it's mentioned in the documentation as @bitsandfoxes suggested.

// Don't print to logs within `onBeforeBreadcrumb` handler as this can lead to creating new breadcrumb
return breadcrumb;
}

FGCScopeGuard GCScopeGuard;

if (FUObjectThreadContext::Get().IsRoutingPostLoad)
{
// Executing `onBeforeBreadcrumb` handler is not allowed when post-loading.
// Don't print to logs within `onBeforeBreadcrumb` handler as this can lead to creating new breadcrumb
return breadcrumb;
}

USentryBeforeBreadcrumbHandler* handler = reinterpret_cast<USentryBeforeBreadcrumbHandler*>(objAddr);

USentryBreadcrumb* BreadcrumbToProcess = USentryBreadcrumb::Create(MakeShareable(new SentryBreadcrumbAndroid(breadcrumb)));
USentryHint* HintToProcess = USentryHint::Create(MakeShareable(new SentryHintAndroid(hint)));

USentryBreadcrumb* ProcessedBreadcrumb = handler->HandleBeforeBreadcrumb(BreadcrumbToProcess, HintToProcess);

return ProcessedBreadcrumb ? breadcrumb : nullptr;
}

JNI_METHOD jfloat Java_io_sentry_unreal_SentryBridgeJava_onTracesSampler(JNIEnv* env, jclass clazz, jlong objAddr, jobject samplingContext)
{
FGCScopeGuard GCScopeGuard;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@
#include "SentrySamplingContextApple.h"
#include "SentryIdApple.h"

#include "SentryBreadcrumb.h
#include "SentryEvent.h"
#include "SentrySettings.h"
#include "SentryBeforeSendHandler.h"
#include "SentryBeforeBreadcrumbHandler.h"
#include "SentryDefines.h"
#include "SentrySamplingContext.h"
#include "SentryTraceSampler.h"
Expand All @@ -30,7 +32,7 @@
#include "UObject/UObjectThreadContext.h"
#include "Utils/SentryLogUtils.h"

void FAppleSentrySubsystem::InitWithSettings(const USentrySettings* settings, USentryBeforeSendHandler* beforeSendHandler, USentryTraceSampler* traceSampler)
void FAppleSentrySubsystem::InitWithSettings(const USentrySettings* settings, USentryBeforeSendHandler* beforeSendHandler, USentryBeforeBreadcrumbHandler* beforeBreadcrumbHandler, USentryTraceSampler* traceSampler)
{
[SENTRY_APPLE_CLASS(PrivateSentrySDKOnly) setSdkName:@"sentry.cocoa.unreal"];

Expand Down Expand Up @@ -98,6 +100,25 @@ void FAppleSentrySubsystem::InitWithSettings(const USentrySettings* settings, US
return traceSampler->Sample(Context, samplingValue) ? [NSNumber numberWithFloat:samplingValue] : nil;
};
}
if (beforeBreadcrumbHandler != nullptr)
{
options.beforeBreadcrumb = ^SentryBreadcrumb* (SentryBreadcrumb* breadcrumb) {
FGCScopeGuard GCScopeGuard;

if (FUObjectThreadContext::Get().IsRoutingPostLoad)
{
// Executing `onBeforeBreadcrumb` handler is not allowed when post-loading.
// Don't print to logs within `onBeforeBreadcrumb` handler as this can lead to creating new breadcrumb
return breadcrumb;
}

USentryBreadcrumb* BreadcrumbToProcess = USentryBreadcrumb::Create(MakeShareable(new SentryBreadcrumbApple(breadcrumb)));

USentryBreadcrumb* ProcessedBreadcrumb = beforeBreadcrumbHandler->HandleBeforeBreadcrumb(BreadcrumbToProcess, nullptr);

return ProcessedBreadcrumb ? breadcrumb : nullptr;
};
}
}];

dispatch_group_leave(sentryDispatchGroup);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
class FAppleSentrySubsystem : public ISentrySubsystem
{
public:
virtual void InitWithSettings(const USentrySettings* settings, USentryBeforeSendHandler* beforeSendHandler, USentryTraceSampler* traceSampler) override;
virtual void InitWithSettings(const USentrySettings* settings, USentryBeforeSendHandler* beforeSendHandler, USentryBeforeBreadcrumbHandler* beforeBreadcrumbHandler, USentryTraceSampler* traceSampler) override;
virtual void Close() override;
virtual bool IsEnabled() override;
virtual ESentryCrashedLastRun IsCrashedLastRun() override;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
#include "SentryEvent.h"
#include "SentryModule.h"
#include "SentryBeforeSendHandler.h"
#include "SentryBeforeBreadcrumbHandler.h"
#include "SentryBreadcrumb.h"

#include "SentryTraceSampler.h"

Expand Down Expand Up @@ -77,6 +79,18 @@ void PrintVerboseLog(sentry_level_t level, const char* message, va_list args, vo
}
}

/* static */ sentry_value_t FGenericPlatformSentrySubsystem::HandleBeforeBreadcrumb(sentry_value_t breadcrumb, void* hint, void* closure)
{
if (closure)
{
return StaticCast<FGenericPlatformSentrySubsystem*>(closure)->OnBeforeBreadcrumb(breadcrumb, hint, closure);
}
else
{
return breadcrumb;
}
}

/* static */ sentry_value_t FGenericPlatformSentrySubsystem::HandleOnCrash(const sentry_ucontext_t* uctx, sentry_value_t event, void* closure)
{
if (closure)
Expand Down Expand Up @@ -104,17 +118,45 @@ sentry_value_t FGenericPlatformSentrySubsystem::OnBeforeSend(sentry_value_t even

if (FUObjectThreadContext::Get().IsRoutingPostLoad)
{
UE_LOG(LogSentrySdk, Log, TEXT("Executing `beforeSend` handler is not allowed when post-loading."));
// Executing `onBeforeBreadcrumb` handler is not allowed when post-loading.
// Don't print to logs within `onBeforeBreadcrumb` handler as this can lead to creating new breadcrumb
return event;
}

USentryEvent* EventToProcess = USentryEvent::Create(Event);

USentryEvent* ProcessedEvent = GetBeforeSendHandler()->HandleBeforeSend(EventToProcess, nullptr);

return ProcessedEvent ? event : sentry_value_new_null();
}

// Currently this handler is not set anywhere since the Unreal SDK doesn't use `sentry_add_breadcrumb` directly and relies on
// custom scope implementation to store breadcrumbs instead.
// The support for it will be enabled with https://github.com/getsentry/sentry-native/pull/1166
sentry_value_t FGenericPlatformSentrySubsystem::OnBeforeBreadcrumb(sentry_value_t breadcrumb, void* hint, void* closure)
{
if (!closure || this != closure)
{
return breadcrumb;
}

FGCScopeGuard GCScopeGuard;

if (FUObjectThreadContext::Get().IsRoutingPostLoad)
{
UE_LOG(LogSentrySdk, Log, TEXT("Executing `beforeBreadcrumb` handler is not allowed when post-loading."));
return breadcrumb;
}

TSharedPtr<FGenericPlatformSentryBreadcrumb> Breadcrumb = MakeShareable(new FGenericPlatformSentryBreadcrumb(breadcrumb));

USentryBreadcrumb* BreadcrumbToProcess = USentryBreadcrumb::Create(Breadcrumb);

USentryBreadcrumb* ProcessedBreadcrumb = GetBeforeBreadcrumbHandler()->HandleBeforeBreadcrumb(BreadcrumbToProcess, nullptr);

return ProcessedBreadcrumb ? breadcrumb : sentry_value_new_null();
}

sentry_value_t FGenericPlatformSentrySubsystem::OnCrash(const sentry_ucontext_t* uctx, sentry_value_t event, void* closure)
{
if (!closure || this != closure)
Expand Down Expand Up @@ -166,9 +208,10 @@ FGenericPlatformSentrySubsystem::FGenericPlatformSentrySubsystem()
{
}

void FGenericPlatformSentrySubsystem::InitWithSettings(const USentrySettings* settings, USentryBeforeSendHandler* beforeSendHandler, USentryTraceSampler* traceSampler)
void FGenericPlatformSentrySubsystem::InitWithSettings(const USentrySettings* settings, USentryBeforeSendHandler* beforeSendHandler, USentryBeforeBreadcrumbHandler* beforeBreadcrumbHandler, USentryTraceSampler* traceSampler)
{
beforeSend = beforeSendHandler;
beforeBreadcrumb = beforeBreadcrumbHandler;

scopeStack.Push(MakeShareable(new FGenericPlatformSentryScope()));

Expand Down Expand Up @@ -292,6 +335,11 @@ ESentryCrashedLastRun FGenericPlatformSentrySubsystem::IsCrashedLastRun()

void FGenericPlatformSentrySubsystem::AddBreadcrumb(TSharedPtr<ISentryBreadcrumb> breadcrumb)
{
if (beforeBreadcrumb != nullptr)
{
HandleBeforeBreadcrumb(StaticCastSharedPtr<FGenericPlatformSentryBreadcrumb>(breadcrumb)->GetNativeObject(), nullptr, this);
}

GetCurrentScope()->AddBreadcrumb(breadcrumb);
}

Expand All @@ -304,7 +352,7 @@ void FGenericPlatformSentrySubsystem::AddBreadcrumbWithParams(const FString& Mes
Breadcrumb->SetData(Data);
Breadcrumb->SetLevel(Level);

GetCurrentScope()->AddBreadcrumb(Breadcrumb);
AddBreadcrumb(Breadcrumb);
}

void FGenericPlatformSentrySubsystem::ClearBreadcrumbs()
Expand Down Expand Up @@ -508,6 +556,11 @@ USentryBeforeSendHandler* FGenericPlatformSentrySubsystem::GetBeforeSendHandler(
return beforeSend;
}

USentryBeforeBreadcrumbHandler* FGenericPlatformSentrySubsystem::GetBeforeBreadcrumbHandler()
{
return beforeBreadcrumb;
}

void FGenericPlatformSentrySubsystem::TryCaptureScreenshot() const
{
if (!isScreenshotAttachmentEnabled)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class FGenericPlatformSentrySubsystem : public ISentrySubsystem
public:
FGenericPlatformSentrySubsystem();

virtual void InitWithSettings(const USentrySettings* settings, USentryBeforeSendHandler* beforeSendHandler, USentryTraceSampler* traceSampler) override;
virtual void InitWithSettings(const USentrySettings* settings, USentryBeforeSendHandler* beforeSendHandler, USentryBeforeBreadcrumbHandler* beforeBreadcrumbHandler, USentryTraceSampler* traceSampler) override;
virtual void Close() override;
virtual bool IsEnabled() override;
virtual ESentryCrashedLastRun IsCrashedLastRun() override;
Expand Down Expand Up @@ -47,6 +47,7 @@ class FGenericPlatformSentrySubsystem : public ISentrySubsystem
virtual TSharedPtr<ISentryTransactionContext> ContinueTrace(const FString& sentryTrace, const TArray<FString>& baggageHeaders) override;

USentryBeforeSendHandler* GetBeforeSendHandler();
USentryBeforeBreadcrumbHandler* GetBeforeBreadcrumbHandler();

void TryCaptureScreenshot() const;

Expand All @@ -67,16 +68,19 @@ class FGenericPlatformSentrySubsystem : public ISentrySubsystem
virtual FString GetHandlerExecutableName() const { return TEXT("invalid"); }

virtual sentry_value_t OnBeforeSend(sentry_value_t event, void* hint, void* closure);
virtual sentry_value_t OnBeforeBreadcrumb(sentry_value_t breadcrumb, void* hint, void* closure);
virtual sentry_value_t OnCrash(const sentry_ucontext_t* uctx, sentry_value_t event, void* closure);

private:
/**
* Static wrappers that are passed to the Sentry library.
*/
static sentry_value_t HandleBeforeSend(sentry_value_t event, void* hint, void* closure);
static sentry_value_t HandleBeforeBreadcrumb(sentry_value_t breadcrumb, void* hint, void* closure);
static sentry_value_t HandleOnCrash(const sentry_ucontext_t* uctx, sentry_value_t event, void* closure);

USentryBeforeSendHandler* beforeSend;
USentryBeforeBreadcrumbHandler* beforeBreadcrumb;

TSharedPtr<FGenericPlatformSentryCrashReporter> crashReporter;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class ISentryScope;

class USentrySettings;
class USentryBeforeSendHandler;
class USentryBeforeBreadcrumbHandler;
class USentryTraceSampler;

DECLARE_DELEGATE_OneParam(FSentryScopeDelegate, TSharedPtr<ISentryScope>);
Expand All @@ -26,7 +27,7 @@ class ISentrySubsystem
public:
virtual ~ISentrySubsystem() = default;

virtual void InitWithSettings(const USentrySettings* settings, USentryBeforeSendHandler* beforeSendHandler, USentryTraceSampler* traceSampler) = 0;
virtual void InitWithSettings(const USentrySettings* settings, USentryBeforeSendHandler* beforeSendHandler, USentryBeforeBreadcrumbHandler* beforeBreadcrumbHandler, USentryTraceSampler* traceSampler) = 0;
virtual void Close() = 0;
virtual bool IsEnabled() = 0;
virtual ESentryCrashedLastRun IsCrashedLastRun() = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@
void FMicrosoftSentrySubsystem::InitWithSettings(
const USentrySettings* Settings,
USentryBeforeSendHandler* BeforeSendHandler,
USentryBeforeBreadcrumbHandler* BeforeBreadcrumbHandler,
USentryTraceSampler* TraceSampler)
{
FGenericPlatformSentrySubsystem::InitWithSettings(Settings, BeforeSendHandler, TraceSampler);
FGenericPlatformSentrySubsystem::InitWithSettings(Settings, BeforeSendHandler, BeforeBreadcrumbHandler, TraceSampler);

#if !UE_VERSION_OLDER_THAN(5, 2, 0)
FPlatformMisc::SetCrashHandlingType(Settings->EnableAutoCrashCapturing
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class FMicrosoftSentrySubsystem : public FGenericPlatformSentrySubsystem
virtual void InitWithSettings(
const USentrySettings* Settings,
USentryBeforeSendHandler* BeforeSendHandler,
USentryBeforeBreadcrumbHandler* BeforeBreadcrumbHandler,
USentryTraceSampler* TraceSampler
) override;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Copyright (c) 2023 Sentry. All Rights Reserved.

#include "SentryBeforeBreadcrumbHandler.h"

#include "SentryBreadcrumb.h"
#include "SentryHint.h"

USentryBreadcrumb* USentryBeforeBreadcrumbHandler::HandleBeforeBreadcrumb_Implementation(USentryBreadcrumb* Breadcrumb, USentryHint* Hint)
{
return Breadcrumb;
}
2 changes: 1 addition & 1 deletion plugin-dev/Source/Sentry/Private/SentryOutputDevice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
void FSentryOutputDevice::Serialize(const TCHAR* V, ELogVerbosity::Type Verbosity, const FName& Category)
{
const FString Message = FString(V).TrimStartAndEnd();
if (Message.IsEmpty() || Message.StartsWith(TEXT("[Callstack]")))
if (Message.IsEmpty() || Message.Contains(TEXT("[Callstack]")))
{
return;
}
Expand Down
1 change: 1 addition & 0 deletions plugin-dev/Source/Sentry/Private/SentrySettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ USentrySettings::USentrySettings(const FObjectInitializer& ObjectInitializer)
, UseProxy(false)
, ProxyUrl()
, BeforeSendHandler(USentryBeforeSendHandler::StaticClass())
, BeforeBreadcrumbHandler(nullptr)
, EnableAutoCrashCapturing(true)
, DatabaseLocation(ESentryDatabaseLocation::ProjectUserDirectory)
, EnableAppNotRespondingTracking(false)
Expand Down
Loading
Loading