Skip to content

Commit cbbca84

Browse files
authored
Fix invalid log file being attached to crash events on Mac/iOS (#873)
* Replace log file path getter with utility method * Update log file attachment for crashes * Fix redundant log file attached to crash events * Update changelog * Add workaround for issue with invalid log file path on iOS * Fix compilation errors on iOS * Refactor attaching log to events
1 parent bdc5b7b commit cbbca84

9 files changed

+168
-67
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
### Fixes
1010

1111
- Fix warnings caused by deprecated Cocoa SDK API usages ([#868](https://github.com/getsentry/sentry-unreal/pull/868))
12+
- Fix invalid log file being attached to crash events on Mac/iOS ([#873](https://github.com/getsentry/sentry-unreal/pull/873))
1213
- Fix Sentry cURL transport can't send envelopes on Linux ([#882](https://github.com/getsentry/sentry-unreal/pull/882))
1314
- The SDK now ensures the execute permission is set for Sentry CLI and symbol upload script when they have been downloaded via the Editor ([#881](https://github.com/getsentry/sentry-unreal/pull/881))
1415

plugin-dev/Source/Sentry/Private/Apple/AppleSentrySubsystem.cpp

+78-39
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@
2626
#include "Convenience/SentryInclude.h"
2727
#include "Convenience/SentryMacro.h"
2828

29+
#include "Utils/SentryFileUtils.h"
30+
#include "Utils/SentryLogUtils.h"
31+
2932
#include "GenericPlatform/GenericPlatformOutputDevices.h"
3033
#include "HAL/FileManager.h"
3134
#include "HAL/PlatformSentryAttachment.h"
@@ -34,10 +37,12 @@
3437
#include "Misc/Paths.h"
3538
#include "UObject/GarbageCollection.h"
3639
#include "UObject/UObjectThreadContext.h"
37-
#include "Utils/SentryLogUtils.h"
3840

3941
void FAppleSentrySubsystem::InitWithSettings(const USentrySettings* settings, USentryBeforeSendHandler* beforeSendHandler, USentryBeforeBreadcrumbHandler* beforeBreadcrumbHandler, USentryTraceSampler* traceSampler)
4042
{
43+
isScreenshotAttachmentEnabled = settings->AttachScreenshot;
44+
isGameLogAttachmentEnabled = settings->EnableAutoLogAttachment;
45+
4146
[SENTRY_APPLE_CLASS(PrivateSentrySDKOnly) setSdkName:@"sentry.cocoa.unreal"];
4247

4348
dispatch_group_t sentryDispatchGroup = dispatch_group_create();
@@ -65,24 +70,18 @@ void FAppleSentrySubsystem::InitWithSettings(const USentrySettings* settings, US
6570
#if SENTRY_UIKIT_AVAILABLE
6671
options.attachScreenshot = settings->AttachScreenshot;
6772
#endif
68-
options.initialScope = ^(SentryScope* scope) {
69-
if(settings->EnableAutoLogAttachment) {
70-
const FString logFilePath = IFileManager::Get().ConvertToAbsolutePathForExternalAppForRead(*FGenericPlatformOutputDevices::GetAbsoluteLogFilename());
71-
SentryAttachment* logAttachment = [[SENTRY_APPLE_CLASS(SentryAttachment) alloc] initWithPath:logFilePath.GetNSString()];
72-
[scope addAttachment:logAttachment];
73-
}
74-
return scope;
75-
};
7673
options.onCrashedLastRun = ^(SentryEvent* event) {
7774
if (settings->AttachScreenshot)
7875
{
7976
// If a screenshot was captured during assertion/crash in the previous app run
8077
// find the most recent one and upload it to Sentry.
81-
const FString& screenshotPath = GetLatestScreenshot();
82-
if (!screenshotPath.IsEmpty())
83-
{
84-
UploadScreenshotForEvent(MakeShareable(new SentryIdApple(event.eventId)), screenshotPath);
85-
}
78+
UploadScreenshotForEvent(MakeShareable(new SentryIdApple(event.eventId)), GetLatestScreenshot());
79+
}
80+
if(settings->EnableAutoLogAttachment)
81+
{
82+
// Unreal creates game log backups automatically on every app run. If logging is enabled for current configuration, SDK can
83+
// find the most recent one and upload it to Sentry.
84+
UploadGameLogForEvent(MakeShareable(new SentryIdApple(event.eventId)), GetLatestGameLog());
8685
}
8786
};
8887
options.beforeSend = ^SentryEvent* (SentryEvent* event) {
@@ -206,40 +205,49 @@ void FAppleSentrySubsystem::ClearBreadcrumbs()
206205

207206
TSharedPtr<ISentryId> FAppleSentrySubsystem::CaptureMessage(const FString& message, ESentryLevel level)
208207
{
209-
SentryId* id = [SENTRY_APPLE_CLASS(SentrySDK) captureMessage:message.GetNSString() withScopeBlock:^(SentryScope* scope){
210-
[scope setLevel:SentryConvertersApple::SentryLevelToNative(level)];
211-
}];
212-
213-
return MakeShareable(new SentryIdApple(id));
208+
FSentryScopeDelegate onConfigureScope;
209+
return CaptureMessageWithScope(message, onConfigureScope, level);
214210
}
215211

216212
TSharedPtr<ISentryId> FAppleSentrySubsystem::CaptureMessageWithScope(const FString& message, const FSentryScopeDelegate& onConfigureScope, ESentryLevel level)
217213
{
218-
SentryId* id = [SENTRY_APPLE_CLASS(SentrySDK) captureMessage:message.GetNSString() withScopeBlock:^(SentryScope* scope){
214+
SentryId* nativeId = [SENTRY_APPLE_CLASS(SentrySDK) captureMessage:message.GetNSString() withScopeBlock:^(SentryScope* scope){
219215
[scope setLevel:SentryConvertersApple::SentryLevelToNative(level)];
220216
onConfigureScope.ExecuteIfBound(MakeShareable(new SentryScopeApple(scope)));
221217
}];
222218

223-
return MakeShareable(new SentryIdApple(id));
219+
TSharedPtr<ISentryId> id = MakeShareable(new SentryIdApple(nativeId));
220+
221+
if (isGameLogAttachmentEnabled)
222+
{
223+
UploadGameLogForEvent(id, GetGameLogPath());
224+
}
225+
226+
return id;
224227
}
225228

226229
TSharedPtr<ISentryId> FAppleSentrySubsystem::CaptureEvent(TSharedPtr<ISentryEvent> event)
227230
{
228-
TSharedPtr<SentryEventApple> eventIOS = StaticCastSharedPtr<SentryEventApple>(event);
229-
230-
SentryId* id = [SENTRY_APPLE_CLASS(SentrySDK) captureEvent:eventIOS->GetNativeObject()];
231-
return MakeShareable(new SentryIdApple(id));
231+
FSentryScopeDelegate onConfigureScope;
232+
return CaptureEventWithScope(event, onConfigureScope);
232233
}
233234

234235
TSharedPtr<ISentryId> FAppleSentrySubsystem::CaptureEventWithScope(TSharedPtr<ISentryEvent> event, const FSentryScopeDelegate& onConfigureScope)
235236
{
236-
TSharedPtr<SentryEventApple> eventIOS = StaticCastSharedPtr<SentryEventApple>(event);
237+
TSharedPtr<SentryEventApple> eventApple = StaticCastSharedPtr<SentryEventApple>(event);
237238

238-
SentryId* id = [SENTRY_APPLE_CLASS(SentrySDK) captureEvent:eventIOS->GetNativeObject() withScopeBlock:^(SentryScope* scope) {
239+
SentryId* nativeId = [SENTRY_APPLE_CLASS(SentrySDK) captureEvent:eventApple->GetNativeObject() withScopeBlock:^(SentryScope* scope) {
239240
onConfigureScope.ExecuteIfBound(MakeShareable(new SentryScopeApple(scope)));
240241
}];
241242

242-
return MakeShareable(new SentryIdApple(id));
243+
TSharedPtr<ISentryId> id = MakeShareable(new SentryIdApple(nativeId));
244+
245+
if (isGameLogAttachmentEnabled)
246+
{
247+
UploadGameLogForEvent(id, GetGameLogPath());
248+
}
249+
250+
return id;
243251
}
244252

245253
TSharedPtr<ISentryId> FAppleSentrySubsystem::CaptureEnsure(const FString& type, const FString& message)
@@ -251,8 +259,16 @@ TSharedPtr<ISentryId> FAppleSentrySubsystem::CaptureEnsure(const FString& type,
251259
SentryEvent *exceptionEvent = [[SENTRY_APPLE_CLASS(SentryEvent) alloc] init];
252260
exceptionEvent.exceptions = nativeExceptionArray;
253261

254-
SentryId* id = [SENTRY_APPLE_CLASS(SentrySDK) captureEvent:exceptionEvent];
255-
return MakeShareable(new SentryIdApple(id));
262+
SentryId* nativeId = [SENTRY_APPLE_CLASS(SentrySDK) captureEvent:exceptionEvent];
263+
264+
TSharedPtr<ISentryId> id = MakeShareable(new SentryIdApple(nativeId));
265+
266+
if (isGameLogAttachmentEnabled)
267+
{
268+
UploadGameLogForEvent(id, GetGameLogPath());
269+
}
270+
271+
return id;
256272
}
257273

258274

@@ -389,37 +405,60 @@ TSharedPtr<ISentryTransactionContext> FAppleSentrySubsystem::ContinueTrace(const
389405
return MakeShareable(new SentryTransactionContextApple(transactionContext));
390406
}
391407

392-
void FAppleSentrySubsystem::UploadScreenshotForEvent(TSharedPtr<ISentryId> eventId, const FString& screenshotPath) const
408+
void FAppleSentrySubsystem::UploadAttachmentForEvent(TSharedPtr<ISentryId> eventId, const FString& filePath, const FString& name, bool deleteAfterUpload) const
393409
{
394410
IFileManager& fileManager = IFileManager::Get();
395-
if (!fileManager.FileExists(*screenshotPath))
411+
if (!fileManager.FileExists(*filePath))
396412
{
397-
UE_LOG(LogSentrySdk, Error, TEXT("Failed to upload screenshot - path provided did not exist: %s"), *screenshotPath);
413+
UE_LOG(LogSentrySdk, Error, TEXT("Failed to upload attachment - file path provided did not exist: %s"), *filePath);
398414
return;
399415
}
400416

401-
const FString& screenshotFilePathExt = fileManager.ConvertToAbsolutePathForExternalAppForRead(*screenshotPath);
417+
const FString& filePathExt = fileManager.ConvertToAbsolutePathForExternalAppForRead(*filePath);
402418

403-
SentryAttachment* screenshotAttachment = [[SENTRY_APPLE_CLASS(SentryAttachment) alloc] initWithPath:screenshotFilePathExt.GetNSString() filename:@"screenshot.png"];
419+
SentryAttachment* attachment = [[SENTRY_APPLE_CLASS(SentryAttachment) alloc] initWithPath:filePathExt.GetNSString() filename:name.GetNSString()];
404420

405421
SentryOptions* options = [SENTRY_APPLE_CLASS(PrivateSentrySDKOnly) options];
406422
int32 size = options.maxAttachmentSize;
407423

408-
SentryEnvelopeItem* envelopeItem = [[SENTRY_APPLE_CLASS(SentryEnvelopeItem) alloc] initWithAttachment:screenshotAttachment maxAttachmentSize:size];
424+
SentryEnvelopeItem* envelopeItem = [[SENTRY_APPLE_CLASS(SentryEnvelopeItem) alloc] initWithAttachment:attachment maxAttachmentSize:size];
409425

410426
SentryId* id = StaticCastSharedPtr<SentryIdApple>(eventId)->GetNativeObject();
411427

412428
SentryEnvelope* envelope = [[SENTRY_APPLE_CLASS(SentryEnvelope) alloc] initWithId:id singleItem:envelopeItem];
413429

414430
[SENTRY_APPLE_CLASS(PrivateSentrySDKOnly) captureEnvelope:envelope];
415431

416-
// After uploading screenshot it's no longer needed so delete
417-
if (!fileManager.Delete(*screenshotPath))
432+
if (deleteAfterUpload)
418433
{
419-
UE_LOG(LogSentrySdk, Error, TEXT("Failed to delete screenshot: %s"), *screenshotPath);
434+
if (!fileManager.Delete(*filePath))
435+
{
436+
UE_LOG(LogSentrySdk, Error, TEXT("Failed to delete file attachment after upload: %s"), *filePath);
437+
}
420438
}
421439
}
422440

441+
void FAppleSentrySubsystem::UploadScreenshotForEvent(TSharedPtr<ISentryId> eventId, const FString& screenshotPath) const
442+
{
443+
if (screenshotPath.IsEmpty())
444+
{
445+
// Screenshot capturing is a best-effort solution so if one wasn't captured (path is empty) skip the upload
446+
return;
447+
}
448+
449+
UploadAttachmentForEvent(eventId, screenshotPath, TEXT("screenshot.png"), true);
450+
}
451+
452+
void FAppleSentrySubsystem::UploadGameLogForEvent(TSharedPtr<ISentryId> eventId, const FString& logFilePath) const
453+
{
454+
#if NO_LOGGING
455+
// If writing logs to a file is disabled (i.e. default behavior for Shipping builds) skip the upload
456+
return;
457+
#endif
458+
459+
UploadAttachmentForEvent(eventId, logFilePath, SentryFileUtils::GetGameLogName());
460+
}
461+
423462
FString FAppleSentrySubsystem::GetScreenshotPath() const
424463
{
425464
return FPaths::Combine(FPaths::ProjectSavedDir(), TEXT("SentryScreenshots"), FString::Printf(TEXT("screenshot-%s.png"), *FDateTime::Now().ToString()));

plugin-dev/Source/Sentry/Private/Apple/AppleSentrySubsystem.h

+9
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,17 @@ class FAppleSentrySubsystem : public ISentrySubsystem
3838
virtual FString TryCaptureScreenshot() const { return FString(); };
3939

4040
protected:
41+
void UploadAttachmentForEvent(TSharedPtr<ISentryId> eventId, const FString& filePath, const FString& name, bool deleteAfterUpload = false) const;
42+
4143
void UploadScreenshotForEvent(TSharedPtr<ISentryId> eventId, const FString& screenshotPath) const;
44+
void UploadGameLogForEvent(TSharedPtr<ISentryId> eventId, const FString& logFilePath) const;
4245

4346
virtual FString GetScreenshotPath() const;
4447
virtual FString GetLatestScreenshot() const;
48+
virtual FString GetGameLogPath() const { return FString(); };
49+
virtual FString GetLatestGameLog() const { return FString(); };
50+
51+
protected:
52+
bool isScreenshotAttachmentEnabled = false;
53+
bool isGameLogAttachmentEnabled = false;
4554
};

plugin-dev/Source/Sentry/Private/IOS/IOSSentrySubsystem.cpp

+32-1
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,13 @@
55
#include "SentryDefines.h"
66
#include "SentrySettings.h"
77

8+
#include "Utils/SentryScreenshotUtils.h"
9+
#include "Utils/SentryFileUtils.h"
10+
11+
#include "HAL/FileManager.h"
812
#include "Misc/CoreDelegates.h"
913
#include "Misc/FileHelper.h"
1014
#include "Misc/Paths.h"
11-
#include "Utils/SentryScreenshotUtils.h"
1215

1316
static FIOSSentrySubsystem* GIOSSentrySubsystem = nullptr;
1417

@@ -103,3 +106,31 @@ FString FIOSSentrySubsystem::TryCaptureScreenshot() const
103106

104107
return ScreenshotPath;
105108
}
109+
110+
FString FIOSSentrySubsystem::GetGameLogPath() const
111+
{
112+
const FString& logFilePath = SentryFileUtils::GetGameLogPath();
113+
return IFileManager::Get().FileExists(*logFilePath) ? logFilePath : NormalizeToPublicIOSPath(logFilePath);
114+
}
115+
116+
FString FIOSSentrySubsystem::GetLatestGameLog() const
117+
{
118+
const FString logFilePath = SentryFileUtils::GetGameLogBackupPath();
119+
return IFileManager::Get().FileExists(*logFilePath) ? logFilePath : NormalizeToPublicIOSPath(logFilePath);
120+
}
121+
122+
FString FIOSSentrySubsystem::NormalizeToPublicIOSPath(const FString& logFilePath) const
123+
{
124+
// This is a workaround for iOS log file not being accessible via the path returned by engine's API.
125+
// See https://github.com/getsentry/sentry-unreal/pull/732
126+
127+
static FString PublicWritePathBase = FString([NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]);
128+
static FString PrivateWritePathBase = FString([NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0]);
129+
130+
if (logFilePath.StartsWith(PrivateWritePathBase))
131+
{
132+
return logFilePath.Replace(*PrivateWritePathBase, *PublicWritePathBase);
133+
}
134+
135+
return logFilePath;
136+
}
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,25 @@
1-
#pragma once
2-
3-
#include "Apple/AppleSentrySubsystem.h"
4-
5-
class FIOSSentrySubsystem : public FAppleSentrySubsystem
6-
{
7-
public:
8-
virtual void InitWithSettings(
9-
const USentrySettings* Settings,
10-
USentryBeforeSendHandler* BeforeSendHandler,
11-
USentryBeforeBreadcrumbHandler* BeforeBreadcrumbHandler,
12-
USentryTraceSampler* TraceSampler
13-
) override;
14-
15-
virtual FString TryCaptureScreenshot() const override;
16-
};
17-
18-
typedef FIOSSentrySubsystem FPlatformSentrySubsystem;
1+
#pragma once
2+
3+
#include "Apple/AppleSentrySubsystem.h"
4+
5+
class FIOSSentrySubsystem : public FAppleSentrySubsystem
6+
{
7+
public:
8+
virtual void InitWithSettings(
9+
const USentrySettings* Settings,
10+
USentryBeforeSendHandler* BeforeSendHandler,
11+
USentryBeforeBreadcrumbHandler* BeforeBreadcrumbHandler,
12+
USentryTraceSampler* TraceSampler
13+
) override;
14+
15+
virtual FString TryCaptureScreenshot() const override;
16+
17+
protected:
18+
virtual FString GetGameLogPath() const override;
19+
virtual FString GetLatestGameLog() const override;
20+
21+
private:
22+
FString NormalizeToPublicIOSPath(const FString& logFilePath) const;
23+
};
24+
25+
typedef FIOSSentrySubsystem FPlatformSentrySubsystem;

plugin-dev/Source/Sentry/Private/Mac/MacSentrySubsystem.cpp

+14-7
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
#include "SentryModule.h"
77
#include "SentrySettings.h"
88

9+
#include "Utils/SentryFileUtils.h"
10+
911
#include "Misc/CoreDelegates.h"
1012
#include "Misc/FileHelper.h"
1113
#include "Misc/Paths.h"
@@ -15,9 +17,7 @@ void FMacSentrySubsystem::InitWithSettings(const USentrySettings* Settings, USen
1517
{
1618
FAppleSentrySubsystem::InitWithSettings(Settings, BeforeSendHandler, BeforeBreadcrumbHandler, TraceSampler);
1719

18-
isScreenshotAttachmentEnabled = Settings->AttachScreenshot;
19-
20-
if (IsEnabled() && Settings->AttachScreenshot)
20+
if (IsEnabled() && isScreenshotAttachmentEnabled)
2121
{
2222
FCoreDelegates::OnHandleSystemError.AddLambda([this]()
2323
{
@@ -33,10 +33,7 @@ TSharedPtr<ISentryId> FMacSentrySubsystem::CaptureEnsure(const FString& type, co
3333
if (isScreenshotAttachmentEnabled)
3434
{
3535
const FString& screenshotPath = TryCaptureScreenshot();
36-
if (!screenshotPath.IsEmpty())
37-
{
38-
UploadScreenshotForEvent(id, screenshotPath);
39-
}
36+
UploadScreenshotForEvent(id, screenshotPath);
4037
}
4138

4239
return id;
@@ -81,3 +78,13 @@ FString FMacSentrySubsystem::TryCaptureScreenshot() const
8178

8279
return ScreenshotPath;
8380
}
81+
82+
FString FMacSentrySubsystem::GetGameLogPath() const
83+
{
84+
return SentryFileUtils::GetGameLogPath();
85+
}
86+
87+
FString FMacSentrySubsystem::GetLatestGameLog() const
88+
{
89+
return SentryFileUtils::GetGameLogBackupPath();
90+
}

plugin-dev/Source/Sentry/Private/Mac/MacSentrySubsystem.h

+3-2
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@ class FMacSentrySubsystem : public FAppleSentrySubsystem
1616

1717
virtual FString TryCaptureScreenshot() const override;
1818

19-
private:
20-
bool isScreenshotAttachmentEnabled = false;
19+
protected:
20+
virtual FString GetGameLogPath() const override;
21+
virtual FString GetLatestGameLog() const override;
2122
};
2223

2324
typedef FMacSentrySubsystem FPlatformSentrySubsystem;

plugin-dev/Source/Sentry/Private/Utils/SentryFileUtils.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ struct FSentrySortFileByDatePredicate
1717
}
1818
};
1919

20+
FString SentryFileUtils::GetGameLogName()
21+
{
22+
return FPaths::GetCleanFilename(FGenericPlatformOutputDevices::GetAbsoluteLogFilename());
23+
}
24+
2025
FString SentryFileUtils::GetGameLogPath()
2126
{
2227
return IFileManager::Get().ConvertToAbsolutePathForExternalAppForRead(*FGenericPlatformOutputDevices::GetAbsoluteLogFilename());

0 commit comments

Comments
 (0)