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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

### Fixes

- Prevent crashes from occurring on Android during OnBeforeSend ([#4022](https://github.com/getsentry/sentry-dotnet/pull/4022))

### Dependencies

- Bump Native SDK from v0.8.0 to v0.8.1 ([#4014](https://github.com/getsentry/sentry-dotnet/pull/4014))
Expand Down
11 changes: 11 additions & 0 deletions samples/Sentry.Samples.Android/MainActivity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,17 @@ protected override void OnCreate(Bundle? savedInstanceState)
// https://docs.sentry.io/platforms/android/configuration/
// Enable Native Android SDK ANR detection
options.Native.AnrEnabled = true;

options.SetBeforeSend(evt =>
{
if (evt.Exception?.Message.Contains("Something you don't care want logged?") ?? false)
{
return null; // return null to filter out event
}
// or add additional data
evt.SetTag("dotnet-Android-Native-Before", "Hello World");
return evt;
});
});

// Here's an example of adding custom scope information.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0-android34.0</TargetFramework>
<TargetFramework>net8.0-android</TargetFramework>
<SupportedOSPlatformVersion>21</SupportedOSPlatformVersion>
<OutputType>Exe</OutputType>
<Nullable>enable</Nullable>
Expand Down
7 changes: 6 additions & 1 deletion samples/Sentry.Samples.Ios/AppDelegate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,13 @@ public override bool FinishedLaunching(UIApplication application, NSDictionary l

options.CacheDirectoryPath = Path.GetTempPath();

options.SetBeforeSend((evt, _) =>
options.SetBeforeSend(evt =>
{
if (evt.Exception?.Message.Contains("Something you don't care want logged?") ?? false)
{
return null; // return null to filter out event
}
// or add additional data
evt.SetTag("dotnet-iOS-Native-Before", "Hello World");
return evt;
});
Expand Down
9 changes: 8 additions & 1 deletion src/Sentry/Internal/Extensions/JsonExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,14 @@ public static void Deconstruct(this JsonProperty jsonProperty, out string name,

foreach (var (name, value) in json.EnumerateObject())
{
result[name] = value.GetString();
if (value.ValueKind == JsonValueKind.String)
{
result[name] = value.GetString();
}
else
{
result[name] = value.ToString();
}
}

return result;
Expand Down
20 changes: 15 additions & 5 deletions src/Sentry/Platforms/Android/Callbacks/BeforeSendCallback.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Sentry.Android.Extensions;
using Sentry.Extensibility;

namespace Sentry.Android.Callbacks;

Expand All @@ -22,10 +23,19 @@ public BeforeSendCallback(
{
// Note: Hint is unused due to:
// https://github.com/getsentry/sentry-dotnet/issues/1469

var evnt = e.ToSentryEvent(_javaOptions);
var hint = h.ToHint();
var result = _beforeSend?.Invoke(evnt, hint);
return result?.ToJavaSentryEvent(_options, _javaOptions);
try
{
// because this can go out to user code, we want to prevent external crashing
// also, native types tend to move before dotnet does, so serialization can fail
var evnt = e.ToSentryEvent(_javaOptions);
var hint = h.ToHint();
var result = _beforeSend?.Invoke(evnt, hint);
return result?.ToJavaSentryEvent(_options, _javaOptions);
}
catch (Exception exception)
{
_options.LogError(exception, "Before Send Error");
return e;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using Sentry.Internal;

namespace Sentry.Android.Extensions;

internal static class SentryEventExtensions
Expand All @@ -17,6 +19,12 @@ internal static class SentryEventExtensions

public static SentryEvent ToSentryEvent(this JavaSdk.SentryEvent sentryEvent, JavaSdk.SentryOptions javaOptions)
{
if (sentryEvent.Sdk != null)
{
// when we cast this serialize this over, this value must be set
sentryEvent.Sdk.Name ??= Constants.SdkName;
sentryEvent.Sdk.Version ??= SdkVersion.Instance.Version ?? "0.0.0";
}
using var stream = new MemoryStream();
using var streamWriter = new JavaOutputStreamWriter(stream);
using var jsonWriter = new JavaSdk.JsonObjectWriter(streamWriter, javaOptions.MaxDepth);
Expand All @@ -31,6 +39,11 @@ public static SentryEvent ToSentryEvent(this JavaSdk.SentryEvent sentryEvent, Ja

public static JavaSdk.SentryEvent ToJavaSentryEvent(this SentryEvent sentryEvent, SentryOptions options, JavaSdk.SentryOptions javaOptions)
{
if (sentryEvent.Sdk != null)
{
sentryEvent.Sdk.Name ??= Constants.SdkName;
sentryEvent.Sdk.Version ??= SdkVersion.Instance.Version ?? "0.0.0";
}
using var stream = new MemoryStream();
using var jsonWriter = new Utf8JsonWriter(stream);
sentryEvent.WriteTo(jsonWriter, options.DiagnosticLogger);
Expand Down
5 changes: 4 additions & 1 deletion src/Sentry/Platforms/Android/SentrySdk.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,10 @@ private static void InitSentryAndroidSdk(SentryOptions options)
}
}

o.BeforeSend = new BeforeSendCallback(BeforeSendWrapper(options), options, o);
if (options.Android.SuppressSegfaults || (options.Native.EnableBeforeSend && options.BeforeSendInternal != null))
{
o.BeforeSend = new BeforeSendCallback(BeforeSendWrapper(options), options, o);
}

// These options are from SentryAndroidOptions
o.AttachScreenshot = options.Native.AttachScreenshot;
Expand Down
126 changes: 126 additions & 0 deletions test/Sentry.Tests/Platforms/Android/JsonExtensionsTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
using System;
using System.Linq;
using FluentAssertions;
using Sentry.Android.Extensions;
using Xunit;

namespace Sentry.Tests.Platforms.Android;

public class JsonExtensionsTests

{
[Fact]
public void ToJavaSentryEvent_Success()

{
var evt = new SentryEvent(new Exception("Test Exception"));

evt.Level = SentryLevel.Debug;
evt.ServerName = "test server name";
evt.Distribution = "test distribution";
evt.Logger = "test logger";
evt.Release = "test release";
evt.Environment = "test environment";
evt.TransactionName = "test transaction name";
evt.Message = new SentryMessage
{
Params = ["Test"]
};

evt.SetTag("TestTagKey", "TestTagValue");
evt.AddBreadcrumb(new Breadcrumb("test breadcrumb", "test type"));
evt.SetExtra("TestExtraKey", "TestExtraValue");
evt.User = new SentryUser
{
Id = "user id",
Username = "test",
Email = "[email protected]",
IpAddress = "127.0.0.1"
};

var native = evt.ToJavaSentryEvent(new SentryOptions(), new JavaSdk.SentryOptions());

AssertEqual(evt, native);
}


[Fact]
public void ToSentryEvent_ConvertToManaged()
{
var native = new JavaSdk.SentryEvent();

native.Throwable = new Exception("Test Exception").ToThrowable();
native.Timestamp = DateTimeOffset.UtcNow.ToJavaDate();
native.Level = JavaSdk.SentryLevel.Debug;
native.ServerName = "native server name";
native.Dist = "native dist";
native.Logger = "native logger";
native.Release = "native release";
native.Environment = "native env";
native.Transaction = "native transaction";
native.Message = new JavaSdk.Protocol.Message
{
Params = ["Test"]
};
native.SetTag("TestTagKey", "TestTagValue");
native.SetExtra("TestExtraKey", "TestExtraValue");
native.Breadcrumbs =
[
new JavaSdk.Breadcrumb
{
Category = "category",
Level = JavaSdk.SentryLevel.Debug
}
];

native.User = new JavaSdk.Protocol.User
{
Id = "user id",
Username = "test",
Email = "[email protected]",
IpAddress = "127.0.0.1"
};

var managed = native.ToSentryEvent(new JavaSdk.SentryOptions());

AssertEqual(managed, native);
}


private static void AssertEqual(SentryEvent managed, JavaSdk.SentryEvent native)
{
native.ServerName.Should().Be(managed.ServerName, "Server Name");
native.Dist.Should().Be(managed.Distribution, "Distribution");
native.Logger.Should().Be(managed.Logger, "Logger");
native.Release.Should().Be(managed.Release, "Release");
native.Environment.Should().Be(managed.Environment, "Environment");
native.Transaction.Should().Be(managed.TransactionName!, "Transaction");
native.Level!.ToString().ToUpper().Should().Be(managed.Level!.ToString()!.ToUpper(), "Level");
// native.Throwable.Message.Should().Be(managed.Exception!.Message, "Message should match");

// extras
native.Extras.Should().NotBeNull("No extras found");
native.Extras!.Count.Should().Be(1, "Extras should have 1 item");
native.Extras!.Keys!.First().Should().Be(managed.Extra.Keys.First(), "Extras key should match");
native.Extras!.Values!.First().ToString().Should().Be(managed.Extra.Values.First().ToString(), "Extra value should match");

// tags
native.Tags.Should().NotBeNull("No tags found");
native.Tags!.Count.Should().Be(1, "Tags should have 1 item");
native.Tags!.Keys!.First().Should().Be(managed.Tags.Keys.First());
native.Tags!.Values!.First().Should().Be(managed.Tags.Values.First());

// breadcrumbs
native.Breadcrumbs.Should().NotBeNull("No breadcrumbs found");
var nb = native.Breadcrumbs!.First();
var mb = managed.Breadcrumbs!.First();
nb.Message.Should().Be(mb.Message, "Breadcrumb message");
nb.Type.Should().Be(mb.Type, "Breadcrumb type");

// user
native.User!.Id.Should().Be(managed.User.Id, "UserId should match");
native.User.Email.Should().Be(managed.User.Email, "Email should match");
native.User.Username.Should().Be(managed.User.Username, "Username should match");
native.User.IpAddress.Should().Be(managed.User.IpAddress, "IpAddress should match");
}
}
Loading