Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

- Allow Sentry CLI to authenticate via environment variables during debug symbols upload ([#836](https://github.com/getsentry/sentry-unreal/pull/836))
- Added the ability to specify a separate DSN for crashes while in editor vs cooked title ([#853](https://github.com/getsentry/sentry-unreal/pull/853))
- Add callback to pre-process breadcrumbs before adding ([#814](https://github.com/getsentry/sentry-unreal/pull/814))

### Fixes

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ Blog posts:

- Only crash events captured on Android contain the full callstack. Events that were captured manually won't have the native C++ part there.

- If an event was captured during the garbage collection, the `BeforeSendHandler` will not be invoked.
- `BeforeSendHandler` and `BeforeBreadcrumbHandler` hooks will not be invoked during garbage collection.

- It may be required to upgrade the C++ standard library (`libstdc++`) on older Linux distributions (such as Ubuntu 18.04 and 20.04) to ensure crashpad handler proper functionality within the deployment environment. This can be achieved with something like this:
```
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 @@ -34,6 +34,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 @@ -85,6 +86,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 @@ -66,6 +69,32 @@ 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 (FUObjectThreadContext::Get().IsRoutingPostLoad)
{
// Don't print to logs within `onBeforeBreadcrumb` handler as this can lead to creating new breadcrumb
return breadcrumb;
}

if (IsGarbageCollecting())
{
// If breadcrumb is added during garbage collection we can't instantiate UObjects safely or obtain a GC lock
// since there is no guarantee it will be ever freed.
// In this case breadcrumb will be added without calling a `beforeBreadcrumb` handler.
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 @@ -110,6 +112,30 @@ void FAppleSentrySubsystem::InitWithSettings(const USentrySettings* settings, US
return traceSampler->Sample(Context, samplingValue) ? [NSNumber numberWithFloat:samplingValue] : nil;
};
}
if (beforeBreadcrumbHandler != nullptr)
{
options.beforeBreadcrumb = ^SentryBreadcrumb* (SentryBreadcrumb* breadcrumb) {
if (FUObjectThreadContext::Get().IsRoutingPostLoad)
{
// Don't print to logs within `onBeforeBreadcrumb` handler as this can lead to creating new breadcrumb
return breadcrumb;
}

if (IsGarbageCollecting())
{
// If breadcrumb is added during garbage collection we can't instantiate UObjects safely or obtain a GC lock
// since there is no guarantee it will be ever freed.
// In this case breadcrumb will be added without calling a `beforeBreadcrumb` handler.
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 @@ -69,6 +71,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 @@ -114,6 +128,39 @@ sentry_value_t FGenericPlatformSentrySubsystem::OnBeforeSend(sentry_value_t even
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;
}

if (FUObjectThreadContext::Get().IsRoutingPostLoad)
{
// Don't print to logs within `onBeforeBreadcrumb` handler as this can lead to creating new breadcrumb
return breadcrumb;
}

if (IsGarbageCollecting())
{
// If breadcrumb is added during garbage collection we can't instantiate UObjects safely or obtain a GC lock
// since there is no guarantee it will be ever freed.
// In this case breadcrumb will be added without calling a `beforeBreadcrumb` handler.
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 @@ -159,16 +206,19 @@ sentry_value_t FGenericPlatformSentrySubsystem::OnCrash(const sentry_ucontext_t*

FGenericPlatformSentrySubsystem::FGenericPlatformSentrySubsystem()
: beforeSend(nullptr)
, beforeBreadcrumb(nullptr)
, crashReporter(MakeShareable(new FGenericPlatformSentryCrashReporter))
, isEnabled(false)
, isStackTraceEnabled(true)
, isPiiAttachmentEnabled(false)
, isScreenshotAttachmentEnabled(false)
{
}

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 @@ -298,6 +348,15 @@ ESentryCrashedLastRun FGenericPlatformSentrySubsystem::IsCrashedLastRun()

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

GetCurrentScope()->AddBreadcrumb(breadcrumb);
}

Expand All @@ -310,6 +369,15 @@ void FGenericPlatformSentrySubsystem::AddBreadcrumbWithParams(const FString& Mes
Breadcrumb->SetData(Data);
Breadcrumb->SetLevel(Level);

if (beforeBreadcrumb != nullptr)
{
sentry_value_t processdBreadcrumb = HandleBeforeBreadcrumb(Breadcrumb->GetNativeObject(), nullptr, this);
if (sentry_value_is_null(processdBreadcrumb))
{
return;
}
}

GetCurrentScope()->AddBreadcrumb(Breadcrumb);
}

Expand Down Expand Up @@ -514,6 +582,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
Loading
Loading