From c4316482057548bb09dd1374609a4de585c313ce Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Thu, 11 Aug 2022 09:45:59 -0700 Subject: [PATCH 01/28] ProcessInfo cleanup --- src/Sentry/Internal/ProcessInfo.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Sentry/Internal/ProcessInfo.cs b/src/Sentry/Internal/ProcessInfo.cs index fa2b03a164..f9779f0e52 100644 --- a/src/Sentry/Internal/ProcessInfo.cs +++ b/src/Sentry/Internal/ProcessInfo.cs @@ -74,7 +74,6 @@ internal Task PreciseAppStartupTask { #if ANDROID || IOS || MACCATALYST options.LogWarning("StartupTimeDetectionMode.Best is not available on this platform. Using 'Fast' mode."); - return; #else // StartupTime is set to UtcNow in this constructor. // That's computationally cheap but not very precise. @@ -99,7 +98,7 @@ internal Task PreciseAppStartupTask } } -#if !ANDROID +#if !(ANDROID || IOS || MACCATALYST) private static DateTimeOffset GetStartupTime() { using var proc = Process.GetCurrentProcess(); From 47613e8c3cce563c87fe686083f4ce764d5b7b2a Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Thu, 11 Aug 2022 09:46:52 -0700 Subject: [PATCH 02/28] Fix typos --- src/Sentry/Platforms/Android/Extensions/MiscExtensions.cs | 2 +- src/Sentry/Platforms/Android/SentryOptions.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Sentry/Platforms/Android/Extensions/MiscExtensions.cs b/src/Sentry/Platforms/Android/Extensions/MiscExtensions.cs index 217c30c7f9..38329ce158 100644 --- a/src/Sentry/Platforms/Android/Extensions/MiscExtensions.cs +++ b/src/Sentry/Platforms/Android/Extensions/MiscExtensions.cs @@ -1,6 +1,6 @@ namespace Sentry.Android.Extensions; -internal static class SMiscExtensions +internal static class MiscExtensions { public static SentryId ToSentryId(this Java.Protocol.SentryId sentryId) => new(Guid.Parse(sentryId.ToString())); diff --git a/src/Sentry/Platforms/Android/SentryOptions.cs b/src/Sentry/Platforms/Android/SentryOptions.cs index c484493e9c..3f089f3cdc 100644 --- a/src/Sentry/Platforms/Android/SentryOptions.cs +++ b/src/Sentry/Platforms/Android/SentryOptions.cs @@ -250,7 +250,7 @@ public class AndroidOptions /// events that originate from the embedded Android SDK. The default value is false (disabled). /// /// - /// This is an experimental feature and is imperefct, as the .NET SDK and the embedded Android SDK don't + /// This is an experimental feature and is imperfect, as the .NET SDK and the embedded Android SDK don't /// implement all of the same features that may be present in the event graph. Some optional elements may /// be stripped away during the roundtripping between the two SDKs. Use with caution. /// From 00139746bcd2283ec573ea782d936e3904056558 Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Thu, 11 Aug 2022 09:48:24 -0700 Subject: [PATCH 03/28] default tags --- src/Sentry/Platforms/Android/SentrySdk.cs | 7 ++----- src/Sentry/Platforms/iOS/SentrySdk.cs | 3 ++- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/Sentry/Platforms/Android/SentrySdk.cs b/src/Sentry/Platforms/Android/SentrySdk.cs index ab0b108ba8..234afe378d 100644 --- a/src/Sentry/Platforms/Android/SentrySdk.cs +++ b/src/Sentry/Platforms/Android/SentrySdk.cs @@ -94,11 +94,8 @@ private static void InitSentryAndroidSdk(SentryOptions options) o.CacheDirPath = Path.Combine(cacheDirectoryPath, "android"); } - var javaTags = o.Tags; - foreach (var tag in options.DefaultTags) - { - javaTags.Add(tag); - } + // 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) { diff --git a/src/Sentry/Platforms/iOS/SentrySdk.cs b/src/Sentry/Platforms/iOS/SentrySdk.cs index 6e654566d5..70a95a4515 100644 --- a/src/Sentry/Platforms/iOS/SentrySdk.cs +++ b/src/Sentry/Platforms/iOS/SentrySdk.cs @@ -54,7 +54,8 @@ private static void InitSentryCocoaSdk(SentryOptions options) // NOTE: options.CacheDirectoryPath - No option for this in Sentry Cocoa, but caching is still enabled // https://github.com/getsentry/sentry-cocoa/issues/1051 - // o.??? = options.DefaultTags + // 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.BeforeBreadcrumb is { } beforeBreadcrumb) { From 8aaf552b4ba91f3f2692cacc7b629be956d1aa0b Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Thu, 11 Aug 2022 15:23:19 -0700 Subject: [PATCH 04/28] Binding updates --- src/Sentry/Platforms/iOS/Bindings/ApiDefinitions.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Sentry/Platforms/iOS/Bindings/ApiDefinitions.cs b/src/Sentry/Platforms/iOS/Bindings/ApiDefinitions.cs index c4ac2e9df1..fff877a44f 100644 --- a/src/Sentry/Platforms/iOS/Bindings/ApiDefinitions.cs +++ b/src/Sentry/Platforms/iOS/Bindings/ApiDefinitions.cs @@ -827,7 +827,7 @@ interface SentryOptions // @property (copy, nonatomic) SentryBeforeSendEventCallback _Nullable beforeSend; [NullAllowed, Export ("beforeSend", ArgumentSemantic.Copy)] - Func BeforeSend { get; set; } + Func BeforeSend { get; set; } // @property (copy, nonatomic) SentryBeforeBreadcrumbCallback _Nullable beforeBreadcrumb; [NullAllowed, Export ("beforeBreadcrumb", ArgumentSemantic.Copy)] @@ -916,7 +916,7 @@ interface SentryOptions // @property (nonatomic) SentryTracesSamplerCallback _Nullable tracesSampler; [NullAllowed, Export ("tracesSampler", ArgumentSemantic.Assign)] - Func TracesSampler { get; set; } + Func TracesSampler { get; set; } // @property (readonly, assign, nonatomic) BOOL isTracingEnabled; [Export ("isTracingEnabled")] From e12358c999e746463673be83a0f61f6468891617 Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Thu, 11 Aug 2022 15:24:10 -0700 Subject: [PATCH 05/28] More Cocoa Extensions --- .../iOS/Extensions/CocoaExtensions.cs | 115 +++++++++++++++++- 1 file changed, 113 insertions(+), 2 deletions(-) diff --git a/src/Sentry/Platforms/iOS/Extensions/CocoaExtensions.cs b/src/Sentry/Platforms/iOS/Extensions/CocoaExtensions.cs index 34ad2fd8ce..fc3d2cfefc 100644 --- a/src/Sentry/Platforms/iOS/Extensions/CocoaExtensions.cs +++ b/src/Sentry/Platforms/iOS/Extensions/CocoaExtensions.cs @@ -10,6 +10,15 @@ internal static class CocoaExtensions public static NSDate ToNSDate(this DateTimeOffset timestamp) => (NSDate)timestamp.UtcDateTime; public static string? ToJsonString(this NSObject? obj, IDiagnosticLogger? logger = null) + { + using var data = obj.ToJsonData(logger); + return data?.ToString(); + } + + public static Stream? ToJsonStream(this NSObject? obj, IDiagnosticLogger? logger = null) => + obj.ToJsonData(logger)?.AsStream(); + + private static NSData? ToJsonData(this NSObject? obj, IDiagnosticLogger? logger = null) { if (obj == null) { @@ -34,8 +43,7 @@ internal static class CocoaExtensions try { - using var data = NSJsonSerialization.Serialize(obj, 0, out _); - return data.ToString(); + return NSJsonSerialization.Serialize(obj, 0, out _); } catch (Exception ex) { @@ -86,6 +94,61 @@ internal static class CocoaExtensions return dict.ToStringDictionary(logger); } + public static Dictionary ToObjectDictionary( + this NSDictionary? dict, + IDiagnosticLogger? logger = null) + where TValue : NSObject + { + if (dict == null) + { + return new Dictionary(); + } + + var result = new Dictionary(capacity: (int)dict.Count); + foreach (var key in dict.Keys) + { + var value = dict[key]; + switch (value) + { + case null or NSNull: + result.Add(key, null); + break; + case NSString s: + result.Add(key, s); + break; + case NSNumber n: + result.Add(key, n.ToObject()); + break; + default: + if (value.ToJsonString(logger) is { } json) + { + result.Add(key, json); + } + else + { + logger?.LogWarning("Could not add value of type {0} to dictionary.", value.GetType()); + } + + break; + } + } + + return result; + } + + public static Dictionary? ToNullableObjectDictionary( + this NSDictionary? dict, + IDiagnosticLogger? logger = null) + where TValue : NSObject + { + if (dict is null || dict.Count == 0) + { + return null; + } + + return dict.ToObjectDictionary(logger); + } + public static NSDictionary ToNSDictionary( this IEnumerable> dict) { @@ -112,4 +175,52 @@ internal static class CocoaExtensions public static NSDictionary? ToNullableNSDictionary( this IReadOnlyCollection> dict) => dict.Count == 0 ? null : dict.ToNSDictionary(); + + /// + /// Converts an to a .NET primitive data type and returns the result box in an . + /// + /// The to convert. + /// An that contains the number in its primitive type. + /// Thrown when the number's ObjCType was unrecognized. + /// + /// This method always returns a result that is compatible with its value, but does not always give the expected result. + /// Specifically: + /// + /// byte returns short + /// ushort return int + /// uint returns long + /// ulong returns long unless it's > long.MaxValue + /// n/nu types return more primitive types (ex. nfloat => double) + /// + /// Type encodings are listed here: + /// https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html + /// + public static object ToObject(this NSNumber n) + { + if (n is NSDecimalNumber d) + { + // handle NSDecimalNumber type directly + return (decimal)d.NSDecimalValue; + } + + return n.ObjCType switch + { + "c" when n.Class.Name == "__NSCFBoolean" => n.BoolValue, // ObjC bool + "c" => n.SByteValue, // signed char + "i" => n.Int32Value, // signed int + "s" => n.Int16Value, // signed short + "l" => n.Int32Value, // signed long (32 bit) + "q" => n.Int64Value, // signed long long (64 bit) + "C" => n.ByteValue, // unsigned char + "I" => n.UInt32Value, // unsigned int + "S" => n.UInt16Value, // unsigned short + "L" => n.UInt32Value, // unsigned long (32 bit) + "Q" => n.UInt64Value, // unsigned long long (64 bit) + "f" => n.FloatValue, // float + "d" => n.DoubleValue, // double + "B" => n.BoolValue, // C++ bool or C99 _Bool + _ => throw new ArgumentOutOfRangeException(nameof(n), n, + $"NSNumber \"{n.StringValue}\" has an unknown ObjCType \"{n.ObjCType}\" (Class: \"{n.Class.Name}\")") + }; + } } From a322d555566e91d6b868b836f0d0304562371385 Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Thu, 11 Aug 2022 15:27:10 -0700 Subject: [PATCH 06/28] default usings --- src/Sentry/Platforms/iOS/Sentry.iOS.props | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Sentry/Platforms/iOS/Sentry.iOS.props b/src/Sentry/Platforms/iOS/Sentry.iOS.props index 66e744b7ea..d4c94d76c8 100644 --- a/src/Sentry/Platforms/iOS/Sentry.iOS.props +++ b/src/Sentry/Platforms/iOS/Sentry.iOS.props @@ -14,6 +14,8 @@ + + From 2d6899db6190a1b65f03f5b24dec567a77cd3c54 Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Thu, 11 Aug 2022 15:27:36 -0700 Subject: [PATCH 07/28] Start event processor --- src/Sentry/Platforms/iOS/IosEventProcessor.cs | 24 +++++++++++++++++++ src/Sentry/Platforms/iOS/SentrySdk.cs | 2 +- 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 src/Sentry/Platforms/iOS/IosEventProcessor.cs diff --git a/src/Sentry/Platforms/iOS/IosEventProcessor.cs b/src/Sentry/Platforms/iOS/IosEventProcessor.cs new file mode 100644 index 0000000000..9bda8b8c59 --- /dev/null +++ b/src/Sentry/Platforms/iOS/IosEventProcessor.cs @@ -0,0 +1,24 @@ +using Sentry.Extensibility; + +namespace Sentry.iOS; + +internal class IosEventProcessor : ISentryEventProcessor, IDisposable +{ + private readonly SentryCocoa.SentryOptions _options; + + public IosEventProcessor(SentryCocoa.SentryOptions options) + { + _options = options; + } + + public SentryEvent Process(SentryEvent @event) + { + // TODO: Apply device/os context info + + return @event; + } + + public void Dispose() + { + } +} diff --git a/src/Sentry/Platforms/iOS/SentrySdk.cs b/src/Sentry/Platforms/iOS/SentrySdk.cs index 70a95a4515..ae9866b283 100644 --- a/src/Sentry/Platforms/iOS/SentrySdk.cs +++ b/src/Sentry/Platforms/iOS/SentrySdk.cs @@ -128,7 +128,7 @@ private static void InitSentryCocoaSdk(SentryOptions options) }); // Set options for the managed SDK that depend on the Cocoa SDK - // options.AddEventProcessor(new CocoaEventProcessor(cocoaOptions!)); + options.AddEventProcessor(new IosEventProcessor(cocoaOptions!)); options.CrashedLastRun = () => SentryCocoa.SentrySDK.CrashedLastRun; options.EnableScopeSync = true; options.ScopeObserver = new IosScopeObserver(options); From c7166b81708b5e20e6442777747cf8225d5dfe20 Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Thu, 11 Aug 2022 15:28:51 -0700 Subject: [PATCH 08/28] iOS options accessor --- src/Sentry/Platforms/iOS/SentryOptions.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Sentry/Platforms/iOS/SentryOptions.cs b/src/Sentry/Platforms/iOS/SentryOptions.cs index 0e4f10bd6d..3d47a9fa4e 100644 --- a/src/Sentry/Platforms/iOS/SentryOptions.cs +++ b/src/Sentry/Platforms/iOS/SentryOptions.cs @@ -6,14 +6,15 @@ public partial class SentryOptions /// /// Exposes additional options for the iOS platform. /// - public IOSOptions IOS { get; } = new(); + // ReSharper disable once InconsistentNaming + public IosOptions iOS { get; } = new(); /// /// Provides additional options for the iOS platform. /// - public class IOSOptions + public class IosOptions { - internal IOSOptions() { } + internal IosOptions() { } // TODO } From 3c9a02f0d124f3e916990ea134c91aa13857fbea Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Thu, 11 Aug 2022 15:29:45 -0700 Subject: [PATCH 09/28] Enums --- .../iOS/Extensions/EnumExtensions.cs | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/src/Sentry/Platforms/iOS/Extensions/EnumExtensions.cs b/src/Sentry/Platforms/iOS/Extensions/EnumExtensions.cs index ca2d7c2601..9c64ac57a5 100644 --- a/src/Sentry/Platforms/iOS/Extensions/EnumExtensions.cs +++ b/src/Sentry/Platforms/iOS/Extensions/EnumExtensions.cs @@ -27,4 +27,70 @@ level switch BreadcrumbLevel.Critical => SentryCocoa.SentryLevel.Fatal, _ => throw new ArgumentOutOfRangeException(nameof(level), level, message: default) }; + + public static bool? ToNullableBoolean(this SentryCocoa.SentrySampleDecision decision) => + decision switch + { + SentryCocoa.SentrySampleDecision.Yes => true, + SentryCocoa.SentrySampleDecision.No => false, + SentryCocoa.SentrySampleDecision.Undecided => null, + _ => throw new ArgumentOutOfRangeException(nameof(decision), decision, null) + }; + + public static SentryCocoa.SentrySampleDecision ToCocoaSampleDecision(this bool? decision) => + decision switch + { + true => SentryCocoa.SentrySampleDecision.Yes, + false => SentryCocoa.SentrySampleDecision.No, + null => SentryCocoa.SentrySampleDecision.Undecided + }; + + public static SpanStatus? ToSpanStatus(this SentryCocoa.SentrySpanStatus status) => + status switch + { + SentryCocoa.SentrySpanStatus.Undefined => null, + SentryCocoa.SentrySpanStatus.Ok => SpanStatus.Ok, + SentryCocoa.SentrySpanStatus.Cancelled => SpanStatus.Cancelled, + SentryCocoa.SentrySpanStatus.InternalError => SpanStatus.InternalError, + SentryCocoa.SentrySpanStatus.UnknownError => SpanStatus.UnknownError, + SentryCocoa.SentrySpanStatus.InvalidArgument => SpanStatus.InvalidArgument, + SentryCocoa.SentrySpanStatus.DeadlineExceeded => SpanStatus.DeadlineExceeded, + SentryCocoa.SentrySpanStatus.NotFound => SpanStatus.NotFound, + SentryCocoa.SentrySpanStatus.AlreadyExists => SpanStatus.AlreadyExists, + SentryCocoa.SentrySpanStatus.PermissionDenied => SpanStatus.PermissionDenied, + SentryCocoa.SentrySpanStatus.ResourceExhausted => SpanStatus.ResourceExhausted, + SentryCocoa.SentrySpanStatus.FailedPrecondition => SpanStatus.FailedPrecondition, + SentryCocoa.SentrySpanStatus.Aborted => SpanStatus.Aborted, + SentryCocoa.SentrySpanStatus.OutOfRange => SpanStatus.OutOfRange, + SentryCocoa.SentrySpanStatus.Unimplemented => SpanStatus.Unimplemented, + SentryCocoa.SentrySpanStatus.Unavailable => SpanStatus.Unavailable, + SentryCocoa.SentrySpanStatus.DataLoss => SpanStatus.DataLoss, + SentryCocoa.SentrySpanStatus.Unauthenticated => SpanStatus.Unauthenticated, + _ => throw new ArgumentOutOfRangeException(nameof(status), status, message: default) + }; + + public static SentryCocoa.SentrySpanStatus ToCocoaSpanStatus(this SpanStatus? status) => + status switch + { + null => SentryCocoa.SentrySpanStatus.Undefined, + SpanStatus.Ok => SentryCocoa.SentrySpanStatus.Ok, + SpanStatus.Cancelled => SentryCocoa.SentrySpanStatus.Cancelled, + SpanStatus.InternalError => SentryCocoa.SentrySpanStatus.InternalError, + SpanStatus.UnknownError => SentryCocoa.SentrySpanStatus.UnknownError, + SpanStatus.InvalidArgument => SentryCocoa.SentrySpanStatus.InvalidArgument, + SpanStatus.DeadlineExceeded => SentryCocoa.SentrySpanStatus.DeadlineExceeded, + SpanStatus.NotFound => SentryCocoa.SentrySpanStatus.NotFound, + SpanStatus.AlreadyExists => SentryCocoa.SentrySpanStatus.AlreadyExists, + SpanStatus.PermissionDenied => SentryCocoa.SentrySpanStatus.PermissionDenied, + SpanStatus.ResourceExhausted => SentryCocoa.SentrySpanStatus.ResourceExhausted, + SpanStatus.FailedPrecondition => SentryCocoa.SentrySpanStatus.FailedPrecondition, + SpanStatus.Aborted => SentryCocoa.SentrySpanStatus.Aborted, + SpanStatus.OutOfRange => SentryCocoa.SentrySpanStatus.OutOfRange, + SpanStatus.Unimplemented => SentryCocoa.SentrySpanStatus.Unimplemented, + SpanStatus.Unavailable => SentryCocoa.SentrySpanStatus.Unavailable, + SpanStatus.DataLoss => SentryCocoa.SentrySpanStatus.DataLoss, + SpanStatus.Unauthenticated => SentryCocoa.SentrySpanStatus.Unauthenticated, + _ => throw new ArgumentOutOfRangeException(nameof(status), status, message: default) + }; + } From 825f24fe1962e325e40586d6491db7e533151690 Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Thu, 11 Aug 2022 15:30:58 -0700 Subject: [PATCH 10/28] Tracing --- .../iOS/Extensions/MiscExtensions.cs | 12 +++++++ .../Extensions/SamplingContextExtensions.cs | 13 ++++++++ .../iOS/Facades/TransactionContextFacade.cs | 31 +++++++++++++++++++ src/Sentry/Platforms/iOS/SentryOptions.cs | 10 ++++++ src/Sentry/Platforms/iOS/SentrySdk.cs | 14 ++++++++- 5 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 src/Sentry/Platforms/iOS/Extensions/MiscExtensions.cs create mode 100644 src/Sentry/Platforms/iOS/Extensions/SamplingContextExtensions.cs create mode 100644 src/Sentry/Platforms/iOS/Facades/TransactionContextFacade.cs diff --git a/src/Sentry/Platforms/iOS/Extensions/MiscExtensions.cs b/src/Sentry/Platforms/iOS/Extensions/MiscExtensions.cs new file mode 100644 index 0000000000..c463560a9d --- /dev/null +++ b/src/Sentry/Platforms/iOS/Extensions/MiscExtensions.cs @@ -0,0 +1,12 @@ +namespace Sentry.iOS.Extensions; + +internal static class MiscExtensions +{ + public static SentryId ToSentryId(this SentryCocoa.SentryId sentryId) => new(Guid.Parse(sentryId.SentryIdString)); + + public static SentryCocoa.SentryId ToCocoaSentryId(this SentryId sentryId) => new(sentryId.ToString()); + + public static SpanId ToSpanId(this SentryCocoa.SentrySpanId spanId) => new(spanId.SentrySpanIdString); + + public static SentryCocoa.SentrySpanId ToCocoaSpanId(this SpanId spanId) => new(spanId.ToString()); +} diff --git a/src/Sentry/Platforms/iOS/Extensions/SamplingContextExtensions.cs b/src/Sentry/Platforms/iOS/Extensions/SamplingContextExtensions.cs new file mode 100644 index 0000000000..a5df765b17 --- /dev/null +++ b/src/Sentry/Platforms/iOS/Extensions/SamplingContextExtensions.cs @@ -0,0 +1,13 @@ +using Sentry.iOS.Facades; + +namespace Sentry.iOS.Extensions; + +internal static class SamplingContextExtensions +{ + public static TransactionSamplingContext ToTransactionSamplingContext(this SentryCocoa.SentrySamplingContext context) + { + var transactionContext = new TransactionContextFacade(context.TransactionContext); + var customSamplingContext = context.CustomSamplingContext.ToObjectDictionary(); + return new TransactionSamplingContext(transactionContext, customSamplingContext); + } +} diff --git a/src/Sentry/Platforms/iOS/Facades/TransactionContextFacade.cs b/src/Sentry/Platforms/iOS/Facades/TransactionContextFacade.cs new file mode 100644 index 0000000000..b1014de5a7 --- /dev/null +++ b/src/Sentry/Platforms/iOS/Facades/TransactionContextFacade.cs @@ -0,0 +1,31 @@ +using Sentry.iOS.Extensions; + +namespace Sentry.iOS.Facades; + +internal class TransactionContextFacade : ITransactionContext +{ + private readonly SentryCocoa.SentryTransactionContext _context; + + internal TransactionContextFacade(SentryCocoa.SentryTransactionContext context) + { + _context = context; + } + + public string Name => _context.Name; + + public bool? IsParentSampled => _context.ParentSampled.ToNullableBoolean(); + + public SpanId SpanId => _context.SpanId.ToSpanId(); + + public SpanId? ParentSpanId => _context.ParentSpanId?.ToSpanId(); + + public SentryId TraceId => _context.TraceId.ToSentryId(); + + public string Operation => _context.Operation; + + public string? Description => _context.Description; + + public SpanStatus? Status => _context.Status.ToSpanStatus(); + + public bool? IsSampled => _context.Sampled.ToNullableBoolean(); +} diff --git a/src/Sentry/Platforms/iOS/SentryOptions.cs b/src/Sentry/Platforms/iOS/SentryOptions.cs index 3d47a9fa4e..8ef83568de 100644 --- a/src/Sentry/Platforms/iOS/SentryOptions.cs +++ b/src/Sentry/Platforms/iOS/SentryOptions.cs @@ -16,6 +16,16 @@ public class IosOptions { internal IosOptions() { } + // ---------- From Cocoa's SentryOptions.h ---------- + // TODO + + // ---------- Other ---------- + + /// + /// Gets or sets a value that indicates if tracing features are enabled on the embedded Cocoa SDK. + /// The default value is false (disabled). + /// + public bool EnableCocoaSdkTracing { get; set; } } } diff --git a/src/Sentry/Platforms/iOS/SentrySdk.cs b/src/Sentry/Platforms/iOS/SentrySdk.cs index ae9866b283..4a7556c9a7 100644 --- a/src/Sentry/Platforms/iOS/SentrySdk.cs +++ b/src/Sentry/Platforms/iOS/SentrySdk.cs @@ -64,7 +64,19 @@ private static void InitSentryCocoaSdk(SentryOptions options) .ToCocoaBreadcrumb()!; } - // TOOD: Work on below (copied from Android) + // These options we have behind feature flags + if (options.iOS.EnableCocoaSdkTracing) + { + o.TracesSampleRate = options.TracesSampleRate; + + if (options.TracesSampler is { } tracesSampler) + { + // Note: Nullable return is allowed but delegate is generated incorrectly + o.TracesSampler = context => tracesSampler(context.ToTransactionSamplingContext())!; + } + } + + // TODO: Work on below (copied from Android implementation - needs updating) // // // These options we have behind feature flags From 4b69cea591ccfeb8c2c91f81c1fd85cf49f44f30 Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Thu, 11 Aug 2022 15:31:40 -0700 Subject: [PATCH 11/28] Start BeforeSend --- .../iOS/Extensions/SentryEventExtensions.cs | 45 +++++++++++++++++++ src/Sentry/Platforms/iOS/SentryOptions.cs | 11 +++++ src/Sentry/Platforms/iOS/SentrySdk.cs | 6 +++ 3 files changed, 62 insertions(+) create mode 100644 src/Sentry/Platforms/iOS/Extensions/SentryEventExtensions.cs diff --git a/src/Sentry/Platforms/iOS/Extensions/SentryEventExtensions.cs b/src/Sentry/Platforms/iOS/Extensions/SentryEventExtensions.cs new file mode 100644 index 0000000000..f9da7d5a3d --- /dev/null +++ b/src/Sentry/Platforms/iOS/Extensions/SentryEventExtensions.cs @@ -0,0 +1,45 @@ +// using Sentry.Extensibility; +// using Sentry.Protocol.Envelopes; +// +// namespace Sentry.iOS.Extensions; +// +// internal static class SentryEventExtensions +// { +// /* +// * These methods map between a SentryEvent and it's Cocoa counterpart by serializing as JSON into memory on one side, +// * then deserializing back to an object on the other side. It is not expected to be performant, as this code is only +// * used when a BeforeSend option is set, and then only when an event is captured by the Cocoa SDK (which should be +// * relatively rare). +// * +// * This approach avoids having to write to/from methods for the entire object graph. However, it's also important to +// * recognize that there's not necessarily a one-to-one mapping available on all objects (even through serialization) +// * between the two SDKs, so some optional details may be lost when roundtripping. That's generally OK, as this is +// * still better than nothing. If a specific part of the object graph becomes important to roundtrip, we can consider +// * updating the objects on either side. +// */ +// +// public static SentryEvent ToSentryEvent(this SentryCocoa.SentryEvent sentryEvent, SentryCocoa.SentryOptions cocoaOptions) +// { +// using var stream = sentryEvent.ToJsonStream()!; +// //stream.Seek(0, SeekOrigin.Begin); ?? +// +// using var json = JsonDocument.Parse(stream); +// var exception = sentryEvent.Error == null ? null : new NSErrorException(sentryEvent.Error); +// return SentryEvent.FromJson(json.RootElement, exception); +// } +// +// public static SentryCocoa.SentryEvent ToCocoaSentryEvent(this SentryEvent sentryEvent, SentryOptions options, SentryCocoa.SentryOptions cocoaOptions) +// { +// var envelope = Envelope.FromEvent(sentryEvent); +// +// using var stream = new MemoryStream(); +// envelope.Serialize(stream, options.DiagnosticLogger); +// stream.Seek(0, SeekOrigin.Begin); +// +// using var data = NSData.FromStream(stream)!; +// var cocoaEnvelope = SentryCocoa.PrivateSentrySDKOnly.EnvelopeWithData(data); +// +// var cocoaEvent = (SentryCocoa.SentryEvent) cocoaEnvelope.Items[0]; +// return cocoaEvent; +// } +// } diff --git a/src/Sentry/Platforms/iOS/SentryOptions.cs b/src/Sentry/Platforms/iOS/SentryOptions.cs index 8ef83568de..49b61c82f8 100644 --- a/src/Sentry/Platforms/iOS/SentryOptions.cs +++ b/src/Sentry/Platforms/iOS/SentryOptions.cs @@ -27,5 +27,16 @@ public class IosOptions /// The default value is false (disabled). /// public bool EnableCocoaSdkTracing { get; set; } + + // /// + // /// Gets or sets a value that indicates if the callback will be invoked for + // /// events that originate from the embedded Cocoa SDK. The default value is false (disabled). + // /// + // /// + // /// This is an experimental feature and is imperfect, as the .NET SDK and the embedded Cocoa SDK don't + // /// implement all of the same features that may be present in the event graph. Some optional elements may + // /// be stripped away during the roundtripping between the two SDKs. Use with caution. + // /// + // public bool EnableCocoaSdkBeforeSend { get; set; } } } diff --git a/src/Sentry/Platforms/iOS/SentrySdk.cs b/src/Sentry/Platforms/iOS/SentrySdk.cs index 4a7556c9a7..55b8529edc 100644 --- a/src/Sentry/Platforms/iOS/SentrySdk.cs +++ b/src/Sentry/Platforms/iOS/SentrySdk.cs @@ -78,6 +78,12 @@ private static void InitSentryCocoaSdk(SentryOptions options) // TODO: Work on below (copied from Android implementation - needs updating) + // if (options.iOS.EnableCocoaSdkBeforeSend && options.BeforeSend is { } beforeSend) + // { + // // Note: Nullable return is allowed but delegate is generated incorrectly + // o.BeforeSend = evt => beforeSend(evt.ToSentryEvent(o))?.ToCocoaSentryEvent(options, o)!; + // } + // // // These options we have behind feature flags // if (options.Android.EnableAndroidSdkTracing) From e3209f717cfaee66fc9566bd1fe165c442f4be8e Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Thu, 11 Aug 2022 16:08:05 -0700 Subject: [PATCH 12/28] In-App Excludes and Includes --- src/Sentry/Platforms/Android/SentryOptions.cs | 21 ++++++------ src/Sentry/Platforms/Android/SentrySdk.cs | 4 +-- src/Sentry/Platforms/iOS/SentryOptions.cs | 33 +++++++++++++++++++ src/Sentry/Platforms/iOS/SentrySdk.cs | 5 +++ 4 files changed, 51 insertions(+), 12 deletions(-) diff --git a/src/Sentry/Platforms/Android/SentryOptions.cs b/src/Sentry/Platforms/Android/SentryOptions.cs index 3f089f3cdc..e07b9a4824 100644 --- a/src/Sentry/Platforms/Android/SentryOptions.cs +++ b/src/Sentry/Platforms/Android/SentryOptions.cs @@ -204,8 +204,8 @@ public class AndroidOptions // ---------- Other ---------- - internal string[]? InAppExclude { get; set; } - internal string[]? InAppInclude { get; set; } + internal List? InAppExcludes { get; private set; } + internal List? InAppIncludes { get; private set; } /// /// Add prefix to exclude from 'InApp' stacktrace list by the Android SDK. @@ -218,10 +218,11 @@ public class AndroidOptions /// /// 'java.util.', 'org.apache.logging.log4j.' /// - public void AddInAppExclude(string prefix) => - InAppExclude = InAppExclude != null - ? InAppExclude.Concat(new[] { prefix }).ToArray() - : new[] { prefix }; + public void AddInAppExclude(string prefix) + { + InAppExcludes ??= new List(); + InAppExcludes.Add(prefix); + } /// /// Add prefix to include as in 'InApp' stacktrace by the Android SDK. @@ -234,10 +235,10 @@ public class AndroidOptions /// /// 'java.util.customcode.', 'io.sentry.samples.' /// - public void AddInAppInclude(string prefix) => - InAppInclude = InAppInclude != null - ? InAppInclude.Concat(new[] { prefix }).ToArray() - : new[] { prefix }; + public void AddInAppInclude(string prefix){ + InAppIncludes ??= new List(); + InAppIncludes.Add(prefix); + } /// /// Gets or sets a value that indicates if tracing features are enabled on the embedded Android SDK. diff --git a/src/Sentry/Platforms/Android/SentrySdk.cs b/src/Sentry/Platforms/Android/SentrySdk.cs index 234afe378d..ea6bfd2767 100644 --- a/src/Sentry/Platforms/Android/SentrySdk.cs +++ b/src/Sentry/Platforms/Android/SentrySdk.cs @@ -157,8 +157,8 @@ private static void InitSentryAndroidSdk(SentryOptions options) o.ReadTimeoutMillis = (int)options.Android.ReadTimeout.TotalMilliseconds; // In-App Excludes and Includes to be passed to the Android SDK - options.Android.InAppExclude?.ToList().ForEach(x => o.AddInAppExclude(x)); - options.Android.InAppInclude?.ToList().ForEach(x => o.AddInAppInclude(x)); + options.Android.InAppExcludes?.ForEach(x => o.AddInAppExclude(x)); + options.Android.InAppIncludes?.ForEach(x => o.AddInAppInclude(x)); // These options are intentionally set and not exposed for modification o.EnableExternalConfiguration = false; diff --git a/src/Sentry/Platforms/iOS/SentryOptions.cs b/src/Sentry/Platforms/iOS/SentryOptions.cs index 49b61c82f8..ec5c8c2401 100644 --- a/src/Sentry/Platforms/iOS/SentryOptions.cs +++ b/src/Sentry/Platforms/iOS/SentryOptions.cs @@ -38,5 +38,38 @@ public class IosOptions // /// be stripped away during the roundtripping between the two SDKs. Use with caution. // /// // public bool EnableCocoaSdkBeforeSend { get; set; } + + // ---------- Other ---------- + + internal List? InAppExcludes { get; private set; } + internal List? InAppIncludes { get; private set; } + + /// + /// Add prefix to exclude from 'InApp' stacktrace list by the Cocoa SDK. + /// Note that this uses iOS module names, not .NET namespaces. + /// + /// The string used to filter the stacktrace to be excluded from InApp. + /// + /// https://docs.sentry.io/platforms/apple/guides/ios/configuration/options/#in-app-exclude + /// + public void AddInAppExclude(string prefix) + { + InAppExcludes ??= new List(); + InAppExcludes.Add(prefix); + } + + /// + /// Add prefix to include as in 'InApp' stacktrace by the Cocoa SDK. + /// Note that this uses Cocoa package names, not .NET namespaces. + /// + /// The string used to filter the stacktrace to be included in InApp. + /// + /// See https://docs.sentry.io/platforms/apple/guides/ios/configuration/options/#in-app-include + /// + public void AddInAppInclude(string prefix) + { + InAppIncludes ??= new List(); + InAppIncludes.Add(prefix); + } } } diff --git a/src/Sentry/Platforms/iOS/SentrySdk.cs b/src/Sentry/Platforms/iOS/SentrySdk.cs index 55b8529edc..f0af22ed82 100644 --- a/src/Sentry/Platforms/iOS/SentrySdk.cs +++ b/src/Sentry/Platforms/iOS/SentrySdk.cs @@ -135,6 +135,11 @@ private static void InitSentryCocoaSdk(SentryOptions options) // o.EnableExternalConfiguration = false; // o.EnableDeduplication = false; // o.AttachServerName = false; + + // In-App Excludes and Includes to be passed to the Cocoa SDK + options.iOS.InAppExcludes?.ForEach(x => o.AddInAppExclude(x)); + options.iOS.InAppIncludes?.ForEach(x => o.AddInAppInclude(x)); + // // // These options are intentionally not expose or modified // //o.MaxRequestBodySize // N/A for Android apps From 448adb48e3ceb553cd870c1e80df8807e68b2126 Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Thu, 11 Aug 2022 16:27:23 -0700 Subject: [PATCH 13/28] Remaining options --- src/Sentry/Platforms/iOS/SentryOptions.cs | 22 ++++- src/Sentry/Platforms/iOS/SentrySdk.cs | 98 +++++++---------------- 2 files changed, 50 insertions(+), 70 deletions(-) diff --git a/src/Sentry/Platforms/iOS/SentryOptions.cs b/src/Sentry/Platforms/iOS/SentryOptions.cs index ec5c8c2401..12dc4b6761 100644 --- a/src/Sentry/Platforms/iOS/SentryOptions.cs +++ b/src/Sentry/Platforms/iOS/SentryOptions.cs @@ -18,7 +18,25 @@ public class IosOptions // ---------- From Cocoa's SentryOptions.h ---------- - // TODO + public bool AttachScreenshot { get; set; } + public TimeSpan AppHangTimeoutInterval { get; set; } + public TimeSpan IdleTimeout { get; set; } + public string? Distribution { get; set; } + public bool EnableAppHangTracking { get; set; } + public bool EnableAutoBreadcrumbTracking { get; set; } + public bool EnableAutoPerformanceTracking { get; set; } + public bool EnableAutoSessionTracking { get; set; } + public bool EnableCoreDataTracking { get; set; } + public bool EnableFileIOTracking { get; set; } + public bool EnableNetworkBreadcrumbs { get; set; } + public bool EnableNetworkTracking { get; set; } + public bool EnableOutOfMemoryTracking { get; set; } + public bool EnableProfiling { get; set; } + public bool EnableSwizzling { get; set; } + public bool EnableUIViewControllerTracking { get; set; } + public bool EnableUserInteractionTracing { get; set; } + public bool StitchAsyncCode { get; set; } + // ---------- Other ---------- @@ -39,8 +57,6 @@ public class IosOptions // /// // public bool EnableCocoaSdkBeforeSend { get; set; } - // ---------- Other ---------- - internal List? InAppExcludes { get; private set; } internal List? InAppIncludes { get; private set; } diff --git a/src/Sentry/Platforms/iOS/SentrySdk.cs b/src/Sentry/Platforms/iOS/SentrySdk.cs index f0af22ed82..bd3a9e4330 100644 --- a/src/Sentry/Platforms/iOS/SentrySdk.cs +++ b/src/Sentry/Platforms/iOS/SentrySdk.cs @@ -28,10 +28,6 @@ private static void InitSentryCocoaSdk(SentryOptions options) // Capture the Cocoa options reference on the outer scope cocoaOptions = o; - // TODO: Equivalent of Android options? - // o.DistinctId - // o.EnableScopeSync - // These options are copied over from our SentryOptions o.AttachStacktrace = options.AttachStacktrace; o.Debug = options.Debug; @@ -39,17 +35,19 @@ private static void InitSentryCocoaSdk(SentryOptions options) o.Dsn = options.Dsn; o.EnableAutoSessionTracking = options.AutoSessionTracking; o.Environment = options.Environment; - //o.FlushTimeoutMillis = (long)options.InitCacheFlushTimeout.TotalMilliseconds; o.MaxAttachmentSize = (nuint) options.MaxAttachmentSize; o.MaxBreadcrumbs = (nuint) options.MaxBreadcrumbs; o.MaxCacheItems = (nuint) options.MaxCacheItems; - // o.MaxQueueSize = options.MaxQueueItems; o.ReleaseName = options.Release; o.SampleRate = options.SampleRate; o.SendClientReports = options.SendClientReports; o.SendDefaultPii = options.SendDefaultPii; o.SessionTrackingIntervalMillis = (nuint) options.AutoSessionTrackingInterval.TotalMilliseconds; - // o.ShutdownTimeoutMillis = (long)options.ShutdownTimeout.TotalMilliseconds; + + // These options are not available in the Sentry Cocoa SDK + // o.? = options.InitCacheFlushTimeout; + // o.? = options.MaxQueueItems; + // o.? = options.ShutdownTimeout; // NOTE: options.CacheDirectoryPath - No option for this in Sentry Cocoa, but caching is still enabled // https://github.com/getsentry/sentry-cocoa/issues/1051 @@ -76,77 +74,43 @@ private static void InitSentryCocoaSdk(SentryOptions options) } } - // TODO: Work on below (copied from Android implementation - needs updating) - + // TODO: Finish SentryEventExtensions to enable this // if (options.iOS.EnableCocoaSdkBeforeSend && options.BeforeSend is { } beforeSend) // { // // Note: Nullable return is allowed but delegate is generated incorrectly // o.BeforeSend = evt => beforeSend(evt.ToSentryEvent(o))?.ToCocoaSentryEvent(options, o)!; // } - // - // // These options we have behind feature flags - // if (options.Android.EnableAndroidSdkTracing) - // { - // o.TracesSampleRate = (JavaDouble?)options.TracesSampleRate; - // - // if (options.TracesSampler is { } tracesSampler) - // { - // o.TracesSampler = new TracesSamplerCallback(tracesSampler); - // } - // } - // - // if (options.Android.EnableAndroidSdkBeforeSend && options.BeforeSend is { } beforeSend) - // { - // o.BeforeSend = new BeforeSendCallback(beforeSend, options, o); - // } - // - // // These options are from SentrycocoaOptions - // o.AttachScreenshot = options.Android.AttachScreenshot; - // o.AnrEnabled = options.Android.AnrEnabled; - // o.AnrReportInDebug = options.Android.AnrReportInDebug; - // o.AnrTimeoutIntervalMillis = (long)options.Android.AnrTimeoutInterval.TotalMilliseconds; - // o.EnableActivityLifecycleBreadcrumbs = options.Android.EnableActivityLifecycleBreadcrumbs; - // o.EnableAutoActivityLifecycleTracing = options.Android.EnableAutoActivityLifecycleTracing; - // o.EnableActivityLifecycleTracingAutoFinish = options.Android.EnableActivityLifecycleTracingAutoFinish; - // o.EnableAppComponentBreadcrumbs = options.Android.EnableAppComponentBreadcrumbs; - // o.EnableAppLifecycleBreadcrumbs = options.Android.EnableAppLifecycleBreadcrumbs; - // o.EnableSystemEventBreadcrumbs = options.Android.EnableSystemEventBreadcrumbs; - // o.EnableUserInteractionBreadcrumbs = options.Android.EnableUserInteractionBreadcrumbs; - // o.EnableUserInteractionTracing = options.Android.EnableUserInteractionTracing; - // o.ProfilingTracesIntervalMillis = (int)options.Android.ProfilingTracesInterval.TotalMilliseconds; - // - // // These options are in Java.SentryOptions but not ours - // o.AttachThreads = options.Android.AttachThreads; - // o.ConnectionTimeoutMillis = (int)options.Android.ConnectionTimeout.TotalMilliseconds; - // o.Dist = options.Android.Distribution; - // o.EnableNdk = options.Android.EnableNdk; - // o.EnableShutdownHook = options.Android.EnableShutdownHook; - // o.EnableUncaughtExceptionHandler = options.Android.EnableUncaughtExceptionHandler; - // o.ProfilingEnabled = options.Android.ProfilingEnabled; - // o.PrintUncaughtStackTrace = options.Android.PrintUncaughtStackTrace; - // o.ReadTimeoutMillis = (int)options.Android.ReadTimeout.TotalMilliseconds; - // - // // In-App Excludes and Includes to be passed to the Android SDK - // options.Android.InAppExclude?.ToList().ForEach(x => o.AddInAppExclude(x)); - // options.Android.InAppInclude?.ToList().ForEach(x => o.AddInAppInclude(x)); - // - // // These options are intentionally set and not exposed for modification - // o.EnableExternalConfiguration = false; - // o.EnableDeduplication = false; - // o.AttachServerName = false; + // These options are from Cocoa's SentryOptions + o.AttachScreenshot = options.iOS.AttachScreenshot; + o.AppHangTimeoutInterval = options.iOS.AppHangTimeoutInterval.TotalSeconds; + o.IdleTimeout = options.iOS.IdleTimeout.TotalSeconds; + o.Dist = options.iOS.Distribution; + o.EnableAppHangTracking = options.iOS.EnableAppHangTracking; + o.EnableAutoBreadcrumbTracking = options.iOS.EnableAutoBreadcrumbTracking; + o.EnableAutoPerformanceTracking = options.iOS.EnableAutoPerformanceTracking; + o.EnableAutoSessionTracking = options.iOS.EnableAutoSessionTracking; + o.EnableCoreDataTracking = options.iOS.EnableCoreDataTracking; + o.EnableFileIOTracking = options.iOS.EnableFileIOTracking; + o.EnableNetworkBreadcrumbs = options.iOS.EnableNetworkBreadcrumbs; + o.EnableNetworkTracking = options.iOS.EnableNetworkTracking; + o.EnableOutOfMemoryTracking = options.iOS.EnableOutOfMemoryTracking; + o.EnableProfiling = options.iOS.EnableProfiling; + o.EnableSwizzling = options.iOS.EnableSwizzling; + o.EnableUIViewControllerTracking = options.iOS.EnableUIViewControllerTracking; + o.EnableUserInteractionTracing = options.iOS.EnableUserInteractionTracing; + o.StitchAsyncCode = options.iOS.StitchAsyncCode; // In-App Excludes and Includes to be passed to the Cocoa SDK options.iOS.InAppExcludes?.ForEach(x => o.AddInAppExclude(x)); options.iOS.InAppIncludes?.ForEach(x => o.AddInAppInclude(x)); - // - // // 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")); + // These options are intentionally not expose or modified + // o.Enabled; + // o.SdkInfo + + // // 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")); }); From a813440e9a1677a07fa9b28dcd738d7fcf4a3a94 Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Thu, 11 Aug 2022 16:51:52 -0700 Subject: [PATCH 14/28] A few more --- src/Sentry/Platforms/iOS/SentryOptions.cs | 4 ++++ src/Sentry/Platforms/iOS/SentrySdk.cs | 14 ++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/Sentry/Platforms/iOS/SentryOptions.cs b/src/Sentry/Platforms/iOS/SentryOptions.cs index 12dc4b6761..3ef26e32df 100644 --- a/src/Sentry/Platforms/iOS/SentryOptions.cs +++ b/src/Sentry/Platforms/iOS/SentryOptions.cs @@ -37,6 +37,10 @@ public class IosOptions public bool EnableUserInteractionTracing { get; set; } public bool StitchAsyncCode { get; set; } + // public Action? OnCrashedLastRun { get; set; } + + public NSUrlSessionDelegate? UrlSessionDelegate { get; set; } + // ---------- Other ---------- diff --git a/src/Sentry/Platforms/iOS/SentrySdk.cs b/src/Sentry/Platforms/iOS/SentrySdk.cs index bd3a9e4330..e1e823c633 100644 --- a/src/Sentry/Platforms/iOS/SentrySdk.cs +++ b/src/Sentry/Platforms/iOS/SentrySdk.cs @@ -74,12 +74,18 @@ private static void InitSentryCocoaSdk(SentryOptions options) } } - // TODO: Finish SentryEventExtensions to enable this + // TODO: Finish SentryEventExtensions to enable these + // // if (options.iOS.EnableCocoaSdkBeforeSend && options.BeforeSend is { } beforeSend) // { // // Note: Nullable return is allowed but delegate is generated incorrectly // o.BeforeSend = evt => beforeSend(evt.ToSentryEvent(o))?.ToCocoaSentryEvent(options, o)!; // } + // + // if (options.iOS.OnCrashedLastRun is { } onCrashedLastRun) + // { + // o.OnCrashedLastRun = evt => onCrashedLastRun(evt.ToSentryEvent(o)); + // } // These options are from Cocoa's SentryOptions o.AttachScreenshot = options.iOS.AttachScreenshot; @@ -100,14 +106,18 @@ private static void InitSentryCocoaSdk(SentryOptions options) o.EnableUIViewControllerTracking = options.iOS.EnableUIViewControllerTracking; o.EnableUserInteractionTracing = options.iOS.EnableUserInteractionTracing; o.StitchAsyncCode = options.iOS.StitchAsyncCode; + o.UrlSessionDelegate = options.iOS.UrlSessionDelegate; // In-App Excludes and Includes to be passed to the Cocoa SDK options.iOS.InAppExcludes?.ForEach(x => o.AddInAppExclude(x)); options.iOS.InAppIncludes?.ForEach(x => o.AddInAppInclude(x)); // These options are intentionally not expose or modified - // o.Enabled; + // o.Enabled // o.SdkInfo + // o.Integrations + // o.DefaultIntegrations + // // 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")); From 8c7c424121634eccb65ccc83b80d8a438e3ba817 Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Thu, 11 Aug 2022 17:18:06 -0700 Subject: [PATCH 15/28] Fix sample --- samples/Sentry.Samples.Maui/MainPage.xaml.cs | 25 +++++++++++++------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/samples/Sentry.Samples.Maui/MainPage.xaml.cs b/samples/Sentry.Samples.Maui/MainPage.xaml.cs index 16c76c2de0..c75754f9bd 100644 --- a/samples/Sentry.Samples.Maui/MainPage.xaml.cs +++ b/samples/Sentry.Samples.Maui/MainPage.xaml.cs @@ -8,10 +8,16 @@ public partial class MainPage { private readonly ILogger _logger; - int count = 0; + private int _count = 0; // NOTE: You can only inject an ILogger, not a plain ILogger public MainPage(ILogger logger) + { + _logger = logger; + InitializeComponent(); + } + + protected override void OnAppearing() { #if !ANDROID JavaCrashBtn.IsVisible = false; @@ -20,22 +26,25 @@ public MainPage(ILogger logger) #if !(ANDROID || IOS || MACCATALYST) NativeCrashBtn.IsVisible = false; #endif - _logger = logger; - InitializeComponent(); + base.OnAppearing(); } private void OnCounterClicked(object sender, EventArgs e) { - count++; + _count++; - if (count == 1) - CounterBtn.Text = $"Clicked {count} time"; + if (_count == 1) + { + CounterBtn.Text = $"Clicked {_count} time"; + } else - CounterBtn.Text = $"Clicked {count} times"; + { + CounterBtn.Text = $"Clicked {_count} times"; + } SemanticScreenReader.Announce(CounterBtn.Text); - _logger.LogInformation("The button has been clicked {ClickCount} times", count); + _logger.LogInformation("The button has been clicked {ClickCount} times", _count); } private void OnUnhandledExceptionClicked(object sender, EventArgs e) From 5fee85c2c30ba065057b6571379bb5ac7c149900 Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Thu, 11 Aug 2022 17:18:30 -0700 Subject: [PATCH 16/28] Update comment about crash duplication --- src/Sentry/Platforms/iOS/SentrySdk.cs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/Sentry/Platforms/iOS/SentrySdk.cs b/src/Sentry/Platforms/iOS/SentrySdk.cs index e1e823c633..2425f1a40a 100644 --- a/src/Sentry/Platforms/iOS/SentrySdk.cs +++ b/src/Sentry/Platforms/iOS/SentrySdk.cs @@ -118,10 +118,17 @@ private static void InitSentryCocoaSdk(SentryOptions options) // o.Integrations // o.DefaultIntegrations - - // // 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")); - + // NOTE + // When we have an unhandled managed exception, we send that to Sentry twice - once managed and once native. + // The managed exception is what a .NET developer would expect, and it is sent by the Sentry.NET SDK + // But we also get a native SIGABRT since it crashed the application, which is sent by the Sentry Cocoa SDK. + // This is partially due to our setting ObjCRuntime.MarshalManagedExceptionMode.UnwindNativeCode above. + // + // A similar thing happens on Android, which we exclude with: + // o.AddIgnoredExceptionForType(JavaClass.ForName("android.runtime.JavaProxyThrowable")); + // + // TODO: How should we handle this for iOS? We probably don't want to completely exclude SIGABRT + // }); // Set options for the managed SDK that depend on the Cocoa SDK From 0f72034b79008c269fc9ec443a20eafb391fb8c0 Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Thu, 11 Aug 2022 17:23:29 -0700 Subject: [PATCH 17/28] Update sample app --- samples/Sentry.Samples.Maui/MainPage.xaml.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/Sentry.Samples.Maui/MainPage.xaml.cs b/samples/Sentry.Samples.Maui/MainPage.xaml.cs index c75754f9bd..2a1b0f0bf1 100644 --- a/samples/Sentry.Samples.Maui/MainPage.xaml.cs +++ b/samples/Sentry.Samples.Maui/MainPage.xaml.cs @@ -56,7 +56,7 @@ private void OnCapturedExceptionClicked(object sender, EventArgs e) { try { - SentrySdk.CauseCrash(CrashType.Managed); + throw new ApplicationException("This exception was thrown and captured manually, without crashing the app."); } catch (Exception ex) { From 1f188cdf131450d0de7bcfa537456e1523375fda Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Thu, 11 Aug 2022 17:49:59 -0700 Subject: [PATCH 18/28] Filter out SIGABRT --- src/Sentry/Platforms/iOS/SentrySdk.cs | 28 +++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/Sentry/Platforms/iOS/SentrySdk.cs b/src/Sentry/Platforms/iOS/SentrySdk.cs index 2425f1a40a..9450a2466b 100644 --- a/src/Sentry/Platforms/iOS/SentrySdk.cs +++ b/src/Sentry/Platforms/iOS/SentrySdk.cs @@ -118,17 +118,30 @@ private static void InitSentryCocoaSdk(SentryOptions options) // o.Integrations // o.DefaultIntegrations - // NOTE // When we have an unhandled managed exception, we send that to Sentry twice - once managed and once native. // The managed exception is what a .NET developer would expect, and it is sent by the Sentry.NET SDK // But we also get a native SIGABRT since it crashed the application, which is sent by the Sentry Cocoa SDK. // This is partially due to our setting ObjCRuntime.MarshalManagedExceptionMode.UnwindNativeCode above. - // - // A similar thing happens on Android, which we exclude with: - // o.AddIgnoredExceptionForType(JavaClass.ForName("android.runtime.JavaProxyThrowable")); - // - // TODO: How should we handle this for iOS? We probably don't want to completely exclude SIGABRT - // + // Thankfully, we can see Xamarin's unhandled exception handler on the stack trace, so we can filter them out. + o.BeforeSend = evt => + { + // There should only be one exception on the event in this case + if (evt.Exceptions?.Length == 1) + { + // It will match the following characteristics + var ex = evt.Exceptions[0]; + if (ex.Type == "SIGABRT" && ex.Value == "Signal 6, Code 0" && + ex.Stacktrace?.Frames.Any(f => f.Function == "xamarin_unhandled_exception_handler") is true) + { + // Don't sent it + return null!; + } + } + + // Other event, send as normal + return evt; + }; + }); // Set options for the managed SDK that depend on the Cocoa SDK @@ -138,6 +151,5 @@ private static void InitSentryCocoaSdk(SentryOptions options) options.ScopeObserver = new IosScopeObserver(options); // TODO: Pause/Resume - } } From 63b6d5a8225e03c147ceb45d92ebcb286688fd99 Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Fri, 12 Aug 2022 08:54:32 -0700 Subject: [PATCH 19/28] comments --- src/Sentry/Platforms/iOS/SentrySdk.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Sentry/Platforms/iOS/SentrySdk.cs b/src/Sentry/Platforms/iOS/SentrySdk.cs index 9450a2466b..ca1e5f354a 100644 --- a/src/Sentry/Platforms/iOS/SentrySdk.cs +++ b/src/Sentry/Platforms/iOS/SentrySdk.cs @@ -123,6 +123,8 @@ private static void InitSentryCocoaSdk(SentryOptions options) // But we also get a native SIGABRT since it crashed the application, which is sent by the Sentry Cocoa SDK. // This is partially due to our setting ObjCRuntime.MarshalManagedExceptionMode.UnwindNativeCode above. // Thankfully, we can see Xamarin's unhandled exception handler on the stack trace, so we can filter them out. + // Here is the function that calls abort(), which we will use as a filter: + // https://github.com/xamarin/xamarin-macios/blob/c55fbdfef95028ba03d0f7a35aebca03bd76f852/runtime/runtime.m#L1114-L1122 o.BeforeSend = evt => { // There should only be one exception on the event in this case From d3c5316eb6dcf2b7bf02928e4a81d52520b5840c Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Fri, 12 Aug 2022 10:55:34 -0700 Subject: [PATCH 20/28] Remove duplicate property --- src/Sentry/Platforms/iOS/SentryOptions.cs | 1 - src/Sentry/Platforms/iOS/SentrySdk.cs | 1 - 2 files changed, 2 deletions(-) diff --git a/src/Sentry/Platforms/iOS/SentryOptions.cs b/src/Sentry/Platforms/iOS/SentryOptions.cs index 3ef26e32df..544e2fbdc2 100644 --- a/src/Sentry/Platforms/iOS/SentryOptions.cs +++ b/src/Sentry/Platforms/iOS/SentryOptions.cs @@ -25,7 +25,6 @@ public class IosOptions public bool EnableAppHangTracking { get; set; } public bool EnableAutoBreadcrumbTracking { get; set; } public bool EnableAutoPerformanceTracking { get; set; } - public bool EnableAutoSessionTracking { get; set; } public bool EnableCoreDataTracking { get; set; } public bool EnableFileIOTracking { get; set; } public bool EnableNetworkBreadcrumbs { get; set; } diff --git a/src/Sentry/Platforms/iOS/SentrySdk.cs b/src/Sentry/Platforms/iOS/SentrySdk.cs index ca1e5f354a..305cc20b40 100644 --- a/src/Sentry/Platforms/iOS/SentrySdk.cs +++ b/src/Sentry/Platforms/iOS/SentrySdk.cs @@ -95,7 +95,6 @@ private static void InitSentryCocoaSdk(SentryOptions options) o.EnableAppHangTracking = options.iOS.EnableAppHangTracking; o.EnableAutoBreadcrumbTracking = options.iOS.EnableAutoBreadcrumbTracking; o.EnableAutoPerformanceTracking = options.iOS.EnableAutoPerformanceTracking; - o.EnableAutoSessionTracking = options.iOS.EnableAutoSessionTracking; o.EnableCoreDataTracking = options.iOS.EnableCoreDataTracking; o.EnableFileIOTracking = options.iOS.EnableFileIOTracking; o.EnableNetworkBreadcrumbs = options.iOS.EnableNetworkBreadcrumbs; From a9a0331254d8e2279e1ecd09cd481a0b02c043d4 Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Fri, 12 Aug 2022 11:42:10 -0700 Subject: [PATCH 21/28] defaults, xmldocs, and comments --- src/Sentry/Platforms/Android/SentryOptions.cs | 26 +-- src/Sentry/Platforms/iOS/SentryOptions.cs | 220 +++++++++++++++--- src/Sentry/Platforms/iOS/SentrySdk.cs | 1 + 3 files changed, 208 insertions(+), 39 deletions(-) diff --git a/src/Sentry/Platforms/Android/SentryOptions.cs b/src/Sentry/Platforms/Android/SentryOptions.cs index e07b9a4824..3742977dd2 100644 --- a/src/Sentry/Platforms/Android/SentryOptions.cs +++ b/src/Sentry/Platforms/Android/SentryOptions.cs @@ -33,7 +33,7 @@ public class AndroidOptions /// /// See https://docs.sentry.io/platforms/android/configuration/app-not-respond/ /// - public bool AnrReportInDebug { get; set; } + public bool AnrReportInDebug { get; set; } = false; /// /// Gets or sets the ANR (Application Not Responding) timeout interval. @@ -53,7 +53,7 @@ public class AndroidOptions /// This feature is provided by the Sentry Android SDK and thus only works for Java-based errors. /// See https://docs.sentry.io/platforms/android/enriching-events/screenshots/ /// - public bool AttachScreenshot { get; set; } + public bool AttachScreenshot { get; set; } = false; /// /// Gets or sets a value that indicates if automatic breadcrumbs for Activity lifecycle events are @@ -129,7 +129,7 @@ public class AndroidOptions /// /// See https://docs.sentry.io/platforms/android/performance/instrumentation/automatic-instrumentation/#user-interaction-instrumentation /// - public bool EnableUserInteractionTracing { get; set; } + public bool EnableUserInteractionTracing { get; set; } = false; /// /// Gets or sets the interval for profiling traces, when enabled with . @@ -144,7 +144,7 @@ public class AndroidOptions /// Gets or sets a value that indicates if all the threads are automatically attached to all logged events. /// The default value is false (disabled). /// - public bool AttachThreads { get; set; } + public bool AttachThreads { get; set; } = false; /// /// Gets or sets the connection timeout on the HTTP connection used by Java when sending data to Sentry. @@ -152,14 +152,14 @@ public class AndroidOptions /// public TimeSpan ConnectionTimeout { get; set; } = TimeSpan.FromSeconds(5); - // TODO: Should we have this Distribution property on SentryOptions (with Release and Environment)? /// - /// Gets or sets the distribution. + /// Gets or sets the distribution of the application. /// /// - /// See https://docs.sentry.io/platforms/java/guides/spring/configuration/#distribution + /// See "dist" in https://develop.sentry.dev/sdk/event-payloads/#optional-attributes /// - public string? Distribution { get; set; } + // TODO: Should we have this property on the main SentryOptions (with Release and Environment)? + public string? Distribution { get; set; } = null; /// /// Gets or sets a value that indicates if the NDK (Android Native Development Kit) is enabled. @@ -186,14 +186,14 @@ public class AndroidOptions /// Gets or sets a value that indicates if uncaught Java errors will have their stack traces /// printed to the standard error stream. The default value is false (disabled). /// - public bool PrintUncaughtStackTrace { get; set; } + public bool PrintUncaughtStackTrace { get; set; } = false; /// /// Gets or sets if profiling is enabled for transactions. /// The default value is false (disabled). /// See also . /// - public bool ProfilingEnabled { get; set; } + public bool ProfilingEnabled { get; set; } = false; /// /// Gets or sets the read timeout on the HTTP connection used by Java when sending data to Sentry. @@ -244,7 +244,7 @@ public void AddInAppExclude(string prefix) /// Gets or sets a value that indicates if tracing features are enabled on the embedded Android SDK. /// The default value is false (disabled). /// - public bool EnableAndroidSdkTracing { get; set; } + public bool EnableAndroidSdkTracing { get; set; } = false; /// /// Gets or sets a value that indicates if the callback will be invoked for @@ -253,8 +253,8 @@ public void AddInAppExclude(string prefix) /// /// This is an experimental feature and is imperfect, as the .NET SDK and the embedded Android SDK don't /// implement all of the same features that may be present in the event graph. Some optional elements may - /// be stripped away during the roundtripping between the two SDKs. Use with caution. + /// be stripped away during the round-tripping between the two SDKs. Use with caution. /// - public bool EnableAndroidSdkBeforeSend { get; set; } + public bool EnableAndroidSdkBeforeSend { get; set; } = false; } } diff --git a/src/Sentry/Platforms/iOS/SentryOptions.cs b/src/Sentry/Platforms/iOS/SentryOptions.cs index 544e2fbdc2..52d4ec2148 100644 --- a/src/Sentry/Platforms/iOS/SentryOptions.cs +++ b/src/Sentry/Platforms/iOS/SentryOptions.cs @@ -1,3 +1,5 @@ +using ObjCRuntime; + // ReSharper disable once CheckNamespace namespace Sentry; @@ -18,27 +20,193 @@ public class IosOptions // ---------- From Cocoa's SentryOptions.h ---------- - public bool AttachScreenshot { get; set; } - public TimeSpan AppHangTimeoutInterval { get; set; } - public TimeSpan IdleTimeout { get; set; } - public string? Distribution { get; set; } - public bool EnableAppHangTracking { get; set; } - public bool EnableAutoBreadcrumbTracking { get; set; } - public bool EnableAutoPerformanceTracking { get; set; } - public bool EnableCoreDataTracking { get; set; } - public bool EnableFileIOTracking { get; set; } - public bool EnableNetworkBreadcrumbs { get; set; } - public bool EnableNetworkTracking { get; set; } - public bool EnableOutOfMemoryTracking { get; set; } - public bool EnableProfiling { get; set; } - public bool EnableSwizzling { get; set; } - public bool EnableUIViewControllerTracking { get; set; } - public bool EnableUserInteractionTracing { get; set; } - public bool StitchAsyncCode { get; set; } - - // public Action? OnCrashedLastRun { get; set; } - - public NSUrlSessionDelegate? UrlSessionDelegate { get; set; } + /// + /// Automatically attaches a screenshot when capturing an error or exception. + /// The default value is false (disabled). + /// + /// + /// See https://docs.sentry.io/platforms/apple/guides/ios/configuration/options/#attach-screenshot + /// + public bool AttachScreenshot { get; set; } = false; + + /// + /// The minimum amount of time an app should be unresponsive to be classified as an App Hanging. + /// The actual amount may be a little longer. Avoid using values lower than 100ms, which may cause a lot + /// of app hangs events being transmitted. + /// The default value is 2 seconds. + /// Requires setting to true. + /// + /// + /// See https://docs.sentry.io/platforms/apple/configuration/app-hangs/ + /// + public TimeSpan AppHangTimeoutInterval { get; set; } = TimeSpan.FromSeconds(2); + + /// + /// How long an idle transaction waits for new children after all its child spans finished. + /// Only UI event transactions are idle transactions. + /// The default value is 3 seconds. + /// + /// + /// See https://docs.sentry.io/platforms/apple/performance/instrumentation/automatic-instrumentation/#user-interaction-instrumentation + /// + public TimeSpan IdleTimeout { get; set; } = TimeSpan.FromSeconds(3); + + /// + /// Gets or sets the distribution of the application. + /// + /// + /// See "dist" in https://develop.sentry.dev/sdk/event-payloads/#optional-attributes + /// + // TODO: Should we have this property on the main SentryOptions (with Release and Environment)? + public string? Distribution { get; set; } = null; + + /// + /// When enabled, the SDK tracks when the application stops responding for a specific amount of + /// time defined by the option. + /// The default value is false (disabled). + /// + /// + /// See https://docs.sentry.io/platforms/apple/configuration/app-hangs/ + /// + public bool EnableAppHangTracking { get; set; } = false; + + /// + /// When enabled, the SDK adds breadcrumbs for various system events. + /// The default value is true (enabled). + /// + /// + /// See https://docs.sentry.io/platforms/apple/enriching-events/breadcrumbs/#automatic-breadcrumbs + /// + public bool EnableAutoBreadcrumbTracking { get; set; } = true; + + /// + /// When enabled, the SDK tracks performance for subclasses and HTTP requests + /// automatically. It also measures the app start and slow and frozen frames. + /// The default value is true (enabled). + /// + /// + /// Performance Monitoring must be enabled for this option to take effect. + /// See: https://docs.sentry.io/platforms/apple/performance/ + /// And: https://docs.sentry.io/platforms/apple/performance/instrumentation/automatic-instrumentation/#opt-out + /// + public bool EnableAutoPerformanceTracking { get; set; } = true; + + /// + /// This feature is experimental. + /// When enabled, the SDK tracks the performance of Core Data operations. + /// It requires enabling performance monitoring. + /// The default value is false (disabled). + /// + /// + /// Performance Monitoring must be enabled for this option to take effect. + /// See https://docs.sentry.io/platforms/apple/performance/instrumentation/automatic-instrumentation/#core-data-instrumentation + /// + public bool EnableCoreDataTracking { get; set; } = false; + + /// + /// This feature is experimental. + /// When enabled, the SDK tracks performance for file IO reads and writes with + /// if auto performance tracking and are enabled. + /// The default value is false (disabled). + /// + /// + /// See https://docs.sentry.io/platforms/apple/performance/instrumentation/automatic-instrumentation/#file-io-instrumentation + /// + public bool EnableFileIOTracking { get; set; } = false; + + /// + /// When enabled, the SDK adds breadcrumbs for each network request + /// if auto performance tracking and are enabled. + /// The default value is true (enabled). + /// + public bool EnableNetworkBreadcrumbs { get; set; } = true; + + /// + /// When enabled, the SDK adds breadcrumbs for HTTP requests and tracks performance for HTTP requests + /// if auto performance tracking and are enabled. + /// The default value is true (enabled). + /// + /// + /// https://docs.sentry.io/platforms/apple/performance/instrumentation/automatic-instrumentation/#http-instrumentation + /// + public bool EnableNetworkTracking { get; set; } = true; + + /// + /// Whether to enable out of memory tracking or not. + /// The default value is true (enabled). + /// + /// + /// https://docs.sentry.io/platforms/apple/configuration/out-of-memory/ + /// + public bool EnableOutOfMemoryTracking { get; set; } = true; + + /// + /// Whether the SDK should use swizzling or not. + /// The default value is true (enabled). + /// + /// + /// When turned off the following features are disabled: breadcrumbs for touch events and + /// navigation with , automatic instrumentation for , + /// automatic instrumentation for HTTP requests, automatic instrumentation for file IO with , + /// and automatically added sentry-trace header to HTTP requests for distributed tracing. + /// See https://docs.sentry.io/platforms/apple/configuration/swizzling/ + /// + public bool EnableSwizzling { get; set; } = true; + + /// + /// When enabled, the SDK tracks performance for subclasses. + /// The default value is true (enabled). + /// + /// + /// See https://docs.sentry.io/platforms/apple/performance/instrumentation/automatic-instrumentation/#uiviewcontroller-instrumentation + /// + public bool EnableUIViewControllerTracking { get; set; } = true; + + /// + /// This feature is experimental. + /// When enabled, the SDK creates transactions for UI events like buttons clicks, switch toggles, + /// and other UI elements that uses . + /// The default value is false (disabled). + /// + /// + /// See https://docs.sentry.io/platforms/apple/performance/instrumentation/automatic-instrumentation/#user-interaction-instrumentation + /// + public bool EnableUserInteractionTracing { get; set; } = false; + + /// + /// This feature is experimental. + /// Turning this feature on can have an impact on the grouping of your issues. + /// When enabled, the Cocoa SDK stitches stack traces of asynchronous code together. + /// This feature affects stack traces from native crashes only. + /// The default value is false (disabled). + /// + /// + /// Official docs TBD. + /// See https://github.com/getsentry/sentry-docs/issues/5215 + /// + public bool StitchAsyncCode { get; set; } = false; + + // /// + // /// This gets called shortly after the initialization of the SDK when the last program execution + // /// terminated with a crash. It is not guaranteed that this is called on the main thread. + // /// + // /// + // /// This callback is only executed once during the entire run of the program to avoid + // /// multiple callbacks if there are multiple crash events to send. This can happen when the program + // /// terminates with a crash before the SDK can send the crash event. + // /// You can use if you prefer a callback for every event. + // /// See also https://docs.sentry.io/platforms/apple/enriching-events/user-feedback/ + // /// + // public Action? OnCrashedLastRun { get; set; } = null; + + /// + /// When provided, this will be set as delegate on the used for network + /// data-transfer tasks performed by the native Sentry Cocoa SDK. + /// + /// + /// See https://github.com/getsentry/sentry-cocoa/issues/1168 + /// + public NSUrlSessionDelegate? UrlSessionDelegate { get; set; } = null; // ---------- Other ---------- @@ -47,7 +215,7 @@ public class IosOptions /// Gets or sets a value that indicates if tracing features are enabled on the embedded Cocoa SDK. /// The default value is false (disabled). /// - public bool EnableCocoaSdkTracing { get; set; } + public bool EnableCocoaSdkTracing { get; set; } = false; // /// // /// Gets or sets a value that indicates if the callback will be invoked for @@ -56,7 +224,7 @@ public class IosOptions // /// // /// This is an experimental feature and is imperfect, as the .NET SDK and the embedded Cocoa SDK don't // /// implement all of the same features that may be present in the event graph. Some optional elements may - // /// be stripped away during the roundtripping between the two SDKs. Use with caution. + // /// be stripped away during the round-tripping between the two SDKs. Use with caution. // /// // public bool EnableCocoaSdkBeforeSend { get; set; } @@ -69,7 +237,7 @@ public class IosOptions /// /// The string used to filter the stacktrace to be excluded from InApp. /// - /// https://docs.sentry.io/platforms/apple/guides/ios/configuration/options/#in-app-exclude + /// https://docs.sentry.io/platforms/apple/configuration/options/#in-app-exclude /// public void AddInAppExclude(string prefix) { @@ -79,11 +247,11 @@ public void AddInAppExclude(string prefix) /// /// Add prefix to include as in 'InApp' stacktrace by the Cocoa SDK. - /// Note that this uses Cocoa package names, not .NET namespaces. + /// Note that this uses iOS package names, not .NET namespaces. /// /// The string used to filter the stacktrace to be included in InApp. /// - /// See https://docs.sentry.io/platforms/apple/guides/ios/configuration/options/#in-app-include + /// See https://docs.sentry.io/platforms/apple/configuration/options/#in-app-include /// public void AddInAppInclude(string prefix) { diff --git a/src/Sentry/Platforms/iOS/SentrySdk.cs b/src/Sentry/Platforms/iOS/SentrySdk.cs index 305cc20b40..72c381e213 100644 --- a/src/Sentry/Platforms/iOS/SentrySdk.cs +++ b/src/Sentry/Platforms/iOS/SentrySdk.cs @@ -116,6 +116,7 @@ private static void InitSentryCocoaSdk(SentryOptions options) // o.SdkInfo // o.Integrations // o.DefaultIntegrations + // o.EnableProfiling (deprecated) // When we have an unhandled managed exception, we send that to Sentry twice - once managed and once native. // The managed exception is what a .NET developer would expect, and it is sent by the Sentry.NET SDK From 38fba0c5bce75d642d0bd7216c2a55938e37b37e Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Fri, 12 Aug 2022 11:43:12 -0700 Subject: [PATCH 22/28] remove deprecated property --- src/Sentry/Platforms/iOS/SentrySdk.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Sentry/Platforms/iOS/SentrySdk.cs b/src/Sentry/Platforms/iOS/SentrySdk.cs index 72c381e213..07116f3ec5 100644 --- a/src/Sentry/Platforms/iOS/SentrySdk.cs +++ b/src/Sentry/Platforms/iOS/SentrySdk.cs @@ -100,7 +100,6 @@ private static void InitSentryCocoaSdk(SentryOptions options) o.EnableNetworkBreadcrumbs = options.iOS.EnableNetworkBreadcrumbs; o.EnableNetworkTracking = options.iOS.EnableNetworkTracking; o.EnableOutOfMemoryTracking = options.iOS.EnableOutOfMemoryTracking; - o.EnableProfiling = options.iOS.EnableProfiling; o.EnableSwizzling = options.iOS.EnableSwizzling; o.EnableUIViewControllerTracking = options.iOS.EnableUIViewControllerTracking; o.EnableUserInteractionTracing = options.iOS.EnableUserInteractionTracing; From 1afe13efce2e4d48e936f53395484ce8dd7845dc Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Fri, 12 Aug 2022 11:45:40 -0700 Subject: [PATCH 23/28] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 14746c2b79..15146e74cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ - Enable Scope Sync for iOS ([#1834](https://github.com/getsentry/sentry-dotnet/pull/1834)) - Add API for deliberately crashing an app ([#1842](https://github.com/getsentry/sentry-dotnet/pull/1842)) - Add Mac Catalyst target ([#1848](https://github.com/getsentry/sentry-dotnet/pull/1848)) +- Add and configure options for the iOS SDK ([#1849](https://github.com/getsentry/sentry-dotnet/pull/1849)) ### Fixes From ad1342d2772c38c46a7b443d035e676ce11de7b2 Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Fri, 12 Aug 2022 12:36:40 -0700 Subject: [PATCH 24/28] Use consistent casing --- src/Sentry/Platforms/iOS/Bindings/ApiDefinitions.cs | 8 ++++---- src/Sentry/Platforms/iOS/IosScopeObserver.cs | 12 ++++++------ src/Sentry/Platforms/iOS/SentrySdk.cs | 4 ++-- src/Sentry/SentrySdk.cs | 2 +- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/Sentry/Platforms/iOS/Bindings/ApiDefinitions.cs b/src/Sentry/Platforms/iOS/Bindings/ApiDefinitions.cs index fff877a44f..0cc9b30e3a 100644 --- a/src/Sentry/Platforms/iOS/Bindings/ApiDefinitions.cs +++ b/src/Sentry/Platforms/iOS/Bindings/ApiDefinitions.cs @@ -66,7 +66,7 @@ interface Constants // @interface PrivateSentrySDKOnly : NSObject [BaseType (typeof(NSObject))] [Internal] -interface PrivateSentrySDKOnly +interface PrivateSentrySdkOnly { // +(void)storeEnvelope:(SentryEnvelope * _Nonnull)envelope; [Static] @@ -117,12 +117,12 @@ interface PrivateSentrySDKOnly // @property (assign, nonatomic, class) BOOL appStartMeasurementHybridSDKMode; [Static] [Export ("appStartMeasurementHybridSDKMode")] - bool AppStartMeasurementHybridSDKMode { get; set; } + bool AppStartMeasurementHybridSdkMode { get; set; } // @property (assign, nonatomic, class) BOOL framesTrackingMeasurementHybridSDKMode; [Static] [Export ("framesTrackingMeasurementHybridSDKMode")] - bool FramesTrackingMeasurementHybridSDKMode { get; set; } + bool FramesTrackingMeasurementHybridSdkMode { get; set; } // @property (readonly, assign, nonatomic, class) BOOL isFramesTrackingRunning; [Static] @@ -1392,7 +1392,7 @@ interface SentryNSError : SentrySerializable [BaseType (typeof(NSObject))] [DisableDefaultCtor] [Internal] -interface SentrySDK +interface SentrySdk { // @property (readonly, nonatomic, class) id _Nullable span; [Static] diff --git a/src/Sentry/Platforms/iOS/IosScopeObserver.cs b/src/Sentry/Platforms/iOS/IosScopeObserver.cs index 807b7fc6aa..e9396b7774 100644 --- a/src/Sentry/Platforms/iOS/IosScopeObserver.cs +++ b/src/Sentry/Platforms/iOS/IosScopeObserver.cs @@ -23,7 +23,7 @@ public void AddBreadcrumb(Breadcrumb breadcrumb) try { var b = breadcrumb.ToCocoaBreadcrumb(); - SentryCocoa.SentrySDK.ConfigureScope(scope => scope.AddBreadcrumb(b)); + SentryCocoa.SentrySdk.ConfigureScope(scope => scope.AddBreadcrumb(b)); } finally { @@ -43,7 +43,7 @@ public void SetExtra(string key, object? value) if (value is string s) { - SentryCocoa.SentrySDK.ConfigureScope(scope => + SentryCocoa.SentrySdk.ConfigureScope(scope => scope.SetExtraValue(NSObject.FromObject(s), key)); return; @@ -52,7 +52,7 @@ public void SetExtra(string key, object? value) try { var json = JsonSerializer.Serialize(value); - SentryCocoa.SentrySDK.ConfigureScope(scope => scope.SetExtraValue(NSObject.FromObject(json), key)); + SentryCocoa.SentrySdk.ConfigureScope(scope => scope.SetExtraValue(NSObject.FromObject(json), key)); } catch (Exception ex) { @@ -69,7 +69,7 @@ public void SetTag(string key, string value) { try { - SentryCocoa.SentrySDK.ConfigureScope(scope => scope.SetTagValue(value, key)); + SentryCocoa.SentrySdk.ConfigureScope(scope => scope.SetTagValue(value, key)); } finally { @@ -81,7 +81,7 @@ public void UnsetTag(string key) { try { - SentryCocoa.SentrySDK.ConfigureScope(scope => scope.RemoveTagForKey(key)); + SentryCocoa.SentrySdk.ConfigureScope(scope => scope.RemoveTagForKey(key)); } finally { @@ -94,7 +94,7 @@ public void SetUser(User? user) try { var u = user?.ToCocoaUser(); - SentryCocoa.SentrySDK.ConfigureScope(scope => scope.SetUser(u)); + SentryCocoa.SentrySdk.ConfigureScope(scope => scope.SetUser(u)); } finally { diff --git a/src/Sentry/Platforms/iOS/SentrySdk.cs b/src/Sentry/Platforms/iOS/SentrySdk.cs index 07116f3ec5..6f95493888 100644 --- a/src/Sentry/Platforms/iOS/SentrySdk.cs +++ b/src/Sentry/Platforms/iOS/SentrySdk.cs @@ -23,7 +23,7 @@ private static void InitSentryCocoaSdk(SentryOptions options) // Now initialize the Cocoa SDK SentryCocoa.SentryOptions? cocoaOptions = null; - SentryCocoa.SentrySDK.StartWithConfigureOptions(o => + SentryCocoa.SentrySdk.StartWithConfigureOptions(o => { // Capture the Cocoa options reference on the outer scope cocoaOptions = o; @@ -147,7 +147,7 @@ private static void InitSentryCocoaSdk(SentryOptions options) // Set options for the managed SDK that depend on the Cocoa SDK options.AddEventProcessor(new IosEventProcessor(cocoaOptions!)); - options.CrashedLastRun = () => SentryCocoa.SentrySDK.CrashedLastRun; + options.CrashedLastRun = () => SentryCocoa.SentrySdk.CrashedLastRun; options.EnableScopeSync = true; options.ScopeObserver = new IosScopeObserver(options); diff --git a/src/Sentry/SentrySdk.cs b/src/Sentry/SentrySdk.cs index 502f556b70..5ee9409f26 100644 --- a/src/Sentry/SentrySdk.cs +++ b/src/Sentry/SentrySdk.cs @@ -500,7 +500,7 @@ public static void CauseCrash(CrashType crashType) break; #elif IOS || MACCATALYST case CrashType.Native: - SentryCocoa.SentrySDK.Crash(); + SentryCocoa.SentrySdk.Crash(); break; #endif default: From 208fc699298219beee77d8d6b0f4f94c6a409cdb Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Fri, 12 Aug 2022 12:40:00 -0700 Subject: [PATCH 25/28] Alias SentryCocoaSdk --- src/Sentry/Platforms/iOS/IosScopeObserver.cs | 12 ++++++------ src/Sentry/Platforms/iOS/Sentry.iOS.props | 1 + src/Sentry/Platforms/iOS/SentrySdk.cs | 4 ++-- src/Sentry/SentrySdk.cs | 2 +- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/Sentry/Platforms/iOS/IosScopeObserver.cs b/src/Sentry/Platforms/iOS/IosScopeObserver.cs index e9396b7774..0ac84e1727 100644 --- a/src/Sentry/Platforms/iOS/IosScopeObserver.cs +++ b/src/Sentry/Platforms/iOS/IosScopeObserver.cs @@ -23,7 +23,7 @@ public void AddBreadcrumb(Breadcrumb breadcrumb) try { var b = breadcrumb.ToCocoaBreadcrumb(); - SentryCocoa.SentrySdk.ConfigureScope(scope => scope.AddBreadcrumb(b)); + SentryCocoaSdk.ConfigureScope(scope => scope.AddBreadcrumb(b)); } finally { @@ -43,7 +43,7 @@ public void SetExtra(string key, object? value) if (value is string s) { - SentryCocoa.SentrySdk.ConfigureScope(scope => + SentryCocoaSdk.ConfigureScope(scope => scope.SetExtraValue(NSObject.FromObject(s), key)); return; @@ -52,7 +52,7 @@ public void SetExtra(string key, object? value) try { var json = JsonSerializer.Serialize(value); - SentryCocoa.SentrySdk.ConfigureScope(scope => scope.SetExtraValue(NSObject.FromObject(json), key)); + SentryCocoaSdk.ConfigureScope(scope => scope.SetExtraValue(NSObject.FromObject(json), key)); } catch (Exception ex) { @@ -69,7 +69,7 @@ public void SetTag(string key, string value) { try { - SentryCocoa.SentrySdk.ConfigureScope(scope => scope.SetTagValue(value, key)); + SentryCocoaSdk.ConfigureScope(scope => scope.SetTagValue(value, key)); } finally { @@ -81,7 +81,7 @@ public void UnsetTag(string key) { try { - SentryCocoa.SentrySdk.ConfigureScope(scope => scope.RemoveTagForKey(key)); + SentryCocoaSdk.ConfigureScope(scope => scope.RemoveTagForKey(key)); } finally { @@ -94,7 +94,7 @@ public void SetUser(User? user) try { var u = user?.ToCocoaUser(); - SentryCocoa.SentrySdk.ConfigureScope(scope => scope.SetUser(u)); + SentryCocoaSdk.ConfigureScope(scope => scope.SetUser(u)); } finally { diff --git a/src/Sentry/Platforms/iOS/Sentry.iOS.props b/src/Sentry/Platforms/iOS/Sentry.iOS.props index d4c94d76c8..563a76dc46 100644 --- a/src/Sentry/Platforms/iOS/Sentry.iOS.props +++ b/src/Sentry/Platforms/iOS/Sentry.iOS.props @@ -17,6 +17,7 @@ + diff --git a/src/Sentry/Platforms/iOS/SentrySdk.cs b/src/Sentry/Platforms/iOS/SentrySdk.cs index 6f95493888..df2f1ed897 100644 --- a/src/Sentry/Platforms/iOS/SentrySdk.cs +++ b/src/Sentry/Platforms/iOS/SentrySdk.cs @@ -23,7 +23,7 @@ private static void InitSentryCocoaSdk(SentryOptions options) // Now initialize the Cocoa SDK SentryCocoa.SentryOptions? cocoaOptions = null; - SentryCocoa.SentrySdk.StartWithConfigureOptions(o => + SentryCocoaSdk.StartWithConfigureOptions(o => { // Capture the Cocoa options reference on the outer scope cocoaOptions = o; @@ -147,7 +147,7 @@ private static void InitSentryCocoaSdk(SentryOptions options) // Set options for the managed SDK that depend on the Cocoa SDK options.AddEventProcessor(new IosEventProcessor(cocoaOptions!)); - options.CrashedLastRun = () => SentryCocoa.SentrySdk.CrashedLastRun; + options.CrashedLastRun = () => SentryCocoaSdk.CrashedLastRun; options.EnableScopeSync = true; options.ScopeObserver = new IosScopeObserver(options); diff --git a/src/Sentry/SentrySdk.cs b/src/Sentry/SentrySdk.cs index 5ee9409f26..9021626398 100644 --- a/src/Sentry/SentrySdk.cs +++ b/src/Sentry/SentrySdk.cs @@ -500,7 +500,7 @@ public static void CauseCrash(CrashType crashType) break; #elif IOS || MACCATALYST case CrashType.Native: - SentryCocoa.SentrySdk.Crash(); + SentryCocoaSdk.Crash(); break; #endif default: From d1ac5e744220d5896bf79f4ae674d341f21d9e4d Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Fri, 12 Aug 2022 12:57:38 -0700 Subject: [PATCH 26/28] Improve SentryCocoaOptions initialization --- .../iOS/Extensions/SentryEventExtensions.cs | 4 +- src/Sentry/Platforms/iOS/IosEventProcessor.cs | 4 +- src/Sentry/Platforms/iOS/Sentry.iOS.props | 1 + src/Sentry/Platforms/iOS/SentrySdk.cs | 223 +++++++++--------- 4 files changed, 115 insertions(+), 117 deletions(-) diff --git a/src/Sentry/Platforms/iOS/Extensions/SentryEventExtensions.cs b/src/Sentry/Platforms/iOS/Extensions/SentryEventExtensions.cs index f9da7d5a3d..9e56a5bc6d 100644 --- a/src/Sentry/Platforms/iOS/Extensions/SentryEventExtensions.cs +++ b/src/Sentry/Platforms/iOS/Extensions/SentryEventExtensions.cs @@ -18,7 +18,7 @@ // * updating the objects on either side. // */ // -// public static SentryEvent ToSentryEvent(this SentryCocoa.SentryEvent sentryEvent, SentryCocoa.SentryOptions cocoaOptions) +// public static SentryEvent ToSentryEvent(this SentryCocoa.SentryEvent sentryEvent, SentryCocoaOptions cocoaOptions) // { // using var stream = sentryEvent.ToJsonStream()!; // //stream.Seek(0, SeekOrigin.Begin); ?? @@ -28,7 +28,7 @@ // return SentryEvent.FromJson(json.RootElement, exception); // } // -// public static SentryCocoa.SentryEvent ToCocoaSentryEvent(this SentryEvent sentryEvent, SentryOptions options, SentryCocoa.SentryOptions cocoaOptions) +// public static SentryCocoa.SentryEvent ToCocoaSentryEvent(this SentryEvent sentryEvent, SentryOptions options, SentryCocoaOptions cocoaOptions) // { // var envelope = Envelope.FromEvent(sentryEvent); // diff --git a/src/Sentry/Platforms/iOS/IosEventProcessor.cs b/src/Sentry/Platforms/iOS/IosEventProcessor.cs index 9bda8b8c59..9a202a575b 100644 --- a/src/Sentry/Platforms/iOS/IosEventProcessor.cs +++ b/src/Sentry/Platforms/iOS/IosEventProcessor.cs @@ -4,9 +4,9 @@ namespace Sentry.iOS; internal class IosEventProcessor : ISentryEventProcessor, IDisposable { - private readonly SentryCocoa.SentryOptions _options; + private readonly SentryCocoaOptions _options; - public IosEventProcessor(SentryCocoa.SentryOptions options) + public IosEventProcessor(SentryCocoaOptions options) { _options = options; } diff --git a/src/Sentry/Platforms/iOS/Sentry.iOS.props b/src/Sentry/Platforms/iOS/Sentry.iOS.props index 563a76dc46..1caae6f101 100644 --- a/src/Sentry/Platforms/iOS/Sentry.iOS.props +++ b/src/Sentry/Platforms/iOS/Sentry.iOS.props @@ -17,6 +17,7 @@ + diff --git a/src/Sentry/Platforms/iOS/SentrySdk.cs b/src/Sentry/Platforms/iOS/SentrySdk.cs index df2f1ed897..0b2b22aa8b 100644 --- a/src/Sentry/Platforms/iOS/SentrySdk.cs +++ b/src/Sentry/Platforms/iOS/SentrySdk.cs @@ -21,129 +21,126 @@ private static void InitSentryCocoaSdk(SentryOptions options) // "Best" mode throws platform not supported exception. Use "Fast" mode instead. options.DetectStartupTime = StartupTimeDetectionMode.Fast; - // Now initialize the Cocoa SDK - SentryCocoa.SentryOptions? cocoaOptions = null; - SentryCocoaSdk.StartWithConfigureOptions(o => + // Set options for the Cocoa SDK + var cocoaOptions = new SentryCocoaOptions(); + + // These options are copied over from our SentryOptions + cocoaOptions.AttachStacktrace = options.AttachStacktrace; + cocoaOptions.Debug = options.Debug; + cocoaOptions.DiagnosticLevel = options.DiagnosticLevel.ToCocoaSentryLevel(); + cocoaOptions.Dsn = options.Dsn; + cocoaOptions.EnableAutoSessionTracking = options.AutoSessionTracking; + cocoaOptions.Environment = options.Environment; + cocoaOptions.MaxAttachmentSize = (nuint) options.MaxAttachmentSize; + cocoaOptions.MaxBreadcrumbs = (nuint) options.MaxBreadcrumbs; + cocoaOptions.MaxCacheItems = (nuint) options.MaxCacheItems; + cocoaOptions.ReleaseName = options.Release; + cocoaOptions.SampleRate = options.SampleRate; + cocoaOptions.SendClientReports = options.SendClientReports; + cocoaOptions.SendDefaultPii = options.SendDefaultPii; + cocoaOptions.SessionTrackingIntervalMillis = (nuint) options.AutoSessionTrackingInterval.TotalMilliseconds; + + // These options are not available in the Sentry Cocoa SDK + // cocoaOptions.? = options.InitCacheFlushTimeout; + // cocoaOptions.? = options.MaxQueueItems; + // cocoaOptions.? = options.ShutdownTimeout; + + // NOTE: options.CacheDirectoryPath - No option for this in Sentry Cocoa, but caching is still enabled + // https://github.com/getsentry/sentry-cocoa/issues/1051 + + // 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.BeforeBreadcrumb is { } beforeBreadcrumb) + { + // Note: Nullable return is allowed but delegate is generated incorrectly + cocoaOptions.BeforeBreadcrumb = b => beforeBreadcrumb(b.ToBreadcrumb(options.DiagnosticLogger))? + .ToCocoaBreadcrumb()!; + } + + // These options we have behind feature flags + if (options.iOS.EnableCocoaSdkTracing) { - // Capture the Cocoa options reference on the outer scope - cocoaOptions = o; - - // These options are copied over from our SentryOptions - o.AttachStacktrace = options.AttachStacktrace; - o.Debug = options.Debug; - o.DiagnosticLevel = options.DiagnosticLevel.ToCocoaSentryLevel(); - o.Dsn = options.Dsn; - o.EnableAutoSessionTracking = options.AutoSessionTracking; - o.Environment = options.Environment; - o.MaxAttachmentSize = (nuint) options.MaxAttachmentSize; - o.MaxBreadcrumbs = (nuint) options.MaxBreadcrumbs; - o.MaxCacheItems = (nuint) options.MaxCacheItems; - o.ReleaseName = options.Release; - o.SampleRate = options.SampleRate; - o.SendClientReports = options.SendClientReports; - o.SendDefaultPii = options.SendDefaultPii; - o.SessionTrackingIntervalMillis = (nuint) options.AutoSessionTrackingInterval.TotalMilliseconds; - - // These options are not available in the Sentry Cocoa SDK - // o.? = options.InitCacheFlushTimeout; - // o.? = options.MaxQueueItems; - // o.? = options.ShutdownTimeout; - - // NOTE: options.CacheDirectoryPath - No option for this in Sentry Cocoa, but caching is still enabled - // https://github.com/getsentry/sentry-cocoa/issues/1051 - - // 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.BeforeBreadcrumb is { } beforeBreadcrumb) + cocoaOptions.TracesSampleRate = options.TracesSampleRate; + + if (options.TracesSampler is { } tracesSampler) { // Note: Nullable return is allowed but delegate is generated incorrectly - o.BeforeBreadcrumb = b => beforeBreadcrumb(b.ToBreadcrumb(options.DiagnosticLogger))? - .ToCocoaBreadcrumb()!; + cocoaOptions.TracesSampler = context => tracesSampler(context.ToTransactionSamplingContext())!; } - - // These options we have behind feature flags - if (options.iOS.EnableCocoaSdkTracing) + } + + // TODO: Finish SentryEventExtensions to enable these + // + // if (options.iOS.EnableCocoaSdkBeforeSend && options.BeforeSend is { } beforeSend) + // { + // // Note: Nullable return is allowed but delegate is generated incorrectly + // cocoaOptions.BeforeSend = evt => beforeSend(evt.ToSentryEvent(o))?.ToCocoaSentryEvent(options, o)!; + // } + // + // if (options.iOS.OnCrashedLastRun is { } onCrashedLastRun) + // { + // cocoaOptions.OnCrashedLastRun = evt => onCrashedLastRun(evt.ToSentryEvent(o)); + // } + + // These options are from Cocoa's SentryOptions + cocoaOptions.AttachScreenshot = options.iOS.AttachScreenshot; + cocoaOptions.AppHangTimeoutInterval = options.iOS.AppHangTimeoutInterval.TotalSeconds; + cocoaOptions.IdleTimeout = options.iOS.IdleTimeout.TotalSeconds; + cocoaOptions.Dist = options.iOS.Distribution; + cocoaOptions.EnableAppHangTracking = options.iOS.EnableAppHangTracking; + cocoaOptions.EnableAutoBreadcrumbTracking = options.iOS.EnableAutoBreadcrumbTracking; + cocoaOptions.EnableAutoPerformanceTracking = options.iOS.EnableAutoPerformanceTracking; + cocoaOptions.EnableCoreDataTracking = options.iOS.EnableCoreDataTracking; + cocoaOptions.EnableFileIOTracking = options.iOS.EnableFileIOTracking; + cocoaOptions.EnableNetworkBreadcrumbs = options.iOS.EnableNetworkBreadcrumbs; + cocoaOptions.EnableNetworkTracking = options.iOS.EnableNetworkTracking; + cocoaOptions.EnableOutOfMemoryTracking = options.iOS.EnableOutOfMemoryTracking; + cocoaOptions.EnableSwizzling = options.iOS.EnableSwizzling; + cocoaOptions.EnableUIViewControllerTracking = options.iOS.EnableUIViewControllerTracking; + cocoaOptions.EnableUserInteractionTracing = options.iOS.EnableUserInteractionTracing; + cocoaOptions.StitchAsyncCode = options.iOS.StitchAsyncCode; + cocoaOptions.UrlSessionDelegate = options.iOS.UrlSessionDelegate; + + // In-App Excludes and Includes to be passed to the Cocoa SDK + options.iOS.InAppExcludes?.ForEach(x => cocoaOptions.AddInAppExclude(x)); + options.iOS.InAppIncludes?.ForEach(x => cocoaOptions.AddInAppInclude(x)); + + // These options are intentionally not expose or modified + // cocoaOptions.Enabled + // cocoaOptions.SdkInfo + // cocoaOptions.Integrations + // cocoaOptions.DefaultIntegrations + // cocoaOptions.EnableProfiling (deprecated) + + // When we have an unhandled managed exception, we send that to Sentry twice - once managed and once native. + // The managed exception is what a .NET developer would expect, and it is sent by the Sentry.NET SDK + // But we also get a native SIGABRT since it crashed the application, which is sent by the Sentry Cocoa SDK. + // This is partially due to our setting ObjCRuntime.MarshalManagedExceptionMode.UnwindNativeCode above. + // Thankfully, we can see Xamarin's unhandled exception handler on the stack trace, so we can filter them out. + // Here is the function that calls abort(), which we will use as a filter: + // https://github.com/xamarin/xamarin-macios/blob/c55fbdfef95028ba03d0f7a35aebca03bd76f852/runtime/runtime.m#L1114-L1122 + cocoaOptions.BeforeSend = evt => + { + // There should only be one exception on the event in this case + if (evt.Exceptions?.Length == 1) { - o.TracesSampleRate = options.TracesSampleRate; - - if (options.TracesSampler is { } tracesSampler) + // It will match the following characteristics + var ex = evt.Exceptions[0]; + if (ex.Type == "SIGABRT" && ex.Value == "Signal 6, Code 0" && + ex.Stacktrace?.Frames.Any(f => f.Function == "xamarin_unhandled_exception_handler") is true) { - // Note: Nullable return is allowed but delegate is generated incorrectly - o.TracesSampler = context => tracesSampler(context.ToTransactionSamplingContext())!; + // Don't sent it + return null!; } } - // TODO: Finish SentryEventExtensions to enable these - // - // if (options.iOS.EnableCocoaSdkBeforeSend && options.BeforeSend is { } beforeSend) - // { - // // Note: Nullable return is allowed but delegate is generated incorrectly - // o.BeforeSend = evt => beforeSend(evt.ToSentryEvent(o))?.ToCocoaSentryEvent(options, o)!; - // } - // - // if (options.iOS.OnCrashedLastRun is { } onCrashedLastRun) - // { - // o.OnCrashedLastRun = evt => onCrashedLastRun(evt.ToSentryEvent(o)); - // } - - // These options are from Cocoa's SentryOptions - o.AttachScreenshot = options.iOS.AttachScreenshot; - o.AppHangTimeoutInterval = options.iOS.AppHangTimeoutInterval.TotalSeconds; - o.IdleTimeout = options.iOS.IdleTimeout.TotalSeconds; - o.Dist = options.iOS.Distribution; - o.EnableAppHangTracking = options.iOS.EnableAppHangTracking; - o.EnableAutoBreadcrumbTracking = options.iOS.EnableAutoBreadcrumbTracking; - o.EnableAutoPerformanceTracking = options.iOS.EnableAutoPerformanceTracking; - o.EnableCoreDataTracking = options.iOS.EnableCoreDataTracking; - o.EnableFileIOTracking = options.iOS.EnableFileIOTracking; - o.EnableNetworkBreadcrumbs = options.iOS.EnableNetworkBreadcrumbs; - o.EnableNetworkTracking = options.iOS.EnableNetworkTracking; - o.EnableOutOfMemoryTracking = options.iOS.EnableOutOfMemoryTracking; - o.EnableSwizzling = options.iOS.EnableSwizzling; - o.EnableUIViewControllerTracking = options.iOS.EnableUIViewControllerTracking; - o.EnableUserInteractionTracing = options.iOS.EnableUserInteractionTracing; - o.StitchAsyncCode = options.iOS.StitchAsyncCode; - o.UrlSessionDelegate = options.iOS.UrlSessionDelegate; - - // In-App Excludes and Includes to be passed to the Cocoa SDK - options.iOS.InAppExcludes?.ForEach(x => o.AddInAppExclude(x)); - options.iOS.InAppIncludes?.ForEach(x => o.AddInAppInclude(x)); - - // These options are intentionally not expose or modified - // o.Enabled - // o.SdkInfo - // o.Integrations - // o.DefaultIntegrations - // o.EnableProfiling (deprecated) - - // When we have an unhandled managed exception, we send that to Sentry twice - once managed and once native. - // The managed exception is what a .NET developer would expect, and it is sent by the Sentry.NET SDK - // But we also get a native SIGABRT since it crashed the application, which is sent by the Sentry Cocoa SDK. - // This is partially due to our setting ObjCRuntime.MarshalManagedExceptionMode.UnwindNativeCode above. - // Thankfully, we can see Xamarin's unhandled exception handler on the stack trace, so we can filter them out. - // Here is the function that calls abort(), which we will use as a filter: - // https://github.com/xamarin/xamarin-macios/blob/c55fbdfef95028ba03d0f7a35aebca03bd76f852/runtime/runtime.m#L1114-L1122 - o.BeforeSend = evt => - { - // There should only be one exception on the event in this case - if (evt.Exceptions?.Length == 1) - { - // It will match the following characteristics - var ex = evt.Exceptions[0]; - if (ex.Type == "SIGABRT" && ex.Value == "Signal 6, Code 0" && - ex.Stacktrace?.Frames.Any(f => f.Function == "xamarin_unhandled_exception_handler") is true) - { - // Don't sent it - return null!; - } - } - - // Other event, send as normal - return evt; - }; + // Other event, send as normal + return evt; + }; - }); + // Now initialize the Cocoa SDK + SentryCocoaSdk.StartWithOptionsObject(cocoaOptions); // Set options for the managed SDK that depend on the Cocoa SDK options.AddEventProcessor(new IosEventProcessor(cocoaOptions!)); From 5d2cfc1db5d21ef10d6ab0e768c31a51ce83bc68 Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Fri, 12 Aug 2022 13:08:29 -0700 Subject: [PATCH 27/28] Cleanup delegate calls --- src/Sentry/Platforms/iOS/SentrySdk.cs | 44 +++++++++++++++++++++------ 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/src/Sentry/Platforms/iOS/SentrySdk.cs b/src/Sentry/Platforms/iOS/SentrySdk.cs index 0b2b22aa8b..8a64c8fdc6 100644 --- a/src/Sentry/Platforms/iOS/SentrySdk.cs +++ b/src/Sentry/Platforms/iOS/SentrySdk.cs @@ -53,9 +53,15 @@ private static void InitSentryCocoaSdk(SentryOptions options) if (options.BeforeBreadcrumb is { } beforeBreadcrumb) { - // Note: Nullable return is allowed but delegate is generated incorrectly - cocoaOptions.BeforeBreadcrumb = b => beforeBreadcrumb(b.ToBreadcrumb(options.DiagnosticLogger))? - .ToCocoaBreadcrumb()!; + cocoaOptions.BeforeBreadcrumb = b => + { + var breadcrumb = b.ToBreadcrumb(options.DiagnosticLogger); + var result = beforeBreadcrumb(breadcrumb)?.ToCocoaBreadcrumb(); + + // Note: Nullable result is allowed but delegate is generated incorrectly + // See https://github.com/xamarin/xamarin-macios/issues/15299#issuecomment-1201863294 + return result!; + }; } // These options we have behind feature flags @@ -65,22 +71,40 @@ private static void InitSentryCocoaSdk(SentryOptions options) if (options.TracesSampler is { } tracesSampler) { - // Note: Nullable return is allowed but delegate is generated incorrectly - cocoaOptions.TracesSampler = context => tracesSampler(context.ToTransactionSamplingContext())!; + cocoaOptions.TracesSampler = cocoaContext => + { + var context = cocoaContext.ToTransactionSamplingContext(); + var result = tracesSampler(context); + + // Note: Nullable result is allowed but delegate is generated incorrectly + // See https://github.com/xamarin/xamarin-macios/issues/15299#issuecomment-1201863294 + return result!; + }; } } // TODO: Finish SentryEventExtensions to enable these - // + // if (options.iOS.EnableCocoaSdkBeforeSend && options.BeforeSend is { } beforeSend) // { - // // Note: Nullable return is allowed but delegate is generated incorrectly - // cocoaOptions.BeforeSend = evt => beforeSend(evt.ToSentryEvent(o))?.ToCocoaSentryEvent(options, o)!; - // } + // cocoaOptions.BeforeSend = evt => + // { + // var sentryEvent = evt.ToSentryEvent(cocoaOptions); + // var result = beforeSend(sentryEvent)?.ToCocoaSentryEvent(options, cocoaOptions); // + // // Note: Nullable result is allowed but delegate is generated incorrectly + // // See https://github.com/xamarin/xamarin-macios/issues/15299#issuecomment-1201863294 + // return result!; + // }; + // } + // if (options.iOS.OnCrashedLastRun is { } onCrashedLastRun) // { - // cocoaOptions.OnCrashedLastRun = evt => onCrashedLastRun(evt.ToSentryEvent(o)); + // cocoaOptions.OnCrashedLastRun = evt => + // { + // var sentryEvent = evt.ToSentryEvent(cocoaOptions); + // onCrashedLastRun(sentryEvent); + // }; // } // These options are from Cocoa's SentryOptions From 937acaa5b3c531b74119b83aee34d2800be82bac Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Sun, 14 Aug 2022 09:54:43 -0700 Subject: [PATCH 28/28] . --- src/Sentry/Internal/Constants.cs | 2 ++ src/Sentry/SentryOptions.cs | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Sentry/Internal/Constants.cs b/src/Sentry/Internal/Constants.cs index 2235ea1d67..b6c0683e6c 100644 --- a/src/Sentry/Internal/Constants.cs +++ b/src/Sentry/Internal/Constants.cs @@ -35,6 +35,8 @@ internal static class Constants // See: https://github.com/getsentry/sentry-release-registry #if ANDROID public const string SdkName = "sentry.dotnet.android"; +#elif IOS || MACCATALYST + public const string SdkName = "sentry.dotnet.cocoa"; #else public const string SdkName = "sentry.dotnet"; #endif diff --git a/src/Sentry/SentryOptions.cs b/src/Sentry/SentryOptions.cs index 41ac67d3a0..03ff94311f 100644 --- a/src/Sentry/SentryOptions.cs +++ b/src/Sentry/SentryOptions.cs @@ -572,7 +572,7 @@ public StackTraceMode StackTraceMode /// public TimeSpan AutoSessionTrackingInterval { get; set; } = TimeSpan.FromSeconds(30); -#if ANDROID +#if ANDROID || IOS || MACCATALYST /// /// Whether the SDK should start a session automatically when it's initialized and /// end the session when it's closed.