-
-
Notifications
You must be signed in to change notification settings - Fork 203
/
SentrySdk.cs
268 lines (230 loc) · 12.1 KB
/
SentrySdk.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
using Android.Content.PM;
using Android.OS;
using Sentry.Android;
using Sentry.Android.Callbacks;
using Sentry.Android.Extensions;
using Sentry.JavaSdk.Android.Core;
// Don't let the Sentry Android SDK auto-init, as we do that manually in SentrySdk.Init
// See https://docs.sentry.io/platforms/android/configuration/manual-init/
// This attribute automatically adds the metadata to the final AndroidManifest.xml
[assembly: MetaData("io.sentry.auto-init", Value = "false")]
// Set the hybrid SDK name
[assembly: MetaData("io.sentry.sdk.name", Value = "sentry.java.android.dotnet")]
// ReSharper disable once CheckNamespace
namespace Sentry;
public static partial class SentrySdk
{
private static AndroidContext AppContext { get; set; } = Application.Context;
/// <summary>
/// Initializes the SDK for Android, with an optional configuration options callback.
/// </summary>
/// <param name="context">The Android application context.</param>
/// <param name="configureOptions">The configuration options callback.</param>
/// <returns>An object that should be disposed when the application terminates.</returns>
[Obsolete("It is no longer required to provide the application context when calling Init. " +
"This method may be removed in a future major release.")]
public static IDisposable Init(AndroidContext context, Action<SentryOptions>? configureOptions)
{
AppContext = context;
return Init(configureOptions);
}
/// <summary>
/// Initializes the SDK for Android, using a configuration options instance.
/// </summary>
/// <param name="context">The Android application context.</param>
/// <param name="options">The configuration options instance.</param>
/// <returns>An object that should be disposed when the application terminates.</returns>
[Obsolete("It is no longer required to provide the application context when calling Init. " +
"This method may be removed in a future major release.")]
public static IDisposable Init(AndroidContext context, SentryOptions options)
{
AppContext = context;
return Init(options);
}
private static void InitSentryAndroidSdk(SentryOptions options)
{
// Set default release and distribution
options.Release ??= GetDefaultReleaseString();
options.Distribution ??= GetDefaultDistributionString();
// Make sure we capture managed exceptions from the Android environment
AndroidEnvironment.UnhandledExceptionRaiser += AndroidEnvironment_UnhandledExceptionRaiser;
// Define the configuration for the Android SDK
SentryAndroidOptions? nativeOptions = null;
var configuration = new OptionsConfigurationCallback(o =>
{
// Capture the android options reference on the outer scope
nativeOptions = o;
// TODO: Should we set the DistinctId to match the one used by GlobalSessionManager?
//o.DistinctId = ?
// These options are copied over from our SentryOptions
o.AttachStacktrace = options.AttachStacktrace;
o.Debug = options.Debug;
o.DiagnosticLevel = options.DiagnosticLevel.ToJavaSentryLevel();
o.Dist = options.Distribution;
o.Dsn = options.Dsn;
o.EnableAutoSessionTracking = options.AutoSessionTracking;
o.Environment = options.Environment;
o.FlushTimeoutMillis = (long)options.InitCacheFlushTimeout.TotalMilliseconds;
o.MaxAttachmentSize = options.MaxAttachmentSize;
o.MaxBreadcrumbs = options.MaxBreadcrumbs;
o.MaxCacheItems = options.MaxCacheItems;
o.MaxQueueSize = options.MaxQueueItems;
o.Release = options.Release;
o.SampleRate = (JavaDouble?)options.SampleRate;
o.SendClientReports = options.SendClientReports;
o.SendDefaultPii = options.SendDefaultPii;
o.ServerName = options.ServerName;
o.SessionTrackingIntervalMillis = (long)options.AutoSessionTrackingInterval.TotalMilliseconds;
o.ShutdownTimeoutMillis = (long)options.ShutdownTimeout.TotalMilliseconds;
if (options.CacheDirectoryPath is { } cacheDirectoryPath)
{
// Set a separate cache path for the Android SDK so we don't step on the managed one
o.CacheDirPath = Path.Combine(cacheDirectoryPath, "android");
}
// NOTE: Tags in options.DefaultTags should not be passed down, because we already call SetTag on each
// one when sending events, which is relayed through the scope observer.
if (options.HttpProxy is System.Net.WebProxy proxy)
{
var creds = proxy.Credentials as System.Net.NetworkCredential;
o.SetProxy(new JavaSdk.SentryOptions.Proxy
{
Host = proxy.Address?.Host,
Port = proxy.Address?.Port.ToString(CultureInfo.InvariantCulture),
User = creds?.UserName,
Pass = creds?.Password
});
}
if (options.BeforeBreadcrumbInternal is { } beforeBreadcrumb)
{
o.BeforeBreadcrumb = new BeforeBreadcrumbCallback(beforeBreadcrumb);
}
// These options we have behind feature flags
if (options is { IsPerformanceMonitoringEnabled: true, Native.EnableTracing: true })
{
o.EnableTracing = (JavaBoolean?)options.EnableTracing;
o.TracesSampleRate = (JavaDouble?)options.TracesSampleRate;
if (options.TracesSampler is { } tracesSampler)
{
o.TracesSampler = new TracesSamplerCallback(tracesSampler);
}
}
if (options.Native.EnableBeforeSend && options.BeforeSendInternal is { } beforeSend)
{
o.BeforeSend = new BeforeSendCallback(beforeSend, options, o);
}
// These options are from SentryAndroidOptions
o.AttachScreenshot = options.Native.AttachScreenshot;
o.AnrEnabled = options.Native.AnrEnabled;
o.AnrReportInDebug = options.Native.AnrReportInDebug;
o.AnrTimeoutIntervalMillis = (long)options.Native.AnrTimeoutInterval.TotalMilliseconds;
o.EnableActivityLifecycleBreadcrumbs = options.Native.EnableActivityLifecycleBreadcrumbs;
o.EnableAutoActivityLifecycleTracing = options.Native.EnableAutoActivityLifecycleTracing;
o.EnableActivityLifecycleTracingAutoFinish = options.Native.EnableActivityLifecycleTracingAutoFinish;
o.EnableAppComponentBreadcrumbs = options.Native.EnableAppComponentBreadcrumbs;
o.EnableAppLifecycleBreadcrumbs = options.Native.EnableAppLifecycleBreadcrumbs;
o.EnableRootCheck = options.Native.EnableRootCheck;
o.EnableSystemEventBreadcrumbs = options.Native.EnableSystemEventBreadcrumbs;
o.EnableUserInteractionBreadcrumbs = options.Native.EnableUserInteractionBreadcrumbs;
o.EnableUserInteractionTracing = options.Native.EnableUserInteractionTracing;
// These options are in Java.SentryOptions but not ours
o.AttachThreads = options.Native.AttachThreads;
o.ConnectionTimeoutMillis = (int)options.Native.ConnectionTimeout.TotalMilliseconds;
o.EnableNdk = options.Native.EnableNdk;
o.EnableShutdownHook = options.Native.EnableShutdownHook;
o.EnableUncaughtExceptionHandler = options.Native.EnableUncaughtExceptionHandler;
o.ProfilesSampleRate = (JavaDouble?)options.Native.ProfilesSampleRate;
o.PrintUncaughtStackTrace = options.Native.PrintUncaughtStackTrace;
o.ReadTimeoutMillis = (int)options.Native.ReadTimeout.TotalMilliseconds;
// In-App Excludes and Includes to be passed to the Android SDK
options.Native.InAppExcludes?.ForEach(o.AddInAppExclude);
options.Native.InAppIncludes?.ForEach(o.AddInAppInclude);
// These options are intentionally set and not exposed for modification
o.EnableExternalConfiguration = false;
o.EnableDeduplication = false;
o.AttachServerName = false;
o.NativeSdkName = "sentry.native.dotnet";
// These options are intentionally not expose or modified
//o.MaxRequestBodySize // N/A for Android apps
//o.MaxSpans // See https://github.com/getsentry/sentry-dotnet/discussions/1698
// Don't capture managed exceptions in the native SDK, since we already capture them in the managed SDK
o.AddIgnoredExceptionForType(JavaClass.ForName("android.runtime.JavaProxyThrowable"));
});
// Now initialize the Android SDK (with a logger only if we're debugging)
if (options.Debug && options.DiagnosticLogger is { } logger)
{
var androidLogger = new AndroidDiagnosticLogger(logger);
SentryAndroid.Init(AppContext, androidLogger, configuration);
}
else
{
SentryAndroid.Init(AppContext, configuration);
}
// Set options for the managed SDK that depend on the Android SDK. (The user will not be able to modify these.)
options.AddEventProcessor(new AndroidEventProcessor(nativeOptions!));
if (options.Android.LogCatIntegration != LogCatIntegrationType.None)
{
options.AddEventProcessor(new LogCatAttachmentEventProcessor(options.DiagnosticLogger, options.Android.LogCatIntegration, options.Android.LogCatMaxLines));
}
options.CrashedLastRun = () => JavaSdk.Sentry.IsCrashedLastRun()?.BooleanValue() is true;
options.EnableScopeSync = true;
options.ScopeObserver = new AndroidScopeObserver(options);
// TODO: Pause/Resume
}
private static void AndroidEnvironment_UnhandledExceptionRaiser(object? _, RaiseThrowableEventArgs e)
{
var description = "This exception was caught by the Android global error handler.";
if (!e.Handled)
{
description += " The application likely crashed as a result of this exception.";
}
e.Exception.SetSentryMechanism("UnhandledExceptionRaiser", description, e.Handled);
CaptureException(e.Exception);
if (!e.Handled)
{
Close();
}
}
private static string? GetDefaultReleaseString()
{
var packageName = AppContext.PackageName;
if (packageName == null)
{
return null;
}
#pragma warning disable CS0618 // Type or member is obsolete
var packageInfo = AppContext.PackageManager?.GetPackageInfo(packageName, PackageInfoFlags.Permissions);
#pragma warning restore CS0618 // Type or member is obsolete
return packageInfo == null ? null : $"{packageName}@{packageInfo.VersionName}+{packageInfo.GetVersionCode()}";
}
private static string? GetDefaultDistributionString() => GetAndroidPackageVersionCode()?.ToString();
private static long? GetAndroidPackageVersionCode()
{
var packageName = AppContext.PackageName;
if (packageName == null)
{
return null;
}
#pragma warning disable CS0618 // Type or member is obsolete
var packageInfo = AppContext.PackageManager?.GetPackageInfo(packageName, PackageInfoFlags.Permissions);
#pragma warning restore CS0618 // Type or member is obsolete
return packageInfo?.GetVersionCode();
}
private static long? GetVersionCode(this PackageInfo packageInfo)
{
// The value comes from different property depending on Android version
if (AndroidBuild.VERSION.SdkInt >= BuildVersionCodes.P)
{
#pragma warning disable CA1416
// callsite only reachable on Android >= P (28)
return packageInfo.LongVersionCode;
#pragma warning restore CA1416
}
#pragma warning disable CS0618
// obsolete on Android >= P (28)
#pragma warning disable CA1422
// 'PackageInfo.VersionCode' is obsoleted on: 'Android' 28.0 and later.
return packageInfo.VersionCode;
#pragma warning restore CA1422
#pragma warning restore CS0618
}
}