Skip to content

Feat/native scope observer #546

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Feb 7, 2022
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
73 changes: 20 additions & 53 deletions src/Sentry.Unity.Android/AndroidJavaScopeObserver.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
using Sentry.Extensibility;
using Sentry.Unity.Json;
using UnityEngine;

namespace Sentry.Unity.Android
Expand All @@ -8,22 +6,15 @@ namespace Sentry.Unity.Android
/// Scope Observer for Android through Java (JNI).
/// </summary>
/// <see href="https://github.com/getsentry/sentry-java"/>
public class AndroidJavaScopeObserver : IScopeObserver
public class AndroidJavaScopeObserver : ScopeObserver
{
private readonly SentryOptions _options;

public AndroidJavaScopeObserver(SentryOptions options) => _options = options;
public AndroidJavaScopeObserver(SentryOptions options) : base("Android", options) { }

private AndroidJavaObject GetSentryJava() => new AndroidJavaClass("io.sentry.Sentry");

public void AddBreadcrumb(Breadcrumb breadcrumb)
public override void AddBreadcrumbImpl(Breadcrumb breadcrumb)
{
AndroidJNI.AttachCurrentThread();

_options.DiagnosticLogger?.LogDebug("Android Scope Sync - Adding breadcrumb m:\"{0}\" l:\"{1}\"",
breadcrumb.Message,
breadcrumb.Level);

using var sentry = GetSentryJava();
using var javaBreadcrumb = new AndroidJavaObject("io.sentry.Breadcrumb");
javaBreadcrumb.Set("message", breadcrumb.Message);
Expand All @@ -34,69 +25,38 @@ public void AddBreadcrumb(Breadcrumb breadcrumb)
sentry.CallStatic("addBreadcrumb", javaBreadcrumb, null);
}

public void SetExtra(string key, object? value)
public override void SetExtraImpl(string key, string? value)
{
AndroidJNI.AttachCurrentThread();

_options.DiagnosticLogger?.LogDebug("Android Scope Sync - Setting Extra k:\"{0}\" v:\"{1}\"", key, value);

string? extraValue = null;
if (value is not null)
{
extraValue = SafeSerializer.SerializeSafely(value);
if (extraValue is null)
{
return;
}
}

using var sentry = GetSentryJava();
sentry.CallStatic("setExtra", key, extraValue);
sentry.CallStatic("setExtra", key, value);
}

public void SetTag(string key, string value)
public override void SetTagImpl(string key, string value)
{
AndroidJNI.AttachCurrentThread();

_options.DiagnosticLogger?.LogDebug("Android Scope Sync - Setting Tag k:\"{0}\" v:\"{1}\"", key, value);

using var sentry = GetSentryJava();
sentry.CallStatic("setTag", key, value);
}

public void UnsetTag(string key)
public override void UnsetTagImpl(string key)
{
AndroidJNI.AttachCurrentThread();

_options.DiagnosticLogger?.LogDebug("Android Scope Sync - Unsetting Tag k:\"{0}\"", key);

using var sentry = GetSentryJava();
sentry.CallStatic("removeTag", key);
}

public void SetUser(User? user)
public override void SetUserImpl(User user)
{
AndroidJNI.AttachCurrentThread();

AndroidJavaObject? javaUser = null;
try
{
if (user is not null)
{
_options.DiagnosticLogger?.LogDebug("Android Scope Sync - Setting User i:\"{0}\" n:\"{1}\"",
user.Id,
user.Username);

javaUser = new AndroidJavaObject("io.sentry.protocol.User");
javaUser.Set("email", user.Email);
javaUser.Set("id", user.Id);
javaUser.Set("username", user.Username);
javaUser.Set("ipAddress", user.IpAddress);
}
else
{
_options.DiagnosticLogger?.LogDebug("Android Scope Sync - Unsetting User");
}
javaUser = new AndroidJavaObject("io.sentry.protocol.User");
javaUser.Set("email", user.Email);
javaUser.Set("id", user.Id);
javaUser.Set("username", user.Username);
javaUser.Set("ipAddress", user.IpAddress);
using var sentry = GetSentryJava();
sentry.CallStatic("setUser", javaUser);
}
Expand All @@ -105,5 +65,12 @@ public void SetUser(User? user)
javaUser?.Dispose();
}
}

public override void UnsetUserImpl()
{
AndroidJNI.AttachCurrentThread();
using var sentry = GetSentryJava();
sentry.CallStatic("setUser", null);
}
}
}
1 change: 0 additions & 1 deletion src/Sentry.Unity.Android/SentryNativeAndroid.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using Sentry.Extensibility;
using UnityEngine;

