Skip to content

Commit 7eaff60

Browse files
authored
Add editor menu for automatic CRC configuration (#152)
* Add editor for automatic CRC configuration * Update changelog
1 parent 03526ce commit 7eaff60

File tree

9 files changed

+162
-5
lines changed

9 files changed

+162
-5
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
### Features
66

77
- Add debug symbols upload settings ([#94](https://github.com/getsentry/sentry-unreal/pull/94))
8+
- Add editor menu for automatic CRC configuration ([#152](https://github.com/getsentry/sentry-unreal/pull/152))
89

910
### Fixes
1011

plugin-dev/Source/Sentry/Private/SentrySettings.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ USentrySettings::USentrySettings(const FObjectInitializer& ObjectInitializer)
1313
{
1414
DsnUrl = TEXT("");
1515
Release = TEXT("");
16+
CrashReporterUrl = TEXT("");
1617

1718
LoadDebugSymbolsProperties();
1819
}

plugin-dev/Source/Sentry/Public/SentrySettings.h

+4
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@ class SENTRY_API USentrySettings : public UObject
7878
Meta = (DisplayName = "Authentication token", ToolTip = "Authentication token for performing actions against Sentry API.", EditCondition = "UploadSymbolsAutomatically"))
7979
FString AuthToken;
8080

81+
UPROPERTY(Config, EditAnywhere, Category = "Crash Reporter",
82+
Meta = (DisplayName = "Crash Reporter Endpoint", ToolTip = "Endpoint that Unreal Engine Crah Reporter should use in order to upload crash data to Sentry."))
83+
FString CrashReporterUrl;
84+
8185
private:
8286
void LoadDebugSymbolsProperties();
8387
};

plugin-dev/Source/SentryEditor/Private/SentryEditorModule.cpp

+7
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
#define LOCTEXT_NAMESPACE "FSentryEditorModule"
1111

12+
const FName FSentryEditorModule::ModuleName = "SentryEditor";
13+
1214
void FSentryEditorModule::StartupModule()
1315
{
1416
// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
@@ -24,6 +26,11 @@ void FSentryEditorModule::ShutdownModule()
2426
// we call this function before unloading the module.
2527
}
2628

29+
FSentryEditorModule& FSentryEditorModule::Get()
30+
{
31+
return FModuleManager::LoadModuleChecked<FSentryEditorModule>(ModuleName);
32+
}
33+
2734
#undef LOCTEXT_NAMESPACE
2835

2936
IMPLEMENT_MODULE(FSentryEditorModule, SentryEditor)

plugin-dev/Source/SentryEditor/Private/SentrySettingsCustomization.cpp

+129-3
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@
2020
#include "EditorStyleSet.h"
2121
#endif
2222

23+
const FString FSentrySettingsCustomization::DefaultCrcEndpoint = TEXT("https://datarouter.ol.epicgames.com/datarouter/api/v1/public/data");
24+
25+
void OnDocumentationLinkClicked(const FSlateHyperlinkRun::FMetadata& Metadata);
26+
2327
TSharedRef<IDetailCustomization> FSentrySettingsCustomization::MakeInstance()
2428
{
2529
return MakeShareable(new FSentrySettingsCustomization);
@@ -35,6 +39,9 @@ void FSentrySettingsCustomization::CustomizeDetails(IDetailLayoutBuilder& Detail
3539
void FSentrySettingsCustomization::DrawDebugSymbolsNotice(IDetailLayoutBuilder& DetailBuilder)
3640
{
3741
IDetailCategoryBuilder& DebugSymbolsCategory = DetailBuilder.EditCategory(TEXT("Debug Symbols"));
42+
IDetailCategoryBuilder& CrashReporterCategory = DetailBuilder.EditCategory(TEXT("Crash Reporter"));
43+
44+
TSharedPtr<IPropertyHandle> CrashReporterUrlHandle = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(USentrySettings, CrashReporterUrl));
3845

3946
#if ENGINE_MAJOR_VERSION >= 5
4047
const ISlateStyle& Style = FAppStyle::Get();
@@ -50,17 +57,97 @@ void FSentrySettingsCustomization::DrawDebugSymbolsNotice(IDetailLayoutBuilder&
5057
[
5158
SNew(SHorizontalBox)
5259
+ SHorizontalBox::Slot()
53-
.Padding(FMargin(10, 10, 10, 10))
54-
.FillWidth(1.0f)
60+
.Padding(FMargin(10, 10, 10, 10))
61+
.FillWidth(1.0f)
5562
[
5663
SNew(SRichTextBlock)
57-
.Text(FText::FromString(TEXT("Note that the Sentry SDK creates a <RichTextBlock.TextHighlight>sentry.properties</> file at project root to store the configuration, that should <RichTextBlock.TextHighlight>NOT</> be made publicly available.")))
64+
.Text(FText::FromString(TEXT("Note that the Sentry SDK creates a <RichTextBlock.TextHighlight>sentry.properties</> file at project root to store the configuration, "
65+
"that should <RichTextBlock.TextHighlight>NOT</> be made publicly available.")))
5866
.TextStyle(Style, "MessageLog")
5967
.DecoratorStyleSet(&Style)
6068
.AutoWrapText(true)
6169
]
6270
]
6371
];
72+
73+
CrashReporterCategory.AddCustomRow(FText::FromString(TEXT("CrashReporter")), false)
74+
.WholeRowWidget
75+
[
76+
SNew(SVerticalBox)
77+
+ SVerticalBox::Slot()
78+
.Padding(1)
79+
.AutoHeight()
80+
[
81+
SNew(SBorder)
82+
.Padding(1)
83+
[
84+
SNew(SHorizontalBox)
85+
+ SHorizontalBox::Slot()
86+
.Padding(FMargin(10, 10, 10, 10))
87+
.FillWidth(1.0f)
88+
[
89+
SNew(SRichTextBlock)
90+
.Text(FText::FromString(TEXT("In order to configure Crash Reporter use Sentry's Unreal Engine Endpoint from the Client Keys settings page. "
91+
"This will include which project within Sentry you want to see the crashes arriving in real time. "
92+
"Note that it's accomplished by modifying the `CrashReportClient` section in the global <RichTextBlock.TextHighlight>DefaultEngine.ini</> file. "
93+
"Changing the engine is necessary for this to work!")))
94+
.TextStyle(Style, "MessageLog")
95+
.DecoratorStyleSet(&Style)
96+
.AutoWrapText(true)
97+
]
98+
]
99+
]
100+
+ SVerticalBox::Slot()
101+
.Padding(FMargin(0, 10, 0, 10))
102+
.VAlign(VAlign_Top)
103+
[
104+
SNew(SRichTextBlock)
105+
.Text(FText::FromString(TEXT("<a id=\"browser\" href=\"https://docs.sentry.io/platforms/unreal/setup-crashreporter/\">View the Crash Reporter setup documentation -></>")))
106+
.AutoWrapText(true)
107+
.DecoratorStyleSet(&FCoreStyle::Get())
108+
+ SRichTextBlock::HyperlinkDecorator(TEXT("browser"), FSlateHyperlinkRun::FOnClick::CreateStatic(&OnDocumentationLinkClicked))
109+
]
110+
+ SVerticalBox::Slot()
111+
.Padding(FMargin(0, 10, 0, 10))
112+
.AutoHeight()
113+
[
114+
SNew(SHorizontalBox)
115+
+ SHorizontalBox::Slot()
116+
.AutoWidth()
117+
.Padding(FMargin(0, 0, 5, 0))
118+
[
119+
SNew(SButton)
120+
.HAlign(HAlign_Center)
121+
.VAlign(VAlign_Center)
122+
.ContentPadding(FMargin(8, 2))
123+
.OnClicked_Lambda([=]() -> FReply
124+
{
125+
FString CrcEndpoint;
126+
CrashReporterUrlHandle->GetValue(CrcEndpoint);
127+
UpdateCrcConfig(CrcEndpoint);
128+
return FReply::Handled();
129+
})
130+
.Text(FText::FromString(TEXT("Update global settings")))
131+
.ToolTipText(FText::FromString(TEXT("Update global crash reporter settings in DefaultEngine.ini configuration file.")))
132+
]
133+
+ SHorizontalBox::Slot()
134+
.AutoWidth()
135+
.Padding(FMargin(5, 0, 5, 0))
136+
[
137+
SNew(SButton)
138+
.HAlign(HAlign_Center)
139+
.VAlign(VAlign_Center)
140+
.ContentPadding(FMargin(8, 2))
141+
.OnClicked_Lambda([=]() -> FReply
142+
{
143+
UpdateCrcConfig(DefaultCrcEndpoint);
144+
return FReply::Handled();
145+
})
146+
.Text(FText::FromString("Reset"))
147+
.ToolTipText(FText::FromString(TEXT("Reset crash reporter settings to defaults.")))
148+
]
149+
]
150+
];
64151
}
65152

66153
void FSentrySettingsCustomization::SetPropertiesUpdateHandler(IDetailLayoutBuilder& DetailBuilder)
@@ -116,3 +203,42 @@ void FSentrySettingsCustomization::UpdatePropertiesFile(const FString& PropertyN
116203
PropertiesFile.SetString(TEXT("Sentry"), *PropertyName, *PropertyValue);
117204
PropertiesFile.Write(PropertiesFilePath);
118205
}
206+
207+
void FSentrySettingsCustomization::UpdateCrcConfig(const FString& Url)
208+
{
209+
if (Url.IsEmpty())
210+
{
211+
return;
212+
}
213+
214+
const FString CrcConfigFilePath = GetCrcConfigPath();
215+
216+
if (!FPaths::FileExists(CrcConfigFilePath))
217+
{
218+
return;
219+
}
220+
221+
FConfigCacheIni CrcConfigFile(EConfigCacheType::DiskBacked);
222+
CrcConfigFile.LoadFile(CrcConfigFilePath);
223+
224+
const FString CrcSectionName = FString(TEXT("CrashReportClient"));
225+
226+
const FString DataRouterUrlKey = FString(TEXT("DataRouterUrl"));
227+
const FString DataRouterUrlValue = Url;
228+
229+
CrcConfigFile.SetString(*CrcSectionName, *DataRouterUrlKey, *DataRouterUrlValue, CrcConfigFilePath);
230+
}
231+
232+
FString FSentrySettingsCustomization::GetCrcConfigPath()
233+
{
234+
return FPaths::Combine(FPaths::EngineDir(), TEXT("Programs"), TEXT("CrashReportClient"), TEXT("Config"), TEXT("DefaultEngine.ini"));
235+
}
236+
237+
void OnDocumentationLinkClicked(const FSlateHyperlinkRun::FMetadata& Metadata)
238+
{
239+
const FString* UrlPtr = Metadata.Find(TEXT("href"));
240+
if (UrlPtr)
241+
{
242+
FPlatformProcess::LaunchURL(**UrlPtr, nullptr, nullptr);
243+
}
244+
}

plugin-dev/Source/SentryEditor/Public/SentryEditorModule.h

+10
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,14 @@ class FSentryEditorModule : public IModuleInterface
1111
/** IModuleInterface implementation */
1212
virtual void StartupModule() override;
1313
virtual void ShutdownModule() override;
14+
15+
/**
16+
* Singleton-like access to this module's interface. This is just for convenience!
17+
* Beware of calling this during the shutdown phase, though. Your module might be already unloaded.
18+
*
19+
* @return Returns singleton instance, loading the module on demand if needed.
20+
*/
21+
static FSentryEditorModule& Get();
22+
23+
static const FName ModuleName;
1424
};

plugin-dev/Source/SentryEditor/Public/SentrySettingsCustomization.h

+8-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "IDetailCustomization.h"
66

77
class IPropertyHandle;
8+
class FSlateHyperlinkRun;
89

910
class FSentrySettingsCustomization : public IDetailCustomization
1011
{
@@ -23,9 +24,15 @@ class FSentrySettingsCustomization : public IDetailCustomization
2324
void UpdateOrganizationName();
2425
void UpdateAuthToken();
2526

26-
void UpdatePropertiesFile(const FString& PropertyName, const FString& PropertyValue);
27+
void UpdatePropertiesFile(const FString& PropertyName, const FString& PropertyValue);
28+
void UpdateCrcConfig(const FString& Url);
29+
30+
// Gets path to CRC's DefaultEngine.ini in engine directory
31+
FString GetCrcConfigPath();
2732

2833
TSharedPtr<IPropertyHandle> ProjectNameHandle;
2934
TSharedPtr<IPropertyHandle> OrganizationNameHandle;
3035
TSharedPtr<IPropertyHandle> AuthTokenHandle;
36+
37+
static const FString DefaultCrcEndpoint;
3138
};

sample/Config/DefaultEngine.ini

+1
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,7 @@ AutomaticBreadcrumbs=(bOnMapLoadingStarted=True,bOnMapLoaded=True,bOnGameStateCl
267267
UploadSymbolsAutomatically=False
268268
Release=
269269
PropertiesFilePath=Config/sentry.properties
270+
CrashReporterUrl="https://o447951.ingest.sentry.io/api/6253052/unreal/93c7a68867db43539980de54f09b139a/"
270271

271272
[/Script/MacTargetPlatform.MacTargetSettings]
272273
SpatializationPlugin=Built-in Spatialization

sample/Config/DefaultGame.ini

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ ProjectID=2F77D1EF624C7FE53C3E46A0AE066C2D
55
[/Script/UnrealEd.ProjectPackagingSettings]
66
BuildConfiguration=PPBC_Shipping
77
ForDistribution=True
8-
IncludeCrashReporter=False
8+
IncludeCrashReporter=True
99
IncludeDebugFiles=True
1010
FullRebuild=True
1111

0 commit comments

Comments
 (0)