BugSplat's Unreal Editor plugin makes adding crash reporting to your game a breeze! With BugSplat, you'll get a crash notification containing a full call stack with function names, line numbers, as well as many other invaluable insights into the issues tripping up your users.
Before diving into the plugin, please complete the following tasks:
- Sign Up as a new BugSplat user
- Complete the Welcome workflow and make a note of your BugSplat database
- Review our MyUnrealCrasher sample application to jump-start your evaluation of BugSplat
You may choose to add BugSplat through the Unreal Marketplace or add the plugin to your Unreal project manually.
Install BugSplat from the Unreal Marketplace
- Navigate to your project folder containing your
[ProjectName].uprojectfile. - Create a
Pluginsfolder if it does not already exist. - Create a
BugSplatfolder in thePluginsfolder and copy the contents of this repo into theBugSplatfolder. - In the Unreal Editor, ensure you can access the BugSplat plugin via
Edit > Project Settingsand scroll to theBugSplatsection underPlugins.
BugSplat's Unreal plugin currently supports adding crash reporting to Windows, macOS, Linux, Android, and iOS games. With a few clicks, the BugSplat plugin can be configured to automatically upload symbol files so crash report stack traces display function names and line numbers.
To get started, generate a Client ID and Client Secret via the Integrations page.
Next, open the BugSplat plugin menu in the Unreal Editor via Edit > Project Settings. Scroll to the BugSplat section of Project Settings and add values for Database, Application, Version, Client ID, and Client Secret:
BugSplat leverages Unreal's CrashReportClient to provide crash reporting for Windows, macOS, and Linux games. Be sure to update your project settings and enable Include Crash Reporter and Include Debug Files in Shipping Builds:
To configure CrashReportClient to post to BugSplat, the DataRouterUrl value needs to be added to DefaultEngine.ini. The bugsplat-unreal plugin automatically updates the value for DataRouterUrl when the Update Engine DefaultEngine.ini option is enabled. Please note the DataRouterUrl value is global and is shared across all packaged builds created by the affected engine. To override the DataRouterUrl value a package build uses, you may optionally use the Update Packaged Game INI button under the Tools section.
In order to get function names and line numbers in crash reports, you'll need to upload your game's .exe, .dll, and .pdb files. To upload debug symbols for reach build, ensure that the Enable Automatic Symbol Uploads option is selected. When selected, a script to execute symbol-upload will be added to the PostBuildSteps field in BugSplat.uplugin. The symbol upload script will run automatically when your game is built.
To create a .dSYM file for a macOS build invoke RunUAT.command with the -EnableDSym flag per the example below:
../../../Engine/Build/BatchFiles/Mac/Build.sh -Project="../../my-unreal-crasher/MyUnrealCrasher.uproject" MyUnrealCrasher Mac Development -NoEditor -EnableDSym -TargetPath="~/Desktop/UnrealEngine"Before attempting to use the BugSplat plugin to capture crashes on Mobile, please ensure you've completed the iOS and Android quickstart guides.
To enable crash reporting, ensure the Enable iOS Crash Reporting and Enable Android Crash Reporting options are selected. Also, ensure that Enable Automatic Symbol Uploads is checked so that your crash reports contain function names and line numbers.
Note
The Unreal iOS project's build process includes a Build Phase called Generate dSYM for archive, and strip, which executes after the Unreal PostBuildSteps. However, this Build Phase must complete before the dSYM file (debug symbols) is generated. Due to this timing, BugSplat cannot upload the dSYM immediately during the initial build. Instead, BugSplat will upload the dSYM during the next incremental build in Xcode. Alternatively, you can follow the example in our bugsplat-apple repo to configure a custom Build Phase for symbol uploads.
To get function names and line numbers in your iOS crash reports, please make the following changes in the iOS section of Project Settings.
| Option | Value |
|---|---|
| Generate dSYMs for code debugging and profiling | true |
| Generate dSYMs as a bundle for third-party crash tools | true |
| Support bitcode in shipping | false |
Note that sometimes iOS applications won't crash while the USB cable is connected. If this happens, disconnect the USB cable and re-run the application to trigger a crash.
Note
Code is aggressively optimized when building for Android. Oftentimes, Unreal's build process optimizes away code that generates simple errors used in testing. To test a null pointer dereference, you can add the volatile keyword to work around compiler optimizations.
Fatal Errors on Android raise a SIGTRAP and require extra configuration so that they can be reported to BugSplat.
Click to reveal Android Error Output Device code
MyUnrealCrasherErrorOutputDevice.h
#pragma once
#include "CoreMinimal.h"
#include "Misc/OutputDeviceError.h"
#if PLATFORM_ANDROID
class FMyUnrealCrasherAndroidErrorOutputDevice : public FOutputDeviceError
{
public:
virtual ~FMyUnrealCrasherAndroidErrorOutputDevice() {}
virtual void Serialize(const TCHAR* V, ELogVerbosity::Type Verbosity, const FName& Category) override;
virtual void HandleError() override;
static FOutputDeviceError* GetErrorOutputDevice();
private:
void RequestExit(bool Force, const TCHAR* CallSite);
};
#endifMyUnrealCrasherErrorOutputDevice.cpp
#include "MyUnrealCrasherAndroidErrorOutputDevice.h"
#include "CoreMinimal.h"
#include "CoreGlobals.h"
#include "Misc/OutputDevice.h"
#include "Misc/OutputDeviceHelper.h"
#include "Misc/App.h"
#include "Misc/CoreDelegates.h"
#include "Misc/FeedbackContext.h"
#include "HAL/PlatformMisc.h"
#include "HAL/PlatformCrt.h"
#if PLATFORM_ANDROID
#include "Android/AndroidPlatform.h" // For LogAndroid
FOutputDeviceError* FMyUnrealCrasherAndroidErrorOutputDevice::GetErrorOutputDevice()
{
static FMyUnrealCrasherAndroidErrorOutputDevice ErrorOutputDevice;
return &ErrorOutputDevice;
}
void FMyUnrealCrasherAndroidErrorOutputDevice::Serialize( const TCHAR* Msg, ELogVerbosity::Type Verbosity, const class FName& Category )
{
FPlatformMisc::LowLevelOutputDebugString(*FOutputDeviceHelper::FormatLogLine(Verbosity, Category, Msg, GPrintLogTimes));
static int32 CallCount = 0;
int32 NewCallCount = FPlatformAtomics::InterlockedIncrement(&CallCount);
if(GIsCriticalError == 0 && NewCallCount == 1)
{
// First appError.
GIsCriticalError = 1;
FCString::Strncpy(GErrorExceptionDescription, Msg, UE_ARRAY_COUNT(GErrorExceptionDescription));
}
else
{
UE_LOG(LogAndroid, Error, TEXT("Error reentered: %s"), Msg);
}
if (GIsGuarded)
{
UE_DEBUG_BREAK();
}
else
{
HandleError();
RequestExit(true, TEXT("MyUnrealCrasherAndroidErrorOutputDevice::Serialize.!GIsGuarded"));
}
}
void FMyUnrealCrasherAndroidErrorOutputDevice::HandleError()
{
static int32 CallCount = 0;
int32 NewCallCount = FPlatformAtomics::InterlockedIncrement(&CallCount);
if (NewCallCount != 1)
{
UE_LOG(LogAndroid, Error, TEXT("HandleError re-entered."));
return;
}
GIsGuarded = 0;
GIsRunning = 0;
GIsCriticalError = 1;
GLogConsole = NULL;
GErrorHist[UE_ARRAY_COUNT(GErrorHist) - 1] = 0;
// Dump the error and flush the log.
#if !NO_LOGGING
FDebug::LogFormattedMessageWithCallstack(LogAndroid.GetCategoryName(), __FILE__, __LINE__, TEXT("=== Critical error: ==="), GErrorHist, ELogVerbosity::Error);
#endif
GLog->Panic();
FCoreDelegates::OnHandleSystemError.Broadcast();
FCoreDelegates::OnShutdownAfterError.Broadcast();
}
void FMyUnrealCrasherAndroidErrorOutputDevice::RequestExit( bool Force, const TCHAR* CallSite)
{
#if PLATFORM_COMPILER_OPTIMIZATION_PG_PROFILING
// Write the PGO profiling file on a clean shutdown.
extern void PGO_WriteFile();
if (!GIsCriticalError)
{
PGO_WriteFile();
// exit now to avoid a possible second PGO write when AndroidMain exits.
Force = true;
}
#endif
UE_LOG(LogAndroid, Log, TEXT("FMyUnrealCrasherAndroidErrorOutputDevice::RequestExit(%i, %s)"), Force,
CallSite ? CallSite : TEXT("<NoCallSiteInfo>"));
if (GLog)
{
GLog->Flush();
}
if (Force)
{
abort(); // Abort to trigger a crash report
}
else
{
RequestEngineExit(TEXT("Android RequestExitWithCrashReporting")); // Called regardless in our version to set up the crash context
}
}
#endif#pragma once
#include "CoreMinimal.h"
#include "Engine/GameInstance.h"
#include "MyUnrealCrasherGameInstance.generated.h"
UCLASS()
class MYUNREALCRASHER_API UMyUnrealCrasherGameInstance : public UGameInstance
{
GENERATED_BODY()
public:
virtual void Init() override;
};MyUnrealCrasherGameInstance.cpp
// Copyright Β© BugSplat. All rights reserved.
#include "MyUnrealCrasherGameInstance.h"
#include "MyUnrealCrasherAndroidErrorOutputDevice.h"
#if PLATFORM_ANDROID
#include <android/log.h>
#endif
void UMyUnrealCrasherGameInstance::Init()
{
Super::Init();
UE_LOG(LogTemp, Log, TEXT("MyUnrealCrasherGameInstance::Init - Setting custom error output device"));
#if PLATFORM_ANDROID
GError = FMyUnrealCrasherAndroidErrorOutputDevice::GetErrorOutputDevice();
#endif
}The BugSplat plugin automatically adds several attributes from the crash context at initialization time. These attributes are not updated automatically after initialization. If you'd like to update them or add new ones, use the SetAttribute and RemoveAttribute functions.
To call SetAttribute from C++, add BugSplatRuntime to your module's PrivateDependencyModuleNames in your .Build.cs file:
PrivateDependencyModuleNames.AddRange(new string[] { "BugSplatRuntime" });Then include the header and call SetAttribute:
#include "BugSplatAttributes.h"
UBugSplatAttributes::SetAttribute(TEXT("userId"), TEXT("12345"));SetAttribute is also available as a Blueprint node under the BugSplat category.
BugSplat can provide instructions for implementing Unreal crash reporting on Xbox and PlayStation. Please email us at support@bugsplat.com for more info.
Once you've installed the plugin, add the following C++ snippet to your game to generate a sample crash.
UE_LOG(LogTemp, Fatal, TEXT("BugSplat!"));Run your application and submit a crash report.
On Desktops, submit a crash report via the Unreal CrashReportClient dialog that appears at crash time. We have developed a handy guide on how you can customize the Unreal CrashReportClient dialog that is available here.
On iOS, after a crash occurs, restart the game and tap the Send Report option when prompted. On Android, crashes are submitted automatically at crash time.
Once you've submitted a crash report, navigate to the Crashes page. On the Crashes page, click the link in the ID column.
If everything is configured correctly, you should see something that resembles the following:
BugSplat includes a built-in user feedback dialog that lets players submit feedback directly from your game. Feedback reports appear alongside crash reports on the Crashes page, tagged with a [User Feedback] prefix.
The plugin provides a ready-to-use feedback dialog with subject and description fields. To add it to your game:
- Open any Widget Blueprint (e.g., your HUD or main menu).
- Add a Button widget and position it where you'd like the feedback trigger to appear.
- In the button's Events section, click the + next to On Clicked.
- In the Event Graph, search for Show Feedback Dialog (under the BugSplat category) and connect it to the On Clicked event.
When a player clicks the button, a modal dialog appears with:
- Subject (required) β a single-line text field for a brief summary
- Description (optional) β a multi-line text area for additional details
- Include application logs (checkbox, on by default) β attaches the Unreal Engine log file to the feedback report
After submitting, a confirmation message is displayed briefly before the dialog closes automatically. The feedback is posted to your BugSplat database using the Database, Application, and Version values from your plugin settings.
The built-in dialog also automatically includes crash context attributes (engine version, platform, CPU, GPU, memory stats, OS info, etc.) with each submission.
You can also open the dialog from C++:
#include "BugSplatFeedbackDialog.h"
SBugSplatFeedbackDialog::Show();If you'd prefer to build your own feedback UI, you can call UBugSplatFeedback::PostFeedback directly from Blueprint or C++. It handles the HTTP submission, crash context attributes, and file attachments for you β just provide a title, optional description, and optional file paths:
#include "BugSplatFeedback.h"
// Submit feedback with the Unreal log attached
TArray<FString> Attachments;
Attachments.Add(UBugSplatFeedback::GetLogFilePath());
UBugSplatFeedback::PostFeedback(TEXT("My feedback title"), TEXT("Details here"), Attachments);For C++ callers that need to handle success or failure, use PostFeedbackWithCallback:
#include "BugSplatFeedback.h"
UBugSplatFeedback::PostFeedbackWithCallback(
TEXT("My feedback title"), TEXT("Details here"), Attachments,
TEXT(""), TEXT(""), TEXT(""), TMap<FString, FString>(),
FBugSplatFeedbackComplete::CreateLambda([](bool bSuccess, int32 HttpStatusCode)
{
if (bSuccess)
{
// Show success in your UI
}
else
{
// Show error in your UI
}
})
);See BugSplatFeedback.h for the full API and BugSplatFeedback.cpp for the implementation. The built-in dialog in BugSplatFeedbackDialog.cpp serves as a reference for how to wire up your own UI.
PostFeedback posts a multipart/form-data request to https://{database}.bugsplat.com/post/feedback/ with the following fields:
| Field | Required | Description |
|---|---|---|
database |
Yes | Your BugSplat database name (from plugin settings) |
appName |
Yes | Application name (from plugin settings) |
appVersion |
Yes | Application version (from plugin settings) |
title |
Yes | Brief summary of the feedback |
description |
No | Additional details |
attributes |
No | JSON string of crash context metadata (included automatically) |
user |
No | Username of the person submitting feedback |
email |
No | Email of the person submitting feedback |
appKey |
No | Application key |
File attachments can be included as additional multipart file fields. Pass absolute file paths via the Attachments parameter.
BugSplat β€οΈs open source! If you feel that this package can be improved, please open an Issue. If you have an awesome new feature you'd like to implement, we'd love to merge your Pull Request. You can also send us an email, join us on Discord, or message us via the in-app chat on bugsplat.com.
