Skip to content

fix: Moved late option mutation into Init #2239

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 62 commits into from
Jul 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
14427b6
initial working implementation
bitsandfoxes Jul 3, 2025
bb4dbc0
fixed using in smoketester
bitsandfoxes Jul 4, 2025
0f97f0b
we actually need both. idk if that's great. doesn't look great
bitsandfoxes Jul 4, 2025
a76e957
bumped .NET to have the comment
bitsandfoxes Jul 7, 2025
e494279
Updated CHANGELOG.md
bitsandfoxes Jul 7, 2025
df211a3
bumped to cleaned up version of .NET
bitsandfoxes Jul 7, 2025
19c4e78
added services logging to init
bitsandfoxes Jul 7, 2025
00e788a
added summary to platformservices
bitsandfoxes Jul 7, 2025
bfcd0f1
fixed namespaces
bitsandfoxes Jul 7, 2025
4448334
cleaned up bugfarmbuttons
bitsandfoxes Jul 7, 2025
232833c
namespace clean followup
bitsandfoxes Jul 7, 2025
bcbb551
fixed qualification
bitsandfoxes Jul 7, 2025
deb6aa7
moved scenetracing integration into the SDK
bitsandfoxes Jul 8, 2025
a66a5a6
updated CI to 2020
bitsandfoxes Jul 8, 2025
857e492
env bump
bitsandfoxes Jul 8, 2025
eefad70
removed now redundant test
bitsandfoxes Jul 8, 2025
16c9fcf
bumped uniy-of-bugs to 2020
bitsandfoxes Jul 8, 2025
6542706
updated the package
bitsandfoxes Jul 8, 2025
b48e60d
removed now redundant asmdef
bitsandfoxes Jul 8, 2025
76e05b2
Updated CHANGELOG.md
bitsandfoxes Jul 8, 2025
3fa372c
missing option field?
bitsandfoxes Jul 8, 2025
1f5e5cc
merged unity 2019 drop
bitsandfoxes Jul 8, 2025
83deeec
finished fixing the startup tracing
bitsandfoxes Jul 8, 2025
011ba1d
merged main
bitsandfoxes Jul 9, 2025
2a46b5f
bump android API level to 21 for 2020
bitsandfoxes Jul 9, 2025
7ac68be
Merge branch 'fix/android-2020-ci' into fix/initialization
bitsandfoxes Jul 9, 2025
43f5ae9
updated test to ignore harmless warning
bitsandfoxes Jul 9, 2025
ad0ae02
move startup tracing into the SDK
bitsandfoxes Jul 9, 2025
bb6f80a
.
bitsandfoxes Jul 9, 2025
c53ccb5
fix naming
bitsandfoxes Jul 9, 2025
d100d4f
Format code
getsentry-bot Jul 9, 2025
7faafb9
added tests
bitsandfoxes Jul 9, 2025
490e033
merged
bitsandfoxes Jul 9, 2025
2f42440
Format code
getsentry-bot Jul 9, 2025
d2709d1
updated snapshot
bitsandfoxes Jul 9, 2025
326dc32
Merge branch 'chore/cleanup-init' of https://github.com/getsentry/sen…
bitsandfoxes Jul 9, 2025
1183370
.
bitsandfoxes Jul 9, 2025
f1388b4
merged the init cleanup
bitsandfoxes Jul 10, 2025
594dc4a
fixed test
bitsandfoxes Jul 10, 2025
3bfc5fa
use platform services where applicable
bitsandfoxes Jul 10, 2025
0843ba5
forgot webgl
bitsandfoxes Jul 10, 2025
a4cbc59
rename setup platform services
bitsandfoxes Jul 10, 2025
32876ac
public close cutback
bitsandfoxes Jul 10, 2025
0b8a02f
moved late integrations and sumsuch into init
bitsandfoxes Jul 10, 2025
7d0ffca
Updated CHANGELOG.md
bitsandfoxes Jul 10, 2025
c6cbb0c
review
bitsandfoxes Jul 10, 2025
2ed2686
Merge branch 'fix/initialization' into fix/new-init-options-creation
bitsandfoxes Jul 10, 2025
3a2328b
Format code
getsentry-bot Jul 10, 2025
4379954
Format code
getsentry-bot Jul 10, 2025
26567b3
Updated CHANGELOG.md
bitsandfoxes Jul 10, 2025
24c4d94
Merge branch 'fix/new-init-options-creation' of https://github.com/ge…
bitsandfoxes Jul 10, 2025
2e4b3db
Merge branch 'main' into chore/cleanup-init
bitsandfoxes Jul 10, 2025
99bbb4d
Merge branch 'chore/cleanup-init' into fix/initialization
bitsandfoxes Jul 10, 2025
d67b2b9
Merge branch 'fix/initialization' of https://github.com/getsentry/sen…
bitsandfoxes Jul 10, 2025
70222d3
Merge branch 'fix/initialization' into fix/new-init-options-creation
bitsandfoxes Jul 10, 2025
502873b
merged main
bitsandfoxes Jul 15, 2025
e518e8e
finished up on merge conflicts and ctor cleanup
bitsandfoxes Jul 15, 2025
4d2d621
final touches
bitsandfoxes Jul 15, 2025
a618ce8
missed a test
bitsandfoxes Jul 15, 2025
758ba57
Updated CHANGELOG.md
bitsandfoxes Jul 15, 2025
1dd06bb
cleanup
bitsandfoxes Jul 15, 2025
5c25980
renamed
bitsandfoxes Jul 16, 2025
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
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
**Migration**: Update your `using` directives from `using Sentry;` to `using Sentry.Unity;`. IDEs like Rider can automatically
import the missing references. In some cases, you may need both `using Sentry.Unity;` (for the static API) and `using Sentry;`
(for types like `SentryId`). No changes are required to your actual SDK method calls (e.g., `SentrySdk.CaptureException()`
remains the same). ([#2227](https://github.com/getsentry/sentry-unity/pull/2227))
remains the same). ([#2227](https://github.com/getsentry/sentry-unity/pull/2227), [#2239](https://github.com/getsentry/sentry-unity/pull/2239))

### Features

Expand Down
1 change: 1 addition & 0 deletions src/Sentry.Unity/Il2CppEventProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public UnityIl2CppEventExceptionProcessor(SentryUnityOptions options)
{
Options = options;
UnityInfo = options.UnityInfo;
// We're throwing here but this should never happen. We're validating UnityInfo before adding the processor.
_il2CppMethods = UnityInfo.Il2CppMethods ?? throw new ArgumentNullException(nameof(UnityInfo.Il2CppMethods),
"Unity IL2CPP methods are not available.");

Expand Down
74 changes: 18 additions & 56 deletions src/Sentry.Unity/ScriptableSentryUnityOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -115,28 +115,31 @@ public static string GetConfigPath(string? notDefaultConfigName = null)
[field: SerializeField] public SentryLevel DiagnosticLevel { get; set; } = SentryLevel.Warning;

/// <summary>
/// Loads the ScriptableSentryUnityOptions from `Resource`.
/// Loads the ScriptableSentryUnityOptions from <c>Resource</c>.
/// </summary>
/// <returns>The SentryUnityOptions generated from the ScriptableSentryUnityOptions</returns>
/// <returns>The <c>SentryUnityOptions</c> generated from the <c>ScriptableSentryUnityOptions</c></returns>
/// <remarks>
/// Used for loading the SentryUnityOptions from the ScriptableSentryUnityOptions during runtime.
/// This gets called from <c>SentryInitialization</c> during the game's startup.
/// </remarks>
public static SentryUnityOptions? LoadSentryUnityOptions()
{
var scriptableOptions = Resources.Load<ScriptableSentryUnityOptions>($"{ConfigRootFolder}/{ConfigName}");
if (scriptableOptions is not null)
{
return scriptableOptions.ToSentryUnityOptions(false);
return scriptableOptions.ToSentryUnityOptions();
}

return null;
}

internal SentryUnityOptions ToSentryUnityOptions(bool isBuilding, ISentryUnityInfo? unityInfo = null, IApplication? application = null)
internal SentryUnityOptions ToSentryUnityOptions(
ISentryUnityInfo? unityInfo = null,
IApplication? application = null,
bool isBuilding = false)
{
application ??= ApplicationAdapter.Instance;

var options = new SentryUnityOptions(isBuilding, application, unityInfo)
var options = new SentryUnityOptions(unityInfo, application, isBuilding: isBuilding)
{
Enabled = Enabled,
Dsn = Dsn,
Expand Down Expand Up @@ -186,6 +189,12 @@ internal SentryUnityOptions ToSentryUnityOptions(bool isBuilding, ISentryUnityIn
PerformanceAutoInstrumentationEnabled = AutoAwakeTraces,
};

// By default, the cacheDirectoryPath gets set on known platforms. We're overwriting this behaviour here.
if (!EnableOfflineCaching)
{
options.CacheDirectoryPath = null;
}

if (!string.IsNullOrWhiteSpace(ReleaseOverride))
{
options.Release = ReleaseOverride;
Expand Down Expand Up @@ -227,23 +236,11 @@ 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.SetupUnityLogging();

if (options.AttachViewHierarchy)
{
options.AddEventProcessor(new ViewHierarchyEventProcessor(options));
}
if (options.AttachScreenshot)
{
options.AddEventProcessor(new ScreenshotEventProcessor(options));
}

if (!application.IsEditor && options.Il2CppLineNumberSupportEnabled && unityInfo?.Il2CppMethods != null)
{
options.AddIl2CppExceptionProcessor();
}

HandlePlatformRestrictedOptions(options, application);
// ExceptionFilters are added by default to the options.
HandleExceptionFilter(options);

// The AnrDetectionIntegration is added by default. Since it is a ScriptableUnityOptions-only property we have to
// remove the integration when creating the options through here
if (!AnrDetectionEnabled)
{
options.DisableAnrIntegration();
Expand All @@ -252,41 +249,6 @@ internal SentryUnityOptions ToSentryUnityOptions(bool isBuilding, ISentryUnityIn
return options;
}

internal void HandlePlatformRestrictedOptions(SentryUnityOptions options, IApplication application)
{
if (!options.UnityInfo.IsKnownPlatform())
{
options.DisableFileWrite = true;

// This is only provided on a best-effort basis for other than the explicitly supported platforms.
if (options.BackgroundWorker is null)
{
options.DiagnosticLogger?.LogDebug("Platform support for background thread execution is unknown: using WebBackgroundWorker.");
options.BackgroundWorker = new WebBackgroundWorker(options, SentryMonoBehaviour.Instance);
}

// Disable offline caching regardless whether it was enabled or not.
options.CacheDirectoryPath = null;
if (EnableOfflineCaching)
{
options.DiagnosticLogger?.LogDebug("Platform support for offline caching is unknown: disabling.");
}

// Requires file access, see https://github.com/getsentry/sentry-unity/issues/290#issuecomment-1163608988
if (options.AutoSessionTracking)
{
options.DiagnosticLogger?.LogDebug("Platform support for automatic session tracking is unknown: disabling.");
options.AutoSessionTracking = false;
}

return;
}

// Only assign the cache directory path if we're on a "known" platform. Accessing `Application.persistentDataPath`
// implicitly creates a directory and leads to crashes i.e. on the Switch.
options.CacheDirectoryPath = EnableOfflineCaching ? application.PersistentDataPath : null;
}

private void HandleExceptionFilter(SentryUnityOptions options)
{
if (!options.FilterBadGatewayExceptions)
Expand Down
62 changes: 36 additions & 26 deletions src/Sentry.Unity/SentryUnityOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -298,45 +298,48 @@ internal string? DefaultUserId
internal ISentryUnityInfo UnityInfo { get; private set; }
internal Action<SentryUnityOptions>? PlatformConfiguration { get; private set; }

public SentryUnityOptions() : this(false, ApplicationAdapter.Instance) { }

internal SentryUnityOptions(bool isBuilding, IApplication application, ISentryUnityInfo? unityInfo = null) :
this(SentryMonoBehaviour.Instance, application, isBuilding, unityInfo)
Comment on lines -303 to -304
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Got rid of a redundant internal constructor.

{ }
public SentryUnityOptions() : this(isBuilding: false) { }

// For testing
internal SentryUnityOptions(SentryMonoBehaviour behaviour, IApplication application, bool isBuilding, ISentryUnityInfo? unityInfo = null)
internal SentryUnityOptions(
ISentryUnityInfo? unityInfo = null,
IApplication? application = null,
SentryMonoBehaviour? behaviour = null,
bool isBuilding = false)
{
// NOTE: 'SentryPlatformServices.UnityInfo' throws when the UnityInfo has not been set. This should not happen.
// The PlatformServices are set through the RuntimeLoad attribute in 'SentryInitialization.cs' and are required
// to be present.
UnityInfo = unityInfo ?? SentryPlatformServices.UnityInfo;
PlatformConfiguration = SentryPlatformServices.PlatformConfiguration;

application ??= ApplicationAdapter.Instance;
behaviour ??= SentryMonoBehaviour.Instance;

// IL2CPP doesn't support Process.GetCurrentProcess().StartupTime
DetectStartupTime = StartupTimeDetectionMode.Fast;

this.AddInAppExclude("UnityEngine");
this.AddInAppExclude("UnityEditor");
AddInAppExclude("UnityEngine");
AddInAppExclude("UnityEditor");
var processor = new UnityEventProcessor(this);
this.AddEventProcessor(processor);
this.AddTransactionProcessor(processor);
this.AddExceptionProcessor(new UnityExceptionProcessor());

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());
this.AddIntegration(new SceneManagerIntegration());
this.AddIntegration(new SceneManagerTracingIntegration());
this.AddIntegration(new SessionIntegration(behaviour));
this.AddIntegration(new TraceGenerationIntegration(behaviour));

this.AddExceptionFilter(new UnityBadGatewayExceptionFilter());
this.AddExceptionFilter(new UnityWebExceptionFilter());
this.AddExceptionFilter(new UnitySocketExceptionFilter());
AddEventProcessor(processor);
AddTransactionProcessor(processor);
AddExceptionProcessor(new UnityExceptionProcessor());

AddIntegration(new UnityLogHandlerIntegration(this));
AddIntegration(new UnityApplicationLoggingIntegration());
AddIntegration(new StartupTracingIntegration());
AddIntegration(new AnrIntegration(behaviour));
AddIntegration(new UnityScopeIntegration(application, unityInfo));
AddIntegration(new UnityBeforeSceneLoadIntegration());
AddIntegration(new SceneManagerIntegration());
AddIntegration(new SceneManagerTracingIntegration());
AddIntegration(new SessionIntegration(behaviour));
AddIntegration(new TraceGenerationIntegration(behaviour));

AddExceptionFilter(new UnityBadGatewayExceptionFilter());
AddExceptionFilter(new UnityWebExceptionFilter());
AddExceptionFilter(new UnitySocketExceptionFilter());

IsGlobalModeEnabled = true;

Expand Down Expand Up @@ -373,6 +376,13 @@ internal SentryUnityOptions(SentryMonoBehaviour behaviour, IApplication applicat
{ LogType.Error, true},
{ LogType.Exception, true},
};

// Only assign the cache directory path if we're on a "known" platform. Accessing `Application.persistentDataPath`
// implicitly creates a directory and leads to crashes i.e. on the Switch.
if (unityInfo?.IsKnownPlatform() ?? false)
{
CacheDirectoryPath = application.PersistentDataPath;
}
}

public override string ToString()
Expand Down
5 changes: 0 additions & 5 deletions src/Sentry.Unity/SentryUnityOptionsExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,6 @@ internal static void SetupUnityLogging(this SentryUnityOptions options)
}
}

internal static void AddIl2CppExceptionProcessor(this SentryUnityOptions options)
{
options.AddExceptionProcessor(new UnityIl2CppEventExceptionProcessor(options));
}

/// <summary>
/// Disables the capture of errors through <see cref="UnityLogHandlerIntegration"/>.
/// </summary>
Expand Down
105 changes: 81 additions & 24 deletions src/Sentry.Unity/SentryUnitySdk.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ internal class SentryUnitySdk
{
private readonly SentryUnityOptions _options;
private IDisposable _dotnetSdk = null!;
private FileStream? _lockFile;
private FileStream? LockFile;

private SentryUnitySdk(SentryUnityOptions options)
{
Expand All @@ -30,30 +30,14 @@ private SentryUnitySdk(SentryUnityOptions options)

MainThreadData.CollectData();

// On Standalone, we disable cache dir in case multiple app instances run over the same path.
// Note: we cannot use a named Mutex, because Unit doesn't support it. Instead, we create a file with `FileShare.None`.
// https://forum.unity.com/threads/unsupported-internal-call-for-il2cpp-mutex-createmutex_internal-named-mutexes-are-not-supported.387334/
if (ApplicationAdapter.Instance.Platform is RuntimePlatform.WindowsPlayer && options.CacheDirectoryPath is not null)
{
try
{
unitySdk._lockFile = new FileStream(Path.Combine(options.CacheDirectoryPath, "sentry-unity.lock"), FileMode.OpenOrCreate,
FileAccess.ReadWrite, FileShare.None);
}
catch (Exception ex)
{
options.DiagnosticLogger?.LogWarning("An exception was thrown while trying to " +
"acquire a lockfile on the config directory: .NET event cache will be disabled.", ex);
options.CacheDirectoryPath = null;
options.AutoSessionTracking = false;
}
}
// Some integrations are controlled through a flag and opt-in. Adding these integrations late so we have equal
// behaviour whether the options got created through the ScriptableObject or the SDK gets manually initialized
AddIntegrations(options);
SetUpWindowsPlayerCaching(unitySdk, options);

unitySdk._dotnetSdk = Sentry.SentrySdk.Init(options);
ConfigureUnsupportedPlatformFallbacks(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();
unitySdk._dotnetSdk = Sentry.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.
Expand Down Expand Up @@ -99,7 +83,7 @@ public void Close()
try
{
// We don't really need to close, Windows would release the lock anyway, but let's be nice.
_lockFile?.Close();
LockFile?.Close();
}
catch (Exception ex)
{
Expand Down Expand Up @@ -141,4 +125,77 @@ public void CaptureFeedback(string message, string? email, string? name, bool ad

Sentry.SentrySdk.CurrentHub.CaptureFeedback(message, email, name, hint: hint);
}

internal static void SetUpWindowsPlayerCaching(SentryUnitySdk unitySdk, SentryUnityOptions options)
{
// On Windows-Standalone, we disable cache dir in case multiple app instances run over the same path.
// Note: we cannot use a named Mutex, because Unity doesn't support it. Instead, we create a file with `FileShare.None`.
// https://forum.unity.com/threads/unsupported-internal-call-for-il2cpp-mutex-createmutex_internal-named-mutexes-are-not-supported.387334/
if (ApplicationAdapter.Instance.Platform is not RuntimePlatform.WindowsPlayer ||
options.CacheDirectoryPath is null)
{
return;
}

try
{
unitySdk.LockFile = new FileStream(Path.Combine(options.CacheDirectoryPath, "sentry-unity.lock"), FileMode.OpenOrCreate,
FileAccess.ReadWrite, FileShare.None);
}
catch (Exception ex)
{
options.DiagnosticLogger?.LogWarning("An exception was thrown while trying to " +
"acquire a lockfile on the config directory: .NET event cache will be disabled.", ex);
options.CacheDirectoryPath = null;
options.AutoSessionTracking = false;
}
}

internal static void AddIntegrations(SentryUnityOptions options)
{
if (options.AttachViewHierarchy)
{
options.AddEventProcessor(new ViewHierarchyEventProcessor(options));
}
if (options.AttachScreenshot)
{
options.AddEventProcessor(new ScreenshotEventProcessor(options));
}

if (!ApplicationAdapter.Instance.IsEditor &&
options.UnityInfo.IL2CPP &&
options.Il2CppLineNumberSupportEnabled)
{
if (options.UnityInfo.Il2CppMethods is not null)
{
options.AddExceptionProcessor(new UnityIl2CppEventExceptionProcessor(options));
}
else
{
options.DiagnosticLogger?.LogWarning("Failed to find required IL2CPP methods - Skipping line number support");
}
}
}

internal static void ConfigureUnsupportedPlatformFallbacks(SentryUnityOptions options)
{
if (!options.UnityInfo.IsKnownPlatform())
{
options.DisableFileWrite = true;

// Requires file access, see https://github.com/getsentry/sentry-unity/issues/290#issuecomment-1163608988
if (options.AutoSessionTracking)
{
options.DiagnosticLogger?.LogDebug("Platform support for automatic session tracking is unknown: disabling.");
options.AutoSessionTracking = false;
}

// This is only provided on a best-effort basis for other than the explicitly supported platforms.
if (options.BackgroundWorker is null)
{
options.DiagnosticLogger?.LogDebug("Platform support for background thread execution is unknown: using WebBackgroundWorker.");
options.BackgroundWorker = new WebBackgroundWorker(options, SentryMonoBehaviour.Instance);
}
}
}
}
Loading
Loading