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
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@

### Features

- The _Metrics_ APIs are now stable: removed `Experimental` from `SentrySdk` and `SentryOptions`. ([#2615](https://github.com/getsentry/sentry-unity/pull/2615))
- The _Metrics_ APIs are now stable: removed `Experimental` from `SentrySdk` and `SentryOptions` ([#2615](https://github.com/getsentry/sentry-unity/pull/2615))
- Attachments added to the scope are now included in native crash reports on Android, Windows, and Linux ([#2609](https://github.com/getsentry/sentry-unity/pull/2609))

### Dependencies

Expand Down
9 changes: 7 additions & 2 deletions src/Sentry.Unity.Android/AndroidJavaScopeObserver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,12 @@ public override void UnsetUserImpl() =>
public override void SetTraceImpl(SentryId traceId, SpanId spanId) =>
_sentryJava.SetTrace(traceId, spanId);

public override void AddAttachmentImpl(SentryAttachment attachment) { }
public override void AddFileAttachmentImpl(string filePath, string fileName, string? contentType) =>
_sentryJava.AddAttachment(filePath, fileName, contentType);

public override void ClearAttachmentsImpl() { }
public override void AddByteAttachmentImpl(byte[] data, string fileName, string? contentType) =>
_sentryJava.AddAttachmentBytes(data, fileName, contentType);

public override void ClearAttachmentsImpl() =>
_sentryJava.ClearAttachments();
}
41 changes: 41 additions & 0 deletions src/Sentry.Unity.Android/SentryJava.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ public void WriteScope(
public void SetUser(SentryUser user);
public void UnsetUser();
public void SetTrace(SentryId traceId, SpanId spanId);
void AddAttachment(string path, string fileName, string? contentType);
void AddAttachmentBytes(byte[] data, string fileName, string? contentType);
void ClearAttachments();
}

/// <summary>
Expand Down Expand Up @@ -361,6 +364,44 @@ public void SetTrace(SentryId traceId, SpanId spanId)
});
}

public void AddAttachment(string path, string fileName, string? contentType)
{
RunJniSafe(() =>
{
using var attachment = contentType is not null
? new AndroidJavaObject("io.sentry.Attachment", path, fileName, contentType)
: new AndroidJavaObject("io.sentry.Attachment", path, fileName);

using var sentry = GetSentryJava();
sentry.CallStatic("configureScope", new ScopeCallback(scope =>
scope.Call("addAttachment", attachment)));
});
}

public void AddAttachmentBytes(byte[] data, string fileName, string? contentType)
{
RunJniSafe(() =>
{
using var attachment = contentType is not null
? new AndroidJavaObject("io.sentry.Attachment", data, fileName, contentType)
: new AndroidJavaObject("io.sentry.Attachment", data, fileName);

using var sentry = GetSentryJava();
sentry.CallStatic("configureScope", new ScopeCallback(scope =>
scope.Call("addAttachment", attachment)));
});
}

public void ClearAttachments()
{
RunJniSafe(() =>
{
using var sentry = GetSentryJava();
sentry.CallStatic("configureScope", new ScopeCallback(scope =>
scope.Call("clearAttachments")));
});
}

// https://github.com/getsentry/sentry-java/blob/db4dfc92f202b1cefc48d019fdabe24d487db923/sentry/src/main/java/io/sentry/SentryLevel.java#L4-L9
internal static string GetLevelString(SentryLevel level) => level switch
{
Expand Down
9 changes: 9 additions & 0 deletions src/Sentry.Unity.Native/CFunctions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,15 @@ internal static void SetValueIfNotNull(sentry_value_t obj, string key, long? val
[DllImport(SentryLib)]
internal static extern void sentry_set_trace(string traceId, string parentSpanId);

[DllImport(SentryLib)]
internal static extern IntPtr sentry_attach_file(string path);

[DllImport(SentryLib)]
internal static extern IntPtr sentry_attach_bytes(byte[] buf, UIntPtr buf_len, string filename);

[DllImport(SentryLib)]
internal static extern void sentry_clear_attachments();
Comment on lines +168 to +175
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: The contentType for attachments is silently discarded in the native implementation for Windows/Linux, as the P/Invoke calls sentry_attach_file and sentry_attach_bytes do not pass this parameter.
Severity: MEDIUM

Suggested Fix

Investigate if the underlying sentry-native C API supports setting the content type. If it does, update the P/Invoke signatures and the NativeScopeObserver.cs implementation to pass the contentType to the native layer. If not, add logging to warn the user that contentType is being ignored on native platforms.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.

Location: src/Sentry.Unity.Native/CFunctions.cs#L168-L175

Potential issue: The abstract `AddAttachment` method and its implementations in
`ScopeObserver` correctly handle a `contentType` parameter. However, the native
implementation for Windows/Linux in `NativeScopeObserver.cs` calls P/Invoke functions
(`sentry_attach_file` and `sentry_attach_bytes`) that do not accept a `contentType`.
This causes the content type information to be silently lost for attachments on these
platforms, creating an inconsistency with the Android implementation which correctly
preserves it. This degrades functionality by stripping metadata from native attachments.

Did we get this right? 👍 / 👎 to inform future reviews.


internal static readonly Lazy<IEnumerable<DebugImage>> DebugImages = new(LoadDebugImages);

private static IEnumerable<DebugImage> LoadDebugImages()
Expand Down
9 changes: 7 additions & 2 deletions src/Sentry.Unity.Native/NativeScopeObserver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,14 @@ public override void SetUserImpl(SentryUser user)
public override void SetTraceImpl(SentryId traceId, SpanId spanId) =>
C.sentry_set_trace(traceId.ToString(), spanId.ToString());

public override void AddAttachmentImpl(SentryAttachment attachment) { }
public override void AddFileAttachmentImpl(string filePath, string fileName, string? contentType) =>
C.sentry_attach_file(filePath);

public override void ClearAttachmentsImpl() { }
public override void AddByteAttachmentImpl(byte[] data, string fileName, string? contentType) =>
C.sentry_attach_bytes(data, (UIntPtr)data.Length, fileName);

public override void ClearAttachmentsImpl() =>
C.sentry_clear_attachments();

private static string GetTimestamp(DateTimeOffset timestamp) =>
// "o": Using ISO 8601 to make sure the timestamp makes it to the bridge correctly.
Expand Down
19 changes: 15 additions & 4 deletions src/Sentry.Unity.iOS/NativeScopeObserver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,26 @@ public override void SetUserImpl(SentryUser user) =>
public override void SetTraceImpl(SentryId traceId, SpanId spanId) =>
SentryCocoaBridgeProxy.SetTrace(traceId.ToString(), spanId.ToString());

public override void AddFileAttachmentImpl(string filePath, string fileName, string? contentType)
{
// iOS/macOS attachment sync to sentry-cocoa is not yet supported.
}

public override void AddByteAttachmentImpl(byte[] data, string fileName, string? contentType)
{
// iOS/macOS attachment sync to sentry-cocoa is not yet supported.
}

public override void ClearAttachmentsImpl()
{
// iOS/macOS attachment sync to sentry-cocoa is not yet supported.
}

internal static string GetTimestamp(DateTimeOffset timestamp) =>
// "o": Using ISO 8601 to make sure the timestamp makes it to the bridge correctly.
// https://docs.microsoft.com/en-gb/dotnet/standard/base-types/standard-date-and-time-format-strings#Roundtrip
timestamp.ToString("o");

public override void AddAttachmentImpl(SentryAttachment attachment) { }

public override void ClearAttachmentsImpl() { }

internal static int GetBreadcrumbLevel(BreadcrumbLevel breadcrumbLevel) =>
// https://github.com/getsentry/sentry-cocoa/blob/50f955aeb214601dd62b5dae7abdaddc8a1f24d9/Sources/Sentry/Public/SentryDefines.h#L99-L105
breadcrumbLevel switch
Expand Down
20 changes: 17 additions & 3 deletions src/Sentry.Unity/ScopeObserver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,25 @@ public void SetTrace(SentryId traceId, SpanId spanId)

public void AddAttachment(SentryAttachment attachment)
{
_options.LogDebug("{0} Scope Sync - Adding attachment", _name);
AddAttachmentImpl(attachment);
if (attachment.Content is FileAttachmentContent fileContent)
{
_options.LogDebug("{0} Scope Sync - Adding file attachment \"{1}\"", _name, fileContent.FilePath);
AddFileAttachmentImpl(fileContent.FilePath, attachment.FileName, attachment.ContentType);
}
else if (attachment.Content is ByteAttachmentContent byteContent)
{
_options.LogDebug("{0} Scope Sync - Adding byte attachment \"{1}\" ({2} bytes)", _name, attachment.FileName, byteContent.Bytes.Length);
AddByteAttachmentImpl(byteContent.Bytes, attachment.FileName, attachment.ContentType);
}
else
{
_options.LogDebug("{0} Scope Sync - Skipping attachment \"{1}\" (unsupported content type for native sync)", _name, attachment.FileName);
}
}

public abstract void AddAttachmentImpl(SentryAttachment attachment);
public abstract void AddFileAttachmentImpl(string filePath, string fileName, string? contentType);

public abstract void AddByteAttachmentImpl(byte[] data, string fileName, string? contentType);

public void ClearAttachments()
{
Expand Down
6 changes: 6 additions & 0 deletions test/Sentry.Unity.Android.Tests/TestSentryJava.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,10 @@ public void SetUser(SentryUser user) { }
public void UnsetUser() { }

public void SetTrace(SentryId traceId, SpanId spanId) { }

public void AddAttachment(string path, string fileName, string? contentType) { }

public void AddAttachmentBytes(byte[] data, string fileName, string? contentType) { }

public void ClearAttachments() { }
}
Loading