Skip to content

chore: Move startup tracing into the SDK #2234

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 11 commits into from
Jul 11, 2025
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
33 changes: 1 addition & 32 deletions package-dev/Runtime/SentryInitialization.cs
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Instead of having Startup Tracing be split between SentryInitialization and SentryIntegration we can delete the latter and move everything into internal StartupTracing inside the SDK.

Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,6 @@ namespace Sentry.Unity
{
public static class SentryInitialization
{
public const string StartupTransactionOperation = "app.start";
public static ISpan InitSpan;
private const string InitSpanOperation = "runtime.init";
public static ISpan SubSystemRegistrationSpan;
private const string SubSystemSpanOperation = "runtime.init.subsystem";

#if SENTRY_WEBGL
// On WebGL SubsystemRegistration is too early for the UnityWebRequestTransport and errors with 'URI empty'
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
Expand All @@ -60,14 +54,9 @@ public static void Init()
var options = ScriptableSentryUnityOptions.LoadSentryUnityOptions(unityInfo);
if (options != null && options.ShouldInitializeSdk())
{
// Certain integrations require access to preprocessor directives so we provide them as `.cs` and
// compile them with the game instead of precompiling them with the rest of the SDK.
// i.e. SceneManagerAPI requires UNITY_2020_3_OR_NEWER
SentryIntegrations.Configure(options);
// Configures scope sync and (by default) initializes the native SDK.
SetupNativeSdk(options, unityInfo);
SentryUnity.Init(options);
SetupStartupTracing(options);
}
else
{
Expand All @@ -92,7 +81,7 @@ private static void SetupNativeSdk(SentryUnityOptions options, SentryUnityInfo u
#elif SENTRY_NATIVE
SentryNative.Configure(options, unityInfo);
#elif SENTRY_WEBGL
SentryWebGL.Configure(options);
SentryWebGL.Configure(options);
#endif
}
catch (DllNotFoundException e)
Expand All @@ -106,26 +95,6 @@ private static void SetupNativeSdk(SentryUnityOptions options, SentryUnityInfo u
options.DiagnosticLogger?.LogError(e, "Sentry native error capture configuration failed.");
}
}

private static void SetupStartupTracing(SentryUnityOptions options)
{
#if !SENTRY_WEBGL
if (options.TracesSampleRate > 0.0f && options.AutoStartupTraces)
{
options.DiagnosticLogger?.LogInfo("Creating '{0}' transaction for runtime initialization.",
StartupTransactionOperation);

var runtimeStartTransaction =
SentrySdk.StartTransaction("runtime.initialization", StartupTransactionOperation);
SentrySdk.ConfigureScope(scope => scope.Transaction = runtimeStartTransaction);

options.DiagnosticLogger?.LogDebug("Creating '{0}' span.", InitSpanOperation);
InitSpan = runtimeStartTransaction.StartChild(InitSpanOperation, "runtime initialization");
options.DiagnosticLogger?.LogDebug("Creating '{0}' span.", SubSystemSpanOperation);
SubSystemRegistrationSpan = InitSpan.StartChild(SubSystemSpanOperation, "subsystem registration");
}
#endif
}
}

public class SentryUnityInfo : ISentryUnityInfo
Expand Down
122 changes: 0 additions & 122 deletions package-dev/Runtime/SentryIntegrations.cs

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ MonoBehaviour:
<DebounceTimeLog>k__BackingField: 1000
<DebounceTimeWarning>k__BackingField: 1000
<DebounceTimeError>k__BackingField: 1000
<TracesSampleRate>k__BackingField: 0
<TracesSampleRate>k__BackingField: 1
<AutoStartupTraces>k__BackingField: 1
<AutoSceneLoadTraces>k__BackingField: 1
<AutoAwakeTraces>k__BackingField: 0
Expand Down
139 changes: 139 additions & 0 deletions src/Sentry.Unity/Integrations/StartupTracingIntegration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
using Sentry.Extensibility;
using Sentry.Integrations;
using UnityEngine;

namespace Sentry.Unity.Integrations;

internal class StartupTracingIntegration : ISdkIntegration
{
private const string StartupTransactionOperation = "app.start";
internal static ISpan? InitSpan;
private const string InitSpanOperation = "runtime.init";
internal static ISpan? SubSystemRegistrationSpan;
private const string SubSystemSpanOperation = "runtime.init.subsystem";
internal static ISpan? AfterAssembliesSpan;
private const string AfterAssembliesSpanOperation = "runtime.init.afterassemblies";
internal static ISpan? SplashScreenSpan;
private const string SplashScreenSpanOperation = "runtime.init.splashscreen";
internal static ISpan? FirstSceneLoadSpan;
private const string FirstSceneLoadSpanOperation = "runtime.init.firstscene";

internal static bool IsGameStartupFinished; // Flag to make sure we only create spans during the game's startup.
internal static bool IsIntegrationRegistered;

private static IDiagnosticLogger? Logger;

// For testing. Methods with the RuntimeLoad attribute cannot have arguments
internal static IApplication? Application = null;

public void Register(IHub hub, SentryOptions options)
{
Logger = options.DiagnosticLogger;

if (options is SentryUnityOptions { TracesSampleRate: > 0, AutoStartupTraces: true })
{
IsIntegrationRegistered = true;
}
}

internal static bool IsStartupTracingAllowed()
{
Application ??= ApplicationAdapter.Instance;
if (!Application.IsEditor
&& Application.Platform != RuntimePlatform.WebGLPlayer // Startup Tracing does not properly work on WebGL
&& IsIntegrationRegistered
&& !IsGameStartupFinished)
{
return true;
}

return false;
}

public static void StartTracing()
{
if (!IsStartupTracingAllowed())
{
return;
}

Logger?.LogInfo("Creating '{0}' transaction for runtime initialization.",
StartupTransactionOperation);

var runtimeStartTransaction =
SentrySdk.StartTransaction("runtime.initialization", StartupTransactionOperation);
SentrySdk.ConfigureScope(scope => scope.Transaction = runtimeStartTransaction);

Logger?.LogDebug("Creating '{0}' span.", InitSpanOperation);
InitSpan = runtimeStartTransaction.StartChild(InitSpanOperation, "runtime initialization");
Logger?.LogDebug("Creating '{0}' span.", SubSystemSpanOperation);
SubSystemRegistrationSpan = InitSpan.StartChild(SubSystemSpanOperation, "subsystem registration");
}

[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterAssembliesLoaded)]
public static void AfterAssembliesLoaded()
{
if (!IsStartupTracingAllowed())
{
return;
}

SubSystemRegistrationSpan?.Finish(SpanStatus.Ok);
SubSystemRegistrationSpan = null;

Logger?.LogDebug("Creating '{0}' span.", AfterAssembliesSpanOperation);
AfterAssembliesSpan = InitSpan?.StartChild(AfterAssembliesSpanOperation, "after assemblies");
}

[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSplashScreen)]
public static void BeforeSplashScreen()
{
if (!IsStartupTracingAllowed())
{
return;
}

AfterAssembliesSpan?.Finish(SpanStatus.Ok);
AfterAssembliesSpan = null;

Logger?.LogDebug("Creating '{0}' span.", SplashScreenSpanOperation);
SplashScreenSpan = InitSpan?.StartChild(SplashScreenSpanOperation, "splashscreen");
}

