Skip to content

Commit 00a7d9d

Browse files
authored
Add runtime attachments support for Windows/Linux (#982)
* Add attachment desktop-specific class * Add attachments support for desktop scope * Adopt wide char version of the attachment API for Microsoft platforms (Win, Xbox) * Add functions to add/remove attachments in global scope * Fix build error * Fix lint errors * Fix comments * Update changelog * Update snapshot * Resert attachment native object reference after it was removed * Remove reference * Return attachment byte data by ref during adding * Add function allowing to clear all attachments from the global scope `ClearAttachments` replaces `RemoveAttachment` which is currently not availbale on mobile. For desktop, user-defined attachments are cached on the Unreal side while this functionality isn't available in sentry-native.
1 parent 2b11b66 commit 00a7d9d

23 files changed

+439
-2
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
### Features
1616

1717
- Adopt generic variant type in public APIs ([#971](https://github.com/getsentry/sentry-unreal/pull/971))
18+
- Add runtime attachments support for Windows/Linux ([#982](https://github.com/getsentry/sentry-unreal/pull/982))
1819

1920
### Dependencies
2021

plugin-dev/Source/Sentry/Private/Android/AndroidSentrySubsystem.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include "AndroidSentrySubsystem.h"
44

5+
#include "AndroidSentryAttachment.h"
56
#include "AndroidSentryBreadcrumb.h"
67
#include "AndroidSentryEvent.h"
78
#include "AndroidSentryId.h"
@@ -127,6 +128,27 @@ void FAndroidSentrySubsystem::ClearBreadcrumbs()
127128
FSentryJavaObjectWrapper::CallStaticMethod<void>(SentryJavaClasses::Sentry, "clearBreadcrumbs", "()V");
128129
}
129130

131+
void FAndroidSentrySubsystem::AddAttachment(TSharedPtr<ISentryAttachment> attachment)
132+
{
133+
TSharedPtr<FAndroidSentryAttachment> attachmentAndroid = StaticCastSharedPtr<FAndroidSentryAttachment>(attachment);
134+
135+
FSentryJavaObjectWrapper::CallStaticMethod<void>(SentryJavaClasses::SentryBridgeJava, "addAttachment", "(Lio/sentry/Attachment;)V",
136+
attachmentAndroid->GetJObject());
137+
}
138+
139+
void FAndroidSentrySubsystem::RemoveAttachment(TSharedPtr<ISentryAttachment> attachment)
140+
{
141+
TSharedPtr<FAndroidSentryAttachment> attachmentAndroid = StaticCastSharedPtr<FAndroidSentryAttachment>(attachment);
142+
143+
FSentryJavaObjectWrapper::CallStaticMethod<void>(SentryJavaClasses::SentryBridgeJava, "removeAttachment", "(Lio/sentry/Attachment;)V",
144+
attachmentAndroid->GetJObject());
145+
}
146+
147+
void FAndroidSentrySubsystem::ClearAttachments()
148+
{
149+
FSentryJavaObjectWrapper::CallStaticMethod<void>(SentryJavaClasses::SentryBridgeJava, "clearAttachments", "()V");
150+
}
151+
130152
TSharedPtr<ISentryId> FAndroidSentrySubsystem::CaptureMessage(const FString& message, ESentryLevel level)
131153
{
132154
auto id = FSentryJavaObjectWrapper::CallStaticObjectMethod<jobject>(SentryJavaClasses::Sentry, "captureMessage", "(Ljava/lang/String;Lio/sentry/SentryLevel;)Lio/sentry/protocol/SentryId;",

plugin-dev/Source/Sentry/Private/Android/AndroidSentrySubsystem.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ class FAndroidSentrySubsystem : public ISentrySubsystem
1414
virtual void AddBreadcrumb(TSharedPtr<ISentryBreadcrumb> breadcrumb) override;
1515
virtual void AddBreadcrumbWithParams(const FString& Message, const FString& Category, const FString& Type, const TMap<FString, FSentryVariant>& Data, ESentryLevel Level) override;
1616
virtual void ClearBreadcrumbs() override;
17+
virtual void AddAttachment(TSharedPtr<ISentryAttachment> attachment) override;
18+
virtual void RemoveAttachment(TSharedPtr<ISentryAttachment> attachment) override;
19+
virtual void ClearAttachments() override;
1720
virtual TSharedPtr<ISentryId> CaptureMessage(const FString& message, ESentryLevel level) override;
1821
virtual TSharedPtr<ISentryId> CaptureMessageWithScope(const FString& message, ESentryLevel level, const FSentryScopeDelegate& onConfigureScope) override;
1922
virtual TSharedPtr<ISentryId> CaptureEvent(TSharedPtr<ISentryEvent> event) override;

plugin-dev/Source/Sentry/Private/Android/Java/SentryBridgeJava.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import java.util.HashMap;
1515
import java.util.Map;
1616

17+
import io.sentry.Attachment;
1718
import io.sentry.Breadcrumb;
1819
import io.sentry.Hint;
1920
import io.sentry.IScopes;
@@ -225,4 +226,17 @@ public static Object getScopeContext(final IScope scope, final String key) {
225226
public static void setScopeExtra(final IScope scope, final String key, final Object values) {
226227
scope.setExtra(key, values.toString());
227228
}
229+
230+
public static void addAttachment(final Attachment attachment) {
231+
Sentry.getGlobalScope().addAttachment(attachment);
232+
}
233+
234+
public static void removeAttachment(final Attachment attachment) {
235+
// Currently, Android SDK doesn't have API allowing to remove individual attachments
236+
}
237+
238+
public static void clearAttachments() {
239+
Sentry.getGlobalScope().clearAttachments();
240+
}
241+
228242
}

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include "AppleSentrySubsystem.h"
44

5+
#include "AppleSentryAttachment.h"
56
#include "AppleSentryBreadcrumb.h"
67
#include "AppleSentryEvent.h"
78
#include "AppleSentryId.h"
@@ -196,6 +197,27 @@ void FAppleSentrySubsystem::ClearBreadcrumbs()
196197
}];
197198
}
198199

200+
void FAppleSentrySubsystem::AddAttachment(TSharedPtr<ISentryAttachment> attachment)
201+
{
202+
TSharedPtr<FAppleSentryAttachment> attachmentApple = StaticCastSharedPtr<FAppleSentryAttachment>(attachment);
203+
204+
[SentrySDK configureScope:^(SentryScope* scope) {
205+
[scope addAttachment:attachmentApple->GetNativeObject()];
206+
}];
207+
}
208+
209+
void FAppleSentrySubsystem::RemoveAttachment(TSharedPtr<ISentryAttachment> attachment)
210+
{
211+
// Currently, Cocoa SDK doesn't have API allowing to remove individual attachments
212+
}
213+
214+
void FAppleSentrySubsystem::ClearAttachments()
215+
{
216+
[SentrySDK configureScope:^(SentryScope* scope) {
217+
[scope clearAttachments];
218+
}];
219+
}
220+
199221
TSharedPtr<ISentryId> FAppleSentrySubsystem::CaptureMessage(const FString& message, ESentryLevel level)
200222
{
201223
FSentryScopeDelegate onConfigureScope;

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ class FAppleSentrySubsystem : public ISentrySubsystem
1414
virtual void AddBreadcrumb(TSharedPtr<ISentryBreadcrumb> breadcrumb) override;
1515
virtual void AddBreadcrumbWithParams(const FString& Message, const FString& Category, const FString& Type, const TMap<FString, FSentryVariant>& Data, ESentryLevel Level) override;
1616
virtual void ClearBreadcrumbs() override;
17+
virtual void AddAttachment(TSharedPtr<ISentryAttachment> attachment) override;
18+
virtual void RemoveAttachment(TSharedPtr<ISentryAttachment> attachment) override;
19+
virtual void ClearAttachments() override;
1720
virtual TSharedPtr<ISentryId> CaptureMessage(const FString& message, ESentryLevel level) override;
1821
virtual TSharedPtr<ISentryId> CaptureMessageWithScope(const FString& message, ESentryLevel level, const FSentryScopeDelegate& onConfigureScope) override;
1922
virtual TSharedPtr<ISentryId> CaptureEvent(TSharedPtr<ISentryEvent> event) override;
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Copyright (c) 2025 Sentry. All Rights Reserved.
2+
3+
#include "GenericPlatformSentryAttachment.h"
4+
5+
#if USE_SENTRY_NATIVE
6+
7+
FGenericPlatformSentryAttachment::FGenericPlatformSentryAttachment(const TArray<uint8>& data, const FString& filename, const FString& contentType)
8+
: Data(data), Filename(filename), ContentType(contentType), Attachment(nullptr)
9+
{
10+
}
11+
12+
FGenericPlatformSentryAttachment::FGenericPlatformSentryAttachment(const FString& path, const FString& filename, const FString& contentType)
13+
: Path(path), Filename(filename), ContentType(contentType), Attachment(nullptr)
14+
{
15+
}
16+
17+
FGenericPlatformSentryAttachment::~FGenericPlatformSentryAttachment()
18+
{
19+
// Put custom destructor logic here if needed
20+
}
21+
22+
void FGenericPlatformSentryAttachment::SetNativeObject(sentry_attachment_t* attachment)
23+
{
24+
Attachment = attachment;
25+
}
26+
27+
sentry_attachment_t* FGenericPlatformSentryAttachment::GetNativeObject()
28+
{
29+
return Attachment;
30+
}
31+
32+
TArray<uint8> FGenericPlatformSentryAttachment::GetData() const
33+
{
34+
return Data;
35+
}
36+
37+
FString FGenericPlatformSentryAttachment::GetPath() const
38+
{
39+
return Path;
40+
}
41+
42+
FString FGenericPlatformSentryAttachment::GetFilename() const
43+
{
44+
return Filename;
45+
}
46+
47+
FString FGenericPlatformSentryAttachment::GetContentType() const
48+
{
49+
return ContentType;
50+
}
51+
52+
const TArray<uint8>& FGenericPlatformSentryAttachment::GetDataByRef() const
53+
{
54+
return Data;
55+
}
56+
57+
#endif
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Copyright (c) 2025 Sentry. All Rights Reserved.
2+
3+
#pragma once
4+
5+
#include "Convenience/GenericPlatformSentryInclude.h"
6+
7+
#include "Interface/SentryAttachmentInterface.h"
8+
9+
#if USE_SENTRY_NATIVE
10+
11+
class FGenericPlatformSentryAttachment : public ISentryAttachment
12+
{
13+
public:
14+
FGenericPlatformSentryAttachment(const TArray<uint8>& data, const FString& filename, const FString& contentType);
15+
FGenericPlatformSentryAttachment(const FString& path, const FString& filename, const FString& contentType);
16+
virtual ~FGenericPlatformSentryAttachment() override;
17+
18+
void SetNativeObject(sentry_attachment_t* attachment);
19+
sentry_attachment_t* GetNativeObject();
20+
21+
virtual TArray<uint8> GetData() const override;
22+
virtual FString GetPath() const override;
23+
virtual FString GetFilename() const override;
24+
virtual FString GetContentType() const override;
25+
26+
const TArray<uint8>& GetDataByRef() const;
27+
28+
private:
29+
TArray<uint8> Data;
30+
FString Path;
31+
FString Filename;
32+
FString ContentType;
33+
34+
sentry_attachment_t* Attachment;
35+
};
36+
37+
typedef FGenericPlatformSentryAttachment FPlatformSentryAttachment;
38+
39+
#endif

plugin-dev/Source/Sentry/Private/GenericPlatform/GenericPlatformSentryScope.cpp

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) 2025 Sentry. All Rights Reserved.
22

33
#include "GenericPlatformSentryScope.h"
4+
#include "GenericPlatformSentryAttachment.h"
45
#include "GenericPlatformSentryBreadcrumb.h"
56
#include "GenericPlatformSentryEvent.h"
67

@@ -39,12 +40,12 @@ void FGenericPlatformSentryScope::ClearBreadcrumbs()
3940

4041
void FGenericPlatformSentryScope::AddAttachment(TSharedPtr<ISentryAttachment> attachment)
4142
{
42-
// Not available for generic platform
43+
Attachments.Add(StaticCastSharedPtr<FGenericPlatformSentryAttachment>(attachment));
4344
}
4445

4546
void FGenericPlatformSentryScope::ClearAttachments()
4647
{
47-
// Not available for generic platform
48+
Attachments.Empty();
4849
}
4950

5051
void FGenericPlatformSentryScope::SetTag(const FString& key, const FString& value)
@@ -195,6 +196,18 @@ void FGenericPlatformSentryScope::Apply(sentry_scope_t* scope)
195196
sentry_scope_add_breadcrumb(scope, nativeBreadcrumb);
196197
}
197198

199+
for (auto& Attachment : Attachments)
200+
{
201+
if (!Attachment->GetPath().IsEmpty())
202+
{
203+
AddFileAttachment(Attachment, scope);
204+
}
205+
else
206+
{
207+
AddByteAttachment(Attachment, scope);
208+
}
209+
}
210+
198211
if (Fingerprint.Num() > 0)
199212
{
200213
sentry_scope_set_fingerprints(scope, FGenericPlatformSentryConverters::StringArrayToNative(Fingerprint));
@@ -218,4 +231,31 @@ void FGenericPlatformSentryScope::Apply(sentry_scope_t* scope)
218231
sentry_scope_set_level(scope, FGenericPlatformSentryConverters::SentryLevelToNative(Level));
219232
}
220233

234+
void FGenericPlatformSentryScope::AddFileAttachment(TSharedPtr<FGenericPlatformSentryAttachment> attachment, sentry_scope_t* scope)
235+
{
236+
sentry_attachment_t* nativeAttachment =
237+
sentry_scope_attach_file(scope, TCHAR_TO_UTF8(*attachment->GetPath()));
238+
239+
if (!attachment->GetFilename().IsEmpty())
240+
sentry_attachment_set_filename(nativeAttachment, TCHAR_TO_UTF8(*attachment->GetFilename()));
241+
242+
if (!attachment->GetContentType().IsEmpty())
243+
sentry_attachment_set_content_type(nativeAttachment, TCHAR_TO_UTF8(*attachment->GetContentType()));
244+
245+
attachment->SetNativeObject(nativeAttachment);
246+
}
247+
248+
void FGenericPlatformSentryScope::AddByteAttachment(TSharedPtr<FGenericPlatformSentryAttachment> attachment, sentry_scope_t* scope)
249+
{
250+
const TArray<uint8>& byteBuf = attachment->GetDataByRef();
251+
252+
sentry_attachment_t* nativeAttachment =
253+
sentry_scope_attach_bytes(scope, reinterpret_cast<const char*>(byteBuf.GetData()), byteBuf.Num(), TCHAR_TO_UTF8(*attachment->GetFilename()));
254+
255+
if (!attachment->GetContentType().IsEmpty())
256+
sentry_attachment_set_content_type(nativeAttachment, TCHAR_TO_UTF8(*attachment->GetContentType()));
257+
258+
attachment->SetNativeObject(nativeAttachment);
259+
}
260+
221261
#endif

plugin-dev/Source/Sentry/Private/GenericPlatform/GenericPlatformSentryScope.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
#if USE_SENTRY_NATIVE
1111

12+
class FGenericPlatformSentryAttachment;
1213
class FGenericPlatformSentryBreadcrumb;
1314
class FGenericPlatformSentryEvent;
1415

@@ -46,6 +47,10 @@ class FGenericPlatformSentryScope : public ISentryScope
4647

4748
void Apply(sentry_scope_t* scope);
4849

50+
protected:
51+
virtual void AddFileAttachment(TSharedPtr<FGenericPlatformSentryAttachment> attachment, sentry_scope_t* scope);
52+
virtual void AddByteAttachment(TSharedPtr<FGenericPlatformSentryAttachment> attachment, sentry_scope_t* scope);
53+
4954
private:
5055
FString Dist;
5156
FString Environment;
@@ -59,9 +64,13 @@ class FGenericPlatformSentryScope : public ISentryScope
5964

6065
TRingBuffer<TSharedPtr<FGenericPlatformSentryBreadcrumb>> Breadcrumbs;
6166

67+
TArray<TSharedPtr<FGenericPlatformSentryAttachment>> Attachments;
68+
6269
ESentryLevel Level;
6370
};
6471

72+
#if !PLATFORM_MICROSOFT
6573
typedef FGenericPlatformSentryScope FPlatformSentryScope;
74+
#endif
6675

6776
#endif

0 commit comments

Comments
 (0)