namespace Sentry.Unity.Android
{
Expand Down
117 changes: 81 additions & 36 deletions src/Sentry.Unity.Native/NativeScopeObserver.cs
Original file line number Diff line number Diff line change
@@ -1,59 +1,104 @@
using System;
using System.Runtime.InteropServices;
using Sentry.Extensibility;
using Sentry.Unity.Json;
using UnityEngine;

namespace Sentry.Unity.Native
namespace Sentry.Unity
{
/// <summary>
/// Scope Observer for Native through P/Invoke.
/// </summary>
/// <see href="https://github.com/getsentry/sentry-native"/>
public class NativeScopeObserver : IScopeObserver
public class NativeScopeObserver : ScopeObserver
{
private readonly SentryOptions _options;
public NativeScopeObserver(SentryOptions options) : base("Native", options) { }

public NativeScopeObserver(SentryOptions options) => _options = options;

public void AddBreadcrumb(Breadcrumb breadcrumb)
public override void AddBreadcrumbImpl(Breadcrumb breadcrumb)
{
_options.DiagnosticLogger?.LogDebug("Native Scope Sync - Adding breadcrumb m:\"{0}\" l:\"{1}\"",
breadcrumb.Message,
breadcrumb.Level);
// TODO implement
// see https://develop.sentry.dev/sdk/event-payloads/breadcrumbs/
var crumb = sentry_value_new_breadcrumb(breadcrumb.Type, breadcrumb.Message);
sentry_value_set_by_key(crumb, "level", sentry_value_new_string(breadcrumb.Level.ToString().ToLower()));
sentry_value_set_by_key(crumb, "timestamp", sentry_value_new_string(GetTimestamp(breadcrumb.Timestamp)));
nativeSetValueIfNotNull(crumb, "category", breadcrumb.Category);
sentry_add_breadcrumb(crumb);
}

public void SetExtra(string key, object? value)
{
_options.DiagnosticLogger?.LogDebug("Native Scope Sync - Setting Extra k:\"{0}\" v:\"{1}\"", key, value);
// TODO implement
}
public override void SetExtraImpl(string key, string? value) =>
sentry_set_extra(key, value is null ? sentry_value_new_null() : sentry_value_new_string(value));

public void SetTag(string key, string value)
{
_options.DiagnosticLogger?.LogDebug("Native Scope Sync - Setting Tag k:\"{0}\" v:\"{1}\"", key, value);
// TODO implement
}
public override void SetTagImpl(string key, string value) => sentry_set_tag(key, value);

public override void UnsetTagImpl(string key) => sentry_remove_tag(key);

public void UnsetTag(string key)
public override void SetUserImpl(User user)
{
_options.DiagnosticLogger?.LogDebug("Native Scope Sync - Unsetting Tag k:\"{0}\"", key);
// TODO implement
// see https://develop.sentry.dev/sdk/event-payloads/user/
var cUser = sentry_value_new_object();
nativeSetValueIfNotNull(cUser, "id", user.Id);
nativeSetValueIfNotNull(cUser, "username", user.Username);
nativeSetValueIfNotNull(cUser, "email", user.Email);
nativeSetValueIfNotNull(cUser, "ip_address", user.IpAddress);
sentry_set_user(cUser);
}

public void SetUser(User? user)
public override void UnsetUserImpl() => sentry_remove_user();

[DllImport("sentry")]
private static extern SentryValueU sentry_value_new_object();

[DllImport("sentry")]
private static extern SentryValueU sentry_value_new_null();

[DllImport("sentry")]
private static extern SentryValueU sentry_value_new_string(string value);

[DllImport("sentry")]
private static extern SentryValueU sentry_value_new_breadcrumb(string? type, string? message);

[DllImport("sentry")]
private static extern int sentry_value_set_by_key(SentryValueU value, string k, SentryValueU v);

private static void nativeSetValueIfNotNull(SentryValueU obj, string key, string? value)
{
if (user is not null)
if (value is not null)
{
_options.DiagnosticLogger?.LogDebug("Native Scope Sync - Setting User i:\"{0}\" n:\"{1}\"",
user.Id,
user.Username);
// TODO implement
}
else
{
_options.DiagnosticLogger?.LogDebug("Native Scope Sync - Unsetting User");
// TODO implement
sentry_value_set_by_key(obj, key, sentry_value_new_string(value));
}
}

[DllImport("sentry")]
private static extern void sentry_add_breadcrumb(SentryValueU breadcrumb);

[DllImport("sentry")]
private static extern void sentry_set_tag(string key, string value);

[DllImport("sentry")]
private static extern void sentry_remove_tag(string key);

[DllImport("sentry")]
private static extern void sentry_set_user(SentryValueU user);

[DllImport("sentry")]
private static extern void sentry_remove_user();

[DllImport("sentry")]
private static extern void sentry_set_extra(string key, SentryValueU value);

[DllImport("sentry")]
private static extern void sentry_remove_extra(string key);

// native union sentry_value_u/t
[StructLayout(LayoutKind.Explicit)]
private struct SentryValueU
{
[FieldOffset(0)]
private ulong _bits;
[FieldOffset(0)]
private double _double;
}

private 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");
}
}
62 changes: 10 additions & 52 deletions src/Sentry.Unity.iOS/IosNativeScopeObserver.cs
Original file line number Diff line number Diff line change
@@ -1,72 +1,30 @@
using System;
using Sentry.Extensibility;
using Sentry.Unity.Json;

namespace Sentry.Unity.iOS
{
public class IosNativeScopeObserver : IScopeObserver
public class IosNativeScopeObserver : ScopeObserver
{
private readonly SentryUnityOptions _options;
public IosNativeScopeObserver(SentryOptions options) : base("iOS", options) { }

public IosNativeScopeObserver(SentryUnityOptions options) => _options = options;

public void AddBreadcrumb(Breadcrumb breadcrumb)
public override void AddBreadcrumbImpl(Breadcrumb breadcrumb)
{
_options.DiagnosticLogger?.LogDebug("iOS Scope Sync - Adding breadcrumb m:\"{0}\" l:\"{1}\"",
breadcrumb.Message,
breadcrumb.Level);

var level = GetBreadcrumbLevel(breadcrumb.Level);
var timestamp = GetTimestamp(breadcrumb.Timestamp);

SentryCocoaBridgeProxy.SentryNativeBridgeAddBreadcrumb(timestamp, breadcrumb.Message, breadcrumb.Type, breadcrumb.Category, level);
}

public void SetExtra(string key, object? value)
{
_options.DiagnosticLogger?.LogDebug("iOS Scope Sync - Setting Extra k:\"{0}\" v:\"{1}\"", key, value);
public override void SetExtraImpl(string key, string? value) =>
SentryCocoaBridgeProxy.SentryNativeBridgeSetExtra(key, value);

string? extraValue = null;
if (value is not null)
{
extraValue = SafeSerializer.SerializeSafely(value);
if (extraValue is null)
{
return;
}
}
public override void SetTagImpl(string key, string value) => SentryCocoaBridgeProxy.SentryNativeBridgeSetTag(key, value);

SentryCocoaBridgeProxy.SentryNativeBridgeSetExtra(key, extraValue);
}

public void SetTag(string key, string value)
{
_options.DiagnosticLogger?.LogDebug("iOS Scope Sync - Setting Tag k:\"{0}\" v:\"{1}\"", key, value);
SentryCocoaBridgeProxy.SentryNativeBridgeSetTag(key, value);
}

public void UnsetTag(string key)
{
_options.DiagnosticLogger?.LogDebug("iOS Scope Sync - Unsetting Tag k:\"{0}\"", key);
SentryCocoaBridgeProxy.SentryNativeBridgeUnsetTag(key);
}

public void SetUser(User? user)
{
if (user is null)
{
_options.DiagnosticLogger?.LogDebug("iOS Scope Sync - Unsetting User");
SentryCocoaBridgeProxy.SentryNativeBridgeUnsetUser();
}
else
{
_options.DiagnosticLogger?.LogDebug("iOS Scope Sync - Setting User i:\"{0}\" n:\"{1}\"",
user.Id,
user.Username);
public override void UnsetTagImpl(string key) => SentryCocoaBridgeProxy.SentryNativeBridgeUnsetTag(key);

public override void SetUserImpl(User user) =>
SentryCocoaBridgeProxy.SentryNativeBridgeSetUser(user.Email, user.Id, user.IpAddress, user.Username);
}
}

public override void UnsetUserImpl() => SentryCocoaBridgeProxy.SentryNativeBridgeUnsetUser();

internal static string GetTimestamp(DateTimeOffset timestamp) =>
// "o": Using ISO 8601 to make sure the timestamp makes it to the bridge correctly.
Expand Down
Loading