[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
public static void BeforeSceneLoad()
{
if (!IsStartupTracingAllowed())
{
return;
}

SplashScreenSpan?.Finish(SpanStatus.Ok);
SplashScreenSpan = null;

Logger?.LogDebug("Creating '{0}' span.", FirstSceneLoadSpanOperation);
FirstSceneLoadSpan = InitSpan?.StartChild(FirstSceneLoadSpanOperation, "first scene load");
}

[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
public static void AfterSceneLoad()
{
if (!IsStartupTracingAllowed())
{
// To make sure late init calls don't try to trace the startup
IsGameStartupFinished = true;
return;
}

FirstSceneLoadSpan?.Finish(SpanStatus.Ok);
FirstSceneLoadSpan = null;

InitSpan?.Finish(SpanStatus.Ok);
InitSpan = null;

Logger?.LogInfo("Finishing '{0}' transaction.", StartupTransactionOperation);
SentrySdk.ConfigureScope(s => s.Transaction?.Finish(SpanStatus.Ok));

IsGameStartupFinished = true;
}
}
1 change: 1 addition & 0 deletions src/Sentry.Unity/SentryUnity.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.ComponentModel;
using Sentry.Extensibility;
using UnityEngine;

namespace Sentry.Unity;

Expand Down
1 change: 1 addition & 0 deletions src/Sentry.Unity/SentryUnityOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,7 @@ internal SentryUnityOptions(SentryMonoBehaviour behaviour, IApplication applicat

this.AddIntegration(new UnityLogHandlerIntegration(this));
this.AddIntegration(new UnityApplicationLoggingIntegration());
this.AddIntegration(new StartupTracingIntegration());
this.AddIntegration(new AnrIntegration(behaviour));
this.AddIntegration(new UnityScopeIntegration(application, unityInfo));
this.AddIntegration(new UnityBeforeSceneLoadIntegration());
Expand Down
4 changes: 4 additions & 0 deletions src/Sentry.Unity/SentryUnitySDK.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ private SentryUnitySdk(SentryUnityOptions options)

unitySdk._dotnetSdk = SentrySdk.Init(options);

// We can safely call this during initialization. If the SDK self-initialized we're right on time. If the SDK
// was initialized manually, the RuntimeOnLoad attributes already triggered, making this call a no-op.
StartupTracingIntegration.StartTracing();

if (options.NativeContextWriter is { } contextWriter)
{
SentrySdk.ConfigureScope((scope) =>
Expand Down
2 changes: 0 additions & 2 deletions test/Scripts.Tests/package-release.zip.snapshot
Original file line number Diff line number Diff line change
Expand Up @@ -1572,8 +1572,6 @@ Runtime/Sentry.xml
Runtime/Sentry.xml.meta
Runtime/SentryInitialization.cs
Runtime/SentryInitialization.cs.meta
Runtime/SentryIntegrations.cs
Runtime/SentryIntegrations.cs.meta
Runtime/SentryUserFeedback.cs
Runtime/SentryUserFeedback.cs.meta
Samples~/unity-of-bugs/Scenes.meta
Expand Down
Loading