-
-
Notifications
You must be signed in to change notification settings - Fork 56
Rework Initialization #2227
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
base: chore/cleanup-init
Are you sure you want to change the base?
Rework Initialization #2227
Conversation
@@ -86,7 +86,7 @@ public void SendFeedback() | |||
else | |||
{ | |||
// Since there is no screenshot added we can capture the feedback right away | |||
SentryUnity.CaptureFeedback(_description.text, _email.text, _name.text, addScreenshot: false); | |||
SentrySdk.CaptureFeedback(_description.text, _email.text, _name.text, addScreenshot: false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the desired effect. All things reside within SentrySdk
.
@@ -39,7 +41,7 @@ | |||
|
|||
namespace Sentry.Unity | |||
{ | |||
public static class SentryInitialization | |||
internal static class SentryInitialization |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can safely make this internal
. The SDK ships with it's own .asmdef
that allows us to hide internal types from the user.
@@ -0,0 +1,15 @@ | |||
using System; | |||
|
|||
namespace Sentry.Unity.NativeUtils; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Putting the services into a very distinctive namespace to "discourage" usage.
@@ -120,12 +121,12 @@ public static string GetConfigPath(string? notDefaultConfigName = null) | |||
/// <remarks> | |||
/// Used for loading the SentryUnityOptions from the ScriptableSentryUnityOptions during runtime. | |||
/// </remarks> | |||
public static SentryUnityOptions? LoadSentryUnityOptions(ISentryUnityInfo unityInfo) | |||
public static SentryUnityOptions? LoadSentryUnityOptions() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We no longer need to provide the IUnityInfo
since these are getting set up during startup as part of the new platform services that are statically available.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should get source generated in a follow up PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a better approach than the test I recommended 👍
@@ -49,11 +49,11 @@ private SentryUnitySdk(SentryUnityOptions options) | |||
} | |||
} | |||
|
|||
unitySdk._dotnetSdk = SentrySdk.Init(options); | |||
unitySdk._dotnetSdk = Sentry.SentrySdk.Init(options); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A drawback is that we now need to fully qualify calls through the static API from within the Unity SDK. But our users won't have that problem.
@@ -79,7 +79,7 @@ public void Arguments() | |||
|
|||
// act | |||
MainThreadData.SentrySystemInfo = sysInfo; | |||
SentryUnity.Init(options); | |||
SentrySdk.Init(options); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No more separation between SentryUnity
and SentrySdk
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
quick pass
|
||
Logger.LogDebug("Creating '{0}' span.", InitSpanOperation); | ||
InitSpan = runtimeStartTransaction.StartChild(InitSpanOperation, "runtime initialization"); | ||
Logger.LogDebug("Creating '{0}' span.", SubSystemSpanOperation); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could these two be combined in 1 log message? Debug log can be overwhelming at times
/// We do not intend to remove it. | ||
/// </remarks> | ||
[Obsolete("WARNING: This method deliberately causes a crash, and should not be used in a real application.")] | ||
public static void CauseCrash(CrashType crashType) => Sentry.SentrySdk.CauseCrash(crashType); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this now will require manually keeping in sync. You could have a test that does reflection on SentrySdk
and gets all public members, does the same on the .NET and Unity SDKs and compares the two to make sure there's no new APIs on the .NET side
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry I didn't notice this before but while in general I think this is a great approach, we now expect the static fields to be set before our public methods are called but we don't verify anywhere that's the case and we have a lot of null propagation patterns which hinder the ability to debug what was going on. In particular when customers change the init code, possibly bypassing our auto init/static property set in order to do things "by hand".
|
||
- **Breaking Change**: The Unity SDK's static API has been simplified moved from `Sentry.Unity.SentryUnity` and `Sentry.SentrySdk` | ||
to `Sentry.Unity.SentrySdk`. | ||
This change enables manual SDK initialization with full functionality, previously only available through auto-initialization. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change enables manual SDK initialization with full functionality, previously only available through auto-initialization. | |
This change enables [manual/programatic SDK initialization](https://docs.sentry.io/platforms/unity/configuration/options/programmatic-configuration/) with full functionality, previously only available through auto-initialization. |
{ | ||
Logger = options.DiagnosticLogger; | ||
|
||
Logger?.LogInfo("Attempting to configure native support via the Native SDK"); | ||
|
||
if (!sentryUnityInfo.IsNativeSupportEnabled(options, ApplicationAdapter.Instance.Platform)) | ||
if (!sentryUnityInfo?.IsNativeSupportEnabled(options, ApplicationAdapter.Instance.Platform) ?? false) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sentryUnityInfo
being null is a bigger problem, no?
Shoujld we split this code so we can log specifically sentryUnityInfo
being null as an error. And the native support being off can stay as debug, as-is?
{ | ||
_isLinux = sentryUnityInfo.IsLinux(); | ||
_isLinux = sentryUnityInfo?.IsLinux() ?? false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
since this is all internal code, should we expect null
ISentryUnityInfo
?
Seems to me the caller should either have the instance or avoid getting an insance of this altogether?
If we have that null here, we'd still go ahead initializing everything below. Is that creating a weird scenario where instead we should have avoided Init
being successful?
/// <param name="sentryUnityInfo">Infos about the current Unity environment</param> | ||
public static void Configure(SentryUnityOptions options, ISentryUnityInfo sentryUnityInfo) => | ||
Configure(options, sentryUnityInfo, ApplicationAdapter.Instance.Platform); | ||
public static void Configure(SentryUnityOptions options) => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a public method, and we only need to pass options
.
But if the static SentryPlatformServices.UnityInfo
wasn't set properly before hand, we'll just pass that through below and it'll be hard to trace back why that was.
I think we could validate here (if we expect SentryPlatformServices.UnityInfo
not to be null here) that it is indeed not null. And deal with it (log and continue, throw?)
{ | ||
options.DiagnosticLogger?.LogInfo("Attempting to configure native support via the Cocoa SDK"); | ||
|
||
if (!sentryUnityInfo.IsNativeSupportEnabled(options, platform)) | ||
if (!sentryUnityInfo?.IsNativeSupportEnabled(options, platform) ?? false) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same situation where it's not clear if it's a mix up with setting up this static thing before calling this, or IsNativeSupportEnabled
returning false
, and making things hard to debug
@@ -67,7 +67,7 @@ internal static void Configure(SentryUnityOptions options, ISentryUnityInfo sent | |||
}; | |||
|
|||
options.NativeSupportCloseCallback += () => Close(options, sentryUnityInfo, platform); | |||
if (sentryUnityInfo.IL2CPP) | |||
if (sentryUnityInfo?.IL2CPP ?? false) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also, lets avoid this pattern throughout.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a better approach than the test I recommended 👍
Fixes #2200
Required getsentry/sentry-dotnet#4322
Goal
As stated in the issue: The SDK needs to initializeable in two ways: self, and manual. Both these paths need to provide the same functionality.
Approach
Sentry.Unity.SentrySdk
- This way the Unity SDK can omit certain methods like the .NET SDKsInit
SentryInitialization.cs
that is getting compiled with the gameInit
. This allows users to call the same method.Implementation Details
Hide the .NET SDK
This is done by conditionally making the .NET SDKs
SentrySdk
s accessorinternal
getsentry/sentry-dotnet#4322The .NET SDK now considers Unity as a platform and our very own
.props
allows us to conditionally set a constant.Rename Rename
SentryUnity
toSentrySdk
Ideally, we'd only have one static API that allows for discoverability of what's available. This could either be
SentrySdk
orSentryUnity
. I'm going withSentrySdk
here as:usings
. This change will require users to change them fromusing Sentry;
tousing Sentry.Unity
. Which is still easier than having to go through every instance of calling into the SDK and change these fromSentrySdk
toSentryUnity
.Sentry.Unity
namespace and avoids conflicts. Attempting to accessSentry.SentrySdk.Capture
will create an errorCannot access internal class 'SentrySdk' here
.summary
to the internalSentrySdk
guiding users towards the right namespaceMake
Sentry.Unity.SentrySdk
apartial
classThe Unity SDK version of
SentrySdk
is now split in two partial classes.SentrySdk.cs
is where the Unity SDK specific code resides and basically contains "overwritten" methods likeInit
,GetLastRunState
.SentrySdk.Dotnet.cs
- TODO: This should be source generated and end up asSentrySdk.Dotnet.g.cs
The .NET SDK
SentrySdk
is now hidden and inaccessible. To give users access to the rest of the API we're putting all the rest of the static API in there hand have it forward the calls into the .NET SDK.Hide the
.cs
scriptsSentryInitialization
andSentryIntegrations
Our package ships with
io.sentry.asmdef
which creates our own assembly for us within the game's project. This allows us to make these classesinternal
, but keep their name. They are now undiscoverable for users - and should have been so in the first place.Changes to
SentryInitialization
The
SentryInitialization
still receives the "first thing that happens when the game starts callback via theRuntimeLoad
attribute. This allows us to set up the SDK all the way to be ready to be initialized. Instead of doing the actual initialization inside the script that gets compiled with the game, this now injects the platform specific configure calls into the SDK.Add a static
SentryPlatformServices
This is now the place that holds the injected platform specific configure callbacks and the
IUnityInfo
that we use to access version and platform specific code like IL2CPP runtime checks and backend calls for line number support.We're setting these as the very first thing and can so guarantee that they are available, regardless whether the SDK auto-initializes or has the user call
Init
manually.Move all initialization code into the SDK
Having the platform specific configure callbacks available via the
SentryPlatformServices
class allows us to call conditionally compiled code from within the pre-compiled SDK. All initialization paths funnel back together in that oneInit
call.Migration Path
Unfortunately, this is a breaking change. As such, we need to provide a migration path. When updating the SDK users will face the following error on the console
Since we can modify the now internal SDK's
<summary>
we can already put some help thereIDEs like Rider will allow to automatically import the missing references
Things to follow up on
using Sentry.Unity
vs.using Sentry
The entirety of the API is now discoverable within
SentrySdk.
but the namespace is splitSentry
- Contains the .NET SDK's types, i.e.SentryId
Sentry.Unity
- Contains the static APISo users end up in situations where they need to always bring
using Sentry.Unity
and sometimes additionally needusing Sentry
. That was an issue before already, it's just something to be aware of.Consider turning
IsEnabled
in the config window intoisSelfInitEnabled
During build the SDK was opinionated regarding whether to bring a native SDK and tied setup and symbol upload to the
isEnabled
check. When we merged all option-configuration callbacks into one in #1888 the line already blurred.And now, the SDK cannot reasonably be certain whether when it will be initialized at runtime. With this I think we have to consider
IsEnabled
cannot be considered anymoreIsEnabled
andInitializationType.BuildTime
-> setup native projectSource Generator for
Sentry.Dotnet
Right now the
Sentry.Dotnet.cs
is created by hand. It forwards all calls to the now internal static API while omittingInit
. This allows us to keep the Unity specific methods in theSentrySdk.cs
file but it also requires us to manually update if and when the .NET SDK's public API changes.Alternative (attempted) approaches
extension members
a try by bumping to C#14. But this cannot be consumed by the user's code.SentrySdk.Init
methodsinternal
. This somewhat works but gets us stuck in a place. We'd have the API split betweenSentryUnity.Init
vsSentrySdk.Capture
.SentryUnity
as name for the type for the static API. But - and that's an assumption on my part - that would break the actual calls fromSentrySdk.CaptureException()
SentryUnity.CaptureException()
instead of having the IDE fix theusings
and be done with it.Sentry
namespace. This did not work for conflicting reasons.