Skip to content
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

fix: Make capturing the ViewHierarchy an EventProcessor #2020

Merged
merged 8 commits into from
Feb 17, 2025
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

### Fixes

- The SDK no longer fails to attach the `ViewHierarchy` when the scope has previously been cleared. ([#2020](https://github.com/getsentry/sentry-unity/pull/2020))
- The SDK no longer fails to attach a screenshot when the scope has previously been cleared. ([#2019](https://github.com/getsentry/sentry-unity/pull/2019))
- The SDK's build logs when targeting Android are not a lot less noisy. The SDK will also no longer omit the sentry-cli logs from the gradle build output. ([#1995](https://github.com/getsentry/sentry-unity/pull/1995))
- When targeting iOS and disabling native support, the SDK no longer causes builds to fail with an `Undefined symbol: _SentryNativeBridgeIsEnabled` error. ([#1983](https://github.com/getsentry/sentry-unity/pull/1983))
Expand Down
4 changes: 4 additions & 0 deletions src/Sentry.Unity/ScriptableSentryUnityOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,10 @@ internal SentryUnityOptions ToSentryUnityOptions(bool isBuilding, ISentryUnityIn
// Without setting up here we might miss out on logs between option-loading (now) and Init - i.e. native configuration
options.SetupLogging();

if (options.AttachViewHierarchy)
{
options.AddEventProcessor(new ViewHierarchyEventProcessor(options));
}
if (options.AttachScreenshot)
{
options.AddEventProcessor(new ScreenshotEventProcessor(options));
Expand Down
7 changes: 0 additions & 7 deletions src/Sentry.Unity/SentryUnitySDK.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,6 @@ private SentryUnitySdk(SentryUnityOptions options)

unitySdk._dotnetSdk = SentrySdk.Init(options);

if (options.AttachViewHierarchy)
{
SentrySdk.ConfigureScope(s =>
s.AddAttachment(new ViewHierarchyAttachment(
new UnityViewHierarchyAttachmentContent(options))));
}

if (options.NativeContextWriter is { } contextWriter)
{
SentrySdk.ConfigureScope((scope) =>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,39 +1,43 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.Json;
using Sentry.Extensibility;
using UnityEngine;
using UnityEngine.SceneManagement;

namespace Sentry.Unity;

internal class UnityViewHierarchyAttachmentContent : IAttachmentContent
public class ViewHierarchyEventProcessor : ISentryEventProcessorWithHint
{
private readonly SentryUnityOptions _options;

public UnityViewHierarchyAttachmentContent(SentryUnityOptions options)
public ViewHierarchyEventProcessor(SentryUnityOptions sentryOptions)
{
_options = options;
_options = sentryOptions;
}

public Stream GetStream()
public SentryEvent? Process(SentryEvent @event)
{
return @event;
}

public SentryEvent? Process(SentryEvent @event, SentryHint hint)
{
// Note: we need to check explicitly that we're on the same thread. While Unity would throw otherwise
// when capturing the screenshot, it would only do so on development builds. On release, it just crashes...
if (!MainThreadData.IsMainThread())
{
_options.DiagnosticLogger?.LogDebug("Can't capture screenshots on other than main (UI) thread.");
return Stream.Null;
return @event;
}

return CaptureViewHierarchy();
hint.AddAttachment(CaptureViewHierarchy(), "view-hierarchy.json", contentType: "application/json");

return @event;
}

internal Stream CaptureViewHierarchy()
internal byte[] CaptureViewHierarchy()
{
var stream = new MemoryStream();
using var stream = new MemoryStream();
using var writer = new Utf8JsonWriter(stream);

var viewHierarchy = CreateViewHierarchy(
Expand All @@ -43,9 +47,7 @@ internal Stream CaptureViewHierarchy()
viewHierarchy.WriteTo(writer, _options.DiagnosticLogger);

writer.Flush();
stream.Seek(0, SeekOrigin.Begin);

return stream;
return stream.ToArray();
}

internal ViewHierarchy CreateViewHierarchy(int maxRootGameObjectCount, int maxChildCount, int maxDepth)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@

namespace Sentry.Unity.Tests;

public class UnityViewHierarchyAttachmentTests
public class UnityViewHierarchyEventProcessorTests
{
private class Fixture
{
public SentryUnityOptions Options = new() { AttachViewHierarchy = true };

public UnityViewHierarchyAttachmentContent GetSut() => new(Options);
public ViewHierarchyEventProcessor GetSut() => new(Options);
}

private Fixture _fixture = null!;
Expand All @@ -30,39 +30,43 @@ public void TearDown()
}

[Test]
public void GetStream_IsMainThread_ReturnsStream()
public void GetStream_IsMainThread_AddsViewHierarchyToHint()
{
var sut = _fixture.GetSut();
var sentryEvent = new SentryEvent();
var hint = new SentryHint();

var stream = sut.GetStream();
sut.Process(sentryEvent, hint);

Assert.IsNotNull(stream);
Assert.AreEqual(1, hint.Attachments.Count);
}

[Test]
public void GetStream_IsNonMainThread_ReturnsNullStream()
public void GetStream_IsNonMainThread_DoesNotAddViewHierarchyToHint()
{
var sut = _fixture.GetSut();
var sentryEvent = new SentryEvent();
var hint = new SentryHint();

new Thread(() =>
{
Thread.CurrentThread.IsBackground = true;

var stream = sut.GetStream();
var stream = sut.Process(sentryEvent, hint);

Assert.NotNull(stream);
Assert.AreEqual(Stream.Null, stream);
Assert.AreEqual(0, hint.Attachments.Count);
}).Start();
}

[Test]
public void CaptureViewHierarchy_ReturnsNonNullStream()
public void CaptureViewHierarchy_ReturnsNonNullOrEmptyByteArray()
{
var sut = _fixture.GetSut();

using var stream = sut.CaptureViewHierarchy();
var byteArray = sut.CaptureViewHierarchy();

Assert.AreNotEqual(Stream.Null, stream);
Assert.That(byteArray, Is.Not.Null);
Assert.That(byteArray.Length, Is.GreaterThan(0));
}

[Test]
Expand Down
Loading