From 94df25ff778101add619e493087065cd79f35315 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Tue, 27 Aug 2024 06:05:03 +1200 Subject: [PATCH 001/363] Removed SentrySdk.Init overloads accepting AndroidContext context (#3562) --- CHANGELOG.md | 3 +++ src/Sentry/Platforms/Android/SentrySdk.cs | 28 ----------------------- 2 files changed, 3 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 03c7e7e99f..1ba1115ac0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Changelog +### API Changes +- You should no longer pass `AndroidContext` as an argument to `SentrySdk.Init` ([#3562](https://github.com/getsentry/sentry-dotnet/pull/3562)) + ## 4.10.2 ### Various fixes & improvements diff --git a/src/Sentry/Platforms/Android/SentrySdk.cs b/src/Sentry/Platforms/Android/SentrySdk.cs index f018ffdcd1..a346f581b3 100644 --- a/src/Sentry/Platforms/Android/SentrySdk.cs +++ b/src/Sentry/Platforms/Android/SentrySdk.cs @@ -20,34 +20,6 @@ public static partial class SentrySdk { private static AndroidContext AppContext { get; set; } = Application.Context; - /// - /// Initializes the SDK for Android, with an optional configuration options callback. - /// - /// The Android application context. - /// The configuration options callback. - /// An object that should be disposed when the application terminates. - [Obsolete("It is no longer required to provide the application context when calling Init. " + - "This method may be removed in a future major release.")] - public static IDisposable Init(AndroidContext context, Action? configureOptions) - { - AppContext = context; - return Init(configureOptions); - } - - /// - /// Initializes the SDK for Android, using a configuration options instance. - /// - /// The Android application context. - /// The configuration options instance. - /// An object that should be disposed when the application terminates. - [Obsolete("It is no longer required to provide the application context when calling Init. " + - "This method may be removed in a future major release.")] - public static IDisposable Init(AndroidContext context, SentryOptions options) - { - AppContext = context; - return Init(options); - } - private static void InitSentryAndroidSdk(SentryOptions options) { // Set default release and distribution From 225fd1e9db7f864d7490912c31b82901d8afcead Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Tue, 27 Aug 2024 06:06:07 +1200 Subject: [PATCH 002/363] Removed obsolete User.Segment property (#3563) --- CHANGELOG.md | 4 ++- src/Sentry.NLog/SentryTarget.cs | 3 -- src/Sentry/DynamicSamplingContext.cs | 10 ------- .../Android/Extensions/UserExtensions.cs | 6 ---- .../Cocoa/Extensions/UserExtensions.cs | 6 ---- src/Sentry/SentryUser.cs | 30 ------------------- ...piApprovalTests.Run.DotNet6_0.verified.txt | 2 -- ...piApprovalTests.Run.DotNet7_0.verified.txt | 2 -- ...piApprovalTests.Run.DotNet8_0.verified.txt | 2 -- .../ApiApprovalTests.Run.Net4_8.verified.txt | 2 -- .../DynamicSamplingContextTests.cs | 6 +--- test/Sentry.Tests/Protocol/ScopeTests.cs | 10 ------- test/Sentry.Tests/Protocol/UserTests.cs | 13 -------- 13 files changed, 4 insertions(+), 92 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ba1115ac0..279b72161c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,10 @@ # Changelog ### API Changes -- You should no longer pass `AndroidContext` as an argument to `SentrySdk.Init` ([#3562](https://github.com/getsentry/sentry-dotnet/pull/3562)) +- You should no longer pass `AndroidContext` as an argument to `SentrySdk.Init` ([#3562](https://github.com/getsentry/sentry-dotnet/pull/3562)) +- The `SentryUser.Segment` property has been deprecated. Consider sending this as a tag or additional data instead ([#3563](https://github.com/getsentry/sentry-dotnet/pull/3563)) + ## 4.10.2 ### Various fixes & improvements diff --git a/src/Sentry.NLog/SentryTarget.cs b/src/Sentry.NLog/SentryTarget.cs index a3a0943132..1a5c4da619 100644 --- a/src/Sentry.NLog/SentryTarget.cs +++ b/src/Sentry.NLog/SentryTarget.cs @@ -453,9 +453,6 @@ private void InnerWrite(LogEventInfo logEvent) Username = User.Username?.Render(logEvent), Email = User.Email?.Render(logEvent), IpAddress = User.IpAddress?.Render(logEvent), -#pragma warning disable CS0618 // Type or member is obsolete - Segment = User.Segment?.Render(logEvent) -#pragma warning restore CS0618 // Type or member is obsolete }; if (User.Other?.Count > 0) diff --git a/src/Sentry/DynamicSamplingContext.cs b/src/Sentry/DynamicSamplingContext.cs index 5c30a97256..26056d17cc 100644 --- a/src/Sentry/DynamicSamplingContext.cs +++ b/src/Sentry/DynamicSamplingContext.cs @@ -26,7 +26,6 @@ private DynamicSamplingContext( double? sampleRate = null, string? release = null, string? environment = null, - string? userSegment = null, string? transactionName = null) { // Validate and set required values @@ -72,11 +71,6 @@ private DynamicSamplingContext( items.Add("environment", environment); } - if (!string.IsNullOrWhiteSpace(userSegment)) - { - items.Add("user_segment", userSegment); - } - if (!string.IsNullOrWhiteSpace(transactionName)) { items.Add("transaction", transactionName); @@ -127,9 +121,6 @@ public static DynamicSamplingContext CreateFromTransaction(TransactionTracer tra var traceId = transaction.TraceId; var sampled = transaction.IsSampled; var sampleRate = transaction.SampleRate!.Value; -#pragma warning disable CS0618 // Type or member is obsolete - var userSegment = transaction.User.Segment; -#pragma warning restore CS0618 // Type or member is obsolete var transactionName = transaction.NameSource.IsHighQuality() ? transaction.Name : null; // These two may not have been set yet on the transaction, but we can get them directly. @@ -143,7 +134,6 @@ public static DynamicSamplingContext CreateFromTransaction(TransactionTracer tra sampleRate, release, environment, - userSegment, transactionName); } diff --git a/src/Sentry/Platforms/Android/Extensions/UserExtensions.cs b/src/Sentry/Platforms/Android/Extensions/UserExtensions.cs index 555ae67ad0..c927cdad0e 100644 --- a/src/Sentry/Platforms/Android/Extensions/UserExtensions.cs +++ b/src/Sentry/Platforms/Android/Extensions/UserExtensions.cs @@ -12,9 +12,6 @@ public static SentryUser ToUser(this JavaSdk.Protocol.User user) => Id = user.Id, IpAddress = user.IpAddress, Username = user.Username, -#pragma warning disable CS0618 // Type or member is obsolete - Segment = user.Segment, -#pragma warning restore CS0618 // Type or member is obsolete Other = user.Data ?? EmptyDictionary }; @@ -25,9 +22,6 @@ public static JavaSdk.Protocol.User ToJavaUser(this SentryUser user) => Id = user.Id, IpAddress = user.IpAddress, Username = user.Username, -#pragma warning disable CS0618 // Type or member is obsolete - Segment = user.Segment, -#pragma warning restore CS0618 // Type or member is obsolete Data = user.Other.Count == 0 ? null : user.Other }; } diff --git a/src/Sentry/Platforms/Cocoa/Extensions/UserExtensions.cs b/src/Sentry/Platforms/Cocoa/Extensions/UserExtensions.cs index 7927184a29..301feb452a 100644 --- a/src/Sentry/Platforms/Cocoa/Extensions/UserExtensions.cs +++ b/src/Sentry/Platforms/Cocoa/Extensions/UserExtensions.cs @@ -11,9 +11,6 @@ public static SentryUser ToUser(this CocoaSdk.SentryUser user, IDiagnosticLogger Id = user.UserId, IpAddress = user.IpAddress, Username = user.Username, -#pragma warning disable CS0618 // Type or member is obsolete - Segment = user.Segment, -#pragma warning restore CS0618 // Type or member is obsolete Other = user.Data.ToStringDictionary(logger) }; @@ -25,9 +22,6 @@ public static CocoaSdk.SentryUser ToCocoaUser(this SentryUser user) UserId = user.Id, IpAddress = user.IpAddress, Username = user.Username, -#pragma warning disable CS0618 // Type or member is obsolete - Segment = user.Segment ?? "", -#pragma warning restore CS0618 // Type or member is obsolete Data = user.Other.ToNullableNSDictionary() }; diff --git a/src/Sentry/SentryUser.cs b/src/Sentry/SentryUser.cs index a5c6ceabed..5558d8cd32 100644 --- a/src/Sentry/SentryUser.cs +++ b/src/Sentry/SentryUser.cs @@ -15,7 +15,6 @@ public sealed class SentryUser : ISentryJsonSerializable private string? _username; private string? _email; private string? _ipAddress; - private string? _segment; private IDictionary? _other; /// @@ -82,23 +81,6 @@ public string? IpAddress } } - /// - /// The segment the user belongs to. - /// - [Obsolete("This property is deprecated and will be removed in a future version.")] - public string? Segment - { - get => _segment; - set - { - if (_segment != value) - { - _segment = value; - PropertyChanged?.Invoke(this); - } - } - } - /// /// Additional information about the user. /// @@ -137,9 +119,6 @@ internal void CopyTo(SentryUser? user) user.Username ??= Username; user.Email ??= Email; user.IpAddress ??= IpAddress; -#pragma warning disable CS0618 // Type or member is obsolete - user.Segment ??= Segment; -#pragma warning restore CS0618 // Type or member is obsolete user._other ??= _other?.ToDictionary( entry => entry.Key, @@ -151,9 +130,6 @@ Id is not null || Username is not null || Email is not null || IpAddress is not null || -#pragma warning disable CS0618 // Type or member is obsolete - Segment is not null || -#pragma warning restore CS0618 // Type or member is obsolete _other?.Count > 0; /// @@ -165,9 +141,6 @@ public void WriteTo(Utf8JsonWriter writer, IDiagnosticLogger? _) writer.WriteStringIfNotWhiteSpace("username", Username); writer.WriteStringIfNotWhiteSpace("email", Email); writer.WriteStringIfNotWhiteSpace("ip_address", IpAddress); -#pragma warning disable CS0618 // Type or member is obsolete - writer.WriteStringIfNotWhiteSpace("segment", Segment); -#pragma warning restore CS0618 // Type or member is obsolete writer.WriteStringDictionaryIfNotEmpty("other", _other!); writer.WriteEndObject(); @@ -191,9 +164,6 @@ public static SentryUser FromJson(JsonElement json) Username = username, Email = email, IpAddress = ip, -#pragma warning disable CS0618 // Type or member is obsolete - Segment = segment, -#pragma warning restore CS0618 // Type or member is obsolete _other = other?.WhereNotNullValue().ToDict() }; } diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt index 9d3f44a7aa..076afa986e 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt @@ -980,8 +980,6 @@ namespace Sentry public string? Id { get; set; } public string? IpAddress { get; set; } public System.Collections.Generic.IDictionary Other { get; set; } - [System.Obsolete("This property is deprecated and will be removed in a future version.")] - public string? Segment { get; set; } public string? Username { get; set; } public Sentry.SentryUser Clone() { } public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? _) { } diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt index 9d3f44a7aa..076afa986e 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt @@ -980,8 +980,6 @@ namespace Sentry public string? Id { get; set; } public string? IpAddress { get; set; } public System.Collections.Generic.IDictionary Other { get; set; } - [System.Obsolete("This property is deprecated and will be removed in a future version.")] - public string? Segment { get; set; } public string? Username { get; set; } public Sentry.SentryUser Clone() { } public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? _) { } diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt index 5fa4dfbeed..2480be6b48 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt @@ -982,8 +982,6 @@ namespace Sentry public string? Id { get; set; } public string? IpAddress { get; set; } public System.Collections.Generic.IDictionary Other { get; set; } - [System.Obsolete("This property is deprecated and will be removed in a future version.")] - public string? Segment { get; set; } public string? Username { get; set; } public Sentry.SentryUser Clone() { } public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? _) { } diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt index b60f14938d..624bcfe712 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt @@ -977,8 +977,6 @@ namespace Sentry public string? Id { get; set; } public string? IpAddress { get; set; } public System.Collections.Generic.IDictionary Other { get; set; } - [System.Obsolete("This property is deprecated and will be removed in a future version.")] - public string? Segment { get; set; } public string? Username { get; set; } public Sentry.SentryUser Clone() { } public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? _) { } diff --git a/test/Sentry.Tests/DynamicSamplingContextTests.cs b/test/Sentry.Tests/DynamicSamplingContextTests.cs index 666c685179..b23a1c4962 100644 --- a/test/Sentry.Tests/DynamicSamplingContextTests.cs +++ b/test/Sentry.Tests/DynamicSamplingContextTests.cs @@ -255,16 +255,13 @@ public void CreateFromTransaction(bool? isSampled) SampleRate = 0.5, User = { -#pragma warning disable CS0618 // Type or member is obsolete - Segment = "Group A" -#pragma warning restore CS0618 // Type or member is obsolete }, }; var dsc = transaction.CreateDynamicSamplingContext(options); Assert.NotNull(dsc); - Assert.Equal(isSampled.HasValue ? 8 : 7, dsc.Items.Count); + Assert.Equal(isSampled.HasValue ? 7 : 6, dsc.Items.Count); Assert.Equal(traceId.ToString(), Assert.Contains("trace_id", dsc.Items)); Assert.Equal("d4d82fc1c2c4032a83f3a29aa3a3aff", Assert.Contains("public_key", dsc.Items)); if (transaction.IsSampled is { } sampled) @@ -278,7 +275,6 @@ public void CreateFromTransaction(bool? isSampled) Assert.Equal("0.5", Assert.Contains("sample_rate", dsc.Items)); Assert.Equal("foo@2.4.5", Assert.Contains("release", dsc.Items)); Assert.Equal("staging", Assert.Contains("environment", dsc.Items)); - Assert.Equal("Group A", Assert.Contains("user_segment", dsc.Items)); Assert.Equal("GET /person/{id}", Assert.Contains("transaction", dsc.Items)); } diff --git a/test/Sentry.Tests/Protocol/ScopeTests.cs b/test/Sentry.Tests/Protocol/ScopeTests.cs index f6ad9cd7e4..e3ac6a6cb0 100644 --- a/test/Sentry.Tests/Protocol/ScopeTests.cs +++ b/test/Sentry.Tests/Protocol/ScopeTests.cs @@ -66,16 +66,6 @@ public void HasUser_UserWithIpAddress_ReturnsTrue() Assert.True(sut.HasUser()); } - [Fact] - public void HasUser_UserWithSegment_ReturnsTrue() - { - var sut = _fixture.GetSut(); -#pragma warning disable CS0618 // Type or member is obsolete - sut.User.Segment = "test"; -#pragma warning restore CS0618 // Type or member is obsolete - Assert.True(sut.HasUser()); - } - [Fact] public void HasUser_UserWithOther_ReturnsTrue() { diff --git a/test/Sentry.Tests/Protocol/UserTests.cs b/test/Sentry.Tests/Protocol/UserTests.cs index f97a43dbae..4de5edab5b 100644 --- a/test/Sentry.Tests/Protocol/UserTests.cs +++ b/test/Sentry.Tests/Protocol/UserTests.cs @@ -18,9 +18,6 @@ public void SerializeObject_AllPropertiesSetToNonDefault_SerializesValidObject() Username = "user-name", Email = "test@sentry.io", IpAddress = "::1", -#pragma warning disable CS0618 // Type or member is obsolete - Segment = "A1", -#pragma warning restore CS0618 // Type or member is obsolete Other = new Dictionary { { "testCustomValueKey", "testCustomValue" } } }; @@ -32,7 +29,6 @@ public void SerializeObject_AllPropertiesSetToNonDefault_SerializesValidObject() "username": "user-name", "email": "test@sentry.io", "ip_address": "::1", - "segment": "A1", "other": { "testCustomValueKey": "testCustomValue" } @@ -50,9 +46,6 @@ public void Clone_CopyValues() Email = "emal@sentry.io", IpAddress = "::1", Username = "user", -#pragma warning disable CS0618 // Type or member is obsolete - Segment = "segment", -#pragma warning restore CS0618 // Type or member is obsolete Other = new Dictionary { {"testCustomValueKey", "testCustomValue"} @@ -65,9 +58,6 @@ public void Clone_CopyValues() Assert.Equal(sut.Username, clone.Username); Assert.Equal(sut.Email, clone.Email); Assert.Equal(sut.IpAddress, clone.IpAddress); -#pragma warning disable CS0618 // Type or member is obsolete - Assert.Equal(sut.Segment, clone.Segment); -#pragma warning restore CS0618 // Type or member is obsolete Assert.Equal(sut.Other, clone.Other); } @@ -86,9 +76,6 @@ public static IEnumerable TestCases() yield return new object[] { (new SentryUser { Username = "some username" }, """{"username":"some username"}""") }; yield return new object[] { (new SentryUser { Email = "some email" }, """{"email":"some email"}""") }; yield return new object[] { (new SentryUser { IpAddress = "some ipAddress" }, """{"ip_address":"some ipAddress"}""") }; -#pragma warning disable CS0618 // Type or member is obsolete - yield return new object[] { (new SentryUser { Segment = "some segment" }, """{"segment":"some segment"}""") }; -#pragma warning restore CS0618 // Type or member is obsolete var other = new Dictionary { { "testCustomValueKey", "testCustomValue" } }; yield return new object[] { (new SentryUser { Other = other }, """{"other":{"testCustomValueKey":"testCustomValue"}}""") }; From e2967f0386a85dfb8d125d45252adb855e337558 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Tue, 27 Aug 2024 06:07:31 +1200 Subject: [PATCH 003/363] Added Origin to ITraceContext (#3564) --- CHANGELOG.md | 5 ++++- src/Sentry/IBaseTracer.cs | 2 +- src/Sentry/ITraceContextInternal.cs | 19 ------------------- src/Sentry/Internal/NoOpSpan.cs | 2 +- .../Facades/TransactionContextFacade.cs | 2 ++ .../Cocoa/Facades/TransactionContextFacade.cs | 2 ++ src/Sentry/Protocol/ITraceContext.cs | 8 ++++++++ src/Sentry/Protocol/Trace.cs | 2 +- src/Sentry/SentrySpan.cs | 2 +- src/Sentry/SentryTransaction.cs | 2 +- src/Sentry/SpanContext.cs | 2 +- ...piApprovalTests.Run.DotNet6_0.verified.txt | 1 + ...piApprovalTests.Run.DotNet7_0.verified.txt | 1 + ...piApprovalTests.Run.DotNet8_0.verified.txt | 1 + .../ApiApprovalTests.Run.Net4_8.verified.txt | 1 + 15 files changed, 26 insertions(+), 26 deletions(-) delete mode 100644 src/Sentry/ITraceContextInternal.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 279b72161c..17dfa270e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,13 @@ # Changelog +## Version Five + ### API Changes - You should no longer pass `AndroidContext` as an argument to `SentrySdk.Init` ([#3562](https://github.com/getsentry/sentry-dotnet/pull/3562)) - The `SentryUser.Segment` property has been deprecated. Consider sending this as a tag or additional data instead ([#3563](https://github.com/getsentry/sentry-dotnet/pull/3563)) - +- The ITraceContext now includes an [Origin](https://develop.sentry.dev/sdk/telemetry/traces/trace-origin/), which is set automatically and is primarily used internally by the Sentry server ([#3564](https://github.com/getsentry/sentry-dotnet/pull/3564)) + ## 4.10.2 ### Various fixes & improvements diff --git a/src/Sentry/IBaseTracer.cs b/src/Sentry/IBaseTracer.cs index b9b318f10f..9ca12f5877 100644 --- a/src/Sentry/IBaseTracer.cs +++ b/src/Sentry/IBaseTracer.cs @@ -1,6 +1,6 @@ namespace Sentry; -internal interface IBaseTracer : ITraceContextInternal +internal interface IBaseTracer { internal bool IsOtelInstrumenter { get; } } diff --git a/src/Sentry/ITraceContextInternal.cs b/src/Sentry/ITraceContextInternal.cs deleted file mode 100644 index 01cfbfc2a3..0000000000 --- a/src/Sentry/ITraceContextInternal.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Sentry; - -/// -/// Internal interface defining members to be added to the TraceContexts for the next major release -/// (since adding members to interfaces is a breaking change). -/// -/// -/// TODO: Remove this interface in the next major version. -/// -internal interface ITraceContextInternal -{ - /// - /// Specifies the origin of the trace. If no origin is set then the trace origin is assumed to be "manual". - /// - /// - /// See https://develop.sentry.dev/sdk/performance/trace-origin/ for more information. - /// - string? Origin { get; } -} diff --git a/src/Sentry/Internal/NoOpSpan.cs b/src/Sentry/Internal/NoOpSpan.cs index 7d6c99f264..babecc4a4d 100644 --- a/src/Sentry/Internal/NoOpSpan.cs +++ b/src/Sentry/Internal/NoOpSpan.cs @@ -5,7 +5,7 @@ namespace Sentry.Internal; /// /// Span class to use when we can't return null but a request to create a span couldn't be completed. /// -internal class NoOpSpan : ISpan, ITraceContextInternal +internal class NoOpSpan : ISpan { public static ISpan Instance { get; } = new NoOpSpan(); diff --git a/src/Sentry/Platforms/Android/Facades/TransactionContextFacade.cs b/src/Sentry/Platforms/Android/Facades/TransactionContextFacade.cs index 73a50df116..184b8bdd66 100644 --- a/src/Sentry/Platforms/Android/Facades/TransactionContextFacade.cs +++ b/src/Sentry/Platforms/Android/Facades/TransactionContextFacade.cs @@ -25,6 +25,8 @@ internal TransactionContextFacade(JavaSdk.TransactionContext context) public string Operation => _context.Operation; + public string? Origin => _context.Origin; + public string? Description => _context.Description; public SpanStatus? Status => _context.Status?.ToSpanStatus(); diff --git a/src/Sentry/Platforms/Cocoa/Facades/TransactionContextFacade.cs b/src/Sentry/Platforms/Cocoa/Facades/TransactionContextFacade.cs index 94e91bb014..6be22bf65c 100644 --- a/src/Sentry/Platforms/Cocoa/Facades/TransactionContextFacade.cs +++ b/src/Sentry/Platforms/Cocoa/Facades/TransactionContextFacade.cs @@ -25,6 +25,8 @@ internal TransactionContextFacade(CocoaSdk.SentryTransactionContext context) public string Operation => _context.Operation; + public string? Origin => _context.Origin; + public string Description => _context.Description; // Note: SentrySpanContext.Status was removed from the Cocoa SDK in 8.0.0 diff --git a/src/Sentry/Protocol/ITraceContext.cs b/src/Sentry/Protocol/ITraceContext.cs index 24d3a095e7..3a0ec29aa5 100644 --- a/src/Sentry/Protocol/ITraceContext.cs +++ b/src/Sentry/Protocol/ITraceContext.cs @@ -25,6 +25,14 @@ public interface ITraceContext /// string Operation { get; } + /// + /// Specifies the origin of the trace. If no origin is set then the trace origin is assumed to be "manual". + /// + /// + /// See https://develop.sentry.dev/sdk/performance/trace-origin/ for more information. + /// + string? Origin { get; } + /// /// Description. /// diff --git a/src/Sentry/Protocol/Trace.cs b/src/Sentry/Protocol/Trace.cs index 8f9df34bd5..289b2a4b08 100644 --- a/src/Sentry/Protocol/Trace.cs +++ b/src/Sentry/Protocol/Trace.cs @@ -7,7 +7,7 @@ namespace Sentry.Protocol; /// /// Trace context data. /// -public class Trace : ITraceContext, ITraceContextInternal, ISentryJsonSerializable, ICloneable, IUpdatable +public class Trace : ITraceContext, ISentryJsonSerializable, ICloneable, IUpdatable { /// /// Tells Sentry which type of context this is. diff --git a/src/Sentry/SentrySpan.cs b/src/Sentry/SentrySpan.cs index 390404c9bb..9031556c7d 100644 --- a/src/Sentry/SentrySpan.cs +++ b/src/Sentry/SentrySpan.cs @@ -10,7 +10,7 @@ namespace Sentry; /// /// Transaction span. /// -public class SentrySpan : ISpanData, ISentryJsonSerializable, ITraceContextInternal +public class SentrySpan : ISpanData, ISentryJsonSerializable { /// public SpanId SpanId { get; private set; } diff --git a/src/Sentry/SentryTransaction.cs b/src/Sentry/SentryTransaction.cs index 2b5aafa52f..b69acd3f52 100644 --- a/src/Sentry/SentryTransaction.cs +++ b/src/Sentry/SentryTransaction.cs @@ -10,7 +10,7 @@ namespace Sentry; /// /// Sentry performance transaction. /// -public class SentryTransaction : ITransactionData, ISentryJsonSerializable, ITraceContextInternal +public class SentryTransaction : ITransactionData, ISentryJsonSerializable { /// /// Transaction's event ID. diff --git a/src/Sentry/SpanContext.cs b/src/Sentry/SpanContext.cs index 58b22dd7c2..70719b528f 100644 --- a/src/Sentry/SpanContext.cs +++ b/src/Sentry/SpanContext.cs @@ -6,7 +6,7 @@ namespace Sentry; /// /// Span metadata used for sampling. /// -public class SpanContext : ITraceContext, ITraceContextInternal +public class SpanContext : ITraceContext { /// public SpanId SpanId { get; } diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt index 076afa986e..a147787b0d 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt @@ -1660,6 +1660,7 @@ namespace Sentry.Protocol string? Description { get; } bool? IsSampled { get; } string Operation { get; } + string? Origin { get; } Sentry.SpanId? ParentSpanId { get; } Sentry.SpanId SpanId { get; } Sentry.SpanStatus? Status { get; } diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt index 076afa986e..a147787b0d 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt @@ -1660,6 +1660,7 @@ namespace Sentry.Protocol string? Description { get; } bool? IsSampled { get; } string Operation { get; } + string? Origin { get; } Sentry.SpanId? ParentSpanId { get; } Sentry.SpanId SpanId { get; } Sentry.SpanStatus? Status { get; } diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt index 2480be6b48..24c0f6ba78 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt @@ -1662,6 +1662,7 @@ namespace Sentry.Protocol string? Description { get; } bool? IsSampled { get; } string Operation { get; } + string? Origin { get; } Sentry.SpanId? ParentSpanId { get; } Sentry.SpanId SpanId { get; } Sentry.SpanStatus? Status { get; } diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt index 624bcfe712..26f502c76e 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt @@ -1658,6 +1658,7 @@ namespace Sentry.Protocol string? Description { get; } bool? IsSampled { get; } string Operation { get; } + string? Origin { get; } Sentry.SpanId? ParentSpanId { get; } Sentry.SpanId SpanId { get; } Sentry.SpanStatus? Status { get; } From d15cdd99167ae4a620f80dcaec51bc94b40f3331 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Tue, 27 Aug 2024 22:12:17 +1200 Subject: [PATCH 004/363] Align device types with Cocoa and Java SDKs (#3567) --- CHANGELOG.md | 1 + src/Sentry.Maui/Internal/MauiDeviceData.cs | 2 +- .../Android/Extensions/DeviceExtensions.cs | 2 +- src/Sentry/Protocol/Device.cs | 25 +- ...piApprovalTests.Run.DotNet6_0.verified.txt | 4 +- ...piApprovalTests.Run.DotNet7_0.verified.txt | 4 +- ...piApprovalTests.Run.DotNet8_0.verified.txt | 4 +- .../ApiApprovalTests.Run.Net4_8.verified.txt | 4 +- .../Protocol/Context/DeviceTests.cs | 221 +++++++++++------- test/Sentry.Tests/Protocol/DeviceTests.cs | 140 ----------- 10 files changed, 159 insertions(+), 248 deletions(-) delete mode 100644 test/Sentry.Tests/Protocol/DeviceTests.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 17dfa270e3..9544743c93 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - You should no longer pass `AndroidContext` as an argument to `SentrySdk.Init` ([#3562](https://github.com/getsentry/sentry-dotnet/pull/3562)) - The `SentryUser.Segment` property has been deprecated. Consider sending this as a tag or additional data instead ([#3563](https://github.com/getsentry/sentry-dotnet/pull/3563)) - The ITraceContext now includes an [Origin](https://develop.sentry.dev/sdk/telemetry/traces/trace-origin/), which is set automatically and is primarily used internally by the Sentry server ([#3564](https://github.com/getsentry/sentry-dotnet/pull/3564)) +- `Device.BatteryLevel` and `Device.ProcessorFrequency` are now stored as floats rather than ints, to align with the Cocoa and Java SDKs ([#3567](https://github.com/getsentry/sentry-dotnet/pull/3567)) ## 4.10.2 diff --git a/src/Sentry.Maui/Internal/MauiDeviceData.cs b/src/Sentry.Maui/Internal/MauiDeviceData.cs index 05c4b2efdf..3e92772ffa 100644 --- a/src/Sentry.Maui/Internal/MauiDeviceData.cs +++ b/src/Sentry.Maui/Internal/MauiDeviceData.cs @@ -40,7 +40,7 @@ public static void ApplyMauiDeviceData(this Device device, IDiagnosticLogger? lo try { var battery = Battery.Default; - device.BatteryLevel ??= battery.ChargeLevel < 0 ? null : (short)(battery.ChargeLevel * 100.0); + device.BatteryLevel ??= battery.ChargeLevel < 0 ? null : (float)(battery.ChargeLevel * 100.0); device.BatteryStatus ??= battery.State.ToString(); device.IsCharging ??= battery.State switch { diff --git a/src/Sentry/Platforms/Android/Extensions/DeviceExtensions.cs b/src/Sentry/Platforms/Android/Extensions/DeviceExtensions.cs index b608480191..d2aed064b7 100644 --- a/src/Sentry/Platforms/Android/Extensions/DeviceExtensions.cs +++ b/src/Sentry/Platforms/Android/Extensions/DeviceExtensions.cs @@ -23,7 +23,7 @@ public static void ApplyFromSentryAndroidSdk(this Device device, JavaSdk.Protoco device.Name ??= d.Name; device.Family ??= d.Family; device.ModelId ??= d.ModelId; - device.BatteryLevel ??= d.BatteryLevel?.ShortValue(); + device.BatteryLevel ??= d.BatteryLevel?.FloatValue(); device.IsCharging ??= d.IsCharging()?.BooleanValue(); device.IsOnline ??= d.IsOnline()?.BooleanValue(); device.Orientation ??= d.Orientation?.ToDeviceOrientation(); diff --git a/src/Sentry/Protocol/Device.cs b/src/Sentry/Protocol/Device.cs index 210aaf6a26..34a458f635 100644 --- a/src/Sentry/Protocol/Device.cs +++ b/src/Sentry/Protocol/Device.cs @@ -70,9 +70,9 @@ public sealed class Device : ISentryJsonSerializable, ICloneable, IUpdat public string? Architecture { get; set; } /// - /// If the device has a battery an integer defining the battery level (in the range 0-100). + /// If the device has a battery a number defining the battery level (in the range 0-100). /// - public short? BatteryLevel { get; set; } + public float? BatteryLevel { get; set; } /// /// True if the device is charging. @@ -184,7 +184,7 @@ public sealed class Device : ISentryJsonSerializable, ICloneable, IUpdat /// /// 2500 /// - public int? ProcessorFrequency { get; set; } + public float? ProcessorFrequency { get; set; } /// /// Kind of device the application is running on. @@ -426,15 +426,9 @@ public static Device FromJson(JsonElement json) var model = json.GetPropertyOrNull("model")?.GetString(); var modelId = json.GetPropertyOrNull("model_id")?.GetString(); var architecture = json.GetPropertyOrNull("arch")?.GetString(); - - // TODO: For next major: Remove this and change BatteryLevel from short to float - // The Java and Cocoa SDK report the battery as `float` - // Cocoa https://github.com/getsentry/sentry-cocoa/blob/e773cad622b86735f1673368414009475e4119fd/Sources/Sentry/include/SentryUIDeviceWrapper.h#L18 - // Java https://github.com/getsentry/sentry-java/blob/25f1ca4e1636a801c17c1662f0145f888550bce8/sentry/src/main/java/io/sentry/protocol/Device.java#L231-L233 var batteryLevel = json.GetPropertyOrNull("battery_level")?.TryGetDouble(out var level) is true - ? (short)level - : (short?)null; - + ? (float)level + : (float?)null; var isCharging = json.GetPropertyOrNull("charging")?.GetBoolean(); var isOnline = json.GetPropertyOrNull("online")?.GetBoolean(); var orientation = json.GetPropertyOrNull("orientation")?.GetString()?.ParseEnum(); @@ -453,14 +447,9 @@ public static Device FromJson(JsonElement json) var bootTime = json.GetPropertyOrNull("boot_time")?.GetDateTimeOffset(); var processorCount = json.GetPropertyOrNull("processor_count")?.GetInt32(); var cpuDescription = json.GetPropertyOrNull("cpu_description")?.GetString(); - - // TODO: For next major: Remove this and change ProcessorFrequency from int to float - // The Java SDK reports the processorFrequency as `double` - // Java https://github.com/getsentry/sentry-java/blob/9762f09afa51944b40a9b77e116a55e54636e6c5/sentry/src/main/java/io/sentry/protocol/Device.java#L130 var processorFrequency = json.GetPropertyOrNull("processor_frequency")?.TryGetDouble(out var frequency) is true - ? (int)frequency - : (int?)null; - + ? (float)frequency + : (float?)null; var deviceType = json.GetPropertyOrNull("device_type")?.GetString(); var batteryStatus = json.GetPropertyOrNull("battery_status")?.GetString(); var deviceUniqueIdentifier = json.GetPropertyOrNull("device_unique_identifier")?.GetString(); diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt index a147787b0d..95b72d1fb6 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt @@ -1588,7 +1588,7 @@ namespace Sentry.Protocol public const string Type = "device"; public Device() { } public string? Architecture { get; set; } - public short? BatteryLevel { get; set; } + public float? BatteryLevel { get; set; } public string? BatteryStatus { get; set; } public System.DateTimeOffset? BootTime { get; set; } public string? Brand { get; set; } @@ -1610,7 +1610,7 @@ namespace Sentry.Protocol public string? Name { get; set; } public Sentry.Protocol.DeviceOrientation? Orientation { get; set; } public int? ProcessorCount { get; set; } - public int? ProcessorFrequency { get; set; } + public float? ProcessorFrequency { get; set; } public float? ScreenDensity { get; set; } public int? ScreenDpi { get; set; } public string? ScreenResolution { get; set; } diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt index a147787b0d..95b72d1fb6 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt @@ -1588,7 +1588,7 @@ namespace Sentry.Protocol public const string Type = "device"; public Device() { } public string? Architecture { get; set; } - public short? BatteryLevel { get; set; } + public float? BatteryLevel { get; set; } public string? BatteryStatus { get; set; } public System.DateTimeOffset? BootTime { get; set; } public string? Brand { get; set; } @@ -1610,7 +1610,7 @@ namespace Sentry.Protocol public string? Name { get; set; } public Sentry.Protocol.DeviceOrientation? Orientation { get; set; } public int? ProcessorCount { get; set; } - public int? ProcessorFrequency { get; set; } + public float? ProcessorFrequency { get; set; } public float? ScreenDensity { get; set; } public int? ScreenDpi { get; set; } public string? ScreenResolution { get; set; } diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt index 24c0f6ba78..568ebe00f6 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt @@ -1590,7 +1590,7 @@ namespace Sentry.Protocol public const string Type = "device"; public Device() { } public string? Architecture { get; set; } - public short? BatteryLevel { get; set; } + public float? BatteryLevel { get; set; } public string? BatteryStatus { get; set; } public System.DateTimeOffset? BootTime { get; set; } public string? Brand { get; set; } @@ -1612,7 +1612,7 @@ namespace Sentry.Protocol public string? Name { get; set; } public Sentry.Protocol.DeviceOrientation? Orientation { get; set; } public int? ProcessorCount { get; set; } - public int? ProcessorFrequency { get; set; } + public float? ProcessorFrequency { get; set; } public float? ScreenDensity { get; set; } public int? ScreenDpi { get; set; } public string? ScreenResolution { get; set; } diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt index 26f502c76e..b2873faa6d 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt @@ -1586,7 +1586,7 @@ namespace Sentry.Protocol public const string Type = "device"; public Device() { } public string? Architecture { get; set; } - public short? BatteryLevel { get; set; } + public float? BatteryLevel { get; set; } public string? BatteryStatus { get; set; } public System.DateTimeOffset? BootTime { get; set; } public string? Brand { get; set; } @@ -1608,7 +1608,7 @@ namespace Sentry.Protocol public string? Name { get; set; } public Sentry.Protocol.DeviceOrientation? Orientation { get; set; } public int? ProcessorCount { get; set; } - public int? ProcessorFrequency { get; set; } + public float? ProcessorFrequency { get; set; } public float? ScreenDensity { get; set; } public int? ScreenDpi { get; set; } public string? ScreenResolution { get; set; } diff --git a/test/Sentry.Tests/Protocol/Context/DeviceTests.cs b/test/Sentry.Tests/Protocol/Context/DeviceTests.cs index 61cf719bf4..f891912702 100644 --- a/test/Sentry.Tests/Protocol/Context/DeviceTests.cs +++ b/test/Sentry.Tests/Protocol/Context/DeviceTests.cs @@ -3,6 +3,7 @@ namespace Sentry.Tests.Protocol.Context; public class DeviceTests { private readonly IDiagnosticLogger _testOutputLogger; + private const float Delta = 0.0001f; public DeviceTests(ITestOutputHelper output) { @@ -116,93 +117,28 @@ public void SerializeObject_AllPropertiesSetToNonDefault_SerializesValidObject() [Fact] public void Clone_CopyValues() { - var sut = new Device - { - Name = "name", - Brand = "brand", - Manufacturer = "manufacturer", - Family = "family", - Model = "Model", - ModelId = "ModelId", - Architecture = "Architecture", - BatteryLevel = 2, - IsCharging = false, - Orientation = DeviceOrientation.Portrait, - Simulator = true, - MemorySize = 3, - FreeMemory = 4, - UsableMemory = 5, - LowMemory = false, - StorageSize = 6, - FreeStorage = 7, - ExternalStorageSize = 8, - ExternalFreeStorage = 9, - ScreenResolution = "1x1", - ScreenDensity = 10, - ScreenDpi = 11, - BootTime = DateTimeOffset.UtcNow, - Timezone = TimeZoneInfo.Utc, - IsOnline = false, - ProcessorCount = 8, - CpuDescription = "Intel(R) Core(TM)2 Quad CPU Q6600 @ 2.40GHz", - ProcessorFrequency = 2500, - DeviceType = "Console", - BatteryStatus = "Charging", - DeviceUniqueIdentifier = "d610540d-11d6-4daa-a98c-b71030acae4d", - SupportsVibration = false, - SupportsAccelerometer = true, - SupportsGyroscope = true, - SupportsAudio = true, - SupportsLocationService = true - }; + var sut = TestDevice(); var clone = sut.Clone(); - Assert.Equal(sut.Name, clone.Name); - Assert.Equal(sut.Family, clone.Family); - Assert.Equal(sut.Brand, clone.Brand); - Assert.Equal(sut.Manufacturer, clone.Manufacturer); - Assert.Equal(sut.Model, clone.Model); - Assert.Equal(sut.ModelId, clone.ModelId); - Assert.Equal(sut.Architecture, clone.Architecture); - Assert.Equal(sut.BatteryLevel, clone.BatteryLevel); - Assert.Equal(sut.IsCharging, clone.IsCharging); - Assert.Equal(sut.Orientation, clone.Orientation); - Assert.Equal(sut.Simulator, clone.Simulator); - Assert.Equal(sut.MemorySize, clone.MemorySize); - Assert.Equal(sut.FreeMemory, clone.FreeMemory); - Assert.Equal(sut.LowMemory, clone.LowMemory); - Assert.Equal(sut.UsableMemory, clone.UsableMemory); - Assert.Equal(sut.StorageSize, clone.StorageSize); - Assert.Equal(sut.FreeStorage, clone.FreeStorage); - Assert.Equal(sut.ExternalStorageSize, clone.ExternalStorageSize); - Assert.Equal(sut.ExternalFreeStorage, clone.ExternalFreeStorage); - Assert.Equal(sut.ScreenResolution, clone.ScreenResolution); - Assert.Equal(sut.ScreenDensity, clone.ScreenDensity); - Assert.Equal(sut.ScreenDpi, clone.ScreenDpi); - Assert.Equal(sut.BootTime, clone.BootTime); - Assert.Equal(sut.Timezone, clone.Timezone); - Assert.Equal(sut.IsOnline, clone.IsOnline); - Assert.Equal(sut.ProcessorCount, clone.ProcessorCount); - Assert.Equal(sut.CpuDescription, clone.CpuDescription); - Assert.Equal(sut.ProcessorFrequency, clone.ProcessorFrequency); - Assert.Equal(sut.DeviceType, clone.DeviceType); - Assert.Equal(sut.BatteryStatus, clone.BatteryStatus); - Assert.Equal(sut.DeviceUniqueIdentifier, clone.DeviceUniqueIdentifier); - Assert.Equal(sut.SupportsVibration, clone.SupportsVibration); - Assert.Equal(sut.SupportsAccelerometer, clone.SupportsAccelerometer); - Assert.Equal(sut.SupportsGyroscope, clone.SupportsGyroscope); - Assert.Equal(sut.SupportsAudio, clone.SupportsAudio); - Assert.Equal(sut.SupportsLocationService, clone.SupportsLocationService); + AssertAreEqual(sut, clone); } - [Theory] - [MemberData(nameof(TestCases))] - public void SerializeObject_TestCase_SerializesAsExpected((Device device, string serialized) @case) + [Fact] + public void WriteTo_FromJson_Symmetric() { - var actual = @case.device.ToJsonString(_testOutputLogger); + // Arrange + var sut = TestDevice(); - Assert.Equal(@case.serialized, actual); + var json = sut.ToJsonString(); + using var document = JsonDocument.Parse(json); + var jsonElement = document.RootElement; + + // Act + var result = Device.FromJson(jsonElement); + + // Assert + AssertAreEqual(result, sut); } [Fact] @@ -220,6 +156,43 @@ public void FromJson_NonSystemTimeZone_NoException() device.Timezone?.DisplayName.Should().Be("tz_name"); } + [Fact] + public void FromJson_BatteryLevelFloat() + { + // Arrange + const string json = """{"type":"device","battery_level":1.5}"""; + + // Act + var device = Json.Parse(json, Device.FromJson); + + // Assert + device.BatteryLevel.Should().NotBeNull(); + device.BatteryLevel?.Should().BeApproximately(1.5f, Delta); + } + + [Fact] + public void FromJson_ProcessorFrequencyFloat() + { + // Arrange + const string json = """{"type":"device","processor_frequency":2500.3}"""; + + // Act + var device = Json.Parse(json, Device.FromJson); + + // Assert + device.ProcessorFrequency.Should().NotBeNull(); + device.ProcessorFrequency?.Should().BeApproximately(2500.3f, Delta); + } + + [Theory] + [MemberData(nameof(TestCases))] + public void SerializeObject_TestCase_SerializesAsExpected((Device device, string serialized) @case) + { + var actual = @case.device.ToJsonString(_testOutputLogger); + + Assert.Equal(@case.serialized, actual); + } + public static IEnumerable TestCases() { yield return new object[] { (new Device(), """{"type":"device"}""") }; @@ -232,6 +205,7 @@ public static IEnumerable TestCases() yield return new object[] { (new Device { ModelId = "some model id" }, """{"type":"device","model_id":"some model id"}""") }; yield return new object[] { (new Device { Architecture = "some arch" }, """{"type":"device","arch":"some arch"}""") }; yield return new object[] { (new Device { BatteryLevel = 1 }, """{"type":"device","battery_level":1}""") }; + yield return new object[] { (new Device { BatteryLevel = 1.5f }, """{"type":"device","battery_level":1.5}""") }; yield return new object[] { (new Device { IsCharging = true }, """{"type":"device","charging":true}""") }; yield return new object[] { (new Device { IsOnline = true }, """{"type":"device","online":true}""") }; yield return new object[] { (new Device { Simulator = false }, """{"type":"device","simulator":false}""") }; @@ -252,6 +226,7 @@ public static IEnumerable TestCases() yield return new object[] { (new Device { ProcessorCount = 8 }, """{"type":"device","processor_count":8}""") }; yield return new object[] { (new Device { CpuDescription = "Intel(R) Core(TM)2 Quad CPU Q6600 @ 2.40GHz" }, """{"type":"device","cpu_description":"Intel(R) Core(TM)2 Quad CPU Q6600 @ 2.40GHz"}""") }; yield return new object[] { (new Device { ProcessorFrequency = 2500 }, """{"type":"device","processor_frequency":2500}""") }; + yield return new object[] { (new Device { ProcessorFrequency = 2500.5f }, """{"type":"device","processor_frequency":2500.5}""") }; yield return new object[] { (new Device { DeviceType = "Handheld" }, """{"type":"device","device_type":"Handheld"}""") }; yield return new object[] { (new Device { BatteryStatus = "Charging" }, """{"type":"device","battery_status":"Charging"}""") }; yield return new object[] { (new Device { DeviceUniqueIdentifier = "d610540d-11d6-4daa-a98c-b71030acae4d" }, """{"type":"device","device_unique_identifier":"d610540d-11d6-4daa-a98c-b71030acae4d"}""") }; @@ -261,4 +236,90 @@ public static IEnumerable TestCases() yield return new object[] { (new Device { SupportsAudio = true }, """{"type":"device","supports_audio":true}""") }; yield return new object[] { (new Device { SupportsLocationService = true }, """{"type":"device","supports_location_service":true}""") }; } + + private static Device TestDevice() + { + return new Device + { + Name = "name", + Brand = "brand", + Manufacturer = "manufacturer", + Family = "family", + Model = "Model", + ModelId = "ModelId", + Architecture = "Architecture", + BatteryLevel = 2, + IsCharging = false, + Orientation = DeviceOrientation.Portrait, + Simulator = true, + MemorySize = 3, + FreeMemory = 4, + UsableMemory = 5, + LowMemory = false, + StorageSize = 6, + FreeStorage = 7, + ExternalStorageSize = 8, + ExternalFreeStorage = 9, + ScreenResolution = "1x1", + ScreenDensity = 10, + ScreenDpi = 11, + BootTime = DateTimeOffset.UtcNow, + Timezone = TimeZoneInfo.Utc, + IsOnline = false, + ProcessorCount = 8, + CpuDescription = "Intel(R) Core(TM)2 Quad CPU Q6600 @ 2.40GHz", + ProcessorFrequency = 2500, + DeviceType = "Console", + BatteryStatus = "Charging", + DeviceUniqueIdentifier = "d610540d-11d6-4daa-a98c-b71030acae4d", + SupportsVibration = false, + SupportsAccelerometer = true, + SupportsGyroscope = true, + SupportsAudio = true, + SupportsLocationService = true + }; + } + + private static void AssertAreEqual(Device actual, Device expected) + { + using (new AssertionScope()) + { + actual.Name.Should().Be(expected.Name); + actual.Manufacturer.Should().Be(expected.Manufacturer); + actual.Brand.Should().Be(expected.Brand); + actual.Architecture.Should().Be(expected.Architecture); + actual.BatteryLevel.Should().BeApproximately(expected.BatteryLevel, Delta); + actual.IsCharging.Should().Be(expected.IsCharging); + actual.IsOnline.Should().Be(expected.IsOnline); + actual.BootTime.Should().Be(expected.BootTime); + actual.ExternalFreeStorage.Should().Be(expected.ExternalFreeStorage); + actual.ExternalStorageSize.Should().Be(expected.ExternalStorageSize); + actual.ScreenResolution.Should().Be(expected.ScreenResolution); + actual.ScreenDensity.Should().Be(expected.ScreenDensity); + actual.ScreenDpi.Should().Be(expected.ScreenDpi); + actual.Family.Should().Be(expected.Family); + actual.FreeMemory.Should().Be(expected.FreeMemory); + actual.FreeStorage.Should().Be(expected.FreeStorage); + actual.MemorySize.Should().Be(expected.MemorySize); + actual.Model.Should().Be(expected.Model); + actual.ModelId.Should().Be(expected.ModelId); + actual.Orientation.Should().Be(expected.Orientation); + actual.Simulator.Should().Be(expected.Simulator); + actual.StorageSize.Should().Be(expected.StorageSize); + actual.Timezone.Should().Be(expected.Timezone); + actual.UsableMemory.Should().Be(expected.UsableMemory); + actual.LowMemory.Should().Be(expected.LowMemory); + actual.ProcessorCount.Should().Be(expected.ProcessorCount); + actual.CpuDescription.Should().Be(expected.CpuDescription); + actual.ProcessorFrequency.Should().BeApproximately(expected.ProcessorFrequency, Delta); + actual.SupportsVibration.Should().Be(expected.SupportsVibration); + actual.DeviceType.Should().Be(expected.DeviceType); + actual.BatteryStatus.Should().Be(expected.BatteryStatus); + actual.DeviceUniqueIdentifier.Should().Be(expected.DeviceUniqueIdentifier); + actual.SupportsAccelerometer.Should().Be(expected.SupportsAccelerometer); + actual.SupportsGyroscope.Should().Be(expected.SupportsGyroscope); + actual.SupportsAudio.Should().Be(expected.SupportsAudio); + actual.SupportsLocationService.Should().Be(expected.SupportsLocationService); + } + } } diff --git a/test/Sentry.Tests/Protocol/DeviceTests.cs b/test/Sentry.Tests/Protocol/DeviceTests.cs deleted file mode 100644 index cadb06d4f1..0000000000 --- a/test/Sentry.Tests/Protocol/DeviceTests.cs +++ /dev/null @@ -1,140 +0,0 @@ -namespace Sentry.Tests.Protocol; - -public class DeviceTests -{ - [Fact] - public void Clone_CopyValues() - { - // Arrange - var sut = TestDevice(); - - // Act - var result = sut.Clone(); - - // Assert - AssertAreEqual(result, sut); - } - - [Fact] - public void WriteTo_FromJson_Symmetric() - { - // Arrange - var sut = TestDevice(); - - var json = sut.ToJsonString(); - using var document = JsonDocument.Parse(json); - var jsonElement = document.RootElement; - - // Act - var result = Device.FromJson(jsonElement); - - // Assert - AssertAreEqual(result, sut); - } - - [Fact] - public void FromJson_JavaTypes_CastCorrectly() - { - // Arrange - var sut = TestDevice(); - - var json = sut.ToJsonString() - // In the Java SDK, the Processor Frequency is stored as a Double - .Replace(@"""processor_frequency"": 12", @"""processor_frequency"": 12.0"); - - using var document = JsonDocument.Parse(json); - var jsonElement = document.RootElement; - - // Act - var result = Device.FromJson(jsonElement); - - // Assert - AssertAreEqual(result, sut); - } - - private static Device TestDevice() - { - return new Device - { - Name = "TestName", - Manufacturer = "TestManufacturer", - Brand = "TestBrand", - Architecture = "TestArchitecture", - BatteryLevel = 1, - IsCharging = true, - IsOnline = true, - BootTime = new DateTimeOffset(2001, 06, 15, 12, 30, 0, TimeSpan.Zero), - ExternalFreeStorage = 2, - ExternalStorageSize = 3, - ScreenResolution = "800x600", - ScreenDensity = 1.2f, - ScreenDpi = 4, - Family = "TestFamily", - FreeMemory = 5, - FreeStorage = 6, - MemorySize = 7, - Model = "TestModel", - ModelId = "TestModelId", - Orientation = DeviceOrientation.Landscape, - Simulator = true, - StorageSize = 8, - Timezone = TimeZoneInfo.Utc, - UsableMemory = 9, - LowMemory = true, - ProcessorCount = 11, - CpuDescription = "TestCpuDescription", - ProcessorFrequency = 12, - SupportsVibration = true, - DeviceType = "TestDeviceType", - BatteryStatus = "TestBatteryStatus", - DeviceUniqueIdentifier = "TestDeviceUniqueIdentifier", - SupportsAccelerometer = true, - SupportsGyroscope = true, - SupportsAudio = true, - SupportsLocationService = true - }; - } - - private static void AssertAreEqual(Device actual, Device expected) - { - using (new AssertionScope()) - { - actual.Name.Should().Be(expected.Name); - actual.Manufacturer.Should().Be(expected.Manufacturer); - actual.Brand.Should().Be(expected.Brand); - actual.Architecture.Should().Be(expected.Architecture); - actual.BatteryLevel.Should().Be(expected.BatteryLevel); - actual.IsCharging.Should().Be(expected.IsCharging); - actual.IsOnline.Should().Be(expected.IsOnline); - actual.BootTime.Should().Be(expected.BootTime); - actual.ExternalFreeStorage.Should().Be(expected.ExternalFreeStorage); - actual.ExternalStorageSize.Should().Be(expected.ExternalStorageSize); - actual.ScreenResolution.Should().Be(expected.ScreenResolution); - actual.ScreenDensity.Should().Be(expected.ScreenDensity); - actual.ScreenDpi.Should().Be(expected.ScreenDpi); - actual.Family.Should().Be(expected.Family); - actual.FreeMemory.Should().Be(expected.FreeMemory); - actual.FreeStorage.Should().Be(expected.FreeStorage); - actual.MemorySize.Should().Be(expected.MemorySize); - actual.Model.Should().Be(expected.Model); - actual.ModelId.Should().Be(expected.ModelId); - actual.Orientation.Should().Be(expected.Orientation); - actual.Simulator.Should().Be(expected.Simulator); - actual.StorageSize.Should().Be(expected.StorageSize); - actual.Timezone.Should().Be(expected.Timezone); - actual.UsableMemory.Should().Be(expected.UsableMemory); - actual.LowMemory.Should().Be(expected.LowMemory); - actual.ProcessorCount.Should().Be(expected.ProcessorCount); - actual.CpuDescription.Should().Be(expected.CpuDescription); - actual.ProcessorFrequency.Should().Be(expected.ProcessorFrequency); - actual.SupportsVibration.Should().Be(expected.SupportsVibration); - actual.DeviceType.Should().Be(expected.DeviceType); - actual.BatteryStatus.Should().Be(expected.BatteryStatus); - actual.DeviceUniqueIdentifier.Should().Be(expected.DeviceUniqueIdentifier); - actual.SupportsAccelerometer.Should().Be(expected.SupportsAccelerometer); - actual.SupportsGyroscope.Should().Be(expected.SupportsGyroscope); - actual.SupportsAudio.Should().Be(expected.SupportsAudio); - actual.SupportsLocationService.Should().Be(expected.SupportsLocationService); - } - } -} From edd65644ce84ccab7e7ad12000375a905e96936c Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Tue, 27 Aug 2024 22:14:00 +1200 Subject: [PATCH 005/363] Removed SentryOptins.EnableTracing (#3569) --- CHANGELOG.md | 1 + src/Sentry/BindableSentryOptions.cs | 3 - src/Sentry/Internal/Hub.cs | 8 +- src/Sentry/Platforms/Android/SentrySdk.cs | 4 +- src/Sentry/Platforms/Cocoa/SentrySdk.cs | 7 -- src/Sentry/SentryOptions.cs | 54 +-------- .../SentryLoggingOptionsSetupTests.cs | 9 -- ...piApprovalTests.Run.DotNet6_0.verified.txt | 2 - ...piApprovalTests.Run.DotNet7_0.verified.txt | 2 - ...piApprovalTests.Run.DotNet8_0.verified.txt | 2 - .../ApiApprovalTests.Run.Net4_8.verified.txt | 2 - test/Sentry.Tests/HubTests.cs | 34 ------ test/Sentry.Tests/SentryOptionsTests.cs | 114 ------------------ 13 files changed, 10 insertions(+), 232 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9544743c93..dab99e27b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - The `SentryUser.Segment` property has been deprecated. Consider sending this as a tag or additional data instead ([#3563](https://github.com/getsentry/sentry-dotnet/pull/3563)) - The ITraceContext now includes an [Origin](https://develop.sentry.dev/sdk/telemetry/traces/trace-origin/), which is set automatically and is primarily used internally by the Sentry server ([#3564](https://github.com/getsentry/sentry-dotnet/pull/3564)) - `Device.BatteryLevel` and `Device.ProcessorFrequency` are now stored as floats rather than ints, to align with the Cocoa and Java SDKs ([#3567](https://github.com/getsentry/sentry-dotnet/pull/3567)) +- `SentryOptions.EnableTracing` has been removed. Instead, tracing should be enabled or disabled by setting the `SentryOptions.TracesSampleRate` or by using `SentryOptions.TracesSampler` to configure a sampling function ([#3569](https://github.com/getsentry/sentry-dotnet/pull/3569)) ## 4.10.2 diff --git a/src/Sentry/BindableSentryOptions.cs b/src/Sentry/BindableSentryOptions.cs index 4630f053c0..cf3816ef1b 100644 --- a/src/Sentry/BindableSentryOptions.cs +++ b/src/Sentry/BindableSentryOptions.cs @@ -83,9 +83,6 @@ public void ApplyTo(SentryOptions options) options.FailedRequestTargets = FailedRequestTargets?.Select(s => new SubstringOrRegexPattern(s)).ToList() ?? options.FailedRequestTargets; options.InitCacheFlushTimeout = InitCacheFlushTimeout ?? options.InitCacheFlushTimeout; options.DefaultTags = DefaultTags ?? options.DefaultTags; -#pragma warning disable CS0618 // Type or member is obsolete - options.EnableTracing = EnableTracing ?? options.EnableTracing; -#pragma warning restore CS0618 // Type or member is obsolete options.TracesSampleRate = TracesSampleRate ?? options.TracesSampleRate; options.ProfilesSampleRate = ProfilesSampleRate ?? options.ProfilesSampleRate; options.TracePropagationTargets = TracePropagationTargets?.Select(s => new SubstringOrRegexPattern(s)).ToList() ?? options.TracePropagationTargets; diff --git a/src/Sentry/Internal/Hub.cs b/src/Sentry/Internal/Hub.cs index 151150514a..240cc0f60f 100644 --- a/src/Sentry/Internal/Hub.cs +++ b/src/Sentry/Internal/Hub.cs @@ -129,9 +129,7 @@ internal ITransactionTracer StartTransaction( // Additionally, we will always sample out if tracing is explicitly disabled. // Do not invoke the TracesSampler, evaluate the TracesSampleRate, and override any sampling decision // that may have been already set (i.e.: from a sentry-trace header). -#pragma warning disable CS0618 // Type or member is obsolete - if (!IsEnabled || _options.EnableTracing is false) -#pragma warning restore CS0618 // Type or member is obsolete + if (!IsEnabled) { transaction.IsSampled = false; transaction.SampleRate = 0.0; @@ -156,9 +154,7 @@ internal ITransactionTracer StartTransaction( // Random sampling runs only if the sampling decision hasn't been made already. if (transaction.IsSampled == null) { -#pragma warning disable CS0618 // Type or member is obsolete - var sampleRate = _options.TracesSampleRate ?? (_options.EnableTracing is true ? 1.0 : 0.0); -#pragma warning restore CS0618 // Type or member is obsolete + var sampleRate = _options.TracesSampleRate ?? 0.0; transaction.IsSampled = _randomValuesFactory.NextBool(sampleRate); transaction.SampleRate = sampleRate; } diff --git a/src/Sentry/Platforms/Android/SentrySdk.cs b/src/Sentry/Platforms/Android/SentrySdk.cs index a346f581b3..5c12bdaec9 100644 --- a/src/Sentry/Platforms/Android/SentrySdk.cs +++ b/src/Sentry/Platforms/Android/SentrySdk.cs @@ -89,9 +89,7 @@ private static void InitSentryAndroidSdk(SentryOptions options) // These options we have behind feature flags if (options is { IsPerformanceMonitoringEnabled: true, Native.EnableTracing: true }) { -#pragma warning disable CS0618 // Type or member is obsolete - o.EnableTracing = (JavaBoolean?)options.EnableTracing; -#pragma warning restore CS0618 // Type or member is obsolete + o.EnableTracing = null; o.TracesSampleRate = (JavaDouble?)options.TracesSampleRate; if (options.TracesSampler is { } tracesSampler) diff --git a/src/Sentry/Platforms/Cocoa/SentrySdk.cs b/src/Sentry/Platforms/Cocoa/SentrySdk.cs index ac075af7a6..5adc96c249 100644 --- a/src/Sentry/Platforms/Cocoa/SentrySdk.cs +++ b/src/Sentry/Platforms/Cocoa/SentrySdk.cs @@ -75,13 +75,6 @@ private static void InitSentryCocoaSdk(SentryOptions options) // These options we have behind feature flags if (options is { IsPerformanceMonitoringEnabled: true, Native.EnableTracing: true }) { -#pragma warning disable CS0618 // Type or member is obsolete - if (options.EnableTracing != null) - { - nativeOptions.EnableTracing = options.EnableTracing.Value; - } -#pragma warning restore CS0618 // Type or member is obsolete - nativeOptions.TracesSampleRate = options.TracesSampleRate; if (options.TracesSampler is { } tracesSampler) diff --git a/src/Sentry/SentryOptions.cs b/src/Sentry/SentryOptions.cs index d354d253e3..3480207ab5 100644 --- a/src/Sentry/SentryOptions.cs +++ b/src/Sentry/SentryOptions.cs @@ -753,56 +753,16 @@ public Dictionary DefaultTags } /// - /// Indicates whether the performance feature is enabled, via any combination of - /// , , or . + /// Indicates whether the performance feature is enabled, via either + /// or . /// -#pragma warning disable CS0618 // Type or member is obsolete - internal bool IsPerformanceMonitoringEnabled => EnableTracing switch - { - false => false, - null => TracesSampler is not null || TracesSampleRate is > 0.0, - true => TracesSampler is not null || TracesSampleRate is > 0.0 or null - }; -#pragma warning restore CS0618 // Type or member is obsolete + internal bool IsPerformanceMonitoringEnabled => TracesSampler is not null || TracesSampleRate is > 0.0; /// - /// Indicates whether profiling is enabled, via any combination of - /// , , or . + /// Indicates whether profiling is enabled, via , or /// internal bool IsProfilingEnabled => IsPerformanceMonitoringEnabled && ProfilesSampleRate > 0.0; - /// - /// Simplified option for enabling or disabling tracing. - /// - /// - /// Value - /// Effect - /// - /// - /// true - /// - /// Tracing is enabled. or will be used if set, - /// or 100% sample rate will be used otherwise. - /// - /// - /// - /// false - /// - /// Tracing is disabled, regardless of or . - /// - /// - /// - /// null - /// - /// The default setting. - /// Tracing is enabled only if or are set. - /// - /// - /// - /// - [Obsolete("Use TracesSampleRate or TracesSampler instead")] - public bool? EnableTracing { get; set; } - private double? _tracesSampleRate; /// @@ -815,8 +775,7 @@ public Dictionary DefaultTags /// /// >= 0.0 and <=1.0 /// - /// A custom sample rate is used unless is false, - /// or unless overriden by a function. + /// A custom sample rate is used unless overriden by a function. /// Values outside of this range are invalid. /// /// @@ -824,8 +783,7 @@ public Dictionary DefaultTags /// null /// /// The default setting. - /// The tracing sample rate is determined by the property, - /// unless overriden by a function. + /// The tracing sample rate is determined by the function. /// /// /// diff --git a/test/Sentry.Extensions.Logging.Tests/SentryLoggingOptionsSetupTests.cs b/test/Sentry.Extensions.Logging.Tests/SentryLoggingOptionsSetupTests.cs index cb20a8014e..d6ffa6ac1e 100644 --- a/test/Sentry.Extensions.Logging.Tests/SentryLoggingOptionsSetupTests.cs +++ b/test/Sentry.Extensions.Logging.Tests/SentryLoggingOptionsSetupTests.cs @@ -43,9 +43,6 @@ public void Configure_BindsConfigurationToOptions() FailedRequestTargets = new List { "target1", "target2" }, InitCacheFlushTimeout = TimeSpan.FromSeconds(27), // DefaultTags = Dictionary, -#pragma warning disable CS0618 // Type or member is obsolete - EnableTracing = true, -#pragma warning restore CS0618 // Type or member is obsolete TracesSampleRate = 0.8f, TracePropagationTargets = new List { "target3", "target4" }, StackTraceMode = StackTraceMode.Enhanced, @@ -96,9 +93,6 @@ public void Configure_BindsConfigurationToOptions() ["FailedRequestTargets:1"] = expected.FailedRequestTargets.Last().ToString(), ["InitCacheFlushTimeout"] = expected.InitCacheFlushTimeout.ToString(), ["DefaultTags"] = expected.DefaultTags.ToString(), -#pragma warning disable CS0618 // Type or member is obsolete - ["EnableTracing"] = expected.EnableTracing.ToString(), -#pragma warning restore CS0618 // Type or member is obsolete ["TracesSampleRate"] = expected.TracesSampleRate.Value.ToString(CultureInfo.InvariantCulture), ["TracePropagationTargets:0"] = expected.TracePropagationTargets.First().ToString(), ["TracePropagationTargets:1"] = expected.TracePropagationTargets.Last().ToString(), @@ -156,9 +150,6 @@ public void Configure_BindsConfigurationToOptions() actual.CaptureFailedRequests.Should().Be(expected.CaptureFailedRequests); actual.FailedRequestTargets.Should().BeEquivalentTo(expected.FailedRequestTargets); actual.InitCacheFlushTimeout.Should().Be(expected.InitCacheFlushTimeout); -#pragma warning disable CS0618 // Type or member is obsolete - actual.EnableTracing.Should().Be(expected.EnableTracing); -#pragma warning restore CS0618 // Type or member is obsolete actual.TracesSampleRate.Should().Be(expected.TracesSampleRate); actual.TracePropagationTargets.Should().BeEquivalentTo(expected.TracePropagationTargets); actual.StackTraceMode.Should().Be(expected.StackTraceMode); diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt index 95b72d1fb6..2fb50223fa 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt @@ -668,8 +668,6 @@ namespace Sentry public string? Dsn { get; set; } public bool EnableScopeSync { get; set; } public bool EnableSpotlight { get; set; } - [System.Obsolete("Use TracesSampleRate or TracesSampler instead")] - public bool? EnableTracing { get; set; } public string? Environment { get; set; } public Sentry.ExperimentalMetricsOptions? ExperimentalMetrics { get; set; } public System.Collections.Generic.IList FailedRequestStatusCodes { get; set; } diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt index 95b72d1fb6..2fb50223fa 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt @@ -668,8 +668,6 @@ namespace Sentry public string? Dsn { get; set; } public bool EnableScopeSync { get; set; } public bool EnableSpotlight { get; set; } - [System.Obsolete("Use TracesSampleRate or TracesSampler instead")] - public bool? EnableTracing { get; set; } public string? Environment { get; set; } public Sentry.ExperimentalMetricsOptions? ExperimentalMetrics { get; set; } public System.Collections.Generic.IList FailedRequestStatusCodes { get; set; } diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt index 568ebe00f6..f593dedb0a 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt @@ -669,8 +669,6 @@ namespace Sentry public string? Dsn { get; set; } public bool EnableScopeSync { get; set; } public bool EnableSpotlight { get; set; } - [System.Obsolete("Use TracesSampleRate or TracesSampler instead")] - public bool? EnableTracing { get; set; } public string? Environment { get; set; } public Sentry.ExperimentalMetricsOptions? ExperimentalMetrics { get; set; } public System.Collections.Generic.IList FailedRequestStatusCodes { get; set; } diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt index b2873faa6d..84c37bccc2 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt @@ -666,8 +666,6 @@ namespace Sentry public string? Dsn { get; set; } public bool EnableScopeSync { get; set; } public bool EnableSpotlight { get; set; } - [System.Obsolete("Use TracesSampleRate or TracesSampler instead")] - public bool? EnableTracing { get; set; } public string? Environment { get; set; } public Sentry.ExperimentalMetricsOptions? ExperimentalMetrics { get; set; } public System.Collections.Generic.IList FailedRequestStatusCodes { get; set; } diff --git a/test/Sentry.Tests/HubTests.cs b/test/Sentry.Tests/HubTests.cs index f285747a82..0dd5eddda8 100644 --- a/test/Sentry.Tests/HubTests.cs +++ b/test/Sentry.Tests/HubTests.cs @@ -601,23 +601,6 @@ public void StartTransaction_EnableTracing_SampledIn() transaction.IsSampled.Should().BeTrue(); } - [Fact] - public void StartTransaction_DisableTracing_SampledOut() - { - // Arrange - _fixture.Options.TracesSampleRate = 1.0; -#pragma warning disable CS0618 // Type or member is obsolete - _fixture.Options.EnableTracing = false; -#pragma warning restore CS0618 // Type or member is obsolete - var hub = _fixture.GetSut(); - - // Act - var transaction = hub.StartTransaction("name", "operation"); - - // Assert - transaction.IsSampled.Should().BeFalse(); - } - [Fact] public void StartTransaction_SameInstrumenter_SampledIn() { @@ -673,23 +656,6 @@ public void StartTransaction_EnableTracing_Sampler_SampledIn() transaction.IsSampled.Should().BeTrue(); } - [Fact] - public void StartTransaction_DisableTracing_Sampler_SampledOut() - { - // Arrange - _fixture.Options.TracesSampler = _ => 1.0; -#pragma warning disable CS0618 // Type or member is obsolete - _fixture.Options.EnableTracing = false; -#pragma warning restore CS0618 // Type or member is obsolete - var hub = _fixture.GetSut(); - - // Act - var transaction = hub.StartTransaction("name", "operation"); - - // Assert - transaction.IsSampled.Should().BeFalse(); - } - [Theory] [InlineData(0.25f)] [InlineData(0.50f)] diff --git a/test/Sentry.Tests/SentryOptionsTests.cs b/test/Sentry.Tests/SentryOptionsTests.cs index 1f5789f45d..d9e8194039 100644 --- a/test/Sentry.Tests/SentryOptionsTests.cs +++ b/test/Sentry.Tests/SentryOptionsTests.cs @@ -32,15 +32,6 @@ public void AttachStackTrace_ByDefault_True() Assert.True(sut.AttachStacktrace); } - [Fact] - public void EnableTracing_Default_Null() - { - var sut = new SentryOptions(); -#pragma warning disable CS0618 // Type or member is obsolete - Assert.Null(sut.EnableTracing); -#pragma warning restore CS0618 // Type or member is obsolete - } - [Fact] public void TracesSampleRate_Default_Null() { @@ -62,32 +53,6 @@ public void IsPerformanceMonitoringEnabled_Default_False() Assert.False(sut.IsPerformanceMonitoringEnabled); } - [Fact] - public void IsPerformanceMonitoringEnabled_EnableTracing_True() - { - var sut = new SentryOptions - { -#pragma warning disable CS0618 // Type or member is obsolete - EnableTracing = true -#pragma warning restore CS0618 // Type or member is obsolete - }; - - Assert.True(sut.IsPerformanceMonitoringEnabled); - } - - [Fact] - public void IsPerformanceMonitoringEnabled_EnableTracing_False() - { - var sut = new SentryOptions - { -#pragma warning disable CS0618 // Type or member is obsolete - EnableTracing = false -#pragma warning restore CS0618 // Type or member is obsolete - }; - - Assert.False(sut.IsPerformanceMonitoringEnabled); - } - [Fact] public void IsPerformanceMonitoringEnabled_TracesSampleRate_Zero() { @@ -137,57 +102,6 @@ public void IsPerformanceMonitoringEnabled_TracesSampler_Provided() Assert.True(sut.IsPerformanceMonitoringEnabled); } - [Fact] - public void IsPerformanceMonitoringEnabled_EnableTracing_True_TracesSampleRate_Zero() - { - // Edge Case: - // Tracing enabled, but sample rate set to zero, and no sampler function, should be treated as disabled. - - var sut = new SentryOptions - { -#pragma warning disable CS0618 // Type or member is obsolete - EnableTracing = true, -#pragma warning restore CS0618 // Type or member is obsolete - TracesSampleRate = 0.0 - }; - - Assert.False(sut.IsPerformanceMonitoringEnabled); - } - - [Fact] - public void IsPerformanceMonitoringEnabled_EnableTracing_False_TracesSampleRate_One() - { - // Edge Case: - // Tracing disabled should be treated as disabled regardless of sample rate set. - - var sut = new SentryOptions - { -#pragma warning disable CS0618 // Type or member is obsolete - EnableTracing = false, -#pragma warning restore CS0618 // Type or member is obsolete - TracesSampleRate = 1.0 - }; - - Assert.False(sut.IsPerformanceMonitoringEnabled); - } - - [Fact] - public void IsPerformanceMonitoringEnabled_EnableTracing_False_TracesSampler_Provided() - { - // Edge Case: - // Tracing disabled should be treated as disabled regardless of sampler function set. - - var sut = new SentryOptions - { -#pragma warning disable CS0618 // Type or member is obsolete - EnableTracing = false, -#pragma warning restore CS0618 // Type or member is obsolete - TracesSampler = _ => null - }; - - Assert.False(sut.IsPerformanceMonitoringEnabled); - } - [Fact] public void ProfilesSampleRate_Default_Null() { @@ -202,34 +116,6 @@ public void IsProfilingEnabled_Default_False() Assert.False(sut.IsProfilingEnabled); } - [Fact] - public void IsProfilingEnabled_EnableTracing_True() - { - var sut = new SentryOptions - { -#pragma warning disable CS0618 // Type or member is obsolete - EnableTracing = true, -#pragma warning restore CS0618 // Type or member is obsolete - ProfilesSampleRate = double.Epsilon - }; - - Assert.True(sut.IsProfilingEnabled); - } - - [Fact] - public void IsProfilingEnabled_EnableTracing_False() - { - var sut = new SentryOptions - { -#pragma warning disable CS0618 // Type or member is obsolete - EnableTracing = false, -#pragma warning restore CS0618 // Type or member is obsolete - ProfilesSampleRate = double.Epsilon - }; - - Assert.False(sut.IsProfilingEnabled); - } - [Fact] public void IsProfilingEnabled_TracesSampleRate_Zero() { From 708c2e974a924ba62ae379b526af97d58b1d010a Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Wed, 4 Sep 2024 09:29:16 +1200 Subject: [PATCH 006/363] Added SubstringOrPatternMatcher (#3566) --- CHANGELOG.md | 1 + src/Sentry/BindableSentryOptions.cs | 6 +- src/Sentry/BuiltInSystemDiagnosticsMeters.cs | 82 ++++----- src/Sentry/ExperimentalMetricsOptions.cs | 14 +- .../DelimitedPrefixOrPatternMatcher.cs | 6 +- src/Sentry/Internal/IStringOrRegexMatcher.cs | 3 + src/Sentry/Internal/PrefixOrPatternMatcher.cs | 2 +- src/Sentry/Internal/StringOrRegex.cs | 82 --------- .../SystemDiagnosticsMetricsListener.cs | 4 +- src/Sentry/Scope.cs | 2 +- src/Sentry/SentryFailedRequestHandler.cs | 2 +- src/Sentry/SentryMessageHandler.cs | 2 +- src/Sentry/SentryOptions.cs | 20 +-- src/Sentry/StringOrRegex.cs | 128 ++++++++++++++ src/Sentry/SubstringOrPatternMatcher.cs | 19 +++ src/Sentry/SubstringOrRegexPattern.cs | 160 ------------------ .../SentryLoggingOptionsSetupTests.cs | 10 +- ...piApprovalTests.Run.DotNet6_0.verified.txt | 48 +++--- ...piApprovalTests.Run.DotNet7_0.verified.txt | 48 +++--- ...piApprovalTests.Run.DotNet8_0.verified.txt | 48 +++--- .../ApiApprovalTests.Run.Net4_8.verified.txt | 48 +++--- .../BuiltInSystemDiagnosticsMetersTests.cs | 8 +- .../Sentry.Tests/FailedRequestTargetsTests.cs | 28 +-- test/Sentry.Tests/ScopeTests.cs | 2 +- ...tryGraphQlHttpFailedRequestHandlerTests.cs | 2 +- .../SentryHttpFailedRequestHandlerTests.cs | 2 +- .../SentryHttpMessageHandlerTests.cs | 8 +- .../{Internals => }/StringOrRegexTests.cs | 6 +- .../SubstringOrPatternMatcherTests.cs | 76 +++++++++ .../SubstringOrRegexPatternTests.cs | 110 ------------ .../TracePropagationTargetTests.cs | 36 ++-- 31 files changed, 446 insertions(+), 567 deletions(-) delete mode 100644 src/Sentry/Internal/StringOrRegex.cs create mode 100644 src/Sentry/StringOrRegex.cs create mode 100644 src/Sentry/SubstringOrPatternMatcher.cs delete mode 100644 src/Sentry/SubstringOrRegexPattern.cs rename test/Sentry.Tests/{Internals => }/StringOrRegexTests.cs (78%) create mode 100644 test/Sentry.Tests/SubstringOrPatternMatcherTests.cs delete mode 100644 test/Sentry.Tests/SubstringOrRegexPatternTests.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index dab99e27b5..58a135506b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - The ITraceContext now includes an [Origin](https://develop.sentry.dev/sdk/telemetry/traces/trace-origin/), which is set automatically and is primarily used internally by the Sentry server ([#3564](https://github.com/getsentry/sentry-dotnet/pull/3564)) - `Device.BatteryLevel` and `Device.ProcessorFrequency` are now stored as floats rather than ints, to align with the Cocoa and Java SDKs ([#3567](https://github.com/getsentry/sentry-dotnet/pull/3567)) - `SentryOptions.EnableTracing` has been removed. Instead, tracing should be enabled or disabled by setting the `SentryOptions.TracesSampleRate` or by using `SentryOptions.TracesSampler` to configure a sampling function ([#3569](https://github.com/getsentry/sentry-dotnet/pull/3569)) +- The `FailedRequestTargets`, `TagFilters` and `TracePropagationTargets` options have all been changed from `SubstringOrRegexPattern` to `IList` ([#3566](https://github.com/getsentry/sentry-dotnet/pull/3566)) ## 4.10.2 diff --git a/src/Sentry/BindableSentryOptions.cs b/src/Sentry/BindableSentryOptions.cs index cf3816ef1b..53f0c076aa 100644 --- a/src/Sentry/BindableSentryOptions.cs +++ b/src/Sentry/BindableSentryOptions.cs @@ -55,7 +55,7 @@ public void ApplyTo(SentryOptions options) { options.IsGlobalModeEnabled = IsGlobalModeEnabled ?? options.IsGlobalModeEnabled; options.EnableScopeSync = EnableScopeSync ?? options.EnableScopeSync; - options.TagFilters = TagFilters?.Select(s => new SubstringOrRegexPattern(s)).ToList() ?? options.TagFilters; + options.TagFilters = TagFilters?.Select(s => new StringOrRegex(s)).ToList() ?? options.TagFilters; options.SendDefaultPii = SendDefaultPii ?? options.SendDefaultPii; options.IsEnvironmentUser = IsEnvironmentUser ?? options.IsEnvironmentUser; options.ServerName = ServerName ?? options.ServerName; @@ -80,12 +80,12 @@ public void ApplyTo(SentryOptions options) options.DeduplicateMode = DeduplicateMode ?? options.DeduplicateMode; options.CacheDirectoryPath = CacheDirectoryPath ?? options.CacheDirectoryPath; options.CaptureFailedRequests = CaptureFailedRequests ?? options.CaptureFailedRequests; - options.FailedRequestTargets = FailedRequestTargets?.Select(s => new SubstringOrRegexPattern(s)).ToList() ?? options.FailedRequestTargets; + options.FailedRequestTargets = FailedRequestTargets?.Select(s => new StringOrRegex(s)).ToList() ?? options.FailedRequestTargets; options.InitCacheFlushTimeout = InitCacheFlushTimeout ?? options.InitCacheFlushTimeout; options.DefaultTags = DefaultTags ?? options.DefaultTags; options.TracesSampleRate = TracesSampleRate ?? options.TracesSampleRate; options.ProfilesSampleRate = ProfilesSampleRate ?? options.ProfilesSampleRate; - options.TracePropagationTargets = TracePropagationTargets?.Select(s => new SubstringOrRegexPattern(s)).ToList() ?? options.TracePropagationTargets; + options.TracePropagationTargets = TracePropagationTargets?.Select(s => new StringOrRegex(s)).ToList() ?? options.TracePropagationTargets; options.StackTraceMode = StackTraceMode ?? options.StackTraceMode; options.MaxAttachmentSize = MaxAttachmentSize ?? options.MaxAttachmentSize; options.DetectStartupTime = DetectStartupTime ?? options.DetectStartupTime; diff --git a/src/Sentry/BuiltInSystemDiagnosticsMeters.cs b/src/Sentry/BuiltInSystemDiagnosticsMeters.cs index df5b2849fa..d631557531 100644 --- a/src/Sentry/BuiltInSystemDiagnosticsMeters.cs +++ b/src/Sentry/BuiltInSystemDiagnosticsMeters.cs @@ -1,7 +1,9 @@ +using Sentry.Internal; + namespace Sentry; /// -/// Well known values for built in metrics that can be configured for +/// Well known values for built-in metrics that can be configured for /// /// public static partial class BuiltInSystemDiagnosticsMeters @@ -20,150 +22,150 @@ public static partial class BuiltInSystemDiagnosticsMeters private const string SystemNetHttpPattern = @"^System\.Net\.Http$"; /// - /// Matches the built in Microsoft.AspNetCore.Hosting metrics + /// Matches the built-in Microsoft.AspNetCore.Hosting metrics /// #if NET8_0_OR_GREATER - public static readonly SubstringOrRegexPattern MicrosoftAspNetCoreHosting = MicrosoftAspNetCoreHostingRegex(); + public static readonly StringOrRegex MicrosoftAspNetCoreHosting = MicrosoftAspNetCoreHostingRegex(); [GeneratedRegex(MicrosoftAspNetCoreHostingPattern, RegexOptions.Compiled)] private static partial Regex MicrosoftAspNetCoreHostingRegex(); #else - public static readonly SubstringOrRegexPattern MicrosoftAspNetCoreHosting = new Regex(MicrosoftAspNetCoreHostingPattern, RegexOptions.Compiled); + public static readonly StringOrRegex MicrosoftAspNetCoreHosting = new Regex(MicrosoftAspNetCoreHostingPattern, RegexOptions.Compiled); #endif /// - /// Matches the built in Microsoft.AspNetCore.Routing metrics + /// Matches the built-in Microsoft.AspNetCore.Routing metrics /// #if NET8_0_OR_GREATER - public static readonly SubstringOrRegexPattern MicrosoftAspNetCoreRouting = MicrosoftAspNetCoreRoutingRegex(); + public static readonly StringOrRegex MicrosoftAspNetCoreRouting = MicrosoftAspNetCoreRoutingRegex(); [GeneratedRegex(MicrosoftAspNetCoreRoutingPattern, RegexOptions.Compiled)] private static partial Regex MicrosoftAspNetCoreRoutingRegex(); #else - public static readonly SubstringOrRegexPattern MicrosoftAspNetCoreRouting = new Regex(MicrosoftAspNetCoreRoutingPattern, RegexOptions.Compiled); + public static readonly StringOrRegex MicrosoftAspNetCoreRouting = new Regex(MicrosoftAspNetCoreRoutingPattern, RegexOptions.Compiled); #endif /// - /// Matches the built in Microsoft.AspNetCore.Diagnostics metrics + /// Matches the built-in Microsoft.AspNetCore.Diagnostics metrics /// #if NET8_0_OR_GREATER - public static readonly SubstringOrRegexPattern MicrosoftAspNetCoreDiagnostics = MicrosoftAspNetCoreDiagnosticsRegex(); + public static readonly StringOrRegex MicrosoftAspNetCoreDiagnostics = MicrosoftAspNetCoreDiagnosticsRegex(); [GeneratedRegex(MicrosoftAspNetCoreDiagnosticsPattern, RegexOptions.Compiled)] private static partial Regex MicrosoftAspNetCoreDiagnosticsRegex(); #else - public static readonly SubstringOrRegexPattern MicrosoftAspNetCoreDiagnostics = new Regex(MicrosoftAspNetCoreDiagnosticsPattern, RegexOptions.Compiled); + public static readonly StringOrRegex MicrosoftAspNetCoreDiagnostics = new Regex(MicrosoftAspNetCoreDiagnosticsPattern, RegexOptions.Compiled); #endif /// - /// Matches the built in Microsoft.AspNetCore.RateLimiting metrics + /// Matches the built-in Microsoft.AspNetCore.RateLimiting metrics /// #if NET8_0_OR_GREATER - public static readonly SubstringOrRegexPattern MicrosoftAspNetCoreRateLimiting = MicrosoftAspNetCoreRateLimitingRegex(); + public static readonly StringOrRegex MicrosoftAspNetCoreRateLimiting = MicrosoftAspNetCoreRateLimitingRegex(); [GeneratedRegex(MicrosoftAspNetCoreRateLimitingPattern, RegexOptions.Compiled)] private static partial Regex MicrosoftAspNetCoreRateLimitingRegex(); #else - public static readonly SubstringOrRegexPattern MicrosoftAspNetCoreRateLimiting = new Regex(MicrosoftAspNetCoreRateLimitingPattern, RegexOptions.Compiled); + public static readonly StringOrRegex MicrosoftAspNetCoreRateLimiting = new Regex(MicrosoftAspNetCoreRateLimitingPattern, RegexOptions.Compiled); #endif /// - /// Matches the built in Microsoft.AspNetCore.HeaderParsing metrics + /// Matches the built-in Microsoft.AspNetCore.HeaderParsing metrics /// #if NET8_0_OR_GREATER - public static readonly SubstringOrRegexPattern MicrosoftAspNetCoreHeaderParsing = MicrosoftAspNetCoreHeaderParsingRegex(); + public static readonly StringOrRegex MicrosoftAspNetCoreHeaderParsing = MicrosoftAspNetCoreHeaderParsingRegex(); [GeneratedRegex(MicrosoftAspNetCoreHeaderParsingPattern, RegexOptions.Compiled)] private static partial Regex MicrosoftAspNetCoreHeaderParsingRegex(); #else - public static readonly SubstringOrRegexPattern MicrosoftAspNetCoreHeaderParsing = new Regex(MicrosoftAspNetCoreHeaderParsingPattern, RegexOptions.Compiled); + public static readonly StringOrRegex MicrosoftAspNetCoreHeaderParsing = new Regex(MicrosoftAspNetCoreHeaderParsingPattern, RegexOptions.Compiled); #endif /// - /// Matches the built in Microsoft.AspNetCore.Server.Kestrel metrics + /// Matches the built-in Microsoft.AspNetCore.Server.Kestrel metrics /// #if NET8_0_OR_GREATER - public static readonly SubstringOrRegexPattern MicrosoftAspNetCoreServerKestrel = MicrosoftAspNetCoreServerKestrelRegex(); + public static readonly StringOrRegex MicrosoftAspNetCoreServerKestrel = MicrosoftAspNetCoreServerKestrelRegex(); [GeneratedRegex(MicrosoftAspNetCoreServerKestrelPattern, RegexOptions.Compiled)] private static partial Regex MicrosoftAspNetCoreServerKestrelRegex(); #else - public static readonly SubstringOrRegexPattern MicrosoftAspNetCoreServerKestrel = new Regex(MicrosoftAspNetCoreServerKestrelPattern, RegexOptions.Compiled); + public static readonly StringOrRegex MicrosoftAspNetCoreServerKestrel = new Regex(MicrosoftAspNetCoreServerKestrelPattern, RegexOptions.Compiled); #endif /// - /// Matches the built in Microsoft.AspNetCore.Http.Connections metrics + /// Matches the built-in Microsoft.AspNetCore.Http.Connections metrics /// #if NET8_0_OR_GREATER - public static readonly SubstringOrRegexPattern MicrosoftAspNetCoreHttpConnections = MicrosoftAspNetCoreHttpConnectionsRegex(); + public static readonly StringOrRegex MicrosoftAspNetCoreHttpConnections = MicrosoftAspNetCoreHttpConnectionsRegex(); [GeneratedRegex(MicrosoftAspNetCoreHttpConnectionsPattern, RegexOptions.Compiled)] private static partial Regex MicrosoftAspNetCoreHttpConnectionsRegex(); #else - public static readonly SubstringOrRegexPattern MicrosoftAspNetCoreHttpConnections = new Regex(MicrosoftAspNetCoreHttpConnectionsPattern, RegexOptions.Compiled); + public static readonly StringOrRegex MicrosoftAspNetCoreHttpConnections = new Regex(MicrosoftAspNetCoreHttpConnectionsPattern, RegexOptions.Compiled); #endif /// - /// Matches the built in Microsoft.Extensions.Diagnostics.HealthChecks metrics + /// Matches the built-in Microsoft.Extensions.Diagnostics.HealthChecks metrics /// #if NET8_0_OR_GREATER - public static readonly SubstringOrRegexPattern MicrosoftExtensionsDiagnosticsHealthChecks = MicrosoftExtensionsDiagnosticsHealthChecksRegex(); + public static readonly StringOrRegex MicrosoftExtensionsDiagnosticsHealthChecks = MicrosoftExtensionsDiagnosticsHealthChecksRegex(); [GeneratedRegex(MicrosoftExtensionsDiagnosticsHealthChecksPattern, RegexOptions.Compiled)] private static partial Regex MicrosoftExtensionsDiagnosticsHealthChecksRegex(); #else - public static readonly SubstringOrRegexPattern MicrosoftExtensionsDiagnosticsHealthChecks = new Regex(MicrosoftExtensionsDiagnosticsHealthChecksPattern, RegexOptions.Compiled); + public static readonly StringOrRegex MicrosoftExtensionsDiagnosticsHealthChecks = new Regex(MicrosoftExtensionsDiagnosticsHealthChecksPattern, RegexOptions.Compiled); #endif /// - /// Matches the built in Microsoft.Extensions.Diagnostics.ResourceMonitoring metrics + /// Matches the built-in Microsoft.Extensions.Diagnostics.ResourceMonitoring metrics /// #if NET8_0_OR_GREATER - public static readonly SubstringOrRegexPattern MicrosoftExtensionsDiagnosticsResourceMonitoring = MicrosoftExtensionsDiagnosticsResourceMonitoringRegex(); + public static readonly StringOrRegex MicrosoftExtensionsDiagnosticsResourceMonitoring = MicrosoftExtensionsDiagnosticsResourceMonitoringRegex(); [GeneratedRegex(MicrosoftExtensionsDiagnosticsResourceMonitoringPattern, RegexOptions.Compiled)] private static partial Regex MicrosoftExtensionsDiagnosticsResourceMonitoringRegex(); #else - public static readonly SubstringOrRegexPattern MicrosoftExtensionsDiagnosticsResourceMonitoring = new Regex(MicrosoftExtensionsDiagnosticsResourceMonitoringPattern, RegexOptions.Compiled); + public static readonly StringOrRegex MicrosoftExtensionsDiagnosticsResourceMonitoring = new Regex(MicrosoftExtensionsDiagnosticsResourceMonitoringPattern, RegexOptions.Compiled); #endif /// - /// Matches the built in System.Net.NameResolution metrics + /// Matches the built-in System.Net.NameResolution metrics /// #if NET8_0_OR_GREATER - public static readonly SubstringOrRegexPattern OpenTelemetryInstrumentationRuntime = OpenTelemetryInstrumentationRuntimeRegex(); + public static readonly StringOrRegex OpenTelemetryInstrumentationRuntime = OpenTelemetryInstrumentationRuntimeRegex(); [GeneratedRegex(OpenTelemetryInstrumentationRuntimePattern, RegexOptions.Compiled)] private static partial Regex OpenTelemetryInstrumentationRuntimeRegex(); #else - public static readonly SubstringOrRegexPattern OpenTelemetryInstrumentationRuntime = new Regex(OpenTelemetryInstrumentationRuntimePattern, RegexOptions.Compiled); + public static readonly StringOrRegex OpenTelemetryInstrumentationRuntime = new Regex(OpenTelemetryInstrumentationRuntimePattern, RegexOptions.Compiled); #endif /// - /// Matches the built in System.Net.NameResolution metrics + /// Matches the built-in System.Net.NameResolution metrics /// #if NET8_0_OR_GREATER - public static readonly SubstringOrRegexPattern SystemNetNameResolution = SystemNetNameResolutionRegex(); + public static readonly StringOrRegex SystemNetNameResolution = SystemNetNameResolutionRegex(); [GeneratedRegex(SystemNetNameResolutionPattern, RegexOptions.Compiled)] private static partial Regex SystemNetNameResolutionRegex(); #else - public static readonly SubstringOrRegexPattern SystemNetNameResolution = new Regex(SystemNetNameResolutionPattern, RegexOptions.Compiled); + public static readonly StringOrRegex SystemNetNameResolution = new Regex(SystemNetNameResolutionPattern, RegexOptions.Compiled); #endif /// - /// Matches the built in metrics + /// Matches the built-in metrics /// #if NET8_0_OR_GREATER - public static readonly SubstringOrRegexPattern SystemNetHttp = SystemNetHttpRegex(); + public static readonly StringOrRegex SystemNetHttp = SystemNetHttpRegex(); [GeneratedRegex(SystemNetHttpPattern, RegexOptions.Compiled)] private static partial Regex SystemNetHttpRegex(); #else - public static readonly SubstringOrRegexPattern SystemNetHttp = new Regex(SystemNetHttpPattern, RegexOptions.Compiled); + public static readonly StringOrRegex SystemNetHttp = new Regex(SystemNetHttpPattern, RegexOptions.Compiled); #endif - private static readonly Lazy> LazyAll = new(() => new List + private static readonly Lazy> LazyAll = new(() => new List { MicrosoftAspNetCoreHosting, MicrosoftAspNetCoreRouting, @@ -180,8 +182,8 @@ public static partial class BuiltInSystemDiagnosticsMeters }); /// - /// Matches all built in metrics + /// Matches all built-in metrics /// /// - public static IList All => LazyAll.Value; + public static IList All => LazyAll.Value; } diff --git a/src/Sentry/ExperimentalMetricsOptions.cs b/src/Sentry/ExperimentalMetricsOptions.cs index 5a71606c36..0b07ad0e0b 100644 --- a/src/Sentry/ExperimentalMetricsOptions.cs +++ b/src/Sentry/ExperimentalMetricsOptions.cs @@ -1,3 +1,5 @@ +using Sentry.Internal; + namespace Sentry; /// @@ -11,7 +13,7 @@ public class ExperimentalMetricsOptions /// public bool EnableCodeLocations { get; set; } = true; - private IList _captureSystemDiagnosticsInstruments = new List(); + private IList _captureSystemDiagnosticsInstruments = new List(); /// /// @@ -19,13 +21,13 @@ public class ExperimentalMetricsOptions /// matches one of the items in this list will be collected and reported to Sentry. /// /// - /// These can be either custom Instruments that you have created or any of the built in metrics that are available. + /// These can be either custom Instruments that you have created or any of the built-in metrics that are available. /// /// /// See https://learn.microsoft.com/en-us/dotnet/core/diagnostics/built-in-metrics for more information. /// /// - public IList CaptureSystemDiagnosticsInstruments + public IList CaptureSystemDiagnosticsInstruments { // NOTE: During configuration binding, .NET 6 and lower used to just call Add on the existing item. // .NET 7 changed this to call the setter with an array that already starts with the old value. @@ -34,7 +36,7 @@ public IList CaptureSystemDiagnosticsInstruments set => _captureSystemDiagnosticsInstruments = value.WithConfigBinding(); } - private IList _captureSystemDiagnosticsMeters = BuiltInSystemDiagnosticsMeters.All; + private IList _captureSystemDiagnosticsMeters = BuiltInSystemDiagnosticsMeters.All; /// /// @@ -42,13 +44,13 @@ public IList CaptureSystemDiagnosticsInstruments /// whose name matches one of the items in this list will be collected and reported to Sentry. /// /// - /// These can be either custom Instruments that you have created or any of the built in metrics that are available. + /// These can be either custom Instruments that you have created or any of the built-in metrics that are available. /// /// /// See https://learn.microsoft.com/en-us/dotnet/core/diagnostics/built-in-metrics for more information. /// /// - public IList CaptureSystemDiagnosticsMeters + public IList CaptureSystemDiagnosticsMeters { // NOTE: During configuration binding, .NET 6 and lower used to just call Add on the existing item. // .NET 7 changed this to call the setter with an array that already starts with the old value. diff --git a/src/Sentry/Internal/DelimitedPrefixOrPatternMatcher.cs b/src/Sentry/Internal/DelimitedPrefixOrPatternMatcher.cs index 29e53f111a..d842c74ef9 100644 --- a/src/Sentry/Internal/DelimitedPrefixOrPatternMatcher.cs +++ b/src/Sentry/Internal/DelimitedPrefixOrPatternMatcher.cs @@ -7,11 +7,11 @@ internal class DelimitedPrefixOrPatternMatcher(char delimiter = '.', StringCompa { public bool IsMatch(StringOrRegex stringOrRegex, string value) { - if (stringOrRegex._prefix is not null) + if (stringOrRegex._string is not null) { // Check for a prefix followed by the separator - return stringOrRegex._prefix != null && value.StartsWith(stringOrRegex._prefix, comparison) && - value.Length > stringOrRegex._prefix.Length && value[stringOrRegex._prefix.Length] == delimiter; + return stringOrRegex._string != null && value.StartsWith(stringOrRegex._string, comparison) && + value.Length > stringOrRegex._string.Length && value[stringOrRegex._string.Length] == delimiter; } // Check for any regex match followed by the separator diff --git a/src/Sentry/Internal/IStringOrRegexMatcher.cs b/src/Sentry/Internal/IStringOrRegexMatcher.cs index 07345d1aca..916eb5fc55 100644 --- a/src/Sentry/Internal/IStringOrRegexMatcher.cs +++ b/src/Sentry/Internal/IStringOrRegexMatcher.cs @@ -2,5 +2,8 @@ namespace Sentry.Internal; internal interface IStringOrRegexMatcher { + /// + /// Evaluates if the given value matches the string or regex. + /// bool IsMatch(StringOrRegex stringOrRegex, string value); } diff --git a/src/Sentry/Internal/PrefixOrPatternMatcher.cs b/src/Sentry/Internal/PrefixOrPatternMatcher.cs index c07e6f6cef..1bf6f17f94 100644 --- a/src/Sentry/Internal/PrefixOrPatternMatcher.cs +++ b/src/Sentry/Internal/PrefixOrPatternMatcher.cs @@ -7,7 +7,7 @@ internal class PrefixOrPatternMatcher(StringComparison comparison = StringCompar { public bool IsMatch(StringOrRegex stringOrRegex, string value) { - return (stringOrRegex._prefix != null && value.StartsWith(stringOrRegex._prefix, comparison)) || + return (stringOrRegex._string != null && value.StartsWith(stringOrRegex._string, comparison)) || stringOrRegex?._regex?.IsMatch(value) == true; } } diff --git a/src/Sentry/Internal/StringOrRegex.cs b/src/Sentry/Internal/StringOrRegex.cs deleted file mode 100644 index 164d41728a..0000000000 --- a/src/Sentry/Internal/StringOrRegex.cs +++ /dev/null @@ -1,82 +0,0 @@ -namespace Sentry.Internal; - -/// -/// Stores either a plain string or a Regular Expression, typically to match against filters in the SentryOptions -/// -internal class StringOrRegex -{ - internal readonly Regex? _regex; - internal readonly string? _prefix; - - /// - /// Constructs a instance. - /// - /// The prefix or regular expression pattern to match on. - public StringOrRegex(string stringOrRegex) - { - _prefix = stringOrRegex; - } - - /// - /// Constructs a instance. - /// - /// - /// - /// Use this constructor when you want the match to be performed using a regular expression. - /// - public StringOrRegex(Regex regex) => _regex = regex; - - /// - /// Implicitly converts a to a . - /// - /// - public static implicit operator StringOrRegex(string stringOrRegex) - { - return new StringOrRegex(stringOrRegex); - } - - /// - /// Implicitly converts a to a . - /// - /// - public static implicit operator StringOrRegex(Regex regex) - { - return new StringOrRegex(regex); - } - - /// - public override string ToString() => _prefix ?? _regex?.ToString() ?? ""; - - /// - public override bool Equals(object? obj) - { - return - (obj is StringOrRegex pattern) - && pattern.ToString() == ToString(); - } - - /// - public override int GetHashCode() - { - return ToString().GetHashCode(); - } -} - -internal static class StringOrRegexExtensions -{ - public static bool MatchesAny(this string parameter, List? patterns, IStringOrRegexMatcher matcher) - { - if (patterns is null) - { - return false; - } - foreach (var stringOrRegex in patterns) - { - if (matcher.IsMatch(stringOrRegex, parameter)) - { - return true; - } - } - return false; - } -} diff --git a/src/Sentry/Internal/SystemDiagnosticsMetricsListener.cs b/src/Sentry/Internal/SystemDiagnosticsMetricsListener.cs index af8fe4fbbd..10d613669f 100644 --- a/src/Sentry/Internal/SystemDiagnosticsMetricsListener.cs +++ b/src/Sentry/Internal/SystemDiagnosticsMetricsListener.cs @@ -24,8 +24,8 @@ internal SystemDiagnosticsMetricsListener(ExperimentalMetricsOptions metricsOpti _metricsAggregator = new Lazy(metricsAggregatorResolver); _sentryListener.InstrumentPublished = (instrument, listener) => { - if (metricsOptions.CaptureSystemDiagnosticsMeters.ContainsMatch(instrument.Meter.Name) - || metricsOptions.CaptureSystemDiagnosticsInstruments.ContainsMatch(instrument.Name)) + if (metricsOptions.CaptureSystemDiagnosticsMeters.MatchesSubstringOrRegex(instrument.Meter.Name) + || metricsOptions.CaptureSystemDiagnosticsInstruments.MatchesSubstringOrRegex(instrument.Name)) { listener.EnableMeasurementEvents(instrument); } diff --git a/src/Sentry/Scope.cs b/src/Sentry/Scope.cs index 6d4cefb92a..8c29c7edd8 100644 --- a/src/Sentry/Scope.cs +++ b/src/Sentry/Scope.cs @@ -307,7 +307,7 @@ public void SetExtra(string key, object? value) /// public void SetTag(string key, string value) { - if (Options.TagFilters.Any(x => x.IsMatch(key))) + if (Options.TagFilters.MatchesSubstringOrRegex(key)) { return; } diff --git a/src/Sentry/SentryFailedRequestHandler.cs b/src/Sentry/SentryFailedRequestHandler.cs index 9ff93bc231..8280dc0661 100644 --- a/src/Sentry/SentryFailedRequestHandler.cs +++ b/src/Sentry/SentryFailedRequestHandler.cs @@ -38,7 +38,7 @@ public void HandleResponse(HttpResponseMessage response) // Ignore requests that don't match the FailedRequestTargets var requestString = uri.ToString(); - if (!Options.FailedRequestTargets.ContainsMatch(requestString)) + if (!Options.FailedRequestTargets.MatchesSubstringOrRegex(requestString)) { return; } diff --git a/src/Sentry/SentryMessageHandler.cs b/src/Sentry/SentryMessageHandler.cs index 693b9ad25e..8784ef1822 100644 --- a/src/Sentry/SentryMessageHandler.cs +++ b/src/Sentry/SentryMessageHandler.cs @@ -133,7 +133,7 @@ private void PropagateTraceHeaders(HttpRequestMessage request, string url) } } - if (_options?.TracePropagationTargets.ContainsMatch(url) is true or null) + if (_options?.TracePropagationTargets.MatchesSubstringOrRegex(url) is true or null) { AddSentryTraceHeader(request); AddBaggageHeader(request); diff --git a/src/Sentry/SentryOptions.cs b/src/Sentry/SentryOptions.cs index 3480207ab5..7ce83f9057 100644 --- a/src/Sentry/SentryOptions.cs +++ b/src/Sentry/SentryOptions.cs @@ -218,7 +218,7 @@ internal IEnumerable Integrations /// /// List of substrings or regular expression patterns to filter out tags /// - public ICollection TagFilters { get; set; } = new List(); + public IList TagFilters { get; set; } = new List(); /// /// The worker used by the client to pass envelopes. @@ -707,16 +707,16 @@ public IDiagnosticLogger? DiagnosticLogger }; // The default failed request target list will match anything, but adding to the list should clear that. - private Lazy> _failedRequestTargets = new(() => - new AutoClearingList( - new[] { new SubstringOrRegexPattern(".*") }, clearOnNextAdd: true)); + private Lazy> _failedRequestTargets = new(() => + new AutoClearingList( + new[] { new StringOrRegex(".*") }, clearOnNextAdd: true)); /// /// The SDK will only capture HTTP Client errors if the HTTP Request URL is a match for any of the failedRequestsTargets. /// Targets may be URLs or Regular expressions. /// Matches "*." by default. /// - public IList FailedRequestTargets + public IList FailedRequestTargets { get => _failedRequestTargets.Value; set => _failedRequestTargets = new(value.WithConfigBinding); @@ -862,11 +862,11 @@ public double? ProfilesSampleRate public Func? TracesSampler { get; set; } // The default propagation list will match anything, but adding to the list should clear that. - private IList _tracePropagationTargets = new AutoClearingList - (new[] { new SubstringOrRegexPattern(".*") }, clearOnNextAdd: true); + private IList _tracePropagationTargets = new AutoClearingList + (new[] { new StringOrRegex(".*") }, clearOnNextAdd: true); /// - /// A customizable list of objects, each containing either a + /// A customizable list of objects, each containing either a /// substring or regular expression pattern that can be used to control which outgoing HTTP requests /// will have the sentry-trace and baggage headers propagated, for purposes of distributed tracing. /// The default value contains a single value of .*, which matches everything. @@ -876,7 +876,7 @@ public double? ProfilesSampleRate /// /// Adding an item to the default list will clear the .* value automatically. /// - public IList TracePropagationTargets + public IList TracePropagationTargets { // NOTE: During configuration binding, .NET 6 and lower used to just call Add on the existing item. // .NET 7 changed this to call the setter with an array that already starts with the old value. @@ -888,7 +888,7 @@ public IList TracePropagationTargets internal ITransactionProfilerFactory? TransactionProfilerFactory { get; set; } private StackTraceMode? _stackTraceMode; - private readonly List _integrations = new(); + private readonly List _integrations; /// /// ATTENTION: This option will change how issues are grouped in Sentry! diff --git a/src/Sentry/StringOrRegex.cs b/src/Sentry/StringOrRegex.cs new file mode 100644 index 0000000000..1a5e672e8a --- /dev/null +++ b/src/Sentry/StringOrRegex.cs @@ -0,0 +1,128 @@ +using Sentry.Internal; + +namespace Sentry; + +/// +/// Stores either a plain string or a Regular Expression, typically to match against filters in the SentryOptions +/// +[TypeConverter(typeof(StringOrRegexTypeConverter))] +public class StringOrRegex +{ + internal readonly Regex? _regex; + internal readonly string? _string; + + /// + /// Constructs a instance. + /// + /// The prefix or regular expression pattern to match on. + public StringOrRegex(string stringOrRegex) + { + _string = stringOrRegex; + } + + /// + /// Constructs a instance. + /// + /// + /// + /// Use this constructor when you want the match to be performed using a regular expression. + /// + public StringOrRegex(Regex regex) => _regex = regex; + + /// + /// Implicitly converts a to a . + /// + /// + public static implicit operator StringOrRegex(string stringOrRegex) + { + return new StringOrRegex(stringOrRegex); + } + + /// + /// Implicitly converts a to a . + /// + /// + public static implicit operator StringOrRegex(Regex regex) + { + return new StringOrRegex(regex); + } + + /// + public override string ToString() => _string ?? _regex?.ToString() ?? ""; + + /// + public override bool Equals(object? obj) + { + return + (obj is StringOrRegex pattern) + && pattern.ToString() == ToString(); + } + + /// + public override int GetHashCode() + { + return ToString().GetHashCode(); + } +} + +internal static class StringOrRegexExtensions +{ + public static bool MatchesAny(this string parameter, IEnumerable? patterns, IStringOrRegexMatcher matcher) + { + if (patterns is null) + { + return false; + } + foreach (var stringOrRegex in patterns) + { + if (matcher.IsMatch(stringOrRegex, parameter)) + { + return true; + } + } + return false; + } + + public static bool MatchesSubstringOrRegex(this IEnumerable? patterns, string parameter) + => parameter.MatchesAny(patterns, SubstringOrPatternMatcher.Default); + + /// + /// During configuration binding, .NET 6 and lower used to just call Add on the existing item. + /// .NET 7 changed this to call the setter with an array that already starts with the old value. + /// We have to handle both cases. + /// + /// The List Type + /// The set of values to be assigned + /// A IList of type T that will be consistent even if it has been set via Config + public static IList WithConfigBinding(this IList value) + where T : StringOrRegex + { + switch (value.Count) + { + case 1 when value[0].ToString() == ".*": + // There's only one item in the list, and it's the wildcard, so reset to the initial state. + return new AutoClearingList(value, clearOnNextAdd: true); + + case > 1: + // There's more than one item in the list. Remove the wildcard. + var targets = value.ToList(); + targets.RemoveAll(t => t.ToString() == ".*"); + return targets; + + default: + return value; + } + } +} + +/// +/// This class allows the TracePropagationTargets option to be set from config, such as appSettings.json +/// +internal class StringOrRegexTypeConverter : TypeConverter +{ + public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) => + sourceType == typeof(string); + + public override object ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value) => + new StringOrRegex((string)value); +} diff --git a/src/Sentry/SubstringOrPatternMatcher.cs b/src/Sentry/SubstringOrPatternMatcher.cs new file mode 100644 index 0000000000..15e716e19b --- /dev/null +++ b/src/Sentry/SubstringOrPatternMatcher.cs @@ -0,0 +1,19 @@ +using Sentry.Internal; + +namespace Sentry; + +/// +/// Provides a pattern that can be used to match against other strings as either a substring or regular expression. +/// +/// The string comparison type to use when matching for substrings. +internal class SubstringOrPatternMatcher(StringComparison comparison = StringComparison.OrdinalIgnoreCase) + : IStringOrRegexMatcher +{ + internal static SubstringOrPatternMatcher Default { get; } = new(); + + public bool IsMatch(StringOrRegex stringOrRegex, string value) + { + return (stringOrRegex._string != null && (stringOrRegex._string == ".*" || value.Contains(stringOrRegex._string, comparison))) || + stringOrRegex._regex?.IsMatch(value) == true; + } +} diff --git a/src/Sentry/SubstringOrRegexPattern.cs b/src/Sentry/SubstringOrRegexPattern.cs deleted file mode 100644 index 1d8913b838..0000000000 --- a/src/Sentry/SubstringOrRegexPattern.cs +++ /dev/null @@ -1,160 +0,0 @@ -using Sentry.Internal; - -namespace Sentry; - -// TODO: This class can be replaced with an implementation of IStringOrRegexMatcher in the next major version bump. -/// -/// Provides a pattern that can be used to match against other strings as either a substring or regular expression. -/// -[TypeConverter(typeof(SubstringOrRegexPatternTypeConverter))] -public class SubstringOrRegexPattern -{ - private readonly Regex? _regex; - private readonly string? _substring; - private readonly StringComparison _stringComparison; - - /// - /// Constructs a instance. - /// - /// The substring or regular expression pattern to match on. - /// The string comparison type to use when matching. - public SubstringOrRegexPattern( - string substringOrRegexPattern, - StringComparison comparison = StringComparison.OrdinalIgnoreCase) - { - _substring = substringOrRegexPattern; - _stringComparison = comparison; - _regex = TryParseRegex(substringOrRegexPattern, comparison); - } - - /// - /// Constructs a instance. - /// - /// - /// - /// Use this constructor when you need to control the regular expression matching options. - /// We recommend setting at least for performance, and - /// (unless you have culture-specific matching needs). - /// The constructor sets these by default. - /// - public SubstringOrRegexPattern(Regex regex) => _regex = regex; - - /// - /// Implicitly converts a to a . - /// - /// - public static implicit operator SubstringOrRegexPattern(string substringOrRegexPattern) - { - return new SubstringOrRegexPattern(substringOrRegexPattern); - } - - /// - /// Implicitly converts a to a . - /// - /// - public static implicit operator SubstringOrRegexPattern(Regex regex) - { - return new SubstringOrRegexPattern(regex); - } - - /// - public override string ToString() => _substring ?? _regex?.ToString() ?? ""; - - /// - public override bool Equals(object? obj) - { - return - (obj is SubstringOrRegexPattern pattern) - && pattern.ToString() == ToString(); - } - - /// - public override int GetHashCode() - { - return ToString().GetHashCode(); - } - - internal Regex? Regex => _regex; - - internal bool IsMatch(string str) => - _substring == ".*" || // perf shortcut - (_substring != null && str.Contains(_substring, _stringComparison)) || - _regex?.IsMatch(str) == true; - - private static Regex? TryParseRegex(string pattern, StringComparison comparison) - { - try - { - var regexOptions = RegexOptions.Compiled; - - if (comparison is - StringComparison.InvariantCulture or - StringComparison.InvariantCultureIgnoreCase or - StringComparison.Ordinal or - StringComparison.OrdinalIgnoreCase) - { - regexOptions |= RegexOptions.CultureInvariant; - } - - if (comparison is - StringComparison.CurrentCultureIgnoreCase or - StringComparison.InvariantCultureIgnoreCase or - StringComparison.OrdinalIgnoreCase) - { - regexOptions |= RegexOptions.IgnoreCase; - } - - return new Regex(pattern, regexOptions); - } - catch - { - // not a valid regex - return null; - } - } -} - -internal static class SubstringOrRegexPatternExtensions -{ - public static bool ContainsMatch(this IEnumerable targets, string str) => - targets.Any(t => t.IsMatch(str)); - - /// - /// During configuration binding, .NET 6 and lower used to just call Add on the existing item. - /// .NET 7 changed this to call the setter with an array that already starts with the old value. - /// We have to handle both cases. - /// - /// The List Type - /// The set of values to be assigned - /// A IList of type T that will be consistent even if it has been set via Config - public static IList WithConfigBinding(this IList value) - where T : SubstringOrRegexPattern - { - switch (value.Count) - { - case 1 when value[0].ToString() == ".*": - // There's only one item in the list, and it's the wildcard, so reset to the initial state. - return new AutoClearingList(value, clearOnNextAdd: true); - - case > 1: - // There's more than one item in the list. Remove the wildcard. - var targets = value.ToList(); - targets.RemoveAll(t => t.ToString() == ".*"); - return targets; - - default: - return value; - } - } -} - -internal class SubstringOrRegexPatternTypeConverter : TypeConverter -{ - // This class allows the TracePropagationTargets option to be set from config, such as appSettings.json - - public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) => - sourceType == typeof(string); - - public override object ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value) => - new SubstringOrRegexPattern((string)value); -} diff --git a/test/Sentry.Extensions.Logging.Tests/SentryLoggingOptionsSetupTests.cs b/test/Sentry.Extensions.Logging.Tests/SentryLoggingOptionsSetupTests.cs index d6ffa6ac1e..778215de16 100644 --- a/test/Sentry.Extensions.Logging.Tests/SentryLoggingOptionsSetupTests.cs +++ b/test/Sentry.Extensions.Logging.Tests/SentryLoggingOptionsSetupTests.cs @@ -14,7 +14,7 @@ public void Configure_BindsConfigurationToOptions() { IsGlobalModeEnabled = true, EnableScopeSync = true, - TagFilters = new List { "tag1", "tag2" }, + TagFilters = new List { "tag1", "tag2" }, SendDefaultPii = true, IsEnvironmentUser = true, ServerName = "FakeServerName", @@ -40,11 +40,11 @@ public void Configure_BindsConfigurationToOptions() CacheDirectoryPath = "~/test", CaptureFailedRequests = true, // FailedRequestStatusCodes = IList, - FailedRequestTargets = new List { "target1", "target2" }, + FailedRequestTargets = ["target1", "target2"], InitCacheFlushTimeout = TimeSpan.FromSeconds(27), // DefaultTags = Dictionary, TracesSampleRate = 0.8f, - TracePropagationTargets = new List { "target3", "target4" }, + TracePropagationTargets = new List { "target3", "target4" }, StackTraceMode = StackTraceMode.Enhanced, MaxAttachmentSize = 21478, DetectStartupTime = StartupTimeDetectionMode.Fast, @@ -62,8 +62,8 @@ public void Configure_BindsConfigurationToOptions() { ["IsGlobalModeEnabled"] = expected.IsGlobalModeEnabled.ToString(), ["EnableScopeSync"] = expected.EnableScopeSync.ToString(), - ["TagFilters:0"] = "tag1", - ["TagFilters:1"] = "tag2", + ["TagFilters:0"] = expected.TagFilters.First().ToString(), + ["TagFilters:1"] = expected.TagFilters.Last().ToString(), ["SendDefaultPii"] = expected.SendDefaultPii.ToString(), ["IsEnvironmentUser"] = expected.IsEnvironmentUser.ToString(), ["ServerName"] = expected.ServerName, diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt index 2fb50223fa..ffeeaf451f 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt @@ -42,19 +42,19 @@ namespace Sentry } public static class BuiltInSystemDiagnosticsMeters { - public static readonly Sentry.SubstringOrRegexPattern MicrosoftAspNetCoreDiagnostics; - public static readonly Sentry.SubstringOrRegexPattern MicrosoftAspNetCoreHeaderParsing; - public static readonly Sentry.SubstringOrRegexPattern MicrosoftAspNetCoreHosting; - public static readonly Sentry.SubstringOrRegexPattern MicrosoftAspNetCoreHttpConnections; - public static readonly Sentry.SubstringOrRegexPattern MicrosoftAspNetCoreRateLimiting; - public static readonly Sentry.SubstringOrRegexPattern MicrosoftAspNetCoreRouting; - public static readonly Sentry.SubstringOrRegexPattern MicrosoftAspNetCoreServerKestrel; - public static readonly Sentry.SubstringOrRegexPattern MicrosoftExtensionsDiagnosticsHealthChecks; - public static readonly Sentry.SubstringOrRegexPattern MicrosoftExtensionsDiagnosticsResourceMonitoring; - public static readonly Sentry.SubstringOrRegexPattern OpenTelemetryInstrumentationRuntime; - public static readonly Sentry.SubstringOrRegexPattern SystemNetHttp; - public static readonly Sentry.SubstringOrRegexPattern SystemNetNameResolution; - public static System.Collections.Generic.IList All { get; } + public static readonly Sentry.StringOrRegex MicrosoftAspNetCoreDiagnostics; + public static readonly Sentry.StringOrRegex MicrosoftAspNetCoreHeaderParsing; + public static readonly Sentry.StringOrRegex MicrosoftAspNetCoreHosting; + public static readonly Sentry.StringOrRegex MicrosoftAspNetCoreHttpConnections; + public static readonly Sentry.StringOrRegex MicrosoftAspNetCoreRateLimiting; + public static readonly Sentry.StringOrRegex MicrosoftAspNetCoreRouting; + public static readonly Sentry.StringOrRegex MicrosoftAspNetCoreServerKestrel; + public static readonly Sentry.StringOrRegex MicrosoftExtensionsDiagnosticsHealthChecks; + public static readonly Sentry.StringOrRegex MicrosoftExtensionsDiagnosticsResourceMonitoring; + public static readonly Sentry.StringOrRegex OpenTelemetryInstrumentationRuntime; + public static readonly Sentry.StringOrRegex SystemNetHttp; + public static readonly Sentry.StringOrRegex SystemNetNameResolution; + public static System.Collections.Generic.IList All { get; } } public class ByteAttachmentContent : Sentry.IAttachmentContent { @@ -106,8 +106,8 @@ namespace Sentry public class ExperimentalMetricsOptions { public ExperimentalMetricsOptions() { } - public System.Collections.Generic.IList CaptureSystemDiagnosticsInstruments { get; set; } - public System.Collections.Generic.IList CaptureSystemDiagnosticsMeters { get; set; } + public System.Collections.Generic.IList CaptureSystemDiagnosticsInstruments { get; set; } + public System.Collections.Generic.IList CaptureSystemDiagnosticsMeters { get; set; } public bool EnableCodeLocations { get; set; } } public class FileAttachmentContent : Sentry.IAttachmentContent @@ -671,7 +671,7 @@ namespace Sentry public string? Environment { get; set; } public Sentry.ExperimentalMetricsOptions? ExperimentalMetrics { get; set; } public System.Collections.Generic.IList FailedRequestStatusCodes { get; set; } - public System.Collections.Generic.IList FailedRequestTargets { get; set; } + public System.Collections.Generic.IList FailedRequestTargets { get; set; } public System.TimeSpan FlushTimeout { get; set; } public System.Net.IWebProxy? HttpProxy { get; set; } public System.TimeSpan InitCacheFlushTimeout { get; set; } @@ -697,8 +697,8 @@ namespace Sentry public System.TimeSpan ShutdownTimeout { get; set; } public string SpotlightUrl { get; set; } public Sentry.StackTraceMode StackTraceMode { get; set; } - public System.Collections.Generic.ICollection TagFilters { get; set; } - public System.Collections.Generic.IList TracePropagationTargets { get; set; } + public System.Collections.Generic.IList TagFilters { get; set; } + public System.Collections.Generic.IList TracePropagationTargets { get; set; } public double? TracesSampleRate { get; set; } public System.Func? TracesSampler { get; set; } public Sentry.Extensibility.ITransport? Transport { get; set; } @@ -1118,16 +1118,16 @@ namespace Sentry public StreamAttachmentContent(System.IO.Stream stream) { } public System.IO.Stream GetStream() { } } - [System.ComponentModel.TypeConverter(typeof(Sentry.SubstringOrRegexPatternTypeConverter))] - public class SubstringOrRegexPattern + [System.ComponentModel.TypeConverter(typeof(Sentry.StringOrRegexTypeConverter))] + public class StringOrRegex { - public SubstringOrRegexPattern(System.Text.RegularExpressions.Regex regex) { } - public SubstringOrRegexPattern(string substringOrRegexPattern, System.StringComparison comparison = 5) { } + public StringOrRegex(string stringOrRegex) { } + public StringOrRegex(System.Text.RegularExpressions.Regex regex) { } public override bool Equals(object? obj) { } public override int GetHashCode() { } public override string ToString() { } - public static Sentry.SubstringOrRegexPattern op_Implicit(string substringOrRegexPattern) { } - public static Sentry.SubstringOrRegexPattern op_Implicit(System.Text.RegularExpressions.Regex regex) { } + public static Sentry.StringOrRegex op_Implicit(string stringOrRegex) { } + public static Sentry.StringOrRegex op_Implicit(System.Text.RegularExpressions.Regex regex) { } } public class TransactionContext : Sentry.SpanContext, Sentry.ITransactionContext, Sentry.Protocol.ITraceContext { diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt index 2fb50223fa..ffeeaf451f 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt @@ -42,19 +42,19 @@ namespace Sentry } public static class BuiltInSystemDiagnosticsMeters { - public static readonly Sentry.SubstringOrRegexPattern MicrosoftAspNetCoreDiagnostics; - public static readonly Sentry.SubstringOrRegexPattern MicrosoftAspNetCoreHeaderParsing; - public static readonly Sentry.SubstringOrRegexPattern MicrosoftAspNetCoreHosting; - public static readonly Sentry.SubstringOrRegexPattern MicrosoftAspNetCoreHttpConnections; - public static readonly Sentry.SubstringOrRegexPattern MicrosoftAspNetCoreRateLimiting; - public static readonly Sentry.SubstringOrRegexPattern MicrosoftAspNetCoreRouting; - public static readonly Sentry.SubstringOrRegexPattern MicrosoftAspNetCoreServerKestrel; - public static readonly Sentry.SubstringOrRegexPattern MicrosoftExtensionsDiagnosticsHealthChecks; - public static readonly Sentry.SubstringOrRegexPattern MicrosoftExtensionsDiagnosticsResourceMonitoring; - public static readonly Sentry.SubstringOrRegexPattern OpenTelemetryInstrumentationRuntime; - public static readonly Sentry.SubstringOrRegexPattern SystemNetHttp; - public static readonly Sentry.SubstringOrRegexPattern SystemNetNameResolution; - public static System.Collections.Generic.IList All { get; } + public static readonly Sentry.StringOrRegex MicrosoftAspNetCoreDiagnostics; + public static readonly Sentry.StringOrRegex MicrosoftAspNetCoreHeaderParsing; + public static readonly Sentry.StringOrRegex MicrosoftAspNetCoreHosting; + public static readonly Sentry.StringOrRegex MicrosoftAspNetCoreHttpConnections; + public static readonly Sentry.StringOrRegex MicrosoftAspNetCoreRateLimiting; + public static readonly Sentry.StringOrRegex MicrosoftAspNetCoreRouting; + public static readonly Sentry.StringOrRegex MicrosoftAspNetCoreServerKestrel; + public static readonly Sentry.StringOrRegex MicrosoftExtensionsDiagnosticsHealthChecks; + public static readonly Sentry.StringOrRegex MicrosoftExtensionsDiagnosticsResourceMonitoring; + public static readonly Sentry.StringOrRegex OpenTelemetryInstrumentationRuntime; + public static readonly Sentry.StringOrRegex SystemNetHttp; + public static readonly Sentry.StringOrRegex SystemNetNameResolution; + public static System.Collections.Generic.IList All { get; } } public class ByteAttachmentContent : Sentry.IAttachmentContent { @@ -106,8 +106,8 @@ namespace Sentry public class ExperimentalMetricsOptions { public ExperimentalMetricsOptions() { } - public System.Collections.Generic.IList CaptureSystemDiagnosticsInstruments { get; set; } - public System.Collections.Generic.IList CaptureSystemDiagnosticsMeters { get; set; } + public System.Collections.Generic.IList CaptureSystemDiagnosticsInstruments { get; set; } + public System.Collections.Generic.IList CaptureSystemDiagnosticsMeters { get; set; } public bool EnableCodeLocations { get; set; } } public class FileAttachmentContent : Sentry.IAttachmentContent @@ -671,7 +671,7 @@ namespace Sentry public string? Environment { get; set; } public Sentry.ExperimentalMetricsOptions? ExperimentalMetrics { get; set; } public System.Collections.Generic.IList FailedRequestStatusCodes { get; set; } - public System.Collections.Generic.IList FailedRequestTargets { get; set; } + public System.Collections.Generic.IList FailedRequestTargets { get; set; } public System.TimeSpan FlushTimeout { get; set; } public System.Net.IWebProxy? HttpProxy { get; set; } public System.TimeSpan InitCacheFlushTimeout { get; set; } @@ -697,8 +697,8 @@ namespace Sentry public System.TimeSpan ShutdownTimeout { get; set; } public string SpotlightUrl { get; set; } public Sentry.StackTraceMode StackTraceMode { get; set; } - public System.Collections.Generic.ICollection TagFilters { get; set; } - public System.Collections.Generic.IList TracePropagationTargets { get; set; } + public System.Collections.Generic.IList TagFilters { get; set; } + public System.Collections.Generic.IList TracePropagationTargets { get; set; } public double? TracesSampleRate { get; set; } public System.Func? TracesSampler { get; set; } public Sentry.Extensibility.ITransport? Transport { get; set; } @@ -1118,16 +1118,16 @@ namespace Sentry public StreamAttachmentContent(System.IO.Stream stream) { } public System.IO.Stream GetStream() { } } - [System.ComponentModel.TypeConverter(typeof(Sentry.SubstringOrRegexPatternTypeConverter))] - public class SubstringOrRegexPattern + [System.ComponentModel.TypeConverter(typeof(Sentry.StringOrRegexTypeConverter))] + public class StringOrRegex { - public SubstringOrRegexPattern(System.Text.RegularExpressions.Regex regex) { } - public SubstringOrRegexPattern(string substringOrRegexPattern, System.StringComparison comparison = 5) { } + public StringOrRegex(string stringOrRegex) { } + public StringOrRegex(System.Text.RegularExpressions.Regex regex) { } public override bool Equals(object? obj) { } public override int GetHashCode() { } public override string ToString() { } - public static Sentry.SubstringOrRegexPattern op_Implicit(string substringOrRegexPattern) { } - public static Sentry.SubstringOrRegexPattern op_Implicit(System.Text.RegularExpressions.Regex regex) { } + public static Sentry.StringOrRegex op_Implicit(string stringOrRegex) { } + public static Sentry.StringOrRegex op_Implicit(System.Text.RegularExpressions.Regex regex) { } } public class TransactionContext : Sentry.SpanContext, Sentry.ITransactionContext, Sentry.Protocol.ITraceContext { diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt index f593dedb0a..1306e6e938 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt @@ -42,19 +42,19 @@ namespace Sentry } public static class BuiltInSystemDiagnosticsMeters { - public static readonly Sentry.SubstringOrRegexPattern MicrosoftAspNetCoreDiagnostics; - public static readonly Sentry.SubstringOrRegexPattern MicrosoftAspNetCoreHeaderParsing; - public static readonly Sentry.SubstringOrRegexPattern MicrosoftAspNetCoreHosting; - public static readonly Sentry.SubstringOrRegexPattern MicrosoftAspNetCoreHttpConnections; - public static readonly Sentry.SubstringOrRegexPattern MicrosoftAspNetCoreRateLimiting; - public static readonly Sentry.SubstringOrRegexPattern MicrosoftAspNetCoreRouting; - public static readonly Sentry.SubstringOrRegexPattern MicrosoftAspNetCoreServerKestrel; - public static readonly Sentry.SubstringOrRegexPattern MicrosoftExtensionsDiagnosticsHealthChecks; - public static readonly Sentry.SubstringOrRegexPattern MicrosoftExtensionsDiagnosticsResourceMonitoring; - public static readonly Sentry.SubstringOrRegexPattern OpenTelemetryInstrumentationRuntime; - public static readonly Sentry.SubstringOrRegexPattern SystemNetHttp; - public static readonly Sentry.SubstringOrRegexPattern SystemNetNameResolution; - public static System.Collections.Generic.IList All { get; } + public static readonly Sentry.StringOrRegex MicrosoftAspNetCoreDiagnostics; + public static readonly Sentry.StringOrRegex MicrosoftAspNetCoreHeaderParsing; + public static readonly Sentry.StringOrRegex MicrosoftAspNetCoreHosting; + public static readonly Sentry.StringOrRegex MicrosoftAspNetCoreHttpConnections; + public static readonly Sentry.StringOrRegex MicrosoftAspNetCoreRateLimiting; + public static readonly Sentry.StringOrRegex MicrosoftAspNetCoreRouting; + public static readonly Sentry.StringOrRegex MicrosoftAspNetCoreServerKestrel; + public static readonly Sentry.StringOrRegex MicrosoftExtensionsDiagnosticsHealthChecks; + public static readonly Sentry.StringOrRegex MicrosoftExtensionsDiagnosticsResourceMonitoring; + public static readonly Sentry.StringOrRegex OpenTelemetryInstrumentationRuntime; + public static readonly Sentry.StringOrRegex SystemNetHttp; + public static readonly Sentry.StringOrRegex SystemNetNameResolution; + public static System.Collections.Generic.IList All { get; } } public class ByteAttachmentContent : Sentry.IAttachmentContent { @@ -107,8 +107,8 @@ namespace Sentry public class ExperimentalMetricsOptions { public ExperimentalMetricsOptions() { } - public System.Collections.Generic.IList CaptureSystemDiagnosticsInstruments { get; set; } - public System.Collections.Generic.IList CaptureSystemDiagnosticsMeters { get; set; } + public System.Collections.Generic.IList CaptureSystemDiagnosticsInstruments { get; set; } + public System.Collections.Generic.IList CaptureSystemDiagnosticsMeters { get; set; } public bool EnableCodeLocations { get; set; } } public class FileAttachmentContent : Sentry.IAttachmentContent @@ -672,7 +672,7 @@ namespace Sentry public string? Environment { get; set; } public Sentry.ExperimentalMetricsOptions? ExperimentalMetrics { get; set; } public System.Collections.Generic.IList FailedRequestStatusCodes { get; set; } - public System.Collections.Generic.IList FailedRequestTargets { get; set; } + public System.Collections.Generic.IList FailedRequestTargets { get; set; } public System.TimeSpan FlushTimeout { get; set; } public System.Net.IWebProxy? HttpProxy { get; set; } public System.TimeSpan InitCacheFlushTimeout { get; set; } @@ -698,8 +698,8 @@ namespace Sentry public System.TimeSpan ShutdownTimeout { get; set; } public string SpotlightUrl { get; set; } public Sentry.StackTraceMode StackTraceMode { get; set; } - public System.Collections.Generic.ICollection TagFilters { get; set; } - public System.Collections.Generic.IList TracePropagationTargets { get; set; } + public System.Collections.Generic.IList TagFilters { get; set; } + public System.Collections.Generic.IList TracePropagationTargets { get; set; } public double? TracesSampleRate { get; set; } public System.Func? TracesSampler { get; set; } public Sentry.Extensibility.ITransport? Transport { get; set; } @@ -1120,16 +1120,16 @@ namespace Sentry public StreamAttachmentContent(System.IO.Stream stream) { } public System.IO.Stream GetStream() { } } - [System.ComponentModel.TypeConverter(typeof(Sentry.SubstringOrRegexPatternTypeConverter))] - public class SubstringOrRegexPattern + [System.ComponentModel.TypeConverter(typeof(Sentry.StringOrRegexTypeConverter))] + public class StringOrRegex { - public SubstringOrRegexPattern(System.Text.RegularExpressions.Regex regex) { } - public SubstringOrRegexPattern(string substringOrRegexPattern, System.StringComparison comparison = 5) { } + public StringOrRegex(string stringOrRegex) { } + public StringOrRegex(System.Text.RegularExpressions.Regex regex) { } public override bool Equals(object? obj) { } public override int GetHashCode() { } public override string ToString() { } - public static Sentry.SubstringOrRegexPattern op_Implicit(string substringOrRegexPattern) { } - public static Sentry.SubstringOrRegexPattern op_Implicit(System.Text.RegularExpressions.Regex regex) { } + public static Sentry.StringOrRegex op_Implicit(string stringOrRegex) { } + public static Sentry.StringOrRegex op_Implicit(System.Text.RegularExpressions.Regex regex) { } } public class TransactionContext : Sentry.SpanContext, Sentry.ITransactionContext, Sentry.Protocol.ITraceContext { diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt index 84c37bccc2..bd232912fd 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt @@ -42,19 +42,19 @@ namespace Sentry } public static class BuiltInSystemDiagnosticsMeters { - public static readonly Sentry.SubstringOrRegexPattern MicrosoftAspNetCoreDiagnostics; - public static readonly Sentry.SubstringOrRegexPattern MicrosoftAspNetCoreHeaderParsing; - public static readonly Sentry.SubstringOrRegexPattern MicrosoftAspNetCoreHosting; - public static readonly Sentry.SubstringOrRegexPattern MicrosoftAspNetCoreHttpConnections; - public static readonly Sentry.SubstringOrRegexPattern MicrosoftAspNetCoreRateLimiting; - public static readonly Sentry.SubstringOrRegexPattern MicrosoftAspNetCoreRouting; - public static readonly Sentry.SubstringOrRegexPattern MicrosoftAspNetCoreServerKestrel; - public static readonly Sentry.SubstringOrRegexPattern MicrosoftExtensionsDiagnosticsHealthChecks; - public static readonly Sentry.SubstringOrRegexPattern MicrosoftExtensionsDiagnosticsResourceMonitoring; - public static readonly Sentry.SubstringOrRegexPattern OpenTelemetryInstrumentationRuntime; - public static readonly Sentry.SubstringOrRegexPattern SystemNetHttp; - public static readonly Sentry.SubstringOrRegexPattern SystemNetNameResolution; - public static System.Collections.Generic.IList All { get; } + public static readonly Sentry.StringOrRegex MicrosoftAspNetCoreDiagnostics; + public static readonly Sentry.StringOrRegex MicrosoftAspNetCoreHeaderParsing; + public static readonly Sentry.StringOrRegex MicrosoftAspNetCoreHosting; + public static readonly Sentry.StringOrRegex MicrosoftAspNetCoreHttpConnections; + public static readonly Sentry.StringOrRegex MicrosoftAspNetCoreRateLimiting; + public static readonly Sentry.StringOrRegex MicrosoftAspNetCoreRouting; + public static readonly Sentry.StringOrRegex MicrosoftAspNetCoreServerKestrel; + public static readonly Sentry.StringOrRegex MicrosoftExtensionsDiagnosticsHealthChecks; + public static readonly Sentry.StringOrRegex MicrosoftExtensionsDiagnosticsResourceMonitoring; + public static readonly Sentry.StringOrRegex OpenTelemetryInstrumentationRuntime; + public static readonly Sentry.StringOrRegex SystemNetHttp; + public static readonly Sentry.StringOrRegex SystemNetNameResolution; + public static System.Collections.Generic.IList All { get; } } public class ByteAttachmentContent : Sentry.IAttachmentContent { @@ -105,8 +105,8 @@ namespace Sentry public class ExperimentalMetricsOptions { public ExperimentalMetricsOptions() { } - public System.Collections.Generic.IList CaptureSystemDiagnosticsInstruments { get; set; } - public System.Collections.Generic.IList CaptureSystemDiagnosticsMeters { get; set; } + public System.Collections.Generic.IList CaptureSystemDiagnosticsInstruments { get; set; } + public System.Collections.Generic.IList CaptureSystemDiagnosticsMeters { get; set; } public bool EnableCodeLocations { get; set; } } public class FileAttachmentContent : Sentry.IAttachmentContent @@ -669,7 +669,7 @@ namespace Sentry public string? Environment { get; set; } public Sentry.ExperimentalMetricsOptions? ExperimentalMetrics { get; set; } public System.Collections.Generic.IList FailedRequestStatusCodes { get; set; } - public System.Collections.Generic.IList FailedRequestTargets { get; set; } + public System.Collections.Generic.IList FailedRequestTargets { get; set; } public System.TimeSpan FlushTimeout { get; set; } public System.Net.IWebProxy? HttpProxy { get; set; } public System.TimeSpan InitCacheFlushTimeout { get; set; } @@ -695,8 +695,8 @@ namespace Sentry public System.TimeSpan ShutdownTimeout { get; set; } public string SpotlightUrl { get; set; } public Sentry.StackTraceMode StackTraceMode { get; set; } - public System.Collections.Generic.ICollection TagFilters { get; set; } - public System.Collections.Generic.IList TracePropagationTargets { get; set; } + public System.Collections.Generic.IList TagFilters { get; set; } + public System.Collections.Generic.IList TracePropagationTargets { get; set; } public double? TracesSampleRate { get; set; } public System.Func? TracesSampler { get; set; } public Sentry.Extensibility.ITransport? Transport { get; set; } @@ -1115,16 +1115,16 @@ namespace Sentry public StreamAttachmentContent(System.IO.Stream stream) { } public System.IO.Stream GetStream() { } } - [System.ComponentModel.TypeConverter(typeof(Sentry.SubstringOrRegexPatternTypeConverter))] - public class SubstringOrRegexPattern + [System.ComponentModel.TypeConverter(typeof(Sentry.StringOrRegexTypeConverter))] + public class StringOrRegex { - public SubstringOrRegexPattern(System.Text.RegularExpressions.Regex regex) { } - public SubstringOrRegexPattern(string substringOrRegexPattern, System.StringComparison comparison = 5) { } + public StringOrRegex(string stringOrRegex) { } + public StringOrRegex(System.Text.RegularExpressions.Regex regex) { } public override bool Equals(object? obj) { } public override int GetHashCode() { } public override string ToString() { } - public static Sentry.SubstringOrRegexPattern op_Implicit(string substringOrRegexPattern) { } - public static Sentry.SubstringOrRegexPattern op_Implicit(System.Text.RegularExpressions.Regex regex) { } + public static Sentry.StringOrRegex op_Implicit(string stringOrRegex) { } + public static Sentry.StringOrRegex op_Implicit(System.Text.RegularExpressions.Regex regex) { } } public class TransactionContext : Sentry.SpanContext, Sentry.ITransactionContext, Sentry.Protocol.ITraceContext { diff --git a/test/Sentry.Tests/BuiltInSystemDiagnosticsMetersTests.cs b/test/Sentry.Tests/BuiltInSystemDiagnosticsMetersTests.cs index d033049610..c9b045f776 100644 --- a/test/Sentry.Tests/BuiltInSystemDiagnosticsMetersTests.cs +++ b/test/Sentry.Tests/BuiltInSystemDiagnosticsMetersTests.cs @@ -93,12 +93,12 @@ public void SystemNetHttp_ExactString_Matches() internal static class BuiltInSystemDiagnosticsMetersTestsExtensions { - internal static void ShouldMatchOnlyExactText(this SubstringOrRegexPattern pattern, string actual) + internal static void ShouldMatchOnlyExactText(this StringOrRegex pattern, string actual) { var withPrefix = "prefix" + actual; var withSuffix = actual + "suffix"; - pattern.IsMatch(actual).Should().BeTrue(); - pattern.IsMatch(withPrefix).Should().BeFalse(); - pattern.IsMatch(withSuffix).Should().BeFalse(); + SubstringOrPatternMatcher.Default.IsMatch(pattern, actual).Should().BeTrue(); + SubstringOrPatternMatcher.Default.IsMatch(pattern, withPrefix).Should().BeFalse(); + SubstringOrPatternMatcher.Default.IsMatch(pattern, withSuffix).Should().BeFalse(); } } diff --git a/test/Sentry.Tests/FailedRequestTargetsTests.cs b/test/Sentry.Tests/FailedRequestTargetsTests.cs index d7392ea3c4..729e7cc593 100644 --- a/test/Sentry.Tests/FailedRequestTargetsTests.cs +++ b/test/Sentry.Tests/FailedRequestTargetsTests.cs @@ -26,7 +26,7 @@ public void SentryOptions_FailedRequestTargets_AddRemovesDefault() public void SentryOptions_FailedRequestTargets_SetRemovesDefault() { var options = new SentryOptions(); - var targets = new List + var targets = new List { ".*", "foo", @@ -45,9 +45,9 @@ public void SentryOptions_FailedRequestTargets_DefaultMatchesAll() { var options = new SentryOptions(); - var result1 = options.FailedRequestTargets.ContainsMatch("foo"); - var result2 = options.FailedRequestTargets.ContainsMatch(""); - var result3 = options.FailedRequestTargets.ContainsMatch(null!); + var result1 = options.FailedRequestTargets.MatchesSubstringOrRegex("foo"); + var result2 = options.FailedRequestTargets.MatchesSubstringOrRegex(""); + var result3 = options.FailedRequestTargets.MatchesSubstringOrRegex(null!); Assert.True(result1); Assert.True(result2); @@ -59,12 +59,12 @@ public void SentryOptions_FailedRequestTargets_EmptyMatchesNone() { var options = new SentryOptions { - FailedRequestTargets = new List() + FailedRequestTargets = new List() }; - var result1 = options.FailedRequestTargets.ContainsMatch("foo"); - var result2 = options.FailedRequestTargets.ContainsMatch(""); - var result3 = options.FailedRequestTargets.ContainsMatch(null!); + var result1 = options.FailedRequestTargets.MatchesSubstringOrRegex("foo"); + var result2 = options.FailedRequestTargets.MatchesSubstringOrRegex(""); + var result3 = options.FailedRequestTargets.MatchesSubstringOrRegex(null!); Assert.False(result1); Assert.False(result2); @@ -76,7 +76,7 @@ public void SentryOptions_FailedRequestTargets_OneMatch() { var options = new SentryOptions { - FailedRequestTargets = new List + FailedRequestTargets = new List { "foo", "localhost", @@ -84,7 +84,7 @@ public void SentryOptions_FailedRequestTargets_OneMatch() } }; - var result = options.FailedRequestTargets.ContainsMatch("http://localhost/abc/123"); + var result = options.FailedRequestTargets.MatchesSubstringOrRegex("http://localhost/abc/123"); Assert.True(result); } @@ -93,7 +93,7 @@ public void SentryOptions_FailedRequestTargets_MultipleMatches() { var options = new SentryOptions { - FailedRequestTargets = new List + FailedRequestTargets = new List { "foo", "localhost", @@ -101,7 +101,7 @@ public void SentryOptions_FailedRequestTargets_MultipleMatches() } }; - var result = options.FailedRequestTargets.ContainsMatch("http://localhost/foo/123"); + var result = options.FailedRequestTargets.MatchesSubstringOrRegex("http://localhost/foo/123"); Assert.True(result); } @@ -110,7 +110,7 @@ public void SentryOptions_FailedRequestTargets_NoMatches() { var options = new SentryOptions { - FailedRequestTargets = new List + FailedRequestTargets = new List { "foo", "localhost", @@ -118,7 +118,7 @@ public void SentryOptions_FailedRequestTargets_NoMatches() } }; - var result = options.FailedRequestTargets.ContainsMatch("https://sentry.io/abc/123"); + var result = options.FailedRequestTargets.MatchesSubstringOrRegex("https://sentry.io/abc/123"); Assert.False(result); } } diff --git a/test/Sentry.Tests/ScopeTests.cs b/test/Sentry.Tests/ScopeTests.cs index 6277367ca4..5815cdb68c 100644 --- a/test/Sentry.Tests/ScopeTests.cs +++ b/test/Sentry.Tests/ScopeTests.cs @@ -605,7 +605,7 @@ public void Filtered_tags_are_not_set() var scope = new Scope(new SentryOptions { - TagFilters = new[] { new SubstringOrRegexPattern("AzureFunctions_") } + TagFilters = new[] { new StringOrRegex("AzureFunctions_") } }); foreach (var (key, value) in tags) diff --git a/test/Sentry.Tests/SentryGraphQlHttpFailedRequestHandlerTests.cs b/test/Sentry.Tests/SentryGraphQlHttpFailedRequestHandlerTests.cs index 63aba9e397..65af1ae103 100644 --- a/test/Sentry.Tests/SentryGraphQlHttpFailedRequestHandlerTests.cs +++ b/test/Sentry.Tests/SentryGraphQlHttpFailedRequestHandlerTests.cs @@ -66,7 +66,7 @@ public void HandleResponse_NoMatchingTarget_DontCapture() var options = new SentryOptions { CaptureFailedRequests = true, - FailedRequestTargets = new List { "http://foo/" } + FailedRequestTargets = new List { "http://foo/" } }; var sut = new SentryGraphQLHttpFailedRequestHandler(hub, options); diff --git a/test/Sentry.Tests/SentryHttpFailedRequestHandlerTests.cs b/test/Sentry.Tests/SentryHttpFailedRequestHandlerTests.cs index 1d336e19a8..4e81fac3c6 100644 --- a/test/Sentry.Tests/SentryHttpFailedRequestHandlerTests.cs +++ b/test/Sentry.Tests/SentryHttpFailedRequestHandlerTests.cs @@ -86,7 +86,7 @@ public void HandleResponse_NoMatchingTarget_DontCapture() var options = new SentryOptions { CaptureFailedRequests = true, - FailedRequestTargets = new List { "http://foo/" } + FailedRequestTargets = new List { "http://foo/" } }; var sut = GetSut(options); diff --git a/test/Sentry.Tests/SentryHttpMessageHandlerTests.cs b/test/Sentry.Tests/SentryHttpMessageHandlerTests.cs index fc38202019..7cc372f5b9 100644 --- a/test/Sentry.Tests/SentryHttpMessageHandlerTests.cs +++ b/test/Sentry.Tests/SentryHttpMessageHandlerTests.cs @@ -41,7 +41,7 @@ public async Task SendAsync_SentryTraceHeaderNotSet_SetsHeader_WhenUrlMatchesPro var failedRequestHandler = Substitute.For(); var options = new SentryOptions { - TracePropagationTargets = new List + TracePropagationTargets = new List { new("localhost") } @@ -73,7 +73,7 @@ public async Task SendAsync_SentryTraceHeaderNotSet_DoesntSetHeader_WhenUrlDoesn var failedRequestHandler = Substitute.For(); var options = new SentryOptions { - TracePropagationTargets = new List + TracePropagationTargets = new List { new("foo") } @@ -323,7 +323,7 @@ public void Send_SentryTraceHeaderNotSet_SetsHeader_WhenUrlMatchesPropagationOpt var failedRequestHandler = Substitute.For(); var options = new SentryOptions { - TracePropagationTargets = new List + TracePropagationTargets = new List { new("localhost") } @@ -355,7 +355,7 @@ public void Send_SentryTraceHeaderNotSet_DoesntSetHeader_WhenUrlDoesntMatchesPro var failedRequestHandler = Substitute.For(); var options = new SentryOptions { - TracePropagationTargets = new List + TracePropagationTargets = new List { new("foo") } diff --git a/test/Sentry.Tests/Internals/StringOrRegexTests.cs b/test/Sentry.Tests/StringOrRegexTests.cs similarity index 78% rename from test/Sentry.Tests/Internals/StringOrRegexTests.cs rename to test/Sentry.Tests/StringOrRegexTests.cs index 407ab20d85..2799f2fffd 100644 --- a/test/Sentry.Tests/Internals/StringOrRegexTests.cs +++ b/test/Sentry.Tests/StringOrRegexTests.cs @@ -1,4 +1,4 @@ -namespace Sentry.Tests.Internals; +namespace Sentry.Tests; public class StringOrRegexTests { @@ -6,7 +6,7 @@ public class StringOrRegexTests public void StringOrRegex_ImplicitlyConvertsFromString() { StringOrRegex target = "abc"; - target._prefix.Should().Be("abc"); + target._string.Should().Be("abc"); target._regex.Should().BeNull(); } @@ -14,7 +14,7 @@ public void StringOrRegex_ImplicitlyConvertsFromString() public void StringOrRegex_ImplicitlyConvertsFromRegex() { StringOrRegex target = new Regex("^abc.*ghi$"); - target._prefix.Should().BeNull(); + target._string.Should().BeNull(); target._regex.Should().NotBeNull(); target._regex?.ToString().Should().Be("^abc.*ghi$"); } diff --git a/test/Sentry.Tests/SubstringOrPatternMatcherTests.cs b/test/Sentry.Tests/SubstringOrPatternMatcherTests.cs new file mode 100644 index 0000000000..bdea298f7c --- /dev/null +++ b/test/Sentry.Tests/SubstringOrPatternMatcherTests.cs @@ -0,0 +1,76 @@ +namespace Sentry.Tests; + +public class SubstringOrPatternMatcherTests +{ + private static class Fixture + { + public static SubstringOrPatternMatcher GetSut() => new(); + public static SubstringOrPatternMatcher GetSut(StringComparison comparison) => new(comparison); + } + + [Theory] + [InlineData("cde", "abcdef", true)] + [InlineData("cDe", "ABCdEF", true)] + [InlineData("xyz", "abcdef", false)] + public void Substring_Matches(string substring, string testString, bool expected) + { + // Arrange + var sut = Fixture.GetSut(); + + // Act + var isMatch = sut.IsMatch(substring, testString); + + // Assert + isMatch.Should().Be(expected); + } + + [Theory] + [InlineData("CdE", true)] + [InlineData("cDe", false)] + public void Substring_Matches_CaseSensitive(string testString, bool expected) + { + // Arrange + var sut = Fixture.GetSut(StringComparison.Ordinal); + + // Act + var isMatch = sut.IsMatch(testString, "ABCdEF"); + + // Assert + isMatch.Should().Be(expected); + } + + [Theory] + [InlineData("^abc.*ghi$", "abcdefghi", true)] + [InlineData("^abc.*ghi$", "aBcDeFgHi", true)] // Case insensitive + [InlineData("^abc.*ghi$", "abcdef", false)] + public void Regex_Matches(string pattern, string testString, bool expected) + { + // Arrange + var sut = Fixture.GetSut(); + var regex = new Regex(pattern, RegexOptions.IgnoreCase); + var stringOrRegex = new StringOrRegex(regex); + + // Act + var isMatch = sut.IsMatch(stringOrRegex, testString); + + // Assert + isMatch.Should().Be(expected); + } + + [Theory] + [InlineData("^aBc.*gHi$", "aBcDeFgHi", true)] + [InlineData("^abc.*ghi$", "aBcDeFgHi", false)] + public void Regex_Matches_CaseSensitive(string pattern, string testString, bool expected) + { + // Arrange + var sut = Fixture.GetSut(); + var regex = new Regex(pattern, RegexOptions.None); + var stringOrRegex = new StringOrRegex(regex); + + // Act + var isMatch = sut.IsMatch(stringOrRegex, testString); + + // Assert + isMatch.Should().Be(expected); + } +} diff --git a/test/Sentry.Tests/SubstringOrRegexPatternTests.cs b/test/Sentry.Tests/SubstringOrRegexPatternTests.cs deleted file mode 100644 index 6921e21e70..0000000000 --- a/test/Sentry.Tests/SubstringOrRegexPatternTests.cs +++ /dev/null @@ -1,110 +0,0 @@ -namespace Sentry.Tests; - -public class SubstringOrRegexPatternTests -{ - [Fact] - public void Substring_Matches() - { - var target = new SubstringOrRegexPattern("cde"); - var isMatch = target.IsMatch("abcdef"); - Assert.True(isMatch); - } - - [Fact] - public void Substring_Doesnt_Match() - { - var target = new SubstringOrRegexPattern("xyz"); - var isMatch = target.IsMatch("abcdef"); - Assert.False(isMatch); - } - - [Fact] - public void Substring_Matches_CaseInsensitive_ByDefault() - { - var target = new SubstringOrRegexPattern("cDe"); - var isMatch = target.IsMatch("ABCdEF"); - Assert.True(isMatch); - } - - [Fact] - public void Substring_Matches_CaseSensitive() - { - var target = new SubstringOrRegexPattern("CdE", StringComparison.Ordinal); - var isMatch = target.IsMatch("ABCdEF"); - Assert.True(isMatch); - } - - [Fact] - public void Substring_Doesnt_Match_WhenCaseSensitive() - { - var target = new SubstringOrRegexPattern("cDe", StringComparison.Ordinal); - var isMatch = target.IsMatch("ABCdEF"); - Assert.False(isMatch); - } - - [Fact] - public void Regex_Object_Matches() - { - var regex = new Regex("^abc.*ghi$"); - var target = new SubstringOrRegexPattern(regex); - var isMatch = target.IsMatch("abcdefghi"); - Assert.True(isMatch); - } - - [Fact] - public void Regex_Object_Doesnt_Match() - { - var regex = new Regex("^abc.*ghi$"); - var target = new SubstringOrRegexPattern(regex); - var isMatch = target.IsMatch("abcdef"); - Assert.False(isMatch); - } - - [Fact] - public void Regex_Pattern_Matches() - { - var target = new SubstringOrRegexPattern("^abc.*ghi$"); - var isMatch = target.IsMatch("abcdefghi"); - Assert.True(isMatch); - } - - [Fact] - public void Regex_Pattern_Matches_CaseInsensitive_ByDefault() - { - var target = new SubstringOrRegexPattern("^abc.*ghi$"); - var isMatch = target.IsMatch("aBcDeFgHi"); - Assert.True(isMatch); - } - - [Fact] - public void Regex_Pattern_Matches_CaseSensitive() - { - var target = new SubstringOrRegexPattern("^aBc.*gHi$", StringComparison.Ordinal); - var isMatch = target.IsMatch("aBcDeFgHi"); - Assert.True(isMatch); - } - - [Fact] - public void Regex_Pattern_Doesnt_Match_WhenCaseSensitive() - { - var target = new SubstringOrRegexPattern("^abc.*ghi$", StringComparison.Ordinal); - var isMatch = target.IsMatch("aBcDeFgHi"); - Assert.False(isMatch); - } - - [Fact] - public void SubstringOrRegexPattern_ImplicitlyConvertsFromString() - { - SubstringOrRegexPattern target = "^abc.*ghi$"; - var isMatch = target.IsMatch("abcdefghi"); - Assert.True(isMatch); - } - - [Fact] - public void SubstringOrRegexPattern_ImplicitlyConvertsFromRegex() - { - SubstringOrRegexPattern target = new Regex("^abc.*ghi$"); - var isMatch = target.IsMatch("abcdefghi"); - Assert.True(isMatch); - } -} diff --git a/test/Sentry.Tests/TracePropagationTargetTests.cs b/test/Sentry.Tests/TracePropagationTargetTests.cs index 171b3d5798..cbba80c283 100644 --- a/test/Sentry.Tests/TracePropagationTargetTests.cs +++ b/test/Sentry.Tests/TracePropagationTargetTests.cs @@ -14,8 +14,8 @@ public void SentryOptions_TracePropagationTargets_DefaultAll() public void SentryOptions_TracePropagationTargets_AddRemovesDefault() { var options = new SentryOptions(); - options.TracePropagationTargets.Add(new SubstringOrRegexPattern("foo")); - options.TracePropagationTargets.Add(new SubstringOrRegexPattern("bar")); + options.TracePropagationTargets.Add(new StringOrRegex("foo")); + options.TracePropagationTargets.Add(new StringOrRegex("bar")); Assert.Equal(2, options.TracePropagationTargets.Count); Assert.Equal("foo", options.TracePropagationTargets[0].ToString()); @@ -28,9 +28,9 @@ public void SentryOptions_TracePropagationTargets_SetRemovesDefault() var options = new SentryOptions(); var targets = new[] { - new SubstringOrRegexPattern(".*"), - new SubstringOrRegexPattern("foo"), - new SubstringOrRegexPattern("bar") + new StringOrRegex(".*"), + new StringOrRegex("foo"), + new StringOrRegex("bar") }; options.TracePropagationTargets = targets; @@ -45,9 +45,9 @@ public void SentryOptions_TracePropagationTargets_DefaultPropagatesAll() { var options = new SentryOptions(); - var result1 = options.TracePropagationTargets.ContainsMatch("foo"); - var result2 = options.TracePropagationTargets.ContainsMatch(""); - var result3 = options.TracePropagationTargets.ContainsMatch(null!); + var result1 = options.TracePropagationTargets.MatchesSubstringOrRegex("foo"); + var result2 = options.TracePropagationTargets.MatchesSubstringOrRegex(""); + var result3 = options.TracePropagationTargets.MatchesSubstringOrRegex(null!); Assert.True(result1); Assert.True(result2); @@ -59,12 +59,12 @@ public void SentryOptions_TracePropagationTargets_EmptyPropagatesNone() { var options = new SentryOptions { - TracePropagationTargets = new List() + TracePropagationTargets = new List() }; - var result1 = options.TracePropagationTargets.ContainsMatch("foo"); - var result2 = options.TracePropagationTargets.ContainsMatch(""); - var result3 = options.TracePropagationTargets.ContainsMatch(null!); + var result1 = options.TracePropagationTargets.MatchesSubstringOrRegex("foo"); + var result2 = options.TracePropagationTargets.MatchesSubstringOrRegex(""); + var result3 = options.TracePropagationTargets.MatchesSubstringOrRegex(null!); Assert.False(result1); Assert.False(result2); @@ -76,7 +76,7 @@ public void SentryOptions_TracePropagationTargets_OneMatchPropagates() { var options = new SentryOptions { - TracePropagationTargets = new List + TracePropagationTargets = new List { new("foo"), new("localhost"), @@ -84,7 +84,7 @@ public void SentryOptions_TracePropagationTargets_OneMatchPropagates() } }; - var result = options.TracePropagationTargets.ContainsMatch("http://localhost/abc/123"); + var result = options.TracePropagationTargets.MatchesSubstringOrRegex("http://localhost/abc/123"); Assert.True(result); } @@ -93,7 +93,7 @@ public void SentryOptions_TracePropagationTargets_MultipleMatchesPropagates() { var options = new SentryOptions { - TracePropagationTargets = new List + TracePropagationTargets = new List { new("foo"), new("localhost"), @@ -101,7 +101,7 @@ public void SentryOptions_TracePropagationTargets_MultipleMatchesPropagates() } }; - var result = options.TracePropagationTargets.ContainsMatch("http://localhost/foo/123"); + var result = options.TracePropagationTargets.MatchesSubstringOrRegex("http://localhost/foo/123"); Assert.True(result); } @@ -110,7 +110,7 @@ public void SentryOptions_TracePropagationTargets_NoMatchesDoesntPropagates() { var options = new SentryOptions { - TracePropagationTargets = new List + TracePropagationTargets = new List { new("foo"), new("localhost"), @@ -118,7 +118,7 @@ public void SentryOptions_TracePropagationTargets_NoMatchesDoesntPropagates() } }; - var result = options.TracePropagationTargets.ContainsMatch("https://sentry.io/abc/123"); + var result = options.TracePropagationTargets.MatchesSubstringOrRegex("https://sentry.io/abc/123"); Assert.False(result); } } From 9f0e0ffb46278a74ca89c37812bed15b45d7bce5 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Tue, 17 Sep 2024 12:46:23 +1200 Subject: [PATCH 007/363] Scope.Transaction is now AsyncLocal (#3596) --- CHANGELOG.md | 2 + src/Sentry/Scope.cs | 72 +++++++++++++++++-- .../Internals/SentryScopeManagerTests.cs | 26 +++++++ 3 files changed, 94 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fe54978e48..6587015203 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ - `Device.BatteryLevel` and `Device.ProcessorFrequency` are now stored as floats rather than ints, to align with the Cocoa and Java SDKs ([#3567](https://github.com/getsentry/sentry-dotnet/pull/3567)) - `SentryOptions.EnableTracing` has been removed. Instead, tracing should be enabled or disabled by setting the `SentryOptions.TracesSampleRate` or by using `SentryOptions.TracesSampler` to configure a sampling function ([#3569](https://github.com/getsentry/sentry-dotnet/pull/3569)) - The `FailedRequestTargets`, `TagFilters` and `TracePropagationTargets` options have all been changed from `SubstringOrRegexPattern` to `IList` ([#3566](https://github.com/getsentry/sentry-dotnet/pull/3566)) +- `Scope.Transaction` is now always stored as an `AsyncLocal` also in [Global Mode](https://docs.sentry.io/platforms/dotnet/configuration/options/#is-global-mode-enabled), to prevent auto-instrumented spans from the UI ending up parented to transactions from a background task (or vice versa). ([#3596](https://github.com/getsentry/sentry-dotnet/pull/3596)) + ## Unreleased ### Fixes diff --git a/src/Sentry/Scope.cs b/src/Sentry/Scope.cs index 8c29c7edd8..9cd253b216 100644 --- a/src/Sentry/Scope.cs +++ b/src/Sentry/Scope.cs @@ -1,3 +1,8 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; using Sentry.Extensibility; using Sentry.Internal; using Sentry.Internal.Extensions; @@ -180,15 +185,52 @@ public string? TransactionName } } - private ITransactionTracer? _transaction; + /// + /// + /// Most of the properties on the Scope should have the same affinity as the Scope... For example, when using a + /// GlobalScopeStackContainer, anything you store on the scope will be applied to all events that get sent to Sentry + /// (no matter which thread they are sent from). + /// + /// + /// Transactions are an exception, however. We don't want spans from threads created on the UI thread to be added as + /// children of Transactions/Spans that get created on the background thread, or vice versa. As such, + /// Scope.Transaction is always stored as an AsyncLocal, regardless of the ScopeStackContainer implementation. + /// + /// + /// See https://github.com/getsentry/sentry-dotnet/issues/3590 for more information. + /// + /// + private readonly AsyncLocal _transaction = new(); /// - /// Transaction. + /// The current Transaction /// public ITransactionTracer? Transaction { - get => _transaction; - set => _transaction = value; + get + { + _transactionLock.EnterReadLock(); + try + { + return _transaction.Value; + } + finally + { + _transactionLock.ExitReadLock(); + } + } + set + { + _transactionLock.EnterWriteLock(); + try + { + _transaction.Value = value; + } + finally + { + _transactionLock.ExitWriteLock(); + } + } } internal SentryPropagationContext PropagationContext { get; set; } @@ -738,6 +780,24 @@ public void AddAttachment(string filePath, AttachmentType type = AttachmentType. Path.GetFileName(filePath), contentType)); - internal void ResetTransaction(ITransactionTracer? expectedCurrentTransaction) => - Interlocked.CompareExchange(ref _transaction, null, expectedCurrentTransaction); + /// + /// We need this lock to prevent a potential race condition in . + /// + private readonly ReaderWriterLockSlim _transactionLock = new(); + + internal void ResetTransaction(ITransactionTracer? expectedCurrentTransaction) + { + _transactionLock.EnterWriteLock(); + try + { + if (ReferenceEquals(_transaction.Value, expectedCurrentTransaction)) + { + _transaction.Value = null; + } + } + finally + { + _transactionLock.ExitWriteLock(); + } + } } diff --git a/test/Sentry.Tests/Internals/SentryScopeManagerTests.cs b/test/Sentry.Tests/Internals/SentryScopeManagerTests.cs index 2c00dae4ef..f511f79f53 100644 --- a/test/Sentry.Tests/Internals/SentryScopeManagerTests.cs +++ b/test/Sentry.Tests/Internals/SentryScopeManagerTests.cs @@ -368,6 +368,32 @@ public void GlobalMode_PushScope_SameScope() client1.Should().BeSameAs(client2); } + /// + /// See https://github.com/getsentry/sentry-dotnet/issues/3590 + /// + [Fact] + public void GlobalMode_ScopeTransaction_NotGlobal() + { + // Arrange + _fixture.SentryOptions.IsGlobalModeEnabled = true; + var sut = _fixture.GetSut(); + + // Act + Task.Run(() => + { + var (scope, _) = sut.GetCurrent(); + scope.SetExtra("Foo", "Bar"); + scope.Transaction = Substitute.For(); + }); + + // Assert + Task.Run(() => + { + var (scope, _) = sut.GetCurrent(); + scope.Extra["Foo"].Should().Be("Bar"); // In global mode most scope data should be shared + scope.Transaction.Should().BeNull(); // But transaction should be isolated + }); + } [Fact] public void GlobalMode_Disabled_Uses_AsyncLocalScopeStackContainer() From a4f29fe841422c06353ef2b3b92a100261b0c8f5 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Thu, 31 Oct 2024 09:18:53 +1300 Subject: [PATCH 008/363] Removed Metrics (deprecated) (#3718) --- .generated.NoMobile.sln | 10 +- CHANGELOG.md | 1 + Sentry-CI-Build-Linux.slnf | 1 - Sentry-CI-Build-Windows.slnf | 1 - Sentry-CI-Build-macOS.slnf | 1 - Sentry.sln | 9 +- SentryCore.slnf | 1 - SentryNoMobile.slnf | 1 - .../Sentry.Samples.Console.Metrics/Program.cs | 141 ---- .../Sentry.Samples.Console.Metrics.csproj | 45 -- .../Program.cs | 25 +- scripts/generate-solution-filters-config.yaml | 1 - src/Sentry/DisabledMetricAggregator.cs | 76 --- src/Sentry/Extensibility/DisabledHub.cs | 5 - src/Sentry/Extensibility/HubAdapter.cs | 7 - src/Sentry/IHub.cs | 5 - src/Sentry/IMetricAggregator.cs | 137 ---- src/Sentry/IMetricHub.cs | 24 - .../SystemDiagnosticsMetricsIntegration.cs | 38 -- src/Sentry/Internal/Hub.cs | 18 +- .../SystemDiagnosticsMetricsListener.cs | 90 --- src/Sentry/MetricAggregator.cs | 537 --------------- src/Sentry/Platforms/Android/SentrySdk.cs | 1 - src/Sentry/SentryOptions.cs | 19 - src/Sentry/SentrySdk.cs | 7 - src/Sentry/Timing.cs | 88 --- .../BindableSentryAspNetCoreOptionsTests.cs | 4 - ...indableSentryAzureFunctionsOptionsTests.cs | 4 - .../SentryLoggingOptionsTests.cs | 4 - .../BindableSentryMauiOptionsTests.cs | 4 - ...piApprovalTests.Run.DotNet6_0.verified.txt | 24 - ...piApprovalTests.Run.DotNet7_0.verified.txt | 24 - ...piApprovalTests.Run.DotNet8_0.verified.txt | 24 - .../ApiApprovalTests.Run.Net4_8.verified.txt | 24 - .../BindableSentryOptionsTests.cs | 4 - ...ystemDiagnosticsMetricsIntegrationTests.cs | 79 --- .../SystemDiagnosticsMetricsListenerTests.cs | 300 -------- test/Sentry.Tests/MetricAggregatorTests.cs | 641 ------------------ ...y_registered.DotNet8_0.DotNet.verified.txt | 10 - ...ered.DotNet8_0.Windows.DotNet.verified.txt | 10 - test/Sentry.Tests/SentryOptionsTests.cs | 11 - test/Sentry.Tests/TimingTests.cs | 143 ---- 42 files changed, 6 insertions(+), 2593 deletions(-) delete mode 100644 samples/Sentry.Samples.Console.Metrics/Program.cs delete mode 100644 samples/Sentry.Samples.Console.Metrics/Sentry.Samples.Console.Metrics.csproj delete mode 100644 src/Sentry/DisabledMetricAggregator.cs delete mode 100644 src/Sentry/IMetricAggregator.cs delete mode 100644 src/Sentry/IMetricHub.cs delete mode 100644 src/Sentry/Integrations/SystemDiagnosticsMetricsIntegration.cs delete mode 100644 src/Sentry/Internal/SystemDiagnosticsMetricsListener.cs delete mode 100644 src/Sentry/MetricAggregator.cs delete mode 100644 src/Sentry/Timing.cs delete mode 100644 test/Sentry.Tests/Integrations/SystemDiagnosticsMetricsIntegrationTests.cs delete mode 100644 test/Sentry.Tests/Internals/SystemDiagnosticsMetricsListenerTests.cs delete mode 100644 test/Sentry.Tests/MetricAggregatorTests.cs delete mode 100644 test/Sentry.Tests/TimingTests.cs diff --git a/.generated.NoMobile.sln b/.generated.NoMobile.sln index 02c720c31a..82631572fc 100644 --- a/.generated.NoMobile.sln +++ b/.generated.NoMobile.sln @@ -155,8 +155,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FastSerialization", "module EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Samples.Console.Native", "samples\Sentry.Samples.Console.Native\Sentry.Samples.Console.Native.csproj", "{FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Samples.Console.Metrics", "samples\Sentry.Samples.Console.Metrics\Sentry.Samples.Console.Metrics.csproj", "{BD2D08FC-8675-4157-A73C-D75F6A3856D3}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Samples.MacOS", "samples\Sentry.Samples.MacOS\Sentry.Samples.MacOS.csproj", "{5B100CC0-1A78-407E-A5A5-94BC06D67461}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Samples.Hangfire", "samples\Sentry.Samples.Hangfire\Sentry.Samples.Hangfire.csproj", "{407C477D-69C0-4B02-8A68-EE6B5A81C696}" @@ -165,7 +163,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Hangfire", "src\Sent EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Hangfire.Tests", "test\Sentry.Hangfire.Tests\Sentry.Hangfire.Tests.csproj", "{46E40BE8-1AB0-4846-B0A2-A40AD0272C64}" EndProject -Project("{{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Samples.AspNetCore.WebAPI.Profiling", "samples\Sentry.Samples.AspNetCore.WebAPI.Profiling\Sentry.Samples.AspNetCore.WebAPI.Profiling.csproj", "{A5B26C14-7313-4EDC-91E3-287F9374AB75}" +Project("{00000000-0000-0000-0000-000000000000}") = "Sentry.Samples.AspNetCore.WebAPI.Profiling", "samples\Sentry.Samples.AspNetCore.WebAPI.Profiling\Sentry.Samples.AspNetCore.WebAPI.Profiling.csproj", "{A5B26C14-7313-4EDC-91E3-287F9374AB75}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "root", "root", "{233D34AB-970E-4913-AA1E-172E833FB5B2}" ProjectSection(SolutionItems) = preProject @@ -276,6 +274,7 @@ Global {4E0DC405-C372-4396-A5DF-F6AA108DA01C}.Release|Any CPU.Build.0 = Release|Any CPU {9B175EC8-6B64-4345-A158-091CB8876077}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9B175EC8-6B64-4345-A158-091CB8876077}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9B175EC8-6B64-4345-A158-091CB8876077}.Debug|Any CPU.Deploy.0 = Debug|Any CPU {9B175EC8-6B64-4345-A158-091CB8876077}.Release|Any CPU.ActiveCfg = Release|Any CPU {9B175EC8-6B64-4345-A158-091CB8876077}.Release|Any CPU.Build.0 = Release|Any CPU {EE0DC846-52F3-46AF-BC0D-DEF81150CEC0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU @@ -470,10 +469,6 @@ Global {FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2}.Debug|Any CPU.Build.0 = Debug|Any CPU {FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2}.Release|Any CPU.ActiveCfg = Release|Any CPU {FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2}.Release|Any CPU.Build.0 = Release|Any CPU - {BD2D08FC-8675-4157-A73C-D75F6A3856D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {BD2D08FC-8675-4157-A73C-D75F6A3856D3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {BD2D08FC-8675-4157-A73C-D75F6A3856D3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {BD2D08FC-8675-4157-A73C-D75F6A3856D3}.Release|Any CPU.Build.0 = Release|Any CPU {5B100CC0-1A78-407E-A5A5-94BC06D67461}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5B100CC0-1A78-407E-A5A5-94BC06D67461}.Debug|Any CPU.Build.0 = Debug|Any CPU {5B100CC0-1A78-407E-A5A5-94BC06D67461}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -574,7 +569,6 @@ Global {67269916-C417-4CEE-BD7D-CA66C3830AEE} = {A3CCA27E-4DF8-479D-833C-CAA0950715AA} {8032310D-3C06-442C-A318-F365BCC4C804} = {A3CCA27E-4DF8-479D-833C-CAA0950715AA} {FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2} = {21B42F60-5802-404E-90F0-AEBCC56760C0} - {BD2D08FC-8675-4157-A73C-D75F6A3856D3} = {21B42F60-5802-404E-90F0-AEBCC56760C0} {5B100CC0-1A78-407E-A5A5-94BC06D67461} = {21B42F60-5802-404E-90F0-AEBCC56760C0} {407C477D-69C0-4B02-8A68-EE6B5A81C696} = {21B42F60-5802-404E-90F0-AEBCC56760C0} {EADF25F5-8D02-4747-AB54-5F2BAA648471} = {230B9384-90FD-4551-A5DE-1A5C197F25B6} diff --git a/CHANGELOG.md b/CHANGELOG.md index e3f642415a..47abaca587 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - `SentryOptions.EnableTracing` has been removed. Instead, tracing should be enabled or disabled by setting the `SentryOptions.TracesSampleRate` or by using `SentryOptions.TracesSampler` to configure a sampling function ([#3569](https://github.com/getsentry/sentry-dotnet/pull/3569)) - The `FailedRequestTargets`, `TagFilters` and `TracePropagationTargets` options have all been changed from `SubstringOrRegexPattern` to `IList` ([#3566](https://github.com/getsentry/sentry-dotnet/pull/3566)) - `Scope.Transaction` is now always stored as an `AsyncLocal` also in [Global Mode](https://docs.sentry.io/platforms/dotnet/configuration/options/#is-global-mode-enabled), to prevent auto-instrumented spans from the UI ending up parented to transactions from a background task (or vice versa). ([#3596](https://github.com/getsentry/sentry-dotnet/pull/3596)) +- Sentry's Experimental Metrics feature has been deprecated and removed from the SDK. ([#3718](https://github.com/getsentry/sentry-dotnet/pull/3718)) ## Unreleased ## Unreleased diff --git a/Sentry-CI-Build-Linux.slnf b/Sentry-CI-Build-Linux.slnf index fb30347797..d772922ad2 100644 --- a/Sentry-CI-Build-Linux.slnf +++ b/Sentry-CI-Build-Linux.slnf @@ -15,7 +15,6 @@ "samples\\Sentry.Samples.Azure.Functions.Worker\\Sentry.Samples.Azure.Functions.Worker.csproj", "samples\\Sentry.Samples.Console.Basic\\Sentry.Samples.Console.Basic.csproj", "samples\\Sentry.Samples.Console.Customized\\Sentry.Samples.Console.Customized.csproj", - "samples\\Sentry.Samples.Console.Metrics\\Sentry.Samples.Console.Metrics.csproj", "samples\\Sentry.Samples.Console.Native\\Sentry.Samples.Console.Native.csproj", "samples\\Sentry.Samples.Console.Profiling\\Sentry.Samples.Console.Profiling.csproj", "samples\\Sentry.Samples.EntityFramework\\Sentry.Samples.EntityFramework.csproj", diff --git a/Sentry-CI-Build-Windows.slnf b/Sentry-CI-Build-Windows.slnf index 489fde2fc7..061bba0a20 100644 --- a/Sentry-CI-Build-Windows.slnf +++ b/Sentry-CI-Build-Windows.slnf @@ -16,7 +16,6 @@ "samples\\Sentry.Samples.Azure.Functions.Worker\\Sentry.Samples.Azure.Functions.Worker.csproj", "samples\\Sentry.Samples.Console.Basic\\Sentry.Samples.Console.Basic.csproj", "samples\\Sentry.Samples.Console.Customized\\Sentry.Samples.Console.Customized.csproj", - "samples\\Sentry.Samples.Console.Metrics\\Sentry.Samples.Console.Metrics.csproj", "samples\\Sentry.Samples.Console.Native\\Sentry.Samples.Console.Native.csproj", "samples\\Sentry.Samples.Console.Profiling\\Sentry.Samples.Console.Profiling.csproj", "samples\\Sentry.Samples.EntityFramework\\Sentry.Samples.EntityFramework.csproj", diff --git a/Sentry-CI-Build-macOS.slnf b/Sentry-CI-Build-macOS.slnf index 9bb9d4847d..0ed945bc3b 100644 --- a/Sentry-CI-Build-macOS.slnf +++ b/Sentry-CI-Build-macOS.slnf @@ -16,7 +16,6 @@ "samples\\Sentry.Samples.Azure.Functions.Worker\\Sentry.Samples.Azure.Functions.Worker.csproj", "samples\\Sentry.Samples.Console.Basic\\Sentry.Samples.Console.Basic.csproj", "samples\\Sentry.Samples.Console.Customized\\Sentry.Samples.Console.Customized.csproj", - "samples\\Sentry.Samples.Console.Metrics\\Sentry.Samples.Console.Metrics.csproj", "samples\\Sentry.Samples.Console.Native\\Sentry.Samples.Console.Native.csproj", "samples\\Sentry.Samples.Console.Profiling\\Sentry.Samples.Console.Profiling.csproj", "samples\\Sentry.Samples.EntityFramework\\Sentry.Samples.EntityFramework.csproj", diff --git a/Sentry.sln b/Sentry.sln index 4ebd081d31..82631572fc 100644 --- a/Sentry.sln +++ b/Sentry.sln @@ -155,8 +155,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FastSerialization", "module EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Samples.Console.Native", "samples\Sentry.Samples.Console.Native\Sentry.Samples.Console.Native.csproj", "{FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Samples.Console.Metrics", "samples\Sentry.Samples.Console.Metrics\Sentry.Samples.Console.Metrics.csproj", "{BD2D08FC-8675-4157-A73C-D75F6A3856D3}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Samples.MacOS", "samples\Sentry.Samples.MacOS\Sentry.Samples.MacOS.csproj", "{5B100CC0-1A78-407E-A5A5-94BC06D67461}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Samples.Hangfire", "samples\Sentry.Samples.Hangfire\Sentry.Samples.Hangfire.csproj", "{407C477D-69C0-4B02-8A68-EE6B5A81C696}" @@ -165,7 +163,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Hangfire", "src\Sent EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Hangfire.Tests", "test\Sentry.Hangfire.Tests\Sentry.Hangfire.Tests.csproj", "{46E40BE8-1AB0-4846-B0A2-A40AD0272C64}" EndProject -Project("{{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Samples.AspNetCore.WebAPI.Profiling", "samples\Sentry.Samples.AspNetCore.WebAPI.Profiling\Sentry.Samples.AspNetCore.WebAPI.Profiling.csproj", "{A5B26C14-7313-4EDC-91E3-287F9374AB75}" +Project("{00000000-0000-0000-0000-000000000000}") = "Sentry.Samples.AspNetCore.WebAPI.Profiling", "samples\Sentry.Samples.AspNetCore.WebAPI.Profiling\Sentry.Samples.AspNetCore.WebAPI.Profiling.csproj", "{A5B26C14-7313-4EDC-91E3-287F9374AB75}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "root", "root", "{233D34AB-970E-4913-AA1E-172E833FB5B2}" ProjectSection(SolutionItems) = preProject @@ -471,10 +469,6 @@ Global {FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2}.Debug|Any CPU.Build.0 = Debug|Any CPU {FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2}.Release|Any CPU.ActiveCfg = Release|Any CPU {FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2}.Release|Any CPU.Build.0 = Release|Any CPU - {BD2D08FC-8675-4157-A73C-D75F6A3856D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {BD2D08FC-8675-4157-A73C-D75F6A3856D3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {BD2D08FC-8675-4157-A73C-D75F6A3856D3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {BD2D08FC-8675-4157-A73C-D75F6A3856D3}.Release|Any CPU.Build.0 = Release|Any CPU {5B100CC0-1A78-407E-A5A5-94BC06D67461}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5B100CC0-1A78-407E-A5A5-94BC06D67461}.Debug|Any CPU.Build.0 = Debug|Any CPU {5B100CC0-1A78-407E-A5A5-94BC06D67461}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -575,7 +569,6 @@ Global {67269916-C417-4CEE-BD7D-CA66C3830AEE} = {A3CCA27E-4DF8-479D-833C-CAA0950715AA} {8032310D-3C06-442C-A318-F365BCC4C804} = {A3CCA27E-4DF8-479D-833C-CAA0950715AA} {FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2} = {21B42F60-5802-404E-90F0-AEBCC56760C0} - {BD2D08FC-8675-4157-A73C-D75F6A3856D3} = {21B42F60-5802-404E-90F0-AEBCC56760C0} {5B100CC0-1A78-407E-A5A5-94BC06D67461} = {21B42F60-5802-404E-90F0-AEBCC56760C0} {407C477D-69C0-4B02-8A68-EE6B5A81C696} = {21B42F60-5802-404E-90F0-AEBCC56760C0} {EADF25F5-8D02-4747-AB54-5F2BAA648471} = {230B9384-90FD-4551-A5DE-1A5C197F25B6} diff --git a/SentryCore.slnf b/SentryCore.slnf index b56bac9e01..d2eb48df03 100644 --- a/SentryCore.slnf +++ b/SentryCore.slnf @@ -4,7 +4,6 @@ "projects": [ "benchmarks\\Sentry.Benchmarks\\Sentry.Benchmarks.csproj", "samples\\Sentry.Samples.Console.Basic\\Sentry.Samples.Console.Basic.csproj", - "samples\\Sentry.Samples.Console.Metrics\\Sentry.Samples.Console.Metrics.csproj", "samples\\Sentry.Samples.Console.Profiling\\Sentry.Samples.Console.Profiling.csproj", "src\\Sentry.Profiling\\Sentry.Profiling.csproj", "src\\Sentry\\Sentry.csproj", diff --git a/SentryNoMobile.slnf b/SentryNoMobile.slnf index 4603d550c3..7c5571e30a 100644 --- a/SentryNoMobile.slnf +++ b/SentryNoMobile.slnf @@ -14,7 +14,6 @@ "samples\\Sentry.Samples.Azure.Functions.Worker\\Sentry.Samples.Azure.Functions.Worker.csproj", "samples\\Sentry.Samples.Console.Basic\\Sentry.Samples.Console.Basic.csproj", "samples\\Sentry.Samples.Console.Customized\\Sentry.Samples.Console.Customized.csproj", - "samples\\Sentry.Samples.Console.Metrics\\Sentry.Samples.Console.Metrics.csproj", "samples\\Sentry.Samples.Console.Native\\Sentry.Samples.Console.Native.csproj", "samples\\Sentry.Samples.Console.Profiling\\Sentry.Samples.Console.Profiling.csproj", "samples\\Sentry.Samples.EntityFramework\\Sentry.Samples.EntityFramework.csproj", diff --git a/samples/Sentry.Samples.Console.Metrics/Program.cs b/samples/Sentry.Samples.Console.Metrics/Program.cs deleted file mode 100644 index e506fc36d1..0000000000 --- a/samples/Sentry.Samples.Console.Metrics/Program.cs +++ /dev/null @@ -1,141 +0,0 @@ -#pragma warning disable CS0618 // Obsolete Warning -using System.Diagnostics.Metrics; -using System.Text.RegularExpressions; - -namespace Sentry.Samples.Console.Metrics; - -internal static class Program -{ - private static readonly Random Roll = Random.Shared; - - // Sentry also supports capturing System.Diagnostics.Metrics - private static readonly Meter HatsMeter = new("HatCo.HatStore", "1.0.0"); - private static readonly Counter HatsSold = HatsMeter.CreateCounter( - name: "hats-sold", - unit: "Hats", - description: "The number of hats sold in our store"); - - private static async Task Main() - { - // Enable the SDK - using (SentrySdk.Init(options => - { - // You can set here in code, or you can set it in the SENTRY_DSN environment variable. - // See https://docs.sentry.io/product/sentry-basics/dsn-explainer/ - options.Dsn = "https://eb18e953812b41c3aeb042e666fd3b5c@o447951.ingest.sentry.io/5428537"; - - options.Debug = true; - options.StackTraceMode = StackTraceMode.Enhanced; - options.SampleRate = 1.0f; // Not recommended in production - may adversely impact quota - options.TracesSampleRate = 1.0f; // Not recommended in production - may adversely impact quota - // Initialize some (non null) ExperimentalMetricsOptions to enable Sentry Metrics, - options.ExperimentalMetrics = new ExperimentalMetricsOptions - { - EnableCodeLocations = true, // Set this to false if you don't want to track code locations - CaptureSystemDiagnosticsInstruments = [ - // Capture System.Diagnostics.Metrics matching the name "HatCo.HatStore", which is the name - // of the custom HatsMeter defined above - "hats-sold" - ], - // Capture all built in metrics (this is the default - you can override this to capture some or - // none of these if you prefer) - CaptureSystemDiagnosticsMeters = BuiltInSystemDiagnosticsMeters.All - }; - })) - { - System.Console.WriteLine("Measure, Yeah, Measure!"); - - Action[] actions = [PlaySetBingo, CreateRevenueGauge, MeasureShrimp, SellHats]; - do - { - // Run a random action - var idx = Roll.Next(0, actions.Length); - actions[idx](); - - // Make an API call - await CallSampleApiAsync(); - - // Optional: Delay to prevent tight looping - var sleepTime = Roll.Next(1, 5); - System.Console.WriteLine($"Sleeping for {sleepTime} second(s)."); - System.Console.WriteLine("Press any key to stop..."); - Thread.Sleep(TimeSpan.FromSeconds(sleepTime)); - } - while (!System.Console.KeyAvailable); - System.Console.WriteLine("Measure up"); - } - } - - private static void PlaySetBingo() - { - const int attempts = 10; - var solution = new[] { 3, 5, 7, 11, 13, 17 }; - - // StartTimer creates a distribution that is designed to measure the amount of time it takes to run code - // blocks. By default it will use a unit of Seconds - we're configuring it to use milliseconds here though. - // The return value is an IDisposable and the timer will stop when the timer is disposed of. - using (SentrySdk.Metrics.StartTimer("bingo", MeasurementUnit.Duration.Millisecond)) - { - for (var i = 0; i < attempts; i++) - { - var guess = Roll.Next(1, 100); - // This demonstrates the use of a set metric. - SentrySdk.Metrics.Gauge("guesses", guess); - - // And this is a counter - SentrySdk.Metrics.Increment(solution.Contains(guess) ? "correct_answers" : "incorrect_answers"); - } - } - } - - private static void CreateRevenueGauge() - { - const int sampleCount = 100; - using (SentrySdk.Metrics.StartTimer(nameof(CreateRevenueGauge), MeasurementUnit.Duration.Millisecond)) - { - for (var i = 0; i < sampleCount; i++) - { - var movement = Roll.NextDouble() * 30 - Roll.NextDouble() * 10; - // This demonstrates measuring something in your app using a gauge... we're also using a custom - // measurement unit here (which is optional - by default the unit will be "None") - SentrySdk.Metrics.Gauge("revenue", movement, MeasurementUnit.Custom("$")); - } - } - } - - private static void MeasureShrimp() - { - const int sampleCount = 30; - using (SentrySdk.Metrics.StartTimer(nameof(MeasureShrimp), MeasurementUnit.Duration.Millisecond)) - { - for (var i = 0; i < sampleCount; i++) - { - var sizeOfShrimp = 15 + Roll.NextDouble() * 30; - // This is an example of emitting a distribution metric - SentrySdk.Metrics.Distribution("shrimp.size", sizeOfShrimp, MeasurementUnit.Custom("cm")); - } - } - } - - private static void SellHats() - { - // Here we're emitting the metric using System.Diagnostics.Metrics instead of SentrySdk.Metrics. - // We won't see accurate code locations for these, so Sentry.Metrics are preferable but support - // for System.Diagnostics.Metrics means Sentry can collect a bunch built in metrics without you - // having to instrument anything... see case 4 below - HatsSold.Add(Roll.Next(0, 1000)); - } - - private static async Task CallSampleApiAsync() - { - // Here we demonstrate collecting some built in metrics for HTTP requests... this works because - // we've configured ExperimentalMetricsOptions.CaptureInstruments to match "http.client.*" - // - // See https://learn.microsoft.com/en-us/dotnet/core/diagnostics/built-in-metrics-system-net#systemnethttp - var httpClient = new HttpClient(); - var url = "https://api.sampleapis.com/coffee/hot"; - var result = await httpClient.GetAsync(url); - System.Console.WriteLine($"GET {url} {result.StatusCode}"); - } -} -#pragma warning restore CS0618 diff --git a/samples/Sentry.Samples.Console.Metrics/Sentry.Samples.Console.Metrics.csproj b/samples/Sentry.Samples.Console.Metrics/Sentry.Samples.Console.Metrics.csproj deleted file mode 100644 index 93cb6b917b..0000000000 --- a/samples/Sentry.Samples.Console.Metrics/Sentry.Samples.Console.Metrics.csproj +++ /dev/null @@ -1,45 +0,0 @@ - - - - Exe - net8.0 - - - - - - sentry-sdks - sentry-dotnet - - - true - true - - - true - - - - - - - - - - - diff --git a/samples/Sentry.Samples.OpenTelemetry.AspNetCore/Program.cs b/samples/Sentry.Samples.OpenTelemetry.AspNetCore/Program.cs index e7bcb3be57..3cb706852c 100644 --- a/samples/Sentry.Samples.OpenTelemetry.AspNetCore/Program.cs +++ b/samples/Sentry.Samples.OpenTelemetry.AspNetCore/Program.cs @@ -1,6 +1,5 @@ using System.Diagnostics; using Microsoft.AspNetCore.Authentication; -using OpenTelemetry.Metrics; using OpenTelemetry.Resources; using OpenTelemetry.Trace; using Sentry.OpenTelemetry; @@ -23,18 +22,7 @@ .AddHttpClientInstrumentation() // Finally we configure OpenTelemetry to send traces to Sentry .AddSentry() - ) - // This block configures OpenTelemetry metrics that we care about... later we'll configure Sentry to capture these - .WithMetrics(metrics => - { - metrics - .AddRuntimeInstrumentation() // <-- Requires the OpenTelemetry.Instrumentation.Runtime package - // Collect some of the built-in ASP.NET Core metrics - .AddMeter( - "Microsoft.AspNetCore.Hosting", - "Microsoft.AspNetCore.Server.Kestrel", - "System.Net.Http"); - }); + ); builder.WebHost.UseSentry(options => { @@ -46,17 +34,6 @@ options.SendDefaultPii = true; options.TracesSampleRate = 1.0; options.UseOpenTelemetry(); // <-- Configure Sentry to use OpenTelemetry trace information - // This shows experimental support for capturing OpenTelemetry metrics with Sentry - options.ExperimentalMetrics = new ExperimentalMetricsOptions() - { - // Here we're telling Sentry to capture all built-in metrics. This includes all the metrics we configured - // OpenTelemetry to emit when we called `builder.Services.AddOpenTelemetry()` above: - // - "OpenTelemetry.Instrumentation.Runtime" - // - "Microsoft.AspNetCore.Hosting", - // - "Microsoft.AspNetCore.Server.Kestrel", - // - "System.Net.Http" - CaptureSystemDiagnosticsMeters = BuiltInSystemDiagnosticsMeters.All - }; }); builder.Services diff --git a/scripts/generate-solution-filters-config.yaml b/scripts/generate-solution-filters-config.yaml index adc0d61fd6..1483f02fd1 100644 --- a/scripts/generate-solution-filters-config.yaml +++ b/scripts/generate-solution-filters-config.yaml @@ -119,7 +119,6 @@ filterConfigs: patterns: - "**/Sentry.Benchmarks.csproj" - "**/Sentry.Samples.Console.Basic.csproj" - - "**/Sentry.Samples.Console.Metrics.csproj" - "**/Sentry.Samples.Console.Profiling.csproj" - "**/Sentry.Profiling.csproj" - "**/Sentry.csproj" diff --git a/src/Sentry/DisabledMetricAggregator.cs b/src/Sentry/DisabledMetricAggregator.cs deleted file mode 100644 index f07306dbeb..0000000000 --- a/src/Sentry/DisabledMetricAggregator.cs +++ /dev/null @@ -1,76 +0,0 @@ -namespace Sentry; - -internal class DisabledMetricAggregator : IMetricAggregator -{ - public void Increment(string key, double value = 1.0, MeasurementUnit? unit = null, - IDictionary? tags = null, - DateTimeOffset? timestamp = null, int stackLevel = 1) - { - // No Op - } - - public void Gauge(string key, double value = 1.0, MeasurementUnit? unit = null, - IDictionary? tags = null, - DateTimeOffset? timestamp = null, int stackLevel = 1) - { - // No Op - } - - public void Distribution(string key, double value = 1.0, MeasurementUnit? unit = null, - IDictionary? tags = null, - DateTimeOffset? timestamp = null, int stackLevel = 1) - { - // No Op - } - - public void Set(string key, int value, MeasurementUnit? unit = null, - IDictionary? tags = null, - DateTimeOffset? timestamp = null, int stackLevel = 1) - { - // No Op - } - - public void Set(string key, string value, MeasurementUnit? unit = null, - IDictionary? tags = null, - DateTimeOffset? timestamp = null, int stackLevel = 1) - { - // No Op - } - - public void Timing(string key, double value, MeasurementUnit.Duration unit = MeasurementUnit.Duration.Second, - IDictionary? tags = null, - DateTimeOffset? timestamp = null, int stackLevel = 1) - { - // No Op - } - - public IDisposable StartTimer(string key, MeasurementUnit.Duration unit = MeasurementUnit.Duration.Second, - IDictionary? tags = null, - int stackLevel = 1) - { - // No Op - return NoOpDisposable.Instance; - } - - public Task FlushAsync(bool force = true, CancellationToken cancellationToken = default) - { - // No Op - return Task.CompletedTask; - } - - public void Dispose() - { - // No Op - } -} - -internal class NoOpDisposable : IDisposable -{ - private static readonly Lazy LazyInstance = new(); - internal static NoOpDisposable Instance => LazyInstance.Value; - - public void Dispose() - { - // No Op - } -} diff --git a/src/Sentry/Extensibility/DisabledHub.cs b/src/Sentry/Extensibility/DisabledHub.cs index d8f302068a..3ed44e3a09 100644 --- a/src/Sentry/Extensibility/DisabledHub.cs +++ b/src/Sentry/Extensibility/DisabledHub.cs @@ -195,11 +195,6 @@ public SentryId CaptureCheckIn( /// public Task FlushAsync(TimeSpan timeout) => Task.CompletedTask; - /// - /// Disabled Metrics Aggregator (all methods are no-op). - /// - public IMetricAggregator Metrics { get; } = new DisabledMetricAggregator(); - /// /// No-Op. /// diff --git a/src/Sentry/Extensibility/HubAdapter.cs b/src/Sentry/Extensibility/HubAdapter.cs index fbb2dbab7b..5a4f01b62e 100644 --- a/src/Sentry/Extensibility/HubAdapter.cs +++ b/src/Sentry/Extensibility/HubAdapter.cs @@ -287,13 +287,6 @@ public SentryId CaptureCheckIn( public Task FlushAsync(TimeSpan timeout) => SentrySdk.FlushAsync(timeout); - /// - [Obsolete("The SentrySdk.Metrics module is deprecated and will be removed in the next major release. " + - "Sentry will reject all metrics sent after October 7, 2024." + - "Learn more: https://sentry.zendesk.com/hc/en-us/articles/26369339769883-Upcoming-API-Changes-to-Metrics")] - public IMetricAggregator Metrics - => SentrySdk.Metrics; - /// /// Forwards the call to /// diff --git a/src/Sentry/IHub.cs b/src/Sentry/IHub.cs index de2cb8be12..5b8d923f7b 100644 --- a/src/Sentry/IHub.cs +++ b/src/Sentry/IHub.cs @@ -17,11 +17,6 @@ public interface IHub : ISentryClient, ISentryScopeManager /// SentryId LastEventId { get; } - /// - /// - /// - IMetricAggregator Metrics { get; } - /// /// Starts a transaction. /// diff --git a/src/Sentry/IMetricAggregator.cs b/src/Sentry/IMetricAggregator.cs deleted file mode 100644 index 2f52ae4eeb..0000000000 --- a/src/Sentry/IMetricAggregator.cs +++ /dev/null @@ -1,137 +0,0 @@ -namespace Sentry; - -/// -/// Exposes EXPERIMENTAL capability to emit metrics. This API is subject to change without major version bumps so use -/// with caution. We advise disabling in production at the moment. -/// -public interface IMetricAggregator : IDisposable -{ - /// - /// Emits a Counter metric - /// - /// A unique key identifying the metric - /// The value to be added - /// An optional - /// Optional Tags to associate with the metric - /// - /// The time when the metric was emitted. Defaults to the time at which the metric is emitted, if no value is provided. - /// - /// Optional number of stacks levels to ignore when determining the code location - void Increment(string key, - double value = 1.0, - MeasurementUnit? unit = null, - IDictionary? tags = null, - DateTimeOffset? timestamp = null, - int stackLevel = 1); - - /// - /// Emits a Gauge metric - /// - /// A unique key identifying the metric - /// The value to be added - /// An optional - /// Optional Tags to associate with the metric - /// - /// The time when the metric was emitted. Defaults to the time at which the metric is emitted, if no value is provided. - /// - /// Optional number of stacks levels to ignore when determining the code location - void Gauge(string key, - double value = 1.0, - MeasurementUnit? unit = null, - IDictionary? tags = null, - DateTimeOffset? timestamp = null, - int stackLevel = 1); - - /// - /// Emits a Distribution metric - /// - /// A unique key identifying the metric - /// The value to be added - /// An optional - /// Optional Tags to associate with the metric - /// - /// The time when the metric was emitted. Defaults to the time at which the metric is emitted, if no value is provided. - /// - /// Optional number of stacks levels to ignore when determining the code location - void Distribution(string key, - double value = 1.0, - MeasurementUnit? unit = null, - IDictionary? tags = null, - DateTimeOffset? timestamp = null, - int stackLevel = 1); - - /// - /// Emits a Set metric - /// - /// A unique key identifying the metric - /// The value to be added - /// An optional - /// Optional Tags to associate with the metric - /// - /// The time when the metric was emitted. Defaults to the time at which the metric is emitted, if no value is provided. - /// - /// Optional number of stacks levels to ignore when determining the code location - void Set(string key, - int value, - MeasurementUnit? unit = null, - IDictionary? tags = null, - DateTimeOffset? timestamp = null, - int stackLevel = 1); - - /// - /// Emits a Set metric a - /// - /// A unique key identifying the metric - /// The value to be added - /// An optional - /// Optional Tags to associate with the metric - /// - /// The time when the metric was emitted. Defaults to the time at which the metric is emitted, if no value is provided. - /// - /// Optional number of stacks levels to ignore when determining the code location - void Set(string key, - string value, - MeasurementUnit? unit = null, - IDictionary? tags = null, - DateTimeOffset? timestamp = null, - int stackLevel = 1); - - /// - /// Emits a distribution with the time it takes to run a given code block. - /// - /// A unique key identifying the metric - /// The value to be added - /// - /// An optional . Defaults to - /// - /// Optional Tags to associate with the metric - /// The time when the metric was emitted - /// Optional number of stacks levels to ignore when determining the code location - void Timing(string key, - double value, - MeasurementUnit.Duration unit = MeasurementUnit.Duration.Second, - IDictionary? tags = null, - DateTimeOffset? timestamp = null, - int stackLevel = 1); - - /// - /// Measures the time it takes to run a given code block and emits this as a metric. - /// - /// - /// using (SentrySdk.Metrics.StartTimer("my-operation")) - /// { - /// ... - /// } - /// - IDisposable StartTimer(string key, MeasurementUnit.Duration unit = MeasurementUnit.Duration.Second, - IDictionary? tags = null, int stackLevel = 1); - - /// - /// Flushes any flushable metrics and/or code locations. - /// If is true then the cutoff is ignored and all metrics are flushed. - /// - /// Forces all buckets to be flushed, ignoring the cutoff - /// A - /// False if a shutdown is requested during flush, true otherwise - Task FlushAsync(bool force = true, CancellationToken cancellationToken = default); -} diff --git a/src/Sentry/IMetricHub.cs b/src/Sentry/IMetricHub.cs deleted file mode 100644 index 99f9d7d778..0000000000 --- a/src/Sentry/IMetricHub.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Sentry.Protocol.Metrics; - -namespace Sentry; - -internal interface IMetricHub -{ - /// - /// Captures one or more metrics to be sent to Sentry. - /// - void CaptureMetrics(IEnumerable metrics); - - /// - /// Captures one or more to be sent to Sentry. - /// - void CaptureCodeLocations(CodeLocations codeLocations); - - /// - /// Starts a child span for the current transaction or, if there is no active transaction, starts a new transaction. - /// - ISpan StartSpan(string operation, string description); - - /// - ISpan? GetSpan(); -} diff --git a/src/Sentry/Integrations/SystemDiagnosticsMetricsIntegration.cs b/src/Sentry/Integrations/SystemDiagnosticsMetricsIntegration.cs deleted file mode 100644 index efa1af2755..0000000000 --- a/src/Sentry/Integrations/SystemDiagnosticsMetricsIntegration.cs +++ /dev/null @@ -1,38 +0,0 @@ -#if NET8_0_OR_GREATER -using Sentry.Extensibility; -using Sentry.Internal; - -namespace Sentry.Integrations; - -internal class SystemDiagnosticsMetricsIntegration : ISdkIntegration -{ - private readonly Action _initializeListener; - internal const string NoListenersAreConfiguredMessage = "System.Diagnostics.Metrics Integration is disabled because no listeners are configured."; - - public SystemDiagnosticsMetricsIntegration() - { - _initializeListener = SystemDiagnosticsMetricsListener.InitializeDefaultListener; - } - - /// - /// Overload for testing purposes - /// - internal SystemDiagnosticsMetricsIntegration(Action initializeListener) - { - _initializeListener = initializeListener; - } - - public void Register(IHub hub, SentryOptions options) - { - var captureInstruments = options.ExperimentalMetrics?.CaptureSystemDiagnosticsInstruments; - var captureMeters = options.ExperimentalMetrics?.CaptureSystemDiagnosticsMeters; - if (captureInstruments is not { Count: > 0 } && captureMeters is not { Count: > 0 }) - { - options.LogInfo(NoListenersAreConfiguredMessage); - return; - } - - _initializeListener(options.ExperimentalMetrics!); - } -} -#endif diff --git a/src/Sentry/Internal/Hub.cs b/src/Sentry/Internal/Hub.cs index cba3580fa9..accec331cc 100644 --- a/src/Sentry/Internal/Hub.cs +++ b/src/Sentry/Internal/Hub.cs @@ -5,7 +5,7 @@ namespace Sentry.Internal; -internal class Hub : IHub, IMetricHub, IDisposable +internal class Hub : IHub, IDisposable { private readonly object _sessionPauseLock = new(); @@ -21,9 +21,6 @@ internal class Hub : IHub, IMetricHub, IDisposable internal IInternalScopeManager ScopeManager { get; } - /// - public IMetricAggregator Metrics { get; } - private int _isEnabled = 1; public bool IsEnabled => _isEnabled == 1; @@ -63,16 +60,6 @@ internal Hub( PushScope(); } - if (options.ExperimentalMetrics is not null) - { - options.LogDebug("Registering integration: Metrics"); - Metrics = new MetricAggregator(options, this); - } - else - { - Metrics = new DisabledMetricAggregator(); - } - foreach (var integration in options.Integrations) { options.LogDebug("Registering integration: '{0}'.", integration.GetType().Name); @@ -536,7 +523,6 @@ public void CaptureTransaction(SentryTransaction transaction, Scope? scope, Sent } } - /// public void CaptureMetrics(IEnumerable metrics) { if (!IsEnabled) @@ -558,7 +544,6 @@ public void CaptureMetrics(IEnumerable metrics) } } - /// public void CaptureCodeLocations(CodeLocations codeLocations) { if (!IsEnabled) @@ -577,7 +562,6 @@ public void CaptureCodeLocations(CodeLocations codeLocations) } } - /// public ISpan StartSpan(string operation, string description) { ITransactionTracer? currentTransaction = null; diff --git a/src/Sentry/Internal/SystemDiagnosticsMetricsListener.cs b/src/Sentry/Internal/SystemDiagnosticsMetricsListener.cs deleted file mode 100644 index 3e9e15b280..0000000000 --- a/src/Sentry/Internal/SystemDiagnosticsMetricsListener.cs +++ /dev/null @@ -1,90 +0,0 @@ -#if NET8_0_OR_GREATER -using System.Diagnostics.Metrics; - -namespace Sentry.Internal; - -internal class SystemDiagnosticsMetricsListener : IDisposable -{ - private readonly Lazy _metricsAggregator; - private IMetricAggregator MetricsAggregator => _metricsAggregator.Value; - private static SystemDiagnosticsMetricsListener? DefaultListener; - - internal readonly MeterListener _sentryListener = new(); - -#pragma warning disable CS0618 // Obsolete Warning - private SystemDiagnosticsMetricsListener(ExperimentalMetricsOptions metricsOptions) - : this(metricsOptions, () => SentrySdk.Metrics) -#pragma warning restore CS0618 - { - } - - - /// - /// Overload for testing purposes - allows us to supply a mock IMetricAggregator - /// - internal SystemDiagnosticsMetricsListener(ExperimentalMetricsOptions metricsOptions, Func metricsAggregatorResolver) - { - _metricsAggregator = new Lazy(metricsAggregatorResolver); - _sentryListener.InstrumentPublished = (instrument, listener) => - { - if (metricsOptions.CaptureSystemDiagnosticsMeters.MatchesSubstringOrRegex(instrument.Meter.Name) - || metricsOptions.CaptureSystemDiagnosticsInstruments.MatchesSubstringOrRegex(instrument.Name)) - { - listener.EnableMeasurementEvents(instrument); - } - }; - _sentryListener.SetMeasurementEventCallback(RecordMeasurement); - _sentryListener.SetMeasurementEventCallback(RecordMeasurement); - _sentryListener.SetMeasurementEventCallback(RecordMeasurement); - _sentryListener.SetMeasurementEventCallback(RecordMeasurement); - _sentryListener.SetMeasurementEventCallback(RecordMeasurement); - _sentryListener.SetMeasurementEventCallback(RecordMeasurement); - _sentryListener.SetMeasurementEventCallback(RecordMeasurement); - _sentryListener.Start(); - } - - internal static void InitializeDefaultListener(ExperimentalMetricsOptions metricsOptions) - { - var oldListener = Interlocked.Exchange( - ref DefaultListener, - new SystemDiagnosticsMetricsListener(metricsOptions) - ); - oldListener?.Dispose(); - } - - internal void RecordMeasurement( - Instrument instrument, - T measurement, - ReadOnlySpan> tags, - object? _) - where T : struct, IConvertible - { - var unit = MeasurementUnit.Parse(instrument.Unit); - var tagDict = tags.ToImmutableArray().ToImmutableDictionary( - kvp => kvp.Key, - kvp => kvp.Value?.ToString() ?? string.Empty - ); - var doubleMeasurement = Convert.ToDouble(measurement); - switch (instrument) - { - case Counter: - case UpDownCounter: - case ObservableCounter: - case ObservableUpDownCounter: - MetricsAggregator.Increment(instrument.Name, doubleMeasurement, unit, tagDict); - break; - case Histogram: - MetricsAggregator.Distribution(instrument.Name, doubleMeasurement, unit, tagDict); - break; - case ObservableGauge: - MetricsAggregator.Gauge(instrument.Name, doubleMeasurement, unit, tagDict); - break; - } - } - - public void Dispose() - { - _sentryListener.Dispose(); - } -} -#endif diff --git a/src/Sentry/MetricAggregator.cs b/src/Sentry/MetricAggregator.cs deleted file mode 100644 index cb1ce2089f..0000000000 --- a/src/Sentry/MetricAggregator.cs +++ /dev/null @@ -1,537 +0,0 @@ -using Sentry.Extensibility; -using Sentry.Force.Crc32; -using Sentry.Internal; -using Sentry.Internal.Extensions; -using Sentry.Protocol.Metrics; - -namespace Sentry; - -internal class MetricAggregator : IMetricAggregator -{ - internal const string DisposingMessage = "Disposing MetricAggregator."; - internal const string AlreadyDisposedMessage = "Already disposed MetricAggregator."; - internal const string CancelledMessage = "Stopping the Metric Aggregator due to a cancellation."; - internal const string ShutdownScheduledMessage = "Shutdown scheduled. Stopping by: {0}."; - internal const string ShutdownImmediatelyMessage = "Exiting immediately due to 0 shutdown timeout."; - internal const string FlushShutdownMessage = "Shutdown token triggered. Exiting metric aggregator."; - - private readonly SentryOptions _options; - private readonly IMetricHub _metricHub; - - private readonly SemaphoreSlim _codeLocationLock = new(1, 1); - private readonly ReaderWriterLockSlim _bucketsLock = new ReaderWriterLockSlim(); - - private readonly CancellationTokenSource _shutdownSource; - private volatile bool _disposed; - - // The key for this dictionary is the Timestamp for the bucket, rounded down to the nearest RollupInSeconds... so it - // aggregates all of the metrics data for a particular time period. The Value is a dictionary for the metrics, - // each of which has a key that uniquely identifies it within the time period - internal Dictionary> Buckets => _buckets.Value; - - private readonly Lazy>> _buckets - = new(() => new Dictionary>()); - - internal long _lastClearedStaleLocations = DateTimeOffset.UtcNow.GetDayBucketKey(); - internal readonly ConcurrentDictionary> _seenLocations = new(); - internal Dictionary> _pendingLocations = new(); - - internal readonly Task _loopTask; - - internal MetricAggregator(SentryOptions options, IMetricHub metricHub, - CancellationTokenSource? shutdownSource = null, bool disableLoopTask = false) - { - _options = options; - _metricHub = metricHub; - _shutdownSource = shutdownSource ?? new CancellationTokenSource(); - - if (disableLoopTask) - { - // We can stop the loop from running during testing - _options.LogDebug("LoopTask disabled."); - _loopTask = Task.CompletedTask; - } - else - { - options.LogDebug("Starting MetricsAggregator."); - _loopTask = Task.Run(RunLoopAsync); - } - } - - /// - public void Increment(string key, - double value = 1.0, - MeasurementUnit? unit = null, - IDictionary? tags = null, - DateTimeOffset? timestamp = null, - int stackLevel = 1) => Emit(MetricType.Counter, key, value, unit, tags, timestamp, stackLevel + 1); - - /// - public void Gauge(string key, - double value = 1.0, - MeasurementUnit? unit = null, - IDictionary? tags = null, - DateTimeOffset? timestamp = null, - int stackLevel = 1) => Emit(MetricType.Gauge, key, value, unit, tags, timestamp, stackLevel + 1); - - /// - public void Distribution(string key, - double value = 1.0, - MeasurementUnit? unit = null, - IDictionary? tags = null, - DateTimeOffset? timestamp = null, - int stackLevel = 1) => Emit(MetricType.Distribution, key, value, unit, tags, timestamp, stackLevel + 1); - - /// - public void Set(string key, - int value, - MeasurementUnit? unit = null, - IDictionary? tags = null, - DateTimeOffset? timestamp = null, - int stackLevel = 1) => Emit(MetricType.Set, key, value, unit, tags, timestamp, stackLevel + 1); - - /// - public void Set(string key, - string value, - MeasurementUnit? unit = null, - IDictionary? tags = null, - DateTimeOffset? timestamp = null, - int stackLevel = 1) - { - // Compute the CRC32 hash of the value as byte array and cast it to a 32-bit signed integer - // Mask the lower 32 bits to ensure the result fits within the 32-bit integer range - var hash = (int)(Crc32Algorithm.Compute(Encoding.UTF8.GetBytes(value)) & 0xFFFFFFFF); - - Emit(MetricType.Set, key, hash, unit, tags, timestamp, stackLevel + 1); - } - - /// - public virtual void Timing(string key, - double value, - MeasurementUnit.Duration unit = MeasurementUnit.Duration.Second, - IDictionary? tags = null, - DateTimeOffset? timestamp = null, - int stackLevel = 1) => Emit(MetricType.Distribution, key, value, unit, tags, timestamp, stackLevel + 1); - - /// - public IDisposable StartTimer(string key, MeasurementUnit.Duration unit = MeasurementUnit.Duration.Second, - IDictionary? tags = null, int stackLevel = 1) - => new Timing(this, _metricHub, _options, key, unit, tags, stackLevel + 1); - - private void Emit( - MetricType type, - string key, - double value = 1.0, - MeasurementUnit? unit = null, - IDictionary? tags = null, - DateTimeOffset? timestamp = null, - int stackLevel = 1 - ) - { - timestamp ??= DateTimeOffset.UtcNow; - unit ??= MeasurementUnit.None; - - var updatedTags = tags != null ? new Dictionary(tags) : new Dictionary(); - updatedTags.AddIfNotNullOrEmpty("release", _options.Release); - updatedTags.AddIfNotNullOrEmpty("environment", _options.Environment); - var span = _metricHub.GetSpan(); - if (span?.GetTransaction() is { } transaction) - { - updatedTags.AddIfNotNullOrEmpty("transaction", transaction.TransactionName); - } - - Func addValuesFactory = type switch - { - MetricType.Counter => _ => new CounterMetric(key, value, unit.Value, updatedTags, timestamp), - MetricType.Gauge => _ => new GaugeMetric(key, value, unit.Value, updatedTags, timestamp), - MetricType.Distribution => _ => new DistributionMetric(key, value, unit.Value, updatedTags, timestamp), - MetricType.Set => _ => new SetMetric(key, (int)value, unit.Value, updatedTags, timestamp), - _ => throw new ArgumentOutOfRangeException(nameof(type), type, "Unknown MetricType") - }; - - var timeBucket = GetOrAddTimeBucket(timestamp.Value.GetTimeBucketKey()); - - timeBucket.AddOrUpdate( - MetricHelper.GetMetricBucketKey(type, key, unit.Value, updatedTags), - addValuesFactory, - (_, metric) => - { - // This prevents multiple threads from trying to mutate the metric at the same time. The only other - // operations performed against metrics are adding one to the bucket (guaranteed to be atomic due to - // the use of a ConcurrentDictionary for the timeBucket) and removing buckets entirely. - // - // With a very small flushShift (e.g. 0.0) it might be possible for a metric to be emitted to a bucket - // that was removed after a flush, in which case that metric.Add(value) would never make it to Sentry. - // We've never seen this happen in unit testing (where we always set the flushShift to 0.0) so this - // remains only a theoretical possibility of data loss (not confirmed). If this becomes a real problem - // and we need to guarantee delivery of every metric.Add, we'll need to build a more complex mechanism - // to coordinate flushing with emission. - lock (metric) - { - metric.Add(value); - } - return metric; - }); - - if (_options.ExperimentalMetrics is { EnableCodeLocations: true }) - { - RecordCodeLocation(type, key, unit.Value, stackLevel + 1, timestamp.Value); - } - - switch (span) - { - case TransactionTracer transactionTracer: - transactionTracer.MetricsSummary.Add(type, key, value, unit, tags); - break; - case SpanTracer spanTracer: - spanTracer.MetricsSummary.Add(type, key, value, unit, tags); - break; - } - } - - private ConcurrentDictionary GetOrAddTimeBucket(long bucketKey) - { - _bucketsLock.EnterUpgradeableReadLock(); - try - { - if (Buckets.TryGetValue(bucketKey, out var existingBucket)) - { - return existingBucket; - } - - _bucketsLock.EnterWriteLock(); - try - { - // Check again in case another thread added the bucket while we were waiting for the write lock - if (Buckets.TryGetValue(bucketKey, out existingBucket)) - { - return existingBucket; - } - - var timeBucket = new ConcurrentDictionary(); - Buckets[bucketKey] = timeBucket; - return timeBucket; - } - finally - { - _bucketsLock.ExitWriteLock(); - } - } - finally - { - _bucketsLock.ExitUpgradeableReadLock(); - } - } - - internal virtual void RecordCodeLocation( - MetricType type, - string key, - MeasurementUnit unit, - int stackLevel, - DateTimeOffset timestamp - ) - { - var startOfDay = timestamp.GetDayBucketKey(); - var metaKey = new MetricResourceIdentifier(type, key, unit); - var seenToday = _seenLocations.GetOrAdd(startOfDay, _ => []); - - _codeLocationLock.Wait(); - try - { - // Group metadata by day to make flushing more efficient. - if (!seenToday.Add(metaKey)) - { - // If we've seen the location, we don't want to create a stack trace etc. again. It could be a different - // location with the same metaKey but the alternative would be to generate the stack trace every time a - // metric is recorded, which would impact performance too much. - return; - } - - if (GetCodeLocation(stackLevel + 1) is not { } location) - { - return; - } - - if (!_pendingLocations.TryGetValue(startOfDay, out var todaysLocations)) - { - todaysLocations = new Dictionary(); - _pendingLocations[startOfDay] = todaysLocations; - } - - todaysLocations[metaKey] = location; - } - finally - { - _codeLocationLock.Release(); - } - } - - internal SentryStackFrame? GetCodeLocation(int stackLevel) - { - var stackTrace = new StackTrace(true); - var frames = DebugStackTrace.Create(_options, stackTrace, false).Frames; - return (frames.Count >= stackLevel) - ? frames[^(stackLevel + 1)] - : null; - } - - private async Task RunLoopAsync() - { - _options.LogDebug("MetricsAggregator Started."); - - using var shutdownTimeout = new CancellationTokenSource(); - var shutdownRequested = false; - - try - { - while (!shutdownTimeout.IsCancellationRequested) - { - // If the cancellation was signaled, run until the end of the queue or shutdownTimeout - try - { - await Task.Delay(_options.ShutdownTimeout, _shutdownSource.Token).ConfigureAwait(false); - } - // Cancellation requested and no timeout allowed, so exit even if there are more items - catch (OperationCanceledException) when (_options.ShutdownTimeout == TimeSpan.Zero) - { - _options.LogDebug(ShutdownImmediatelyMessage); - - await shutdownTimeout.CancelAsync().ConfigureAwait(false); - - return; - } - // Cancellation requested, scheduled shutdown - catch (OperationCanceledException) - { - _options.LogDebug(ShutdownScheduledMessage, _options.ShutdownTimeout); - - shutdownTimeout.CancelAfterSafe(_options.ShutdownTimeout); - - shutdownRequested = true; - } - - await FlushAsync(shutdownRequested, shutdownTimeout.Token).ConfigureAwait(false); - - if (shutdownRequested) - { - return; - } - } - } - catch (Exception e) - { - _options.LogFatal(e, "Exception in the Metric Aggregator."); - throw; - } - } - - private readonly SemaphoreSlim _flushLock = new(1, 1); - - /// - public async Task FlushAsync(bool force = true, CancellationToken cancellationToken = default) - { - try - { - await _flushLock.WaitAsync(cancellationToken).ConfigureAwait(false); - - foreach (var key in GetFlushableBuckets(force)) - { - cancellationToken.ThrowIfCancellationRequested(); - - _options.LogDebug("Flushing metrics for bucket {0}", key); - ConcurrentDictionary? bucket; - _bucketsLock.EnterWriteLock(); - try - { - if (!Buckets.ContainsKey(key)) - { - continue; - } - bucket = Buckets[key]; - Buckets.Remove(key); - } - finally - { - _bucketsLock.ExitWriteLock(); - } - - _metricHub.CaptureMetrics(bucket.Values); - _options.LogDebug("Metric flushed for bucket {0}", key); - } - - foreach (var (timestamp, locations) in FlushableLocations()) - { - cancellationToken.ThrowIfCancellationRequested(); - - _options.LogDebug("Flushing code locations: ", timestamp); - var codeLocations = new CodeLocations(timestamp, locations); - _metricHub.CaptureCodeLocations(codeLocations); - _options.LogDebug("Code locations flushed: ", timestamp); - } - - ClearStaleLocations(); - } - catch (OperationCanceledException) - { - _options.LogInfo(FlushShutdownMessage); - } - catch (Exception exception) - { - _options.LogError(exception, "Error processing metrics."); - } - finally - { - // If the shutdown token was cancelled before we start this method, we can get here - // without the _flushLock.CurrentCount (i.e. available threads) having been decremented - if (_flushLock.CurrentCount < 1) - { - _flushLock.Release(); - } - } - } - - /// - /// Returns the keys for any buckets that are ready to be flushed (i.e. are for periods before the cutoff) - /// - /// Forces all buckets to be flushed, ignoring the cutoff - /// - /// An enumerable containing the keys for any buckets that are ready to be flushed - /// - internal IEnumerable GetFlushableBuckets(bool force = false) - { - if (!_buckets.IsValueCreated) - { - yield break; - } - - long[] keys; - _bucketsLock.EnterReadLock(); - try - { - keys = Buckets.Keys.ToArray(); - } - finally - { - _bucketsLock.ExitReadLock(); - } - if (force) - { - // Return all the buckets in this case - foreach (var key in keys) - { - yield return key; - } - } - else - { - var cutoff = MetricHelper.GetCutoff(); - foreach (var key in keys) - { - var bucketTime = DateTimeOffset.FromUnixTimeSeconds(key); - if (bucketTime < cutoff) - { - yield return key; - } - } - } - } - - private Dictionary> FlushableLocations() - { - _codeLocationLock.Wait(); - try - { - var result = _pendingLocations; - _pendingLocations = new Dictionary>(); - return result; - } - finally - { - _codeLocationLock.Release(); - } - } - - /// - /// Clear out stale seen locations once a day - /// - internal void ClearStaleLocations(DateTimeOffset? testNow = null) - { - var now = testNow ?? DateTimeOffset.UtcNow; - var today = now.GetDayBucketKey(); - if (_lastClearedStaleLocations == today) - { - return; - } - // Allow 60 seconds for all code locations to be sent at the transition from one day to the next - const int staleGraceInMinutes = 1; - if (now.Minute < staleGraceInMinutes) - { - return; - } - - foreach (var dailyValues in _seenLocations.Keys.ToArray()) - { - if (dailyValues < today) - { - _seenLocations.TryRemove(dailyValues, out _); - } - } - _lastClearedStaleLocations = today; - } - - /// - public async ValueTask DisposeAsync() - { - _options.LogDebug(DisposingMessage); - - if (_disposed) - { - _options.LogDebug(AlreadyDisposedMessage); - return; - } - - _disposed = true; - - try - { - // Request the LoopTask stop. - await _shutdownSource.CancelAsync().ConfigureAwait(false); - - // Now wait for the Loop to stop. - // NOTE: While non-intuitive, do not pass a timeout or cancellation token here. We are waiting for - // the _continuation_ of the method, not its _execution_. If we stop waiting prematurely, this may cause - // unexpected behavior in client applications. - await _loopTask.ConfigureAwait(false); - } - catch (OperationCanceledException) - { - _options.LogDebug(CancelledMessage); - } - catch (Exception exception) - { - _options.LogError(exception, "Async Disposing the Metric Aggregator threw an exception."); - } - finally - { - _flushLock.Dispose(); - _shutdownSource.Dispose(); - _loopTask.Dispose(); - } - } - - public void Dispose() - { - try - { - DisposeAsync().AsTask().GetAwaiter().GetResult(); - } - catch (OperationCanceledException) - { - // Ignore - } - catch (Exception exception) - { - _options.LogError(exception, "Disposing the Metric Aggregator threw an exception."); - } - } -} diff --git a/src/Sentry/Platforms/Android/SentrySdk.cs b/src/Sentry/Platforms/Android/SentrySdk.cs index d2454fc396..6b5d2ba42d 100644 --- a/src/Sentry/Platforms/Android/SentrySdk.cs +++ b/src/Sentry/Platforms/Android/SentrySdk.cs @@ -89,7 +89,6 @@ private static void InitSentryAndroidSdk(SentryOptions options) // These options we have behind feature flags if (options is { IsPerformanceMonitoringEnabled: true, Native.EnableTracing: true }) { - o.EnableTracing = null; o.TracesSampleRate = (JavaDouble?)options.TracesSampleRate; if (options.TracesSampler is { } tracesSampler) diff --git a/src/Sentry/SentryOptions.cs b/src/Sentry/SentryOptions.cs index 66ebac2c7c..37e9a04c4b 100644 --- a/src/Sentry/SentryOptions.cs +++ b/src/Sentry/SentryOptions.cs @@ -199,13 +199,6 @@ internal IEnumerable Integrations } #endif -#if NET8_0_OR_GREATER - if ((_defaultIntegrations & DefaultIntegrations.SystemDiagnosticsMetricsIntegration) != 0) - { - yield return new SystemDiagnosticsMetricsIntegration(); - } -#endif - foreach (var integration in _integrations) { yield return integration; @@ -1098,18 +1091,6 @@ public bool JsonPreserveReferences [EditorBrowsable(EditorBrowsableState.Never)] public Func? AssemblyReader { get; set; } - /// - /// - /// Settings for the EXPERIMENTAL metrics feature. This feature is preview only and subject to change without a - /// major version bump. Currently it's recommended for noodling only - DON'T USE IN PRODUCTION! - /// - /// - /// By default the ExperimentalMetrics Options is null, which means the feature is disabled. If you want to enable - /// Experimental metrics, you must set this property to a non-null value. - /// - /// - public ExperimentalMetricsOptions? ExperimentalMetrics { get; set; } - /// /// The Spotlight URL. Defaults to http://localhost:8969/stream /// diff --git a/src/Sentry/SentrySdk.cs b/src/Sentry/SentrySdk.cs index bf32c20d6c..4fb044be15 100644 --- a/src/Sentry/SentrySdk.cs +++ b/src/Sentry/SentrySdk.cs @@ -667,13 +667,6 @@ public static TransactionContext ContinueTrace( string? operation = null) => CurrentHub.ContinueTrace(traceHeader, baggageHeader, name, operation); - /// - [Obsolete("The SentrySdk.Metrics module is deprecated and will be removed in the next major release. " + - "Sentry will reject all metrics sent after October 7, 2024." + - "Learn more: https://sentry.zendesk.com/hc/en-us/articles/26369339769883-Upcoming-API-Changes-to-Metrics")] - public static IMetricAggregator Metrics - => CurrentHub.Metrics; - /// [DebuggerStepThrough] public static void StartSession() diff --git a/src/Sentry/Timing.cs b/src/Sentry/Timing.cs deleted file mode 100644 index 2c6a0d6517..0000000000 --- a/src/Sentry/Timing.cs +++ /dev/null @@ -1,88 +0,0 @@ -using Sentry.Extensibility; -using Sentry.Internal; -using Sentry.Protocol.Metrics; - -namespace Sentry; - -/// -/// Measures the time it takes to run a given code block and emits this as a metric. -/// -/// -/// using (var timing = new Timing("my-operation")) -/// { -/// ... -/// } -/// -internal class Timing : IDisposable -{ - internal const string OperationName = "metric.timing"; - public const string MetricsOrigin = "auto.metrics"; - - private readonly SentryOptions _options; - private readonly MetricAggregator _metricAggregator; - private readonly string _key; - private readonly MeasurementUnit.Duration _unit; - private readonly IDictionary? _tags; - internal readonly Stopwatch _stopwatch = new(); - private readonly ISpan _span; - internal readonly DateTime _startTime = DateTime.UtcNow; - - /// - /// Creates a new instance. - /// - internal Timing(MetricAggregator metricAggregator, IMetricHub metricHub, SentryOptions options, - string key, MeasurementUnit.Duration unit, IDictionary? tags, int stackLevel) - { - _options = options; - _metricAggregator = metricAggregator; - _key = key; - _unit = unit; - _tags = tags; - _stopwatch.Start(); - - _span = metricHub.StartSpan(OperationName, key); - _span.SetOrigin(MetricsOrigin); - if (tags is not null) - { - _span.SetTags(tags); - } - - // Report code locations here for better accuracy - _metricAggregator.RecordCodeLocation(MetricType.Distribution, key, unit, stackLevel + 1, _startTime); - } - - /// - public void Dispose() - { - _stopwatch.Stop(); - DisposeInternal(_stopwatch.Elapsed); - } - - internal void DisposeInternal(TimeSpan elapsed) - { - try - { - var value = _unit switch - { - MeasurementUnit.Duration.Week => elapsed.TotalDays / 7, - MeasurementUnit.Duration.Day => elapsed.TotalDays, - MeasurementUnit.Duration.Hour => elapsed.TotalHours, - MeasurementUnit.Duration.Minute => elapsed.TotalMinutes, - MeasurementUnit.Duration.Second => elapsed.TotalSeconds, - MeasurementUnit.Duration.Millisecond => elapsed.TotalMilliseconds, - MeasurementUnit.Duration.Microsecond => elapsed.TotalMilliseconds * 1000, - MeasurementUnit.Duration.Nanosecond => elapsed.TotalMilliseconds * 1000000, - _ => throw new ArgumentOutOfRangeException(nameof(_unit), _unit, null) - }; - _metricAggregator.Timing(_key, value, _unit, _tags, _startTime); - } - catch (Exception e) - { - _options.LogError(e, "Error capturing timing '{0}'", _key); - } - finally - { - _span?.Finish(); - } - } -} diff --git a/test/Sentry.AspNetCore.Tests/BindableSentryAspNetCoreOptionsTests.cs b/test/Sentry.AspNetCore.Tests/BindableSentryAspNetCoreOptionsTests.cs index 8bf4b185d9..c7c18cc0e5 100644 --- a/test/Sentry.AspNetCore.Tests/BindableSentryAspNetCoreOptionsTests.cs +++ b/test/Sentry.AspNetCore.Tests/BindableSentryAspNetCoreOptionsTests.cs @@ -5,10 +5,6 @@ namespace Sentry.AspNetCore.Tests; public class BindableSentryAspNetCoreOptionsTests : BindableTests { - public BindableSentryAspNetCoreOptionsTests() : base(nameof(SentryAspNetCoreOptions.ExperimentalMetrics)) - { - } - [Fact] public void BindableProperties_MatchOptionsProperties() { diff --git a/test/Sentry.Azure.Functions.Worker.Tests/BindableSentryAzureFunctionsOptionsTests.cs b/test/Sentry.Azure.Functions.Worker.Tests/BindableSentryAzureFunctionsOptionsTests.cs index 3e196cee11..2f061b45e2 100644 --- a/test/Sentry.Azure.Functions.Worker.Tests/BindableSentryAzureFunctionsOptionsTests.cs +++ b/test/Sentry.Azure.Functions.Worker.Tests/BindableSentryAzureFunctionsOptionsTests.cs @@ -5,10 +5,6 @@ namespace Sentry.Azure.Functions.Worker.Tests; public class BindableSentryAzureFunctionsOptionsTests : BindableTests { - public BindableSentryAzureFunctionsOptionsTests() : base(nameof(SentryAzureFunctionsOptions.ExperimentalMetrics)) - { - } - [Fact] public void BindableProperties_MatchOptionsProperties() { diff --git a/test/Sentry.Extensions.Logging.Tests/SentryLoggingOptionsTests.cs b/test/Sentry.Extensions.Logging.Tests/SentryLoggingOptionsTests.cs index 26ba0fb740..772473b267 100644 --- a/test/Sentry.Extensions.Logging.Tests/SentryLoggingOptionsTests.cs +++ b/test/Sentry.Extensions.Logging.Tests/SentryLoggingOptionsTests.cs @@ -5,10 +5,6 @@ namespace Sentry.Extensions.Logging.Tests; public class SentryLoggingOptionsTests : BindableTests { - public SentryLoggingOptionsTests() : base(nameof(SentryLoggingOptions.ExperimentalMetrics)) - { - } - [Fact] public void BindableProperties_MatchOptionsProperties() { diff --git a/test/Sentry.Maui.Tests/BindableSentryMauiOptionsTests.cs b/test/Sentry.Maui.Tests/BindableSentryMauiOptionsTests.cs index 1a5b885b33..5da7c9ba30 100644 --- a/test/Sentry.Maui.Tests/BindableSentryMauiOptionsTests.cs +++ b/test/Sentry.Maui.Tests/BindableSentryMauiOptionsTests.cs @@ -5,10 +5,6 @@ namespace Sentry.Maui.Tests; public class BindableSentryMauiOptionsTests : BindableTests { - public BindableSentryMauiOptionsTests() : base(nameof(SentryMauiOptions.ExperimentalMetrics)) - { - } - [Fact] public void BindableProperties_MatchOptionsProperties() { diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt index 3f6d8bb6de..225135ec55 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt @@ -193,7 +193,6 @@ namespace Sentry public interface IHub : Sentry.ISentryClient, Sentry.ISentryScopeManager { Sentry.SentryId LastEventId { get; } - Sentry.IMetricAggregator Metrics { get; } void BindException(System.Exception exception, Sentry.ISpan span); Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, System.Action configureScope); Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, Sentry.SentryHint? hint, System.Action configureScope); @@ -208,17 +207,6 @@ namespace Sentry void StartSession(); Sentry.ITransactionTracer StartTransaction(Sentry.ITransactionContext context, System.Collections.Generic.IReadOnlyDictionary customSamplingContext); } - public interface IMetricAggregator : System.IDisposable - { - void Distribution(string key, double value = 1, Sentry.MeasurementUnit? unit = default, System.Collections.Generic.IDictionary? tags = null, System.DateTimeOffset? timestamp = default, int stackLevel = 1); - System.Threading.Tasks.Task FlushAsync(bool force = true, System.Threading.CancellationToken cancellationToken = default); - void Gauge(string key, double value = 1, Sentry.MeasurementUnit? unit = default, System.Collections.Generic.IDictionary? tags = null, System.DateTimeOffset? timestamp = default, int stackLevel = 1); - void Increment(string key, double value = 1, Sentry.MeasurementUnit? unit = default, System.Collections.Generic.IDictionary? tags = null, System.DateTimeOffset? timestamp = default, int stackLevel = 1); - void Set(string key, int value, Sentry.MeasurementUnit? unit = default, System.Collections.Generic.IDictionary? tags = null, System.DateTimeOffset? timestamp = default, int stackLevel = 1); - void Set(string key, string value, Sentry.MeasurementUnit? unit = default, System.Collections.Generic.IDictionary? tags = null, System.DateTimeOffset? timestamp = default, int stackLevel = 1); - System.IDisposable StartTimer(string key, Sentry.MeasurementUnit.Duration unit = 3, System.Collections.Generic.IDictionary? tags = null, int stackLevel = 1); - void Timing(string key, double value, Sentry.MeasurementUnit.Duration unit = 3, System.Collections.Generic.IDictionary? tags = null, System.DateTimeOffset? timestamp = default, int stackLevel = 1); - } public interface IScopeObserver { void AddBreadcrumb(Sentry.Breadcrumb breadcrumb); @@ -670,7 +658,6 @@ namespace Sentry public bool EnableScopeSync { get; set; } public bool EnableSpotlight { get; set; } public string? Environment { get; set; } - public Sentry.ExperimentalMetricsOptions? ExperimentalMetrics { get; set; } public System.Collections.Generic.IList FailedRequestStatusCodes { get; set; } public System.Collections.Generic.IList FailedRequestTargets { get; set; } public System.TimeSpan FlushTimeout { get; set; } @@ -782,11 +769,6 @@ namespace Sentry { public static bool IsEnabled { get; } public static Sentry.SentryId LastEventId { get; } - [System.Obsolete("The SentrySdk.Metrics module is deprecated and will be removed in the next major " + - "release. Sentry will reject all metrics sent after October 7, 2024.Learn more: h" + - "ttps://sentry.zendesk.com/hc/en-us/articles/26369339769883-Upcoming-API-Changes-" + - "to-Metrics")] - public static Sentry.IMetricAggregator Metrics { get; } public static void AddBreadcrumb(Sentry.Breadcrumb breadcrumb, Sentry.SentryHint? hint = null) { } public static void AddBreadcrumb(string message, string? category = null, string? type = null, System.Collections.Generic.IDictionary? data = null, Sentry.BreadcrumbLevel level = 0) { } public static void AddBreadcrumb(Sentry.Infrastructure.ISystemClock? clock, string message, string? category = null, string? type = null, System.Collections.Generic.IDictionary? data = null, Sentry.BreadcrumbLevel level = 0) { } @@ -1283,7 +1265,6 @@ namespace Sentry.Extensibility public static readonly Sentry.Extensibility.DisabledHub Instance; public bool IsEnabled { get; } public Sentry.SentryId LastEventId { get; } - public Sentry.IMetricAggregator Metrics { get; } public void BindClient(Sentry.ISentryClient client) { } public void BindException(System.Exception exception, Sentry.ISpan span) { } public Sentry.SentryId CaptureCheckIn(string monitorSlug, Sentry.CheckInStatus status, Sentry.SentryId? sentryId = default, System.TimeSpan? duration = default, Sentry.Scope? scope = null, System.Action? configureMonitorOptions = null) { } @@ -1323,11 +1304,6 @@ namespace Sentry.Extensibility public static readonly Sentry.Extensibility.HubAdapter Instance; public bool IsEnabled { get; } public Sentry.SentryId LastEventId { get; } - [System.Obsolete("The SentrySdk.Metrics module is deprecated and will be removed in the next major " + - "release. Sentry will reject all metrics sent after October 7, 2024.Learn more: h" + - "ttps://sentry.zendesk.com/hc/en-us/articles/26369339769883-Upcoming-API-Changes-" + - "to-Metrics")] - public Sentry.IMetricAggregator Metrics { get; } public void AddBreadcrumb(string message, string? category = null, string? type = null, System.Collections.Generic.IDictionary? data = null, Sentry.BreadcrumbLevel level = 0) { } public void AddBreadcrumb(Sentry.Infrastructure.ISystemClock clock, string message, string? category = null, string? type = null, System.Collections.Generic.IDictionary? data = null, Sentry.BreadcrumbLevel level = 0) { } public void BindClient(Sentry.ISentryClient client) { } diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt index 3f6d8bb6de..225135ec55 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt @@ -193,7 +193,6 @@ namespace Sentry public interface IHub : Sentry.ISentryClient, Sentry.ISentryScopeManager { Sentry.SentryId LastEventId { get; } - Sentry.IMetricAggregator Metrics { get; } void BindException(System.Exception exception, Sentry.ISpan span); Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, System.Action configureScope); Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, Sentry.SentryHint? hint, System.Action configureScope); @@ -208,17 +207,6 @@ namespace Sentry void StartSession(); Sentry.ITransactionTracer StartTransaction(Sentry.ITransactionContext context, System.Collections.Generic.IReadOnlyDictionary customSamplingContext); } - public interface IMetricAggregator : System.IDisposable - { - void Distribution(string key, double value = 1, Sentry.MeasurementUnit? unit = default, System.Collections.Generic.IDictionary? tags = null, System.DateTimeOffset? timestamp = default, int stackLevel = 1); - System.Threading.Tasks.Task FlushAsync(bool force = true, System.Threading.CancellationToken cancellationToken = default); - void Gauge(string key, double value = 1, Sentry.MeasurementUnit? unit = default, System.Collections.Generic.IDictionary? tags = null, System.DateTimeOffset? timestamp = default, int stackLevel = 1); - void Increment(string key, double value = 1, Sentry.MeasurementUnit? unit = default, System.Collections.Generic.IDictionary? tags = null, System.DateTimeOffset? timestamp = default, int stackLevel = 1); - void Set(string key, int value, Sentry.MeasurementUnit? unit = default, System.Collections.Generic.IDictionary? tags = null, System.DateTimeOffset? timestamp = default, int stackLevel = 1); - void Set(string key, string value, Sentry.MeasurementUnit? unit = default, System.Collections.Generic.IDictionary? tags = null, System.DateTimeOffset? timestamp = default, int stackLevel = 1); - System.IDisposable StartTimer(string key, Sentry.MeasurementUnit.Duration unit = 3, System.Collections.Generic.IDictionary? tags = null, int stackLevel = 1); - void Timing(string key, double value, Sentry.MeasurementUnit.Duration unit = 3, System.Collections.Generic.IDictionary? tags = null, System.DateTimeOffset? timestamp = default, int stackLevel = 1); - } public interface IScopeObserver { void AddBreadcrumb(Sentry.Breadcrumb breadcrumb); @@ -670,7 +658,6 @@ namespace Sentry public bool EnableScopeSync { get; set; } public bool EnableSpotlight { get; set; } public string? Environment { get; set; } - public Sentry.ExperimentalMetricsOptions? ExperimentalMetrics { get; set; } public System.Collections.Generic.IList FailedRequestStatusCodes { get; set; } public System.Collections.Generic.IList FailedRequestTargets { get; set; } public System.TimeSpan FlushTimeout { get; set; } @@ -782,11 +769,6 @@ namespace Sentry { public static bool IsEnabled { get; } public static Sentry.SentryId LastEventId { get; } - [System.Obsolete("The SentrySdk.Metrics module is deprecated and will be removed in the next major " + - "release. Sentry will reject all metrics sent after October 7, 2024.Learn more: h" + - "ttps://sentry.zendesk.com/hc/en-us/articles/26369339769883-Upcoming-API-Changes-" + - "to-Metrics")] - public static Sentry.IMetricAggregator Metrics { get; } public static void AddBreadcrumb(Sentry.Breadcrumb breadcrumb, Sentry.SentryHint? hint = null) { } public static void AddBreadcrumb(string message, string? category = null, string? type = null, System.Collections.Generic.IDictionary? data = null, Sentry.BreadcrumbLevel level = 0) { } public static void AddBreadcrumb(Sentry.Infrastructure.ISystemClock? clock, string message, string? category = null, string? type = null, System.Collections.Generic.IDictionary? data = null, Sentry.BreadcrumbLevel level = 0) { } @@ -1283,7 +1265,6 @@ namespace Sentry.Extensibility public static readonly Sentry.Extensibility.DisabledHub Instance; public bool IsEnabled { get; } public Sentry.SentryId LastEventId { get; } - public Sentry.IMetricAggregator Metrics { get; } public void BindClient(Sentry.ISentryClient client) { } public void BindException(System.Exception exception, Sentry.ISpan span) { } public Sentry.SentryId CaptureCheckIn(string monitorSlug, Sentry.CheckInStatus status, Sentry.SentryId? sentryId = default, System.TimeSpan? duration = default, Sentry.Scope? scope = null, System.Action? configureMonitorOptions = null) { } @@ -1323,11 +1304,6 @@ namespace Sentry.Extensibility public static readonly Sentry.Extensibility.HubAdapter Instance; public bool IsEnabled { get; } public Sentry.SentryId LastEventId { get; } - [System.Obsolete("The SentrySdk.Metrics module is deprecated and will be removed in the next major " + - "release. Sentry will reject all metrics sent after October 7, 2024.Learn more: h" + - "ttps://sentry.zendesk.com/hc/en-us/articles/26369339769883-Upcoming-API-Changes-" + - "to-Metrics")] - public Sentry.IMetricAggregator Metrics { get; } public void AddBreadcrumb(string message, string? category = null, string? type = null, System.Collections.Generic.IDictionary? data = null, Sentry.BreadcrumbLevel level = 0) { } public void AddBreadcrumb(Sentry.Infrastructure.ISystemClock clock, string message, string? category = null, string? type = null, System.Collections.Generic.IDictionary? data = null, Sentry.BreadcrumbLevel level = 0) { } public void BindClient(Sentry.ISentryClient client) { } diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt index 67d3cb89b7..5d13ca7595 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt @@ -194,7 +194,6 @@ namespace Sentry public interface IHub : Sentry.ISentryClient, Sentry.ISentryScopeManager { Sentry.SentryId LastEventId { get; } - Sentry.IMetricAggregator Metrics { get; } void BindException(System.Exception exception, Sentry.ISpan span); Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, System.Action configureScope); Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, Sentry.SentryHint? hint, System.Action configureScope); @@ -209,17 +208,6 @@ namespace Sentry void StartSession(); Sentry.ITransactionTracer StartTransaction(Sentry.ITransactionContext context, System.Collections.Generic.IReadOnlyDictionary customSamplingContext); } - public interface IMetricAggregator : System.IDisposable - { - void Distribution(string key, double value = 1, Sentry.MeasurementUnit? unit = default, System.Collections.Generic.IDictionary? tags = null, System.DateTimeOffset? timestamp = default, int stackLevel = 1); - System.Threading.Tasks.Task FlushAsync(bool force = true, System.Threading.CancellationToken cancellationToken = default); - void Gauge(string key, double value = 1, Sentry.MeasurementUnit? unit = default, System.Collections.Generic.IDictionary? tags = null, System.DateTimeOffset? timestamp = default, int stackLevel = 1); - void Increment(string key, double value = 1, Sentry.MeasurementUnit? unit = default, System.Collections.Generic.IDictionary? tags = null, System.DateTimeOffset? timestamp = default, int stackLevel = 1); - void Set(string key, int value, Sentry.MeasurementUnit? unit = default, System.Collections.Generic.IDictionary? tags = null, System.DateTimeOffset? timestamp = default, int stackLevel = 1); - void Set(string key, string value, Sentry.MeasurementUnit? unit = default, System.Collections.Generic.IDictionary? tags = null, System.DateTimeOffset? timestamp = default, int stackLevel = 1); - System.IDisposable StartTimer(string key, Sentry.MeasurementUnit.Duration unit = 3, System.Collections.Generic.IDictionary? tags = null, int stackLevel = 1); - void Timing(string key, double value, Sentry.MeasurementUnit.Duration unit = 3, System.Collections.Generic.IDictionary? tags = null, System.DateTimeOffset? timestamp = default, int stackLevel = 1); - } public interface IScopeObserver { void AddBreadcrumb(Sentry.Breadcrumb breadcrumb); @@ -671,7 +659,6 @@ namespace Sentry public bool EnableScopeSync { get; set; } public bool EnableSpotlight { get; set; } public string? Environment { get; set; } - public Sentry.ExperimentalMetricsOptions? ExperimentalMetrics { get; set; } public System.Collections.Generic.IList FailedRequestStatusCodes { get; set; } public System.Collections.Generic.IList FailedRequestTargets { get; set; } public System.TimeSpan FlushTimeout { get; set; } @@ -784,11 +771,6 @@ namespace Sentry { public static bool IsEnabled { get; } public static Sentry.SentryId LastEventId { get; } - [System.Obsolete("The SentrySdk.Metrics module is deprecated and will be removed in the next major " + - "release. Sentry will reject all metrics sent after October 7, 2024.Learn more: h" + - "ttps://sentry.zendesk.com/hc/en-us/articles/26369339769883-Upcoming-API-Changes-" + - "to-Metrics")] - public static Sentry.IMetricAggregator Metrics { get; } public static void AddBreadcrumb(Sentry.Breadcrumb breadcrumb, Sentry.SentryHint? hint = null) { } public static void AddBreadcrumb(string message, string? category = null, string? type = null, System.Collections.Generic.IDictionary? data = null, Sentry.BreadcrumbLevel level = 0) { } public static void AddBreadcrumb(Sentry.Infrastructure.ISystemClock? clock, string message, string? category = null, string? type = null, System.Collections.Generic.IDictionary? data = null, Sentry.BreadcrumbLevel level = 0) { } @@ -1285,7 +1267,6 @@ namespace Sentry.Extensibility public static readonly Sentry.Extensibility.DisabledHub Instance; public bool IsEnabled { get; } public Sentry.SentryId LastEventId { get; } - public Sentry.IMetricAggregator Metrics { get; } public void BindClient(Sentry.ISentryClient client) { } public void BindException(System.Exception exception, Sentry.ISpan span) { } public Sentry.SentryId CaptureCheckIn(string monitorSlug, Sentry.CheckInStatus status, Sentry.SentryId? sentryId = default, System.TimeSpan? duration = default, Sentry.Scope? scope = null, System.Action? configureMonitorOptions = null) { } @@ -1325,11 +1306,6 @@ namespace Sentry.Extensibility public static readonly Sentry.Extensibility.HubAdapter Instance; public bool IsEnabled { get; } public Sentry.SentryId LastEventId { get; } - [System.Obsolete("The SentrySdk.Metrics module is deprecated and will be removed in the next major " + - "release. Sentry will reject all metrics sent after October 7, 2024.Learn more: h" + - "ttps://sentry.zendesk.com/hc/en-us/articles/26369339769883-Upcoming-API-Changes-" + - "to-Metrics")] - public Sentry.IMetricAggregator Metrics { get; } public void AddBreadcrumb(string message, string? category = null, string? type = null, System.Collections.Generic.IDictionary? data = null, Sentry.BreadcrumbLevel level = 0) { } public void AddBreadcrumb(Sentry.Infrastructure.ISystemClock clock, string message, string? category = null, string? type = null, System.Collections.Generic.IDictionary? data = null, Sentry.BreadcrumbLevel level = 0) { } public void BindClient(Sentry.ISentryClient client) { } diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt index 9364b82ee2..5c6899448c 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt @@ -192,7 +192,6 @@ namespace Sentry public interface IHub : Sentry.ISentryClient, Sentry.ISentryScopeManager { Sentry.SentryId LastEventId { get; } - Sentry.IMetricAggregator Metrics { get; } void BindException(System.Exception exception, Sentry.ISpan span); Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, System.Action configureScope); Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, Sentry.SentryHint? hint, System.Action configureScope); @@ -207,17 +206,6 @@ namespace Sentry void StartSession(); Sentry.ITransactionTracer StartTransaction(Sentry.ITransactionContext context, System.Collections.Generic.IReadOnlyDictionary customSamplingContext); } - public interface IMetricAggregator : System.IDisposable - { - void Distribution(string key, double value = 1, Sentry.MeasurementUnit? unit = default, System.Collections.Generic.IDictionary? tags = null, System.DateTimeOffset? timestamp = default, int stackLevel = 1); - System.Threading.Tasks.Task FlushAsync(bool force = true, System.Threading.CancellationToken cancellationToken = default); - void Gauge(string key, double value = 1, Sentry.MeasurementUnit? unit = default, System.Collections.Generic.IDictionary? tags = null, System.DateTimeOffset? timestamp = default, int stackLevel = 1); - void Increment(string key, double value = 1, Sentry.MeasurementUnit? unit = default, System.Collections.Generic.IDictionary? tags = null, System.DateTimeOffset? timestamp = default, int stackLevel = 1); - void Set(string key, int value, Sentry.MeasurementUnit? unit = default, System.Collections.Generic.IDictionary? tags = null, System.DateTimeOffset? timestamp = default, int stackLevel = 1); - void Set(string key, string value, Sentry.MeasurementUnit? unit = default, System.Collections.Generic.IDictionary? tags = null, System.DateTimeOffset? timestamp = default, int stackLevel = 1); - System.IDisposable StartTimer(string key, Sentry.MeasurementUnit.Duration unit = 3, System.Collections.Generic.IDictionary? tags = null, int stackLevel = 1); - void Timing(string key, double value, Sentry.MeasurementUnit.Duration unit = 3, System.Collections.Generic.IDictionary? tags = null, System.DateTimeOffset? timestamp = default, int stackLevel = 1); - } public interface IScopeObserver { void AddBreadcrumb(Sentry.Breadcrumb breadcrumb); @@ -668,7 +656,6 @@ namespace Sentry public bool EnableScopeSync { get; set; } public bool EnableSpotlight { get; set; } public string? Environment { get; set; } - public Sentry.ExperimentalMetricsOptions? ExperimentalMetrics { get; set; } public System.Collections.Generic.IList FailedRequestStatusCodes { get; set; } public System.Collections.Generic.IList FailedRequestTargets { get; set; } public System.TimeSpan FlushTimeout { get; set; } @@ -779,11 +766,6 @@ namespace Sentry { public static bool IsEnabled { get; } public static Sentry.SentryId LastEventId { get; } - [System.Obsolete("The SentrySdk.Metrics module is deprecated and will be removed in the next major " + - "release. Sentry will reject all metrics sent after October 7, 2024.Learn more: h" + - "ttps://sentry.zendesk.com/hc/en-us/articles/26369339769883-Upcoming-API-Changes-" + - "to-Metrics")] - public static Sentry.IMetricAggregator Metrics { get; } public static void AddBreadcrumb(Sentry.Breadcrumb breadcrumb, Sentry.SentryHint? hint = null) { } public static void AddBreadcrumb(string message, string? category = null, string? type = null, System.Collections.Generic.IDictionary? data = null, Sentry.BreadcrumbLevel level = 0) { } public static void AddBreadcrumb(Sentry.Infrastructure.ISystemClock? clock, string message, string? category = null, string? type = null, System.Collections.Generic.IDictionary? data = null, Sentry.BreadcrumbLevel level = 0) { } @@ -1280,7 +1262,6 @@ namespace Sentry.Extensibility public static readonly Sentry.Extensibility.DisabledHub Instance; public bool IsEnabled { get; } public Sentry.SentryId LastEventId { get; } - public Sentry.IMetricAggregator Metrics { get; } public void BindClient(Sentry.ISentryClient client) { } public void BindException(System.Exception exception, Sentry.ISpan span) { } public Sentry.SentryId CaptureCheckIn(string monitorSlug, Sentry.CheckInStatus status, Sentry.SentryId? sentryId = default, System.TimeSpan? duration = default, Sentry.Scope? scope = null, System.Action? configureMonitorOptions = null) { } @@ -1320,11 +1301,6 @@ namespace Sentry.Extensibility public static readonly Sentry.Extensibility.HubAdapter Instance; public bool IsEnabled { get; } public Sentry.SentryId LastEventId { get; } - [System.Obsolete("The SentrySdk.Metrics module is deprecated and will be removed in the next major " + - "release. Sentry will reject all metrics sent after October 7, 2024.Learn more: h" + - "ttps://sentry.zendesk.com/hc/en-us/articles/26369339769883-Upcoming-API-Changes-" + - "to-Metrics")] - public Sentry.IMetricAggregator Metrics { get; } public void AddBreadcrumb(string message, string? category = null, string? type = null, System.Collections.Generic.IDictionary? data = null, Sentry.BreadcrumbLevel level = 0) { } public void AddBreadcrumb(Sentry.Infrastructure.ISystemClock clock, string message, string? category = null, string? type = null, System.Collections.Generic.IDictionary? data = null, Sentry.BreadcrumbLevel level = 0) { } public void BindClient(Sentry.ISentryClient client) { } diff --git a/test/Sentry.Tests/BindableSentryOptionsTests.cs b/test/Sentry.Tests/BindableSentryOptionsTests.cs index 977f8570d8..50f39e954a 100644 --- a/test/Sentry.Tests/BindableSentryOptionsTests.cs +++ b/test/Sentry.Tests/BindableSentryOptionsTests.cs @@ -5,10 +5,6 @@ namespace Sentry.Tests; public class BindableSentryOptionsTests : BindableTests { - public BindableSentryOptionsTests() : base(nameof(SentryOptions.ExperimentalMetrics)) - { - } - [Fact] public void BindableProperties_MatchOptionsProperties() { diff --git a/test/Sentry.Tests/Integrations/SystemDiagnosticsMetricsIntegrationTests.cs b/test/Sentry.Tests/Integrations/SystemDiagnosticsMetricsIntegrationTests.cs deleted file mode 100644 index 431afe8a31..0000000000 --- a/test/Sentry.Tests/Integrations/SystemDiagnosticsMetricsIntegrationTests.cs +++ /dev/null @@ -1,79 +0,0 @@ -#if NET8_0_OR_GREATER -namespace Sentry.Tests.Integrations; - -public class SystemDiagnosticsMetricsIntegrationTests -{ - [Fact] - public void Register_NoListenersConfigured_LogsDisabledMessage() - { - // Arrange - var logger = Substitute.For(); - logger.IsEnabled(Arg.Any()).Returns(true); - var options = new SentryOptions - { - Debug = true, - DiagnosticLogger = logger, - ExperimentalMetrics = new ExperimentalMetricsOptions() - { - CaptureSystemDiagnosticsInstruments = [], - CaptureSystemDiagnosticsMeters = [] - } - }; - var integration = new SystemDiagnosticsMetricsIntegration(); - - // Act - integration.Register(null!, options); - - // Assert - logger.Received(1).Log(SentryLevel.Info, SystemDiagnosticsMetricsIntegration.NoListenersAreConfiguredMessage, null); - } - - [Fact] - public void Register_CaptureSystemDiagnosticsInstruments_Succeeds() - { - // Arrange - var logger = Substitute.For(); - var options = new SentryOptions - { - DiagnosticLogger = logger, - ExperimentalMetrics = new ExperimentalMetricsOptions() - { - CaptureSystemDiagnosticsInstruments = [".*"], - CaptureSystemDiagnosticsMeters = [] - } - }; - var initializeDefaultListener = Substitute.For>(); - var integration = new SystemDiagnosticsMetricsIntegration(initializeDefaultListener); - - // Act - integration.Register(null!, options); - - // Assert - initializeDefaultListener.Received(1)(options.ExperimentalMetrics); - } - - [Fact] - public void Register_CaptureSystemDiagnosticsMeters_Succeeds() - { - // Arrange - var logger = Substitute.For(); - var options = new SentryOptions - { - DiagnosticLogger = logger, - ExperimentalMetrics = new ExperimentalMetricsOptions() - { - CaptureSystemDiagnosticsInstruments = [], - CaptureSystemDiagnosticsMeters = [".*"] - } - }; - var initializeDefaultListener = Substitute.For>(); - var integration = new SystemDiagnosticsMetricsIntegration(initializeDefaultListener); - - // Act - integration.Register(null!, options); - - // Assert - initializeDefaultListener.Received(1)(options.ExperimentalMetrics); - } -} -#endif diff --git a/test/Sentry.Tests/Internals/SystemDiagnosticsMetricsListenerTests.cs b/test/Sentry.Tests/Internals/SystemDiagnosticsMetricsListenerTests.cs deleted file mode 100644 index 7934326ec0..0000000000 --- a/test/Sentry.Tests/Internals/SystemDiagnosticsMetricsListenerTests.cs +++ /dev/null @@ -1,300 +0,0 @@ -#if NET8_0_OR_GREATER -using System.Diagnostics.Metrics; - -namespace Sentry.Tests.Internals; - -public class SystemDiagnosticsMetricsListenerTests -{ - private class Fixture - { - public readonly IMetricAggregator MockAggregator = Substitute.For(); - public readonly ExperimentalMetricsOptions MetricsOptions = new(); - - public SystemDiagnosticsMetricsListener GetSut() - { - return new SystemDiagnosticsMetricsListener(MetricsOptions, () => MockAggregator); - } - } - - private readonly Fixture _fixture = new(); - - private static Meter GetMeter() => new Meter(UniqueName(), "1.0.0"); - private static string UniqueName() => Guid.NewGuid().ToString(); - - [Fact] - public void RecordMeasurement_CounterInstrument_CallsIncrement() - { - // Arrange - var testMeter = GetMeter(); - var instrument = testMeter.CreateCounter(UniqueName(), "unit"); - const int measurement = 2; - ReadOnlySpan> tags = [ - new KeyValuePair("tag1", "value1"), - new KeyValuePair("tag2", 2), - ]; - var expectedTags = tags.ToImmutableArray().ToImmutableDictionary( - kvp => kvp.Key, - kvp => kvp.Value?.ToString() ?? string.Empty - ); - - // Act - var sut = _fixture.GetSut(); - sut.RecordMeasurement(instrument, measurement, tags, null); - - // Assert - _fixture.MockAggregator.Received().Increment( - instrument.Name, - measurement, - MeasurementUnit.Custom(instrument.Unit!), - Arg.Is>(arg => - expectedTags.All(tag => arg.ContainsKey(tag.Key) && arg[tag.Key] == tag.Value) - ) - ); - } - - [Fact] - public void RecordMeasurement_UpDownCounterInstrument_CallsIncrement() - { - // Arrange - var testMeter = GetMeter(); - var instrument = testMeter.CreateUpDownCounter(UniqueName(), "unit"); - const int measurement = -2; - ReadOnlySpan> tags = [ - new KeyValuePair("tag1", "value1"), - new KeyValuePair("tag2", 2), - ]; - var expectedTags = tags.ToImmutableArray().ToImmutableDictionary( - kvp => kvp.Key, - kvp => kvp.Value?.ToString() ?? string.Empty - ); - - // Act - var sut = _fixture.GetSut(); - sut.RecordMeasurement(instrument, measurement, tags, null); - - // Assert - _fixture.MockAggregator.Received().Increment( - instrument.Name, - measurement, - MeasurementUnit.Custom(instrument.Unit!), - Arg.Is>(arg => - expectedTags.All(tag => arg.ContainsKey(tag.Key) && arg[tag.Key] == tag.Value) - ) - ); - } - - [Fact] - public void RecordMeasurement_HistogramInstrument_CallsDistribution() - { - // Arrange - var testMeter = GetMeter(); - var instrument = testMeter.CreateHistogram(UniqueName(), "unit"); - const int measurement = 2; - ReadOnlySpan> tags = [ - new KeyValuePair("tag1", "value1"), - new KeyValuePair("tag2", 2), - ]; - var expectedTags = tags.ToImmutableArray().ToImmutableDictionary( - kvp => kvp.Key, - kvp => kvp.Value?.ToString() ?? string.Empty - ); - - // Act - var sut = _fixture.GetSut(); - sut.RecordMeasurement(instrument, measurement, tags, null); - - // Assert - _fixture.MockAggregator.Received().Distribution( - instrument.Name, - measurement, - MeasurementUnit.Custom(instrument.Unit!), - Arg.Is>(arg => - expectedTags.All(tag => arg.ContainsKey(tag.Key) && arg[tag.Key] == tag.Value) - ) - ); - } - - [Fact] - public void RecordMeasurement_ObservableGaugeInstrument_CallsGauge() - { - // Arrange - var testMeter = GetMeter(); - var instrument = testMeter.CreateObservableGauge(UniqueName(), () => new[] { new Measurement(2) }, "unit"); - const int measurement = 2; - ReadOnlySpan> tags = [ - new KeyValuePair("tag1", "value1"), - new KeyValuePair("tag2", 2), - ]; - var expectedTags = tags.ToImmutableArray().ToImmutableDictionary( - kvp => kvp.Key, - kvp => kvp.Value?.ToString() ?? string.Empty - ); - - // Act - var sut = _fixture.GetSut(); - sut.RecordMeasurement(instrument, measurement, tags, null); - - // Assert - _fixture.MockAggregator.Received().Gauge( - instrument.Name, - measurement, - MeasurementUnit.Custom(instrument.Unit!), - Arg.Is>(arg => - expectedTags.All(tag => arg.ContainsKey(tag.Key) && arg[tag.Key] == tag.Value) - ) - ); - } - - [Fact] - public void SystemDiagnosticsMetricsListener_Counter_AggregatesCorrectly() - { - // Arrange - var testMeter = GetMeter(); - var instrument = testMeter.CreateCounter(UniqueName(), "unit"); - _fixture.MetricsOptions.CaptureSystemDiagnosticsInstruments.Add(instrument.Name); - var total = 0d; - _fixture.MockAggregator.Increment( - instrument.Name, - Arg.Do(x => total += x), - Arg.Any(), - Arg.Any>()); - - // Act - var sut = _fixture.GetSut(); - instrument.Add(2); - instrument.Add(3); - - // Assert - _fixture.MockAggregator.Received(2).Increment( - instrument.Name, - Arg.Any(), - Arg.Any(), - Arg.Any>() - ); - total.Should().Be(5); - } - - [Fact] - public void SystemDiagnosticsMetricsListener_ObservableCounter_AggregatesCorrectly() - { - // Arrange - var testMeter = GetMeter(); - List> observedValues = [new Measurement(2), new Measurement(3)]; - var instrument = testMeter.CreateObservableCounter(UniqueName(), - () => observedValues); - _fixture.MetricsOptions.CaptureSystemDiagnosticsInstruments.Add(instrument.Name); - var total = 0d; - _fixture.MockAggregator.Increment( - instrument.Name, - Arg.Do(x => total += x), - Arg.Any(), - Arg.Any>()); - - // Act - var sut = _fixture.GetSut(); - sut._sentryListener.RecordObservableInstruments(); - - // Assert - _fixture.MockAggregator.Received(2).Increment( - instrument.Name, - Arg.Any(), - Arg.Any(), - Arg.Any>() - ); - total.Should().Be(5); - } - - [Fact] - public void SystemDiagnosticsMetricsListener_ObservableUpDownCounter_AggregatesCorrectly() - { - // Arrange - var testMeter = GetMeter(); - List> observedValues = [new Measurement(12), new Measurement(-5)]; - var instrument = testMeter.CreateObservableUpDownCounter(UniqueName(), - () => observedValues); - _fixture.MetricsOptions.CaptureSystemDiagnosticsInstruments.Add(instrument.Name); - var total = 0d; - _fixture.MockAggregator.Increment( - instrument.Name, - Arg.Do(x => total += x), - Arg.Any(), - Arg.Any>()); - - // Act - var sut = _fixture.GetSut(); - sut._sentryListener.RecordObservableInstruments(); - - // Assert - _fixture.MockAggregator.Received(2).Increment( - instrument.Name, - Arg.Any(), - Arg.Any(), - Arg.Any>() - ); - total.Should().Be(7); - } - - [Fact] - public void SystemDiagnosticsMetricsListener_OnlyListensToMatchingInstruments() - { - // Arrange - var testMeter = GetMeter(); - var match = testMeter.CreateCounter(UniqueName()); - var noMatch = testMeter.CreateCounter(UniqueName()); - _fixture.MetricsOptions.CaptureSystemDiagnosticsInstruments.Add(match.Name); - var total = 0d; - _fixture.MockAggregator.Increment( - Arg.Any(), - Arg.Do(x => total += x), - Arg.Any(), - Arg.Any>()); - - // Act - var sut = _fixture.GetSut(); - match.Add(5); - noMatch.Add(3); - - // Assert - _fixture.MockAggregator.Received(1).Increment( - Arg.Any(), - Arg.Any(), - Arg.Any(), - Arg.Any>() - ); - total.Should().Be(5); - } - - [Fact] - public void SystemDiagnosticsMetricsListener_OnlyListensToMatchingMeters() - { - // Arrange - var matchingMeter = GetMeter(); - var matching1 = matchingMeter.CreateCounter(UniqueName()); - var matching2 = matchingMeter.CreateCounter(UniqueName()); - var nonMatchingMeter = GetMeter(); - var nonMatching1 = nonMatchingMeter.CreateCounter(UniqueName()); - _fixture.MetricsOptions.CaptureSystemDiagnosticsMeters.Add(matchingMeter.Name); - var total = 0d; - _fixture.MockAggregator.Increment( - Arg.Any(), - Arg.Do(x => total += x), - Arg.Any(), - Arg.Any>()); - - // Act - var sut = _fixture.GetSut(); - matching1.Add(3); - matching2.Add(5); - nonMatching1.Add(7); - - // Assert - _fixture.MockAggregator.Received(2).Increment( - Arg.Any(), - Arg.Any(), - Arg.Any(), - Arg.Any>() - ); - total.Should().Be(8); - } -} -#endif diff --git a/test/Sentry.Tests/MetricAggregatorTests.cs b/test/Sentry.Tests/MetricAggregatorTests.cs deleted file mode 100644 index 638dfa1e1e..0000000000 --- a/test/Sentry.Tests/MetricAggregatorTests.cs +++ /dev/null @@ -1,641 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using FluentAssertions; -using NSubstitute; -using Sentry.Extensibility; -using Sentry.Protocol.Metrics; -using Xunit; - -namespace Sentry.Tests; - -public class MetricAggregatorTests -{ - private class Fixture - { - public readonly IDiagnosticLogger Logger; - public readonly SentryOptions Options; - - public readonly IMetricHub MetricHub; - public bool DisableFlushLoop; - public readonly CancellationTokenSource CancellationTokenSource; - - public Fixture() - { - Logger = Substitute.For(); - Options = new SentryOptions - { - Debug = true, - DiagnosticLogger = Logger - }; - MetricHub = Substitute.For(); - DisableFlushLoop = true; - CancellationTokenSource = new CancellationTokenSource(); - } - - public MetricAggregator GetSut() => new(Options, MetricHub, CancellationTokenSource, DisableFlushLoop); - } - - // private readonly Fixture _fixture = new(); - private readonly Fixture _fixture = new(); - - [Fact] - public void Increment_AggregatesMetrics() - { - // Arrange - var tx = Substitute.For(); - _fixture.MetricHub.GetSpan().Returns(tx); - - var metricType = MetricType.Counter; - var key = "counter_key"; - var unit = MeasurementUnit.None; - var tags = new Dictionary { ["tag1"] = "value1" }; - var sut = _fixture.GetSut(); - - // Act - DateTimeOffset firstTime = new(1970, 1, 1, 0, 0, 31, 0, TimeSpan.Zero); - sut.Increment(key, 3, unit, tags, firstTime); - - DateTimeOffset secondTime = new(1970, 1, 1, 0, 0, 38, 0, TimeSpan.Zero); - sut.Increment(key, 5, unit, tags, secondTime); - - DateTimeOffset thirdTime = new(1970, 1, 1, 0, 0, 40, 0, TimeSpan.Zero); - sut.Increment(key, 13, unit, tags, thirdTime); - - // Assert - var bucket1 = sut.Buckets[firstTime.GetTimeBucketKey()]; - var data1 = (CounterMetric)bucket1[MetricHelper.GetMetricBucketKey(metricType, key, unit, tags)]; - data1.Value.Should().Be(8); // First two emits are in the same bucket - - var bucket2 = sut.Buckets[thirdTime.GetTimeBucketKey()]; - var data2 = (CounterMetric)bucket2[MetricHelper.GetMetricBucketKey(metricType, key, unit, tags)]; - data2.Value.Should().Be(13); // First two emits are in the same bucket - } - - [Fact] - public void Gauge_AggregatesMetrics() - { - // Arrange - var tx = Substitute.For(); - _fixture.MetricHub.GetSpan().Returns(tx); - - var metricType = MetricType.Gauge; - var key = "gauge_key"; - var unit = MeasurementUnit.None; - var tags = new Dictionary { ["tag1"] = "value1" }; - var sut = _fixture.GetSut(); - - // Act - DateTimeOffset time1 = new(1970, 1, 1, 0, 0, 31, 0, TimeSpan.Zero); - sut.Gauge(key, 3, unit, tags, time1); - - DateTimeOffset time2 = new(1970, 1, 1, 0, 0, 38, 0, TimeSpan.Zero); - sut.Gauge(key, 5, unit, tags, time2); - - DateTimeOffset time3 = new(1970, 1, 1, 0, 0, 40, 0, TimeSpan.Zero); - sut.Gauge(key, 13, unit, tags, time3); - - // Assert - var bucket1 = sut.Buckets[time1.GetTimeBucketKey()]; - var data1 = (GaugeMetric)bucket1[MetricHelper.GetMetricBucketKey(metricType, key, unit, tags)]; - data1.Value.Should().Be(5); - data1.First.Should().Be(3); - data1.Min.Should().Be(3); - data1.Max.Should().Be(5); - data1.Sum.Should().Be(8); - data1.Count.Should().Be(2); - - var bucket2 = sut.Buckets[time3.GetTimeBucketKey()]; - var data2 = (GaugeMetric)bucket2[MetricHelper.GetMetricBucketKey(metricType, key, unit, tags)]; - data2.Value.Should().Be(13); - data2.First.Should().Be(13); - data2.Min.Should().Be(13); - data2.Max.Should().Be(13); - data2.Sum.Should().Be(13); - data2.Count.Should().Be(1); - } - - [Fact] - public void Distribution_AggregatesMetrics() - { - // Arrange - var tx = Substitute.For(); - _fixture.MetricHub.GetSpan().Returns(tx); - - var metricType = MetricType.Distribution; - var key = "distribution_key"; - var unit = MeasurementUnit.None; - var tags = new Dictionary { ["tag1"] = "value1" }; - var sut = _fixture.GetSut(); - - // Act - DateTimeOffset time1 = new(1970, 1, 1, 0, 0, 31, 0, TimeSpan.Zero); - sut.Distribution(key, 3, unit, tags, time1); - - DateTimeOffset time2 = new(1970, 1, 1, 0, 0, 38, 0, TimeSpan.Zero); - sut.Distribution(key, 5, unit, tags, time2); - - DateTimeOffset time3 = new(1970, 1, 1, 0, 0, 40, 0, TimeSpan.Zero); - sut.Distribution(key, 13, unit, tags, time3); - - // Assert - var bucket1 = sut.Buckets[time1.GetTimeBucketKey()]; - var data1 = (DistributionMetric)bucket1[MetricHelper.GetMetricBucketKey(metricType, key, unit, tags)]; - data1.Value.Should().BeEquivalentTo(new[] { 3, 5 }); - - var bucket2 = sut.Buckets[time3.GetTimeBucketKey()]; - var data2 = (DistributionMetric)bucket2[MetricHelper.GetMetricBucketKey(metricType, key, unit, tags)]; - data2.Value.Should().BeEquivalentTo(new[] { 13 }); - } - - [Fact] - public void Set_Int_AggregatesMetrics() - { - // Arrange - var tx = Substitute.For(); - _fixture.MetricHub.GetSpan().Returns(tx); - - var metricType = MetricType.Set; - var key = "set_key"; - var unit = MeasurementUnit.None; - var tags = new Dictionary { ["tag1"] = "value1" }; - var sut = _fixture.GetSut(); - - // Act - DateTimeOffset time1 = new(1970, 1, 1, 0, 0, 31, 0, TimeSpan.Zero); - sut.Set(key, 3, unit, tags, time1); - - DateTimeOffset time2 = new(1970, 1, 1, 0, 0, 38, 0, TimeSpan.Zero); - sut.Set(key, 5, unit, tags, time2); - - DateTimeOffset time3 = new(1970, 1, 1, 0, 0, 40, 0, TimeSpan.Zero); - sut.Set(key, 13, unit, tags, time3); - - DateTimeOffset time4 = new(1970, 1, 1, 0, 0, 42, 0, TimeSpan.Zero); - sut.Set(key, 13, unit, tags, time4); - - // Assert - var bucket1 = sut.Buckets[time1.GetTimeBucketKey()]; - var data1 = (SetMetric)bucket1[MetricHelper.GetMetricBucketKey(metricType, key, unit, tags)]; - data1.Value.Should().BeEquivalentTo(new[] { 3, 5 }); - - var bucket2 = sut.Buckets[time3.GetTimeBucketKey()]; - var data2 = (SetMetric)bucket2[MetricHelper.GetMetricBucketKey(metricType, key, unit, tags)]; - data2.Value.Should().BeEquivalentTo(new[] { 13 }); - } - - [Fact] - public void Set_String_AggregatesMetrics() - { - // Arrange - var tx = Substitute.For(); - _fixture.MetricHub.GetSpan().Returns(tx); - - var metricType = MetricType.Set; - var key = "set_key"; - var unit = MeasurementUnit.None; - var tags = new Dictionary { ["tag1"] = "value1" }; - var sut = _fixture.GetSut(); - - // Act - DateTimeOffset time1 = new(1970, 1, 1, 0, 0, 31, 0, TimeSpan.Zero); - sut.Set(key, "test_1", unit, tags, time1); - - DateTimeOffset time2 = new(1970, 1, 1, 0, 0, 38, 0, TimeSpan.Zero); - sut.Set(key, "test_2", unit, tags, time2); - - DateTimeOffset time3 = new(1970, 1, 1, 0, 0, 40, 0, TimeSpan.Zero); - sut.Set(key, "test_3", unit, tags, time3); - - DateTimeOffset time4 = new(1970, 1, 1, 0, 0, 42, 0, TimeSpan.Zero); - sut.Set(key, "test_3", unit, tags, time4); - - // Assert - var bucket1 = sut.Buckets[time1.GetTimeBucketKey()]; - var data1 = (SetMetric)bucket1[MetricHelper.GetMetricBucketKey(metricType, key, unit, tags)]; - data1.Value.Should().HaveCount(2); - - var bucket2 = sut.Buckets[time3.GetTimeBucketKey()]; - var data2 = (SetMetric)bucket2[MetricHelper.GetMetricBucketKey(metricType, key, unit, tags)]; - data2.Value.Should().HaveCount(1); - } - - [Fact] - public async Task GetFlushableBuckets_IsThreadsafe() - { - // Arrange - var tx = Substitute.For(); - _fixture.MetricHub.GetSpan().Returns(tx); - - const int numThreads = 100; - const int numThreadIterations = 1000; - var sent = 0; - MetricHelper.FlushShift = 0.0; - _fixture.DisableFlushLoop = false; - _fixture.MetricHub.CaptureMetrics(Arg.Do>(metrics => - { - foreach (var metric in metrics) - { - Interlocked.Add(ref sent, (int)((CounterMetric)metric).Value); - } - } - )); - var sut = _fixture.GetSut(); - - // Act... spawn some threads that add loads of metrics - var resetEvent = new ManualResetEvent(false); - var toProcess = numThreads; - for (var i = 0; i < numThreads; i++) - { - new Thread(delegate () - { - for (var i = 0; i < numThreadIterations; i++) - { - sut.Increment("counter"); - } - // If we're the last thread, signal - if (Interlocked.Decrement(ref toProcess) == 0) - { - resetEvent.Set(); - } - }).Start(); - } - - // Wait for workers. - resetEvent.WaitOne(); - await sut.FlushAsync(); - - // Assert - sent.Should().Be(numThreads * numThreadIterations); - } - - [Fact] - public void TestGetCodeLocation() - { - // Arrange - _fixture.Options.StackTraceMode = StackTraceMode.Enhanced; - var sut = _fixture.GetSut(); - - // Act - var result = sut.GetCodeLocation(1); - - // Assert - result.Should().NotBeNull(); - result!.Function.Should().Be($"void {nameof(MetricAggregatorTests)}.{nameof(TestGetCodeLocation)}()"); - } - - [Fact] - public void RecordCodeLocation_AddsMetricToSeenAndPendingLocations() - { - // Arrange - var type = MetricType.Counter; - var key = "counter_key"; - var unit = MeasurementUnit.None; - var stackLevel = 1; - var timestamp = DateTimeOffset.Now; - var sut = _fixture.GetSut(); - - // Act - sut.RecordCodeLocation(type, key, unit, stackLevel, timestamp); - - // Assert - var startOfDay = timestamp.GetDayBucketKey(); - sut._seenLocations.Keys.Should().Contain(startOfDay); - - var metaKey = new MetricResourceIdentifier(type, key, unit); - sut._seenLocations[startOfDay].Should().Contain(metaKey); - - sut._pendingLocations.Keys.Should().Contain(startOfDay); - sut._pendingLocations[startOfDay].Should().NotBeNull(); - sut._pendingLocations[startOfDay].Keys.Should().Contain(metaKey); - sut._pendingLocations[startOfDay][metaKey].Should().NotBeNull(); - sut._pendingLocations[startOfDay][metaKey].Function.Should().Be( - $"void {nameof(MetricAggregatorTests)}.{nameof(RecordCodeLocation_AddsMetricToSeenAndPendingLocations)}()" - ); - } - - [Fact] - public void RecordCodeLocation_RecordsLocationOnlyOnce() - { - // Arrange - var type = MetricType.Counter; - var key = "counter_key"; - var unit = MeasurementUnit.None; - var stackLevel = 1; - var timestamp = DateTimeOffset.Now; - var sut = _fixture.GetSut(); - - // Act - sut.RecordCodeLocation(type, key, unit, stackLevel, timestamp); - sut.RecordCodeLocation(type, key, unit, stackLevel, timestamp); - - // Assert - sut._pendingLocations.SelectMany(x => x.Value).Count().Should().Be(1); - } - - [Fact] - public void RecordCodeLocation_BadStackLevel_AddsToSeenButNotPending() - { - // Arrange - var type = MetricType.Counter; - var key = "counter_key"; - var unit = MeasurementUnit.None; - var stackLevel = short.MaxValue; - var timestamp = DateTimeOffset.Now; - var sut = _fixture.GetSut(); - - // Act - sut.RecordCodeLocation(type, key, unit, stackLevel, timestamp); - - // Assert - var startOfDay = timestamp.GetDayBucketKey(); - sut._seenLocations.Keys.Should().Contain(startOfDay); - - var metaKey = new MetricResourceIdentifier(type, key, unit); - sut._seenLocations[startOfDay].Should().Contain(metaKey); - - sut._pendingLocations.SelectMany(x => x.Value).Should().BeEmpty(); - } - - [Fact] - public void Dispose_OnlyExecutesOnce() - { - // Arrange - _fixture.Logger.IsEnabled(Arg.Any()).Returns(true); - var sut = _fixture.GetSut(); - - // Act - sut.Dispose(); - sut.Dispose(); - sut.Dispose(); - - // Assert - _fixture.Logger.Received(2).Log(SentryLevel.Debug, MetricAggregator.AlreadyDisposedMessage, null); - } - - [Fact] - public void Dispose_StopsLoopTask() - { - // Arrange - _fixture.Logger.IsEnabled(Arg.Any()).Returns(true); - _fixture.DisableFlushLoop = false; - _fixture.Options.ShutdownTimeout = TimeSpan.Zero; - var sut = _fixture.GetSut(); - - // Act - sut.Dispose(); - - // Assert - _fixture.Logger.Received(1).Log(SentryLevel.Debug, MetricAggregator.DisposingMessage, null); - sut._loopTask.Status.Should().BeOneOf(TaskStatus.RanToCompletion, TaskStatus.Faulted); - } - - [Fact] - public async Task Dispose_SwallowsException() - { - // Arrange - _fixture.CancellationTokenSource.Dispose(); - _fixture.DisableFlushLoop = false; - var sut = _fixture.GetSut(); - - // We expect an exception here, because we disposed the cancellation token source - await Assert.ThrowsAsync(() => sut._loopTask); - - // Act - await sut.DisposeAsync(); - - // Assert - sut._loopTask.Status.Should().Be(TaskStatus.Faulted); - } - - [Fact] - public async Task Cancel_NonZeroTimeout_SchedulesShutdown() - { - // Arrange - _fixture.Logger.IsEnabled(Arg.Any()).Returns(true); - _fixture.DisableFlushLoop = false; - _fixture.Options.ShutdownTimeout = TimeSpan.FromSeconds(1); - var sut = _fixture.GetSut(); - - // Act - await _fixture.CancellationTokenSource.CancelAsync(); -#pragma warning disable xUnit1031 - sut._loopTask.Wait(10000); -#pragma warning restore xUnit1031 - - // Assert - _fixture.Logger.Received(1).Log(SentryLevel.Debug, MetricAggregator.ShutdownScheduledMessage, null, Arg.Any()); - } - - [Fact] - public async Task Cancel_ZeroTimeout_ShutdownImmediately() - { - // Arrange - _fixture.Logger.IsEnabled(Arg.Any()).Returns(true); - _fixture.DisableFlushLoop = false; - _fixture.Options.ShutdownTimeout = TimeSpan.Zero; - var sut = _fixture.GetSut(); - - // Act - await _fixture.CancellationTokenSource.CancelAsync(); -#pragma warning disable xUnit1031 - sut._loopTask.Wait(10000); -#pragma warning restore xUnit1031 - - // Assert - _fixture.Logger.Received(1).Log(SentryLevel.Debug, MetricAggregator.ShutdownImmediatelyMessage, null); - } - - [Fact] - public void Emit_ActiveSpan_AppliesSpanTags() - { - // Arrange - _fixture.Options.Release = "test_release"; - _fixture.Options.Environment = "test_env"; - - var tx = Substitute.For(); - tx.TransactionName = "test_name"; - - _fixture.DisableFlushLoop = false; - _fixture.MetricHub.GetSpan().Returns(tx); - var sut = _fixture.GetSut(); - - // Act - sut.Increment("test_key"); - - // Assert - var bucket = sut.Buckets.SingleOrDefault().Value; - var metric = bucket.SingleOrDefault().Value; - metric.Should().BeOfType(); - var counter = (metric as CounterMetric)!; - counter.Key.Should().Be("test_key"); - counter.Tags["release"].Should().Be("test_release"); - counter.Tags["environment"].Should().Be("test_env"); - counter.Tags["transaction"].Should().Be("test_name"); - } - - [Fact] - public void Emit_ActiveSpan_SpanAggregates() - { - // Arrange - var hub = Substitute.For(); - var tx = new TransactionTracer(hub, "test_name", "test_op"); - tx.Release = "test_release"; - tx.Environment = "test_env"; - - var span = new SpanTracer(hub, tx, null, SentryId.Create(), "test_op"); - - _fixture.DisableFlushLoop = false; - _fixture.MetricHub.GetSpan().Returns(span); - var sut = _fixture.GetSut(); - - // Act - sut.Increment("test_key", 3); - sut.Increment("test_key", 5); - - // Assert - tx.MetricsSummary.Measurements.Should().BeEmpty(); - - var bucketKey = MetricHelper.GetMetricBucketKey(MetricType.Counter, "test_key", MeasurementUnit.None, null); - span.MetricsSummary.Measurements.Should().ContainKey(bucketKey); - var metric = span.MetricsSummary.Measurements[bucketKey]; - metric.Min.Should().Be(3); - metric.Max.Should().Be(5); - metric.Sum.Should().Be(8); - metric.Count.Should().Be(2); - } - - [Fact] - public void Emit_ActiveSpan_TransactionAggregates() - { - // Arrange - var hub = Substitute.For(); - var tx = new TransactionTracer(hub, "test_name", "test_op"); - tx.Release = "test_release"; - tx.Environment = "test_env"; - - _fixture.DisableFlushLoop = false; - _fixture.MetricHub.GetSpan().Returns(tx); - var sut = _fixture.GetSut(); - - // Act - sut.Increment("test_key", 3); - sut.Increment("test_key", 5); - - // Assert - var bucketKey = MetricHelper.GetMetricBucketKey(MetricType.Counter, "test_key", MeasurementUnit.None, null); - tx.MetricsSummary.Measurements.Should().ContainKey(bucketKey); - var metric = tx.MetricsSummary.Measurements[bucketKey]; - metric.Min.Should().Be(3); - metric.Max.Should().Be(5); - metric.Sum.Should().Be(8); - metric.Count.Should().Be(2); - } - - [Fact] - public async Task FlushAsync_FlushesPendingLocations() - { - // Arrange - var type = MetricType.Counter; - var key = "counter_key"; - var unit = MeasurementUnit.None; - var stackLevel = 1; - var timestamp = DateTimeOffset.Now.Subtract(TimeSpan.FromSeconds(20)); - var sut = _fixture.GetSut(); - sut.RecordCodeLocation(type, key, unit, stackLevel, timestamp); - - // Act - await sut.FlushAsync(); - - // Assert - _fixture.MetricHub.Received(1).CaptureCodeLocations(Arg.Any()); - } - - [Fact] - public async Task FlushAsync_Cancel_Exists() - { - // Arrange - _fixture.DisableFlushLoop = false; - _fixture.Logger.IsEnabled(Arg.Any()).Returns(true); - var cancellationTokenSource = new CancellationTokenSource(); - await cancellationTokenSource.CancelAsync(); - var sut = _fixture.GetSut(); - - // Act - await sut.FlushAsync(true, cancellationTokenSource.Token); - - // Assert - _fixture.Logger.Received(1).Log(SentryLevel.Info, MetricAggregator.FlushShutdownMessage, null); - } - - [Fact] - public void ClearStaleLocations_SameDay_NoClear() - { - // Arrange - var time = new DateTimeOffset(2000, 1, 1, 12, 0, 0, TimeSpan.Zero); - - var sut = _fixture.GetSut(); - sut._lastClearedStaleLocations = time.GetDayBucketKey(); - - var type = MetricType.Counter; - var key = "counter_key"; - var unit = MeasurementUnit.None; - var stackLevel = 1; - sut.RecordCodeLocation(type, key, unit, stackLevel, time.Subtract(TimeSpan.FromDays(1))); - - // Act - sut.ClearStaleLocations(time); - - // Assert - // (You need some way to check that "_seenLocations" are not modified. This is stubbed in as "SeenLocations") - sut._seenLocations.Should().NotBeEmpty(); - } - - [Fact] - public void ClearStaleLocations_GraceTime_NoClear() - { - // Arrange - var time = new DateTimeOffset(2000, 1, 1, 0, 0, 30, TimeSpan.Zero); - - var sut = _fixture.GetSut(); - sut._lastClearedStaleLocations = time.GetDayBucketKey() - 1; - - var type = MetricType.Counter; - var key = "counter_key"; - var unit = MeasurementUnit.None; - var stackLevel = 1; - sut.RecordCodeLocation(type, key, unit, stackLevel, time.Subtract(TimeSpan.FromDays(1))); - - // Act - sut.ClearStaleLocations(time); - - // Assert - // (You need some way to check that "_seenLocations" are not modified. This is stubbed in as "SeenLocations") - sut._seenLocations.Should().NotBeEmpty(); - } - - [Fact] - public void ClearStaleLocations_AfterGraceTime_Clear() - { - // Arrange - var time = new DateTimeOffset(2000, 1, 1, 0, 1, 30, TimeSpan.Zero); - - var sut = _fixture.GetSut(); - sut._lastClearedStaleLocations = time.GetDayBucketKey() - 1; - - var type = MetricType.Counter; - var key = "counter_key"; - var unit = MeasurementUnit.None; - var stackLevel = 1; - sut.RecordCodeLocation(type, key, unit, stackLevel, time.Subtract(TimeSpan.FromDays(1))); - - // Act - sut.ClearStaleLocations(time); - - // Assert - // (You need some way to check that "_seenLocations" are not modified. This is stubbed in as "SeenLocations") - sut._seenLocations.Should().BeEmpty(); - } -} diff --git a/test/Sentry.Tests/SentryOptionsTests.Integrations_default_ones_are_properly_registered.DotNet8_0.DotNet.verified.txt b/test/Sentry.Tests/SentryOptionsTests.Integrations_default_ones_are_properly_registered.DotNet8_0.DotNet.verified.txt index 9e3f2681b5..dfbc55fc32 100644 --- a/test/Sentry.Tests/SentryOptionsTests.Integrations_default_ones_are_properly_registered.DotNet8_0.DotNet.verified.txt +++ b/test/Sentry.Tests/SentryOptionsTests.Integrations_default_ones_are_properly_registered.DotNet8_0.DotNet.verified.txt @@ -34,15 +34,5 @@ Args: [ SentryDiagnosticListenerIntegration ] - }, - { - Message: Registering integration: '{0}'., - Args: [ - SystemDiagnosticsMetricsIntegration - ] - }, - { - Level: info, - Message: System.Diagnostics.Metrics Integration is disabled because no listeners are configured. } ] \ No newline at end of file diff --git a/test/Sentry.Tests/SentryOptionsTests.Integrations_default_ones_are_properly_registered.DotNet8_0.Windows.DotNet.verified.txt b/test/Sentry.Tests/SentryOptionsTests.Integrations_default_ones_are_properly_registered.DotNet8_0.Windows.DotNet.verified.txt index e14cfaf075..10eaeaf749 100644 --- a/test/Sentry.Tests/SentryOptionsTests.Integrations_default_ones_are_properly_registered.DotNet8_0.Windows.DotNet.verified.txt +++ b/test/Sentry.Tests/SentryOptionsTests.Integrations_default_ones_are_properly_registered.DotNet8_0.Windows.DotNet.verified.txt @@ -40,15 +40,5 @@ Args: [ WinUIUnhandledExceptionIntegration ] - }, - { - Message: Registering integration: '{0}'., - Args: [ - SystemDiagnosticsMetricsIntegration - ] - }, - { - Level: info, - Message: System.Diagnostics.Metrics Integration is disabled because no listeners are configured. } ] \ No newline at end of file diff --git a/test/Sentry.Tests/SentryOptionsTests.cs b/test/Sentry.Tests/SentryOptionsTests.cs index d9e8194039..cd20a5c7d3 100644 --- a/test/Sentry.Tests/SentryOptionsTests.cs +++ b/test/Sentry.Tests/SentryOptionsTests.cs @@ -303,17 +303,6 @@ public void DisableTaskUnobservedTaskExceptionCapture_UnobservedTaskExceptionInt p => p is UnobservedTaskExceptionIntegration); } -#if NET8_0_OR_GREATER - [Fact] - public void DisableSystemDiagnosticsMetricsIntegration_RemovesSystemDiagnosticsMetricsIntegration() - { - var sut = new SentryOptions(); - sut.DisableSystemDiagnosticsMetricsIntegration(); - Assert.DoesNotContain(sut.Integrations, - p => p.GetType() == typeof(SystemDiagnosticsMetricsIntegration)); - } -#endif - [Fact] public void AddIntegration_StoredInOptions() { diff --git a/test/Sentry.Tests/TimingTests.cs b/test/Sentry.Tests/TimingTests.cs deleted file mode 100644 index 941b0d41c3..0000000000 --- a/test/Sentry.Tests/TimingTests.cs +++ /dev/null @@ -1,143 +0,0 @@ -using Sentry.Protocol.Metrics; - -namespace Sentry.Tests; - -public class TimingTests -{ - private class Fixture - { - public readonly IHub Hub; - public IMetricHub MetricHub { get; } - public SentryOptions Options { get; } - public IDiagnosticLogger Logger { get; } - public MetricAggregator MetricAggregator { get; } - - public string Key { get; set; } = "key"; - - public MeasurementUnit.Duration Unit { get; set; } = MeasurementUnit.Duration.Second; - public Dictionary Tags { get; set; } = new(); - - public Fixture() - { - Hub = Substitute.For(); - MetricHub = Substitute.For(); - Logger = Substitute.For(); - Logger.IsEnabled(Arg.Any()).Returns(true); - Options = new() - { - Debug = true, - DiagnosticLogger = Logger - }; - MetricAggregator = Substitute.For(Options, MetricHub, null, true); - } - - public Timing GetSut() => new(MetricAggregator, MetricHub, Options, Key, Unit, Tags, 1); - } - private readonly Fixture _fixture = new(); - - [Fact] - public void Constructor_CreatesSpan() - { - // Arrange - _fixture.Tags = new Dictionary { { "tag1", "value1" } }; - - var span = new TransactionTracer(_fixture.Hub, Timing.OperationName, _fixture.Key); - _fixture.MetricHub.StartSpan(Timing.OperationName, _fixture.Key).Returns(span); - - // Act - _ = _fixture.GetSut(); - - // Assert - _fixture.MetricHub.Received(1).StartSpan(Timing.OperationName, _fixture.Key); - span.Tags.Should().BeEquivalentTo(_fixture.Tags); - span.Origin.Should().Be(Timing.MetricsOrigin); - } - - [Fact] - public void Constructor_RecordsCodeLocation() - { - // Act - var timing = _fixture.GetSut(); - - // Assert - _fixture.MetricAggregator.Received(1).RecordCodeLocation(MetricType.Distribution, _fixture.Key, MeasurementUnit.Duration.Second, 2, timing._startTime); - } - - [Fact] - public void Constructor_StartsStopwatch() - { - // Act - var timing = _fixture.GetSut(); - - // Assert - timing._stopwatch.IsRunning.Should().BeTrue(); - } - - [Fact] - public void Dispose_StopsStopwatch() - { - // Arrange - var timing = _fixture.GetSut(); - - // Act - var stopwatch = timing._stopwatch; - timing.Dispose(); - - // Assert - stopwatch.IsRunning.Should().BeFalse(); - } - - [Theory] - [InlineData(MeasurementUnit.Duration.Week, 7)] - [InlineData(MeasurementUnit.Duration.Day, 1)] - [InlineData(MeasurementUnit.Duration.Hour, 1 / 24.0)] - [InlineData(MeasurementUnit.Duration.Minute, 1 / (24.0 * 60))] - [InlineData(MeasurementUnit.Duration.Second, 1 / (24.0 * 60 * 60))] - [InlineData(MeasurementUnit.Duration.Millisecond, 1 / (24.0 * 60 * 60 * 1000))] - [InlineData(MeasurementUnit.Duration.Microsecond, 1 / (24.0 * 60 * 60 * 1000000))] - [InlineData(MeasurementUnit.Duration.Nanosecond, 1 / (24.0 * 60 * 60 * 1000000000))] - public void DisposeInternal_ValidUnits_RecordsTiming(MeasurementUnit.Duration unit, double expectedRatio) - { - // Arrange - _fixture.Unit = unit; - var timing = _fixture.GetSut(); - var elapsed = TimeSpan.FromDays(1); // 1 day - - // Act - timing.DisposeInternal(elapsed); - - // Assert - _fixture.MetricAggregator.Received(1).Timing( - Arg.Any(), - elapsed.TotalDays / expectedRatio, // Expected value - unit, - Arg.Any>(), - Arg.Any()); - } - - [Fact] - public void Dispose_InvalidUnit_LogsError() - { - // Arrange - _fixture.Unit = (MeasurementUnit.Duration)int.MaxValue; - var timing = _fixture.GetSut(); - - // Act - timing.Dispose(); - - // Assert - _fixture.MetricAggregator.Received(0).Timing( - Arg.Any(), - Arg.Any(), - Arg.Any(), - Arg.Any>(), - Arg.Any() - ); - _fixture.Logger.Received(1).Log( - SentryLevel.Error, - "Error capturing timing '{0}'", - Arg.Any(), - _fixture.Key - ); - } -} From cdbec91115e542ddd788e827291adf68ba1d77fb Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Sun, 3 Nov 2024 23:46:02 +1300 Subject: [PATCH 009/363] Add net9.0 support (#3699) --- .github/actions/environment/action.yml | 5 +- .github/actions/freediskspace/action.yml | 48 + .github/workflows/build.yml | 49 +- .github/workflows/codeql-analysis.yml | 5 +- .github/workflows/device-tests-android.yml | 2 +- .github/workflows/device-tests-ios.yml | 39 +- .github/workflows/format-code.yml | 9 +- CHANGELOG.md | 4 +- Directory.Build.props | 12 +- Directory.Build.targets | 6 +- Sentry.sln | 6 + global.json | 4 +- ...ntry.Samples.AspNetCore.Blazor.Wasm.csproj | 4 + .../Sentry.Samples.Console.Basic.csproj | 2 +- .../Sentry.Samples.EntityFramework.csproj | 2 +- .../Sentry.Samples.GenericHost.csproj | 3 +- .../Sentry.Samples.GraphQL.Server.csproj | 2 +- .../Sentry.Samples.Hangfire.csproj | 2 + .../Sentry.Samples.Maui.csproj | 4 +- scripts/device-test.ps1 | 9 +- src/Directory.Build.props | 11 +- .../Sentry.AspNetCore.csproj | 2 +- .../Sentry.Azure.Functions.Worker.csproj | 2 +- .../Sentry.Bindings.Android.csproj | 3 +- .../Sentry.Bindings.Cocoa.csproj | 6 +- .../Sentry.EntityFramework.csproj | 5 +- .../Sentry.Extensions.Logging.csproj | 2 +- src/Sentry.Hangfire/Sentry.Hangfire.csproj | 2 +- .../Internal/SentryMauiOptionsSetup.cs | 2 + src/Sentry.Maui/Sentry.Maui.csproj | 12 +- src/Sentry.NLog/Sentry.NLog.csproj | 2 +- .../Sentry.OpenTelemetry.csproj | 2 +- src/Sentry.Profiling/Sentry.Profiling.csproj | 2 +- src/Sentry.Serilog/Sentry.Serilog.csproj | 2 +- .../Android/Extensions/JavaExtensions.cs | 6 +- .../buildTransitive/Sentry.Native.targets | 12 +- src/Sentry/Sentry.csproj | 18 +- src/Sentry/buildTransitive/Sentry.targets | 5 +- ...piApprovalTests.Run.DotNet8_0.verified.txt | 12 + ...Sentry.Android.AssemblyReader.Tests.csproj | 4 +- .../Sentry.AspNetCore.TestUtils.csproj | 5 + .../LocalDbFixture.cs | 17 +- ...y.DiagnosticSource.IntegrationTests.csproj | 4 +- .../SqlListenerTests.verify.cs | 3 + .../Sentry.DiagnosticSource.Tests.csproj | 4 + .../Sentry.EntityFramework.Tests.csproj | 7 +- ...piApprovalTests.Run.DotNet9_0.verified.txt | 59 + .../Sentry.Extensions.Logging.Tests.csproj | 8 +- .../Sentry.Maui.Device.TestApp.csproj | 19 +- ...piApprovalTests.Run.DotNet9_0.verified.txt | 22 + .../Sentry.Maui.Tests.csproj | 17 +- test/Sentry.Testing/Sentry.Testing.csproj | 8 +- ...piApprovalTests.Run.DotNet9_0.verified.txt | 1804 +++++++++++++++++ test/Sentry.Tests/HubTests.cs | 6 +- .../Internals/ILSpy/SingleFileAppTests.cs | 6 +- ...ryInfoTests.WriteTo.DotNet9_0.verified.txt | 19 + test/Sentry.Tests/Sentry.Tests.csproj | 8 +- ...y_registered.DotNet9_0.DotNet.verified.txt | 38 + ...ered.DotNet9_0.Windows.DotNet.verified.txt | 44 + ...UnobservedTaskExceptionIntegrationTests.cs | 4 +- 60 files changed, 2271 insertions(+), 160 deletions(-) create mode 100644 .github/actions/freediskspace/action.yml create mode 100644 test/Sentry.Android.AssemblyReader.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt create mode 100644 test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt create mode 100644 test/Sentry.Maui.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt create mode 100644 test/Sentry.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt create mode 100644 test/Sentry.Tests/Internals/MemoryInfoTests.WriteTo.DotNet9_0.verified.txt create mode 100644 test/Sentry.Tests/SentryOptionsTests.Integrations_default_ones_are_properly_registered.DotNet9_0.DotNet.verified.txt create mode 100644 test/Sentry.Tests/SentryOptionsTests.Integrations_default_ones_are_properly_registered.DotNet9_0.Windows.DotNet.verified.txt diff --git a/.github/actions/environment/action.yml b/.github/actions/environment/action.yml index 4b39841de0..455a2b0954 100644 --- a/.github/actions/environment/action.yml +++ b/.github/actions/environment/action.yml @@ -30,18 +30,19 @@ runs: # .NET 6 and .NET 8 are not built-in with macos-13 - name: Install .NET SDK - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 with: dotnet-version: | 6.0.x 7.0.x 8.0.x + 9.0.100-rc.2.24474.11 - name: Install .NET Workloads shell: bash run: > dotnet workload install \ - wasm-tools maui-android \ + wasm-tools wasm-tools-net8 maui-android \ ${{ runner.os == 'macOS' && 'maui-ios maui-maccatalyst maui-windows macos' || '' }} \ ${{ runner.os == 'Windows' && 'maui-windows' || '' }} \ --temp-dir "${{ runner.temp }}" \ diff --git a/.github/actions/freediskspace/action.yml b/.github/actions/freediskspace/action.yml new file mode 100644 index 0000000000..e2c75b64c4 --- /dev/null +++ b/.github/actions/freediskspace/action.yml @@ -0,0 +1,48 @@ +name: Remove unused applications +description: Frees disk space by removing unused applications +runs: + using: composite + steps: + + - name: Free Disk Space + if: runner.os == 'Linux' + uses: jlumbroso/free-disk-space@f68fdb76e2ea636224182cfb7377ff9a1708f9b8 + with: + android: false + dotnet: false + haskell: true + large-packages: false + docker-images: true + swap-storage: true + + # TODO: Do we need all of these or just one? + # 514M /usr/lib/llvm-15 + # 486M /usr/lib/llvm-14 + # 448M /usr/lib/llvm-13 + - name: Remove unused applications + if: runner.os == 'Linux' + shell: bash + run: | + df -h /dev/root + sudo rm -rf /usr/local/.ghcup + sudo rm -rf /usr/local/julia1.10.5 + sudo rm -rf /usr/lib/heroku + sudo rm -rf /opt/hostedtoolcache/go + sudo rm -rf /opt/hostedtoolcache/Ruby + sudo apt-get remove -y '^mysql-.*' --fix-missing || echo "::warning::The command [sudo apt-get remove -y '^mysql-.*' --fix-missing] failed to complete successfully. Proceeding..." + sudo apt-get autoremove -y || echo "::warning::The command [sudo apt-get autoremove -y] failed to complete successfully. Proceeding..." + sudo apt-get clean || echo "::warning::The command [sudo apt-get clean] failed to complete successfully. Proceeding..." + df -h /dev/root + + # We only use Xcode 16 + - name: Remove unused applications + if: runner.os == 'macOS' + shell: bash + run: | + df -hI /dev/disk3s1s1 + sudo rm -rf /Applications/Xcode_14.3.1.app + sudo rm -rf /Applications/Xcode_15.0.1.app + sudo rm -rf /Applications/Xcode_15.1.app + sudo rm -rf /Applications/Xcode_15.2.app + sudo rm -rf /Applications/Xcode_15.3.app + df -hI /dev/disk3s1s1 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4d4d390647..041cd35e9b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -15,11 +15,11 @@ env: jobs: build-sentry-native: name: sentry-native (${{ matrix.os }}) - runs-on: ${{ matrix.os }}-latest + runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: - os: [ubuntu, windows, macos] + os: [ubuntu-latest, windows-latest, macos-latest] steps: - name: Checkout @@ -34,16 +34,8 @@ jobs: key: sentry-native-${{ runner.os }}-${{ hashFiles('scripts/build-sentry-native.ps1') }}-${{ hashFiles('.git/modules/modules/sentry-native/HEAD') }} enableCrossOsArchive: true - - name: Free Disk Space (Ubuntu) - if: matrix.os == 'ubuntu-latest' - uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be - with: - android: true - dotnet: false - haskell: true - large-packages: false - docker-images: false - swap-storage: true + - name: Remove unused applications + uses: ./.github/actions/freediskspace - name: Install build dependencies if: steps.cache.outputs.cache-hit != 'true' && runner.os == 'Linux' @@ -70,31 +62,22 @@ jobs: if: github.ref_name != 'main' && !startsWith(github.ref_name, 'release/') uses: styfle/cancel-workflow-action@85880fa0301c86cca9da44039ee3bb12d3bedbfa # Tag: 0.12.1 + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + fetch-depth: 2 # default is 1 and codecov needs > 1 + - name: Setup Xcode if: matrix.os == 'macos-latest' run: | sudo xcode-select --switch /Applications/Xcode_${{env.XCODE_VERSION}}.app/Contents/Developer xcodebuild -version - # We only use Xcode 16 - name: Remove unused applications - if: matrix.os == 'macos-latest' - run: | - df -hI /dev/disk3s1s1 - sudo rm -rf /Applications/Xcode_14.3.1.app - sudo rm -rf /Applications/Xcode_15.0.1.app - sudo rm -rf /Applications/Xcode_15.1.app - sudo rm -rf /Applications/Xcode_15.2.app - sudo rm -rf /Applications/Xcode_15.3.app - df -hI /dev/disk3s1s1 - - - name: Checkout - uses: actions/checkout@v4 - with: - submodules: recursive - fetch-depth: 2 # default is 1 and codecov needs > 1 + uses: ./.github/actions/freediskspace - # We use macOS for the final publishing build so we we get all the iOS/macCatalyst targets in the packages + # We use macOS for the final publishing build so we get all the iOS/macCatalyst targets in the packages - name: Set Environment Variables if: startsWith(matrix.os, 'macos') run: echo "CI_PUBLISHING_BUILD=true" >> $GITHUB_ENV @@ -199,9 +182,12 @@ jobs: sudo apt update sudo apt install libcurl4-openssl-dev + # Possibly instead of installing net9.0 here we could pin the workload version when installing workloads - uses: actions/setup-dotnet@v4 with: - dotnet-version: 8.0.x + dotnet-version: | + 8.0.x + 9.0.100-rc.2.24474.11 - name: Setup Xcode if: matrix.os == 'macos-latest' @@ -218,7 +204,8 @@ jobs: - name: Setup Android SDK uses: android-actions/setup-android@00854ea68c109d98c75d956347303bf7c45b0277 # v3.2.1 - - run: dotnet workload install android maui-android + - name: Install android Workloads + run: dotnet workload install maui-android - name: Test uses: getsentry/github-workflows/sentry-cli/integration-test/@v2 diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index f238206072..305d1d1f6d 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -40,7 +40,10 @@ jobs: languages: csharp - name: Restore .NET Dependencies - run: dotnet restore Sentry-CI-CodeQL.slnf --nologo + # We should be able to get rid of the restore here, if we install the correct workloads in actions/environment + run: | + dotnet workload restore + dotnet restore Sentry-CI-CodeQL.slnf --nologo - name: Build run: dotnet build Sentry-CI-CodeQL.slnf --no-restore --nologo diff --git a/.github/workflows/device-tests-android.yml b/.github/workflows/device-tests-android.yml index 87c629548c..adbb50fd74 100644 --- a/.github/workflows/device-tests-android.yml +++ b/.github/workflows/device-tests-android.yml @@ -39,7 +39,7 @@ jobs: with: name: device-test-android if-no-files-found: error - path: test/Sentry.Maui.Device.TestApp/bin/Release/net7.0-android/android-x64/io.sentry.dotnet.maui.device.testapp-Signed.apk + path: test/Sentry.Maui.Device.TestApp/bin/Release/net8.0-android/android-x64/io.sentry.dotnet.maui.device.testapp-Signed.apk android: needs: [build] diff --git a/.github/workflows/device-tests-ios.yml b/.github/workflows/device-tests-ios.yml index cf3db8c515..3e248ca0eb 100644 --- a/.github/workflows/device-tests-ios.yml +++ b/.github/workflows/device-tests-ios.yml @@ -9,10 +9,12 @@ on: paths-ignore: - "**.md" +env: + XCODE_VERSION: 16 + jobs: - build: - # Pinning `macos-13` because Microsoft.iOS 16.4 requires Xcode 14.3 which is only built-in in 13 - runs-on: macos-13 + ios-tests: + runs-on: macos-latest env: DOTNET_CLI_TELEMETRY_OPTOUT: 1 DOTNET_NOLOGO: 1 @@ -34,35 +36,14 @@ jobs: - name: Setup Environment uses: ./.github/actions/environment + - name: Setup Xcode + run: | + sudo xcode-select --switch /Applications/Xcode_${{env.XCODE_VERSION}}.app/Contents/Developer + xcodebuild -version + - name: Build iOS Test App run: pwsh ./scripts/device-test.ps1 ios -Build - - name: Upload iOS Test App - uses: actions/upload-artifact@v4 - with: - name: device-test-ios - if-no-files-found: error - path: test/Sentry.Maui.Device.TestApp/bin/Release/net7.0-ios/iossimulator-x64/Sentry.Maui.Device.TestApp.app - - ios: - needs: [build] - name: Run iOS Tests - runs-on: macos-13 - strategy: - fail-fast: false - env: - DOTNET_CLI_TELEMETRY_OPTOUT: 1 - DOTNET_NOLOGO: 1 - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Download test app artifact - uses: actions/download-artifact@v4 - with: - name: device-test-ios - path: bin/Sentry.Maui.Device.TestApp.app - - name: Run Tests id: first-run continue-on-error: true diff --git a/.github/workflows/format-code.yml b/.github/workflows/format-code.yml index 9a867477be..341c8a33cd 100644 --- a/.github/workflows/format-code.yml +++ b/.github/workflows/format-code.yml @@ -23,13 +23,16 @@ jobs: uses: ./.github/actions/environment - name: Restore .NET Dependencies - run: dotnet restore Sentry.sln --nologo + # We should be able to get rid of the restore here, if we install the correct workloads in actions/environment + run: | + dotnet workload restore + dotnet restore Sentry.sln --nologo - name: Install dotnet format run: dotnet tool install -g dotnet-format - name: Format Code - # We're excluding `./**/*OptionsSetup.cs` from the format because the tool struggles with + # We're excluding `./**/*OptionsSetup.cs` from the format because the tool struggles with # source generators run: dotnet format Sentry.sln --no-restore --exclude ./modules --exclude ./**/*OptionsSetup.cs @@ -37,4 +40,4 @@ jobs: # we need to pass the current branch, otherwise we can't commit the changes. # GITHUB_HEAD_REF is the name of the head branch. GitHub Actions only sets this for PRs. - name: Commit Formatted Code - run: ./scripts/commit-formatted-code.sh $GITHUB_HEAD_REF \ No newline at end of file + run: ./scripts/commit-formatted-code.sh $GITHUB_HEAD_REF diff --git a/CHANGELOG.md b/CHANGELOG.md index 47abaca587..21cbcbea0c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,9 @@ - `Scope.Transaction` is now always stored as an `AsyncLocal` also in [Global Mode](https://docs.sentry.io/platforms/dotnet/configuration/options/#is-global-mode-enabled), to prevent auto-instrumented spans from the UI ending up parented to transactions from a background task (or vice versa). ([#3596](https://github.com/getsentry/sentry-dotnet/pull/3596)) - Sentry's Experimental Metrics feature has been deprecated and removed from the SDK. ([#3718](https://github.com/getsentry/sentry-dotnet/pull/3718)) -## Unreleased +### Features +- Added support for `.NET 9` (preview) ([#3699](https://github.com/getsentry/sentry-dotnet/pull/3699)) + ## Unreleased ### Features diff --git a/Directory.Build.props b/Directory.Build.props index e3c8cd9e3b..020df25154 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -13,6 +13,12 @@ $(NoWarn);CS8002 + + + $(NoWarn);NU1903 + + + NU1903 @@ -34,10 +40,14 @@ $([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) - 10.0 + 11.0 13.1 21.0 10.0.17763.0 diff --git a/Directory.Build.targets b/Directory.Build.targets index 321b0cd940..5e9d4873c2 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -3,10 +3,14 @@ $([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) - 10.0 + 11.0 13.1 21.0 10.0.17763.0 diff --git a/Sentry.sln b/Sentry.sln index 82631572fc..ff4cd98d0b 100644 --- a/Sentry.sln +++ b/Sentry.sln @@ -62,6 +62,10 @@ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Samples.Serilog", "samples\Sentry.Samples.Serilog\Sentry.Samples.Serilog.csproj", "{AA98FD8D-1254-4B34-840C-06BB263933DE}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{230B9384-90FD-4551-A5DE-1A5C197F25B6}" + ProjectSection(SolutionItems) = preProject + src\Directory.Build.props = src\Directory.Build.props + src\Directory.Build.targets = src\Directory.Build.targets + EndProjectSection EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Android.AssemblyReader", "src\Sentry.Android.AssemblyReader\Sentry.Android.AssemblyReader.csproj", "{20386BBE-1F55-4503-9F5F-F2C6B29DE865}" EndProject @@ -174,6 +178,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "root", "root", "{233D34AB-9 Directory.Build.targets = Directory.Build.targets global.json = global.json nuget.config = nuget.config + .codecov.yml = .codecov.yml + .gitignore = .gitignore EndProjectSection EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.AspNetCore.Blazor.WebAssembly", "src\Sentry.AspNetCore.Blazor.WebAssembly\Sentry.AspNetCore.Blazor.WebAssembly.csproj", "{8298202C-9983-4D0A-851D-805539EE481A}" diff --git a/global.json b/global.json index 60b4c0250d..102a17f7b9 100644 --- a/global.json +++ b/global.json @@ -1,7 +1,7 @@ { "sdk": { - "version": "8.0.100", + "version": "9.0.100-rc.2.24474.11", "rollForward": "latestMinor", "allowPrerelease": false } -} \ No newline at end of file +} diff --git a/samples/Sentry.Samples.AspNetCore.Blazor.Wasm/Sentry.Samples.AspNetCore.Blazor.Wasm.csproj b/samples/Sentry.Samples.AspNetCore.Blazor.Wasm/Sentry.Samples.AspNetCore.Blazor.Wasm.csproj index b9365ff13c..113bdd422e 100644 --- a/samples/Sentry.Samples.AspNetCore.Blazor.Wasm/Sentry.Samples.AspNetCore.Blazor.Wasm.csproj +++ b/samples/Sentry.Samples.AspNetCore.Blazor.Wasm/Sentry.Samples.AspNetCore.Blazor.Wasm.csproj @@ -20,4 +20,8 @@ + + + + diff --git a/samples/Sentry.Samples.Console.Basic/Sentry.Samples.Console.Basic.csproj b/samples/Sentry.Samples.Console.Basic/Sentry.Samples.Console.Basic.csproj index 3fd37ae5d1..e5fb119c7e 100644 --- a/samples/Sentry.Samples.Console.Basic/Sentry.Samples.Console.Basic.csproj +++ b/samples/Sentry.Samples.Console.Basic/Sentry.Samples.Console.Basic.csproj @@ -4,7 +4,7 @@ Exe enable enable - net8.0;net6.0;net462 + net9.0;net8.0;net6.0;net462 true diff --git a/samples/Sentry.Samples.EntityFramework/Sentry.Samples.EntityFramework.csproj b/samples/Sentry.Samples.EntityFramework/Sentry.Samples.EntityFramework.csproj index 1c554bc094..2e27079e5d 100644 --- a/samples/Sentry.Samples.EntityFramework/Sentry.Samples.EntityFramework.csproj +++ b/samples/Sentry.Samples.EntityFramework/Sentry.Samples.EntityFramework.csproj @@ -15,7 +15,7 @@ - + diff --git a/samples/Sentry.Samples.GenericHost/Sentry.Samples.GenericHost.csproj b/samples/Sentry.Samples.GenericHost/Sentry.Samples.GenericHost.csproj index c262f00213..1081c24c62 100644 --- a/samples/Sentry.Samples.GenericHost/Sentry.Samples.GenericHost.csproj +++ b/samples/Sentry.Samples.GenericHost/Sentry.Samples.GenericHost.csproj @@ -21,6 +21,7 @@ + + - diff --git a/samples/Sentry.Samples.GraphQL.Server/Sentry.Samples.GraphQL.Server.csproj b/samples/Sentry.Samples.GraphQL.Server/Sentry.Samples.GraphQL.Server.csproj index f0fdf28753..2078f97579 100644 --- a/samples/Sentry.Samples.GraphQL.Server/Sentry.Samples.GraphQL.Server.csproj +++ b/samples/Sentry.Samples.GraphQL.Server/Sentry.Samples.GraphQL.Server.csproj @@ -17,7 +17,7 @@ - + diff --git a/samples/Sentry.Samples.Hangfire/Sentry.Samples.Hangfire.csproj b/samples/Sentry.Samples.Hangfire/Sentry.Samples.Hangfire.csproj index 7268266316..ac7347715a 100644 --- a/samples/Sentry.Samples.Hangfire/Sentry.Samples.Hangfire.csproj +++ b/samples/Sentry.Samples.Hangfire/Sentry.Samples.Hangfire.csproj @@ -10,6 +10,8 @@ + + diff --git a/samples/Sentry.Samples.Maui/Sentry.Samples.Maui.csproj b/samples/Sentry.Samples.Maui/Sentry.Samples.Maui.csproj index 5e6c4cb102..0fe2a399fb 100644 --- a/samples/Sentry.Samples.Maui/Sentry.Samples.Maui.csproj +++ b/samples/Sentry.Samples.Maui/Sentry.Samples.Maui.csproj @@ -6,7 +6,7 @@ On Mac, we'll also build for iOS and MacCatalyst. On Windows, we'll also build for Windows 10. --> - net8.0-android + $(TargetFrameworks);net8.0-android $(TargetFrameworks);net8.0-windows10.0.19041.0;net8.0-ios;net8.0-maccatalyst $(TargetFrameworks);net8.0-ios;net8.0-maccatalyst Exe @@ -98,7 +98,7 @@ - + diff --git a/scripts/device-test.ps1 b/scripts/device-test.ps1 index 09ea8a8635..0d6ced343c 100644 --- a/scripts/device-test.ps1 +++ b/scripts/device-test.ps1 @@ -21,7 +21,7 @@ $CI = Test-Path env:CI Push-Location $PSScriptRoot/.. try { - $tfm = 'net7.0-' + $tfm = 'net8.0-' $arch = (!$IsWindows -and $(uname -m) -eq 'arm64') ? 'arm64' : 'x64' if ($Platform -eq 'android') { @@ -47,7 +47,7 @@ try $group = 'apple' # Always use x64 on iOS, since arm64 doesn't support JIT, which is required for tests using NSubstitute $arch = 'x64' - $buildDir = $CI ? 'bin' : "test/Sentry.Maui.Device.TestApp/bin/Release/$tfm/iossimulator-$arch" + $buildDir = "test/Sentry.Maui.Device.TestApp/bin/Release/$tfm/iossimulator-$arch" $envValue = $CI ? 'true' : 'false' $arguments = @( '--app', "$buildDir/Sentry.Maui.Device.TestApp.app", @@ -59,7 +59,8 @@ try if ($Build) { - dotnet build -f $tfm -c Release test/Sentry.Maui.Device.TestApp + # We disable AOT for device tests: https://github.com/nsubstitute/NSubstitute/issues/834 + dotnet build -f $tfm -c Release -p:EnableAot=false test/Sentry.Maui.Device.TestApp if ($LASTEXITCODE -ne 0) { throw 'Failed to build Sentry.Maui.Device.TestApp' @@ -71,7 +72,7 @@ try if (!(Get-Command xharness -ErrorAction SilentlyContinue)) { Push-Location ($CI ? $env:RUNNER_TEMP : $IsWindows ? $env:TMP : $IsMacos ? $env:TMPDIR : '/temp') - dotnet tool install Microsoft.DotNet.XHarness.CLI --global --version '9.*-*' ` + dotnet tool install Microsoft.DotNet.XHarness.CLI --global --version '10.0.0-prerelease*' ` --add-source https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-eng/nuget/v3/index.json Pop-Location } diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 1a3c15e297..a63860aea1 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -35,14 +35,9 @@ $(MSBuildThisFileDirectory)CodeAnalysis.ruleset - - - - true + + + true diff --git a/src/Sentry.AspNetCore/Sentry.AspNetCore.csproj b/src/Sentry.AspNetCore/Sentry.AspNetCore.csproj index 22e4a40824..7c0d439b94 100644 --- a/src/Sentry.AspNetCore/Sentry.AspNetCore.csproj +++ b/src/Sentry.AspNetCore/Sentry.AspNetCore.csproj @@ -6,7 +6,7 @@ Official ASP.NET Core integration for Sentry - Open-source error tracking that helps developers monitor and fix crashes in real time. - + true true diff --git a/src/Sentry.Azure.Functions.Worker/Sentry.Azure.Functions.Worker.csproj b/src/Sentry.Azure.Functions.Worker/Sentry.Azure.Functions.Worker.csproj index 333ad8d8c0..7237feda50 100644 --- a/src/Sentry.Azure.Functions.Worker/Sentry.Azure.Functions.Worker.csproj +++ b/src/Sentry.Azure.Functions.Worker/Sentry.Azure.Functions.Worker.csproj @@ -6,7 +6,7 @@ Official Azure Functions Worker SDK integration for Sentry - Open-source error tracking that helps developers monitor and fix crashes in real time. - + true true diff --git a/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj b/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj index 963f70b866..542fe3b26e 100644 --- a/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj +++ b/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj @@ -1,6 +1,6 @@ - net7.0-android + net8.0-android $(NoWarn);BG8605;BG8606 7.16.0 @@ -12,6 +12,7 @@ + diff --git a/src/Sentry.Bindings.Cocoa/Sentry.Bindings.Cocoa.csproj b/src/Sentry.Bindings.Cocoa/Sentry.Bindings.Cocoa.csproj index 773c19feee..e9380973e4 100644 --- a/src/Sentry.Bindings.Cocoa/Sentry.Bindings.Cocoa.csproj +++ b/src/Sentry.Bindings.Cocoa/Sentry.Bindings.Cocoa.csproj @@ -1,9 +1,9 @@ - net7.0-ios;net7.0-maccatalyst - net7.0-ios - net7.0-maccatalyst + net8.0-ios;net8.0-maccatalyst + net8.0-ios + net8.0-maccatalyst true true .NET Bindings for the Sentry Cocoa SDK diff --git a/src/Sentry.EntityFramework/Sentry.EntityFramework.csproj b/src/Sentry.EntityFramework/Sentry.EntityFramework.csproj index c7f7be1556..bd9096007f 100644 --- a/src/Sentry.EntityFramework/Sentry.EntityFramework.csproj +++ b/src/Sentry.EntityFramework/Sentry.EntityFramework.csproj @@ -26,10 +26,7 @@ - - - - + diff --git a/src/Sentry.Extensions.Logging/Sentry.Extensions.Logging.csproj b/src/Sentry.Extensions.Logging/Sentry.Extensions.Logging.csproj index 33c4a4e8ee..e01605c30a 100644 --- a/src/Sentry.Extensions.Logging/Sentry.Extensions.Logging.csproj +++ b/src/Sentry.Extensions.Logging/Sentry.Extensions.Logging.csproj @@ -6,7 +6,7 @@ Official Microsoft.Extensions.Logging integration for Sentry - Open-source error tracking that helps developers monitor and fix crashes in real time. - + true true diff --git a/src/Sentry.Hangfire/Sentry.Hangfire.csproj b/src/Sentry.Hangfire/Sentry.Hangfire.csproj index 1cc87f3387..3adcde449f 100644 --- a/src/Sentry.Hangfire/Sentry.Hangfire.csproj +++ b/src/Sentry.Hangfire/Sentry.Hangfire.csproj @@ -7,7 +7,7 @@ enable - + true diff --git a/src/Sentry.Maui/Internal/SentryMauiOptionsSetup.cs b/src/Sentry.Maui/Internal/SentryMauiOptionsSetup.cs index 2e2ed0f2fd..d13b9731d8 100644 --- a/src/Sentry.Maui/Internal/SentryMauiOptionsSetup.cs +++ b/src/Sentry.Maui/Internal/SentryMauiOptionsSetup.cs @@ -1,7 +1,9 @@ using System; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Options; +#if !PLATFORM_NEUTRAL using Microsoft.Maui.Networking; +#endif using Sentry.Infrastructure; namespace Sentry.Maui.Internal; diff --git a/src/Sentry.Maui/Sentry.Maui.csproj b/src/Sentry.Maui/Sentry.Maui.csproj index 6c90db5d83..be26d74d91 100644 --- a/src/Sentry.Maui/Sentry.Maui.csproj +++ b/src/Sentry.Maui/Sentry.Maui.csproj @@ -6,11 +6,11 @@ Target net6.0 so we can run unit tests on platform-neutral code. Target other platforms so we can include platform-specific code, and bundle native SDKs. --> - net8.0;net7.0 - $(TargetFrameworks);net8.0-android;net7.0-android - $(TargetFrameworks);net8.0-ios;net7.0-ios - $(TargetFrameworks);net8.0-maccatalyst;net7.0-maccatalyst - $(TargetFrameworks);net8.0-windows10.0.19041.0;net7.0-windows10.0.19041.0 + net9.0;net8.0 + $(TargetFrameworks);net8.0-android;net9.0-android + $(TargetFrameworks);net8.0-ios;net9.0-ios + $(TargetFrameworks);net8.0-maccatalyst;net9.0-maccatalyst + $(TargetFrameworks);net8.0-windows10.0.19041.0 - + + + + true + + + - + - + diff --git a/src/Sentry/Sentry.csproj b/src/Sentry/Sentry.csproj index c4154bceaa..ee0082347b 100644 --- a/src/Sentry/Sentry.csproj +++ b/src/Sentry/Sentry.csproj @@ -9,9 +9,9 @@ net8.0;net6.0;netstandard2.1;netstandard2.0;net462 - $(TargetFrameworks);net7.0-android;net8.0-android - $(TargetFrameworks);net7.0-ios;net8.0-ios - $(TargetFrameworks);net7.0-maccatalyst;net8.0-maccatalyst + $(TargetFrameworks);net8.0-android + $(TargetFrameworks);net8.0-ios + $(TargetFrameworks);net8.0-maccatalyst @@ -24,7 +24,7 @@ - + true @@ -81,12 +81,12 @@ - + - + NU1903 diff --git a/src/Sentry/buildTransitive/Sentry.targets b/src/Sentry/buildTransitive/Sentry.targets index 34c931a129..d7aeada335 100644 --- a/src/Sentry/buildTransitive/Sentry.targets +++ b/src/Sentry/buildTransitive/Sentry.targets @@ -142,7 +142,10 @@ See https://github.com/dotnet/sdk/issues/26324#issuecomment-1169236993 Note 2: Target framework conditions should be kept synchronized with src/Sentry/Platforms/Native/buildTransitive/Sentry.Native.targets --> - net7.0;net6.0 - $(TargetFrameworks);net7.0-android + net8.0;net6.0 + $(TargetFrameworks);net8.0-android enable diff --git a/test/Sentry.AspNetCore.TestUtils/Sentry.AspNetCore.TestUtils.csproj b/test/Sentry.AspNetCore.TestUtils/Sentry.AspNetCore.TestUtils.csproj index 38685ecdc0..e2221aa14a 100644 --- a/test/Sentry.AspNetCore.TestUtils/Sentry.AspNetCore.TestUtils.csproj +++ b/test/Sentry.AspNetCore.TestUtils/Sentry.AspNetCore.TestUtils.csproj @@ -30,10 +30,15 @@ + + + + + diff --git a/test/Sentry.DiagnosticSource.IntegrationTests/LocalDbFixture.cs b/test/Sentry.DiagnosticSource.IntegrationTests/LocalDbFixture.cs index 7b04cc60a5..0e1a01e200 100644 --- a/test/Sentry.DiagnosticSource.IntegrationTests/LocalDbFixture.cs +++ b/test/Sentry.DiagnosticSource.IntegrationTests/LocalDbFixture.cs @@ -4,6 +4,21 @@ public sealed class LocalDbFixture : IDisposable { public SqlInstance SqlInstance { get; } + public static string InstanceName => +#if NETFRAMEWORK + "SqlListenerTests4"; +#elif NET6_0 + "SqlListenerTests6"; +#elif NET7_0 + "SqlListenerTests7"; +#elif NET8_0 + "SqlListenerTests8"; +#elif NET9_0 + "SqlListenerTests9"; +#else +#error Needs a version specific name to prevent the tests from tripping over one another when running in parallel +#endif + public LocalDbFixture() { if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) @@ -12,7 +27,7 @@ public LocalDbFixture() } SqlInstance = new( - name: "SqlListenerTests", + name: InstanceName, buildTemplate: TestDbBuilder.CreateTableAsync); } diff --git a/test/Sentry.DiagnosticSource.IntegrationTests/Sentry.DiagnosticSource.IntegrationTests.csproj b/test/Sentry.DiagnosticSource.IntegrationTests/Sentry.DiagnosticSource.IntegrationTests.csproj index 325ed0e9a2..d395ccdc54 100644 --- a/test/Sentry.DiagnosticSource.IntegrationTests/Sentry.DiagnosticSource.IntegrationTests.csproj +++ b/test/Sentry.DiagnosticSource.IntegrationTests/Sentry.DiagnosticSource.IntegrationTests.csproj @@ -38,7 +38,9 @@ - + + + diff --git a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.verify.cs b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.verify.cs index 2706cb42fe..9c1761ad21 100644 --- a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.verify.cs +++ b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.verify.cs @@ -50,6 +50,7 @@ public async Task RecordsSqlAsync() var result = await Verify(transport.Payloads) .IgnoreMember(_ => _.Environment) + .ScrubLinesWithReplace(line => line.Replace(LocalDbFixture.InstanceName, "SqlListenerTests")) .IgnoreStandardSentryMembers(); Assert.DoesNotContain("SHOULD NOT APPEAR IN PAYLOAD", result.Text); } @@ -118,6 +119,7 @@ void ApplyOptions(SentryLoggingOptions sentryOptions) var result = await Verify(transport.Payloads) .ScrubInlineGuids() .IgnoreMember(_ => _.Environment) + .ScrubLinesWithReplace(line => line.Replace(LocalDbFixture.InstanceName, "SqlListenerTests")) // Really not sure why, but bytes received for this test varies randomly when run in CI // TODO: remove this and investigate @@ -214,6 +216,7 @@ public async Task RecordsEfAsync() var result = await Verify(transport.Payloads) .IgnoreMember(_ => _.Environment) + .ScrubLinesWithReplace(line => line.Replace(LocalDbFixture.InstanceName, "SqlListenerTests")) .IgnoreStandardSentryMembers() .UniqueForRuntimeAndVersion(); Assert.DoesNotContain("SHOULD NOT APPEAR IN PAYLOAD", result.Text); diff --git a/test/Sentry.DiagnosticSource.Tests/Sentry.DiagnosticSource.Tests.csproj b/test/Sentry.DiagnosticSource.Tests/Sentry.DiagnosticSource.Tests.csproj index bf6224ec40..1e7bf12162 100644 --- a/test/Sentry.DiagnosticSource.Tests/Sentry.DiagnosticSource.Tests.csproj +++ b/test/Sentry.DiagnosticSource.Tests/Sentry.DiagnosticSource.Tests.csproj @@ -8,12 +8,16 @@ + + + + diff --git a/test/Sentry.EntityFramework.Tests/Sentry.EntityFramework.Tests.csproj b/test/Sentry.EntityFramework.Tests/Sentry.EntityFramework.Tests.csproj index 056ff3ca41..e2d4c6a37d 100644 --- a/test/Sentry.EntityFramework.Tests/Sentry.EntityFramework.Tests.csproj +++ b/test/Sentry.EntityFramework.Tests/Sentry.EntityFramework.Tests.csproj @@ -10,12 +10,9 @@ - + - - - - + diff --git a/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt b/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt new file mode 100644 index 0000000000..b438b0af45 --- /dev/null +++ b/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt @@ -0,0 +1,59 @@ +namespace Microsoft.Extensions.Logging +{ + public static class LoggingBuilderExtensions + { + public static Microsoft.Extensions.Logging.ILoggingBuilder AddSentry(this Microsoft.Extensions.Logging.ILoggingBuilder builder) { } + public static Microsoft.Extensions.Logging.ILoggingBuilder AddSentry(this Microsoft.Extensions.Logging.ILoggingBuilder builder, System.Action? optionsConfiguration) { } + public static Microsoft.Extensions.Logging.ILoggingBuilder AddSentry(this Microsoft.Extensions.Logging.ILoggingBuilder builder, string dsn) { } + } + public static class SentryLoggerFactoryExtensions + { + public static Microsoft.Extensions.Logging.ILoggerFactory AddSentry(this Microsoft.Extensions.Logging.ILoggerFactory factory, System.Action? optionsConfiguration = null) { } + } +} +namespace Sentry.Extensions.Logging +{ + public class DelegateLogEntryFilter : Sentry.Extensions.Logging.ILogEntryFilter + { + public DelegateLogEntryFilter(System.Func filter) { } + public bool Filter(string categoryName, Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, System.Exception? exception) { } + } + public interface ILogEntryFilter + { + bool Filter(string categoryName, Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, System.Exception? exception); + } + public class MelDiagnosticLogger : Sentry.Extensibility.IDiagnosticLogger + { + public MelDiagnosticLogger(Microsoft.Extensions.Logging.ILogger logger, Sentry.SentryLevel level) { } + public bool IsEnabled(Sentry.SentryLevel level) { } + public void Log(Sentry.SentryLevel logLevel, string message, System.Exception? exception = null, params object?[] args) { } + } + [Microsoft.Extensions.Logging.ProviderAlias("Sentry")] + public class SentryLoggerProvider : Microsoft.Extensions.Logging.ILoggerProvider, System.IDisposable + { + public SentryLoggerProvider(Microsoft.Extensions.Options.IOptions options, Sentry.IHub hub) { } + public Microsoft.Extensions.Logging.ILogger CreateLogger(string categoryName) { } + public void Dispose() { } + } + public class SentryLoggingOptions : Sentry.SentryOptions + { + public SentryLoggingOptions() { } + public bool InitializeSdk { get; set; } + public Microsoft.Extensions.Logging.LogLevel MinimumBreadcrumbLevel { get; set; } + public Microsoft.Extensions.Logging.LogLevel MinimumEventLevel { get; set; } + public void ConfigureScope(System.Action action) { } + } + public static class SentryLoggingOptionsExtensions + { + public static void AddLogEntryFilter(this Sentry.Extensions.Logging.SentryLoggingOptions options, Sentry.Extensions.Logging.ILogEntryFilter filter) { } + public static void AddLogEntryFilter(this Sentry.Extensions.Logging.SentryLoggingOptions options, System.Func filter) { } + } +} +namespace Sentry.Extensions.Logging.Extensions.DependencyInjection +{ + public static class ServiceCollectionExtensions + { + public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddSentry(this Microsoft.Extensions.DependencyInjection.IServiceCollection services) + where TOptions : Sentry.Extensions.Logging.SentryLoggingOptions, new () { } + } +} \ No newline at end of file diff --git a/test/Sentry.Extensions.Logging.Tests/Sentry.Extensions.Logging.Tests.csproj b/test/Sentry.Extensions.Logging.Tests/Sentry.Extensions.Logging.Tests.csproj index ea160bea24..a95627f76a 100644 --- a/test/Sentry.Extensions.Logging.Tests/Sentry.Extensions.Logging.Tests.csproj +++ b/test/Sentry.Extensions.Logging.Tests/Sentry.Extensions.Logging.Tests.csproj @@ -1,10 +1,10 @@  - net8.0;net7.0;net6.0;net48 - $(TargetFrameworks);net7.0-android - $(TargetFrameworks);net7.0-ios - $(TargetFrameworks);net7.0-maccatalyst + net9.0;net8.0;net6.0;net48 + $(TargetFrameworks);net8.0-android + $(TargetFrameworks);net8.0-ios + $(TargetFrameworks);net8.0-maccatalyst diff --git a/test/Sentry.Maui.Device.TestApp/Sentry.Maui.Device.TestApp.csproj b/test/Sentry.Maui.Device.TestApp/Sentry.Maui.Device.TestApp.csproj index 1bf06b90c0..6ca1bad83b 100644 --- a/test/Sentry.Maui.Device.TestApp/Sentry.Maui.Device.TestApp.csproj +++ b/test/Sentry.Maui.Device.TestApp/Sentry.Maui.Device.TestApp.csproj @@ -2,8 +2,8 @@ - $(TargetFrameworks);net7.0-android - $(TargetFrameworks);net7.0-ios + $(TargetFrameworks);net8.0-android + $(TargetFrameworks);net8.0-ios false @@ -39,6 +39,10 @@ $(NoWarn);XA4218 + + true + + + diff --git a/test/Sentry.Maui.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt b/test/Sentry.Maui.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt new file mode 100644 index 0000000000..2113579db2 --- /dev/null +++ b/test/Sentry.Maui.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt @@ -0,0 +1,22 @@ +namespace Microsoft.Maui.Hosting +{ + public static class SentryMauiAppBuilderExtensions + { + public static Microsoft.Maui.Hosting.MauiAppBuilder UseSentry(this Microsoft.Maui.Hosting.MauiAppBuilder builder) { } + public static Microsoft.Maui.Hosting.MauiAppBuilder UseSentry(this Microsoft.Maui.Hosting.MauiAppBuilder builder, System.Action? configureOptions) { } + public static Microsoft.Maui.Hosting.MauiAppBuilder UseSentry(this Microsoft.Maui.Hosting.MauiAppBuilder builder, string dsn) { } + } +} +namespace Sentry.Maui +{ + public class SentryMauiOptions : Sentry.Extensions.Logging.SentryLoggingOptions + { + public SentryMauiOptions() { } + public bool AttachScreenshot { get; set; } + public bool CreateElementEventsBreadcrumbs { get; set; } + public bool IncludeBackgroundingStateInBreadcrumbs { get; set; } + public bool IncludeTextInBreadcrumbs { get; set; } + public bool IncludeTitleInBreadcrumbs { get; set; } + public void SetBeforeScreenshotCapture(System.Func beforeCapture) { } + } +} \ No newline at end of file diff --git a/test/Sentry.Maui.Tests/Sentry.Maui.Tests.csproj b/test/Sentry.Maui.Tests/Sentry.Maui.Tests.csproj index 63ce3a9229..c6e78da6c9 100644 --- a/test/Sentry.Maui.Tests/Sentry.Maui.Tests.csproj +++ b/test/Sentry.Maui.Tests/Sentry.Maui.Tests.csproj @@ -1,17 +1,24 @@ - net7.0;net8.0 - $(TargetFrameworks);net7.0-android;net8.0-android - $(TargetFrameworks);net7.0-ios;net8.0-ios - $(TargetFrameworks);net7.0-maccatalyst;net8.0-maccatalyst + net9.0;net8.0 + $(TargetFrameworks);net8.0-android + $(TargetFrameworks);net8.0-ios + $(TargetFrameworks);net8.0-maccatalyst true - + + + + + + + + diff --git a/test/Sentry.Testing/Sentry.Testing.csproj b/test/Sentry.Testing/Sentry.Testing.csproj index 743b97be54..44946840ac 100644 --- a/test/Sentry.Testing/Sentry.Testing.csproj +++ b/test/Sentry.Testing/Sentry.Testing.csproj @@ -1,10 +1,10 @@  - net8.0;net7.0;net6.0;net48 - $(TargetFrameworks);net7.0-android;net8.0-android - $(TargetFrameworks);net7.0-ios;net8.0-ios - $(TargetFrameworks);net7.0-maccatalyst;net8.0-maccatalyst + net9.0;net8.0;net6.0;net48 + $(TargetFrameworks);net8.0-android;net9.0-android + $(TargetFrameworks);net8.0-ios;net9.0-ios + $(TargetFrameworks);net8.0-maccatalyst;net9.0-maccatalyst false diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt new file mode 100644 index 0000000000..5d13ca7595 --- /dev/null +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt @@ -0,0 +1,1804 @@ +[assembly: System.CLSCompliant(true)] +namespace Sentry +{ + public enum AttachmentType + { + Default = 0, + Minidump = 1, + AppleCrashReport = 2, + UnrealContext = 3, + UnrealLogs = 4, + ViewHierarchy = 5, + } + public class BaggageHeader + { + public override string ToString() { } + } + [System.Diagnostics.DebuggerDisplay("Message: {Message}, Type: {Type}")] + public sealed class Breadcrumb : Sentry.ISentryJsonSerializable + { + public Breadcrumb(string message, string type, System.Collections.Generic.IReadOnlyDictionary? data = null, string? category = null, Sentry.BreadcrumbLevel level = 0) { } + public string? Category { get; } + public System.Collections.Generic.IReadOnlyDictionary? Data { get; } + public Sentry.BreadcrumbLevel Level { get; } + public string? Message { get; } + public System.DateTimeOffset Timestamp { get; } + public string? Type { get; } + public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } + public static Sentry.Breadcrumb FromJson(System.Text.Json.JsonElement json) { } + } + public enum BreadcrumbLevel + { + [System.Runtime.Serialization.EnumMember(Value="debug")] + Debug = -1, + [System.Runtime.Serialization.EnumMember(Value="info")] + Info = 0, + [System.Runtime.Serialization.EnumMember(Value="warning")] + Warning = 1, + [System.Runtime.Serialization.EnumMember(Value="error")] + Error = 2, + [System.Runtime.Serialization.EnumMember(Value="critical")] + Critical = 3, + } + public static class BuiltInSystemDiagnosticsMeters + { + public static readonly Sentry.StringOrRegex MicrosoftAspNetCoreDiagnostics; + public static readonly Sentry.StringOrRegex MicrosoftAspNetCoreHeaderParsing; + public static readonly Sentry.StringOrRegex MicrosoftAspNetCoreHosting; + public static readonly Sentry.StringOrRegex MicrosoftAspNetCoreHttpConnections; + public static readonly Sentry.StringOrRegex MicrosoftAspNetCoreRateLimiting; + public static readonly Sentry.StringOrRegex MicrosoftAspNetCoreRouting; + public static readonly Sentry.StringOrRegex MicrosoftAspNetCoreServerKestrel; + public static readonly Sentry.StringOrRegex MicrosoftExtensionsDiagnosticsHealthChecks; + public static readonly Sentry.StringOrRegex MicrosoftExtensionsDiagnosticsResourceMonitoring; + public static readonly Sentry.StringOrRegex OpenTelemetryInstrumentationRuntime; + public static readonly Sentry.StringOrRegex SystemNetHttp; + public static readonly Sentry.StringOrRegex SystemNetNameResolution; + public static System.Collections.Generic.IList All { get; } + } + public class ByteAttachmentContent : Sentry.IAttachmentContent + { + public ByteAttachmentContent(byte[] bytes) { } + public System.IO.Stream GetStream() { } + } + public enum CheckInStatus + { + InProgress = 0, + Ok = 1, + Error = 2, + } + [System.Obsolete("WARNING: This method deliberately causes a crash, and should not be used in a rea" + + "l application.")] + public enum CrashType + { + Managed = 0, + ManagedBackgroundThread = 1, + Native = 2, + } + [System.Flags] + public enum DeduplicateMode + { + SameEvent = 1, + SameExceptionInstance = 2, + InnerException = 4, + AggregateException = 8, + All = 2147483647, + } + public class DefaultSentryScopeStateProcessor : Sentry.ISentryScopeStateProcessor + { + public DefaultSentryScopeStateProcessor() { } + public void Apply(Sentry.Scope scope, object state) { } + } + [System.AttributeUsage(System.AttributeTargets.Assembly)] + public class DsnAttribute : System.Attribute + { + public DsnAttribute(string dsn) { } + public string Dsn { get; } + } + public static class EventLikeExtensions + { + public static void AddBreadcrumb(this Sentry.IEventLike eventLike, string message, string? category, string? type, System.ValueTuple? dataPair = default, Sentry.BreadcrumbLevel level = 0) { } + public static void AddBreadcrumb(this Sentry.IEventLike eventLike, string message, string? category = null, string? type = null, System.Collections.Generic.IReadOnlyDictionary? data = null, Sentry.BreadcrumbLevel level = 0) { } + public static void AddBreadcrumb(this Sentry.IEventLike eventLike, System.DateTimeOffset? timestamp, string message, string? category = null, string? type = null, System.Collections.Generic.IReadOnlyDictionary? data = null, Sentry.BreadcrumbLevel level = 0) { } + public static bool HasUser(this Sentry.IEventLike eventLike) { } + public static void SetFingerprint(this Sentry.IEventLike eventLike, System.Collections.Generic.IEnumerable fingerprint) { } + public static void SetFingerprint(this Sentry.IEventLike eventLike, params string[] fingerprint) { } + } + public class ExperimentalMetricsOptions + { + public ExperimentalMetricsOptions() { } + public System.Collections.Generic.IList CaptureSystemDiagnosticsInstruments { get; set; } + public System.Collections.Generic.IList CaptureSystemDiagnosticsMeters { get; set; } + public bool EnableCodeLocations { get; set; } + } + public class FileAttachmentContent : Sentry.IAttachmentContent + { + public FileAttachmentContent(string filePath) { } + public FileAttachmentContent(string filePath, bool readFileAsynchronously) { } + public System.IO.Stream GetStream() { } + } + public static class HasExtraExtensions + { + public static void SetExtras(this Sentry.IHasExtra hasExtra, System.Collections.Generic.IEnumerable> values) { } + } + public static class HasTagsExtensions + { + public static void SetTags(this Sentry.IHasTags hasTags, System.Collections.Generic.IEnumerable> tags) { } + } + public static class HintTypes + { + public const string HttpResponseMessage = "http-response-message"; + } + public readonly struct HttpStatusCodeRange : System.IEquatable + { + public HttpStatusCodeRange(int statusCode) { } + public HttpStatusCodeRange(int start, int end) { } + public int End { get; init; } + public int Start { get; init; } + public bool Contains(int statusCode) { } + public bool Contains(System.Net.HttpStatusCode statusCode) { } + public static Sentry.HttpStatusCodeRange op_Implicit(int statusCode) { } + public static Sentry.HttpStatusCodeRange op_Implicit(System.Net.HttpStatusCode statusCode) { } + public static Sentry.HttpStatusCodeRange op_Implicit([System.Runtime.CompilerServices.TupleElementNames(new string[] { + "Start", + "End"})] System.ValueTuple range) { } + public static Sentry.HttpStatusCodeRange op_Implicit([System.Runtime.CompilerServices.TupleElementNames(new string[] { + "start", + "end"})] System.ValueTuple range) { } + } + public static class HubExtensions + { + public static void AddBreadcrumb(this Sentry.IHub hub, Sentry.Breadcrumb breadcrumb, Sentry.SentryHint? hint = null) { } + public static void AddBreadcrumb(this Sentry.IHub hub, string message, string? category = null, string? type = null, System.Collections.Generic.IDictionary? data = null, Sentry.BreadcrumbLevel level = 0) { } + public static void AddBreadcrumb(this Sentry.IHub hub, Sentry.Infrastructure.ISystemClock? clock, string message, string? category = null, string? type = null, System.Collections.Generic.IDictionary? data = null, Sentry.BreadcrumbLevel level = 0) { } + public static Sentry.SentryId CaptureException(this Sentry.IHub hub, System.Exception ex, System.Action configureScope) { } + public static Sentry.SentryId CaptureMessage(this Sentry.IHub hub, string message, System.Action configureScope, Sentry.SentryLevel level = 1) { } + public static void LockScope(this Sentry.IHub hub) { } + public static System.IDisposable PushAndLockScope(this Sentry.IHub hub) { } + public static Sentry.ITransactionTracer StartTransaction(this Sentry.IHub hub, Sentry.ITransactionContext context) { } + public static Sentry.ITransactionTracer StartTransaction(this Sentry.IHub hub, string name, string operation) { } + public static Sentry.ITransactionTracer StartTransaction(this Sentry.IHub hub, string name, string operation, Sentry.SentryTraceHeader traceHeader) { } + public static Sentry.ITransactionTracer StartTransaction(this Sentry.IHub hub, string name, string operation, string? description) { } + public static void UnlockScope(this Sentry.IHub hub) { } + } + public interface IAttachmentContent + { + System.IO.Stream GetStream(); + } + public interface IEventLike : Sentry.IHasExtra, Sentry.IHasTags + { + System.Collections.Generic.IReadOnlyCollection Breadcrumbs { get; } + Sentry.SentryContexts Contexts { get; set; } + string? Distribution { get; set; } + string? Environment { get; set; } + System.Collections.Generic.IReadOnlyList Fingerprint { get; set; } + Sentry.SentryLevel? Level { get; set; } + string? Release { get; set; } + Sentry.SentryRequest Request { get; set; } + Sentry.SdkVersion Sdk { get; } + string? TransactionName { get; set; } + Sentry.SentryUser User { get; set; } + void AddBreadcrumb(Sentry.Breadcrumb breadcrumb); + } + public interface IHasExtra + { + System.Collections.Generic.IReadOnlyDictionary Extra { get; } + void SetExtra(string key, object? value); + } + public interface IHasTags + { + System.Collections.Generic.IReadOnlyDictionary Tags { get; } + void SetTag(string key, string value); + void UnsetTag(string key); + } + public interface IHub : Sentry.ISentryClient, Sentry.ISentryScopeManager + { + Sentry.SentryId LastEventId { get; } + void BindException(System.Exception exception, Sentry.ISpan span); + Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, System.Action configureScope); + Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, Sentry.SentryHint? hint, System.Action configureScope); + Sentry.TransactionContext ContinueTrace(Sentry.SentryTraceHeader? traceHeader, Sentry.BaggageHeader? baggageHeader, string? name = null, string? operation = null); + Sentry.TransactionContext ContinueTrace(string? traceHeader, string? baggageHeader, string? name = null, string? operation = null); + void EndSession(Sentry.SessionEndStatus status = 0); + Sentry.BaggageHeader? GetBaggage(); + Sentry.ISpan? GetSpan(); + Sentry.SentryTraceHeader? GetTraceHeader(); + void PauseSession(); + void ResumeSession(); + void StartSession(); + Sentry.ITransactionTracer StartTransaction(Sentry.ITransactionContext context, System.Collections.Generic.IReadOnlyDictionary customSamplingContext); + } + public interface IScopeObserver + { + void AddBreadcrumb(Sentry.Breadcrumb breadcrumb); + void SetExtra(string key, object? value); + void SetTag(string key, string value); + void SetUser(Sentry.SentryUser? user); + void UnsetTag(string key); + } + public interface ISentryClient + { + bool IsEnabled { get; } + Sentry.SentryId CaptureCheckIn(string monitorSlug, Sentry.CheckInStatus status, Sentry.SentryId? sentryId = default, System.TimeSpan? duration = default, Sentry.Scope? scope = null, System.Action? configureMonitorOptions = null); + bool CaptureEnvelope(Sentry.Protocol.Envelopes.Envelope envelope); + Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, Sentry.Scope? scope = null, Sentry.SentryHint? hint = null); + void CaptureSession(Sentry.SessionUpdate sessionUpdate); + void CaptureTransaction(Sentry.SentryTransaction transaction); + void CaptureTransaction(Sentry.SentryTransaction transaction, Sentry.Scope? scope, Sentry.SentryHint? hint); + void CaptureUserFeedback(Sentry.UserFeedback userFeedback); + System.Threading.Tasks.Task FlushAsync(System.TimeSpan timeout); + } + public interface ISentryJsonSerializable + { + void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger); + } + public interface ISentryScopeManager + { + void BindClient(Sentry.ISentryClient client); + void ConfigureScope(System.Action configureScope); + System.Threading.Tasks.Task ConfigureScopeAsync(System.Func configureScope); + System.IDisposable PushScope(); + System.IDisposable PushScope(TState state); + } + public interface ISentryScopeStateProcessor + { + void Apply(Sentry.Scope scope, object state); + } + public interface ISentrySession + { + string? DistinctId { get; } + string? Environment { get; } + int ErrorCount { get; } + Sentry.SentryId Id { get; } + string? IpAddress { get; } + string Release { get; } + System.DateTimeOffset StartTimestamp { get; } + string? UserAgent { get; } + } + public interface ISentryUserFactory + { + Sentry.SentryUser? Create(); + } + public interface ISpan : Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpanData, Sentry.Protocol.ITraceContext + { + new string? Description { get; set; } + new string Operation { get; set; } + new Sentry.SpanStatus? Status { get; set; } + void Finish(); + void Finish(Sentry.SpanStatus status); + void Finish(System.Exception exception); + void Finish(System.Exception exception, Sentry.SpanStatus status); + Sentry.ISpan StartChild(string operation); + } + public interface ISpanData : Sentry.IHasExtra, Sentry.IHasTags, Sentry.Protocol.ITraceContext + { + System.DateTimeOffset? EndTimestamp { get; } + bool IsFinished { get; } + System.Collections.Generic.IReadOnlyDictionary Measurements { get; } + System.DateTimeOffset StartTimestamp { get; } + Sentry.SentryTraceHeader GetTraceHeader(); + void SetMeasurement(string name, Sentry.Protocol.Measurement measurement); + } + public interface ITransactionContext : Sentry.Protocol.ITraceContext + { + bool? IsParentSampled { get; } + string Name { get; } + Sentry.TransactionNameSource NameSource { get; } + } + public interface ITransactionData : Sentry.IEventLike, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.Protocol.ITraceContext + { + string? Platform { get; set; } + } + public interface ITransactionTracer : Sentry.IEventLike, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpan, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.Protocol.ITraceContext + { + new bool? IsParentSampled { get; set; } + new string Name { get; set; } + System.Collections.Generic.IReadOnlyCollection Spans { get; } + Sentry.ISpan? GetLastActiveSpan(); + } + public enum InstructionAddressAdjustment + { + Auto = 0, + All = 1, + AllButFirst = 2, + None = 3, + } + public enum Instrumenter + { + Sentry = 0, + OpenTelemetry = 1, + } + public readonly struct MeasurementUnit : System.IEquatable + { + public static Sentry.MeasurementUnit None; + public bool Equals(Sentry.MeasurementUnit other) { } + public override bool Equals(object? obj) { } + public override int GetHashCode() { } + public override string ToString() { } + public static Sentry.MeasurementUnit Custom(string name) { } + public static Sentry.MeasurementUnit op_Implicit(Sentry.MeasurementUnit.Duration unit) { } + public static Sentry.MeasurementUnit op_Implicit(Sentry.MeasurementUnit.Fraction unit) { } + public static Sentry.MeasurementUnit op_Implicit(Sentry.MeasurementUnit.Information unit) { } + public static bool operator !=(Sentry.MeasurementUnit left, Sentry.MeasurementUnit right) { } + public static bool operator ==(Sentry.MeasurementUnit left, Sentry.MeasurementUnit right) { } + public enum Duration + { + Nanosecond = 0, + Microsecond = 1, + Millisecond = 2, + Second = 3, + Minute = 4, + Hour = 5, + Day = 6, + Week = 7, + } + public enum Fraction + { + Ratio = 0, + Percent = 1, + } + public enum Information + { + Bit = 0, + Byte = 1, + Kilobyte = 2, + Kibibyte = 3, + Megabyte = 4, + Mebibyte = 5, + Gigabyte = 6, + Gibibyte = 7, + Terabyte = 8, + Tebibyte = 9, + Petabyte = 10, + Pebibyte = 11, + Exabyte = 12, + Exbibyte = 13, + } + } + public enum ReportAssembliesMode + { + None = 0, + Version = 1, + InformationalVersion = 2, + } + public class Scope : Sentry.IEventLike, Sentry.IHasExtra, Sentry.IHasTags + { + public Scope(Sentry.SentryOptions? options) { } + public System.Collections.Generic.IReadOnlyCollection Attachments { get; } + public System.Collections.Generic.IReadOnlyCollection Breadcrumbs { get; } + public Sentry.SentryContexts Contexts { get; set; } + public string? Distribution { get; set; } + public string? Environment { get; set; } + public System.Collections.Generic.IReadOnlyDictionary Extra { get; } + public System.Collections.Generic.IReadOnlyList Fingerprint { get; set; } + public Sentry.SentryLevel? Level { get; set; } + public string? Release { get; set; } + public Sentry.SentryRequest Request { get; set; } + public Sentry.SdkVersion Sdk { get; } + public Sentry.ISpan? Span { get; set; } + public System.Collections.Generic.IReadOnlyDictionary Tags { get; } + public Sentry.ITransactionTracer? Transaction { get; set; } + public string? TransactionName { get; set; } + public Sentry.SentryUser User { get; set; } + public void AddAttachment(Sentry.SentryAttachment attachment) { } + public void AddAttachment(string filePath, Sentry.AttachmentType type = 0, string? contentType = null) { } + public void AddAttachment(byte[] data, string fileName, Sentry.AttachmentType type = 0, string? contentType = null) { } + public void AddAttachment(System.IO.Stream stream, string fileName, Sentry.AttachmentType type = 0, string? contentType = null) { } + public void AddBreadcrumb(Sentry.Breadcrumb breadcrumb) { } + public void AddBreadcrumb(Sentry.Breadcrumb breadcrumb, Sentry.SentryHint hint) { } + public void AddEventProcessor(Sentry.Extensibility.ISentryEventProcessor processor) { } + public void AddEventProcessor(System.Func processor) { } + public void AddEventProcessors(System.Collections.Generic.IEnumerable processors) { } + public void AddExceptionProcessor(Sentry.Extensibility.ISentryEventExceptionProcessor processor) { } + public void AddExceptionProcessors(System.Collections.Generic.IEnumerable processors) { } + public void AddTransactionProcessor(Sentry.Extensibility.ISentryTransactionProcessor processor) { } + public void AddTransactionProcessor(System.Func processor) { } + public void AddTransactionProcessors(System.Collections.Generic.IEnumerable processors) { } + public void Apply(Sentry.IEventLike other) { } + public void Apply(Sentry.Scope other) { } + public void Apply(object state) { } + public void Clear() { } + public void ClearAttachments() { } + public void ClearBreadcrumbs() { } + public Sentry.Scope Clone() { } + public System.Collections.Generic.IEnumerable GetAllEventProcessors() { } + public System.Collections.Generic.IEnumerable GetAllExceptionProcessors() { } + public System.Collections.Generic.IEnumerable GetAllTransactionProcessors() { } + public void SetExtra(string key, object? value) { } + public void SetTag(string key, string value) { } + public void UnsetTag(string key) { } + } + public sealed class SdkVersion : Sentry.ISentryJsonSerializable + { + public SdkVersion() { } + public string? Name { get; set; } + public System.Collections.Generic.IEnumerable Packages { get; } + public string? Version { get; set; } + public void AddIntegration(string integration) { } + public void AddPackage(string name, string version) { } + public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } + public static Sentry.SdkVersion FromJson(System.Text.Json.JsonElement json) { } + } + [System.Diagnostics.DebuggerDisplay("{FileName}")] + public class SentryAttachment + { + public SentryAttachment(Sentry.AttachmentType type, Sentry.IAttachmentContent content, string fileName, string? contentType) { } + public Sentry.IAttachmentContent Content { get; } + public string? ContentType { get; } + public string FileName { get; } + public Sentry.AttachmentType Type { get; } + } + public class SentryCheckIn : Sentry.ISentryJsonSerializable + { + public SentryCheckIn(string monitorSlug, Sentry.CheckInStatus status, Sentry.SentryId? sentryId = default) { } + public System.TimeSpan? Duration { get; set; } + public string? Environment { get; set; } + public Sentry.SentryId Id { get; } + public string MonitorSlug { get; } + public string? Release { get; set; } + public Sentry.CheckInStatus Status { get; } + public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } + } + public class SentryClient : Sentry.ISentryClient, System.IDisposable + { + public SentryClient(Sentry.SentryOptions options) { } + public bool IsEnabled { get; } + public Sentry.SentryId CaptureCheckIn(string monitorSlug, Sentry.CheckInStatus status, Sentry.SentryId? sentryId = default, System.TimeSpan? duration = default, Sentry.Scope? scope = null, System.Action? configureMonitorOptions = null) { } + public bool CaptureEnvelope(Sentry.Protocol.Envelopes.Envelope envelope) { } + public Sentry.SentryId CaptureEvent(Sentry.SentryEvent? @event, Sentry.Scope? scope = null, Sentry.SentryHint? hint = null) { } + public void CaptureSession(Sentry.SessionUpdate sessionUpdate) { } + public void CaptureTransaction(Sentry.SentryTransaction transaction) { } + public void CaptureTransaction(Sentry.SentryTransaction transaction, Sentry.Scope? scope, Sentry.SentryHint? hint) { } + public void CaptureUserFeedback(Sentry.UserFeedback userFeedback) { } + public void Dispose() { } + public System.Threading.Tasks.Task FlushAsync(System.TimeSpan timeout) { } + } + public static class SentryClientExtensions + { + public static Sentry.SentryId CaptureException(this Sentry.ISentryClient client, System.Exception ex) { } + public static Sentry.SentryId CaptureMessage(this Sentry.ISentryClient client, string message, Sentry.SentryLevel level = 1) { } + public static void CaptureUserFeedback(this Sentry.ISentryClient client, Sentry.SentryId eventId, string email, string comments, string? name = null) { } + public static void Flush(this Sentry.ISentryClient client) { } + public static void Flush(this Sentry.ISentryClient client, System.TimeSpan timeout) { } + public static System.Threading.Tasks.Task FlushAsync(this Sentry.ISentryClient client) { } + } + public static class SentryConstants + { + public const int DefaultMaxBreadcrumbs = 100; + public const string DisableSdkDsnValue = ""; + public const string Platform = "csharp"; + public const int ProtocolVersion = 7; + } + public sealed class SentryContexts : Sentry.ISentryJsonSerializable, System.Collections.Generic.ICollection>, System.Collections.Generic.IDictionary, System.Collections.Generic.IEnumerable>, System.Collections.IEnumerable + { + public SentryContexts() { } + public Sentry.Protocol.App App { get; } + public Sentry.Protocol.Browser Browser { get; } + public int Count { get; } + public Sentry.Protocol.Device Device { get; } + public Sentry.Protocol.Gpu Gpu { get; } + public bool IsReadOnly { get; } + public object this[string key] { get; set; } + public System.Collections.Generic.ICollection Keys { get; } + public Sentry.Protocol.OperatingSystem OperatingSystem { get; } + public Sentry.Protocol.Response Response { get; } + public Sentry.Protocol.Runtime Runtime { get; } + public Sentry.Protocol.Trace Trace { get; } + public System.Collections.Generic.ICollection Values { get; } + public void Add(System.Collections.Generic.KeyValuePair item) { } + public void Add(string key, object value) { } + public void Clear() { } + public bool Contains(System.Collections.Generic.KeyValuePair item) { } + public bool ContainsKey(string key) { } + public void CopyTo(System.Collections.Generic.KeyValuePair[] array, int arrayIndex) { } + public System.Collections.Generic.IEnumerator> GetEnumerator() { } + public bool Remove(System.Collections.Generic.KeyValuePair item) { } + public bool Remove(string key) { } + public bool TryGetValue(string key, out object value) { } + public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } + public static Sentry.SentryContexts FromJson(System.Text.Json.JsonElement json) { } + } + [System.Diagnostics.DebuggerDisplay("{GetType().Name,nq}: {EventId,nq}")] + public sealed class SentryEvent : Sentry.IEventLike, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISentryJsonSerializable + { + public SentryEvent() { } + public SentryEvent(System.Exception? exception) { } + public System.Collections.Generic.IReadOnlyCollection Breadcrumbs { get; } + public Sentry.SentryContexts Contexts { get; set; } + public System.Collections.Generic.List? DebugImages { get; set; } + public string? Distribution { get; set; } + public string? Environment { get; set; } + public Sentry.SentryId EventId { get; } + public System.Exception? Exception { get; } + public System.Collections.Generic.IReadOnlyDictionary Extra { get; } + public System.Collections.Generic.IReadOnlyList Fingerprint { get; set; } + public Sentry.SentryLevel? Level { get; set; } + public string? Logger { get; set; } + public Sentry.SentryMessage? Message { get; set; } + public System.Collections.Generic.IDictionary Modules { get; } + public string? Platform { get; set; } + public string? Release { get; set; } + public Sentry.SentryRequest Request { get; set; } + public Sentry.SdkVersion Sdk { get; } + public System.Collections.Generic.IEnumerable? SentryExceptions { get; set; } + public System.Collections.Generic.IEnumerable? SentryThreads { get; set; } + public string? ServerName { get; set; } + public System.Collections.Generic.IReadOnlyDictionary Tags { get; } + public System.DateTimeOffset Timestamp { get; } + public string? TransactionName { get; set; } + public Sentry.SentryUser User { get; set; } + public void AddBreadcrumb(Sentry.Breadcrumb breadcrumb) { } + public void SetExtra(string key, object? value) { } + public void SetTag(string key, string value) { } + public void UnsetTag(string key) { } + public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } + public static Sentry.SentryEvent FromJson(System.Text.Json.JsonElement json) { } + } + public class SentryGraphQLHttpMessageHandler : Sentry.SentryMessageHandler + { + public SentryGraphQLHttpMessageHandler(System.Net.Http.HttpMessageHandler? innerHandler = null, Sentry.IHub? hub = null) { } + protected override void HandleResponse(System.Net.Http.HttpResponseMessage response, Sentry.ISpan? span, string method, string url) { } + protected override Sentry.ISpan? ProcessRequest(System.Net.Http.HttpRequestMessage request, string method, string url) { } + } + public class SentryHint + { + public SentryHint() { } + public SentryHint(string key, object? value) { } + public System.Collections.Generic.ICollection Attachments { get; } + public System.Collections.Generic.IDictionary Items { get; } + public void AddAttachment(string filePath, Sentry.AttachmentType type = 0, string? contentType = null) { } + public void AddAttachment(byte[] data, string fileName, Sentry.AttachmentType type = 0, string? contentType = null) { } + public static Sentry.SentryHint WithAttachments(params Sentry.SentryAttachment[] attachments) { } + public static Sentry.SentryHint WithAttachments(System.Collections.Generic.IEnumerable attachments) { } + } + public class SentryHttpMessageHandler : Sentry.SentryMessageHandler + { + public SentryHttpMessageHandler() { } + public SentryHttpMessageHandler(Sentry.IHub hub) { } + public SentryHttpMessageHandler(System.Net.Http.HttpMessageHandler innerHandler) { } + public SentryHttpMessageHandler(System.Net.Http.HttpMessageHandler innerHandler, Sentry.IHub hub) { } + protected override void HandleResponse(System.Net.Http.HttpResponseMessage response, Sentry.ISpan? span, string method, string url) { } + protected override Sentry.ISpan? ProcessRequest(System.Net.Http.HttpRequestMessage request, string method, string url) { } + } + public readonly struct SentryId : Sentry.ISentryJsonSerializable, System.IEquatable + { + public static readonly Sentry.SentryId Empty; + public SentryId(System.Guid guid) { } + public bool Equals(Sentry.SentryId other) { } + public override bool Equals(object? obj) { } + public override int GetHashCode() { } + public override string ToString() { } + public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } + public static Sentry.SentryId Create() { } + public static Sentry.SentryId FromJson(System.Text.Json.JsonElement json) { } + public static Sentry.SentryId Parse(string value) { } + public static System.Guid op_Implicit(Sentry.SentryId sentryId) { } + public static Sentry.SentryId op_Implicit(System.Guid guid) { } + public static bool operator !=(Sentry.SentryId left, Sentry.SentryId right) { } + public static bool operator ==(Sentry.SentryId left, Sentry.SentryId right) { } + } + public enum SentryLevel : short + { + [System.Runtime.Serialization.EnumMember(Value="debug")] + Debug = 0, + [System.Runtime.Serialization.EnumMember(Value="info")] + Info = 1, + [System.Runtime.Serialization.EnumMember(Value="warning")] + Warning = 2, + [System.Runtime.Serialization.EnumMember(Value="error")] + Error = 3, + [System.Runtime.Serialization.EnumMember(Value="fatal")] + Fatal = 4, + } + public sealed class SentryMessage : Sentry.ISentryJsonSerializable + { + public SentryMessage() { } + public string? Formatted { get; set; } + public string? Message { get; set; } + public System.Collections.Generic.IEnumerable? Params { get; set; } + public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } + public static Sentry.SentryMessage FromJson(System.Text.Json.JsonElement json) { } + public static Sentry.SentryMessage op_Implicit(string? message) { } + } + public abstract class SentryMessageHandler : System.Net.Http.DelegatingHandler + { + protected SentryMessageHandler() { } + protected SentryMessageHandler(Sentry.IHub hub) { } + protected SentryMessageHandler(System.Net.Http.HttpMessageHandler innerHandler) { } + protected SentryMessageHandler(System.Net.Http.HttpMessageHandler innerHandler, Sentry.IHub hub) { } + protected abstract void HandleResponse(System.Net.Http.HttpResponseMessage response, Sentry.ISpan? span, string method, string url); + protected abstract Sentry.ISpan? ProcessRequest(System.Net.Http.HttpRequestMessage request, string method, string url); + protected override System.Net.Http.HttpResponseMessage Send(System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) { } + protected override System.Threading.Tasks.Task SendAsync(System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) { } + } + public enum SentryMonitorInterval + { + Year = 0, + Month = 1, + Week = 2, + Day = 3, + Hour = 4, + Minute = 5, + } + public class SentryMonitorOptions : Sentry.ISentryJsonSerializable + { + public System.TimeSpan? CheckInMargin { get; set; } + public int? FailureIssueThreshold { get; set; } + public System.TimeSpan? MaxRuntime { get; set; } + public string? Owner { get; set; } + public int? RecoveryThreshold { get; set; } + public string? TimeZone { get; set; } + public void Interval(string crontab) { } + public void Interval(int interval, Sentry.SentryMonitorInterval unit) { } + public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } + } + public class SentryOptions + { + public SentryOptions() { } + [System.CLSCompliant(false)] + public System.Func? AssemblyReader { get; set; } + public bool AttachStacktrace { get; set; } + public bool AutoSessionTracking { get; set; } + public System.TimeSpan AutoSessionTrackingInterval { get; set; } + public Sentry.Extensibility.IBackgroundWorker? BackgroundWorker { get; set; } + public string? CacheDirectoryPath { get; set; } + public bool CaptureFailedRequests { get; set; } + public System.Action? ConfigureClient { get; set; } + public System.Func? CrashedLastRun { get; set; } + public System.Func? CreateHttpMessageHandler { get; set; } + public bool Debug { get; set; } + public System.Net.DecompressionMethods DecompressionMethods { get; set; } + public Sentry.DeduplicateMode DeduplicateMode { get; set; } + public System.Collections.Generic.Dictionary DefaultTags { get; } + public Sentry.StartupTimeDetectionMode DetectStartupTime { get; set; } + public Sentry.SentryLevel DiagnosticLevel { get; set; } + public Sentry.Extensibility.IDiagnosticLogger? DiagnosticLogger { get; set; } + public bool DisableFileWrite { get; set; } + public string? Distribution { get; set; } + public string? Dsn { get; set; } + public bool EnableScopeSync { get; set; } + public bool EnableSpotlight { get; set; } + public string? Environment { get; set; } + public System.Collections.Generic.IList FailedRequestStatusCodes { get; set; } + public System.Collections.Generic.IList FailedRequestTargets { get; set; } + public System.TimeSpan FlushTimeout { get; set; } + public System.Net.IWebProxy? HttpProxy { get; set; } + public System.TimeSpan InitCacheFlushTimeout { get; set; } + public bool IsEnvironmentUser { get; set; } + public bool IsGlobalModeEnabled { get; set; } + public bool JsonPreserveReferences { get; set; } + public long MaxAttachmentSize { get; set; } + public int MaxBreadcrumbs { get; set; } + public int MaxCacheItems { get; set; } + public int MaxQueueItems { get; set; } + public Sentry.Extensibility.INetworkStatusListener? NetworkStatusListener { get; set; } + public double? ProfilesSampleRate { get; set; } + public string? Release { get; set; } + public Sentry.ReportAssembliesMode ReportAssembliesMode { get; set; } + public bool RequestBodyCompressionBuffered { get; set; } + public System.IO.Compression.CompressionLevel RequestBodyCompressionLevel { get; set; } + public float? SampleRate { get; set; } + public Sentry.IScopeObserver? ScopeObserver { get; set; } + public bool SendClientReports { get; set; } + public bool SendDefaultPii { get; set; } + public Sentry.ISentryScopeStateProcessor SentryScopeStateProcessor { get; set; } + public string? ServerName { get; set; } + public System.TimeSpan ShutdownTimeout { get; set; } + public string SpotlightUrl { get; set; } + public Sentry.StackTraceMode StackTraceMode { get; set; } + public System.Collections.Generic.IList TagFilters { get; set; } + public System.Collections.Generic.IList TracePropagationTargets { get; set; } + public double? TracesSampleRate { get; set; } + public System.Func? TracesSampler { get; set; } + public Sentry.Extensibility.ITransport? Transport { get; set; } + public bool UseAsyncFileIO { get; set; } + public void AddEventProcessor(Sentry.Extensibility.ISentryEventProcessor processor) { } + public void AddEventProcessorProvider(System.Func> processorProvider) { } + public void AddEventProcessors(System.Collections.Generic.IEnumerable processors) { } + public void AddExceptionFilter(Sentry.Extensibility.IExceptionFilter exceptionFilter) { } + public void AddExceptionFilterForType() + where TException : System.Exception { } + public void AddExceptionProcessor(Sentry.Extensibility.ISentryEventExceptionProcessor processor) { } + public void AddExceptionProcessorProvider(System.Func> processorProvider) { } + public void AddExceptionProcessors(System.Collections.Generic.IEnumerable processors) { } + public void AddInAppExclude(string prefix) { } + public void AddInAppExclude(System.Text.RegularExpressions.Regex regex) { } + public void AddInAppExcludeRegex(string pattern) { } + public void AddInAppInclude(string prefix) { } + public void AddInAppInclude(System.Text.RegularExpressions.Regex regex) { } + public void AddInAppIncludeRegex(string pattern) { } + public void AddIntegration(Sentry.Integrations.ISdkIntegration integration) { } + public void AddJsonConverter(System.Text.Json.Serialization.JsonConverter converter) { } + public void AddJsonSerializerContext(System.Func contextBuilder) + where T : System.Text.Json.Serialization.JsonSerializerContext { } + public void AddTransactionProcessor(Sentry.Extensibility.ISentryTransactionProcessor processor) { } + public void AddTransactionProcessorProvider(System.Func> processorProvider) { } + public void AddTransactionProcessors(System.Collections.Generic.IEnumerable processors) { } + public void ApplyDefaultTags(Sentry.IHasTags hasTags) { } + public void DisableAppDomainProcessExitFlush() { } + public void DisableAppDomainUnhandledExceptionCapture() { } + public void DisableDiagnosticSourceIntegration() { } + public void DisableDuplicateEventDetection() { } + public void DisableSystemDiagnosticsMetricsIntegration() { } + public void DisableUnobservedTaskExceptionCapture() { } + public void DisableWinUiUnhandledExceptionIntegration() { } + public System.Collections.Generic.IEnumerable GetAllEventProcessors() { } + public System.Collections.Generic.IEnumerable GetAllExceptionProcessors() { } + public System.Collections.Generic.IEnumerable GetAllTransactionProcessors() { } + public void RemoveEventProcessor() + where TProcessor : Sentry.Extensibility.ISentryEventProcessor { } + public void RemoveExceptionFilter() + where TFilter : Sentry.Extensibility.IExceptionFilter { } + public void RemoveIntegration() + where TIntegration : Sentry.Integrations.ISdkIntegration { } + public void RemoveTransactionProcessor() + where TProcessor : Sentry.Extensibility.ISentryTransactionProcessor { } + public void SetBeforeBreadcrumb(System.Func beforeBreadcrumb) { } + public void SetBeforeBreadcrumb(System.Func beforeBreadcrumb) { } + public void SetBeforeSend(System.Func beforeSend) { } + public void SetBeforeSend(System.Func beforeSend) { } + public void SetBeforeSendTransaction(System.Func beforeSendTransaction) { } + public void SetBeforeSendTransaction(System.Func beforeSendTransaction) { } + public Sentry.SentryOptions UseStackTraceFactory(Sentry.Extensibility.ISentryStackTraceFactory sentryStackTraceFactory) { } + } + public sealed class SentryPackage : Sentry.ISentryJsonSerializable + { + public SentryPackage(string name, string version) { } + public string Name { get; } + public string Version { get; } + public override bool Equals(object? obj) { } + public override int GetHashCode() { } + public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } + public static Sentry.SentryPackage FromJson(System.Text.Json.JsonElement json) { } + } + public sealed class SentryRequest : Sentry.ISentryJsonSerializable + { + public SentryRequest() { } + public string? ApiTarget { get; set; } + public string? Cookies { get; set; } + public object? Data { get; set; } + public System.Collections.Generic.IDictionary Env { get; } + public System.Collections.Generic.IDictionary Headers { get; } + public string? Method { get; set; } + public System.Collections.Generic.IDictionary Other { get; } + public string? QueryString { get; set; } + public string? Url { get; set; } + public Sentry.SentryRequest Clone() { } + public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } + public static Sentry.SentryRequest FromJson(System.Text.Json.JsonElement json) { } + } + public static class SentrySdk + { + public static bool IsEnabled { get; } + public static Sentry.SentryId LastEventId { get; } + public static void AddBreadcrumb(Sentry.Breadcrumb breadcrumb, Sentry.SentryHint? hint = null) { } + public static void AddBreadcrumb(string message, string? category = null, string? type = null, System.Collections.Generic.IDictionary? data = null, Sentry.BreadcrumbLevel level = 0) { } + public static void AddBreadcrumb(Sentry.Infrastructure.ISystemClock? clock, string message, string? category = null, string? type = null, System.Collections.Generic.IDictionary? data = null, Sentry.BreadcrumbLevel level = 0) { } + public static void BindClient(Sentry.ISentryClient client) { } + public static void BindException(System.Exception exception, Sentry.ISpan span) { } + public static Sentry.SentryId CaptureCheckIn(string monitorSlug, Sentry.CheckInStatus status, Sentry.SentryId? sentryId = default, System.TimeSpan? duration = default, Sentry.Scope? scope = null, System.Action? configureMonitorOptions = null) { } + public static bool CaptureEnvelope(Sentry.Protocol.Envelopes.Envelope envelope) { } + public static Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, System.Action configureScope) { } + public static Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, Sentry.Scope? scope = null, Sentry.SentryHint? hint = null) { } + public static Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, Sentry.SentryHint? hint, System.Action configureScope) { } + public static Sentry.SentryId CaptureException(System.Exception exception) { } + public static Sentry.SentryId CaptureException(System.Exception exception, System.Action configureScope) { } + public static Sentry.SentryId CaptureMessage(string message, Sentry.SentryLevel level = 1) { } + public static Sentry.SentryId CaptureMessage(string message, System.Action configureScope, Sentry.SentryLevel level = 1) { } + public static void CaptureSession(Sentry.SessionUpdate sessionUpdate) { } + public static void CaptureTransaction(Sentry.SentryTransaction transaction) { } + public static void CaptureTransaction(Sentry.SentryTransaction transaction, Sentry.Scope? scope, Sentry.SentryHint? hint) { } + public static void CaptureUserFeedback(Sentry.UserFeedback userFeedback) { } + public static void CaptureUserFeedback(Sentry.SentryId eventId, string email, string comments, string? name = null) { } + [System.Obsolete("WARNING: This method deliberately causes a crash, and should not be used in a rea" + + "l application.")] + public static void CauseCrash(Sentry.CrashType crashType) { } + public static void Close() { } + public static void ConfigureScope(System.Action configureScope) { } + public static System.Threading.Tasks.Task ConfigureScopeAsync(System.Func configureScope) { } + public static Sentry.TransactionContext ContinueTrace(Sentry.SentryTraceHeader? traceHeader, Sentry.BaggageHeader? baggageHeader, string? name = null, string? operation = null) { } + public static Sentry.TransactionContext ContinueTrace(string? traceHeader, string? baggageHeader, string? name = null, string? operation = null) { } + public static void EndSession(Sentry.SessionEndStatus status = 0) { } + public static void Flush() { } + public static void Flush(System.TimeSpan timeout) { } + public static System.Threading.Tasks.Task FlushAsync() { } + public static System.Threading.Tasks.Task FlushAsync(System.TimeSpan timeout) { } + public static Sentry.BaggageHeader? GetBaggage() { } + public static Sentry.ISpan? GetSpan() { } + public static Sentry.SentryTraceHeader? GetTraceHeader() { } + public static System.IDisposable Init() { } + public static System.IDisposable Init(Sentry.SentryOptions options) { } + public static System.IDisposable Init(System.Action? configureOptions) { } + public static System.IDisposable Init(string? dsn) { } + public static void PauseSession() { } + public static System.IDisposable PushScope() { } + public static System.IDisposable PushScope(TState state) { } + public static void ResumeSession() { } + public static void StartSession() { } + public static Sentry.ITransactionTracer StartTransaction(Sentry.ITransactionContext context) { } + public static Sentry.ITransactionTracer StartTransaction(Sentry.ITransactionContext context, System.Collections.Generic.IReadOnlyDictionary customSamplingContext) { } + public static Sentry.ITransactionTracer StartTransaction(string name, string operation) { } + public static Sentry.ITransactionTracer StartTransaction(string name, string operation, Sentry.SentryTraceHeader traceHeader) { } + public static Sentry.ITransactionTracer StartTransaction(string name, string operation, string? description) { } + } + public class SentrySession : Sentry.ISentrySession + { + public SentrySession(string? distinctId, string release, string? environment) { } + public string? DistinctId { get; } + public string? Environment { get; } + public int ErrorCount { get; } + public Sentry.SentryId Id { get; } + public string? IpAddress { get; } + public string Release { get; } + public System.DateTimeOffset StartTimestamp { get; } + public string? UserAgent { get; } + public void ReportError() { } + } + public class SentrySpan : Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISentryJsonSerializable, Sentry.ISpanData, Sentry.Protocol.ITraceContext + { + public SentrySpan(Sentry.ISpan tracer) { } + public SentrySpan(Sentry.SpanId? parentSpanId, string operation) { } + public string? Description { get; set; } + public System.DateTimeOffset? EndTimestamp { get; } + public System.Collections.Generic.IReadOnlyDictionary Extra { get; } + public bool IsFinished { get; } + public bool? IsSampled { get; } + public System.Collections.Generic.IReadOnlyDictionary Measurements { get; } + public string Operation { get; set; } + public string? Origin { get; } + public Sentry.SpanId? ParentSpanId { get; } + public Sentry.SpanId SpanId { get; } + public System.DateTimeOffset StartTimestamp { get; } + public Sentry.SpanStatus? Status { get; set; } + public System.Collections.Generic.IReadOnlyDictionary Tags { get; } + public Sentry.SentryId TraceId { get; } + public Sentry.SentryTraceHeader GetTraceHeader() { } + public void SetExtra(string key, object? value) { } + public void SetMeasurement(string name, Sentry.Protocol.Measurement measurement) { } + public void SetTag(string key, string value) { } + public void UnsetTag(string key) { } + public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } + public static Sentry.SentrySpan FromJson(System.Text.Json.JsonElement json) { } + } + [System.Diagnostics.DebuggerDisplay("{Function}")] + public sealed class SentryStackFrame : Sentry.ISentryJsonSerializable + { + public SentryStackFrame() { } + public string? AbsolutePath { get; set; } + public string? AddressMode { get; set; } + public int? ColumnNumber { get; set; } + public string? ContextLine { get; set; } + public string? FileName { get; set; } + public System.Collections.Generic.IList FramesOmitted { get; } + public string? Function { get; set; } + public long? FunctionId { get; set; } + public long? ImageAddress { get; set; } + public bool? InApp { get; set; } + public long? InstructionAddress { get; set; } + public int? LineNumber { get; set; } + public string? Module { get; set; } + public string? Package { get; set; } + public string? Platform { get; set; } + public System.Collections.Generic.IList PostContext { get; } + public System.Collections.Generic.IList PreContext { get; } + public long? SymbolAddress { get; set; } + public System.Collections.Generic.IDictionary Vars { get; } + public void ConfigureAppFrame(Sentry.SentryOptions options) { } + public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } + public static Sentry.SentryStackFrame FromJson(System.Text.Json.JsonElement json) { } + } + public class SentryStackTrace : Sentry.ISentryJsonSerializable + { + public SentryStackTrace() { } + public Sentry.InstructionAddressAdjustment? AddressAdjustment { get; set; } + public System.Collections.Generic.IList Frames { get; set; } + public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } + public static Sentry.SentryStackTrace FromJson(System.Text.Json.JsonElement json) { } + } + public sealed class SentryThread : Sentry.ISentryJsonSerializable + { + public SentryThread() { } + public bool? Crashed { get; set; } + public bool? Current { get; set; } + public int? Id { get; set; } + public string? Name { get; set; } + public Sentry.SentryStackTrace? Stacktrace { get; set; } + public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } + public static Sentry.SentryThread FromJson(System.Text.Json.JsonElement json) { } + } + public class SentryTraceHeader + { + public SentryTraceHeader(Sentry.SentryId traceId, Sentry.SpanId spanSpanId, bool? isSampled) { } + public bool? IsSampled { get; } + public Sentry.SpanId SpanId { get; } + public Sentry.SentryId TraceId { get; } + public override string ToString() { } + public static Sentry.SentryTraceHeader Parse(string value) { } + } + public class SentryTransaction : Sentry.IEventLike, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISentryJsonSerializable, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.Protocol.ITraceContext + { + public SentryTransaction(Sentry.ITransactionTracer tracer) { } + public SentryTransaction(string name, string operation) { } + public SentryTransaction(string name, string operation, Sentry.TransactionNameSource nameSource) { } + public System.Collections.Generic.IReadOnlyCollection Breadcrumbs { get; } + public Sentry.SentryContexts Contexts { get; set; } + public string? Description { get; set; } + public string? Distribution { get; set; } + public System.DateTimeOffset? EndTimestamp { get; } + public string? Environment { get; set; } + public Sentry.SentryId EventId { get; } + public System.Collections.Generic.IReadOnlyDictionary Extra { get; } + public System.Collections.Generic.IReadOnlyList Fingerprint { get; set; } + public bool IsFinished { get; } + public bool? IsParentSampled { get; set; } + public bool? IsSampled { get; } + public Sentry.SentryLevel? Level { get; set; } + public System.Collections.Generic.IReadOnlyDictionary Measurements { get; } + public string Name { get; } + public Sentry.TransactionNameSource NameSource { get; } + public string Operation { get; } + public string? Origin { get; } + public Sentry.SpanId? ParentSpanId { get; } + public string? Platform { get; set; } + public string? Release { get; set; } + public Sentry.SentryRequest Request { get; set; } + public double? SampleRate { get; } + public Sentry.SdkVersion Sdk { get; } + public Sentry.SpanId SpanId { get; } + public System.Collections.Generic.IReadOnlyCollection Spans { get; } + public System.DateTimeOffset StartTimestamp { get; } + public Sentry.SpanStatus? Status { get; } + public System.Collections.Generic.IReadOnlyDictionary Tags { get; } + public Sentry.SentryId TraceId { get; } + public Sentry.SentryUser User { get; set; } + public void AddBreadcrumb(Sentry.Breadcrumb breadcrumb) { } + public Sentry.SentryTraceHeader GetTraceHeader() { } + public void SetExtra(string key, object? value) { } + public void SetMeasurement(string name, Sentry.Protocol.Measurement measurement) { } + public void SetTag(string key, string value) { } + public void UnsetTag(string key) { } + public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } + public static Sentry.SentryTransaction FromJson(System.Text.Json.JsonElement json) { } + } + public sealed class SentryUser : Sentry.ISentryJsonSerializable + { + public SentryUser() { } + public string? Email { get; set; } + public string? Id { get; set; } + public string? IpAddress { get; set; } + public System.Collections.Generic.IDictionary Other { get; set; } + public string? Username { get; set; } + public Sentry.SentryUser Clone() { } + public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? _) { } + public static Sentry.SentryUser FromJson(System.Text.Json.JsonElement json) { } + } + public enum SessionEndStatus + { + Exited = 0, + Crashed = 1, + Abnormal = 2, + } + public class SessionUpdate : Sentry.ISentryJsonSerializable, Sentry.ISentrySession + { + public SessionUpdate(Sentry.SessionUpdate sessionUpdate, bool isInitial) { } + public SessionUpdate(Sentry.SessionUpdate sessionUpdate, bool isInitial, Sentry.SessionEndStatus? endStatus) { } + public SessionUpdate(Sentry.ISentrySession session, bool isInitial, System.DateTimeOffset timestamp, int sequenceNumber, Sentry.SessionEndStatus? endStatus) { } + public SessionUpdate(Sentry.SentryId id, string? distinctId, System.DateTimeOffset startTimestamp, string release, string? environment, string? ipAddress, string? userAgent, int errorCount, bool isInitial, System.DateTimeOffset timestamp, int sequenceNumber, Sentry.SessionEndStatus? endStatus) { } + public string? DistinctId { get; } + public System.TimeSpan Duration { get; } + public Sentry.SessionEndStatus? EndStatus { get; } + public string? Environment { get; } + public int ErrorCount { get; } + public Sentry.SentryId Id { get; } + public string? IpAddress { get; } + public bool IsInitial { get; } + public string Release { get; } + public int SequenceNumber { get; } + public System.DateTimeOffset StartTimestamp { get; } + public System.DateTimeOffset Timestamp { get; } + public string? UserAgent { get; } + public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } + public static Sentry.SessionUpdate FromJson(System.Text.Json.JsonElement json) { } + } + public class SpanContext : Sentry.Protocol.ITraceContext + { + public SpanContext(string operation, Sentry.SpanId? spanId = default, Sentry.SpanId? parentSpanId = default, Sentry.SentryId? traceId = default, string? description = null, Sentry.SpanStatus? status = default, bool? isSampled = default) { } + public string? Description { get; } + public Sentry.Instrumenter Instrumenter { get; } + public bool? IsSampled { get; } + public string Operation { get; set; } + public string? Origin { get; } + public Sentry.SpanId? ParentSpanId { get; } + public Sentry.SpanId SpanId { get; } + public Sentry.SpanStatus? Status { get; } + public Sentry.SentryId TraceId { get; } + } + public static class SpanDataExtensions + { + public static void SetMeasurement(this Sentry.ISpanData spanData, string name, double value, Sentry.MeasurementUnit unit = default) { } + public static void SetMeasurement(this Sentry.ISpanData spanData, string name, int value, Sentry.MeasurementUnit unit = default) { } + public static void SetMeasurement(this Sentry.ISpanData spanData, string name, long value, Sentry.MeasurementUnit unit = default) { } + [System.CLSCompliant(false)] + public static void SetMeasurement(this Sentry.ISpanData spanData, string name, ulong value, Sentry.MeasurementUnit unit = default) { } + } + public static class SpanExtensions + { + public static Sentry.ITransactionTracer GetTransaction(this Sentry.ISpan span) { } + public static Sentry.ISpan StartChild(this Sentry.ISpan span, string operation, string? description) { } + } + public readonly struct SpanId : Sentry.ISentryJsonSerializable, System.IEquatable + { + public static readonly Sentry.SpanId Empty; + public SpanId(long value) { } + public SpanId(string value) { } + public bool Equals(Sentry.SpanId other) { } + public override bool Equals(object? obj) { } + public override int GetHashCode() { } + public override string ToString() { } + public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? _) { } + public static Sentry.SpanId Create() { } + public static Sentry.SpanId FromJson(System.Text.Json.JsonElement json) { } + public static Sentry.SpanId Parse(string value) { } + public static string op_Implicit(Sentry.SpanId id) { } + public static bool operator !=(Sentry.SpanId left, Sentry.SpanId right) { } + public static bool operator ==(Sentry.SpanId left, Sentry.SpanId right) { } + } + public enum SpanStatus + { + Ok = 0, + DeadlineExceeded = 1, + Unauthenticated = 2, + PermissionDenied = 3, + NotFound = 4, + ResourceExhausted = 5, + InvalidArgument = 6, + Unimplemented = 7, + Unavailable = 8, + InternalError = 9, + UnknownError = 10, + Cancelled = 11, + AlreadyExists = 12, + FailedPrecondition = 13, + Aborted = 14, + OutOfRange = 15, + DataLoss = 16, + } + public class SpanTracer : Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpan, Sentry.ISpanData, Sentry.Protocol.ITraceContext + { + public SpanTracer(Sentry.IHub hub, Sentry.TransactionTracer transaction, Sentry.SpanId? parentSpanId, Sentry.SentryId traceId, string operation) { } + public string? Description { get; set; } + public System.DateTimeOffset? EndTimestamp { get; } + public System.Collections.Generic.IReadOnlyDictionary Extra { get; } + public bool IsFinished { get; } + public bool? IsSampled { get; } + public System.Collections.Generic.IReadOnlyDictionary Measurements { get; } + public string Operation { get; set; } + public string? Origin { get; } + public Sentry.SpanId? ParentSpanId { get; } + public Sentry.SpanId SpanId { get; } + public System.DateTimeOffset StartTimestamp { get; } + public Sentry.SpanStatus? Status { get; set; } + public System.Collections.Generic.IReadOnlyDictionary Tags { get; } + public Sentry.SentryId TraceId { get; } + public void Finish() { } + public void Finish(Sentry.SpanStatus status) { } + public void Finish(System.Exception exception) { } + public void Finish(System.Exception exception, Sentry.SpanStatus status) { } + public Sentry.SentryTraceHeader GetTraceHeader() { } + public void SetExtra(string key, object? value) { } + public void SetMeasurement(string name, Sentry.Protocol.Measurement measurement) { } + public void SetTag(string key, string value) { } + public Sentry.ISpan StartChild(string operation) { } + public void UnsetTag(string key) { } + } + public enum StackTraceMode + { + Original = 0, + Enhanced = 1, + } + public enum StartupTimeDetectionMode + { + None = 0, + Fast = 1, + Best = 2, + } + public class StreamAttachmentContent : Sentry.IAttachmentContent + { + public StreamAttachmentContent(System.IO.Stream stream) { } + public System.IO.Stream GetStream() { } + } + [System.ComponentModel.TypeConverter(typeof(Sentry.StringOrRegexTypeConverter))] + public class StringOrRegex + { + public StringOrRegex(string stringOrRegex) { } + public StringOrRegex(System.Text.RegularExpressions.Regex regex) { } + public override bool Equals(object? obj) { } + public override int GetHashCode() { } + public override string ToString() { } + public static Sentry.StringOrRegex op_Implicit(string stringOrRegex) { } + public static Sentry.StringOrRegex op_Implicit(System.Text.RegularExpressions.Regex regex) { } + } + public class TransactionContext : Sentry.SpanContext, Sentry.ITransactionContext, Sentry.Protocol.ITraceContext + { + public TransactionContext(string name, string operation, Sentry.SpanId? spanId = default, Sentry.SpanId? parentSpanId = default, Sentry.SentryId? traceId = default, string? description = "", Sentry.SpanStatus? status = default, bool? isSampled = default, bool? isParentSampled = default, Sentry.TransactionNameSource nameSource = 0) { } + public bool? IsParentSampled { get; } + public string Name { get; set; } + public Sentry.TransactionNameSource NameSource { get; set; } + } + public enum TransactionNameSource + { + Custom = 0, + Url = 1, + Route = 2, + View = 3, + Component = 4, + Task = 5, + } + public class TransactionSamplingContext + { + public TransactionSamplingContext(Sentry.ITransactionContext transactionContext, System.Collections.Generic.IReadOnlyDictionary customSamplingContext) { } + public System.Collections.Generic.IReadOnlyDictionary CustomSamplingContext { get; } + public Sentry.ITransactionContext TransactionContext { get; } + } + public class TransactionTracer : Sentry.IEventLike, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpan, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.ITransactionTracer, Sentry.Protocol.ITraceContext + { + public TransactionTracer(Sentry.IHub hub, Sentry.ITransactionContext context) { } + public System.Collections.Generic.IReadOnlyCollection Breadcrumbs { get; } + public Sentry.SentryContexts Contexts { get; set; } + public string? Description { get; set; } + public string? Distribution { get; set; } + public System.DateTimeOffset? EndTimestamp { get; } + public string? Environment { get; set; } + public System.Collections.Generic.IReadOnlyDictionary Extra { get; } + public System.Collections.Generic.IReadOnlyList Fingerprint { get; set; } + public bool IsFinished { get; } + public bool? IsParentSampled { get; set; } + public bool? IsSampled { get; } + public Sentry.SentryLevel? Level { get; set; } + public System.Collections.Generic.IReadOnlyDictionary Measurements { get; } + public string Name { get; set; } + public Sentry.TransactionNameSource NameSource { get; set; } + public string Operation { get; set; } + public string? Origin { get; } + public Sentry.SpanId? ParentSpanId { get; } + public string? Platform { get; set; } + public string? Release { get; set; } + public Sentry.SentryRequest Request { get; set; } + public double? SampleRate { get; } + public Sentry.SdkVersion Sdk { get; } + public Sentry.SpanId SpanId { get; } + public System.Collections.Generic.IReadOnlyCollection Spans { get; } + public System.DateTimeOffset StartTimestamp { get; } + public Sentry.SpanStatus? Status { get; set; } + public System.Collections.Generic.IReadOnlyDictionary Tags { get; } + public Sentry.SentryId TraceId { get; } + public Sentry.SentryUser User { get; set; } + public void AddBreadcrumb(Sentry.Breadcrumb breadcrumb) { } + public void Finish() { } + public void Finish(Sentry.SpanStatus status) { } + public void Finish(System.Exception exception) { } + public void Finish(System.Exception exception, Sentry.SpanStatus status) { } + public Sentry.ISpan? GetLastActiveSpan() { } + public Sentry.SentryTraceHeader GetTraceHeader() { } + public void SetExtra(string key, object? value) { } + public void SetMeasurement(string name, Sentry.Protocol.Measurement measurement) { } + public void SetTag(string key, string value) { } + public Sentry.ISpan StartChild(string operation) { } + public void UnsetTag(string key) { } + } + public sealed class UserFeedback : Sentry.ISentryJsonSerializable + { + public UserFeedback(Sentry.SentryId eventId, string? name, string? email, string? comments) { } + public string? Comments { get; } + public string? Email { get; } + public Sentry.SentryId EventId { get; } + public string? Name { get; } + public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } + public static Sentry.UserFeedback FromJson(System.Text.Json.JsonElement json) { } + } + public sealed class ViewHierarchy : Sentry.ISentryJsonSerializable + { + public ViewHierarchy(string renderingSystem) { } + public string RenderingSystem { get; set; } + public System.Collections.Generic.List Windows { get; } + public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } + } + public class ViewHierarchyAttachment : Sentry.SentryAttachment + { + public ViewHierarchyAttachment(Sentry.IAttachmentContent content) { } + } + public abstract class ViewHierarchyNode : Sentry.ISentryJsonSerializable + { + protected ViewHierarchyNode(string type) { } + public System.Collections.Generic.List Children { get; set; } + public string Type { get; set; } + protected abstract void WriteAdditionalProperties(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger); + public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } + } +} +namespace Sentry.Ben.BlockingDetector +{ + public class SuppressBlockingDetection : System.IDisposable + { + public SuppressBlockingDetection() { } + public void Dispose() { } + } +} +namespace Sentry.Extensibility +{ + public abstract class BaseRequestPayloadExtractor : Sentry.Extensibility.IRequestPayloadExtractor + { + protected BaseRequestPayloadExtractor() { } + protected abstract object? DoExtractPayLoad(Sentry.Extensibility.IHttpRequest request); + public object? ExtractPayload(Sentry.Extensibility.IHttpRequest request) { } + protected abstract bool IsSupported(Sentry.Extensibility.IHttpRequest request); + } + public class DefaultRequestPayloadExtractor : Sentry.Extensibility.BaseRequestPayloadExtractor + { + public DefaultRequestPayloadExtractor() { } + protected override object? DoExtractPayLoad(Sentry.Extensibility.IHttpRequest request) { } + protected override bool IsSupported(Sentry.Extensibility.IHttpRequest request) { } + } + public static class DiagnosticLoggerExtensions + { + public static void LogDebug(this Sentry.Extensibility.IDiagnosticLogger logger, string message) { } + public static void LogDebug(this Sentry.Extensibility.IDiagnosticLogger logger, string message, TArg arg) { } + public static void LogDebug(this Sentry.Extensibility.IDiagnosticLogger logger, string message, TArg arg, TArg2 arg2) { } + public static void LogError(this Sentry.Extensibility.IDiagnosticLogger logger, string message) { } + public static void LogError(this Sentry.Extensibility.IDiagnosticLogger logger, System.Exception exception, string message) { } + public static void LogError(this Sentry.Extensibility.IDiagnosticLogger logger, string message, TArg arg) { } + public static void LogError(this Sentry.Extensibility.IDiagnosticLogger logger, System.Exception exception, string message, TArg arg) { } + public static void LogError(this Sentry.Extensibility.IDiagnosticLogger logger, System.Exception exception, string message, TArg arg, TArg2 arg2) { } + public static void LogError(this Sentry.Extensibility.IDiagnosticLogger logger, System.Exception exception, string message, TArg arg, TArg2 arg2, TArg3 arg3) { } + public static void LogError(this Sentry.Extensibility.IDiagnosticLogger logger, System.Exception exception, string message, TArg arg, TArg2 arg2, TArg3 arg3, TArg4 arg4) { } + public static void LogFatal(this Sentry.Extensibility.IDiagnosticLogger logger, string message) { } + public static void LogFatal(this Sentry.Extensibility.IDiagnosticLogger logger, System.Exception exception, string message) { } + public static void LogInfo(this Sentry.Extensibility.IDiagnosticLogger logger, string message) { } + public static void LogInfo(this Sentry.Extensibility.IDiagnosticLogger logger, string message, TArg arg) { } + public static void LogInfo(this Sentry.Extensibility.IDiagnosticLogger logger, string message, TArg arg, TArg2 arg2) { } + public static void LogInfo(this Sentry.Extensibility.IDiagnosticLogger logger, string message, TArg arg, TArg2 arg2, TArg3 arg3) { } + public static void LogWarning(this Sentry.Extensibility.IDiagnosticLogger logger, string message) { } + public static void LogWarning(this Sentry.Extensibility.IDiagnosticLogger logger, System.Exception exception, string message) { } + public static void LogWarning(this Sentry.Extensibility.IDiagnosticLogger logger, string message, TArg arg) { } + public static void LogWarning(this Sentry.Extensibility.IDiagnosticLogger logger, string message, TArg arg, TArg2 arg2) { } + } + public class DisabledHub : Sentry.IHub, Sentry.ISentryClient, Sentry.ISentryScopeManager, System.IDisposable + { + public static readonly Sentry.Extensibility.DisabledHub Instance; + public bool IsEnabled { get; } + public Sentry.SentryId LastEventId { get; } + public void BindClient(Sentry.ISentryClient client) { } + public void BindException(System.Exception exception, Sentry.ISpan span) { } + public Sentry.SentryId CaptureCheckIn(string monitorSlug, Sentry.CheckInStatus status, Sentry.SentryId? sentryId = default, System.TimeSpan? duration = default, Sentry.Scope? scope = null, System.Action? configureMonitorOptions = null) { } + public bool CaptureEnvelope(Sentry.Protocol.Envelopes.Envelope envelope) { } + public Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, System.Action configureScope) { } + public Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, Sentry.Scope? scope = null, Sentry.SentryHint? hint = null) { } + public Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, Sentry.SentryHint? hint, System.Action configureScope) { } + public void CaptureSession(Sentry.SessionUpdate sessionUpdate) { } + public void CaptureTransaction(Sentry.SentryTransaction transaction) { } + public void CaptureTransaction(Sentry.SentryTransaction transaction, Sentry.Scope? scope, Sentry.SentryHint? hint) { } + public void CaptureUserFeedback(Sentry.UserFeedback userFeedback) { } + public void ConfigureScope(System.Action configureScope) { } + public System.Threading.Tasks.Task ConfigureScopeAsync(System.Func configureScope) { } + public Sentry.TransactionContext ContinueTrace(Sentry.SentryTraceHeader? traceHeader, Sentry.BaggageHeader? baggageHeader, string? name = null, string? operation = null) { } + public Sentry.TransactionContext ContinueTrace(string? traceHeader, string? baggageHeader, string? name = null, string? operation = null) { } + public void Dispose() { } + public void EndSession(Sentry.SessionEndStatus status = 0) { } + public System.Threading.Tasks.Task FlushAsync(System.TimeSpan timeout) { } + public Sentry.BaggageHeader? GetBaggage() { } + public Sentry.ISpan? GetSpan() { } + public Sentry.SentryTraceHeader? GetTraceHeader() { } + public void PauseSession() { } + public System.IDisposable PushScope() { } + public System.IDisposable PushScope(TState state) { } + public void ResumeSession() { } + public void StartSession() { } + public Sentry.ITransactionTracer StartTransaction(Sentry.ITransactionContext context, System.Collections.Generic.IReadOnlyDictionary customSamplingContext) { } + } + public class FormRequestPayloadExtractor : Sentry.Extensibility.BaseRequestPayloadExtractor + { + public FormRequestPayloadExtractor() { } + protected override object? DoExtractPayLoad(Sentry.Extensibility.IHttpRequest request) { } + protected override bool IsSupported(Sentry.Extensibility.IHttpRequest request) { } + } + public sealed class HubAdapter : Sentry.IHub, Sentry.ISentryClient, Sentry.ISentryScopeManager + { + public static readonly Sentry.Extensibility.HubAdapter Instance; + public bool IsEnabled { get; } + public Sentry.SentryId LastEventId { get; } + public void AddBreadcrumb(string message, string? category = null, string? type = null, System.Collections.Generic.IDictionary? data = null, Sentry.BreadcrumbLevel level = 0) { } + public void AddBreadcrumb(Sentry.Infrastructure.ISystemClock clock, string message, string? category = null, string? type = null, System.Collections.Generic.IDictionary? data = null, Sentry.BreadcrumbLevel level = 0) { } + public void BindClient(Sentry.ISentryClient client) { } + public void BindException(System.Exception exception, Sentry.ISpan span) { } + public Sentry.SentryId CaptureCheckIn(string monitorSlug, Sentry.CheckInStatus status, Sentry.SentryId? sentryId = default, System.TimeSpan? duration = default, Sentry.Scope? scope = null, System.Action? monitorOptions = null) { } + public bool CaptureEnvelope(Sentry.Protocol.Envelopes.Envelope envelope) { } + public Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt) { } + public Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, Sentry.Scope? scope) { } + public Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, System.Action configureScope) { } + public Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, Sentry.Scope? scope, Sentry.SentryHint? hint = null) { } + public Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, Sentry.SentryHint? hint, System.Action configureScope) { } + public Sentry.SentryId CaptureException(System.Exception exception) { } + public void CaptureSession(Sentry.SessionUpdate sessionUpdate) { } + public void CaptureTransaction(Sentry.SentryTransaction transaction) { } + public void CaptureTransaction(Sentry.SentryTransaction transaction, Sentry.Scope? scope, Sentry.SentryHint? hint) { } + public void CaptureUserFeedback(Sentry.UserFeedback sentryUserFeedback) { } + public void ConfigureScope(System.Action configureScope) { } + public System.Threading.Tasks.Task ConfigureScopeAsync(System.Func configureScope) { } + public Sentry.TransactionContext ContinueTrace(Sentry.SentryTraceHeader? traceHeader, Sentry.BaggageHeader? baggageHeader, string? name = null, string? operation = null) { } + public Sentry.TransactionContext ContinueTrace(string? traceHeader, string? baggageHeader, string? name = null, string? operation = null) { } + public void EndSession(Sentry.SessionEndStatus status = 0) { } + public System.Threading.Tasks.Task FlushAsync(System.TimeSpan timeout) { } + public Sentry.BaggageHeader? GetBaggage() { } + public Sentry.ISpan? GetSpan() { } + public Sentry.SentryTraceHeader? GetTraceHeader() { } + public void PauseSession() { } + public System.IDisposable PushScope() { } + public System.IDisposable PushScope(TState state) { } + public void ResumeSession() { } + public void StartSession() { } + public Sentry.ITransactionTracer StartTransaction(Sentry.ITransactionContext context, System.Collections.Generic.IReadOnlyDictionary customSamplingContext) { } + } + public interface IBackgroundWorker + { + int QueuedItems { get; } + bool EnqueueEnvelope(Sentry.Protocol.Envelopes.Envelope envelope); + System.Threading.Tasks.Task FlushAsync(System.TimeSpan timeout); + } + public interface IDiagnosticLogger + { + bool IsEnabled(Sentry.SentryLevel level); + void Log(Sentry.SentryLevel logLevel, string message, System.Exception? exception = null, params object?[] args); + } + public interface IExceptionFilter + { + bool Filter(System.Exception ex); + } + public interface IHttpRequest + { + System.IO.Stream? Body { get; } + long? ContentLength { get; } + string? ContentType { get; } + System.Collections.Generic.IEnumerable>>? Form { get; } + } + public interface INetworkStatusListener + { + bool Online { get; } + System.Threading.Tasks.Task WaitForNetworkOnlineAsync(System.Threading.CancellationToken cancellationToken); + } + public interface IRequestPayloadExtractor + { + object? ExtractPayload(Sentry.Extensibility.IHttpRequest request); + } + public interface ISentryEventExceptionProcessor + { + void Process(System.Exception exception, Sentry.SentryEvent sentryEvent); + } + public interface ISentryEventProcessor + { + Sentry.SentryEvent? Process(Sentry.SentryEvent @event); + } + public interface ISentryEventProcessorWithHint : Sentry.Extensibility.ISentryEventProcessor + { + Sentry.SentryEvent? Process(Sentry.SentryEvent @event, Sentry.SentryHint hint); + } + public interface ISentryStackTraceFactory + { + Sentry.SentryStackTrace? Create(System.Exception? exception = null); + } + public interface ISentryTransactionProcessor + { + Sentry.SentryTransaction? Process(Sentry.SentryTransaction transaction); + } + public interface ISentryTransactionProcessorWithHint : Sentry.Extensibility.ISentryTransactionProcessor + { + Sentry.SentryTransaction? Process(Sentry.SentryTransaction transaction, Sentry.SentryHint hint); + } + public interface ITransport + { + System.Threading.Tasks.Task SendEnvelopeAsync(Sentry.Protocol.Envelopes.Envelope envelope, System.Threading.CancellationToken cancellationToken = default); + } + public class RequestBodyExtractionDispatcher : Sentry.Extensibility.IRequestPayloadExtractor + { + public RequestBodyExtractionDispatcher(System.Collections.Generic.IEnumerable extractors, Sentry.SentryOptions options, System.Func sizeSwitch) { } + public object? ExtractPayload(Sentry.Extensibility.IHttpRequest request) { } + } + public enum RequestSize + { + None = 0, + Small = 1, + Medium = 2, + Always = 3, + } + public abstract class SentryEventExceptionProcessor : Sentry.Extensibility.ISentryEventExceptionProcessor + where TException : System.Exception + { + protected SentryEventExceptionProcessor() { } + public void Process(System.Exception? exception, Sentry.SentryEvent sentryEvent) { } + protected abstract void ProcessException(TException exception, Sentry.SentryEvent sentryEvent); + } + public sealed class SentryStackTraceFactory : Sentry.Extensibility.ISentryStackTraceFactory + { + public SentryStackTraceFactory(Sentry.SentryOptions options) { } + public Sentry.SentryStackTrace? Create(System.Exception? exception = null) { } + } +} +namespace Sentry.Http +{ + public abstract class HttpTransportBase + { + protected HttpTransportBase(Sentry.SentryOptions options, System.Func? getEnvironmentVariable = null, Sentry.Infrastructure.ISystemClock? clock = null) { } + protected virtual System.Net.Http.HttpRequestMessage CreateRequest(Sentry.Protocol.Envelopes.Envelope envelope) { } + protected void HandleResponse(System.Net.Http.HttpResponseMessage response, Sentry.Protocol.Envelopes.Envelope envelope) { } + protected System.Threading.Tasks.Task HandleResponseAsync(System.Net.Http.HttpResponseMessage response, Sentry.Protocol.Envelopes.Envelope envelope, System.Threading.CancellationToken cancellationToken) { } + protected Sentry.Protocol.Envelopes.Envelope ProcessEnvelope(Sentry.Protocol.Envelopes.Envelope envelope) { } + protected System.IO.Stream ReadStreamFromHttpContent(System.Net.Http.HttpContent content) { } + } + public interface ISentryHttpClientFactory + { + System.Net.Http.HttpClient Create(Sentry.SentryOptions options); + } +} +namespace Sentry.Infrastructure +{ + public class ConsoleAndTraceDiagnosticLogger : Sentry.Infrastructure.DiagnosticLogger + { + public ConsoleAndTraceDiagnosticLogger(Sentry.SentryLevel minimalLevel) { } + protected override void LogMessage(string message) { } + } + public class ConsoleDiagnosticLogger : Sentry.Infrastructure.DiagnosticLogger + { + public ConsoleDiagnosticLogger(Sentry.SentryLevel minimalLevel) { } + protected override void LogMessage(string message) { } + } + public abstract class DiagnosticLogger : Sentry.Extensibility.IDiagnosticLogger + { + protected DiagnosticLogger(Sentry.SentryLevel minimalLevel) { } + public bool IsEnabled(Sentry.SentryLevel level) { } + public void Log(Sentry.SentryLevel logLevel, string message, System.Exception? exception = null, params object?[] args) { } + protected abstract void LogMessage(string message); + } + public class FileDiagnosticLogger : Sentry.Infrastructure.DiagnosticLogger + { + public FileDiagnosticLogger(string path, bool alsoWriteToConsole = false) { } + public FileDiagnosticLogger(string path, Sentry.SentryLevel minimalLevel, bool alsoWriteToConsole = false) { } + protected override void LogMessage(string message) { } + } + public interface ISystemClock + { + System.DateTimeOffset GetUtcNow(); + } + public sealed class SystemClock : Sentry.Infrastructure.ISystemClock + { + public static readonly Sentry.Infrastructure.SystemClock Clock; + public System.DateTimeOffset GetUtcNow() { } + } + public class TraceDiagnosticLogger : Sentry.Infrastructure.DiagnosticLogger + { + public TraceDiagnosticLogger(Sentry.SentryLevel minimalLevel) { } + protected override void LogMessage(string message) { } + } +} +namespace Sentry.Integrations +{ + public interface ISdkIntegration + { + void Register(Sentry.IHub hub, Sentry.SentryOptions options); + } +} +namespace Sentry.PlatformAbstractions +{ + public static class FrameworkInfo + { + public static System.Collections.Generic.IReadOnlyDictionary NetFxReleaseVersionMap { get; } + public static System.Collections.Generic.IEnumerable GetInstallations() { } + public static Sentry.PlatformAbstractions.FrameworkInstallation? GetLatest(int clr) { } + } + public class FrameworkInstallation + { + public FrameworkInstallation() { } + public Sentry.PlatformAbstractions.FrameworkProfile? Profile { get; set; } + public int? Release { get; set; } + public int? ServicePack { get; set; } + public string? ShortName { get; set; } + public System.Version? Version { get; set; } + public override string ToString() { } + } + public enum FrameworkProfile + { + Client = 0, + Full = 1, + } + public class SentryRuntime : System.IEquatable + { + public SentryRuntime(string? name = null, string? version = null, string? raw = null, string? identifier = null) { } + public string? Identifier { get; } + public string? Name { get; } + public string? Raw { get; } + public string? Version { get; } + public static Sentry.PlatformAbstractions.SentryRuntime Current { get; } + public bool Equals(Sentry.PlatformAbstractions.SentryRuntime? other) { } + public override bool Equals(object? obj) { } + public override int GetHashCode() { } + public override string? ToString() { } + } + public static class SentryRuntimeExtensions + { + public static bool IsMono(this Sentry.PlatformAbstractions.SentryRuntime runtime) { } + public static bool IsNetCore(this Sentry.PlatformAbstractions.SentryRuntime runtime) { } + public static bool IsNetFx(this Sentry.PlatformAbstractions.SentryRuntime runtime) { } + } +} +namespace Sentry.Protocol +{ + public sealed class App : Sentry.ISentryJsonSerializable + { + public const string Type = "app"; + public App() { } + public string? Build { get; set; } + public string? BuildType { get; set; } + public string? Hash { get; set; } + public string? Identifier { get; set; } + public bool? InForeground { get; set; } + public string? Name { get; set; } + public System.DateTimeOffset? StartTime { get; set; } + public string? Version { get; set; } + public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? _) { } + public static Sentry.Protocol.App FromJson(System.Text.Json.JsonElement json) { } + } + public sealed class Browser : Sentry.ISentryJsonSerializable + { + public const string Type = "browser"; + public Browser() { } + public string? Name { get; set; } + public string? Version { get; set; } + public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? _) { } + public static Sentry.Protocol.Browser FromJson(System.Text.Json.JsonElement json) { } + } + public sealed class DebugImage : Sentry.ISentryJsonSerializable + { + public DebugImage() { } + public string? CodeFile { get; set; } + public string? CodeId { get; set; } + public string? DebugChecksum { get; set; } + public string? DebugFile { get; set; } + public string? DebugId { get; set; } + public long? ImageAddress { get; set; } + public long? ImageSize { get; set; } + public string? Type { get; set; } + public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } + public static Sentry.Protocol.DebugImage FromJson(System.Text.Json.JsonElement json) { } + } + public sealed class Device : Sentry.ISentryJsonSerializable + { + public const string Type = "device"; + public Device() { } + public string? Architecture { get; set; } + public float? BatteryLevel { get; set; } + public string? BatteryStatus { get; set; } + public System.DateTimeOffset? BootTime { get; set; } + public string? Brand { get; set; } + public string? CpuDescription { get; set; } + public string? DeviceType { get; set; } + public string? DeviceUniqueIdentifier { get; set; } + public long? ExternalFreeStorage { get; set; } + public long? ExternalStorageSize { get; set; } + public string? Family { get; set; } + public long? FreeMemory { get; set; } + public long? FreeStorage { get; set; } + public bool? IsCharging { get; set; } + public bool? IsOnline { get; set; } + public bool? LowMemory { get; set; } + public string? Manufacturer { get; set; } + public long? MemorySize { get; set; } + public string? Model { get; set; } + public string? ModelId { get; set; } + public string? Name { get; set; } + public Sentry.Protocol.DeviceOrientation? Orientation { get; set; } + public int? ProcessorCount { get; set; } + public float? ProcessorFrequency { get; set; } + public float? ScreenDensity { get; set; } + public int? ScreenDpi { get; set; } + public string? ScreenResolution { get; set; } + public bool? Simulator { get; set; } + public long? StorageSize { get; set; } + public bool? SupportsAccelerometer { get; set; } + public bool? SupportsAudio { get; set; } + public bool? SupportsGyroscope { get; set; } + public bool? SupportsLocationService { get; set; } + public bool? SupportsVibration { get; set; } + public System.TimeZoneInfo? Timezone { get; set; } + public long? UsableMemory { get; set; } + public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? _) { } + public static Sentry.Protocol.Device FromJson(System.Text.Json.JsonElement json) { } + } + public enum DeviceOrientation + { + [System.Runtime.Serialization.EnumMember(Value="portrait")] + Portrait = 0, + [System.Runtime.Serialization.EnumMember(Value="landscape")] + Landscape = 1, + } + public sealed class Gpu : Sentry.ISentryJsonSerializable + { + public const string Type = "gpu"; + public Gpu() { } + public string? ApiType { get; set; } + public string? GraphicsShaderLevel { get; set; } + public int? Id { get; set; } + public int? MaxTextureSize { get; set; } + public int? MemorySize { get; set; } + public bool? MultiThreadedRendering { get; set; } + public string? Name { get; set; } + public string? NpotSupport { get; set; } + public bool? SupportsComputeShaders { get; set; } + public bool? SupportsDrawCallInstancing { get; set; } + public bool? SupportsGeometryShaders { get; set; } + public bool? SupportsRayTracing { get; set; } + public string? VendorId { get; set; } + public string? VendorName { get; set; } + public string? Version { get; set; } + public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? _) { } + public static Sentry.Protocol.Gpu FromJson(System.Text.Json.JsonElement json) { } + } + public interface ITraceContext + { + string? Description { get; } + bool? IsSampled { get; } + string Operation { get; } + string? Origin { get; } + Sentry.SpanId? ParentSpanId { get; } + Sentry.SpanId SpanId { get; } + Sentry.SpanStatus? Status { get; } + Sentry.SentryId TraceId { get; } + } + public sealed class Measurement : Sentry.ISentryJsonSerializable + { + public Sentry.MeasurementUnit Unit { get; } + public object Value { get; } + public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } + public static Sentry.Protocol.Measurement FromJson(System.Text.Json.JsonElement json) { } + } + public sealed class Mechanism : Sentry.ISentryJsonSerializable + { + public static readonly string DescriptionKey; + public static readonly string HandledKey; + public static readonly string MechanismKey; + public Mechanism() { } + public System.Collections.Generic.IDictionary Data { get; } + public string? Description { get; set; } + public int? ExceptionId { get; set; } + public bool? Handled { get; set; } + public string? HelpLink { get; set; } + public bool IsExceptionGroup { get; set; } + public System.Collections.Generic.IDictionary Meta { get; } + public int? ParentId { get; set; } + public string? Source { get; set; } + public bool Synthetic { get; set; } + public string Type { get; set; } + public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } + public static Sentry.Protocol.Mechanism FromJson(System.Text.Json.JsonElement json) { } + } + public sealed class OperatingSystem : Sentry.ISentryJsonSerializable + { + public const string Type = "os"; + public OperatingSystem() { } + public string? Build { get; set; } + public string? KernelVersion { get; set; } + public string? Name { get; set; } + public string? RawDescription { get; set; } + public bool? Rooted { get; set; } + public string? Version { get; set; } + public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? _) { } + public static Sentry.Protocol.OperatingSystem FromJson(System.Text.Json.JsonElement json) { } + } + public sealed class Response : Sentry.ISentryJsonSerializable + { + public const string Type = "response"; + public Response() { } + public long? BodySize { get; set; } + public string? Cookies { get; set; } + public object? Data { get; set; } + public System.Collections.Generic.IDictionary Headers { get; } + public short? StatusCode { get; set; } + public Sentry.Protocol.Response Clone() { } + public void UpdateFrom(Sentry.Protocol.Response source) { } + public void UpdateFrom(object source) { } + public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } + public static Sentry.Protocol.Response FromJson(System.Text.Json.JsonElement json) { } + } + public sealed class Runtime : Sentry.ISentryJsonSerializable + { + public const string Type = "runtime"; + public Runtime() { } + public string? Build { get; set; } + public string? Identifier { get; set; } + public string? Name { get; set; } + public string? RawDescription { get; set; } + public string? Version { get; set; } + public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? _) { } + public static Sentry.Protocol.Runtime FromJson(System.Text.Json.JsonElement json) { } + } + public sealed class SentryException : Sentry.ISentryJsonSerializable + { + public SentryException() { } + public Sentry.Protocol.Mechanism? Mechanism { get; set; } + public string? Module { get; set; } + public Sentry.SentryStackTrace? Stacktrace { get; set; } + public int ThreadId { get; set; } + public string? Type { get; set; } + public string? Value { get; set; } + public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } + public static Sentry.Protocol.SentryException FromJson(System.Text.Json.JsonElement json) { } + } + public class Trace : Sentry.ISentryJsonSerializable, Sentry.Protocol.ITraceContext + { + public const string Type = "trace"; + public Trace() { } + public string? Description { get; set; } + public bool? IsSampled { get; } + public string Operation { get; set; } + public string? Origin { get; } + public Sentry.SpanId? ParentSpanId { get; set; } + public Sentry.SpanId SpanId { get; set; } + public Sentry.SpanStatus? Status { get; set; } + public Sentry.SentryId TraceId { get; set; } + public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } + public static Sentry.Protocol.Trace FromJson(System.Text.Json.JsonElement json) { } + } +} +namespace Sentry.Protocol.Envelopes +{ + public sealed class Envelope : Sentry.Protocol.Envelopes.ISerializable, System.IDisposable + { + public Envelope(System.Collections.Generic.IReadOnlyDictionary header, System.Collections.Generic.IReadOnlyList items) { } + public System.Collections.Generic.IReadOnlyDictionary Header { get; } + public System.Collections.Generic.IReadOnlyList Items { get; } + public void Dispose() { } + public void Serialize(System.IO.Stream stream, Sentry.Extensibility.IDiagnosticLogger? logger) { } + public System.Threading.Tasks.Task SerializeAsync(System.IO.Stream stream, Sentry.Extensibility.IDiagnosticLogger? logger, System.Threading.CancellationToken cancellationToken = default) { } + public Sentry.SentryId? TryGetEventId() { } + public static System.Threading.Tasks.Task DeserializeAsync(System.IO.Stream stream, System.Threading.CancellationToken cancellationToken = default) { } + public static Sentry.Protocol.Envelopes.Envelope FromCheckIn(Sentry.SentryCheckIn checkIn) { } + public static Sentry.Protocol.Envelopes.Envelope FromEvent(Sentry.SentryEvent @event, Sentry.Extensibility.IDiagnosticLogger? logger = null, System.Collections.Generic.IReadOnlyCollection? attachments = null, Sentry.SessionUpdate? sessionUpdate = null) { } + public static Sentry.Protocol.Envelopes.Envelope FromSession(Sentry.SessionUpdate sessionUpdate) { } + public static Sentry.Protocol.Envelopes.Envelope FromTransaction(Sentry.SentryTransaction transaction) { } + public static Sentry.Protocol.Envelopes.Envelope FromUserFeedback(Sentry.UserFeedback sentryUserFeedback) { } + } + public sealed class EnvelopeItem : Sentry.Protocol.Envelopes.ISerializable, System.IDisposable + { + public EnvelopeItem(System.Collections.Generic.IReadOnlyDictionary header, Sentry.Protocol.Envelopes.ISerializable payload) { } + public System.Collections.Generic.IReadOnlyDictionary Header { get; } + public Sentry.Protocol.Envelopes.ISerializable Payload { get; } + public void Dispose() { } + public void Serialize(System.IO.Stream stream, Sentry.Extensibility.IDiagnosticLogger? logger) { } + public System.Threading.Tasks.Task SerializeAsync(System.IO.Stream stream, Sentry.Extensibility.IDiagnosticLogger? logger, System.Threading.CancellationToken cancellationToken = default) { } + public string? TryGetFileName() { } + public long? TryGetLength() { } + public string? TryGetType() { } + public static System.Threading.Tasks.Task DeserializeAsync(System.IO.Stream stream, System.Threading.CancellationToken cancellationToken = default) { } + public static Sentry.Protocol.Envelopes.EnvelopeItem FromAttachment(Sentry.SentryAttachment attachment) { } + public static Sentry.Protocol.Envelopes.EnvelopeItem FromCheckIn(Sentry.SentryCheckIn checkIn) { } + public static Sentry.Protocol.Envelopes.EnvelopeItem FromEvent(Sentry.SentryEvent @event) { } + public static Sentry.Protocol.Envelopes.EnvelopeItem FromSession(Sentry.SessionUpdate sessionUpdate) { } + public static Sentry.Protocol.Envelopes.EnvelopeItem FromTransaction(Sentry.SentryTransaction transaction) { } + public static Sentry.Protocol.Envelopes.EnvelopeItem FromUserFeedback(Sentry.UserFeedback sentryUserFeedback) { } + } + public interface ISerializable + { + void Serialize(System.IO.Stream stream, Sentry.Extensibility.IDiagnosticLogger? logger); + System.Threading.Tasks.Task SerializeAsync(System.IO.Stream stream, Sentry.Extensibility.IDiagnosticLogger? logger, System.Threading.CancellationToken cancellationToken = default); + } +} +namespace Sentry.Reflection +{ + public static class AssemblyExtensions + { + public static Sentry.SdkVersion GetNameAndVersion(this System.Reflection.Assembly asm) { } + } +} +public static class SentryExceptionExtensions +{ + public static void AddSentryContext(this System.Exception ex, string name, System.Collections.Generic.IReadOnlyDictionary data) { } + public static void AddSentryTag(this System.Exception ex, string name, string value) { } + public static void SetSentryMechanism(this System.Exception ex, string type, string? description = null, bool? handled = default) { } +} \ No newline at end of file diff --git a/test/Sentry.Tests/HubTests.cs b/test/Sentry.Tests/HubTests.cs index 79976a9b7b..17206a5c4f 100644 --- a/test/Sentry.Tests/HubTests.cs +++ b/test/Sentry.Tests/HubTests.cs @@ -1553,11 +1553,15 @@ public void CaptureTransaction_Client_Gets_Hint() _fixture.Client.Received().CaptureTransaction(Arg.Any(), Arg.Any(), Arg.Any()); } - [Theory] + [SkippableTheory] [InlineData(false)] [InlineData(true)] public async Task FlushOnDispose_SendsEnvelope(bool cachingEnabled) { +#if __IOS__ + Skip.If(true, "Flaky on iOS"); +#endif + // Arrange using var cacheDirectory = new TempDirectory(); var transport = Substitute.For(); diff --git a/test/Sentry.Tests/Internals/ILSpy/SingleFileAppTests.cs b/test/Sentry.Tests/Internals/ILSpy/SingleFileAppTests.cs index e5bce1ef4a..784c580d59 100644 --- a/test/Sentry.Tests/Internals/ILSpy/SingleFileAppTests.cs +++ b/test/Sentry.Tests/Internals/ILSpy/SingleFileAppTests.cs @@ -30,7 +30,9 @@ static SingleFileAppTests() InValidBundleFile = Path.Combine(testRoot, invalidBundle); } -#if NET8_0 +#if NET9_0 + private static string TargetFramework => "net9.0"; +#elif NET8_0 private static string TargetFramework => "net8.0"; #elif NET7_0 private static string TargetFramework => "net7.0"; @@ -40,7 +42,7 @@ static SingleFileAppTests() private static string TargetFramework => "net5.0"; #else // Adding a new TFM to the project? Include it above - private static string TargetFramework => throw new Exception("Target Framework not yet supported for single file apps"); +#error "Target Framework not yet supported for single file apps" #endif private static string SingleFileAppName => RuntimeInformation.IsOSPlatform(OSPlatform.Windows) diff --git a/test/Sentry.Tests/Internals/MemoryInfoTests.WriteTo.DotNet9_0.verified.txt b/test/Sentry.Tests/Internals/MemoryInfoTests.WriteTo.DotNet9_0.verified.txt new file mode 100644 index 0000000000..05e978d7b7 --- /dev/null +++ b/test/Sentry.Tests/Internals/MemoryInfoTests.WriteTo.DotNet9_0.verified.txt @@ -0,0 +1,19 @@ +{ + allocated_bytes: 1, + fragmented_bytes: 2, + heap_size_bytes: 3, + high_memory_load_threshold_bytes: 4, + total_available_memory_bytes: 5, + memory_load_bytes: 6, + total_committed_bytes: 7, + promoted_bytes: 8, + pinned_objects_count: 9, + pause_time_percentage: 10, + index: 11, + finalization_pending_count: 12, + compacted: true, + concurrent: false, + pause_durations: [ + 1000 + ] +} \ No newline at end of file diff --git a/test/Sentry.Tests/Sentry.Tests.csproj b/test/Sentry.Tests/Sentry.Tests.csproj index 43604e7e8c..5789100c5d 100644 --- a/test/Sentry.Tests/Sentry.Tests.csproj +++ b/test/Sentry.Tests/Sentry.Tests.csproj @@ -1,10 +1,10 @@  - net8.0;net7.0;net6.0;net48 - $(TargetFrameworks);net7.0-android;net8.0-android - $(TargetFrameworks);net7.0-ios;net8.0-ios - $(TargetFrameworks);net7.0-maccatalyst;net8.0-maccatalyst + net9.0;net8.0;net6.0;net48 + $(TargetFrameworks);net8.0-android;net9.0-android + $(TargetFrameworks);net8.0-ios;net9.0-ios + $(TargetFrameworks);net8.0-maccatalyst;net9.0-maccatalyst diff --git a/test/Sentry.Tests/SentryOptionsTests.Integrations_default_ones_are_properly_registered.DotNet9_0.DotNet.verified.txt b/test/Sentry.Tests/SentryOptionsTests.Integrations_default_ones_are_properly_registered.DotNet9_0.DotNet.verified.txt new file mode 100644 index 0000000000..dfbc55fc32 --- /dev/null +++ b/test/Sentry.Tests/SentryOptionsTests.Integrations_default_ones_are_properly_registered.DotNet9_0.DotNet.verified.txt @@ -0,0 +1,38 @@ +[ + { + Message: Initializing Hub for Dsn: '{0}'., + Args: [ + https://d4d82fc1c2c4032a83f3a29aa3a3aff@fake-sentry.io:65535/2147483647 + ] + }, + { + Message: Registering integration: '{0}'., + Args: [ + AutoSessionTrackingIntegration + ] + }, + { + Message: Registering integration: '{0}'., + Args: [ + AppDomainUnhandledExceptionIntegration + ] + }, + { + Message: Registering integration: '{0}'., + Args: [ + AppDomainProcessExitIntegration + ] + }, + { + Message: Registering integration: '{0}'., + Args: [ + UnobservedTaskExceptionIntegration + ] + }, + { + Message: Registering integration: '{0}'., + Args: [ + SentryDiagnosticListenerIntegration + ] + } +] \ No newline at end of file diff --git a/test/Sentry.Tests/SentryOptionsTests.Integrations_default_ones_are_properly_registered.DotNet9_0.Windows.DotNet.verified.txt b/test/Sentry.Tests/SentryOptionsTests.Integrations_default_ones_are_properly_registered.DotNet9_0.Windows.DotNet.verified.txt new file mode 100644 index 0000000000..10eaeaf749 --- /dev/null +++ b/test/Sentry.Tests/SentryOptionsTests.Integrations_default_ones_are_properly_registered.DotNet9_0.Windows.DotNet.verified.txt @@ -0,0 +1,44 @@ +[ + { + Message: Initializing Hub for Dsn: '{0}'., + Args: [ + https://d4d82fc1c2c4032a83f3a29aa3a3aff@fake-sentry.io:65535/2147483647 + ] + }, + { + Message: Registering integration: '{0}'., + Args: [ + AutoSessionTrackingIntegration + ] + }, + { + Message: Registering integration: '{0}'., + Args: [ + AppDomainUnhandledExceptionIntegration + ] + }, + { + Message: Registering integration: '{0}'., + Args: [ + AppDomainProcessExitIntegration + ] + }, + { + Message: Registering integration: '{0}'., + Args: [ + UnobservedTaskExceptionIntegration + ] + }, + { + Message: Registering integration: '{0}'., + Args: [ + SentryDiagnosticListenerIntegration + ] + }, + { + Message: Registering integration: '{0}'., + Args: [ + WinUIUnhandledExceptionIntegration + ] + } +] \ No newline at end of file diff --git a/test/Sentry.Tests/UnobservedTaskExceptionIntegrationTests.cs b/test/Sentry.Tests/UnobservedTaskExceptionIntegrationTests.cs index 020a9f1e16..d6fd0af0bc 100644 --- a/test/Sentry.Tests/UnobservedTaskExceptionIntegrationTests.cs +++ b/test/Sentry.Tests/UnobservedTaskExceptionIntegrationTests.cs @@ -29,8 +29,8 @@ public void Handle_WithException_CaptureEvent() } #endif - // Test is flaky on mobile in CI. -#if !(__MOBILE__ && CI_BUILD) + // Test is flaky on mobile +#if !(__MOBILE__) [Fact] public void Handle_UnobservedTaskException_CaptureEvent() { From d1e73924ad4b25e89c0aee587323c2d215942222 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Tue, 5 Nov 2024 18:08:34 +1300 Subject: [PATCH 010/363] Support 16 KB page sizes on Android - libsentrysupplemental.so (#3723) --- .github/actions/environment/action.yml | 2 +- CHANGELOG.md | 1 + lib/sentrysupplemental/CMakeLists.txt | 3 +++ .../bin/arm64-v8a/libsentrysupplemental.so | Bin 6096 -> 6144 bytes .../bin/armeabi-v7a/libsentrysupplemental.so | Bin 4412 -> 4472 bytes .../bin/x86/libsentrysupplemental.so | Bin 4488 -> 4316 bytes .../bin/x86_64/libsentrysupplemental.so | Bin 5752 -> 5808 bytes samples/Sentry.Samples.Maui/App.xaml.cs | 6 +++++- .../Sentry.Samples.Maui.csproj | 10 +++++----- 9 files changed, 15 insertions(+), 7 deletions(-) diff --git a/.github/actions/environment/action.yml b/.github/actions/environment/action.yml index 455a2b0954..ff101a98f1 100644 --- a/.github/actions/environment/action.yml +++ b/.github/actions/environment/action.yml @@ -44,6 +44,6 @@ runs: dotnet workload install \ wasm-tools wasm-tools-net8 maui-android \ ${{ runner.os == 'macOS' && 'maui-ios maui-maccatalyst maui-windows macos' || '' }} \ - ${{ runner.os == 'Windows' && 'maui-windows' || '' }} \ + ${{ runner.os == 'Windows' && 'maui-ios maui-maccatalyst maui-windows' || '' }} \ --temp-dir "${{ runner.temp }}" \ --skip-sign-check diff --git a/CHANGELOG.md b/CHANGELOG.md index 21cbcbea0c..427f2cc820 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ ### Features - Added support for `.NET 9` (preview) ([#3699](https://github.com/getsentry/sentry-dotnet/pull/3699)) +- libsentrysupplemental.so now supports 16 KB page sizes on Android ([#3723](https://github.com/getsentry/sentry-dotnet/pull/3723)) ## Unreleased diff --git a/lib/sentrysupplemental/CMakeLists.txt b/lib/sentrysupplemental/CMakeLists.txt index 0db4750f18..a6fc90decc 100644 --- a/lib/sentrysupplemental/CMakeLists.txt +++ b/lib/sentrysupplemental/CMakeLists.txt @@ -3,4 +3,7 @@ project(sentrysupplemental LANGUAGES C) add_library(sentrysupplemental SHARED sentrysupplemental.c) +# Set alignment to 16 KB. See https://developer.android.com/guide/practices/page-sizes#cmake +target_link_options(sentrysupplemental PRIVATE "-Wl,-z,max-page-size=16384") + set(LIBRARY_OUTPUT_PATH ../../bin/${ANDROID_ABI}) diff --git a/lib/sentrysupplemental/bin/arm64-v8a/libsentrysupplemental.so b/lib/sentrysupplemental/bin/arm64-v8a/libsentrysupplemental.so index 9259ab7c87a6db1a77b9ed5ba79a486577f25ea3..f0c6ab28e5a5b6e771c219484a1947fe72fd4c55 100755 GIT binary patch literal 6144 zcmd5=U2Ggz6~4P`2RDxY5*H^;NruLiT!CkQ?D!{v*72_Hp-4R7R6Kwb2_8WBDMI3bAkhj5QI$#{Rq$XcT3Q8azBBin z^?I^tA${Sj?tJ%r_ndp~-`sm=zdk#EBAdx5k(~OeBJC@knwjB(nE}oKX4I7G!gH_M zqqafsNH|?SqXDH_*0^fHF6N2m5X@|abuJW?3dxUR3co(9{RH3Qc!J`WWxpgTl{xG{z)CwQIZt? zT(e40;!!_eK%#aZ3EU{Gy6S%9qtaLzaL+P1IXOL1D%`U_wQmq9hxGXAv+CX6n@`NW z@`q<yP$PeUwO-sX=y&kgu{VXW6#w*jui~tSnRm+pj+FDJQb) z73EjUj#&@Y{K=!oNU4zmbbZd$y&#TO>W!r(->V^SCj~l`yeB#Cpy(g)dnCNiPbhyY*Y#Q->M)e?I|{P?wO?K@pnTxXV;}avz3-|4|NTbpbxBFt z+bGNKPs$SgFVG*-I@QrmFw}{_J`vHUQu+bb={+JjqCxFrl(D|Ou@Mp_^?ZiQ(dR_O zZjSl;7m#ghq{fTgJ`sM*4Hx7++!&dhERGir zPJhuT+jY-1!oY~eixb7EF{k*5;rMp2Xk7B5dKCr5l36gPjFCzlFVzp_^R_+*%*8NV z^u2o6h#b#!!dgBKL*J>`)gbTtmujO+QFy^~V#AppJLpbNyOYjBsZg9KO-+v#O5??n zJ6V{T7%#d9OKxdl((@is^VQ&@7acO@=Z`m|=ARDzM$N0|x#{_Gqw2fmFwWN_N7|wT zau4!hBi_;iDDMBz3Y8hiJlwmhGuPWcw6}L4vlELWYl7UlZD#i?NB87<4|N~zerykR z>v8p9pH@%q!i(Og2s(#VuJ_UIZdyZUk@Q;VN|yQ#b(`nxIsUwm3pVD^iNbpQInVd=PUw1heGrYhVH|jv zv1*x_)}tvOmM`S(s5UV^dTGj5OL3$=ZH;ZtKS#Yt1FU>JGnvfo-W~#)F{I3=45(Dw zh{<}muQ~05o!hq$;<#+NY$JSH$f-m9eIw1|41n}0Ij^N$?{odleMfjKUI=y{KEH+6 z`}8g~oU~3@mE3J`0;^To*tlpU!Jg*#fOw1|lWA#>?B_G!vue{`_P8 z=gTgH%w#v`Mfzn>Q(*71z3@}azr?(e#8c`gz;^R~Gbxv7Yz)xrfo|$sMO%J$D+x`n zhdbw8uZKJ5{XkNKoVuIU{W{5R-jk(7xAV?=ZoweavaV!#&0-u>cA)tkCiT#E*zVp= zcf@x5V+p_69;siMK^S|c9A#A(_h9B0gNB)$F{Aj_G2NA*zEV>rv3wwIQLIR-6}ozx z`bj?6r_9LnZK{BCOFlw)6>A~FUT9TZScs-fTt?We!A)`wZWAVgTdSViahD9YgFK~4xz(BU&ToI;7O)Q`ZC(ax> zHEYeDK5kji^rsWIIkKLM?4>2N;N!llT6+mYuhxkDy446G3{yS!BI}~JqWF_ce}h@a zpE-Tx)X8JAZ0{NAHK03cbGwlYqwHf6pt-)8pdFyqU-mP>VR2+k`;UO9dyIr#F;xFGv zf^%d9k@(Vn8UFxfQ42r8`?rKhvhVO80N+X#h+mTiA@5F;LuQ#aRmgds82W$U^GGuO^8V6Sqs$%I di~OGdV0j=FFr})UckzE$Ul7&lltW5Y{{iH-=IsCg literal 6096 zcmd5=Z)_Y_5ue@jId1$XcK#4Ifn=+?(p8YXb8@jSX-S>fNgd)^Atf|Wq|M&$+IP*~ z?rC?O#-$PhRS*e2#1f#23Y3<9ND&f2s?fkY%g20>6%h?&_p zb9dQXTNL#Jb9y^(elzpt{o8qO?`wzWj_8^OLOFN^+7iGND~3nJg0Kqo;d49O4ZR5W zV3}fdmS5q!FjHCW5d)il5iMSGWn58@ZnD=T==^jPq_H$>{sA+3+yNSBKrxK zexCSa;?np&D&Fc{svq189B028IU{BHg9--WYzCi4Kkg!1{BMl>(>7?JL>d0NLP5C1 zqj5fih5CIkaHFv9!WOJYyZ7Sr&Z#mvxp%yD=WS5`He<svSAJ#h>&&DnWw2ivqpGxsTc#$`y(el-CDz)@?FJin6!5Jh&9X`!= z6p#8!d>a;eKZpq1`qF_7G#)BqgVoz8T{6pZ&e_&NJ+S@yQywVMPP}vV1f0l@Yv9+b zjv0qAcl6LS2@MjEyFOQ9FG!;0c&XX+y$06pRH4Gjdy=EhAc;K6?}OmJ%tHqM0^{#) zRsK4*Lt_^DQwJ_vkaXJi+yk`NX>b1-%PB1X#?r*ngAJTVK7jlpas&CR$R9+06*&>@ z1tRUK6QDi4L)$*EGnEPdB$n@Bk+mTO;bkn(V3Btjt0_K*Jn4eFh*2S5A*w%(ui1!jg?;jOXU+lX9-snL}&Y>wnBIqMzb|5;nxO8hjy%d0u~3FtfD5#4(v z16;s)y9`&itQ_QH8p@yW_(jH(@bt`P=CRNh;ftd!mR4ac{GMdPd&#dXBT%Vx=(FbcIK zX~z4DMOz&c=3*Ew`d%C^MUH1WVWXIYq3_h}dQkNJvyENND15?m5~J))l=gTF)so}b zrOC3lZ}*;xy-=x^$4e8=o|5glrf*X6MMsq{^`yc3k=x$7_WzbnXx(PGN1|XIngWS-8{(f4_$FQhH=}Y(A z0O-8;E}^C~?dzR_7QTRLP0#5oeK)DrGmlT`c@!V{AL0XB`@f!3T#$$lj^fD}m$cZh zlcYEux?XY7OVn9WRBV?UCV`g})@hIhOoMCv9 z@y(L3S@Cto2Qv7#8Gkl|(^*WBM>F_E#!qGNzaXybgWz(0lzGcj7ZFbwmvdqVfV$wDWUpzuUUMk@ly3&rC&p{5q{jG(iE>I&NtLTvO|qSuE74b-aDO)H>e2 zUTPiX^lXQ@id7BT_Oi^XpKV+`4&yw4J_7PRCHU{L((S%|AKG`rA4>7|{20=Z8H9;v z%GpzQF+G987K0_Tx>WbwUHI){y30Yl+yIkMKEZAgtVoG9x=NesNj{jx%*gX?YJh7^ zA47OGYay~5o>g4;HWirAhmvs(wB2z{Ei%b`^OIRjQ^qCikT3E!G1o4owF?7{b7E zea8=D55~@!pNZmbh;@~5Pt;6#(mW5L0bI5Bf zC4Ouz1rd%pPQ1uE<1M3^`b6SkM%I&&-E3ko{&KqIGpch!_-M3dzBGUEk)zYH+wPdt zYe09(R=SZ2bIFGiK;A2n=}qtSmpmtYObnT4{R@cqW09~|xFY;C`%#{vzDmB5du|Ca zx@(HR+((6rJzq;(am4&m>+(12%#LxDhzRj+8_|I}6;RZP)Nqp&#LIJ*r z6-v<(zs82b3v4L<5?}aNP)|DXm;1hOl`pvwWHg7JIc{cv{Gyd}3B77?2Kb(=DV}F@n`F}(HzsvSFD_=r?x|@l=e6Nl2fU@zj{x731 znTo%hhw{JUMcy#tCtTY71^SYS_{;a|oBW|#KwT$^zhG}*h5SnbFkfYVx|ehkcPI5B z{I^&H>$H2G1I>1VU2K2X1tXVh?Eg3k9h8lA4Rzi6Kf`|jSQ!Tqh5v*7J3^$@AM*$M zk1`7JYrzP}yVJt3GD}Gn=`=6QHmu)+Mdn}LU;4O|wIh3>OZ)cF84>;q9eBcM diff --git a/lib/sentrysupplemental/bin/armeabi-v7a/libsentrysupplemental.so b/lib/sentrysupplemental/bin/armeabi-v7a/libsentrysupplemental.so index 200d6f929fea3065b676ced77900873e454f599d..b9f0c81c17748781f15c1a86e4011a16561843b7 100755 GIT binary patch delta 1902 zcmbtVU1%It6h1S%lWaDd&E$uqp=lkmDb1?M>~1!@*#u(J1Wa&660Pw;9A;;3l1*l3 z!pv@LN-=^IUjngSK_BY>lOR&bi{e7X2ho~`3h_yhwIoucLKK4)TjRO28`<_j)LuCE zobQ}_?wothopXy_H@jXs;(y8m5C?!G4HvpjkrE z8@T=k+8A9$%!t{&NHe>6{ceBh!k6dy{tTVwhJBDsCR2%61VM^!abj!uiQ&ha9(yi1 zxjp~=C)4XcB}1(r&^=D#g0zWm=Xe_CXK|k9+F#~{ql%)PQ57{$&SdjY26I_cHL_<3 z{fHlJW+As@7&{eyeVqC|@t#Z1KZ8nkwl5|C+VButjHAK}Z|(wYl-dA(JU2?`Jok45 zQ7j!C9Hd79{`_ux+gs}0g&#JqdFjWVqsP5h)~~_O#ocWj@HuJ`|GHPkrE&U;$K;C> zG~*rNcgE>E-o&Z5JOGTwOx?D!y3~_QM&prz)Pqt+b%-umrew#XiRfUz77a_9p_=p3 z3bCCm8lthjNZ%lRyJl!jOZ5-vsg$17=3m6O|359{EYjDCCGESE7(Jg{+}xti0^lnyi*XlMQ1f*INljwhH;OV%fG9 z$&AL3F8%v3U@V3CR*9O)0w<$5j)zG>jlXux$JOzEFWnRp&99#7@C1kIhwC5e0Pw`2 zvyt8uj;%e4>)Ca}nN|G zvO#iaRSnt7EXt~#!xT5t2yVnE-^q9jr%|Ye+sIXBH#|5vsW)99HpR97&K)h9j*}Bmx{2Ah9{2)sk z&j;W)g^P$kt-{Q{Mfd9- zNQH4L)@R+pi|}nlWULwK`dv7L>tDy6_{~-S2$$(r`yzl-47&BcD4Y7RZR(DtEZ|RX z5Jj_0hjmiRRCGf#EQj0y6a#xLnEI09HVnlqU+xv~8a23%Bs){Xi8__<#dqXrD zY@xRh$@q#rLYu^pa2BO$SDzL|;VVQl=o8{4A026EqZd&*;RAS)ekg{xE&7dkk}J~N P;z?nr9w0`!rk1||wddDe delta 1843 zcmb`ITWB0r7=ZsXd+B7eNp`cjRGaFWM3Yprvztuzl1R+OOCSMzF;>MoncX>?U9&s8 z%uH%3EomMKJ_OqI6!8|T4}yIVQj6e2KoJy_6b$&_LmxsQqT)k@h((L>KeJoh#ur7; z@Sp$x&VS}y{&VJ^o2{#@7xsGqi;Qx^yVN>Dw;cdnT)f!_Kv3Cj0n}VLqqtGHUqE#W z``kzaK97f=$57Jec!kA&1ZpMB)WqmWiJPx@N&g$66xA$!WKoc4_xW*x!aCr;h{Knk?KQ8)M+Ht6>9?gy2 z-QDML#|tYEgbVPxI7h-_k*pM-z*jc9x8S?;n}l6)9Jq&kwYUjCuWo&V1-`&mLl@Q>*iq-8uoGYxozdYH3?P?bT{8+k6iFsy-b&NewfL?GZT?4n>*A)juVtqTybePKHw{H5`*^qNg{mrsGLD5{{;N!>Xpq zawMLPcELnepP@#7aAM-o@&GvZ0ER-PYBCEXg63#XD4YPdXWy_$ssxY2OIV{jTIZkx&V)FsK ziFUai)sb2!yoxPOu-LfRGW1_}#4d~Njn$rUms@gq+#wfnPYX^Uc)<8Iwy^I-?>t|E zfT!Jp`WZasQt`rlXlNDu05bmy`5hrp3klSj3jZK-4w?J;c!yD6s_;)D%Q*3Cs0lt9 z)MEtZQSm~+zuS*1WJM&C>j= zl{1xD-JsK1)1rnlLr>w9rPNzqjiJ0`sKp{RpwxUvdcI@b*W5L3fjrG>>h5*f`H)5G z+FbT8hdqlHPv1Li0WJGi!>*vUh8cT}t=Dz9H}E8`0{f$`)wP7bu7}yW7+`Uq&vhMX z%%&-%M*J-uv*{J243f&0eK^oQfKK)~T03aH!Fycv`^W|6_ji!9EP?g=O!YVa2||*; AivR!s diff --git a/lib/sentrysupplemental/bin/x86/libsentrysupplemental.so b/lib/sentrysupplemental/bin/x86/libsentrysupplemental.so index 7e698873f12840156a98186cc7564be8f548959a..09819b1db0b3665607bd4f39fd4cc0d2fe0112fa 100755 GIT binary patch delta 1836 zcmbtVU1%It6h1RMll{piyGc_`lm1MyF(#O1b~l^-NyK#9-_CeHM zICIW-zI*SSIp^LpFGsF-EVl-hJpc&+crUopc|hd=$|yWKU>^X3isJI9D|Zl6sY9VW zi)sP+1>~u$A*Mokbi=V*wsQaa<@%LtZF~!|Eh|4Su>%Eih8gxjDwWD4<52_|vcZH~ zzW?g(a8bK`{UiCx?{9wFt8BX#X(wS;U>Zq`-Nr|8dmlN-KFzSWMoGfFz^9y@}}<~Ys)Orm6U#WYHa(348V64Aj-pHRqKsv;P=U?yV8Sb9K? z^$N0<*G~&)RnsaV5sUXn`_tr&x}ilmGcc%RGD=FGj7MY1cser>jVEGpB^6C46ES5l zuEZx(s@e;=l0L1PLqaY$TH8SA5sw?%?2KxOwJ5~GY)MlJhAmpAEau(NL`^$8v$qzE zXw2HKqih;eYE-635&m@;B;?)Q{M!BC$Ymr%VkZSK{l_6Mqx^vOJ|c0 zx)J%s_mH=O$>2aELI2L7{~Hkix#QbT7X983O@9j?2ozZs7zW7O{_aIgJuMsqA?QR+ zyHo@<3k@KmrniA!Jc{u$!G}(H(ZChQ@TqOVrx1@Le%)aiID@*lg?}FL#uoe{;=|8w zaeRr68~DOKs9ClF?xCg~(Fu?Zfgk67jyzpL`3`CzNdrYpUI}cU7)Rcjk7w`-`g7Q9 zoWV9jKhCG!)5T42qh?aojB@)v+=KI-BfEMx5M^?TWk^N5S(+-zhHlZ;XXuim$(muQ ze*=<+%@K5ET5@isq|eS2RMT8#?kC*FT6>i#oip?0 zGOqrIu8Z7X!I6z%Tb;Yb>PW+n4AVk(h4%PqUZ|wW7$RC`D({n-kicWU_HpD=D8gfM z${hLFwQP>ubgc!B1i}#>Q<>vQJp3q+N8%BNoCqOqP&cL+dEHj8=yi;1Kx^ kt!ZrFFOfgO5%yOf>1^s|#{=Y0Q+KOY50IkOtBp%DFlIkAySb&)O`$k;9aN*woAJTwhrfY*oaNY1nq49*xP}%v;M}e z5Bi>meX>UgiThE9kmGw8c8LE)VgJz5?>HtQ@f>7=dXA0!I_wfz$OQF7AMiN9btt=* z@6|1VX$b$G1NeS;Dosor7%P1EHaNCD0G38Pc;tjwd-Bc8t3O|SIh{zolKMsVC3U)X ze&^`A_i`9UY|j=^{v?pXUfl^kEO=N{{3+qzU9>&c9lL?D5gMX%$$bp4apbEpKlO1TZo!}*A;%z;k)HcXu=DH`pg#`2 z2)+*91m6Jv7WfarFM_`Tz6s8Wb1z8B`C~#^?^8y0ClXIVpM~xY;7x#QD9T#jw3ceC zv)Z1rmin^RSJt>jT-zDoPXL=FQFjXX0}$RfaJJ_FZ12;+j`kL0dj*)D1vth$u*2_= zw&$>xp3&|oYng{+9gg!iG471(JfThvBp=0ODDR+!_VvE2)SdbEmER2s@k~40>2xk6 zvR8i%W&5?hGgmgcX}h$saqX{g?{esFuZ{Mep2?=3xE!dR&8V$zy9s;ydLrubnWuwy zI-U7WkWD{)^<>z0`plI#PF#4q{pa^F$k%7G;rP*DV(?de?N^nj`Yv35^a{t`MV*)F z)w#qo!eMnT?MA6ak85vXsGUpPks8g{HX$g`WxViSS{%!8ean9_?Hf`MeQ@(A=`;EjxD0 zTk%ahXL^l%;CYT&GwN>Man3dNHGS_f+YB;AbG)$MUaA&M(ZdPFWio$EebUj4`CY;gMuuX6+PMm{rz0vqW~8YU+$gQE#4;s{TPrTQV+2c{-)QCI6O*6Ld)3GCtEF+#41DoXwRw|xZ;J16rA2@*Qla;> z0WxkHpnOCf3vtt)!=sT5Pr;sK|E}*Qd)vufyjfxzMhJ?^SK9QAKY_5e%8Tz+_ifmUW|A_3d*~s=G^Q zAhLv`iHc6$wM7n73XCdv4W_6Raa`q~YA@Gaz2)Uhq*k7*TaM{?E#_HP%hPLEGso6V z4|CjR*Kw6?!DZFsW!0l)4ON_0WOOnAsARjAaILetTvwLEmDk;sM%DIjes^?z?pXQo ztUh~WM%RV@VCCpkxuPGPn_HMYt{*Q?Rc4{xnIYPZmB4A~E3S{pwF2AM&)REni?YUe zQojCm-)J_`ahyy>&1?xDOh1lz`poGg<->=jQ-x;zd^&~#2(y#mX zMrg!`_kuJE@Y!KKzVXB)%6zxk7ybC=GvDC5O+UWn1buMDLZY7dJivFJWxn-92IcPd zAc_j`&)AQ-1Hl+1+mquv30)L}qTGV{1;^a&N8d+c9n4{F!u*7}i6qX2WC6DV^kYsG z0_E=ZIMxP0zbSyZ5g~IU#sl_AzjFZFW_y$HA?`<6lAOomP)I+GwPo^h0-Hyllcx_C;S={t?Q26X?g>Er1{ACrSGK z3<{2oe&O$I_-&#red)*YF9F#v<8B3&m`_TQ?Y#yC{p8pe;m24li8kW5F_-!w9PWpt z&}I8$SDEJ1@LOOZCbmWT3((!~i|{)wjj@g5-{Ue^NJ$)u7o-3#tK2}DXF$?L_$|W7 lT$uSb=gU1oIu@=SiV@-RrDY?~$YN@TUm8CTCZw^;??1pu^bi05 diff --git a/lib/sentrysupplemental/bin/x86_64/libsentrysupplemental.so b/lib/sentrysupplemental/bin/x86_64/libsentrysupplemental.so index 4be4aeb7644fa7f445c5d2fcb03dddffd1f5d087..c3a8db6fcfb8d71a93c550836299373fe6fc0bb3 100755 GIT binary patch literal 5808 zcmd5=TWlOx89uYNQzwouanmNLTe3i0xq{5DH@>XXHrR=iI>JelNGUBsOlN1t-c4p_ zmYLbqE(r2aZbC(V;8KKyEcJy-6)F-BeL<>Psj8|-_=$=KqzI{{0Um<5D5z4F@1OJk z+x5gL(7f=k&V1*;pL1r;nSb^h#}`lJ3_}TZsUwQGwH_^*WySSD?f_<0S#8I4MD0`C zz;~xIt)JBZt)?3sTCkVpB<2(>*$hPr3=))pAG8O2v=uPvhH- z)`j-i>GSIK-@f~cJ$xdetnhTDIq5$nvs4RgzxJ%BoXC!=%CA)%GY-|_$)m@JsS^WqeeT3wkVLC-v(fOqI{J31(1~P! z$-WT2%=<{>ugJNPk;{3a7IeJ30G;#c?U1MQ(PqjR>3W#<2|G88( zxPKmsKet+~4`pi$-)tqh)*s&xqlNF@M2PobwD9~rk5i<>aK6%}!M-b3(rA2XP5x<4 zsn+OUaHV{$QR1tly>=B*TBCm;-XMMhEW6zTWI$BZ&|bT8hNrziMEi*d!BhGKqpaZv ztyV}PiA&E5(O9=ev0KnA-bb4DGX9JGY2Og-(V!aaFKaL36}$Hz(}r}Ph$OF^$v4=s zPpg&WlV{>B4zz>yUu68pO+BB>tmk2?aB6Uly}XOn11gSyW1d6MmKGXFI5xHX&B zi{08F@42>sJNNR$9%SURJU=!)Jw8#KocV0NV#l7F4}*L(F+Me3KIDu)ly`hPSk7Pa zqPPadc*!i9<@{JRNgDCtLc!MW19LeHmwhh|n~~$0PFOD_Vdy(myA~9D|5E*6BML8g zPLg+K4o$i{2Pfd)wlO?ybH0^m0sl{4w(Tfh}7Z>N- zSqo2uezWey1s-~#(yaMzB}@u&hFZzbTp~%v$*v943}+&yp;b>ZzP_Ah+j*E z^D&;I!UY@m(22r0e#Z0tf)l!4LBEg=x?vJ{xMQ^nOFCasg|Kp=uu`6~rY2M)iPW8j z+u|kYsBhEiDj!$FFg_UQC!pJfX53*yrMAqNJ`CI2_k4HHjvc#mC@4}nGT)9!->v?7 z%Q;QuyP*c#b+VkFpMpH6`rGyXP7YjPxxCNie49)4hFX&YlXwqIv^SU~*l)F->GnhAaFq{oInVb}z}&9(_XW6=-o|ySy7Had$cGVU826=f)2D&VAWS?{ zj@X)uI!$TWa?mu>v+p2kanoH5;?=q`Ny-QHCdP_1Tcy&_)K~Js7BVBxwP~=F z+Imx4)`@d-r;c04PtRKxMxd*89%N)a6WNUh@_tk)-#IN9di7@F$5u0lF!MO^BI}~J zs`x8Fm;KiKQ>W)nojjI4J$HPe$D3***@m*O1!aFrBC!*mz7ae9WuFTk5JN`US0Wxm zhu)hKSL!jrr`eFs0UE2E2U4d|y+^f@_)9${DE3@dweyJmqwuHNOL(cj1oO}nN&f8k z=|%m|4*$!XNH9;%AjvP|(Nd+JK!=Vf$-mBqf@5qb{*qttOVE=}{H0zMlzNrJq-!XB zLKlrlKe7$S^oe!@;^#OAgRX%JL_>eFSGtDkflc~IfK@DVuFuoar?^Z zHvcX3I{mM*f#Az+OWl$DB3@&EE|=Ty%{6{+xMIjC^}C3Fl<`kLY)Q!hW&UEH_5UgR z%lgW9xconr9dDNZIsB;>5P$jJD0BX7zO4VR;7g|BFW(jNzuG!2E=c?YCGM}GBNOqL z@2&UwLu-tjJ4pOR^d>swf6F?t;V9=9dtn8C!~UWdoa7J4&x?Rj{6zeFbh0wiU1tBM z#E>y-dku1uWyk+J_CJ+z5LPfP_cJ+&eULv$zn9U8Ut0`;?A^8uombgk{->qy(XI9$ ieWl2~>-2x6Uvu|m5@uLMb7%g5Pomh>v&_wKe1ywX&@UxN~5${yYXLI+N4T~k`SRpZAwr<&c?f`O`UbI zT@=bs8nr?#2vm1C01_xY<`z^A6-W@o0fBl52c$||0xA+g9H<0pBo1Mo_iYJ1z=4i5 z`@Q*QW_RA-?CQR)y;q_E!ex--jaqkUal&a?HWI&OQv~L+`H3)(qMp-}CL17`fvu5329pk?O@ zrdBMRGl*qsRb!!0h1=rrqCIsNCNr_^X+AZZUz05V%zgYa=as1CZ$RLyP$%~6;Mwxo zLU)M=A19>ODjYMW(c?!vgQy^0LX06UBMu_oK%9Z^96fXys1#{^7j2~PNeD;ds8=8^ zMUJv;yL1-9ysx01N98lX5h3lU@1U~yHdJ!N>wjGsYI06_8eFxdpgi){p%pwen-}3d zX*ic?&5do8t{<7rrn0$?CT?#Olx7!9IM)PM6q*}e6Q;v(wc&)257(B$qbYv)p5S=I z)XSxUuI$Mq`eX5QX0I}}Z=OA>mKY<3PtMehOkLH9r_A!33E+#C%O;_wj$(xXK5E*^Hf-8l89 zQ5~qbYxPa_knOrf$OjDhRM`#*I}Li`52UCd3blnl*~sw}K8fQPKBV99;iTkW*Qfmf zE|5A^!Sz+=wQ5xzD(QxL%BWThvs!*hwP2rKs+vYst(0@>G0QPg)T(o(@_a?*WvWAJ zY4&+_A(P6c5+s*Du9ZoytX1;lOtnlN&?ms}4mZ5d7K&71pSykV|JDpk?uQ?;Hzp*x z8=sV%7o=shuyVrc|o2w$v;@6`+j2(wbPq2lJhIe)q^yQ)EiL}1J<0kI&z6x9EV8CO(M|w=xExH&_U~Z= zzDCRbNlb$rO3U`V^Dr7pH@c$M>|HK+BXn4f@wLRoXpX?oAw~XyGb0daNz2``6@C&X o(Yna3XQ77HMx*8X5Wa0m)AJyQ2j~Kfh6m)UUdz)7li`lP0KLtYM*si- diff --git a/samples/Sentry.Samples.Maui/App.xaml.cs b/samples/Sentry.Samples.Maui/App.xaml.cs index fd978a504e..c400ec6e5d 100644 --- a/samples/Sentry.Samples.Maui/App.xaml.cs +++ b/samples/Sentry.Samples.Maui/App.xaml.cs @@ -5,7 +5,11 @@ public partial class App public App() { InitializeComponent(); + } - MainPage = new AppShell(); + protected override Window CreateWindow(IActivationState activationState) + { + Windows[0].Page = new AppShell(); + return base.CreateWindow(activationState); } } diff --git a/samples/Sentry.Samples.Maui/Sentry.Samples.Maui.csproj b/samples/Sentry.Samples.Maui/Sentry.Samples.Maui.csproj index 0fe2a399fb..a8df32a874 100644 --- a/samples/Sentry.Samples.Maui/Sentry.Samples.Maui.csproj +++ b/samples/Sentry.Samples.Maui/Sentry.Samples.Maui.csproj @@ -6,9 +6,9 @@ On Mac, we'll also build for iOS and MacCatalyst. On Windows, we'll also build for Windows 10. --> - $(TargetFrameworks);net8.0-android - $(TargetFrameworks);net8.0-windows10.0.19041.0;net8.0-ios;net8.0-maccatalyst - $(TargetFrameworks);net8.0-ios;net8.0-maccatalyst + $(TargetFrameworks);net9.0-android + $(TargetFrameworks);net9.0-windows10.0.19041.0;net9.0-ios;net9.0-maccatalyst + $(TargetFrameworks);net9.0-ios;net9.0-maccatalyst Exe Sentry.Samples.Maui true @@ -29,8 +29,8 @@ 1.0 1 - 11.0 - 13.1 + 12.2 + 15.0 21.0 10.0.17763.0 10.0.17763.0 From 40e5793895db40868896b18aa3d8951986c306b2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 6 Nov 2024 09:18:01 +0100 Subject: [PATCH 011/363] chore: update modules/sentry-native to 0.7.12 (#3731) --- CHANGELOG.md | 8 ++++++++ modules/sentry-native | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b9acea5d58..4f317f4864 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## Unreleased + +### Dependencies + +- Bump Native SDK from v0.7.11 to v0.7.12 ([#3731](https://github.com/getsentry/sentry-dotnet/pull/3731)) + - [changelog](https://github.com/getsentry/sentry-native/blob/master/CHANGELOG.md#0712) + - [diff](https://github.com/getsentry/sentry-native/compare/0.7.11...0.7.12) + ## 4.13.0 ### Features diff --git a/modules/sentry-native b/modules/sentry-native index 33739b5ccf..bf14b8533a 160000 --- a/modules/sentry-native +++ b/modules/sentry-native @@ -1 +1 @@ -Subproject commit 33739b5ccf0c0d1513d284ac64b610e74adb62cf +Subproject commit bf14b8533a3b26853e4e6fecf2f955deaa29e2d8 From d179ec9157934e9fbaa0e00860e6c1e090029414 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Thu, 7 Nov 2024 03:02:26 +1300 Subject: [PATCH 012/363] Use pre-built version of sentry-cocoa SDK (#3727) * chore: update modules/sentry-cocoa to 8.39.0 * Download sentry-cocoa SDK instead of rebuilding this * Update CHANGELOG.md * Remove github actions to build the cocoa sdk * Fixed regular expression for extracting the CocoaVersion --------- Co-authored-by: GitHub --- .github/actions/buildcocoasdk/action.yml | 17 ----- .github/workflows/build.yml | 4 -- .github/workflows/device-tests-ios.yml | 3 - .github/workflows/update-deps.yml | 2 +- .gitignore | 3 + .gitmodules | 3 - CHANGELOG.md | 3 + Sentry.sln | 3 + modules/sentry-cocoa | 1 - modules/sentry-cocoa.properties | 2 + scripts/generate-cocoa-bindings.ps1 | 36 ++++++++-- src/Sentry.Bindings.Cocoa/ApiDefinitions.cs | 67 +++++++++++++------ .../Sentry.Bindings.Cocoa.csproj | 56 +++++++++++++--- src/Sentry.Bindings.Cocoa/StructsAndEnums.cs | 1 + 14 files changed, 133 insertions(+), 68 deletions(-) delete mode 100644 .github/actions/buildcocoasdk/action.yml delete mode 160000 modules/sentry-cocoa create mode 100644 modules/sentry-cocoa.properties diff --git a/.github/actions/buildcocoasdk/action.yml b/.github/actions/buildcocoasdk/action.yml deleted file mode 100644 index d7dc4066b6..0000000000 --- a/.github/actions/buildcocoasdk/action.yml +++ /dev/null @@ -1,17 +0,0 @@ -name: Build Native Dependencies -description: Builds Sentry Cocoa SDK with custom Carthage -runs: - using: composite - - steps: - - name: Cache Sentry Cocoa SDK - id: cache-sentry-cocoa - uses: actions/cache@v3 - with: - path: modules/sentry-cocoa/Carthage - key: sentry-cocoa-${{ hashFiles('scripts/build-sentry-cocoa.sh') }}-${{ hashFiles('.git/modules/modules/sentry-cocoa/HEAD') }} - - - name: Build Sentry Cocoa SDK - if: ${{ steps.cache-sentry-cocoa.outputs.cache-hit != 'true' }} - shell: bash - run: scripts/build-sentry-cocoa.sh diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4d4d390647..317dd9327e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -130,10 +130,6 @@ jobs: - name: Build Native Dependencies uses: ./.github/actions/buildnative - - name: Build Cocoa SDK - if: runner.os == 'macOS' - uses: ./.github/actions/buildcocoasdk - - name: Restore .NET Dependencies run: dotnet restore Sentry-CI-Build-${{ runner.os }}.slnf --nologo diff --git a/.github/workflows/device-tests-ios.yml b/.github/workflows/device-tests-ios.yml index cf3db8c515..f79b9488bb 100644 --- a/.github/workflows/device-tests-ios.yml +++ b/.github/workflows/device-tests-ios.yml @@ -28,9 +28,6 @@ jobs: with: submodules: recursive - - name: Build Cocoa SDK - uses: ./.github/actions/buildcocoasdk - - name: Setup Environment uses: ./.github/actions/environment diff --git a/.github/workflows/update-deps.yml b/.github/workflows/update-deps.yml index 45ef93876b..5fccddf94a 100644 --- a/.github/workflows/update-deps.yml +++ b/.github/workflows/update-deps.yml @@ -15,7 +15,7 @@ jobs: matrix: include: - name: Cocoa SDK - path: modules/sentry-cocoa + path: modules/sentry-cocoa.properties - name: Java SDK path: scripts/update-java.ps1 - name: Native SDK diff --git a/.gitignore b/.gitignore index c7e1484b7e..f8d880a8e7 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,6 @@ test/**/*.apk /tools/ *.log .sentry-native + +# Download cache for Cocoa SDK +modules/sentry-cocoa diff --git a/.gitmodules b/.gitmodules index 172d270909..71bd96d198 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,9 +1,6 @@ [submodule "modules/Ben.Demystifier"] path = modules/Ben.Demystifier url = https://github.com/getsentry/Ben.Demystifier -[submodule "modules/sentry-cocoa"] - path = modules/sentry-cocoa - url = https://github.com/getsentry/sentry-cocoa.git [submodule "modules/perfview"] path = modules/perfview url = https://github.com/getsentry/perfview.git diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f317f4864..c1fb939ec6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,9 @@ ### Dependencies +- Bump Cocoa SDK from v8.36.0 to v8.39.0 ([#3727](https://github.com/getsentry/sentry-dotnet/pull/3727)) + - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#8390) + - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.36.0...8.39.0) - Bump CLI from v2.38.1 to v2.38.2 ([#3728](https://github.com/getsentry/sentry-dotnet/pull/3728)) - [changelog](https://github.com/getsentry/sentry-cli/blob/master/CHANGELOG.md#2382) - [diff](https://github.com/getsentry/sentry-cli/compare/2.38.1...2.38.2) diff --git a/Sentry.sln b/Sentry.sln index 4ebd081d31..e8af1ab290 100644 --- a/Sentry.sln +++ b/Sentry.sln @@ -148,6 +148,9 @@ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SingleFileTestApp", "test\SingleFileTestApp\SingleFileTestApp.csproj", "{162A1CAE-ACEE-45CA-A6D0-7702ADE4D3DE}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "modules", "modules", "{A3CCA27E-4DF8-479D-833C-CAA0950715AA}" + ProjectSection(SolutionItems) = preProject + modules\sentry-cocoa.properties = modules\sentry-cocoa.properties + EndProjectSection EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TraceEvent", "modules\perfview\src\TraceEvent\TraceEvent.csproj", "{67269916-C417-4CEE-BD7D-CA66C3830AEE}" EndProject diff --git a/modules/sentry-cocoa b/modules/sentry-cocoa deleted file mode 160000 index 5575af93ef..0000000000 --- a/modules/sentry-cocoa +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 5575af93efb776414f243e93d6af9f6258dc539a diff --git a/modules/sentry-cocoa.properties b/modules/sentry-cocoa.properties new file mode 100644 index 0000000000..799f17ac06 --- /dev/null +++ b/modules/sentry-cocoa.properties @@ -0,0 +1,2 @@ +version = 8.39.0 +repo = https://github.com/getsentry/sentry-cocoa diff --git a/scripts/generate-cocoa-bindings.ps1 b/scripts/generate-cocoa-bindings.ps1 index 1fd8dfe6c7..bac0ad65bc 100644 --- a/scripts/generate-cocoa-bindings.ps1 +++ b/scripts/generate-cocoa-bindings.ps1 @@ -2,7 +2,7 @@ Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' $RootPath = (Get-Item $PSScriptRoot).Parent.FullName -$CocoaSdkPath = "$RootPath/modules/sentry-cocoa" +$CocoaSdkPath = "$RootPath/modules/sentry-cocoa/Sentry.framework" $BindingsPath = "$RootPath/src/Sentry.Bindings.Cocoa" $BackupPath = "$BindingsPath/obj/_unpatched" @@ -41,16 +41,38 @@ if (!(Test-Path '/Library/Frameworks/Xamarin.iOS.framework/Versions/Current/lib/ $iPhoneSdkVersion = sharpie xcode -sdks | grep -o -m 1 'iphoneos\S*' Write-Output "iPhoneSdkVersion: $iPhoneSdkVersion" -# The umbrella header is provided in the "new" style of `#import ` instead of `#import "SomeHeader.h"` which causes sharpie to fail resolve those headers -$umbrellaHeader = "$CocoaSdkPath/Carthage/Headers/Sentry.h" -Set-Content -Path $umbrellaHeader -Value ((Get-Content -Path $umbrellaHeader -Raw) -replace ']+)>', '"$1"') +## Imports in the various header files are provided in the "new" style of: +# `#import ` +# ...instead of: +# `#import "SomeHeader.h"` +# This causes sharpie to fail resolve those headers +$filesToPatch = Get-ChildItem -Path "$CocoaSdkPath/Headers" -Filter *.h -Recurse | Select-Object -ExpandProperty FullName +foreach ($file in $filesToPatch) { + if (Test-Path $file) { + $content = Get-Content -Path $file -Raw + $content = $content -replace ']+)>', '"$1"' + Set-Content -Path $file -Value $content + } else { + Write-Host "File not found: $file" + } +} +$privateHeaderFile = "$CocoaSdkPath/PrivateHeaders/PrivatesHeader.h" +if (Test-Path $privateHeaderFile) { + $content = Get-Content -Path $privateHeaderFile -Raw + $content = $content -replace '"SentryDefines.h"', '"../Headers/SentryDefines.h"' + $content = $content -replace '"SentryProfilingConditionals.h"', '"../Headers/SentryProfilingConditionals.h"' + Set-Content -Path $privateHeaderFile -Value $content + Write-Host "Patched includes: $privateHeaderFile" +} else { + Write-Host "File not found: $privateHeaderFile" +} # Generate bindings Write-Output 'Generating bindings with Objective Sharpie.' sharpie bind -sdk $iPhoneSdkVersion ` - -scope "$CocoaSdkPath/Carthage/Headers" ` - "$CocoaSdkPath/Carthage/Headers/Sentry.h" ` - "$CocoaSdkPath/Carthage/Headers/PrivateSentrySDKOnly.h" ` + -scope "$CocoaSdkPath" ` + "$CocoaSdkPath/Headers/Sentry.h" ` + "$CocoaSdkPath/PrivateHeaders/PrivateSentrySDKOnly.h" ` -o $BindingsPath ` -c -Wno-objc-property-no-attribute diff --git a/src/Sentry.Bindings.Cocoa/ApiDefinitions.cs b/src/Sentry.Bindings.Cocoa/ApiDefinitions.cs index 2e6eb14a11..3bbfc298dc 100644 --- a/src/Sentry.Bindings.Cocoa/ApiDefinitions.cs +++ b/src/Sentry.Bindings.Cocoa/ApiDefinitions.cs @@ -7,6 +7,7 @@ using System; using Foundation; using ObjCRuntime; +using Sentry; using UIKit; namespace Sentry.CocoaSdk; @@ -1521,6 +1522,14 @@ interface SentryOptions [Export ("enableAppHangTracking")] bool EnableAppHangTracking { get; set; } + // @property (assign, nonatomic) BOOL enableAppHangTrackingV2; + [Export ("enableAppHangTrackingV2")] + bool EnableAppHangTrackingV2 { get; set; } + + // @property (assign, nonatomic) BOOL enableReportNonFullyBlockingAppHangs; + [Export ("enableReportNonFullyBlockingAppHangs")] + bool EnableReportNonFullyBlockingAppHangs { get; set; } + // @property (assign, nonatomic) NSTimeInterval appHangTimeoutInterval; [Export ("appHangTimeoutInterval")] double AppHangTimeoutInterval { get; set; } @@ -1567,22 +1576,36 @@ interface SentryOptions // @property (copy, nonatomic) NSString * _Nonnull spotlightUrl; [Export ("spotlightUrl")] string SpotlightUrl { get; set; } +} + +// @interface SentryReplayApi : NSObject +[BaseType (typeof(NSObject))] +[Internal] +interface SentryReplayApi +{ + // -(void)maskView:(UIView * _Nonnull)view __attribute__((swift_name("maskView(_:)"))); + [Export ("maskView:")] + void MaskView (UIView view); + + // -(void)unmaskView:(UIView * _Nonnull)view __attribute__((swift_name("unmaskView(_:)"))); + [Export ("unmaskView:")] + void UnmaskView (UIView view); - // @property (assign, nonatomic) BOOL enableMetrics; - [Export ("enableMetrics")] - bool EnableMetrics { get; set; } + // -(void)pause; + [Export ("pause")] + void Pause (); - // @property (assign, nonatomic) BOOL enableDefaultTagsForMetrics; - [Export ("enableDefaultTagsForMetrics")] - bool EnableDefaultTagsForMetrics { get; set; } + // -(void)resume; + [Export ("resume")] + void Resume (); - // @property (assign, nonatomic) BOOL enableSpanLocalMetricAggregation; - [Export ("enableSpanLocalMetricAggregation")] - bool EnableSpanLocalMetricAggregation { get; set; } + // -(void)start; + [Export ("start")] + void Start (); - // @property (copy, nonatomic) SentryBeforeEmitMetricCallback _Nullable beforeEmitMetric; - [NullAllowed, Export ("beforeEmitMetric", ArgumentSemantic.Copy)] - SentryBeforeEmitMetricCallback BeforeEmitMetric { get; set; } + // -(void)stop; + [Export ("stop")] + void Stop (); } // @interface SentryRequest : NSObject @@ -1635,6 +1658,11 @@ interface SentrySDK [Export ("isEnabled")] bool IsEnabled { get; } + // @property (readonly, nonatomic, class) SentryReplayApi * _Nonnull replay; + [Static] + [Export ("replay")] + SentryReplayApi Replay { get; } + // +(void)startWithOptions:(SentryOptions * _Nonnull)options __attribute__((swift_name("start(options:)"))); [Static] [Export ("startWithOptions:")] @@ -1740,6 +1768,11 @@ interface SentrySDK [Export ("captureUserFeedback:")] void CaptureUserFeedback (SentryUserFeedback userFeedback); + // +(void)showUserFeedbackForm; + [Static] + [Export ("showUserFeedbackForm")] + void ShowUserFeedbackForm (); + // +(void)addBreadcrumb:(SentryBreadcrumb * _Nonnull)crumb __attribute__((swift_name("addBreadcrumb(_:)"))); [Static] [Export ("addBreadcrumb:")] @@ -1805,16 +1838,6 @@ interface SentrySDK [Export ("close")] void Close (); - // +(void)replayRedactView:(UIView * _Nonnull)view; - [Static] - [Export ("replayRedactView:")] - void ReplayRedactView (UIView view); - - // +(void)replayIgnoreView:(UIView * _Nonnull)view; - [Static] - [Export ("replayIgnoreView:")] - void ReplayIgnoreView (UIView view); - // +(void)startProfiler; [Static] [Export ("startProfiler")] diff --git a/src/Sentry.Bindings.Cocoa/Sentry.Bindings.Cocoa.csproj b/src/Sentry.Bindings.Cocoa/Sentry.Bindings.Cocoa.csproj index 773c19feee..2004d3487f 100644 --- a/src/Sentry.Bindings.Cocoa/Sentry.Bindings.Cocoa.csproj +++ b/src/Sentry.Bindings.Cocoa/Sentry.Bindings.Cocoa.csproj @@ -7,8 +7,9 @@ true true .NET Bindings for the Sentry Cocoa SDK - ..\..\modules\sentry-cocoa\ - $(SentryCocoaSdkDirectory)Carthage\Build-$(TargetPlatformIdentifier)\Sentry.xcframework + ..\..\modules\sentry-cocoa\ + $(SentryCocoaCache)Sentry.framework\ + $(SentryCocoaCache)Sentry-Dynamic.xcframework @@ -44,18 +45,53 @@ - - - + + + + + $([System.IO.File]::ReadAllText("../../modules/sentry-cocoa.properties")) + $([System.Text.RegularExpressions.Regex]::Match($(PropertiesContent), 'version\s*=\s*([^\s]+)').Groups[1].Value) + + + + + + + + + + + + + + + + + + + + + - - + + + - + diff --git a/src/Sentry.Bindings.Cocoa/StructsAndEnums.cs b/src/Sentry.Bindings.Cocoa/StructsAndEnums.cs index 4acb1ce487..7fb471de9c 100644 --- a/src/Sentry.Bindings.Cocoa/StructsAndEnums.cs +++ b/src/Sentry.Bindings.Cocoa/StructsAndEnums.cs @@ -7,6 +7,7 @@ using System.Runtime.InteropServices; using Foundation; using ObjCRuntime; +using Sentry; namespace Sentry.CocoaSdk; From cb67d21ba8a9d1ecc23a853f6be2fc6e77a69316 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Thu, 7 Nov 2024 11:59:26 +1300 Subject: [PATCH 013/363] Fix changelog for cocoa dependency (#3733) --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c1fb939ec6..3027d43a88 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ ### Dependencies +- Bump Cocoa SDK from v8.36.0 to v8.39.0 ([#3727](https://github.com/getsentry/sentry-dotnet/pull/3727)) + - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#8390) + - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.36.0...8.39.0) - Bump Native SDK from v0.7.11 to v0.7.12 ([#3731](https://github.com/getsentry/sentry-dotnet/pull/3731)) - [changelog](https://github.com/getsentry/sentry-native/blob/master/CHANGELOG.md#0712) - [diff](https://github.com/getsentry/sentry-native/compare/0.7.11...0.7.12) @@ -20,9 +23,6 @@ ### Dependencies -- Bump Cocoa SDK from v8.36.0 to v8.39.0 ([#3727](https://github.com/getsentry/sentry-dotnet/pull/3727)) - - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#8390) - - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.36.0...8.39.0) - Bump CLI from v2.38.1 to v2.38.2 ([#3728](https://github.com/getsentry/sentry-dotnet/pull/3728)) - [changelog](https://github.com/getsentry/sentry-cli/blob/master/CHANGELOG.md#2382) - [diff](https://github.com/getsentry/sentry-cli/compare/2.38.1...2.38.2) From 3df18fae27c15b1c2fa7df95592797feb188cdd7 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Thu, 7 Nov 2024 13:58:16 +1300 Subject: [PATCH 014/363] Disable flaky profiling tests --- .../SamplingTransactionProfilerTests.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/Sentry.Profiling.Tests/SamplingTransactionProfilerTests.cs b/test/Sentry.Profiling.Tests/SamplingTransactionProfilerTests.cs index 8d42e1e13a..ab9693307e 100644 --- a/test/Sentry.Profiling.Tests/SamplingTransactionProfilerTests.cs +++ b/test/Sentry.Profiling.Tests/SamplingTransactionProfilerTests.cs @@ -129,6 +129,9 @@ public void Profiler_WithZeroStartupTimeout_CapturesAfterStartingAsynchronously( [InlineData(10)] private void Profiler_SingleProfile_Works(int startTimeoutSeconds) { + // This test is flaky both on CI and locally. + Skip.If(true); + using var factory = new SamplingTransactionProfilerFactory(_testSentryOptions, TimeSpan.FromSeconds(startTimeoutSeconds)); // in the async startup case, we need to wait before collecting if (startTimeoutSeconds == 0) @@ -286,7 +289,8 @@ async Task VerifyAsync(HttpRequestMessage message) [SkippableFact] private async Task Profiler_ThrowingOnSessionStartup_DoesntBreakSentryInit() { - Skip.If(TestEnvironment.IsGitHubActions); + // This test is flaky both on CI and locally. + Skip.If(true); SampleProfilerSession.ThrowOnNextStartupForTests = true; From 69d9a679225286a1e49c9ddcdc9e8fcaee777e61 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Nov 2024 10:05:50 +1300 Subject: [PATCH 015/363] build(deps): bump android-actions/setup-android from 3.2.1 to 3.2.2 (#3741) Bumps [android-actions/setup-android](https://github.com/android-actions/setup-android) from 3.2.1 to 3.2.2. - [Release notes](https://github.com/android-actions/setup-android/releases) - [Commits](https://github.com/android-actions/setup-android/compare/00854ea68c109d98c75d956347303bf7c45b0277...9fc6c4e9069bf8d3d10b2204b1fb8f6ef7065407) --- updated-dependencies: - dependency-name: android-actions/setup-android dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 317dd9327e..0afa61169c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -212,7 +212,7 @@ jobs: java-version: '17' - name: Setup Android SDK - uses: android-actions/setup-android@00854ea68c109d98c75d956347303bf7c45b0277 # v3.2.1 + uses: android-actions/setup-android@9fc6c4e9069bf8d3d10b2204b1fb8f6ef7065407 # v3.2.2 - run: dotnet workload install android maui-android From f7ce11d99865fa4e78777744561a77693bb03922 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Tue, 12 Nov 2024 13:36:16 +1300 Subject: [PATCH 016/363] Fix: ArgumentNullException in FormRequestPayloadExtractor when handling invalid form data on ASP.NET (#3734) --- CHANGELOG.md | 4 ++ .../Internal/SystemWebHttpRequest.cs | 16 ++++++- .../FormRequestPayloadExtractor.cs | 6 +-- .../Internal/SystemWebHttpRequestTests.cs | 48 +++++++++++++++++++ .../BaseRequestPayloadExtractorTests.cs | 3 ++ .../DefaultRequestPayloadExtractorTests.cs | 2 + .../FormRequestPayloadExtractorTests.cs | 13 +++-- 7 files changed, 83 insertions(+), 9 deletions(-) create mode 100644 test/Sentry.AspNet.Tests/Internal/SystemWebHttpRequestTests.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 3027d43a88..4082a81567 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Fixes + +- Fixed ArgumentNullException in FormRequestPayloadExtractor when handling invalid form data on ASP.NET ([#3734](https://github.com/getsentry/sentry-dotnet/pull/3734)) + ### Dependencies - Bump Cocoa SDK from v8.36.0 to v8.39.0 ([#3727](https://github.com/getsentry/sentry-dotnet/pull/3727)) diff --git a/src/Sentry.AspNet/Internal/SystemWebHttpRequest.cs b/src/Sentry.AspNet/Internal/SystemWebHttpRequest.cs index 464e94a040..3e0d384941 100644 --- a/src/Sentry.AspNet/Internal/SystemWebHttpRequest.cs +++ b/src/Sentry.AspNet/Internal/SystemWebHttpRequest.cs @@ -1,3 +1,4 @@ +using System.Collections.Specialized; using Sentry.Extensibility; namespace Sentry.AspNet.Internal; @@ -12,8 +13,19 @@ internal class SystemWebHttpRequest : IHttpRequest public Stream? Body => _request?.InputStream; - public IEnumerable>>? Form - => _request.Form.AllKeys.Select(kv => new KeyValuePair>(kv, _request.Form.GetValues(kv))); + public IEnumerable>>? Form => GetFormData(_request.Form); public SystemWebHttpRequest(HttpRequest request) => _request = request; + + internal static IEnumerable>> GetFormData(NameValueCollection formdata) + { + return StripNulls(formdata.AllKeys).Select(key => new KeyValuePair>( + key, StripNulls(formdata.GetValues(key) + ))); + + // Poorly constructed form submissions can result in null keys/values on .NET Framework. + // See: https://github.com/getsentry/sentry-dotnet/issues/3701 + IEnumerable StripNulls(IEnumerable? values) => values?.Where(x => x is not null) ?? []; + } + } diff --git a/src/Sentry/Extensibility/FormRequestPayloadExtractor.cs b/src/Sentry/Extensibility/FormRequestPayloadExtractor.cs index 2f30953264..29cce79caf 100644 --- a/src/Sentry/Extensibility/FormRequestPayloadExtractor.cs +++ b/src/Sentry/Extensibility/FormRequestPayloadExtractor.cs @@ -10,9 +10,9 @@ public class FormRequestPayloadExtractor : BaseRequestPayloadExtractor /// /// Supports with content type application/x-www-form-urlencoded. /// - protected override bool IsSupported(IHttpRequest request) - => SupportedContentType - .Equals(request.ContentType, StringComparison.InvariantCulture); + protected override bool IsSupported(IHttpRequest request) => + SupportedContentType.Equals(request.ContentType, StringComparison.InvariantCulture) + || (request.ContentType?.StartsWith($"{SupportedContentType};", StringComparison.InvariantCulture) == true); /// /// Extracts the request form data as a dictionary. diff --git a/test/Sentry.AspNet.Tests/Internal/SystemWebHttpRequestTests.cs b/test/Sentry.AspNet.Tests/Internal/SystemWebHttpRequestTests.cs new file mode 100644 index 0000000000..b844195db5 --- /dev/null +++ b/test/Sentry.AspNet.Tests/Internal/SystemWebHttpRequestTests.cs @@ -0,0 +1,48 @@ +using System.Collections.Specialized; +using Sentry.AspNet.Internal; + +namespace Sentry.AspNet.Tests.Internal; + +public class SystemWebHttpRequestTests +{ + [Fact] + public void GetFormData_GoodData_ReturnsCorrectValues() + { + // Arrange + var formCollection = new NameValueCollection + { + { "key1", "value1" }, + { "key2", "value2" } + }; + + // Act + var form = SystemWebHttpRequest.GetFormData(formCollection).ToDict(); + + // Assert + form.Should().NotBeNull(); + form.Should().Contain(kvp => kvp.Key == "key1" && kvp.Value.Contains("value1")); + form.Should().Contain(kvp => kvp.Key == "key2" && kvp.Value.Contains("value2")); + } + + [Fact] + public void GetFormData_BadData_ReturnsCorrectValues() + { + // Arrange + var formCollection = new NameValueCollection + { + { "key1", "value1" }, + { "key2", "value2" }, + { null, "badkey" }, + { "badvalue", null }, + { null, null } + }; + + // Act + var form = SystemWebHttpRequest.GetFormData(formCollection).ToDict(); + + // Assert + form.Should().NotBeNull(); + form.Should().Contain(kvp => kvp.Key == "key1" && kvp.Value.Contains("value1")); + form.Should().Contain(kvp => kvp.Key == "key2" && kvp.Value.Contains("value2")); + } +} diff --git a/test/Sentry.AspNetCore.Tests/BaseRequestPayloadExtractorTests.cs b/test/Sentry.AspNetCore.Tests/BaseRequestPayloadExtractorTests.cs index a13bea393d..64ed508a05 100644 --- a/test/Sentry.AspNetCore.Tests/BaseRequestPayloadExtractorTests.cs +++ b/test/Sentry.AspNetCore.Tests/BaseRequestPayloadExtractorTests.cs @@ -32,6 +32,7 @@ public void ExtractPayload_OriginalStreamPosition_Reset() { const int originalPosition = 100; _ = TestFixture.Stream.Position.Returns(originalPosition); + TestFixture.HttpRequestCore.ContentType.Returns(SupportedContentType); var sut = TestFixture.GetSut(); @@ -40,6 +41,8 @@ public void ExtractPayload_OriginalStreamPosition_Reset() TestFixture.Stream.Received().Position = originalPosition; } + protected abstract string SupportedContentType { get; } + [Fact] public void ExtractPayload_OriginalStream_NotClosed() { diff --git a/test/Sentry.AspNetCore.Tests/DefaultRequestPayloadExtractorTests.cs b/test/Sentry.AspNetCore.Tests/DefaultRequestPayloadExtractorTests.cs index 312dcaa4b6..93bbc59a15 100644 --- a/test/Sentry.AspNetCore.Tests/DefaultRequestPayloadExtractorTests.cs +++ b/test/Sentry.AspNetCore.Tests/DefaultRequestPayloadExtractorTests.cs @@ -2,6 +2,8 @@ namespace Sentry.AspNetCore.Tests; public class DefaultRequestPayloadExtractorTests : BaseRequestPayloadExtractorTests { + protected override string SupportedContentType => string.Empty; + [Fact] public void ExtractPayload_StringData_ReadCorrectly() { diff --git a/test/Sentry.AspNetCore.Tests/FormRequestPayloadExtractorTests.cs b/test/Sentry.AspNetCore.Tests/FormRequestPayloadExtractorTests.cs index ba7f066d02..70c44675ed 100644 --- a/test/Sentry.AspNetCore.Tests/FormRequestPayloadExtractorTests.cs +++ b/test/Sentry.AspNetCore.Tests/FormRequestPayloadExtractorTests.cs @@ -5,15 +5,20 @@ namespace Sentry.AspNetCore.Tests; public class FormRequestPayloadExtractorTests : BaseRequestPayloadExtractorTests { + protected override string SupportedContentType => "application/x-www-form-urlencoded"; + public FormRequestPayloadExtractorTests() { TestFixture = new Fixture(); - _ = TestFixture.HttpRequest.ContentType.Returns("application/x-www-form-urlencoded"); } - [Fact] - public void ExtractPayload_SupportedContentType_ReadForm() + [Theory] + [InlineData("application/x-www-form-urlencoded")] + [InlineData("application/x-www-form-urlencoded; charset=utf-8")] + public void ExtractPayload_SupportedContentType_ReadForm(string contentType) { + TestFixture.HttpRequest.ContentType.Returns(contentType); + var expected = new Dictionary { { "key", new StringValues("val") } }; var f = new FormCollection(expected); _ = TestFixture.HttpRequestCore.Form.Returns(f); @@ -32,7 +37,7 @@ public void ExtractPayload_SupportedContentType_ReadForm() [Fact] public void ExtractPayload_UnsupportedContentType_DoesNotReadStream() { - _ = TestFixture.HttpRequest.ContentType.Returns("application/json"); + TestFixture.HttpRequest.ContentType.Returns("application/json"); var sut = TestFixture.GetSut(); From 268462f9347433a1946227c1b2fba2e25b109d64 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos <6349682+vaind@users.noreply.github.com> Date: Tue, 12 Nov 2024 14:58:11 +0100 Subject: [PATCH 017/363] chore: fix CHANGELOG.md formatting (#3746) --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4082a81567..a34d770031 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,8 +9,8 @@ ### Dependencies - Bump Cocoa SDK from v8.36.0 to v8.39.0 ([#3727](https://github.com/getsentry/sentry-dotnet/pull/3727)) - - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#8390) - - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.36.0...8.39.0) + - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#8390) + - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.36.0...8.39.0) - Bump Native SDK from v0.7.11 to v0.7.12 ([#3731](https://github.com/getsentry/sentry-dotnet/pull/3731)) - [changelog](https://github.com/getsentry/sentry-native/blob/master/CHANGELOG.md#0712) - [diff](https://github.com/getsentry/sentry-native/compare/0.7.11...0.7.12) From 4eedecfff55144c2c8d76fee8541cb7f35a55275 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Wed, 13 Nov 2024 22:56:54 +1300 Subject: [PATCH 018/363] Only regenerate the cocoa bindings when they change (#3742) --- src/Sentry.Bindings.Cocoa/Sentry.Bindings.Cocoa.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Sentry.Bindings.Cocoa/Sentry.Bindings.Cocoa.csproj b/src/Sentry.Bindings.Cocoa/Sentry.Bindings.Cocoa.csproj index 2004d3487f..faf8a45263 100644 --- a/src/Sentry.Bindings.Cocoa/Sentry.Bindings.Cocoa.csproj +++ b/src/Sentry.Bindings.Cocoa/Sentry.Bindings.Cocoa.csproj @@ -91,6 +91,7 @@ From ba94729d926df4b9e47b72cd06b92e96a8ac7df3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 14 Nov 2024 09:22:49 +1300 Subject: [PATCH 019/363] build(deps): bump github/codeql-action from 3.27.0 to 3.27.1 (#3740) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.27.0 to 3.27.1. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/662472033e021d55d94146f66f6058822b0b39fd...4f3212b61783c3c68e8309a0f18a699764811cda) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 4ba74d79e1..4c20bd7d67 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -35,7 +35,7 @@ jobs: uses: ./.github/actions/environment - name: Initialize CodeQL - uses: github/codeql-action/init@662472033e021d55d94146f66f6058822b0b39fd # pin@v2 + uses: github/codeql-action/init@4f3212b61783c3c68e8309a0f18a699764811cda # pin@v2 with: languages: csharp @@ -46,6 +46,6 @@ jobs: run: dotnet build Sentry-CI-CodeQL.slnf --no-restore --nologo - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@662472033e021d55d94146f66f6058822b0b39fd # pin@v2 + uses: github/codeql-action/analyze@4f3212b61783c3c68e8309a0f18a699764811cda # pin@v2 with: category: '/language:csharp' From b47866510738f6fec8291a42761aa5351e38abec Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Fri, 15 Nov 2024 19:13:19 +1300 Subject: [PATCH 020/363] Pin macos-15 build runner (#3754) --- .github/actions/environment/action.yml | 23 +++++++++- .github/workflows/build.yml | 58 +++++++++----------------- .github/workflows/format-code.yml | 10 ++--- .github/workflows/vulnerabilities.yml | 3 +- integration-test/cli.Tests.ps1 | 9 ++++ 5 files changed, 57 insertions(+), 46 deletions(-) diff --git a/.github/actions/environment/action.yml b/.github/actions/environment/action.yml index 4b39841de0..ac61ac303b 100644 --- a/.github/actions/environment/action.yml +++ b/.github/actions/environment/action.yml @@ -22,14 +22,35 @@ runs: with: log-accepted-android-sdk-licenses: false + # https://github.com/actions/runner-images/issues/10814 + - name: Workaround build-tools issue + if: runner.os == 'macOS' + shell: bash + run: | + curl https://dl.google.com/android/repository/build-tools_r35_macosx.zip > $ANDROID_HOME/build-tools_r35_macosx.zip + cd $ANDROID_HOME + mkdir -p build-tools + unzip build-tools_r35_macosx.zip + mv android-15 build-tools/35.0.0 + cd - + - name: Set Java Version uses: actions/setup-java@v3 with: distribution: 'temurin' java-version: '11' - # .NET 6 and .NET 8 are not built-in with macos-13 + - name: Install Mono + if: runner.os == 'macOS' + shell: bash + # Attempt to install Mono, allowing it to fail silently + # Then ensure mono is correctly linked, overwriting any conflicting symlinks + run: | + brew install mono || true + brew link --overwrite mono + - name: Install .NET SDK + if: runner.os != 'Windows' uses: actions/setup-dotnet@v3 with: dotnet-version: | diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0afa61169c..967fd16fa0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -15,11 +15,12 @@ env: jobs: build-sentry-native: name: sentry-native (${{ matrix.os }}) - runs-on: ${{ matrix.os }}-latest + runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: - os: [ubuntu, windows, macos] + # Pin macos to get the version of XCode that we need: https://github.com/actions/runner-images/issues/10703 + os: [ubuntu-latest, windows-latest, macos-15] steps: - name: Checkout @@ -35,7 +36,7 @@ jobs: enableCrossOsArchive: true - name: Free Disk Space (Ubuntu) - if: matrix.os == 'ubuntu-latest' + if: runner.os == 'Linux' uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be with: android: true @@ -63,7 +64,8 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest, windows-latest, macos-latest] + # Pin macos to get the version of XCode that we need: https://github.com/actions/runner-images/issues/10703 + os: [ubuntu-latest, windows-latest, macos-15] steps: - name: Cancel Previous Runs @@ -71,23 +73,11 @@ jobs: uses: styfle/cancel-workflow-action@85880fa0301c86cca9da44039ee3bb12d3bedbfa # Tag: 0.12.1 - name: Setup Xcode - if: matrix.os == 'macos-latest' + if: runner.os == 'macOS' run: | sudo xcode-select --switch /Applications/Xcode_${{env.XCODE_VERSION}}.app/Contents/Developer xcodebuild -version - # We only use Xcode 16 - - name: Remove unused applications - if: matrix.os == 'macos-latest' - run: | - df -hI /dev/disk3s1s1 - sudo rm -rf /Applications/Xcode_14.3.1.app - sudo rm -rf /Applications/Xcode_15.0.1.app - sudo rm -rf /Applications/Xcode_15.1.app - sudo rm -rf /Applications/Xcode_15.2.app - sudo rm -rf /Applications/Xcode_15.3.app - df -hI /dev/disk3s1s1 - - name: Checkout uses: actions/checkout@v4 with: @@ -96,7 +86,7 @@ jobs: # We use macOS for the final publishing build so we we get all the iOS/macCatalyst targets in the packages - name: Set Environment Variables - if: startsWith(matrix.os, 'macos') + if: runner.os == 'macOS' run: echo "CI_PUBLISHING_BUILD=true" >> $GITHUB_ENV - name: Download sentry-native (Linux) @@ -133,6 +123,12 @@ jobs: - name: Restore .NET Dependencies run: dotnet restore Sentry-CI-Build-${{ runner.os }}.slnf --nologo + - name: Install Android SDKs + if: runner.os == 'macOS' + run: | + dotnet build src/Sentry/Sentry.csproj -t:InstallAndroidDependencies -f:net7.0-android -p:AcceptAndroidSDKLicenses=True -p:AndroidSdkPath="/usr/local/lib/android/sdk/" + dotnet build src/Sentry/Sentry.csproj -t:InstallAndroidDependencies -f:net8.0-android -p:AcceptAndroidSDKLicenses=True -p:AndroidSdkPath="/usr/local/lib/android/sdk/" + - name: Build run: dotnet build Sentry-CI-Build-${{ runner.os }}.slnf -c Release --no-restore --nologo -v:minimal -flp:logfile=build.log -p:CopyLocalLockFileAssemblies=true @@ -173,7 +169,8 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest, windows-latest, macos-latest] + # Pin macos to get the version of XCode that we need: https://github.com/actions/runner-images/issues/10703 + os: [ubuntu-latest, windows-latest, macos-15] steps: - uses: actions/checkout@v4 @@ -183,6 +180,7 @@ jobs: sparse-checkout: | Directory.Build.props integration-test + .github - name: Fetch Nuget Packages uses: actions/download-artifact@v4 with: @@ -195,26 +193,8 @@ jobs: sudo apt update sudo apt install libcurl4-openssl-dev - - uses: actions/setup-dotnet@v4 - with: - dotnet-version: 8.0.x - - - name: Setup Xcode - if: matrix.os == 'macos-latest' - run: | - sudo xcode-select --switch /Applications/Xcode_${{env.XCODE_VERSION}}.app/Contents/Developer - xcodebuild -version - - # Needed for Android SDK setup step - - uses: actions/setup-java@v4 - with: - distribution: 'temurin' - java-version: '17' - - - name: Setup Android SDK - uses: android-actions/setup-android@9fc6c4e9069bf8d3d10b2204b1fb8f6ef7065407 # v3.2.2 - - - run: dotnet workload install android maui-android + - name: Setup Environment + uses: ./.github/actions/environment - name: Test uses: getsentry/github-workflows/sentry-cli/integration-test/@v2 diff --git a/.github/workflows/format-code.yml b/.github/workflows/format-code.yml index 9a867477be..453d598fbc 100644 --- a/.github/workflows/format-code.yml +++ b/.github/workflows/format-code.yml @@ -10,9 +10,9 @@ on: jobs: format-code: name: Format Code - # Running on 'macos' because Linux is missing the `ios` workload - # See: https://github.com/dotnet/runtime/issues/85505 - runs-on: macos-latest + # Run on 'macos' because Linux is missing the `ios` workload: https://github.com/dotnet/runtime/issues/85505 + # Pin macos to get the version of XCode that we need: https://github.com/actions/runner-images/issues/10703 + runs-on: macos-15 steps: - name: Checkout uses: actions/checkout@v4 @@ -29,7 +29,7 @@ jobs: run: dotnet tool install -g dotnet-format - name: Format Code - # We're excluding `./**/*OptionsSetup.cs` from the format because the tool struggles with + # We're excluding `./**/*OptionsSetup.cs` from the format because the tool struggles with # source generators run: dotnet format Sentry.sln --no-restore --exclude ./modules --exclude ./**/*OptionsSetup.cs @@ -37,4 +37,4 @@ jobs: # we need to pass the current branch, otherwise we can't commit the changes. # GITHUB_HEAD_REF is the name of the head branch. GitHub Actions only sets this for PRs. - name: Commit Formatted Code - run: ./scripts/commit-formatted-code.sh $GITHUB_HEAD_REF \ No newline at end of file + run: ./scripts/commit-formatted-code.sh $GITHUB_HEAD_REF diff --git a/.github/workflows/vulnerabilities.yml b/.github/workflows/vulnerabilities.yml index 24e0684fc7..d7f1d994f5 100644 --- a/.github/workflows/vulnerabilities.yml +++ b/.github/workflows/vulnerabilities.yml @@ -10,7 +10,8 @@ on: jobs: list-vulnerable-packages: name: List vulnerable packages - runs-on: macos-latest + # Pin macos to get the version of XCode that we need: https://github.com/actions/runner-images/issues/10703 + runs-on: macos-15 steps: - name: Checkout diff --git a/integration-test/cli.Tests.ps1 b/integration-test/cli.Tests.ps1 index 54b4102a9b..bd9d26ff78 100644 --- a/integration-test/cli.Tests.ps1 +++ b/integration-test/cli.Tests.ps1 @@ -141,6 +141,15 @@ Describe 'MAUI' -ForEach @( } AddPackageReference $name 'Sentry.Maui' + + if (Test-Path env:CI) + { + dotnet build $name/$name.csproj -t:InstallAndroidDependencies -f:$framework-android -p:AcceptAndroidSDKLicenses=True -p:AndroidSdkPath="/usr/local/lib/android/sdk/" | ForEach-Object { Write-Host $_ } + if ($LASTEXITCODE -ne 0) + { + throw "Failed to install android dependencies." + } + } } It "uploads symbols and sources for an Android build" { From 6c3eb137bffb7bcca5300b3a487e9bd7ec628e84 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Fri, 15 Nov 2024 22:05:19 +1300 Subject: [PATCH 021/363] Update action.yml --- .github/actions/freediskspace/action.yml | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/.github/actions/freediskspace/action.yml b/.github/actions/freediskspace/action.yml index e2c75b64c4..584430d227 100644 --- a/.github/actions/freediskspace/action.yml +++ b/.github/actions/freediskspace/action.yml @@ -33,16 +33,3 @@ runs: sudo apt-get autoremove -y || echo "::warning::The command [sudo apt-get autoremove -y] failed to complete successfully. Proceeding..." sudo apt-get clean || echo "::warning::The command [sudo apt-get clean] failed to complete successfully. Proceeding..." df -h /dev/root - - # We only use Xcode 16 - - name: Remove unused applications - if: runner.os == 'macOS' - shell: bash - run: | - df -hI /dev/disk3s1s1 - sudo rm -rf /Applications/Xcode_14.3.1.app - sudo rm -rf /Applications/Xcode_15.0.1.app - sudo rm -rf /Applications/Xcode_15.1.app - sudo rm -rf /Applications/Xcode_15.2.app - sudo rm -rf /Applications/Xcode_15.3.app - df -hI /dev/disk3s1s1 From 0e610deea2bcd1ee5770de72db9e2e7d2f1b9653 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Fri, 15 Nov 2024 22:14:17 +1300 Subject: [PATCH 022/363] Install .NET 9 on the windows runners --- .github/actions/environment/action.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/actions/environment/action.yml b/.github/actions/environment/action.yml index 1d9f18f01d..ef4c183d5b 100644 --- a/.github/actions/environment/action.yml +++ b/.github/actions/environment/action.yml @@ -59,6 +59,13 @@ runs: 8.0.x 9.0.100-rc.2.24474.11 + - name: Install .NET 9 + if: runner.os == 'Windows' + uses: actions/setup-dotnet@v4 + with: + dotnet-version: | + 9.0.100-rc.2.24474.11 + - name: Install .NET Workloads shell: bash run: > From 6de0d2a354461d3247b9ad00dbe7502f982ff1d4 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Fri, 15 Nov 2024 22:59:53 +1300 Subject: [PATCH 023/363] Run ios device tests on macos-15 --- .github/workflows/device-tests-ios.yml | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/.github/workflows/device-tests-ios.yml b/.github/workflows/device-tests-ios.yml index 288f032e9d..3474a210c5 100644 --- a/.github/workflows/device-tests-ios.yml +++ b/.github/workflows/device-tests-ios.yml @@ -9,12 +9,9 @@ on: paths-ignore: - "**.md" -env: - XCODE_VERSION: 16 - jobs: ios-tests: - runs-on: macos-latest + runs-on: macos-15 env: DOTNET_CLI_TELEMETRY_OPTOUT: 1 DOTNET_NOLOGO: 1 @@ -33,11 +30,6 @@ jobs: - name: Setup Environment uses: ./.github/actions/environment - - name: Setup Xcode - run: | - sudo xcode-select --switch /Applications/Xcode_${{env.XCODE_VERSION}}.app/Contents/Developer - xcodebuild -version - - name: Build iOS Test App run: pwsh ./scripts/device-test.ps1 ios -Build From 3d05ea4a1fad086e7861d664da4dcf9c99d97de7 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Fri, 15 Nov 2024 23:00:20 +1300 Subject: [PATCH 024/363] Remove net7.0 Android SDKs from build runners --- .github/workflows/build.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2cdd65244f..ed031f1ee7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -121,7 +121,6 @@ jobs: - name: Install Android SDKs if: runner.os == 'macOS' run: | - dotnet build src/Sentry/Sentry.csproj -t:InstallAndroidDependencies -f:net7.0-android -p:AcceptAndroidSDKLicenses=True -p:AndroidSdkPath="/usr/local/lib/android/sdk/" dotnet build src/Sentry/Sentry.csproj -t:InstallAndroidDependencies -f:net8.0-android -p:AcceptAndroidSDKLicenses=True -p:AndroidSdkPath="/usr/local/lib/android/sdk/" - name: Build From cbe1af4c722b22eb6ee0cbe5c70b25a8d7056d41 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 15 Nov 2024 13:19:24 +0100 Subject: [PATCH 025/363] chore: update scripts/update-java.ps1 to 7.17.0 (#3749) --- CHANGELOG.md | 3 +++ src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a34d770031..1249f0ee13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,9 @@ - Bump Native SDK from v0.7.11 to v0.7.12 ([#3731](https://github.com/getsentry/sentry-dotnet/pull/3731)) - [changelog](https://github.com/getsentry/sentry-native/blob/master/CHANGELOG.md#0712) - [diff](https://github.com/getsentry/sentry-native/compare/0.7.11...0.7.12) +- Bump Java SDK from v7.16.0 to v7.17.0 ([#3749](https://github.com/getsentry/sentry-dotnet/pull/3749)) + - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#7170) + - [diff](https://github.com/getsentry/sentry-java/compare/7.16.0...7.17.0) ## 4.13.0 diff --git a/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj b/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj index a0eb9f33bc..75cb692443 100644 --- a/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj +++ b/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj @@ -3,7 +3,7 @@ net7.0-android $(NoWarn);BG8605;BG8606 - 7.16.0 + 7.17.0 $(BaseIntermediateOutputPath)sdks\Sentry\Android\$(SentryAndroidSdkVersion)\ .NET Bindings for the Sentry Android SDK From a4074a6b829f1c47717486ef15e64e812d6833e4 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Sat, 16 Nov 2024 11:01:53 +1300 Subject: [PATCH 026/363] Upgrade to .NET 9.0.100 (#3753) * Upgrade to .NET 9.0.100 * Update action.yml * Bump Android SDK version for net9.0 support * Run ios device tests on macos-15 * Update build.yml --- .github/actions/environment/action.yml | 4 ++-- .github/workflows/build.yml | 10 +--------- Directory.Build.props | 4 ++-- Directory.Build.targets | 4 ++-- global.json | 2 +- .../Sentry.Maui.Device.TestApp.csproj | 6 +++--- 6 files changed, 11 insertions(+), 19 deletions(-) diff --git a/.github/actions/environment/action.yml b/.github/actions/environment/action.yml index ef4c183d5b..1630160069 100644 --- a/.github/actions/environment/action.yml +++ b/.github/actions/environment/action.yml @@ -57,14 +57,14 @@ runs: 6.0.x 7.0.x 8.0.x - 9.0.100-rc.2.24474.11 + 9.0.100 - name: Install .NET 9 if: runner.os == 'Windows' uses: actions/setup-dotnet@v4 with: dotnet-version: | - 9.0.100-rc.2.24474.11 + 9.0.100 - name: Install .NET Workloads shell: bash diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ed031f1ee7..e5d4464afa 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -9,9 +9,6 @@ on: paths-ignore: - "**.md" -env: - XCODE_VERSION: 16 - jobs: build-sentry-native: name: sentry-native (${{ matrix.os }}) @@ -70,12 +67,6 @@ jobs: submodules: recursive fetch-depth: 2 # default is 1 and codecov needs > 1 - - name: Setup Xcode - if: matrix.os == 'macos-latest' - run: | - sudo xcode-select --switch /Applications/Xcode_${{env.XCODE_VERSION}}.app/Contents/Developer - xcodebuild -version - - name: Remove unused applications uses: ./.github/actions/freediskspace @@ -175,6 +166,7 @@ jobs: Directory.Build.props integration-test .github + - name: Fetch Nuget Packages uses: actions/download-artifact@v4 with: diff --git a/Directory.Build.props b/Directory.Build.props index f4bef87ba0..d439f8d5ad 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -47,8 +47,8 @@ --> $([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) - 11.0 - 13.1 + 12.2 + 15.0 21.0 10.0.17763.0 10.0.17763.0 diff --git a/Directory.Build.targets b/Directory.Build.targets index 5e9d4873c2..7593967e09 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -10,8 +10,8 @@ --> $([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) - 11.0 - 13.1 + 12.2 + 15.0 21.0 10.0.17763.0 10.0.17763.0 diff --git a/global.json b/global.json index 102a17f7b9..40584df3e9 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "9.0.100-rc.2.24474.11", + "version": "9.0.100", "rollForward": "latestMinor", "allowPrerelease": false } diff --git a/test/Sentry.Maui.Device.TestApp/Sentry.Maui.Device.TestApp.csproj b/test/Sentry.Maui.Device.TestApp/Sentry.Maui.Device.TestApp.csproj index 6ca1bad83b..35bd519823 100644 --- a/test/Sentry.Maui.Device.TestApp/Sentry.Maui.Device.TestApp.csproj +++ b/test/Sentry.Maui.Device.TestApp/Sentry.Maui.Device.TestApp.csproj @@ -64,9 +64,9 @@ - - - + + + From ec3e21f9895a1974b8317b1d9a4f8fe862df5809 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Sat, 16 Nov 2024 20:36:14 +1300 Subject: [PATCH 027/363] Bumped System.Text.Jason (vulnerability) --- .../Sentry.Samples.AspNetCore.Blazor.Wasm.csproj | 4 ++-- .../Sentry.Samples.GenericHost.csproj | 4 ++-- .../Sentry.DiagnosticSource.Tests.csproj | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/samples/Sentry.Samples.AspNetCore.Blazor.Wasm/Sentry.Samples.AspNetCore.Blazor.Wasm.csproj b/samples/Sentry.Samples.AspNetCore.Blazor.Wasm/Sentry.Samples.AspNetCore.Blazor.Wasm.csproj index 113bdd422e..c64bb41a0a 100644 --- a/samples/Sentry.Samples.AspNetCore.Blazor.Wasm/Sentry.Samples.AspNetCore.Blazor.Wasm.csproj +++ b/samples/Sentry.Samples.AspNetCore.Blazor.Wasm/Sentry.Samples.AspNetCore.Blazor.Wasm.csproj @@ -21,7 +21,7 @@ - - + + diff --git a/samples/Sentry.Samples.GenericHost/Sentry.Samples.GenericHost.csproj b/samples/Sentry.Samples.GenericHost/Sentry.Samples.GenericHost.csproj index 1081c24c62..78283ed704 100644 --- a/samples/Sentry.Samples.GenericHost/Sentry.Samples.GenericHost.csproj +++ b/samples/Sentry.Samples.GenericHost/Sentry.Samples.GenericHost.csproj @@ -21,7 +21,7 @@ - - + + diff --git a/test/Sentry.DiagnosticSource.Tests/Sentry.DiagnosticSource.Tests.csproj b/test/Sentry.DiagnosticSource.Tests/Sentry.DiagnosticSource.Tests.csproj index 1e7bf12162..500062fcdc 100644 --- a/test/Sentry.DiagnosticSource.Tests/Sentry.DiagnosticSource.Tests.csproj +++ b/test/Sentry.DiagnosticSource.Tests/Sentry.DiagnosticSource.Tests.csproj @@ -8,16 +8,16 @@ - - + + - - + + From ed36c19d659b10d2f5e8365d300f42ee70a23b31 Mon Sep 17 00:00:00 2001 From: Victor Hugo do Nascimento Date: Sun, 17 Nov 2024 17:46:32 -0300 Subject: [PATCH 028/363] Prevent NullReferenceException in SentryTraceHeader Parsing (#3757) --- CHANGELOG.md | 4 ++++ src/Sentry/SentryTraceHeader.cs | 19 +++++++++++++++++-- ...piApprovalTests.Run.DotNet6_0.verified.txt | 2 +- ...piApprovalTests.Run.DotNet7_0.verified.txt | 2 +- ...piApprovalTests.Run.DotNet8_0.verified.txt | 2 +- ...piApprovalTests.Run.DotNet9_0.verified.txt | 2 +- .../ApiApprovalTests.Run.Net4_8.verified.txt | 2 +- .../Protocol/SentryTraceHeaderTests.cs | 13 +++++++++++++ 8 files changed, 39 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cfeccd882b..4e2279d98d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,10 @@ - Added support for `.NET 9` (preview) ([#3699](https://github.com/getsentry/sentry-dotnet/pull/3699)) - libsentrysupplemental.so now supports 16 KB page sizes on Android ([#3723](https://github.com/getsentry/sentry-dotnet/pull/3723)) +### Fixes + +- Fixed NullReferenceException in SentryTraceHeader when parsing null or empty values ([#3757](https://github.com/getsentry/sentry-dotnet/pull/3757)) + ## Unreleased ### Fixes diff --git a/src/Sentry/SentryTraceHeader.cs b/src/Sentry/SentryTraceHeader.cs index 212deb99bd..ed4d35c372 100644 --- a/src/Sentry/SentryTraceHeader.cs +++ b/src/Sentry/SentryTraceHeader.cs @@ -40,10 +40,25 @@ public override string ToString() => IsSampled is { } isSampled : $"{TraceId}-{SpanId}"; /// - /// Parses from string. + /// Parses a from a string representation of the Sentry trace header. /// - public static SentryTraceHeader Parse(string value) + /// + /// A string containing the Sentry trace header, expected to follow the format "traceId-spanId-sampled", + /// where "sampled" is optional. + /// + /// + /// A object if parsing succeeds, or null if the input string is null, empty, or whitespace. + /// + /// + /// Thrown if the input string does not contain a valid trace header format, specifically if it lacks required trace ID and span ID components. + /// + public static SentryTraceHeader? Parse(string value) { + if (string.IsNullOrWhiteSpace(value)) + { + return null; + } + var components = value.Split('-', StringSplitOptions.RemoveEmptyEntries); if (components.Length < 2) { diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt index 225135ec55..54518243dc 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt @@ -911,7 +911,7 @@ namespace Sentry public Sentry.SpanId SpanId { get; } public Sentry.SentryId TraceId { get; } public override string ToString() { } - public static Sentry.SentryTraceHeader Parse(string value) { } + public static Sentry.SentryTraceHeader? Parse(string value) { } } public class SentryTransaction : Sentry.IEventLike, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISentryJsonSerializable, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.Protocol.ITraceContext { diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt index 225135ec55..54518243dc 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt @@ -911,7 +911,7 @@ namespace Sentry public Sentry.SpanId SpanId { get; } public Sentry.SentryId TraceId { get; } public override string ToString() { } - public static Sentry.SentryTraceHeader Parse(string value) { } + public static Sentry.SentryTraceHeader? Parse(string value) { } } public class SentryTransaction : Sentry.IEventLike, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISentryJsonSerializable, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.Protocol.ITraceContext { diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt index 5d13ca7595..d10a1dd45a 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt @@ -913,7 +913,7 @@ namespace Sentry public Sentry.SpanId SpanId { get; } public Sentry.SentryId TraceId { get; } public override string ToString() { } - public static Sentry.SentryTraceHeader Parse(string value) { } + public static Sentry.SentryTraceHeader? Parse(string value) { } } public class SentryTransaction : Sentry.IEventLike, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISentryJsonSerializable, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.Protocol.ITraceContext { diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt index 5d13ca7595..d10a1dd45a 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt @@ -913,7 +913,7 @@ namespace Sentry public Sentry.SpanId SpanId { get; } public Sentry.SentryId TraceId { get; } public override string ToString() { } - public static Sentry.SentryTraceHeader Parse(string value) { } + public static Sentry.SentryTraceHeader? Parse(string value) { } } public class SentryTransaction : Sentry.IEventLike, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISentryJsonSerializable, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.Protocol.ITraceContext { diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt index 5c6899448c..460ab5309d 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt @@ -908,7 +908,7 @@ namespace Sentry public Sentry.SpanId SpanId { get; } public Sentry.SentryId TraceId { get; } public override string ToString() { } - public static Sentry.SentryTraceHeader Parse(string value) { } + public static Sentry.SentryTraceHeader? Parse(string value) { } } public class SentryTransaction : Sentry.IEventLike, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISentryJsonSerializable, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.Protocol.ITraceContext { diff --git a/test/Sentry.Tests/Protocol/SentryTraceHeaderTests.cs b/test/Sentry.Tests/Protocol/SentryTraceHeaderTests.cs index 0c8ff348e1..11b46dc58c 100644 --- a/test/Sentry.Tests/Protocol/SentryTraceHeaderTests.cs +++ b/test/Sentry.Tests/Protocol/SentryTraceHeaderTests.cs @@ -46,4 +46,17 @@ public void Parse_WithSampledFalse_Works() header.SpanId.Should().Be(SpanId.Parse("1000000000000000")); header.IsSampled.Should().BeFalse(); } + + [Theory] + [InlineData(null)] + [InlineData("")] + [InlineData(" ")] + public void Parse_WithoutHeaderValue_ReturnsNull(string headerValue) + { + // Act + var header = SentryTraceHeader.Parse(headerValue); + + // Assert + header.Should().BeNull(); + } } From 3fde00a69fae336e7d369a80ecd3bbc01e7b1d2d Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Tue, 19 Nov 2024 21:01:13 +1300 Subject: [PATCH 029/363] Automatically capture heap dumps (#3667) --- .generated.NoMobile.sln | 7 + CHANGELOG.md | 1 + Directory.Build.props | 9 + Sentry-CI-Build-Linux.slnf | 1 + Sentry-CI-Build-Windows.slnf | 1 + Sentry-CI-Build-macOS.slnf | 1 + Sentry.sln | 7 + Sentry.sln.DotSettings | 1 + SentryNoMobile.slnf | 1 + samples/Directory.Build.props | 2 +- .../Sentry.Samples.Console.Basic/Program.cs | 6 +- .../Program.cs | 72 ++++++ .../Sentry.Samples.Console.HeapDump.csproj | 55 ++++ .../runtimeconfig.template.json | 6 + src/Sentry/Debouncer.cs | 103 ++++++++ src/Sentry/HeapDumpTriggers.cs | 27 ++ .../Internal/GarbageCollectionMonitor.cs | 39 +++ src/Sentry/Internal/HeapDumpOptions.cs | 3 + src/Sentry/Internal/Hub.cs | 50 ++++ src/Sentry/Internal/IGCImplementation.cs | 12 + src/Sentry/Internal/MemoryMonitor.cs | 195 ++++++++++++++ src/Sentry/Internal/SystemGCImplementation.cs | 29 +++ src/Sentry/SentryOptions.cs | 48 ++++ test/Sentry.Hangfire.Tests/HangfireTests.cs | 6 +- test/Sentry.Testing/BindableTests.cs | 1 + .../Sentry.Testing/SentryOptionsExtensions.cs | 70 +++++ ...piApprovalTests.Run.DotNet6_0.verified.txt | 10 + ...piApprovalTests.Run.DotNet7_0.verified.txt | 10 + ...piApprovalTests.Run.DotNet8_0.verified.txt | 10 + ...piApprovalTests.Run.DotNet9_0.verified.txt | 10 + .../ApiApprovalTests.Run.Net4_8.verified.txt | 8 + test/Sentry.Tests/DebouncerTests.cs | 144 +++++++++++ .../GarbageCollectionMonitorTests.cs | 73 ++++++ .../Internals/MemoryMonitorTests.cs | 242 ++++++++++++++++++ .../Internals/SentryStackTraceFactoryTests.cs | 4 +- .../Protocol/Envelopes/EnvelopeTests.cs | 2 +- 36 files changed, 1256 insertions(+), 10 deletions(-) create mode 100644 samples/Sentry.Samples.Console.HeapDump/Program.cs create mode 100644 samples/Sentry.Samples.Console.HeapDump/Sentry.Samples.Console.HeapDump.csproj create mode 100644 samples/Sentry.Samples.Console.HeapDump/runtimeconfig.template.json create mode 100644 src/Sentry/Debouncer.cs create mode 100644 src/Sentry/HeapDumpTriggers.cs create mode 100644 src/Sentry/Internal/GarbageCollectionMonitor.cs create mode 100644 src/Sentry/Internal/HeapDumpOptions.cs create mode 100644 src/Sentry/Internal/IGCImplementation.cs create mode 100644 src/Sentry/Internal/MemoryMonitor.cs create mode 100644 src/Sentry/Internal/SystemGCImplementation.cs create mode 100644 test/Sentry.Testing/SentryOptionsExtensions.cs create mode 100644 test/Sentry.Tests/DebouncerTests.cs create mode 100644 test/Sentry.Tests/Internals/GarbageCollectionMonitorTests.cs create mode 100644 test/Sentry.Tests/Internals/MemoryMonitorTests.cs diff --git a/.generated.NoMobile.sln b/.generated.NoMobile.sln index 82631572fc..a90cf85ad2 100644 --- a/.generated.NoMobile.sln +++ b/.generated.NoMobile.sln @@ -178,6 +178,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "root", "root", "{233D34AB-9 EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.AspNetCore.Blazor.WebAssembly", "src\Sentry.AspNetCore.Blazor.WebAssembly\Sentry.AspNetCore.Blazor.WebAssembly.csproj", "{8298202C-9983-4D0A-851D-805539EE481A}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Samples.Console.HeapDump", "samples\Sentry.Samples.Console.HeapDump\Sentry.Samples.Console.HeapDump.csproj", "{D7DF0B26-AD43-4F8B-9BFE-C4471CCC9821}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -493,6 +495,10 @@ Global {A5B26C14-7313-4EDC-91E3-287F9374AB75}.Debug|Any CPU.Build.0 = Debug|Any CPU {A5B26C14-7313-4EDC-91E3-287F9374AB75}.Release|Any CPU.ActiveCfg = Release|Any CPU {A5B26C14-7313-4EDC-91E3-287F9374AB75}.Release|Any CPU.Build.0 = Release|Any CPU + {D7DF0B26-AD43-4F8B-9BFE-C4471CCC9821}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D7DF0B26-AD43-4F8B-9BFE-C4471CCC9821}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D7DF0B26-AD43-4F8B-9BFE-C4471CCC9821}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D7DF0B26-AD43-4F8B-9BFE-C4471CCC9821}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -575,5 +581,6 @@ Global {46E40BE8-1AB0-4846-B0A2-A40AD0272C64} = {6987A1CC-608E-4868-A02C-09D30C8B7B2D} {8298202C-9983-4D0A-851D-805539EE481A} = {230B9384-90FD-4551-A5DE-1A5C197F25B6} {A5B26C14-7313-4EDC-91E3-287F9374AB75} = {21B42F60-5802-404E-90F0-AEBCC56760C0} + {D7DF0B26-AD43-4F8B-9BFE-C4471CCC9821} = {21B42F60-5802-404E-90F0-AEBCC56760C0} EndGlobalSection EndGlobal diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e2279d98d..8030297552 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - `SentryOptions.EnableTracing` has been removed. Instead, tracing should be enabled or disabled by setting the `SentryOptions.TracesSampleRate` or by using `SentryOptions.TracesSampler` to configure a sampling function ([#3569](https://github.com/getsentry/sentry-dotnet/pull/3569)) - The `FailedRequestTargets`, `TagFilters` and `TracePropagationTargets` options have all been changed from `SubstringOrRegexPattern` to `IList` ([#3566](https://github.com/getsentry/sentry-dotnet/pull/3566)) - `Scope.Transaction` is now always stored as an `AsyncLocal` also in [Global Mode](https://docs.sentry.io/platforms/dotnet/configuration/options/#is-global-mode-enabled), to prevent auto-instrumented spans from the UI ending up parented to transactions from a background task (or vice versa). ([#3596](https://github.com/getsentry/sentry-dotnet/pull/3596)) +- Heap dumps can be captured automatically when memory usage exceeds a configurable threshold ([#3667](https://github.com/getsentry/sentry-dotnet/pull/3667)) - Sentry's Experimental Metrics feature has been deprecated and removed from the SDK. ([#3718](https://github.com/getsentry/sentry-dotnet/pull/3718)) ### Features diff --git a/Directory.Build.props b/Directory.Build.props index d439f8d5ad..d10026aaad 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -84,6 +84,15 @@ $(MSBuildThisFileDirectory)tools\sentry-cli\$(SentryCLIVersion)\ + + + true + true + true + + $(DefineConstants);MEMORY_DUMP_SUPPORTED + + 002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8 diff --git a/Sentry-CI-Build-Linux.slnf b/Sentry-CI-Build-Linux.slnf index d772922ad2..44fdaabe1b 100644 --- a/Sentry-CI-Build-Linux.slnf +++ b/Sentry-CI-Build-Linux.slnf @@ -15,6 +15,7 @@ "samples\\Sentry.Samples.Azure.Functions.Worker\\Sentry.Samples.Azure.Functions.Worker.csproj", "samples\\Sentry.Samples.Console.Basic\\Sentry.Samples.Console.Basic.csproj", "samples\\Sentry.Samples.Console.Customized\\Sentry.Samples.Console.Customized.csproj", + "samples\\Sentry.Samples.Console.HeapDump\\Sentry.Samples.Console.HeapDump.csproj", "samples\\Sentry.Samples.Console.Native\\Sentry.Samples.Console.Native.csproj", "samples\\Sentry.Samples.Console.Profiling\\Sentry.Samples.Console.Profiling.csproj", "samples\\Sentry.Samples.EntityFramework\\Sentry.Samples.EntityFramework.csproj", diff --git a/Sentry-CI-Build-Windows.slnf b/Sentry-CI-Build-Windows.slnf index 061bba0a20..00f48bf6de 100644 --- a/Sentry-CI-Build-Windows.slnf +++ b/Sentry-CI-Build-Windows.slnf @@ -16,6 +16,7 @@ "samples\\Sentry.Samples.Azure.Functions.Worker\\Sentry.Samples.Azure.Functions.Worker.csproj", "samples\\Sentry.Samples.Console.Basic\\Sentry.Samples.Console.Basic.csproj", "samples\\Sentry.Samples.Console.Customized\\Sentry.Samples.Console.Customized.csproj", + "samples\\Sentry.Samples.Console.HeapDump\\Sentry.Samples.Console.HeapDump.csproj", "samples\\Sentry.Samples.Console.Native\\Sentry.Samples.Console.Native.csproj", "samples\\Sentry.Samples.Console.Profiling\\Sentry.Samples.Console.Profiling.csproj", "samples\\Sentry.Samples.EntityFramework\\Sentry.Samples.EntityFramework.csproj", diff --git a/Sentry-CI-Build-macOS.slnf b/Sentry-CI-Build-macOS.slnf index 0ed945bc3b..b62140126c 100644 --- a/Sentry-CI-Build-macOS.slnf +++ b/Sentry-CI-Build-macOS.slnf @@ -16,6 +16,7 @@ "samples\\Sentry.Samples.Azure.Functions.Worker\\Sentry.Samples.Azure.Functions.Worker.csproj", "samples\\Sentry.Samples.Console.Basic\\Sentry.Samples.Console.Basic.csproj", "samples\\Sentry.Samples.Console.Customized\\Sentry.Samples.Console.Customized.csproj", + "samples\\Sentry.Samples.Console.HeapDump\\Sentry.Samples.Console.HeapDump.csproj", "samples\\Sentry.Samples.Console.Native\\Sentry.Samples.Console.Native.csproj", "samples\\Sentry.Samples.Console.Profiling\\Sentry.Samples.Console.Profiling.csproj", "samples\\Sentry.Samples.EntityFramework\\Sentry.Samples.EntityFramework.csproj", diff --git a/Sentry.sln b/Sentry.sln index a2416a2434..bce391e0e8 100644 --- a/Sentry.sln +++ b/Sentry.sln @@ -187,6 +187,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "root", "root", "{233D34AB-9 EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.AspNetCore.Blazor.WebAssembly", "src\Sentry.AspNetCore.Blazor.WebAssembly\Sentry.AspNetCore.Blazor.WebAssembly.csproj", "{8298202C-9983-4D0A-851D-805539EE481A}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Samples.Console.HeapDump", "samples\Sentry.Samples.Console.HeapDump\Sentry.Samples.Console.HeapDump.csproj", "{D7DF0B26-AD43-4F8B-9BFE-C4471CCC9821}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -502,6 +504,10 @@ Global {A5B26C14-7313-4EDC-91E3-287F9374AB75}.Debug|Any CPU.Build.0 = Debug|Any CPU {A5B26C14-7313-4EDC-91E3-287F9374AB75}.Release|Any CPU.ActiveCfg = Release|Any CPU {A5B26C14-7313-4EDC-91E3-287F9374AB75}.Release|Any CPU.Build.0 = Release|Any CPU + {D7DF0B26-AD43-4F8B-9BFE-C4471CCC9821}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D7DF0B26-AD43-4F8B-9BFE-C4471CCC9821}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D7DF0B26-AD43-4F8B-9BFE-C4471CCC9821}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D7DF0B26-AD43-4F8B-9BFE-C4471CCC9821}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -584,5 +590,6 @@ Global {46E40BE8-1AB0-4846-B0A2-A40AD0272C64} = {6987A1CC-608E-4868-A02C-09D30C8B7B2D} {8298202C-9983-4D0A-851D-805539EE481A} = {230B9384-90FD-4551-A5DE-1A5C197F25B6} {A5B26C14-7313-4EDC-91E3-287F9374AB75} = {21B42F60-5802-404E-90F0-AEBCC56760C0} + {D7DF0B26-AD43-4F8B-9BFE-C4471CCC9821} = {21B42F60-5802-404E-90F0-AEBCC56760C0} EndGlobalSection EndGlobal diff --git a/Sentry.sln.DotSettings b/Sentry.sln.DotSettings index 3491ad6fec..a06bb386ee 100644 --- a/Sentry.sln.DotSettings +++ b/Sentry.sln.DotSettings @@ -4,6 +4,7 @@ OS QL UI + True True True True diff --git a/SentryNoMobile.slnf b/SentryNoMobile.slnf index 7c5571e30a..425f20e7f8 100644 --- a/SentryNoMobile.slnf +++ b/SentryNoMobile.slnf @@ -14,6 +14,7 @@ "samples\\Sentry.Samples.Azure.Functions.Worker\\Sentry.Samples.Azure.Functions.Worker.csproj", "samples\\Sentry.Samples.Console.Basic\\Sentry.Samples.Console.Basic.csproj", "samples\\Sentry.Samples.Console.Customized\\Sentry.Samples.Console.Customized.csproj", + "samples\\Sentry.Samples.Console.HeapDump\\Sentry.Samples.Console.HeapDump.csproj", "samples\\Sentry.Samples.Console.Native\\Sentry.Samples.Console.Native.csproj", "samples\\Sentry.Samples.Console.Profiling\\Sentry.Samples.Console.Profiling.csproj", "samples\\Sentry.Samples.EntityFramework\\Sentry.Samples.EntityFramework.csproj", diff --git a/samples/Directory.Build.props b/samples/Directory.Build.props index 87af636244..828eba441b 100644 --- a/samples/Directory.Build.props +++ b/samples/Directory.Build.props @@ -7,7 +7,7 @@ - + false diff --git a/samples/Sentry.Samples.Console.Basic/Program.cs b/samples/Sentry.Samples.Console.Basic/Program.cs index 0cd25ae811..be18640323 100644 --- a/samples/Sentry.Samples.Console.Basic/Program.cs +++ b/samples/Sentry.Samples.Console.Basic/Program.cs @@ -8,10 +8,10 @@ * For more advanced features of the SDK, see Sentry.Samples.Console.Customized. */ -// Initialize the Sentry SDK. (It is not necessary to dispose it.) - using System.Net.Http; +using static System.Console; +// Initialize the Sentry SDK. (It is not necessary to dispose it.) SentrySdk.Init(options => { // You can set here in code, or you can set it in the SENTRY_DSN environment variable. @@ -55,7 +55,7 @@ async Task FirstFunction() var messageHandler = new SentryHttpMessageHandler(); var httpClient = new HttpClient(messageHandler, true); var html = await httpClient.GetStringAsync("https://example.com/"); - Console.WriteLine(html); + WriteLine(html); } async Task SecondFunction() diff --git a/samples/Sentry.Samples.Console.HeapDump/Program.cs b/samples/Sentry.Samples.Console.HeapDump/Program.cs new file mode 100644 index 0000000000..ef5138c405 --- /dev/null +++ b/samples/Sentry.Samples.Console.HeapDump/Program.cs @@ -0,0 +1,72 @@ +/* + * This sample demonstrates how you can configure Sentry to automatically capture heap dumps based on certain memory + * triggers (e.g. if memory consumption exceeds a certain percentage threshold). + * + * Note that this functionality is only available when targeting net6.0 or above and is not available on iOS, Android + * or Mac Catalyst. + */ + +using System.Reflection; +using static System.Console; + +var cts = new CancellationTokenSource(); + +// Initialize the Sentry SDK. (It is not necessary to dispose it.) +SentrySdk.Init(options => +{ + // You can set here in code, or you can set it in the SENTRY_DSN environment variable. + // See https://docs.sentry.io/product/sentry-basics/dsn-explainer/ + options.Dsn = "https://eb18e953812b41c3aeb042e666fd3b5c@o447951.ingest.sentry.io/5428537"; + + // When debug is enabled, the Sentry client will emit detailed debugging information to the console. + // This might be helpful, or might interfere with the normal operation of your application. + // We enable it here for demonstration purposes. + // You should not do this in your applications unless you are troubleshooting issues with Sentry. + options.Debug = true; + + // Set TracesSampleRate = 0 to disable tracing for this demo + options.TracesSampleRate = 0; + + // This option tells Sentry to capture a heap dump and send these as a file attachment in a Sentry event + options.EnableHeapDumps( + // Triggers a heap dump if the process uses more than 5% of the total memory. We could use any threshold or even + // provide a custom trigger function here instead. + 5, + // Limit the frequency of heap dumps to a maximum of 3 events per day and at least 1 hour between each event. + Debouncer.PerDay(3, TimeSpan.FromHours(1)), + // Set the level for heap dump events to Info + SentryLevel.Info + ); + + // This is an example of intercepting events before they get sent to Sentry. Typically, you might use this to + // filter events that you didn't want to send but in this case we're using it to detect when a heap dump has + // been captured, so we know when to stop allocating memory in the heap dump demo. + options.SetBeforeSend((evt, hint) => + { + if (hint.Attachments.Any(a => a.FileName.EndsWith("gcdump"))) + { + cts.Cancel(); + } + return evt; // If we returned null here, that would stop the event from being sent + }); +}); + +// In Debug mode there will be a bit of stuff logged out during initialization... wait for that to play out +await Task.Delay(1000); + +var memoryHog = new List(); +WriteLine(); + +WriteLine("Hogging memory..."); + +// Sentry checks memory usage every time a full garbage collection occurs. It might take a while to trigger this, +// although we've configured some ridiculously aggressive settings in the runtimeconfig.template.json file to make +// this happen more quickly, for the purposes of this demo... definitely don't do this in production! +while (cts.Token.IsCancellationRequested == false) +{ + var array = new byte[2_000_000_000]; + array.Initialize(); + memoryHog.Add(array); +} + +GC.KeepAlive(memoryHog); diff --git a/samples/Sentry.Samples.Console.HeapDump/Sentry.Samples.Console.HeapDump.csproj b/samples/Sentry.Samples.Console.HeapDump/Sentry.Samples.Console.HeapDump.csproj new file mode 100644 index 0000000000..cfcc72e664 --- /dev/null +++ b/samples/Sentry.Samples.Console.HeapDump/Sentry.Samples.Console.HeapDump.csproj @@ -0,0 +1,55 @@ + + + + Exe + net8.0 + enable + enable + true + + + + + sentry-sdks + sentry-dotnet + + + true + true + + + true + + + true + --local + + + + + + + + + + + + diff --git a/samples/Sentry.Samples.Console.HeapDump/runtimeconfig.template.json b/samples/Sentry.Samples.Console.HeapDump/runtimeconfig.template.json new file mode 100644 index 0000000000..e33343cc37 --- /dev/null +++ b/samples/Sentry.Samples.Console.HeapDump/runtimeconfig.template.json @@ -0,0 +1,6 @@ +{ + "configProperties": { + "System.GC.ConserveMemory": 9, + "System.GC.HighMemoryPercent": 20 + } +} diff --git a/src/Sentry/Debouncer.cs b/src/Sentry/Debouncer.cs new file mode 100644 index 0000000000..0bd4e68ae9 --- /dev/null +++ b/src/Sentry/Debouncer.cs @@ -0,0 +1,103 @@ +namespace Sentry; + +/// +/// A debouncer that can be used to limit the number of occurrences of an event within a given interval and optionally, +/// enforce a minimum cooldown period between events. +/// +public class Debouncer +{ + internal enum DebouncerInterval { Minute, Hour, Day, ApplicationLifetime } + + internal DateTimeOffset _intervalStart = DateTimeOffset.MinValue; + internal DateTimeOffset _lastEvent = DateTimeOffset.MinValue; + internal int _occurrences; + + internal readonly DebouncerInterval _intervalType; + internal readonly int _eventMaximum; + private readonly TimeSpan? _cooldown; + + private Debouncer(DebouncerInterval intervalType, int eventMaximum = 1, TimeSpan? cooldown = null) + { + _intervalType = intervalType; + _cooldown = cooldown; + _eventMaximum = eventMaximum; + } + + /// + /// Creates a debouncer that limits the number of events per minute + /// + /// The maximum number of events that will be processed per minute + /// An optional obligatory cooldown since the last event before any other events will be processed + /// + public static Debouncer PerMinute(int eventMaximum = 1, TimeSpan? cooldown = null) + => new(DebouncerInterval.Minute, eventMaximum, cooldown); + + /// + /// Creates a debouncer that limits the number of events per hour + /// + /// The maximum number of events that will be processed per hour + /// An optional obligatory cooldown since the last event before any other events will be processed + /// + public static Debouncer PerHour(int eventMaximum = 1, TimeSpan? cooldown = null) + => new(DebouncerInterval.Hour, eventMaximum, cooldown); + + /// + /// Creates a debouncer that limits the number of events per day + /// + /// The maximum number of events that will be processed per day + /// An optional obligatory cooldown since the last event before any other events will be processed + /// + public static Debouncer PerDay(int eventMaximum = 1, TimeSpan? cooldown = null) + => new(DebouncerInterval.Day, eventMaximum, cooldown); + + /// + /// Creates a debouncer that limits the number of events that will be processed for the lifetime of the application + /// + /// The maximum number of events that will be processed + /// An optional obligatory cooldown since the last event before any other events will be processed + /// + public static Debouncer PerApplicationLifetime(int eventMaximum = 1, TimeSpan? cooldown = null) + => new(DebouncerInterval.ApplicationLifetime, eventMaximum, cooldown); + + private TimeSpan IntervalTimeSpan() + { + switch (_intervalType) + { + case DebouncerInterval.Minute: + return TimeSpan.FromMinutes(1); + case DebouncerInterval.Hour: + return TimeSpan.FromHours(1); + case DebouncerInterval.Day: + return TimeSpan.FromDays(1); + case DebouncerInterval.ApplicationLifetime: + return TimeSpan.MaxValue; + default: + throw new ArgumentOutOfRangeException(nameof(_intervalType)); + } + } + + internal void RecordOccurence(DateTimeOffset? timestamp = null) + { + var eventTime = timestamp ?? DateTimeOffset.UtcNow; + + if (eventTime - _intervalStart >= IntervalTimeSpan()) + { + _intervalStart = eventTime; + _occurrences = 0; + } + + _occurrences++; + _lastEvent = eventTime; + } + + internal bool CanProcess(DateTimeOffset? timestamp = null) + { + if (_occurrences >= _eventMaximum) + { + return false; + } + + var eventTime = timestamp ?? DateTimeOffset.UtcNow; + return _cooldown is not { } cooldown || _lastEvent + cooldown <= eventTime; + } +} diff --git a/src/Sentry/HeapDumpTriggers.cs b/src/Sentry/HeapDumpTriggers.cs new file mode 100644 index 0000000000..411270b6ef --- /dev/null +++ b/src/Sentry/HeapDumpTriggers.cs @@ -0,0 +1,27 @@ +namespace Sentry; + +/// +/// Delegate that determines whether a heap dump should be triggered or not. +/// +/// Memory currently used by the process +/// Total available memory +/// if the heap dump should be triggered; otherwise, . +public delegate bool HeapDumpTrigger(long usedMemory, long totalMemory); + +internal static class HeapDumpTriggers +{ + internal static HeapDumpTrigger MemoryPercentageThreshold(int memoryPercentageThreshold) + { + if (memoryPercentageThreshold is < 0 or > 100) + { + throw new ArgumentException("Must be a value between 0 and 100", nameof(memoryPercentageThreshold)); + } + + return (long usedMemory, long totalMemory) => + { + var portion = (double)memoryPercentageThreshold / 100; + var thresholdBytes = (long)Math.Ceiling(portion * totalMemory); + return usedMemory > thresholdBytes; + }; + } +} diff --git a/src/Sentry/Internal/GarbageCollectionMonitor.cs b/src/Sentry/Internal/GarbageCollectionMonitor.cs new file mode 100644 index 0000000000..a17486b8fd --- /dev/null +++ b/src/Sentry/Internal/GarbageCollectionMonitor.cs @@ -0,0 +1,39 @@ +using Sentry.Extensibility; + +namespace Sentry.Internal; + +/// +/// Simple class to detect when Full Garbage Collection occurs +/// +internal sealed class GarbageCollectionMonitor +{ + private const int MaxGenerationThreshold = 10; + private const int LargeObjectHeapThreshold = 10; + + public static Task Start(Action onGarbageCollected, CancellationToken cancellationToken, IGCImplementation? gc = null) => + Task.Run(() => MonitorGarbageCollection(onGarbageCollected, cancellationToken, gc), cancellationToken); + + private static void MonitorGarbageCollection(Action onGarbageCollected, CancellationToken cancellationToken, IGCImplementation? gc = null) + { + gc ??= new SystemGCImplementation(); + try + { + gc.RegisterForFullGCNotification(MaxGenerationThreshold, LargeObjectHeapThreshold); + while (!cancellationToken.IsCancellationRequested) + { + if (gc.WaitForFullGCComplete(TimeSpan.FromSeconds(1)) == GCNotificationStatus.Succeeded) + { + onGarbageCollected?.Invoke(); + } + } + } + catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested) + { + // Ignore + } + finally + { + gc.CancelFullGCNotification(); + } + } +} diff --git a/src/Sentry/Internal/HeapDumpOptions.cs b/src/Sentry/Internal/HeapDumpOptions.cs new file mode 100644 index 0000000000..3d68d84d58 --- /dev/null +++ b/src/Sentry/Internal/HeapDumpOptions.cs @@ -0,0 +1,3 @@ +namespace Sentry.Internal; + +internal record HeapDumpOptions(HeapDumpTrigger Trigger, Debouncer Debouncer, SentryLevel Level); diff --git a/src/Sentry/Internal/Hub.cs b/src/Sentry/Internal/Hub.cs index accec331cc..b90f4ca603 100644 --- a/src/Sentry/Internal/Hub.cs +++ b/src/Sentry/Internal/Hub.cs @@ -14,6 +14,10 @@ internal class Hub : IHub, IDisposable private readonly SentryOptions _options; private readonly RandomValuesFactory _randomValuesFactory; +#if MEMORY_DUMP_SUPPORTED + private readonly MemoryMonitor? _memoryMonitor; +#endif + private int _isPersistedSessionRecovered; // Internal for testability @@ -60,6 +64,20 @@ internal Hub( PushScope(); } +#if MEMORY_DUMP_SUPPORTED + if (options.HeapDumpOptions is not null) + { + if (_options.DisableFileWrite) + { + _options.LogError("Automatic Heap Dumps cannot be used with file write disabled."); + } + else + { + _memoryMonitor = new MemoryMonitor(options, CaptureHeapDump); + } + } +#endif + foreach (var integration in options.Integrations) { options.LogDebug("Registering integration: '{0}'.", integration.GetType().Name); @@ -483,6 +501,34 @@ private SentryId CaptureEvent(SentryEvent evt, SentryHint? hint, Scope scope) } } +#if MEMORY_DUMP_SUPPORTED + internal void CaptureHeapDump(string dumpFile) + { + if (!IsEnabled) + { + return; + } + + try + { + _options.LogDebug("Capturing heap dump '{0}'", dumpFile); + + var evt = new SentryEvent + { + Message = "Memory threshold exceeded", + Level = _options.HeapDumpOptions?.Level ?? SentryLevel.Warning, + }; + var hint = new SentryHint(_options); + hint.AddAttachment(dumpFile); + CaptureEvent(evt, CurrentScope, hint); + } + catch (Exception e) + { + _options.LogError(e, "Failure to capture heap dump"); + } + } +#endif + public void CaptureUserFeedback(UserFeedback userFeedback) { if (!IsEnabled) @@ -637,6 +683,10 @@ public void Dispose() return; } +#if MEMORY_DUMP_SUPPORTED + _memoryMonitor?.Dispose(); +#endif + try { CurrentClient.FlushAsync(_options.ShutdownTimeout).ConfigureAwait(false).GetAwaiter().GetResult(); diff --git a/src/Sentry/Internal/IGCImplementation.cs b/src/Sentry/Internal/IGCImplementation.cs new file mode 100644 index 0000000000..a148aa2936 --- /dev/null +++ b/src/Sentry/Internal/IGCImplementation.cs @@ -0,0 +1,12 @@ +namespace Sentry.Internal; + +/// +/// This allows us to test the GarbageCollectionMonitor class without a dependency on System.GC, which is static +/// +internal interface IGCImplementation +{ + void RegisterForFullGCNotification(int maxGenerationThreshold, int largeObjectHeapThreshold); + GCNotificationStatus WaitForFullGCComplete(TimeSpan timeout); + void CancelFullGCNotification(); + long TotalAvailableMemoryBytes { get; } +} diff --git a/src/Sentry/Internal/MemoryMonitor.cs b/src/Sentry/Internal/MemoryMonitor.cs new file mode 100644 index 0000000000..03813a5268 --- /dev/null +++ b/src/Sentry/Internal/MemoryMonitor.cs @@ -0,0 +1,195 @@ +/* + * dotnet-gcdump needs .NET 6 or later: + * https://www.nuget.org/packages/dotnet-gcdump#supportedframeworks-body-tab + * + * Also `GC.GetGCMemoryInfo()` is not available in NetFX or NetStandard + */ +#if MEMORY_DUMP_SUPPORTED + +using Sentry.Extensibility; +using Sentry.Internal.Extensions; + +namespace Sentry.Internal; + +internal sealed class MemoryMonitor : IDisposable +{ + private readonly long _totalMemory; + + private readonly SentryOptions _options; + private readonly HeapDumpOptions _dumpOptions; + + private readonly CancellationTokenSource _cancellationTokenSource = new(); + + private readonly Action _onCaptureDump; // Just for testing purposes + private readonly Action _onDumpCollected; + + private Task? _monitorTask; + + public MemoryMonitor(SentryOptions options, Action onDumpCollected, Action? onCaptureDump = null, IGCImplementation? gc = null) + { + if (options.HeapDumpOptions is null) + { + throw new ArgumentException("No heap dump options provided", nameof(options)); + } + + _options = options; + _dumpOptions = options.HeapDumpOptions; + _onDumpCollected = onDumpCollected; + _onCaptureDump = onCaptureDump ?? CaptureMemoryDump; + + gc ??= new SystemGCImplementation(); + _totalMemory = gc.TotalAvailableMemoryBytes; + + // Since we're not awaiting the task, the continuation will happen elsewhere but that's OK - all we care about + // is that any exceptions get logged as soon as possible. + _monitorTask = GarbageCollectionMonitor.Start(CheckMemoryUsage, _cancellationTokenSource.Token, gc) + .ContinueWith( + t => _options.LogError(t.Exception!, "Garbage collection monitor failed"), + TaskContinuationOptions.OnlyOnFaulted // guarantees that the exception is not null + ); + } + + internal void CheckMemoryUsage() + { + var eventTime = DateTimeOffset.UtcNow; + if (!_dumpOptions.Debouncer.CanProcess(eventTime)) + { + return; + } + + var usedMemory = Environment.WorkingSet; + if (!_dumpOptions.Trigger(usedMemory, _totalMemory)) + { + return; + } + + _dumpOptions.Debouncer.RecordOccurence(eventTime); + + var usedMemoryPercentage = ((double)usedMemory / _totalMemory) * 100; + _options.LogDebug("Auto heap dump triggered: Total: {0:N0} bytes, Used: {1:N0} bytes ({2:N2}%)", + _totalMemory, usedMemory, usedMemoryPercentage); + _onCaptureDump(); + } + + public void CaptureMemoryDump() => CaptureMemoryDump(DefaultProcessRunner); + + /// + /// Override used for testing + /// + internal void CaptureMemoryDump(Action dumpProcessRunner) + { + if (_options.DisableFileWrite) + { + _options.LogDebug("File write has been disabled via the options. Unable to create memory dump."); + return; + } + + var dumpFile = TryGetDumpLocation(); + if (dumpFile is null) + { + return; + } + + var command = $"dotnet-gcdump collect -v -p {Environment.ProcessId} -o '{dumpFile}'"; + dumpProcessRunner.Invoke(command); + + if (!_options.FileSystem.FileExists(dumpFile)) + { + // if this happens, hopefully there would be more information in the standard output from the process above + _options.LogError("Unexpected error creating memory dump. Use debug-level to see output of dotnet-gcdump."); + return; + } + + _onDumpCollected(dumpFile); + } + + /// + /// Default process runner for the memory dump command + /// + /// + /// This code hangs the test runners on Windows in CI so must be mocked for testing. + /// + private void DefaultProcessRunner(string command) + { + _options.LogDebug($"Starting process: {command}"); + using var process = new Process(); + process.StartInfo = new ProcessStartInfo + { + FileName = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "cmd.exe" : "/bin/bash", + Arguments = $"-c \"{command}\"", + RedirectStandardOutput = true, + RedirectStandardError = false, + UseShellExecute = false, + CreateNoWindow = true, + }; + process.Start(); + while (!process.StandardOutput.EndOfStream) + { + if (process.StandardOutput.ReadLine() is { } line) + { + _options.LogDebug($"gcdump: {line}"); + } + } +#if NET8_0_OR_GREATER + process.WaitForExit(TimeSpan.FromSeconds(5)); +#else + process.WaitForExit(5000); +#endif + } + + internal string? TryGetDumpLocation() + { + try + { + var rootPath = _options.CacheDirectoryPath ?? + Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); + var directoryPath = Path.Combine(rootPath, "Sentry", _options.Dsn!.GetHashString()); + var fileSystem = _options.FileSystem; + + if (!fileSystem.CreateDirectory(directoryPath)) + { + _options.LogWarning("Failed to create a directory for memory dump ({0}).", directoryPath); + return null; + } + _options.LogDebug("Created directory for heap dump ({0}).", directoryPath); + + var timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss"); + var processId = Environment.ProcessId; + var filePath = Path.Combine(directoryPath, $"{timestamp}_{processId}.gcdump"); + if (fileSystem.FileExists(filePath)) + { + _options.LogWarning("Duplicate dump file detected."); + return null; + } + + return filePath; + } + // If there's no write permission or the platform doesn't support this, we handle simply log and bug out + catch (Exception ex) + { + _options.LogError(ex, "Failed to resolve appropriate memory dump location."); + return null; + } + } + + public void Dispose() + { + // Important no exceptions can be thrown from this method as it's called when disposing the Hub + _cancellationTokenSource.Cancel(); + try + { + _monitorTask?.Wait(500); // This should complete very quickly (possibly before we even wait) + } + catch (OperationCanceledException) + { + // Ignore + } + catch (Exception e) + { + _options.LogError(e, "Error waiting for GarbageCollectionMonitor task to complete"); + } + _cancellationTokenSource.Dispose(); + } +} + +#endif diff --git a/src/Sentry/Internal/SystemGCImplementation.cs b/src/Sentry/Internal/SystemGCImplementation.cs new file mode 100644 index 0000000000..6bcaff650a --- /dev/null +++ b/src/Sentry/Internal/SystemGCImplementation.cs @@ -0,0 +1,29 @@ +namespace Sentry.Internal; + +/// +/// When not testing we use `System.GC`. +/// +/// +/// All these methods can throw an exception if concurrent garbage collection has been enabled in the runtime +/// settings for the application. +/// +internal class SystemGCImplementation : IGCImplementation +{ + public void RegisterForFullGCNotification(int maxGenerationThreshold, int largeObjectHeapThreshold) => + GC.RegisterForFullGCNotification(maxGenerationThreshold, largeObjectHeapThreshold); + + public GCNotificationStatus WaitForFullGCComplete(TimeSpan timeout) => +#if NET8_0_OR_GREATER + GC.WaitForFullGCComplete(timeout); +#else + GC.WaitForFullGCComplete((int)timeout.TotalMilliseconds); +#endif + + public void CancelFullGCNotification() => GC.CancelFullGCNotification(); + +#if NET6_0_OR_GREATER + public long TotalAvailableMemoryBytes => GC.GetGCMemoryInfo().TotalAvailableMemoryBytes; +#else + public long TotalAvailableMemoryBytes => throw new PlatformNotSupportedException("This method is only available on .NET 5.0 or later."); +#endif +} diff --git a/src/Sentry/SentryOptions.cs b/src/Sentry/SentryOptions.cs index 37e9a04c4b..b91e099b48 100644 --- a/src/Sentry/SentryOptions.cs +++ b/src/Sentry/SentryOptions.cs @@ -519,6 +519,54 @@ public int MaxQueueItems } } + /* + * dotnet-gcdump needs .NET 6 or later... also `GC.GetGCMemoryInfo()` is not available in NetFX or NetStandard + */ +#if MEMORY_DUMP_SUPPORTED + + /// + /// Configures a heap dump to be captured if the percentage of memory used exceeds a certain threshold. + /// This can be useful to diagnose memory leaks. + /// + /// + /// The memory threshold at which to trigger a heap dump, as a percentage of total available memory. + /// Must be a number between 1 and 99. + /// + /// + /// Limits the frequency at which heap dumps are captured. If no debouncer is set then this defaults to + /// Debouncer.PerApplicationLifetime() + /// + /// Optional parameter controlling the event level associated with heap dumps. + /// Defaults to . + public void EnableHeapDumps(short memoryPercentageThreshold, Debouncer? debouncer = null, SentryLevel level = SentryLevel.Warning) + => EnableHeapDumps(HeapDumpTriggers.MemoryPercentageThreshold(memoryPercentageThreshold), debouncer, level); + + /// + /// + /// Configures Sentry to capture a heap dump based on a trigger function. This can be useful to diagnose memory leaks. + /// + /// + /// Note: This feature requires `dotnet-gcdump` to be installed globally on the machine or container where the heap + /// dumps will be captured. You can install this by running: `dotnet tool install --global dotnet-gcdump` + /// + /// + /// + /// A custom trigger function that accepts the current memory usage and total available memory as arguments and + /// return true to indicate that a heap dump should be captured or false otherwise. + /// + /// + /// Limits the frequency at which heap dumps are captured. If no debouncer is set then this defaults to + /// Debouncer.PerApplicationLifetime() + /// + /// Optional parameter controlling the event level associated with heap dumps. + /// Defaults to . + public void EnableHeapDumps(HeapDumpTrigger trigger, Debouncer? debouncer = null, SentryLevel level = SentryLevel.Warning) + => HeapDumpOptions = new HeapDumpOptions(trigger, debouncer ?? Debouncer.PerApplicationLifetime(), level); + + internal HeapDumpOptions? HeapDumpOptions { get; set; } + +#endif + private int _maxCacheItems = 30; /// diff --git a/test/Sentry.Hangfire.Tests/HangfireTests.cs b/test/Sentry.Hangfire.Tests/HangfireTests.cs index 97fb67333e..0e4601363e 100644 --- a/test/Sentry.Hangfire.Tests/HangfireTests.cs +++ b/test/Sentry.Hangfire.Tests/HangfireTests.cs @@ -10,7 +10,7 @@ public HangfireTests(HangfireFixture hangfireFixture) } [Fact] - public async void ExecuteJobWithAttribute_CapturesCheckInInProgressAndOkWithDuration() + public async Task ExecuteJobWithAttribute_CapturesCheckInInProgressAndOkWithDuration() { var sentryId = SentryId.Create(); _fixture.Hub.CaptureCheckIn(Arg.Any(), Arg.Any()).Returns(sentryId); @@ -29,7 +29,7 @@ public async void ExecuteJobWithAttribute_CapturesCheckInInProgressAndOkWithDura } [Fact] - public async void ExecuteJobWithException_CapturesCheckInInProgressAndErrorWithDuration() + public async Task ExecuteJobWithException_CapturesCheckInInProgressAndErrorWithDuration() { var sentryId = SentryId.Create(); _fixture.Hub.CaptureCheckIn(Arg.Any(), Arg.Any()).Returns(sentryId); @@ -50,7 +50,7 @@ public async void ExecuteJobWithException_CapturesCheckInInProgressAndErrorWithD } [Fact] - public async void ExecuteJobWithoutAttribute_DoesNotCapturesCheckInButLogs() + public async Task ExecuteJobWithoutAttribute_DoesNotCapturesCheckInButLogs() { var sentryId = SentryId.Create(); _fixture.Hub.CaptureCheckIn(Arg.Any(), Arg.Any()).Returns(sentryId); diff --git a/test/Sentry.Testing/BindableTests.cs b/test/Sentry.Testing/BindableTests.cs index 199eb481da..68dd553a36 100644 --- a/test/Sentry.Testing/BindableTests.cs +++ b/test/Sentry.Testing/BindableTests.cs @@ -53,6 +53,7 @@ private static KeyValuePair GetDummyBindableValue(Property not null when propertyType == typeof(bool) => true, not null when propertyType == typeof(string) => $"fake {propertyInfo.Name}", not null when propertyType == typeof(int) => 7, + not null when propertyType == typeof(short) => 7, not null when propertyType == typeof(long) => 7, not null when propertyType == typeof(float) => 0.3f, not null when propertyType == typeof(double) => 0.6, diff --git a/test/Sentry.Testing/SentryOptionsExtensions.cs b/test/Sentry.Testing/SentryOptionsExtensions.cs new file mode 100644 index 0000000000..7e18d2b8ab --- /dev/null +++ b/test/Sentry.Testing/SentryOptionsExtensions.cs @@ -0,0 +1,70 @@ +namespace Sentry.Testing; + +/// +/// We most frequently call IDiagnosticLogger via extension methods on the SentryOptions class, which obscures what +/// we're expecting to receive via mocks when writing tests. This class provides some test extensions that mirror the +/// extensions provided for SentryOptions to make it easier to write tests that match the code they're testing. +/// +public static class SentryOptionsExtensions +{ + private static SentryOptions DidNotReceiveLog(this SentryOptions substitute, SentryLevel level) + { + substitute.DiagnosticLogger.DidNotReceive().Log(level, Arg.Any(), null, Arg.Any()); + return substitute; + } + + private static SentryOptions DidNotReceiveLog(this SentryOptions substitute, SentryLevel level, string message, params object[] args) + { + substitute.DiagnosticLogger.DidNotReceive().Log(level, message, null, args); + return substitute; + } + + private static SentryOptions ReceivedLog(this SentryOptions substitute, SentryLevel level) + { + substitute.DiagnosticLogger.Received().Log(level, Arg.Any(), null, Arg.Any()); + return substitute; + } + + private static SentryOptions ReceivedLog(this SentryOptions substitute, SentryLevel level, string message, params object[] args) + { + substitute.DiagnosticLogger.Received().Log(level, message, null, args); + return substitute; + } + + public static SentryOptions ReceivedLogDebug(this SentryOptions substitute) + => ReceivedLog(substitute, SentryLevel.Debug); + + public static SentryOptions ReceivedLogDebug(this SentryOptions substitute, string message, params object[] args) + => ReceivedLog(substitute, SentryLevel.Debug, message, args); + + public static SentryOptions ReceivedLogInfo(this SentryOptions substitute) + => ReceivedLog(substitute, SentryLevel.Info); + + public static SentryOptions ReceivedLogInfo(this SentryOptions substitute, string message, params object[] args) + => ReceivedLog(substitute, SentryLevel.Info, message, args); + + public static SentryOptions DidNotReceiveReceiveLogInfo(this SentryOptions substitute) + => DidNotReceiveLog(substitute, SentryLevel.Info); + + public static SentryOptions DidNotReceiveReceiveLogInfo(this SentryOptions substitute, string message, params object[] args) + => DidNotReceiveLog(substitute, SentryLevel.Info, message, args); + + public static SentryOptions ReceivedLogWarning(this SentryOptions substitute) + => ReceivedLog(substitute, SentryLevel.Warning); + + public static SentryOptions ReceivedLogWarning(this SentryOptions substitute, string message, params object[] args) + => ReceivedLog(substitute, SentryLevel.Warning, message, args); + + public static SentryOptions ReceivedLogError(this SentryOptions substitute) + => ReceivedLogError(substitute, string.Empty); + + public static SentryOptions ReceivedLogError(this SentryOptions substitute, string message, params object[] args) + => ReceivedLog(substitute, SentryLevel.Error, message, args); + + public static SentryOptions ReceivedLogError(this SentryOptions substitute, Exception exception, string message, + params object[] args) + { + substitute.DiagnosticLogger.Received().Log(SentryLevel.Error, message, exception, Arg.Any()); + return substitute; + } +} diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt index 54518243dc..bb10750b76 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt @@ -74,6 +74,13 @@ namespace Sentry Managed = 0, ManagedBackgroundThread = 1, } + public class Debouncer + { + public static Sentry.Debouncer PerApplicationLifetime(int eventMaximum = 1, System.TimeSpan? cooldown = default) { } + public static Sentry.Debouncer PerDay(int eventMaximum = 1, System.TimeSpan? cooldown = default) { } + public static Sentry.Debouncer PerHour(int eventMaximum = 1, System.TimeSpan? cooldown = default) { } + public static Sentry.Debouncer PerMinute(int eventMaximum = 1, System.TimeSpan? cooldown = default) { } + } [System.Flags] public enum DeduplicateMode { @@ -124,6 +131,7 @@ namespace Sentry { public static void SetTags(this Sentry.IHasTags hasTags, System.Collections.Generic.IEnumerable> tags) { } } + public delegate bool HeapDumpTrigger(long usedMemory, long totalMemory); public static class HintTypes { public const string HttpResponseMessage = "http-response-message"; @@ -720,6 +728,8 @@ namespace Sentry public void DisableDuplicateEventDetection() { } public void DisableUnobservedTaskExceptionCapture() { } public void DisableWinUiUnhandledExceptionIntegration() { } + public void EnableHeapDumps(Sentry.HeapDumpTrigger trigger, Sentry.Debouncer? debouncer = null, Sentry.SentryLevel level = 2) { } + public void EnableHeapDumps(short memoryPercentageThreshold, Sentry.Debouncer? debouncer = null, Sentry.SentryLevel level = 2) { } public System.Collections.Generic.IEnumerable GetAllEventProcessors() { } public System.Collections.Generic.IEnumerable GetAllExceptionProcessors() { } public System.Collections.Generic.IEnumerable GetAllTransactionProcessors() { } diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt index 54518243dc..bb10750b76 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt @@ -74,6 +74,13 @@ namespace Sentry Managed = 0, ManagedBackgroundThread = 1, } + public class Debouncer + { + public static Sentry.Debouncer PerApplicationLifetime(int eventMaximum = 1, System.TimeSpan? cooldown = default) { } + public static Sentry.Debouncer PerDay(int eventMaximum = 1, System.TimeSpan? cooldown = default) { } + public static Sentry.Debouncer PerHour(int eventMaximum = 1, System.TimeSpan? cooldown = default) { } + public static Sentry.Debouncer PerMinute(int eventMaximum = 1, System.TimeSpan? cooldown = default) { } + } [System.Flags] public enum DeduplicateMode { @@ -124,6 +131,7 @@ namespace Sentry { public static void SetTags(this Sentry.IHasTags hasTags, System.Collections.Generic.IEnumerable> tags) { } } + public delegate bool HeapDumpTrigger(long usedMemory, long totalMemory); public static class HintTypes { public const string HttpResponseMessage = "http-response-message"; @@ -720,6 +728,8 @@ namespace Sentry public void DisableDuplicateEventDetection() { } public void DisableUnobservedTaskExceptionCapture() { } public void DisableWinUiUnhandledExceptionIntegration() { } + public void EnableHeapDumps(Sentry.HeapDumpTrigger trigger, Sentry.Debouncer? debouncer = null, Sentry.SentryLevel level = 2) { } + public void EnableHeapDumps(short memoryPercentageThreshold, Sentry.Debouncer? debouncer = null, Sentry.SentryLevel level = 2) { } public System.Collections.Generic.IEnumerable GetAllEventProcessors() { } public System.Collections.Generic.IEnumerable GetAllExceptionProcessors() { } public System.Collections.Generic.IEnumerable GetAllTransactionProcessors() { } diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt index d10a1dd45a..a66f1c5ea4 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt @@ -75,6 +75,13 @@ namespace Sentry ManagedBackgroundThread = 1, Native = 2, } + public class Debouncer + { + public static Sentry.Debouncer PerApplicationLifetime(int eventMaximum = 1, System.TimeSpan? cooldown = default) { } + public static Sentry.Debouncer PerDay(int eventMaximum = 1, System.TimeSpan? cooldown = default) { } + public static Sentry.Debouncer PerHour(int eventMaximum = 1, System.TimeSpan? cooldown = default) { } + public static Sentry.Debouncer PerMinute(int eventMaximum = 1, System.TimeSpan? cooldown = default) { } + } [System.Flags] public enum DeduplicateMode { @@ -125,6 +132,7 @@ namespace Sentry { public static void SetTags(this Sentry.IHasTags hasTags, System.Collections.Generic.IEnumerable> tags) { } } + public delegate bool HeapDumpTrigger(long usedMemory, long totalMemory); public static class HintTypes { public const string HttpResponseMessage = "http-response-message"; @@ -722,6 +730,8 @@ namespace Sentry public void DisableSystemDiagnosticsMetricsIntegration() { } public void DisableUnobservedTaskExceptionCapture() { } public void DisableWinUiUnhandledExceptionIntegration() { } + public void EnableHeapDumps(Sentry.HeapDumpTrigger trigger, Sentry.Debouncer? debouncer = null, Sentry.SentryLevel level = 2) { } + public void EnableHeapDumps(short memoryPercentageThreshold, Sentry.Debouncer? debouncer = null, Sentry.SentryLevel level = 2) { } public System.Collections.Generic.IEnumerable GetAllEventProcessors() { } public System.Collections.Generic.IEnumerable GetAllExceptionProcessors() { } public System.Collections.Generic.IEnumerable GetAllTransactionProcessors() { } diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt index d10a1dd45a..a66f1c5ea4 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt @@ -75,6 +75,13 @@ namespace Sentry ManagedBackgroundThread = 1, Native = 2, } + public class Debouncer + { + public static Sentry.Debouncer PerApplicationLifetime(int eventMaximum = 1, System.TimeSpan? cooldown = default) { } + public static Sentry.Debouncer PerDay(int eventMaximum = 1, System.TimeSpan? cooldown = default) { } + public static Sentry.Debouncer PerHour(int eventMaximum = 1, System.TimeSpan? cooldown = default) { } + public static Sentry.Debouncer PerMinute(int eventMaximum = 1, System.TimeSpan? cooldown = default) { } + } [System.Flags] public enum DeduplicateMode { @@ -125,6 +132,7 @@ namespace Sentry { public static void SetTags(this Sentry.IHasTags hasTags, System.Collections.Generic.IEnumerable> tags) { } } + public delegate bool HeapDumpTrigger(long usedMemory, long totalMemory); public static class HintTypes { public const string HttpResponseMessage = "http-response-message"; @@ -722,6 +730,8 @@ namespace Sentry public void DisableSystemDiagnosticsMetricsIntegration() { } public void DisableUnobservedTaskExceptionCapture() { } public void DisableWinUiUnhandledExceptionIntegration() { } + public void EnableHeapDumps(Sentry.HeapDumpTrigger trigger, Sentry.Debouncer? debouncer = null, Sentry.SentryLevel level = 2) { } + public void EnableHeapDumps(short memoryPercentageThreshold, Sentry.Debouncer? debouncer = null, Sentry.SentryLevel level = 2) { } public System.Collections.Generic.IEnumerable GetAllEventProcessors() { } public System.Collections.Generic.IEnumerable GetAllExceptionProcessors() { } public System.Collections.Generic.IEnumerable GetAllTransactionProcessors() { } diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt index 460ab5309d..d0a17a5b77 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt @@ -74,6 +74,13 @@ namespace Sentry Managed = 0, ManagedBackgroundThread = 1, } + public class Debouncer + { + public static Sentry.Debouncer PerApplicationLifetime(int eventMaximum = 1, System.TimeSpan? cooldown = default) { } + public static Sentry.Debouncer PerDay(int eventMaximum = 1, System.TimeSpan? cooldown = default) { } + public static Sentry.Debouncer PerHour(int eventMaximum = 1, System.TimeSpan? cooldown = default) { } + public static Sentry.Debouncer PerMinute(int eventMaximum = 1, System.TimeSpan? cooldown = default) { } + } [System.Flags] public enum DeduplicateMode { @@ -123,6 +130,7 @@ namespace Sentry { public static void SetTags(this Sentry.IHasTags hasTags, System.Collections.Generic.IEnumerable> tags) { } } + public delegate bool HeapDumpTrigger(long usedMemory, long totalMemory); public static class HintTypes { public const string HttpResponseMessage = "http-response-message"; diff --git a/test/Sentry.Tests/DebouncerTests.cs b/test/Sentry.Tests/DebouncerTests.cs new file mode 100644 index 0000000000..39807e9bd4 --- /dev/null +++ b/test/Sentry.Tests/DebouncerTests.cs @@ -0,0 +1,144 @@ +namespace Sentry.Tests; + +public class DebouncerTests +{ + [Fact] + public void PerMinute_InitialisedCorrectly() + { + // Act + var debouncer = Debouncer.PerMinute(); + + // Assert + debouncer.Should().NotBeNull(); + debouncer._intervalType.Should().Be(Debouncer.DebouncerInterval.Minute); + debouncer._eventMaximum.Should().Be(1); + } + + [Fact] + public void PerHour_InitialisedCorrectly() + { + // Act + var debouncer = Debouncer.PerHour(); + + // Assert + debouncer.Should().NotBeNull(); + debouncer._intervalType.Should().Be(Debouncer.DebouncerInterval.Hour); + debouncer._eventMaximum.Should().Be(1); + } + + [Fact] + public void PerDay_InitialisedCorrectly() + { + // Act + var debouncer = Debouncer.PerDay(); + + // Assert + debouncer.Should().NotBeNull(); + debouncer._intervalType.Should().Be(Debouncer.DebouncerInterval.Day); + debouncer._eventMaximum.Should().Be(1); + } + + [Fact] + public void PerApplicationLifetime_InitialisedCorrectly() + { + // Act + var debouncer = Debouncer.PerApplicationLifetime(); + + // Assert + debouncer.Should().NotBeNull(); + debouncer._intervalType.Should().Be(Debouncer.DebouncerInterval.ApplicationLifetime); + debouncer._eventMaximum.Should().Be(1); + } + + [Fact] + public void CanProcess_WithCooldown_RespectsCooldown() + { + // Arrange + var debouncer = Debouncer.PerMinute(10, TimeSpan.FromMinutes(1)); + var initialTime = DateTimeOffset.UtcNow; + debouncer.RecordOccurence(initialTime); + + // Act & Assert + debouncer.CanProcess(initialTime.AddSeconds(59)).Should().BeFalse(); + + // Act & Assert + debouncer.CanProcess(initialTime.AddSeconds(60)).Should().BeTrue(); + + // Act & Assert + debouncer.CanProcess(initialTime.AddSeconds(61)).Should().BeTrue(); + } + + [Fact] + public void CanProcess_NoCooldown_IgnoresCooldown() + { + // Arrange + var debouncer = Debouncer.PerMinute(10); + var initialTime = DateTimeOffset.UtcNow; + debouncer.RecordOccurence(initialTime); + + // Act & Assert + debouncer.CanProcess(initialTime.AddSeconds(59)).Should().BeTrue(); + + // Act & Assert + debouncer.CanProcess(initialTime.AddSeconds(60)).Should().BeTrue(); + + // Act & Assert + debouncer.CanProcess(initialTime.AddSeconds(61)).Should().BeTrue(); + } + + [Fact] + public void CanProcess_RespectsMaximum() + { + // Arrange + var debouncer = Debouncer.PerApplicationLifetime(1); + + // Act + var canProcess = debouncer.CanProcess(); + + // Assert + canProcess.Should().BeTrue(); + + // Act + debouncer.RecordOccurence(); + canProcess = debouncer.CanProcess(); + + // Assert + canProcess.Should().BeFalse(); + } + + [Fact] + public void RecordOccurence_WithinInterval_IncrementsOccurrences() + { + // Arrange + var debouncer = Debouncer.PerMinute(10); + var initialTime = DateTimeOffset.UtcNow; + var secondEventTime = initialTime.AddSeconds(10); + + // Act + debouncer.RecordOccurence(initialTime); + debouncer.RecordOccurence(secondEventTime); + + // Assert + debouncer._occurrences.Should().Be(2); + debouncer._intervalStart.Should().Be(initialTime); + debouncer._lastEvent.Should().Be(secondEventTime); + } + + [Fact] + public void RecordOccurence_AfterInterval_ResetsInterval() + { + // Arrange + var debouncer = Debouncer.PerMinute(10); + var initialTime = DateTimeOffset.UtcNow; + var secondEventTime = initialTime.AddSeconds(70); + + // Act + debouncer.RecordOccurence(initialTime); + debouncer.RecordOccurence(secondEventTime); + + // Assert + debouncer._occurrences.Should().Be(1); + debouncer._intervalStart.Should().Be(secondEventTime); + debouncer._lastEvent.Should().Be(secondEventTime); + } +} diff --git a/test/Sentry.Tests/Internals/GarbageCollectionMonitorTests.cs b/test/Sentry.Tests/Internals/GarbageCollectionMonitorTests.cs new file mode 100644 index 0000000000..afb1abd6de --- /dev/null +++ b/test/Sentry.Tests/Internals/GarbageCollectionMonitorTests.cs @@ -0,0 +1,73 @@ +namespace Sentry.Tests.Internals; + +public class GarbageCollectionMonitorTests +{ + [SkippableFact] + public async Task MonitorGarbageCollection_TaskCancelled_CancelsFullGCNotification() + { + // Arrange + var reset = new ManualResetEventSlim(false); + var gc = Substitute.For(); + gc.When(x => x.RegisterForFullGCNotification(Arg.Any(), Arg.Any())) + .Do(_ => reset.Set()); + gc.WaitForFullGCComplete(Arg.Any()).Returns(GCNotificationStatus.Succeeded); + + var cancellationTokenSource = new CancellationTokenSource(); + + // Act + var task = GarbageCollectionMonitor.Start(() => { }, cancellationTokenSource.Token, gc); + reset.Wait(); // Wait until the task is running + await cancellationTokenSource.CancelAsync(); + await task; + + // Assert + task.Status.Should().Be(TaskStatus.RanToCompletion); + gc.Received(1).CancelFullGCNotification(); + } + + [SkippableFact] + public async Task MonitorGarbageCollection_WaitForFullGCCompleteSucceeds_InvokesOnGarbageCollected() + { + // Arrange + var reset = new ManualResetEventSlim(false); + var onGarbageCollected = Substitute.For(); + var gc = Substitute.For(); + gc.When(x => x.RegisterForFullGCNotification(Arg.Any(), Arg.Any())) + .Do(_ => reset.Set()); + gc.WaitForFullGCComplete(Arg.Any()).Returns(GCNotificationStatus.Succeeded); + + var cancellationTokenSource = new CancellationTokenSource(); + + // Act + var task = GarbageCollectionMonitor.Start(onGarbageCollected, cancellationTokenSource.Token, gc); + reset.Wait(); // Wait until the task is running + await Task.Delay(100); // Give it some time to invoke the callback + + await cancellationTokenSource.CancelAsync(); + await task; + + // Assert + onGarbageCollected.Received(1); + } + + [SkippableFact] + public async Task MonitorGarbageCollection_GCException_Throws() + { + // Arrange + var onGarbageCollected = Substitute.For(); + var gc = Substitute.For(); + gc.When(x => x.RegisterForFullGCNotification(Arg.Any(), Arg.Any())) + .Do(_ => throw new Exception()); + + var cancellationTokenSource = new CancellationTokenSource(); + + // Act + var task = GarbageCollectionMonitor.Start(onGarbageCollected, cancellationTokenSource.Token, gc); + var timeout = Task.Delay(5000, cancellationTokenSource.Token); + await Task.WhenAny([task, timeout]); + + // Assert + task.Status.Should().Be(TaskStatus.Faulted); + task.Exception.Should().NotBeNull(); + } +} diff --git a/test/Sentry.Tests/Internals/MemoryMonitorTests.cs b/test/Sentry.Tests/Internals/MemoryMonitorTests.cs new file mode 100644 index 0000000000..27f3968a1c --- /dev/null +++ b/test/Sentry.Tests/Internals/MemoryMonitorTests.cs @@ -0,0 +1,242 @@ +#if MEMORY_DUMP_SUPPORTED +namespace Sentry.Tests.Internals; + +public class MemoryMonitorTests +{ + private HeapDumpTrigger NeverTrigger { get; } = (_, _) => false; + private HeapDumpTrigger AlwaysTrigger { get; } = (_, _) => true; + + private class Fixture + { + private IGCImplementation GCImplementation { get; set; } + + public SentryOptions Options { get; set; } = new() + { + Dsn = ValidDsn, + Debug = true, + DiagnosticLogger = Substitute.For() + }; + + public Action OnDumpCollected { get; set; } = _ => { }; + + public Action OnCaptureDump { get; set; } = null; + + private const short ThresholdPercentage = 5; + + public static IGCImplementation MockGCImplementation() + { + var gc = Substitute.For(); + gc.TotalAvailableMemoryBytes.Returns(1024 * 1024 * 1024); + return gc; + } + + public MemoryMonitor GetSut() + { + Options.DiagnosticLogger?.IsEnabled(Arg.Any()).Returns(true); + return new MemoryMonitor(Options, OnDumpCollected, OnCaptureDump, GCImplementation ?? MockGCImplementation()); + } + } + + private readonly Fixture _fixture = new(); + + [Fact] + public void Constructor_NoHeapdumpsConfigured_Throws() + { + var doNothing = new Action(() => { }); + + // Arrange + _fixture.Options.HeapDumpOptions = null; + + // Act + MemoryMonitor sut = null; + try + { + Assert.Throws(() => sut = new MemoryMonitor( + _fixture.Options, _fixture.OnDumpCollected, doNothing, Fixture.MockGCImplementation() + )); + } + finally + { + sut?.Dispose(); + } + } + + [Fact] + public void CheckMemoryUsage_Debounced_DoesNotCapture() + { + // Arrange + _fixture.Options.EnableHeapDumps( + AlwaysTrigger, + Debouncer.PerApplicationLifetime(0) // always debounce + ); + var dumpCaptured = false; + _fixture.OnCaptureDump = () => dumpCaptured = true; + using var sut = _fixture.GetSut(); + + // Act + sut.CheckMemoryUsage(); + + // Assert + dumpCaptured.Should().BeFalse(); + } + + [Fact] + public void CheckMemoryUsage_NotTriggered_DoesNotCapture() + { + // Arrange + _fixture.Options.EnableHeapDumps( + NeverTrigger, + Debouncer.PerApplicationLifetime(int.MaxValue) // never debounce + ); + var dumpCaptured = false; + _fixture.OnCaptureDump = () => dumpCaptured = true; + using var sut = _fixture.GetSut(); + + // Act + sut.CheckMemoryUsage(); + + // Assert + dumpCaptured.Should().BeFalse(); + } + + [Fact] + public void CheckMemoryUsage_TriggeredNotDebounced_Captures() + { + // Arrange + _fixture.Options.EnableHeapDumps( + AlwaysTrigger, + Debouncer.PerApplicationLifetime(int.MaxValue) // never debounce + ); + var dumpCaptured = false; + _fixture.OnCaptureDump = () => dumpCaptured = true; + using var sut = _fixture.GetSut(); + + // Act + sut.CheckMemoryUsage(); + + // Assert + dumpCaptured.Should().BeTrue(); + } + + [Fact] + public void CaptureMemoryDump_DisableFileWrite_DoesNotCapture() + { + // Arrange + _fixture.Options.EnableHeapDumps(AlwaysTrigger); + _fixture.Options.DisableFileWrite = true; + using var sut = _fixture.GetSut(); + var processRunner = Substitute.For>(); + + // Act + sut.CaptureMemoryDump(processRunner); + + // Assert + _fixture.Options.ReceivedLogDebug("File write has been disabled via the options. Unable to create memory dump."); + processRunner.DidNotReceive().Invoke(Arg.Any()); + } + + [Fact] + public void CaptureMemoryDump_UnresolvedDumpLocation_DoesNotCapture() + { + // Arrange + _fixture.Options.EnableHeapDumps(AlwaysTrigger); + _fixture.Options.FileSystem = Substitute.For(); + _fixture.Options.FileSystem.CreateDirectory(Arg.Any()).Returns(false); + using var sut = _fixture.GetSut(); + var processRunner = Substitute.For>(); + + // Act + sut.CaptureMemoryDump(processRunner); + + // Assert + processRunner.DidNotReceive().Invoke(Arg.Any()); + } + + [Fact] + public void CaptureMemoryDump_CapturesDump() + { + // Arrange + _fixture.Options.EnableHeapDumps(AlwaysTrigger); + _fixture.Options.FileSystem = new FakeFileSystem(); + using var sut = _fixture.GetSut(); + var processRunner = Substitute.For>(); + + // Act + sut.CaptureMemoryDump(processRunner); + + // Assert + processRunner.Received(1).Invoke(Arg.Any()); + } + + [Fact] + public void TryGetDumpLocation_DirectoryCreationFails_ReturnsNull() + { + // Arrange + _fixture.Options.EnableHeapDumps(AlwaysTrigger); + _fixture.Options.FileSystem = Substitute.For(); + _fixture.Options.FileSystem.CreateDirectory(Arg.Any()).Returns(false); + using var sut = _fixture.GetSut(); + + // Act + var result = sut.TryGetDumpLocation(); + + // Assert + result.Should().BeNull(); + _fixture.Options.FileSystem.Received().CreateDirectory(Arg.Any()); + _fixture.Options.FileSystem.DidNotReceive().FileExists(Arg.Any()); + _fixture.Options.ReceivedLogWarning("Failed to create a directory for memory dump ({0}).", Arg.Any()); + } + + [Fact] + public void TryGetDumpLocation_DumpFileExists_ReturnsNull() + { + // Arrange + _fixture.Options.EnableHeapDumps(AlwaysTrigger); + _fixture.Options.FileSystem = Substitute.For(); + _fixture.Options.FileSystem.CreateDirectory(Arg.Any()).Returns(true); + _fixture.Options.FileSystem.FileExists(Arg.Any()).Returns(true); + using var sut = _fixture.GetSut(); + + // Act + var result = sut.TryGetDumpLocation(); + + // Assert + result.Should().BeNull(); + _fixture.Options.FileSystem.Received().CreateDirectory(Arg.Any()); + _fixture.Options.FileSystem.Received().FileExists(Arg.Any()); + _fixture.Options.ReceivedLogWarning("Duplicate dump file detected."); + } + + [Fact] + public void TryGetDumpLocation_Exception_LogsError() + { + // Arrange + _fixture.Options.EnableHeapDumps(AlwaysTrigger); + _fixture.Options.FileSystem = Substitute.For(); + _fixture.Options.FileSystem.CreateDirectory(Arg.Any()).Throws(_ => new Exception()); + using var sut = _fixture.GetSut(); + + // Act + var result = sut.TryGetDumpLocation(); + + // Assert + result.Should().BeNull(); + _fixture.Options.ReceivedLogError(Arg.Any(), "Failed to resolve appropriate memory dump location."); + } + + [Fact] + public void TryGetDumpLocation_ReturnsFilePath() + { + // Arrange + _fixture.Options.EnableHeapDumps(AlwaysTrigger); + _fixture.Options.FileSystem = new FakeFileSystem(); + using var sut = _fixture.GetSut(); + + // Act + var result = sut.TryGetDumpLocation(); + + // Assert + result.Should().NotBeNull(); + } +} +#endif diff --git a/test/Sentry.Tests/Internals/SentryStackTraceFactoryTests.cs b/test/Sentry.Tests/Internals/SentryStackTraceFactoryTests.cs index 1897b635f1..10643e6bc2 100644 --- a/test/Sentry.Tests/Internals/SentryStackTraceFactoryTests.cs +++ b/test/Sentry.Tests/Internals/SentryStackTraceFactoryTests.cs @@ -51,8 +51,8 @@ public void Create_NoExceptionAndAttachStackTraceOptionOnWithOriginalMode_Curren // See https://github.com/getsentry/sentry-dotnet/pull/2732#discussion_r1371006441 [Theory] [InlineData(StackTraceMode.Original, "AsyncWithWait_StackTrace { }")] - [InlineData(StackTraceMode.Enhanced, "void SentryStackTraceFactoryTests.AsyncWithWait_StackTrace(StackTraceMode mode, string method)+() => { }")] - public async void AsyncWithWait_StackTrace(StackTraceMode mode, string method) + [InlineData(StackTraceMode.Enhanced, "Task SentryStackTraceFactoryTests.AsyncWithWait_StackTrace(StackTraceMode mode, string method)+() => { }")] + public async Task AsyncWithWait_StackTrace(StackTraceMode mode, string method) { _fixture.SentryOptions.StackTraceMode = mode; _fixture.SentryOptions.AttachStacktrace = true; diff --git a/test/Sentry.Tests/Protocol/Envelopes/EnvelopeTests.cs b/test/Sentry.Tests/Protocol/Envelopes/EnvelopeTests.cs index f165b78c96..93f28b7379 100644 --- a/test/Sentry.Tests/Protocol/Envelopes/EnvelopeTests.cs +++ b/test/Sentry.Tests/Protocol/Envelopes/EnvelopeTests.cs @@ -568,7 +568,7 @@ public void Serialization_EnvelopeWithThrowingItem_DoesntThrow() } [Fact] - public async void AsyncSerialization_EnvelopeWithThrowingItem_DoesntThrow() + public async Task AsyncSerialization_EnvelopeWithThrowingItem_DoesntThrow() { // Arrange using var envelope = new Envelope( From 661c242b45f0fc92f8173a4bfd0e72d29eed11c9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 20 Nov 2024 14:13:23 +1300 Subject: [PATCH 030/363] chore: update modules/sentry-native to 0.7.13 (#3770) Co-authored-by: GitHub Co-authored-by: James Crosswell --- CHANGELOG.md | 6 +++--- modules/sentry-native | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d889225e4..0342c8f0b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,9 +33,9 @@ - Bump Cocoa SDK from v8.36.0 to v8.39.0 ([#3727](https://github.com/getsentry/sentry-dotnet/pull/3727)) - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#8390) - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.36.0...8.39.0) -- Bump Native SDK from v0.7.11 to v0.7.12 ([#3731](https://github.com/getsentry/sentry-dotnet/pull/3731)) - - [changelog](https://github.com/getsentry/sentry-native/blob/master/CHANGELOG.md#0712) - - [diff](https://github.com/getsentry/sentry-native/compare/0.7.11...0.7.12) +- Bump Native SDK from v0.7.11 to v0.7.13 ([#3731](https://github.com/getsentry/sentry-dotnet/pull/3731), [#3770](https://github.com/getsentry/sentry-dotnet/pull/3770)) + - [changelog](https://github.com/getsentry/sentry-native/blob/master/CHANGELOG.md#0713) + - [diff](https://github.com/getsentry/sentry-native/compare/0.7.11...0.7.13) - Bump Java SDK from v7.16.0 to v7.17.0 ([#3749](https://github.com/getsentry/sentry-dotnet/pull/3749)) - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#7170) - [diff](https://github.com/getsentry/sentry-java/compare/7.16.0...7.17.0) diff --git a/modules/sentry-native b/modules/sentry-native index bf14b8533a..0f98b0d69c 160000 --- a/modules/sentry-native +++ b/modules/sentry-native @@ -1 +1 @@ -Subproject commit bf14b8533a3b26853e4e6fecf2f955deaa29e2d8 +Subproject commit 0f98b0d69c86ab26873899cbf532152656264790 From 8f69ecd7283a60f629b4d1e23144a59befb50de6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 20 Nov 2024 14:14:56 +1300 Subject: [PATCH 031/363] chore: update scripts/update-java.ps1 to 7.18.0 (#3771) Co-authored-by: GitHub Co-authored-by: James Crosswell --- CHANGELOG.md | 12 ++++++------ .../Sentry.Bindings.Android.csproj | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0342c8f0b9..809be9caf8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,12 +33,12 @@ - Bump Cocoa SDK from v8.36.0 to v8.39.0 ([#3727](https://github.com/getsentry/sentry-dotnet/pull/3727)) - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#8390) - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.36.0...8.39.0) -- Bump Native SDK from v0.7.11 to v0.7.13 ([#3731](https://github.com/getsentry/sentry-dotnet/pull/3731), [#3770](https://github.com/getsentry/sentry-dotnet/pull/3770)) - - [changelog](https://github.com/getsentry/sentry-native/blob/master/CHANGELOG.md#0713) - - [diff](https://github.com/getsentry/sentry-native/compare/0.7.11...0.7.13) -- Bump Java SDK from v7.16.0 to v7.17.0 ([#3749](https://github.com/getsentry/sentry-dotnet/pull/3749)) - - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#7170) - - [diff](https://github.com/getsentry/sentry-java/compare/7.16.0...7.17.0) +- Bump Native SDK from v0.7.11 to v0.7.12 ([#3731](https://github.com/getsentry/sentry-dotnet/pull/3731)) + - [changelog](https://github.com/getsentry/sentry-native/blob/master/CHANGELOG.md#0712) + - [diff](https://github.com/getsentry/sentry-native/compare/0.7.11...0.7.12) +- Bump Java SDK from v7.16.0 to v7.18.0 ([#3749](https://github.com/getsentry/sentry-dotnet/pull/3749), [#3771](https://github.com/getsentry/sentry-dotnet/pull/3771)) + - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#7180) + - [diff](https://github.com/getsentry/sentry-java/compare/7.16.0...7.18.0) ## 4.13.0 diff --git a/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj b/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj index 63ae94badf..d98083bee4 100644 --- a/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj +++ b/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj @@ -3,7 +3,7 @@ net8.0-android $(NoWarn);BG8605;BG8606 - 7.17.0 + 7.18.0 $(BaseIntermediateOutputPath)sdks\Sentry\Android\$(SentryAndroidSdkVersion)\ .NET Bindings for the Sentry Android SDK From ac0ddf354b7e199ba6acca4e559d3e8ddebd6df6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Nov 2024 14:15:52 +1300 Subject: [PATCH 032/363] build(deps): bump gradle/actions from 4.1.0 to 4.2.0 (#3768) Bumps [gradle/actions](https://github.com/gradle/actions) from 4.1.0 to 4.2.0. - [Release notes](https://github.com/gradle/actions/releases) - [Commits](https://github.com/gradle/actions/compare/d156388eb19639ec20ade50009f3d199ce1e2808...473878a77f1b98e2b5ac4af93489d1656a80a5ed) --- updated-dependencies: - dependency-name: gradle/actions dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: James Crosswell --- .github/workflows/device-tests-android.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/device-tests-android.yml b/.github/workflows/device-tests-android.yml index adbb50fd74..299c08fe92 100644 --- a/.github/workflows/device-tests-android.yml +++ b/.github/workflows/device-tests-android.yml @@ -74,7 +74,7 @@ jobs: path: bin - name: Setup Gradle - uses: gradle/actions/setup-gradle@d156388eb19639ec20ade50009f3d199ce1e2808 # pin@v3 + uses: gradle/actions/setup-gradle@473878a77f1b98e2b5ac4af93489d1656a80a5ed # pin@v3 # Cached AVD setup per https://github.com/ReactiveCircus/android-emulator-runner/blob/main/README.md From 146fdbbaced1b4161c58fc88cc4c707b73ca8c21 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Nov 2024 14:16:23 +1300 Subject: [PATCH 033/363] build(deps): bump codecov/codecov-action from 4.6.0 to 5.0.2 (#3769) Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 4.6.0 to 5.0.2. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238...5c47607acb93fed5485fdbf7232e8a31425f672a) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: James Crosswell --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e5d4464afa..fb90633fe9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -121,7 +121,7 @@ jobs: run: dotnet test Sentry-CI-Build-${{ runner.os }}.slnf -c Release --no-build --nologo -l GitHubActions -l "trx;LogFilePrefix=testresults_${{ runner.os }}" --collect "XPlat Code Coverage" - name: Upload code coverage - uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 + uses: codecov/codecov-action@5c47607acb93fed5485fdbf7232e8a31425f672a - name: Upload build and test outputs if: failure() From 12693ed56d6ffe4efb9f5af584ecc13b4b52d7c4 Mon Sep 17 00:00:00 2001 From: Bruno Garcia Date: Tue, 19 Nov 2024 20:16:45 -0500 Subject: [PATCH 034/363] update codeowners (#3760) --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 0a795d964d..3d362c4857 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1 @@ -* @bitsandfoxes @jamescrosswell +* @bruno-garcia @jamescrosswell From 0ed4aa642d0679c25022212b0d433b8f2d8312a4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Nov 2024 16:01:00 +1300 Subject: [PATCH 035/363] build(deps): bump github/codeql-action from 3.27.1 to 3.27.4 (#3767) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.27.1 to 3.27.4. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/4f3212b61783c3c68e8309a0f18a699764811cda...ea9e4e37992a54ee68a9622e985e60c8e8f12d9f) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: James Crosswell --- .github/workflows/codeql-analysis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 8d37dc20cc..e148f20af5 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -35,7 +35,7 @@ jobs: uses: ./.github/actions/environment - name: Initialize CodeQL - uses: github/codeql-action/init@4f3212b61783c3c68e8309a0f18a699764811cda # pin@v2 + uses: github/codeql-action/init@ea9e4e37992a54ee68a9622e985e60c8e8f12d9f # pin@v2 with: languages: csharp @@ -49,6 +49,6 @@ jobs: run: dotnet build Sentry-CI-CodeQL.slnf --no-restore --nologo - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@4f3212b61783c3c68e8309a0f18a699764811cda # pin@v2 + uses: github/codeql-action/analyze@ea9e4e37992a54ee68a9622e985e60c8e8f12d9f # pin@v2 with: category: '/language:csharp' From 1928a48ae56c9bd00e9c1789bb37ff683b33f281 Mon Sep 17 00:00:00 2001 From: Rok Rode Date: Wed, 20 Nov 2024 09:13:01 +0100 Subject: [PATCH 036/363] Filesystem usage analyzer (#3725) --- .generated.NoMobile.sln | 22 +++ Sentry-CI-Build-Linux.slnf | 2 + Sentry-CI-Build-Windows.slnf | 2 + Sentry-CI-Build-macOS.slnf | 2 + Sentry-CI-CodeQL.slnf | 1 + Sentry.Azure.Functions.slnf | 2 + Sentry.sln | 13 ++ SentryAspNetCore.slnf | 2 + SentryCore.slnf | 2 + SentryDiagnosticSource.slnf | 2 + SentryMobile.slnf | 1 + SentryNoMobile.slnf | 2 + SentryNoSamples.slnf | 2 + modules/Ben.Demystifier | 2 +- scripts/generate-solution-filters-config.yaml | 10 ++ .../AnalyzerReleases.Shipped.md | 0 .../AnalyzerReleases.Unshipped.md | 5 + src/Sentry.Analyzers/FileSystemAnalyzer.cs | 103 +++++++++++++ src/Sentry.Analyzers/README.md | 1 + src/Sentry.Analyzers/Sentry.Analyzers.csproj | 40 +++++ .../Infrastructure/FileDiagnosticLogger.cs | 3 + .../Platforms/Android/AndroidHelpers.cs | 3 + src/Sentry/Platforms/Native/CFunctions.cs | 3 + src/Sentry/Sentry.csproj | 5 + .../FileSystemAnalyzerTests.cs | 145 ++++++++++++++++++ .../Sentry.Analyzers.Tests.csproj | 25 +++ 26 files changed, 399 insertions(+), 1 deletion(-) create mode 100644 src/Sentry.Analyzers/AnalyzerReleases.Shipped.md create mode 100644 src/Sentry.Analyzers/AnalyzerReleases.Unshipped.md create mode 100644 src/Sentry.Analyzers/FileSystemAnalyzer.cs create mode 100644 src/Sentry.Analyzers/README.md create mode 100644 src/Sentry.Analyzers/Sentry.Analyzers.csproj create mode 100644 test/Sentry.Analyzers.Tests/FileSystemAnalyzerTests.cs create mode 100644 test/Sentry.Analyzers.Tests/Sentry.Analyzers.Tests.csproj diff --git a/.generated.NoMobile.sln b/.generated.NoMobile.sln index a90cf85ad2..ccd7ca8b47 100644 --- a/.generated.NoMobile.sln +++ b/.generated.NoMobile.sln @@ -62,6 +62,10 @@ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Samples.Serilog", "samples\Sentry.Samples.Serilog\Sentry.Samples.Serilog.csproj", "{AA98FD8D-1254-4B34-840C-06BB263933DE}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{230B9384-90FD-4551-A5DE-1A5C197F25B6}" + ProjectSection(SolutionItems) = preProject + src\Directory.Build.props = src\Directory.Build.props + src\Directory.Build.targets = src\Directory.Build.targets + EndProjectSection EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Android.AssemblyReader", "src\Sentry.Android.AssemblyReader\Sentry.Android.AssemblyReader.csproj", "{20386BBE-1F55-4503-9F5F-F2C6B29DE865}" EndProject @@ -148,6 +152,9 @@ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SingleFileTestApp", "test\SingleFileTestApp\SingleFileTestApp.csproj", "{162A1CAE-ACEE-45CA-A6D0-7702ADE4D3DE}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "modules", "modules", "{A3CCA27E-4DF8-479D-833C-CAA0950715AA}" + ProjectSection(SolutionItems) = preProject + modules\sentry-cocoa.properties = modules\sentry-cocoa.properties + EndProjectSection EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TraceEvent", "modules\perfview\src\TraceEvent\TraceEvent.csproj", "{67269916-C417-4CEE-BD7D-CA66C3830AEE}" EndProject @@ -174,10 +181,15 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "root", "root", "{233D34AB-9 Directory.Build.targets = Directory.Build.targets global.json = global.json nuget.config = nuget.config + .codecov.yml = .codecov.yml + .gitignore = .gitignore EndProjectSection EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.AspNetCore.Blazor.WebAssembly", "src\Sentry.AspNetCore.Blazor.WebAssembly\Sentry.AspNetCore.Blazor.WebAssembly.csproj", "{8298202C-9983-4D0A-851D-805539EE481A}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Analyzers", "src\Sentry.Analyzers\Sentry.Analyzers.csproj", "{E36C8DCA-464E-41CB-B189-F58553AAA8D4}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Analyzers.Tests", "test\Sentry.Analyzers.Tests\Sentry.Analyzers.Tests.csproj", "{5A17FEF9-07BB-47B8-9883-9C2CC93F09E8}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Samples.Console.HeapDump", "samples\Sentry.Samples.Console.HeapDump\Sentry.Samples.Console.HeapDump.csproj", "{D7DF0B26-AD43-4F8B-9BFE-C4471CCC9821}" EndProject Global @@ -495,6 +507,14 @@ Global {A5B26C14-7313-4EDC-91E3-287F9374AB75}.Debug|Any CPU.Build.0 = Debug|Any CPU {A5B26C14-7313-4EDC-91E3-287F9374AB75}.Release|Any CPU.ActiveCfg = Release|Any CPU {A5B26C14-7313-4EDC-91E3-287F9374AB75}.Release|Any CPU.Build.0 = Release|Any CPU + {E36C8DCA-464E-41CB-B189-F58553AAA8D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E36C8DCA-464E-41CB-B189-F58553AAA8D4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E36C8DCA-464E-41CB-B189-F58553AAA8D4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E36C8DCA-464E-41CB-B189-F58553AAA8D4}.Release|Any CPU.Build.0 = Release|Any CPU + {5A17FEF9-07BB-47B8-9883-9C2CC93F09E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5A17FEF9-07BB-47B8-9883-9C2CC93F09E8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5A17FEF9-07BB-47B8-9883-9C2CC93F09E8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5A17FEF9-07BB-47B8-9883-9C2CC93F09E8}.Release|Any CPU.Build.0 = Release|Any CPU {D7DF0B26-AD43-4F8B-9BFE-C4471CCC9821}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D7DF0B26-AD43-4F8B-9BFE-C4471CCC9821}.Debug|Any CPU.Build.0 = Debug|Any CPU {D7DF0B26-AD43-4F8B-9BFE-C4471CCC9821}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -581,6 +601,8 @@ Global {46E40BE8-1AB0-4846-B0A2-A40AD0272C64} = {6987A1CC-608E-4868-A02C-09D30C8B7B2D} {8298202C-9983-4D0A-851D-805539EE481A} = {230B9384-90FD-4551-A5DE-1A5C197F25B6} {A5B26C14-7313-4EDC-91E3-287F9374AB75} = {21B42F60-5802-404E-90F0-AEBCC56760C0} + {E36C8DCA-464E-41CB-B189-F58553AAA8D4} = {230B9384-90FD-4551-A5DE-1A5C197F25B6} + {5A17FEF9-07BB-47B8-9883-9C2CC93F09E8} = {6987A1CC-608E-4868-A02C-09D30C8B7B2D} {D7DF0B26-AD43-4F8B-9BFE-C4471CCC9821} = {21B42F60-5802-404E-90F0-AEBCC56760C0} EndGlobalSection EndGlobal diff --git a/Sentry-CI-Build-Linux.slnf b/Sentry-CI-Build-Linux.slnf index 44fdaabe1b..bc53783e35 100644 --- a/Sentry-CI-Build-Linux.slnf +++ b/Sentry-CI-Build-Linux.slnf @@ -31,6 +31,7 @@ "samples\\Sentry.Samples.OpenTelemetry.AspNetCore\\Sentry.Samples.OpenTelemetry.AspNetCore.csproj", "samples\\Sentry.Samples.OpenTelemetry.Console\\Sentry.Samples.OpenTelemetry.Console.csproj", "samples\\Sentry.Samples.Serilog\\Sentry.Samples.Serilog.csproj", + "src\\Sentry.Analyzers\\Sentry.Analyzers.csproj", "src\\Sentry.Android.AssemblyReader\\Sentry.Android.AssemblyReader.csproj", "src\\Sentry.AspNetCore.Blazor.WebAssembly\\Sentry.AspNetCore.Blazor.WebAssembly.csproj", "src\\Sentry.AspNetCore.Grpc\\Sentry.AspNetCore.Grpc.csproj", @@ -49,6 +50,7 @@ "src\\Sentry.Profiling\\Sentry.Profiling.csproj", "src\\Sentry.Serilog\\Sentry.Serilog.csproj", "src\\Sentry\\Sentry.csproj", + "test\\Sentry.Analyzers.Tests\\Sentry.Analyzers.Tests.csproj", "test\\Sentry.Android.AssemblyReader.Tests\\Sentry.Android.AssemblyReader.Tests.csproj", "test\\Sentry.AspNet.Tests\\Sentry.AspNet.Tests.csproj", "test\\Sentry.AspNetCore.Grpc.Tests\\Sentry.AspNetCore.Grpc.Tests.csproj", diff --git a/Sentry-CI-Build-Windows.slnf b/Sentry-CI-Build-Windows.slnf index 00f48bf6de..1ad751775d 100644 --- a/Sentry-CI-Build-Windows.slnf +++ b/Sentry-CI-Build-Windows.slnf @@ -32,6 +32,7 @@ "samples\\Sentry.Samples.OpenTelemetry.AspNetCore\\Sentry.Samples.OpenTelemetry.AspNetCore.csproj", "samples\\Sentry.Samples.OpenTelemetry.Console\\Sentry.Samples.OpenTelemetry.Console.csproj", "samples\\Sentry.Samples.Serilog\\Sentry.Samples.Serilog.csproj", + "src\\Sentry.Analyzers\\Sentry.Analyzers.csproj", "src\\Sentry.Android.AssemblyReader\\Sentry.Android.AssemblyReader.csproj", "src\\Sentry.AspNet\\Sentry.AspNet.csproj", "src\\Sentry.AspNetCore.Blazor.WebAssembly\\Sentry.AspNetCore.Blazor.WebAssembly.csproj", @@ -51,6 +52,7 @@ "src\\Sentry.Profiling\\Sentry.Profiling.csproj", "src\\Sentry.Serilog\\Sentry.Serilog.csproj", "src\\Sentry\\Sentry.csproj", + "test\\Sentry.Analyzers.Tests\\Sentry.Analyzers.Tests.csproj", "test\\Sentry.Android.AssemblyReader.Tests\\Sentry.Android.AssemblyReader.Tests.csproj", "test\\Sentry.AspNet.Tests\\Sentry.AspNet.Tests.csproj", "test\\Sentry.AspNetCore.Grpc.Tests\\Sentry.AspNetCore.Grpc.Tests.csproj", diff --git a/Sentry-CI-Build-macOS.slnf b/Sentry-CI-Build-macOS.slnf index b62140126c..9ef5164ed0 100644 --- a/Sentry-CI-Build-macOS.slnf +++ b/Sentry-CI-Build-macOS.slnf @@ -35,6 +35,7 @@ "samples\\Sentry.Samples.OpenTelemetry.AspNetCore\\Sentry.Samples.OpenTelemetry.AspNetCore.csproj", "samples\\Sentry.Samples.OpenTelemetry.Console\\Sentry.Samples.OpenTelemetry.Console.csproj", "samples\\Sentry.Samples.Serilog\\Sentry.Samples.Serilog.csproj", + "src\\Sentry.Analyzers\\Sentry.Analyzers.csproj", "src\\Sentry.Android.AssemblyReader\\Sentry.Android.AssemblyReader.csproj", "src\\Sentry.AspNet\\Sentry.AspNet.csproj", "src\\Sentry.AspNetCore.Blazor.WebAssembly\\Sentry.AspNetCore.Blazor.WebAssembly.csproj", @@ -55,6 +56,7 @@ "src\\Sentry.Profiling\\Sentry.Profiling.csproj", "src\\Sentry.Serilog\\Sentry.Serilog.csproj", "src\\Sentry\\Sentry.csproj", + "test\\Sentry.Analyzers.Tests\\Sentry.Analyzers.Tests.csproj", "test\\Sentry.Android.AssemblyReader.Tests\\Sentry.Android.AssemblyReader.Tests.csproj", "test\\Sentry.AspNet.Tests\\Sentry.AspNet.Tests.csproj", "test\\Sentry.AspNetCore.Grpc.Tests\\Sentry.AspNetCore.Grpc.Tests.csproj", diff --git a/Sentry-CI-CodeQL.slnf b/Sentry-CI-CodeQL.slnf index ad51bed849..44eb07a6e4 100644 --- a/Sentry-CI-CodeQL.slnf +++ b/Sentry-CI-CodeQL.slnf @@ -2,6 +2,7 @@ "solution": { "path": "Sentry.sln", "projects": [ + "src\\Sentry.Analyzers\\Sentry.Analyzers.csproj", "src\\Sentry.Android.AssemblyReader\\Sentry.Android.AssemblyReader.csproj", "src\\Sentry.AspNet\\Sentry.AspNet.csproj", "src\\Sentry.AspNetCore.Blazor.WebAssembly\\Sentry.AspNetCore.Blazor.WebAssembly.csproj", diff --git a/Sentry.Azure.Functions.slnf b/Sentry.Azure.Functions.slnf index eda8164723..54c80b23c9 100644 --- a/Sentry.Azure.Functions.slnf +++ b/Sentry.Azure.Functions.slnf @@ -4,9 +4,11 @@ "projects": [ "samples\\Sentry.Samples.Azure.Functions.Worker\\Sentry.Samples.Azure.Functions.Worker.csproj", "samples\\Sentry.Samples.Console.Basic\\Sentry.Samples.Console.Basic.csproj", + "src\\Sentry.Analyzers\\Sentry.Analyzers.csproj", "src\\Sentry.Azure.Functions.Worker\\Sentry.Azure.Functions.Worker.csproj", "src\\Sentry.Extensions.Logging\\Sentry.Extensions.Logging.csproj", "src\\Sentry\\Sentry.csproj", + "test\\Sentry.Analyzers.Tests\\Sentry.Analyzers.Tests.csproj", "test\\Sentry.Azure.Functions.Worker.Tests\\Sentry.Azure.Functions.Worker.Tests.csproj", "test\\Sentry.Testing.CrashableApp\\Sentry.Testing.CrashableApp.csproj", "test\\Sentry.Testing\\Sentry.Testing.csproj", diff --git a/Sentry.sln b/Sentry.sln index bce391e0e8..ccd7ca8b47 100644 --- a/Sentry.sln +++ b/Sentry.sln @@ -187,6 +187,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "root", "root", "{233D34AB-9 EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.AspNetCore.Blazor.WebAssembly", "src\Sentry.AspNetCore.Blazor.WebAssembly\Sentry.AspNetCore.Blazor.WebAssembly.csproj", "{8298202C-9983-4D0A-851D-805539EE481A}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Analyzers", "src\Sentry.Analyzers\Sentry.Analyzers.csproj", "{E36C8DCA-464E-41CB-B189-F58553AAA8D4}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Analyzers.Tests", "test\Sentry.Analyzers.Tests\Sentry.Analyzers.Tests.csproj", "{5A17FEF9-07BB-47B8-9883-9C2CC93F09E8}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Samples.Console.HeapDump", "samples\Sentry.Samples.Console.HeapDump\Sentry.Samples.Console.HeapDump.csproj", "{D7DF0B26-AD43-4F8B-9BFE-C4471CCC9821}" EndProject Global @@ -504,6 +507,14 @@ Global {A5B26C14-7313-4EDC-91E3-287F9374AB75}.Debug|Any CPU.Build.0 = Debug|Any CPU {A5B26C14-7313-4EDC-91E3-287F9374AB75}.Release|Any CPU.ActiveCfg = Release|Any CPU {A5B26C14-7313-4EDC-91E3-287F9374AB75}.Release|Any CPU.Build.0 = Release|Any CPU + {E36C8DCA-464E-41CB-B189-F58553AAA8D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E36C8DCA-464E-41CB-B189-F58553AAA8D4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E36C8DCA-464E-41CB-B189-F58553AAA8D4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E36C8DCA-464E-41CB-B189-F58553AAA8D4}.Release|Any CPU.Build.0 = Release|Any CPU + {5A17FEF9-07BB-47B8-9883-9C2CC93F09E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5A17FEF9-07BB-47B8-9883-9C2CC93F09E8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5A17FEF9-07BB-47B8-9883-9C2CC93F09E8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5A17FEF9-07BB-47B8-9883-9C2CC93F09E8}.Release|Any CPU.Build.0 = Release|Any CPU {D7DF0B26-AD43-4F8B-9BFE-C4471CCC9821}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D7DF0B26-AD43-4F8B-9BFE-C4471CCC9821}.Debug|Any CPU.Build.0 = Debug|Any CPU {D7DF0B26-AD43-4F8B-9BFE-C4471CCC9821}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -590,6 +601,8 @@ Global {46E40BE8-1AB0-4846-B0A2-A40AD0272C64} = {6987A1CC-608E-4868-A02C-09D30C8B7B2D} {8298202C-9983-4D0A-851D-805539EE481A} = {230B9384-90FD-4551-A5DE-1A5C197F25B6} {A5B26C14-7313-4EDC-91E3-287F9374AB75} = {21B42F60-5802-404E-90F0-AEBCC56760C0} + {E36C8DCA-464E-41CB-B189-F58553AAA8D4} = {230B9384-90FD-4551-A5DE-1A5C197F25B6} + {5A17FEF9-07BB-47B8-9883-9C2CC93F09E8} = {6987A1CC-608E-4868-A02C-09D30C8B7B2D} {D7DF0B26-AD43-4F8B-9BFE-C4471CCC9821} = {21B42F60-5802-404E-90F0-AEBCC56760C0} EndGlobalSection EndGlobal diff --git a/SentryAspNetCore.slnf b/SentryAspNetCore.slnf index 0b9a18a458..543d1de30d 100644 --- a/SentryAspNetCore.slnf +++ b/SentryAspNetCore.slnf @@ -12,6 +12,7 @@ "samples\\Sentry.Samples.Aws.Lambda.AspNetCoreServer\\Sentry.Samples.Aws.Lambda.AspNetCoreServer.csproj", "samples\\Sentry.Samples.Hangfire\\Sentry.Samples.Hangfire.csproj", "samples\\Sentry.Samples.OpenTelemetry.AspNetCore\\Sentry.Samples.OpenTelemetry.AspNetCore.csproj", + "src\\Sentry.Analyzers\\Sentry.Analyzers.csproj", "src\\Sentry.AspNetCore.Blazor.WebAssembly\\Sentry.AspNetCore.Blazor.WebAssembly.csproj", "src\\Sentry.AspNetCore.Grpc\\Sentry.AspNetCore.Grpc.csproj", "src\\Sentry.AspNetCore\\Sentry.AspNetCore.csproj", @@ -21,6 +22,7 @@ "src\\Sentry.OpenTelemetry\\Sentry.OpenTelemetry.csproj", "src\\Sentry.Serilog\\Sentry.Serilog.csproj", "src\\Sentry\\Sentry.csproj", + "test\\Sentry.Analyzers.Tests\\Sentry.Analyzers.Tests.csproj", "test\\Sentry.AspNetCore.Grpc.Tests\\Sentry.AspNetCore.Grpc.Tests.csproj", "test\\Sentry.AspNetCore.Tests\\Sentry.AspNetCore.Tests.csproj", "test\\Sentry.AspNetCore.TestUtils\\Sentry.AspNetCore.TestUtils.csproj", diff --git a/SentryCore.slnf b/SentryCore.slnf index d2eb48df03..ffb3286f6e 100644 --- a/SentryCore.slnf +++ b/SentryCore.slnf @@ -5,8 +5,10 @@ "benchmarks\\Sentry.Benchmarks\\Sentry.Benchmarks.csproj", "samples\\Sentry.Samples.Console.Basic\\Sentry.Samples.Console.Basic.csproj", "samples\\Sentry.Samples.Console.Profiling\\Sentry.Samples.Console.Profiling.csproj", + "src\\Sentry.Analyzers\\Sentry.Analyzers.csproj", "src\\Sentry.Profiling\\Sentry.Profiling.csproj", "src\\Sentry\\Sentry.csproj", + "test\\Sentry.Analyzers.Tests\\Sentry.Analyzers.Tests.csproj", "test\\Sentry.Profiling.Tests\\Sentry.Profiling.Tests.csproj", "test\\Sentry.Testing.CrashableApp\\Sentry.Testing.CrashableApp.csproj", "test\\Sentry.Testing\\Sentry.Testing.csproj", diff --git a/SentryDiagnosticSource.slnf b/SentryDiagnosticSource.slnf index def6547901..da3b9f50d2 100644 --- a/SentryDiagnosticSource.slnf +++ b/SentryDiagnosticSource.slnf @@ -2,9 +2,11 @@ "solution": { "path": ".generated.NoMobile.sln", "projects": [ + "src\\Sentry.Analyzers\\Sentry.Analyzers.csproj", "src\\Sentry.DiagnosticSource\\Sentry.DiagnosticSource.csproj", "src\\Sentry.Extensions.Logging\\Sentry.Extensions.Logging.csproj", "src\\Sentry\\Sentry.csproj", + "test\\Sentry.Analyzers.Tests\\Sentry.Analyzers.Tests.csproj", "test\\Sentry.DiagnosticSource.IntegrationTests\\Sentry.DiagnosticSource.IntegrationTests.csproj", "test\\Sentry.DiagnosticSource.Tests\\Sentry.DiagnosticSource.Tests.csproj", "test\\Sentry.Testing.CrashableApp\\Sentry.Testing.CrashableApp.csproj", diff --git a/SentryMobile.slnf b/SentryMobile.slnf index b5d76fafbd..c38fa573cc 100644 --- a/SentryMobile.slnf +++ b/SentryMobile.slnf @@ -6,6 +6,7 @@ "samples\\Sentry.Samples.Ios\\Sentry.Samples.Ios.csproj", "samples\\Sentry.Samples.MacCatalyst\\Sentry.Samples.MacCatalyst.csproj", "samples\\Sentry.Samples.Maui\\Sentry.Samples.Maui.csproj", + "src\\Sentry.Analyzers\\Sentry.Analyzers.csproj", "src\\Sentry.Android.AssemblyReader\\Sentry.Android.AssemblyReader.csproj", "src\\Sentry.Bindings.Android\\Sentry.Bindings.Android.csproj", "src\\Sentry.Bindings.Cocoa\\Sentry.Bindings.Cocoa.csproj", diff --git a/SentryNoMobile.slnf b/SentryNoMobile.slnf index 425f20e7f8..e6e27c6ee7 100644 --- a/SentryNoMobile.slnf +++ b/SentryNoMobile.slnf @@ -31,6 +31,7 @@ "samples\\Sentry.Samples.OpenTelemetry.AspNetCore\\Sentry.Samples.OpenTelemetry.AspNetCore.csproj", "samples\\Sentry.Samples.OpenTelemetry.Console\\Sentry.Samples.OpenTelemetry.Console.csproj", "samples\\Sentry.Samples.Serilog\\Sentry.Samples.Serilog.csproj", + "src\\Sentry.Analyzers\\Sentry.Analyzers.csproj", "src\\Sentry.AspNet\\Sentry.AspNet.csproj", "src\\Sentry.AspNetCore.Blazor.WebAssembly\\Sentry.AspNetCore.Blazor.WebAssembly.csproj", "src\\Sentry.AspNetCore.Grpc\\Sentry.AspNetCore.Grpc.csproj", @@ -47,6 +48,7 @@ "src\\Sentry.Profiling\\Sentry.Profiling.csproj", "src\\Sentry.Serilog\\Sentry.Serilog.csproj", "src\\Sentry\\Sentry.csproj", + "test\\Sentry.Analyzers.Tests\\Sentry.Analyzers.Tests.csproj", "test\\Sentry.AspNet.Tests\\Sentry.AspNet.Tests.csproj", "test\\Sentry.AspNetCore.Grpc.Tests\\Sentry.AspNetCore.Grpc.Tests.csproj", "test\\Sentry.AspNetCore.Tests\\Sentry.AspNetCore.Tests.csproj", diff --git a/SentryNoSamples.slnf b/SentryNoSamples.slnf index 43e154e9f8..66ce4d76a5 100644 --- a/SentryNoSamples.slnf +++ b/SentryNoSamples.slnf @@ -3,6 +3,7 @@ "path": "Sentry.sln", "projects": [ "benchmarks\\Sentry.Benchmarks\\Sentry.Benchmarks.csproj", + "src\\Sentry.Analyzers\\Sentry.Analyzers.csproj", "src\\Sentry.Android.AssemblyReader\\Sentry.Android.AssemblyReader.csproj", "src\\Sentry.AspNet\\Sentry.AspNet.csproj", "src\\Sentry.AspNetCore.Blazor.WebAssembly\\Sentry.AspNetCore.Blazor.WebAssembly.csproj", @@ -21,6 +22,7 @@ "src\\Sentry.Profiling\\Sentry.Profiling.csproj", "src\\Sentry.Serilog\\Sentry.Serilog.csproj", "src\\Sentry\\Sentry.csproj", + "test\\Sentry.Analyzers.Tests\\Sentry.Analyzers.Tests.csproj", "test\\Sentry.Android.AssemblyReader.Tests\\Sentry.Android.AssemblyReader.Tests.csproj", "test\\Sentry.AspNet.Tests\\Sentry.AspNet.Tests.csproj", "test\\Sentry.AspNetCore.Grpc.Tests\\Sentry.AspNetCore.Grpc.Tests.csproj", diff --git a/modules/Ben.Demystifier b/modules/Ben.Demystifier index ad4c115b2f..b1dd53b9b1 160000 --- a/modules/Ben.Demystifier +++ b/modules/Ben.Demystifier @@ -1 +1 @@ -Subproject commit ad4c115b2f87646e85956a5555a7313c06f5a065 +Subproject commit b1dd53b9b194de9430dc31df27184966ee28e7d0 diff --git a/scripts/generate-solution-filters-config.yaml b/scripts/generate-solution-filters-config.yaml index 1483f02fd1..9f49c579d6 100644 --- a/scripts/generate-solution-filters-config.yaml +++ b/scripts/generate-solution-filters-config.yaml @@ -96,11 +96,13 @@ filterConfigs: - "**/Sentry.OpenTelemetry.csproj" - "**/Sentry.Serilog.csproj" - "**/Sentry.csproj" + - "**/Sentry.Analyzers.csproj" - "**/Sentry.Hangfire.csproj" - "**/Sentry.Samples.Hangfire.csproj" - "**/Sentry.Testing.csproj" - "**/Sentry.Testing.CrashableApp.csproj" - "**/Sentry.Tests.csproj" + - "**/Sentry.Analyzers.Tests.csproj" - outputPath: Sentry.Azure.Functions.slnf include: @@ -109,9 +111,11 @@ filterConfigs: - "**/Sentry.Samples.Console.Basic.csproj" - "**/Sentry.Extensions.Logging.csproj" - "**/Sentry.csproj" + - "**/Sentry.Analyzers.csproj" - "**/Sentry.Testing.csproj" - "**/Sentry.Testing.CrashableApp.csproj" - "**/Sentry.Tests.csproj" + - "**/Sentry.Analyzers.Tests.csproj" - outputPath: SentryCore.slnf solution: .generated.NoMobile.sln @@ -122,10 +126,12 @@ filterConfigs: - "**/Sentry.Samples.Console.Profiling.csproj" - "**/Sentry.Profiling.csproj" - "**/Sentry.csproj" + - "**/Sentry.Analyzers.csproj" - "**/Sentry.Profiling.Tests.csproj" - "**/Sentry.Testing.csproj" - "**/Sentry.Testing.CrashableApp.csproj" - "**/Sentry.Tests.csproj" + - "**/Sentry.Analyzers.Tests.csproj" - outputPath: SentryDiagnosticSource.slnf solution: .generated.NoMobile.sln @@ -133,10 +139,12 @@ filterConfigs: patterns: - "**/*DiagnosticSource*.csproj" - "**/Sentry.csproj" + - "**/Sentry.Analyzers.csproj" - "**/Sentry.Extensions.Logging.csproj" - "**/Sentry.Testing.csproj" - "**/Sentry.Testing.CrashableApp.csproj" - "**/Sentry.Tests.csproj" + - "**/Sentry.Analyzers.Tests.csproj" - outputPath: SentryMobile.slnf include: @@ -146,6 +154,7 @@ filterConfigs: - "samples/**/*MacCatalyst.csproj" - "samples/**/*Maui.csproj" - "src/**/Sentry.csproj" + - "src/**/Sentry.Analyzers.csproj" - "src/**/*Android*.csproj" - "src/**/*Bindings.Android.csproj" - "src/**/*Bindings.Cocoa.csproj" @@ -157,6 +166,7 @@ filterConfigs: - "test/**/Sentry.Maui.Tests.csproj" - "test/**/Sentry.Testing.csproj" - "test/**/Sentry.Tests.csproj" + - "src/**/Sentry.Analyzers.Tests.csproj" - outputPath: SentryNoMobile.slnf solution: .generated.NoMobile.sln diff --git a/src/Sentry.Analyzers/AnalyzerReleases.Shipped.md b/src/Sentry.Analyzers/AnalyzerReleases.Shipped.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/Sentry.Analyzers/AnalyzerReleases.Unshipped.md b/src/Sentry.Analyzers/AnalyzerReleases.Unshipped.md new file mode 100644 index 0000000000..ba7c64a71c --- /dev/null +++ b/src/Sentry.Analyzers/AnalyzerReleases.Unshipped.md @@ -0,0 +1,5 @@ +### New Rules + +Rule ID | Category | Severity | Notes +--------|----------|----------|------- +SN0001 | Usage | Warning | FileSystemAnalyzer diff --git a/src/Sentry.Analyzers/FileSystemAnalyzer.cs b/src/Sentry.Analyzers/FileSystemAnalyzer.cs new file mode 100644 index 0000000000..18af086ce7 --- /dev/null +++ b/src/Sentry.Analyzers/FileSystemAnalyzer.cs @@ -0,0 +1,103 @@ +using System; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Sentry.Analyzers; + +/// +/// Analyzer that issues a warning if file system access is done outside IFileSystem wrapper implementations. +/// +[DiagnosticAnalyzer(LanguageNames.CSharp)] +public class FileSystemAnalyzer : DiagnosticAnalyzer +{ + private const string DiagnosticId = "SN0001"; + + private const string Title = "Use IFileSystem wrapper"; + private const string MessageFormat = "Route file system access through IFileSystem wrapper"; + private const string Category = "Usage"; + + private static readonly DiagnosticDescriptor Rule = new(DiagnosticId, Title, MessageFormat, Category, + DiagnosticSeverity.Warning, isEnabledByDefault: true); + + /// + /// Returns a set of descriptors for the diagnostics that this analyzer is capable of producing. + /// + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(Rule); + + /// + /// Called once at session start to register actions in the analysis context. + /// + /// + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.EnableConcurrentExecution(); + + context.RegisterSyntaxNodeAction( + AnalyzeNode, + SyntaxKind.ObjectCreationExpression, + SyntaxKind.SimpleMemberAccessExpression); + } + + private static void AnalyzeNode(SyntaxNodeAnalysisContext context) + { + var memberSymbol = context.SemanticModel.GetSymbolInfo(context.Node, context.CancellationToken).Symbol; + + if (memberSymbol is not { ContainingType.Name: ("Directory" or "File" or "FileInfo" or "DirectoryInfo") }) + { + return; + } + + if (ContainingTypeImplementsInterface(context, "Sentry.Internal.IFileSystem")) + { + // Allow direct file system access in IFileSystem implementations + return; + } + + context.ReportDiagnostic(Diagnostic.Create(Rule, context.Node.GetLocation())); + } + + private static bool ContainingTypeImplementsInterface(SyntaxNodeAnalysisContext context, string interfaceName) + { + if (!TryFindDeclarationForNode(context, out var typeDeclarationSyntax)) + { + return false; + } + + var namedTypeSymbol = context.SemanticModel.GetDeclaredSymbol(typeDeclarationSyntax, context.CancellationToken); + if (namedTypeSymbol == null) + { + return false; + } + + return namedTypeSymbol.AllInterfaces.Any(i => i.ToDisplayString() == interfaceName); + } + + private static bool TryFindDeclarationForNode( + SyntaxNodeAnalysisContext context, + [NotNullWhen(true)] out TypeDeclarationSyntax? typeDeclarationSyntax) + { + var parentClassNode = context.Node; + + // Traverse parent nodes until type declaration is found + while (parentClassNode is not TypeDeclarationSyntax && parentClassNode != null) + { + parentClassNode = parentClassNode.Parent; + } + + if (parentClassNode is not TypeDeclarationSyntax typeDeclaration) + { + typeDeclarationSyntax = null; + return false; + } + + typeDeclarationSyntax = typeDeclaration; + return true; + } +} diff --git a/src/Sentry.Analyzers/README.md b/src/Sentry.Analyzers/README.md new file mode 100644 index 0000000000..2f1456e010 --- /dev/null +++ b/src/Sentry.Analyzers/README.md @@ -0,0 +1 @@ +Analyzer ensures that all file system access continues to be routed through the SDK’s custom wrappers. It is meant for consumption from `Sentry` itself, for development of the SDK. For details see [issue #3643.](https://github.com/getsentry/sentry-dotnet/issues/3643) diff --git a/src/Sentry.Analyzers/Sentry.Analyzers.csproj b/src/Sentry.Analyzers/Sentry.Analyzers.csproj new file mode 100644 index 0000000000..e82ec6a6c8 --- /dev/null +++ b/src/Sentry.Analyzers/Sentry.Analyzers.csproj @@ -0,0 +1,40 @@ + + + + netstandard2.0 + false + enable + latest + + true + true + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Sentry/Infrastructure/FileDiagnosticLogger.cs b/src/Sentry/Infrastructure/FileDiagnosticLogger.cs index 2d067a1850..e3f99238a1 100644 --- a/src/Sentry/Infrastructure/FileDiagnosticLogger.cs +++ b/src/Sentry/Infrastructure/FileDiagnosticLogger.cs @@ -30,7 +30,10 @@ public FileDiagnosticLogger(string path, bool alsoWriteToConsole = false) public FileDiagnosticLogger(string path, SentryLevel minimalLevel, bool alsoWriteToConsole = false) : base(minimalLevel) { + // Allow direct file system usage +#pragma warning disable SN0001 var stream = File.OpenWrite(path); +#pragma warning restore SN0001 _writer = new StreamWriter(stream); _alsoWriteToConsole = alsoWriteToConsole; diff --git a/src/Sentry/Platforms/Android/AndroidHelpers.cs b/src/Sentry/Platforms/Android/AndroidHelpers.cs index 3138e551ab..c33d404e08 100644 --- a/src/Sentry/Platforms/Android/AndroidHelpers.cs +++ b/src/Sentry/Platforms/Android/AndroidHelpers.cs @@ -31,7 +31,10 @@ public static IList GetSupportedAbis() return null; } + // Allow direct file system usage +#pragma warning disable SN0001 if (!File.Exists(apkPath)) +#pragma warning restore SN0001 { logger?.LogWarning("APK doesn't exist at {0}", apkPath); return null; diff --git a/src/Sentry/Platforms/Native/CFunctions.cs b/src/Sentry/Platforms/Native/CFunctions.cs index 5079a9f271..05be757fa9 100644 --- a/src/Sentry/Platforms/Native/CFunctions.cs +++ b/src/Sentry/Platforms/Native/CFunctions.cs @@ -158,8 +158,11 @@ internal static string GetCacheDirectory(SentryOptions options) { if (options.CacheDirectoryPath is null) { + // Allow direct file system usage - Not application for Nintendo Switch +#pragma warning disable SN0001 // same as the default of sentry-native return Path.Combine(Directory.GetCurrentDirectory(), ".sentry-native"); +#pragma warning restore SN0001 } else { diff --git a/src/Sentry/Sentry.csproj b/src/Sentry/Sentry.csproj index 1987be5063..06bc0e76c0 100644 --- a/src/Sentry/Sentry.csproj +++ b/src/Sentry/Sentry.csproj @@ -49,6 +49,10 @@ + + + + $(DefineConstants);HAS_DIAGNOSTIC_INTEGRATION @@ -182,6 +186,7 @@ + diff --git a/test/Sentry.Analyzers.Tests/FileSystemAnalyzerTests.cs b/test/Sentry.Analyzers.Tests/FileSystemAnalyzerTests.cs new file mode 100644 index 0000000000..588957fe99 --- /dev/null +++ b/test/Sentry.Analyzers.Tests/FileSystemAnalyzerTests.cs @@ -0,0 +1,145 @@ +using Microsoft.CodeAnalysis; +using Verifier = + Microsoft.CodeAnalysis.CSharp.Testing.CSharpAnalyzerVerifier; + +namespace Sentry.Analyzers.Tests; + +public class FileSystemAnalyzerTests +{ + [Fact] + public async Task Verify_UsingMethodsOnFileClass_TriggersAUseFileSystemWrapperWarning() + { + const string text = """ + using System.IO; + + namespace Sentry.Internal; + + public class SomeClass + { + public void SomeMethod() + { + var t = {|SN0001:File.Exists|}("fileName.txt"); + } + } + + """; + + await Verifier.VerifyAnalyzerAsync(text); + } + + [Fact] + public async Task Verify_UsingMethodsOnDirectoryClass_TriggersAUseFileSystemWrapperWarning() + { + const string text = """ + using System.IO; + + namespace Sentry.Internal; + + public class SomeClass + { + public void SomeMethod() + { + var t = {|SN0001:Directory.Exists|}("fileName.txt"); + } + } + + """; + + await Verifier.VerifyAnalyzerAsync(text); + } + + [Fact] + public async Task Verify_UsingFileInfoClass_TriggersAUseFileSystemWrapperWarning() + { + const string text = """ + using System.IO; + + namespace Sentry.Internal; + + public class SomeClass + { + public void SomeMethod() + { + var f = {|SN0001:new FileInfo("fileName.txt")|}; + var t = {|SN0001:f.Exists|}; + } + } + + """; + + await Verifier.VerifyAnalyzerAsync(text); + } + + [Fact] + public async Task Verify_UsingDirectoryInfoClass_TriggersAUseFileSystemWrapperWarning() + { + const string text = """ + using System.IO; + + namespace Sentry.Internal; + + public class SomeClass + { + public void SomeMethod() + { + var d = {|SN0001:new DirectoryInfo("directoryName")|}; + var t = {|SN0001:d.Exists|}; + } + } + + """; + + await Verifier.VerifyAnalyzerAsync(text); + } + + [Fact] + public async Task Verify_AccessingFileSystemWithinIFileSystemImplementation_ProducesNoWarning() + { + const string text = """ + using System.IO; + + namespace Sentry.Internal; + + public class SomeClass : IFileSystem + { + public void SomeMethod() + { + var d = new DirectoryInfo("directoryName"); + } + } + + public interface IFileSystem {} + """; + + await Verifier.VerifyAnalyzerAsync(text); + } + + [Fact] + public async Task Verify_UsingFileSystemOutsideOfContainingType_TriggersAUseFileSystemWrapperWarning() + { + // CS8805 error is not relevant for the test, but is for top level statement + const string text = """ + using System.IO; + {|CS8805:var t = {|SN0001:File.Exists|}("fileName.txt");|} + """; + + await Verifier.VerifyAnalyzerAsync(text); + } + + [Fact] + public async Task Verify_ContainingTypeNull_DoesNotThrow() + { + // Containing type will be null when namespace member access will be checked. + var testCode = """ + internal class TestClass + { + public void TestMethod() + { + _ = System.String.Empty; + } + } + """; + + await Verifier.VerifyAnalyzerAsync(testCode); + } +} diff --git a/test/Sentry.Analyzers.Tests/Sentry.Analyzers.Tests.csproj b/test/Sentry.Analyzers.Tests/Sentry.Analyzers.Tests.csproj new file mode 100644 index 0000000000..9750861210 --- /dev/null +++ b/test/Sentry.Analyzers.Tests/Sentry.Analyzers.Tests.csproj @@ -0,0 +1,25 @@ + + + + net8.0 + enable + + false + + + + + + + + + + + + + + + + + + From d93f96b65534421c81d05c31fe9df6a931dd4ad9 Mon Sep 17 00:00:00 2001 From: Brice Friha <37577669+bricefriha@users.noreply.github.com> Date: Wed, 20 Nov 2024 09:26:56 +0000 Subject: [PATCH 037/363] fix: `Release` Builds with Trimming enabled crashing on launch with NLog Integration (#3743) --- CHANGELOG.md | 3 ++- src/Sentry.NLog/ConfigurationExtensions.cs | 6 +++++- src/Sentry.NLog/Sentry.NLog.csproj | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 809be9caf8..514e1dd26a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,7 +26,8 @@ ### Fixes -- Fixed ArgumentNullException in FormRequestPayloadExtractor when handling invalid form data on ASP.NET ([#3734](https://github.com/getsentry/sentry-dotnet/pull/3734)) +- ArgumentNullException in FormRequestPayloadExtractor when handling invalid form data on ASP.NET ([#3734](https://github.com/getsentry/sentry-dotnet/pull/3734)) +- Crash when using NLog with FailedRequestStatusCodes options in a Maui app with Trimming enabled ([#3743](https://github.com/getsentry/sentry-dotnet/pull/3743)) ### Dependencies diff --git a/src/Sentry.NLog/ConfigurationExtensions.cs b/src/Sentry.NLog/ConfigurationExtensions.cs index c58ad24baa..596fb9bca0 100644 --- a/src/Sentry.NLog/ConfigurationExtensions.cs +++ b/src/Sentry.NLog/ConfigurationExtensions.cs @@ -83,7 +83,11 @@ public static LoggingConfiguration AddSentry( optionsConfig?.Invoke(options); - LogManager.Setup().SetupExtensions(e => e.RegisterTarget("Sentry")); + LogManager.Setup().SetupExtensions(e => + { + e.RegisterTarget("Sentry"); + e.RegisterType(); + }); var target = new SentryTarget(options) { diff --git a/src/Sentry.NLog/Sentry.NLog.csproj b/src/Sentry.NLog/Sentry.NLog.csproj index d82ae44fe1..1fba6ee962 100644 --- a/src/Sentry.NLog/Sentry.NLog.csproj +++ b/src/Sentry.NLog/Sentry.NLog.csproj @@ -12,7 +12,7 @@ - + From 91a813859c5644058c2eab86e3490915ebfe5c6a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 22 Nov 2024 08:39:10 +1300 Subject: [PATCH 038/363] chore: update modules/sentry-native to 0.7.14 (#3775) Co-authored-by: GitHub --- CHANGELOG.md | 8 ++++++++ modules/sentry-native | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 514e1dd26a..1bcbcba917 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## Unreleased + +### Dependencies + +- Bump Native SDK from v0.7.13 to v0.7.14 ([#3775](https://github.com/getsentry/sentry-dotnet/pull/3775)) + - [changelog](https://github.com/getsentry/sentry-native/blob/master/CHANGELOG.md#0714) + - [diff](https://github.com/getsentry/sentry-native/compare/0.7.13...0.7.14) + ## Version Five ### API Changes diff --git a/modules/sentry-native b/modules/sentry-native index 0f98b0d69c..6227eeaf17 160000 --- a/modules/sentry-native +++ b/modules/sentry-native @@ -1 +1 @@ -Subproject commit 0f98b0d69c86ab26873899cbf532152656264790 +Subproject commit 6227eeaf174021c8356a7377d4e3a20a22b666c0 From 7bda98c06deaffea02c9bca97d65f27accec7f45 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Fri, 22 Nov 2024 15:42:49 +1300 Subject: [PATCH 039/363] Update changelog for unreleased v5 changes (#3778) --- CHANGELOG.md | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1bcbcba917..44f7d7888c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,14 +2,6 @@ ## Unreleased -### Dependencies - -- Bump Native SDK from v0.7.13 to v0.7.14 ([#3775](https://github.com/getsentry/sentry-dotnet/pull/3775)) - - [changelog](https://github.com/getsentry/sentry-native/blob/master/CHANGELOG.md#0714) - - [diff](https://github.com/getsentry/sentry-native/compare/0.7.13...0.7.14) - -## Version Five - ### API Changes - You should no longer pass `AndroidContext` as an argument to `SentrySdk.Init` ([#3562](https://github.com/getsentry/sentry-dotnet/pull/3562)) @@ -29,11 +21,6 @@ ### Fixes - Fixed NullReferenceException in SentryTraceHeader when parsing null or empty values ([#3757](https://github.com/getsentry/sentry-dotnet/pull/3757)) - -## Unreleased - -### Fixes - - ArgumentNullException in FormRequestPayloadExtractor when handling invalid form data on ASP.NET ([#3734](https://github.com/getsentry/sentry-dotnet/pull/3734)) - Crash when using NLog with FailedRequestStatusCodes options in a Maui app with Trimming enabled ([#3743](https://github.com/getsentry/sentry-dotnet/pull/3743)) @@ -42,12 +29,15 @@ - Bump Cocoa SDK from v8.36.0 to v8.39.0 ([#3727](https://github.com/getsentry/sentry-dotnet/pull/3727)) - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#8390) - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.36.0...8.39.0) -- Bump Native SDK from v0.7.11 to v0.7.12 ([#3731](https://github.com/getsentry/sentry-dotnet/pull/3731)) - - [changelog](https://github.com/getsentry/sentry-native/blob/master/CHANGELOG.md#0712) - - [diff](https://github.com/getsentry/sentry-native/compare/0.7.11...0.7.12) - Bump Java SDK from v7.16.0 to v7.18.0 ([#3749](https://github.com/getsentry/sentry-dotnet/pull/3749), [#3771](https://github.com/getsentry/sentry-dotnet/pull/3771)) - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#7180) - [diff](https://github.com/getsentry/sentry-java/compare/7.16.0...7.18.0) +- Bump Native SDK from v0.7.11 to v0.7.12 ([#3731](https://github.com/getsentry/sentry-dotnet/pull/3731)) + - [changelog](https://github.com/getsentry/sentry-native/blob/master/CHANGELOG.md#0712) + - [diff](https://github.com/getsentry/sentry-native/compare/0.7.11...0.7.12) +- Bump Native SDK from v0.7.13 to v0.7.14 ([#3775](https://github.com/getsentry/sentry-dotnet/pull/3775)) + - [changelog](https://github.com/getsentry/sentry-native/blob/master/CHANGELOG.md#0714) + - [diff](https://github.com/getsentry/sentry-native/compare/0.7.13...0.7.14) ## 4.13.0 From 86af8f043b9d9e5f8c970f142df59800bced3bd2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 22 Nov 2024 08:29:02 +0100 Subject: [PATCH 040/363] chore(deps): update Native SDK to v0.7.15 (#3779) * chore: update modules/sentry-native to 0.7.15 * Update CHANGELOG.md --------- Co-authored-by: GitHub Co-authored-by: Ivan Dlugos <6349682+vaind@users.noreply.github.com> --- CHANGELOG.md | 9 +++------ modules/sentry-native | 2 +- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 44f7d7888c..866d5d9fd6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,12 +32,9 @@ - Bump Java SDK from v7.16.0 to v7.18.0 ([#3749](https://github.com/getsentry/sentry-dotnet/pull/3749), [#3771](https://github.com/getsentry/sentry-dotnet/pull/3771)) - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#7180) - [diff](https://github.com/getsentry/sentry-java/compare/7.16.0...7.18.0) -- Bump Native SDK from v0.7.11 to v0.7.12 ([#3731](https://github.com/getsentry/sentry-dotnet/pull/3731)) - - [changelog](https://github.com/getsentry/sentry-native/blob/master/CHANGELOG.md#0712) - - [diff](https://github.com/getsentry/sentry-native/compare/0.7.11...0.7.12) -- Bump Native SDK from v0.7.13 to v0.7.14 ([#3775](https://github.com/getsentry/sentry-dotnet/pull/3775)) - - [changelog](https://github.com/getsentry/sentry-native/blob/master/CHANGELOG.md#0714) - - [diff](https://github.com/getsentry/sentry-native/compare/0.7.13...0.7.14) +- Bump Native SDK from v0.7.11 to v0.7.15 ([#3731](https://github.com/getsentry/sentry-dotnet/pull/3731), [#3770](https://github.com/getsentry/sentry-dotnet/pull/3770), [#3775](https://github.com/getsentry/sentry-dotnet/pull/3775), [#3779](https://github.com/getsentry/sentry-dotnet/pull/3779)) + - [changelog](https://github.com/getsentry/sentry-native/blob/master/CHANGELOG.md#0715) + - [diff](https://github.com/getsentry/sentry-native/compare/0.7.11...0.7.15) ## 4.13.0 diff --git a/modules/sentry-native b/modules/sentry-native index 6227eeaf17..a2bd91c7fe 160000 --- a/modules/sentry-native +++ b/modules/sentry-native @@ -1 +1 @@ -Subproject commit 6227eeaf174021c8356a7377d4e3a20a22b666c0 +Subproject commit a2bd91c7fe29efb9c47f6dfd680bae103b60adbd From 3832bcc15aab98ad642470c52515ef144304ab96 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos <6349682+vaind@users.noreply.github.com> Date: Fri, 22 Nov 2024 12:23:41 +0100 Subject: [PATCH 041/363] feat: options.AddProfilingIntegration() (#3660) * feat: options.AddProfilingIntegration() * chore: update changelog * chore: update changelog * chore: add ProfilingIntegration to cocoa * check prior to adding the default integration --------- Co-authored-by: Bruno Garcia --- CHANGELOG.md | 1 + .../Program.cs | 2 +- .../Program.cs | 2 +- src/Sentry.Profiling/ProfilingIntegration.cs | 4 + .../SentryOptionsProfilingExtensions.cs | 44 +++++++++++ .../Platforms/Cocoa/ProfilingIntegration.cs | 31 ++++++++ src/Sentry/Platforms/Cocoa/SentryOptions.cs | 36 +++++++++ src/Sentry/Platforms/Cocoa/SentrySdk.cs | 7 +- src/Sentry/SentrySdk.cs | 2 +- .../ProfilingSentryOptionsExtensionsTests.cs | 76 +++++++++++++++++++ .../Resources/README.md | 2 +- .../SamplingTransactionProfilerTests.cs | 10 +-- 12 files changed, 204 insertions(+), 13 deletions(-) create mode 100644 src/Sentry.Profiling/SentryOptionsProfilingExtensions.cs create mode 100644 src/Sentry/Platforms/Cocoa/ProfilingIntegration.cs create mode 100644 test/Sentry.Profiling.Tests/ProfilingSentryOptionsExtensionsTests.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 866d5d9fd6..d000372eb9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ ### Features - Added support for `.NET 9` (preview) ([#3699](https://github.com/getsentry/sentry-dotnet/pull/3699)) - libsentrysupplemental.so now supports 16 KB page sizes on Android ([#3723](https://github.com/getsentry/sentry-dotnet/pull/3723)) +- Added `SentryOptions` extension for profiling: `options.AddProfilingIntegration()` ([#3660](https://github.com/getsentry/sentry-dotnet/pull/3660)) ### Fixes diff --git a/samples/Sentry.Samples.AspNetCore.WebAPI.Profiling/Program.cs b/samples/Sentry.Samples.AspNetCore.WebAPI.Profiling/Program.cs index dde356bb5d..07b0147102 100644 --- a/samples/Sentry.Samples.AspNetCore.WebAPI.Profiling/Program.cs +++ b/samples/Sentry.Samples.AspNetCore.WebAPI.Profiling/Program.cs @@ -3,7 +3,7 @@ builder.WebHost.UseSentry(o => { o.Dsn = "https://eb18e953812b41c3aeb042e666fd3b5c@o447951.ingest.sentry.io/5428537"; - o.AddIntegration(new ProfilingIntegration()); + o.AddProfilingIntegration(); o.ProfilesSampleRate = 0.1; o.TracesSampleRate = 1.0; }); diff --git a/samples/Sentry.Samples.Console.Profiling/Program.cs b/samples/Sentry.Samples.Console.Profiling/Program.cs index bea41d8756..7c0b1528f4 100644 --- a/samples/Sentry.Samples.Console.Profiling/Program.cs +++ b/samples/Sentry.Samples.Console.Profiling/Program.cs @@ -22,7 +22,7 @@ private static void Main() // Debugging options.ShutdownTimeout = TimeSpan.FromMinutes(5); - options.AddIntegration(new ProfilingIntegration(TimeSpan.FromMilliseconds(500))); + options.AddProfilingIntegration(TimeSpan.FromMilliseconds(500)); })) { var tx = SentrySdk.StartTransaction("app", "run"); diff --git a/src/Sentry.Profiling/ProfilingIntegration.cs b/src/Sentry.Profiling/ProfilingIntegration.cs index d88eee852e..195dfa9dc8 100644 --- a/src/Sentry.Profiling/ProfilingIntegration.cs +++ b/src/Sentry.Profiling/ProfilingIntegration.cs @@ -42,5 +42,9 @@ public void Register(IHub hub, SentryOptions options) options.LogError(e, "Failed to initialize the profiler"); } } + else + { + options.LogInfo("Profiling Integration is disabled because profiling is disabled by configuration."); + } } } diff --git a/src/Sentry.Profiling/SentryOptionsProfilingExtensions.cs b/src/Sentry.Profiling/SentryOptionsProfilingExtensions.cs new file mode 100644 index 0000000000..2988912400 --- /dev/null +++ b/src/Sentry.Profiling/SentryOptionsProfilingExtensions.cs @@ -0,0 +1,44 @@ +using Sentry.Extensibility; +using Sentry.Profiling; + +namespace Sentry; + +/// +/// The additional Sentry Options extensions from Sentry Profiling. +/// +[EditorBrowsable(EditorBrowsableState.Never)] +public static class SentryOptionsProfilingExtensions +{ + /// + /// Adds ProfilingIntegration to Sentry. + /// + /// The Sentry options. + /// + /// If not given or TimeSpan.Zero, then the profiler initialization is asynchronous. + /// This is useful for applications that need to start quickly. The profiler will start in the background + /// and will be ready to capture transactions that have started after the profiler has started. + /// + /// If given a non-zero timeout, profiling startup blocks up to the given amount of time. If the timeout is reached + /// and the profiler session hasn't started yet, the execution is unblocked and behaves as the async startup, + /// i.e. transactions will be profiled only after the session is eventually started. + /// + public static void AddProfilingIntegration(this SentryOptions options, TimeSpan startupTimeout = default) + { + if (options.HasIntegration()) + { + options.LogWarning($"{nameof(ProfilingIntegration)} has already been added. The second call to {nameof(AddProfilingIntegration)} will be ignored."); + return; + } + + options.AddIntegration(new ProfilingIntegration(startupTimeout)); + } + + /// + /// Disables the Profiling integration. + /// + /// The SentryOptions to remove the integration from. + public static void DisableProfilingIntegration(this SentryOptions options) + { + options.RemoveIntegration(); + } +} diff --git a/src/Sentry/Platforms/Cocoa/ProfilingIntegration.cs b/src/Sentry/Platforms/Cocoa/ProfilingIntegration.cs new file mode 100644 index 0000000000..9114ac6c0d --- /dev/null +++ b/src/Sentry/Platforms/Cocoa/ProfilingIntegration.cs @@ -0,0 +1,31 @@ +using Sentry.Extensibility; +using Sentry.Integrations; + +namespace Sentry.Cocoa; + +/// +/// Enables transaction performance profiling. +/// +public class ProfilingIntegration : ISdkIntegration +{ + /// + public void Register(IHub hub, SentryOptions options) + { + if (options.IsProfilingEnabled) + { + try + { + options.LogDebug("Profiling is enabled, attaching native SDK profiler factory"); + options.TransactionProfilerFactory ??= new CocoaProfilerFactory(options); + } + catch (Exception e) + { + options.LogError(e, "Failed to initialize the profiler"); + } + } + else + { + options.LogInfo("Profiling Integration is disabled because profiling is disabled by configuration."); + } + } +} diff --git a/src/Sentry/Platforms/Cocoa/SentryOptions.cs b/src/Sentry/Platforms/Cocoa/SentryOptions.cs index c14213087a..ae28a38afb 100644 --- a/src/Sentry/Platforms/Cocoa/SentryOptions.cs +++ b/src/Sentry/Platforms/Cocoa/SentryOptions.cs @@ -1,4 +1,6 @@ using ObjCRuntime; +using Sentry.Cocoa; +using Sentry.Extensibility; // ReSharper disable once CheckNamespace namespace Sentry; @@ -215,4 +217,38 @@ public void AddInAppInclude(string prefix) InAppIncludes.Add(prefix); } } + + // We actually add the profiling integration automatically in InitSentryCocoaSdk(). + // However, if user calls AddProfilingIntegration() multiple times, we print a warning, as usual. + private bool _profilingIntegrationAddedByUser = false; + + /// + /// Adds ProfilingIntegration to Sentry. + /// + /// + /// Unused, only here so that the signature is the same as AddProfilingIntegration() from package Sentry.Profiling. + /// + public void AddProfilingIntegration(TimeSpan startupTimeout = default) + { + if (HasIntegration()) + { + if (_profilingIntegrationAddedByUser) + { + DiagnosticLogger?.LogWarning($"{nameof(ProfilingIntegration)} has already been added. The second call to {nameof(AddProfilingIntegration)} will be ignored."); + } + return; + } + + _profilingIntegrationAddedByUser = true; + AddIntegration(new ProfilingIntegration()); + } + + /// + /// Disables the Profiling integration. + /// + public void DisableProfilingIntegration() + { + _profilingIntegrationAddedByUser = false; + RemoveIntegration(); + } } diff --git a/src/Sentry/Platforms/Cocoa/SentrySdk.cs b/src/Sentry/Platforms/Cocoa/SentrySdk.cs index 5adc96c249..812733c7c9 100644 --- a/src/Sentry/Platforms/Cocoa/SentrySdk.cs +++ b/src/Sentry/Platforms/Cocoa/SentrySdk.cs @@ -185,12 +185,11 @@ private static void InitSentryCocoaSdk(SentryOptions options) options.EnableScopeSync = true; options.ScopeObserver = new CocoaScopeObserver(options); - if (options.IsProfilingEnabled) + // Note: don't use AddProfilingIntegration as it would print a warning if user used it too. + if (!options.HasIntegration()) { - options.LogDebug("Profiling is enabled, attaching native SDK profiler factory"); - options.TransactionProfilerFactory ??= new CocoaProfilerFactory(options); + options.AddIntegration(new ProfilingIntegration()); } - // TODO: Pause/Resume } diff --git a/src/Sentry/SentrySdk.cs b/src/Sentry/SentrySdk.cs index 4fb044be15..cfd0fe2956 100644 --- a/src/Sentry/SentrySdk.cs +++ b/src/Sentry/SentrySdk.cs @@ -102,7 +102,7 @@ internal static IHub InitHub(SentryOptions options) #endif { LogWarningIfProfilingMisconfigured(options, ", because ProfilingIntegration from package Sentry.Profiling" + - " hasn't been registered. You can do that by calling 'options.AddIntegration(new ProfilingIntegration())'"); + " hasn't been registered. You can do that by calling 'options.AddProfilingIntegration()'"); } #endif diff --git a/test/Sentry.Profiling.Tests/ProfilingSentryOptionsExtensionsTests.cs b/test/Sentry.Profiling.Tests/ProfilingSentryOptionsExtensionsTests.cs new file mode 100644 index 0000000000..5e1c160d9f --- /dev/null +++ b/test/Sentry.Profiling.Tests/ProfilingSentryOptionsExtensionsTests.cs @@ -0,0 +1,76 @@ +namespace Sentry.Profiling.Tests; + +#nullable enable + +[UsesVerify] + +public class ProfilingSentryOptionsExtensionsTests +{ + private readonly InMemoryDiagnosticLogger _logger = new(); + private readonly SentryOptions _options = new() + { + Dsn = ValidDsn, + AutoSessionTracking = false, + IsGlobalModeEnabled = true, + BackgroundWorker = Substitute.For(), + Debug = true, + + // Set explicitly for this test in case the defaults change in the future. + TracesSampleRate = 0.0, + TracesSampler = null + }; + + public ProfilingSentryOptionsExtensionsTests() + { + _options.DiagnosticLogger = _logger; + _options.AddProfilingIntegration(); + } + + private Hub GetSut() => new(_options, Substitute.For()); + + private static IEnumerable GetIntegrations(ISentryClient hub) => + hub.GetSentryOptions()?.Integrations ?? Enumerable.Empty(); + + [Fact] + public void Integration_DisabledWithDefaultOptions() + { + using var hub = GetSut(); + var integrations = GetIntegrations(hub); + Assert.Contains(_logger.Entries, x => x.Message == "Profiling Integration is disabled because profiling is disabled by configuration." + && x.Level == SentryLevel.Info); + } + + [Fact] + public void Integration_EnabledBySampleRate() + { + _options.TracesSampleRate = 1.0; + _options.ProfilesSampleRate = 1.0; + + using var hub = GetSut(); + var integrations = GetIntegrations(hub); + Assert.Contains(integrations, i => i is ProfilingIntegration); + } + + [Fact] + public void DisableProfilingIntegration_RemovesProfilingIntegration() + { + _options.TracesSampleRate = 1.0; + _options.ProfilesSampleRate = 1.0; + _options.DisableProfilingIntegration(); + + using var hub = GetSut(); + var integrations = GetIntegrations(hub); + Assert.DoesNotContain(integrations, i => i is ProfilingIntegration); + } + + [Fact] + public void AddProfilingIntegration_DoesntDuplicate() + { + var options = new SentryOptions(); + + options.AddProfilingIntegration(); + options.AddProfilingIntegration(); + + Assert.Single(options.Integrations, x => x is ProfilingIntegration); + } +} diff --git a/test/Sentry.Profiling.Tests/Resources/README.md b/test/Sentry.Profiling.Tests/Resources/README.md index 9d90debc08..db5693a9ee 100644 --- a/test/Sentry.Profiling.Tests/Resources/README.md +++ b/test/Sentry.Profiling.Tests/Resources/README.md @@ -22,7 +22,7 @@ public static int Main(string[] args) o.Dsn = DefaultDsn; o.Debug = true; o.TracesSampleRate = 1.0; - o.AddIntegration(new ProfilingIntegration(Path.GetTempPath())); + o.AddProfilingIntegration(); o.DiagnosticLogger = new FileAppenderDiagnosticLogger("C:/dev/Aura.UI/test.log", SentryLevel.Debug); })) { diff --git a/test/Sentry.Profiling.Tests/SamplingTransactionProfilerTests.cs b/test/Sentry.Profiling.Tests/SamplingTransactionProfilerTests.cs index ab9693307e..216ffe28e2 100644 --- a/test/Sentry.Profiling.Tests/SamplingTransactionProfilerTests.cs +++ b/test/Sentry.Profiling.Tests/SamplingTransactionProfilerTests.cs @@ -227,7 +227,7 @@ async Task VerifyAsync(HttpRequestMessage message) // Disable process exit flush to resolve "There is no currently active test." errors. options.DisableAppDomainProcessExitFlush(); - options.AddIntegration(new ProfilingIntegration(TimeSpan.FromSeconds(10))); + options.AddProfilingIntegration(TimeSpan.FromSeconds(10)); try { @@ -318,7 +318,7 @@ async Task VerifyAsync(HttpRequestMessage message) ProfilesSampleRate = 1.0, }; - options.AddIntegration(new ProfilingIntegration(TimeSpan.FromSeconds(10))); + options.AddProfilingIntegration(TimeSpan.FromSeconds(10)); try { @@ -365,7 +365,7 @@ public void ProfilerIntegration_WithProfilingDisabled_LeavesFactoryNull() TracesSampleRate = 1.0, ProfilesSampleRate = 0, }; - options.AddIntegration(new ProfilingIntegration()); + options.AddProfilingIntegration(); using var hub = new Hub(options); Assert.Null(hub.Options.TransactionProfilerFactory); } @@ -379,7 +379,7 @@ public void ProfilerIntegration_WithTracingDisabled_LeavesFactoryNull() TracesSampleRate = 0, ProfilesSampleRate = 1.0, }; - options.AddIntegration(new ProfilingIntegration()); + options.AddProfilingIntegration(); using var hub = new Hub(options); Assert.Null(hub.Options.TransactionProfilerFactory); } @@ -393,7 +393,7 @@ public void ProfilerIntegration_WithProfilingEnabled_SetsFactory() TracesSampleRate = 1.0, ProfilesSampleRate = 1.0, }; - options.AddIntegration(new ProfilingIntegration()); + options.AddProfilingIntegration(); using var hub = new Hub(options); Assert.NotNull(hub.Options.TransactionProfilerFactory); } From 2ec949f8609536be48d94009554e68ed9b89b239 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos <6349682+vaind@users.noreply.github.com> Date: Sun, 24 Nov 2024 01:12:54 +0100 Subject: [PATCH 042/363] ci: change updater PR strategy to update (#3780) The default (which was the previous behaviour when the udpater was created) creates new branch & PR for every release. The "update" strategy updates a single PR per dependency. --- .github/workflows/update-deps.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/update-deps.yml b/.github/workflows/update-deps.yml index 5fccddf94a..626240a456 100644 --- a/.github/workflows/update-deps.yml +++ b/.github/workflows/update-deps.yml @@ -26,5 +26,6 @@ jobs: with: name: ${{ matrix.name }} path: ${{ matrix.path }} + pr-strategy: update secrets: api-token: ${{ secrets.CI_DEPLOY_KEY }} From ecc58120104d94213f0b798b26a0789fd9413632 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 24 Nov 2024 19:50:04 +0100 Subject: [PATCH 043/363] chore(deps): update CLI to v2.39.0 (#3782) Co-authored-by: GitHub --- CHANGELOG.md | 3 +++ Directory.Build.props | 2 +- src/Sentry/Sentry.csproj | 14 +++++++------- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d000372eb9..8cf5a415cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,9 @@ - Bump Native SDK from v0.7.11 to v0.7.15 ([#3731](https://github.com/getsentry/sentry-dotnet/pull/3731), [#3770](https://github.com/getsentry/sentry-dotnet/pull/3770), [#3775](https://github.com/getsentry/sentry-dotnet/pull/3775), [#3779](https://github.com/getsentry/sentry-dotnet/pull/3779)) - [changelog](https://github.com/getsentry/sentry-native/blob/master/CHANGELOG.md#0715) - [diff](https://github.com/getsentry/sentry-native/compare/0.7.11...0.7.15) +- Bump CLI from v2.38.2 to v2.39.0 ([#3782](https://github.com/getsentry/sentry-dotnet/pull/3782)) + - [changelog](https://github.com/getsentry/sentry-cli/blob/master/CHANGELOG.md#2390) + - [diff](https://github.com/getsentry/sentry-cli/compare/2.38.2...2.39.0) ## 4.13.0 diff --git a/Directory.Build.props b/Directory.Build.props index d10026aaad..b516aa07f7 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -80,7 +80,7 @@ - 2.38.2 + 2.39.0 $(MSBuildThisFileDirectory)tools\sentry-cli\$(SentryCLIVersion)\ diff --git a/src/Sentry/Sentry.csproj b/src/Sentry/Sentry.csproj index 06bc0e76c0..d66af6163b 100644 --- a/src/Sentry/Sentry.csproj +++ b/src/Sentry/Sentry.csproj @@ -120,13 +120,13 @@ <_OSArchitecture>$([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture) - - - - - - - + + + + + + + From 0d33f4f2a1736fcbfdad38326534e316a3d976f1 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Mon, 25 Nov 2024 12:53:56 +1300 Subject: [PATCH 044/363] Remove duplicate Sentry.Bindings.Android.targets from NuGet (#3784) --- src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj b/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj index d98083bee4..a437fc4975 100644 --- a/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj +++ b/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj @@ -12,8 +12,11 @@ + - From 9fd5f83dd573c5c62a9b64864605acef4f761a4e Mon Sep 17 00:00:00 2001 From: getsentry-bot Date: Mon, 25 Nov 2024 00:02:59 +0000 Subject: [PATCH 045/363] release: 5.0.0-alpha.1 --- CHANGELOG.md | 2 +- Directory.Build.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8cf5a415cb..2e8b54b607 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## Unreleased +## 5.0.0-alpha.1 ### API Changes diff --git a/Directory.Build.props b/Directory.Build.props index b516aa07f7..1f5581f379 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,7 +1,7 @@ - 4.13.0 + 5.0.0-alpha.1 12 true true From 9d7ab5a81aea0b7bf3dc745d75ebf55944ca72b1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 26 Nov 2024 10:17:47 +1300 Subject: [PATCH 046/363] build(deps): bump codecov/codecov-action from 5.0.2 to 5.0.7 (#3787) Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 5.0.2 to 5.0.7. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/5c47607acb93fed5485fdbf7232e8a31425f672a...015f24e6818733317a2da2edd6290ab26238649a) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fb90633fe9..d9f07ac142 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -121,7 +121,7 @@ jobs: run: dotnet test Sentry-CI-Build-${{ runner.os }}.slnf -c Release --no-build --nologo -l GitHubActions -l "trx;LogFilePrefix=testresults_${{ runner.os }}" --collect "XPlat Code Coverage" - name: Upload code coverage - uses: codecov/codecov-action@5c47607acb93fed5485fdbf7232e8a31425f672a + uses: codecov/codecov-action@015f24e6818733317a2da2edd6290ab26238649a - name: Upload build and test outputs if: failure() From f5e13c07a5fd604defa78187e36876fa2603453e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 26 Nov 2024 10:18:16 +1300 Subject: [PATCH 047/363] build(deps): bump github/codeql-action from 3.27.4 to 3.27.5 (#3788) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.27.4 to 3.27.5. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/ea9e4e37992a54ee68a9622e985e60c8e8f12d9f...f09c1c0a94de965c15400f5634aa42fac8fb8f88) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index e148f20af5..5df0e480d6 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -35,7 +35,7 @@ jobs: uses: ./.github/actions/environment - name: Initialize CodeQL - uses: github/codeql-action/init@ea9e4e37992a54ee68a9622e985e60c8e8f12d9f # pin@v2 + uses: github/codeql-action/init@f09c1c0a94de965c15400f5634aa42fac8fb8f88 # pin@v2 with: languages: csharp @@ -49,6 +49,6 @@ jobs: run: dotnet build Sentry-CI-CodeQL.slnf --no-restore --nologo - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@ea9e4e37992a54ee68a9622e985e60c8e8f12d9f # pin@v2 + uses: github/codeql-action/analyze@f09c1c0a94de965c15400f5634aa42fac8fb8f88 # pin@v2 with: category: '/language:csharp' From 5a0b8ce59479e163d67b7365c54a0730692d3eb0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 26 Nov 2024 10:18:40 +1300 Subject: [PATCH 048/363] build(deps): bump gradle/actions from 4.2.0 to 4.2.1 (#3789) Bumps [gradle/actions](https://github.com/gradle/actions) from 4.2.0 to 4.2.1. - [Release notes](https://github.com/gradle/actions/releases) - [Commits](https://github.com/gradle/actions/compare/473878a77f1b98e2b5ac4af93489d1656a80a5ed...cc4fc85e6b35bafd578d5ffbc76a5518407e1af0) --- updated-dependencies: - dependency-name: gradle/actions dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/device-tests-android.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/device-tests-android.yml b/.github/workflows/device-tests-android.yml index 299c08fe92..52a3c94a40 100644 --- a/.github/workflows/device-tests-android.yml +++ b/.github/workflows/device-tests-android.yml @@ -74,7 +74,7 @@ jobs: path: bin - name: Setup Gradle - uses: gradle/actions/setup-gradle@473878a77f1b98e2b5ac4af93489d1656a80a5ed # pin@v3 + uses: gradle/actions/setup-gradle@cc4fc85e6b35bafd578d5ffbc76a5518407e1af0 # pin@v3 # Cached AVD setup per https://github.com/ReactiveCircus/android-emulator-runner/blob/main/README.md From 3d1e1e2527008213ab7867d5075bdb60cc0f79fb Mon Sep 17 00:00:00 2001 From: Brice Friha <37577669+bricefriha@users.noreply.github.com> Date: Tue, 26 Nov 2024 21:10:26 +0000 Subject: [PATCH 049/363] MAUI - Sample | fix sample crash in .NET 9 (#3796) --- samples/Sentry.Samples.Maui/App.xaml.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/samples/Sentry.Samples.Maui/App.xaml.cs b/samples/Sentry.Samples.Maui/App.xaml.cs index c400ec6e5d..8d5a529251 100644 --- a/samples/Sentry.Samples.Maui/App.xaml.cs +++ b/samples/Sentry.Samples.Maui/App.xaml.cs @@ -9,7 +9,6 @@ public App() protected override Window CreateWindow(IActivationState activationState) { - Windows[0].Page = new AppShell(); - return base.CreateWindow(activationState); + return new Window(new AppShell()); } } From ae04948da11f7ce8a7f4b823009312b87e46ecc6 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos <6349682+vaind@users.noreply.github.com> Date: Wed, 27 Nov 2024 10:09:20 +0100 Subject: [PATCH 050/363] fix: cocoa bindings generated code line endings (#3800) --- scripts/generate-cocoa-bindings.ps1 | 6 +++--- src/Sentry.Bindings.Cocoa/Sentry.Bindings.Cocoa.csproj | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/generate-cocoa-bindings.ps1 b/scripts/generate-cocoa-bindings.ps1 index bac0ad65bc..1a9f7a6a9e 100644 --- a/scripts/generate-cocoa-bindings.ps1 +++ b/scripts/generate-cocoa-bindings.ps1 @@ -147,8 +147,8 @@ internal enum SentryTransactionNameSource : long } '@ -$Text += "`r`n$SentryLevel" -$Text += "`r`n$SentryTransactionNameSource" +$Text += "`n$SentryLevel" +$Text += "`n$SentryTransactionNameSource" # Add header and output file $Text = "$Header`n`n$Text" @@ -296,7 +296,7 @@ interface SentryId } '@ -$Text += "`r`n$SentryId" +$Text += "`n$SentryId" # Add header and output file $Text = "$Header`n`n$Text" diff --git a/src/Sentry.Bindings.Cocoa/Sentry.Bindings.Cocoa.csproj b/src/Sentry.Bindings.Cocoa/Sentry.Bindings.Cocoa.csproj index 5e61d87cce..563e5a3c37 100644 --- a/src/Sentry.Bindings.Cocoa/Sentry.Bindings.Cocoa.csproj +++ b/src/Sentry.Bindings.Cocoa/Sentry.Bindings.Cocoa.csproj @@ -91,7 +91,7 @@ From 9bea4f5d3e0c753b8180a395520d8512b9ea2fa7 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Wed, 27 Nov 2024 23:43:11 +1300 Subject: [PATCH 051/363] Pin Target Platform Version in mobile TFM monkiers (#3798) --- .github/workflows/build.yml | 2 +- .github/workflows/device-tests-android.yml | 2 +- Directory.Build.props | 2 +- integration-test/cli.Tests.ps1 | 11 +++++++---- .../Sentry.Samples.Android.csproj | 2 +- samples/Sentry.Samples.Ios/Sentry.Samples.Ios.csproj | 2 +- .../Sentry.Samples.MacCatalyst.csproj | 2 +- .../Sentry.Samples.Maui/Sentry.Samples.Maui.csproj | 6 +++--- scripts/device-test.ps1 | 4 ++-- .../Sentry.Bindings.Android.csproj | 2 +- .../Sentry.Bindings.Cocoa.csproj | 6 +++--- src/Sentry.Maui/Sentry.Maui.csproj | 6 +++--- src/Sentry/Sentry.csproj | 6 +++--- test/AndroidTestApp/AndroidTestApp.csproj | 2 +- .../Sentry.Android.AssemblyReader.Tests.csproj | 2 +- .../Sentry.Extensions.Logging.Tests.csproj | 6 +++--- .../Sentry.Maui.Device.TestApp.csproj | 4 ++-- test/Sentry.Maui.Tests/Sentry.Maui.Tests.csproj | 6 +++--- test/Sentry.Testing/Sentry.Testing.csproj | 6 +++--- test/Sentry.Tests/Sentry.Tests.csproj | 6 +++--- 20 files changed, 44 insertions(+), 41 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d9f07ac142..ba2f621abf 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -112,7 +112,7 @@ jobs: - name: Install Android SDKs if: runner.os == 'macOS' run: | - dotnet build src/Sentry/Sentry.csproj -t:InstallAndroidDependencies -f:net8.0-android -p:AcceptAndroidSDKLicenses=True -p:AndroidSdkPath="/usr/local/lib/android/sdk/" + dotnet build src/Sentry/Sentry.csproj -t:InstallAndroidDependencies -f:net8.0-android34.0 -p:AcceptAndroidSDKLicenses=True -p:AndroidSdkPath="/usr/local/lib/android/sdk/" - name: Build run: dotnet build Sentry-CI-Build-${{ runner.os }}.slnf -c Release --no-restore --nologo -v:minimal -flp:logfile=build.log -p:CopyLocalLockFileAssemblies=true diff --git a/.github/workflows/device-tests-android.yml b/.github/workflows/device-tests-android.yml index 52a3c94a40..bcc222c94f 100644 --- a/.github/workflows/device-tests-android.yml +++ b/.github/workflows/device-tests-android.yml @@ -39,7 +39,7 @@ jobs: with: name: device-test-android if-no-files-found: error - path: test/Sentry.Maui.Device.TestApp/bin/Release/net8.0-android/android-x64/io.sentry.dotnet.maui.device.testapp-Signed.apk + path: test/Sentry.Maui.Device.TestApp/bin/Release/net8.0-android34.0/android-x64/io.sentry.dotnet.maui.device.testapp-Signed.apk android: needs: [build] diff --git a/Directory.Build.props b/Directory.Build.props index 1f5581f379..2097f910c1 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -87,7 +87,7 @@ true - true + true true $(DefineConstants);MEMORY_DUMP_SUPPORTED diff --git a/integration-test/cli.Tests.ps1 b/integration-test/cli.Tests.ps1 index bd9d26ff78..3910024cee 100644 --- a/integration-test/cli.Tests.ps1 +++ b/integration-test/cli.Tests.ps1 @@ -119,10 +119,13 @@ Describe 'MAUI' -ForEach @( } $name = 'maui-app' + $androidTpv = '34.0' + $iosTpv = '17.0' + DotnetNew 'maui' $name $framework # Workaround for the missing "ios" workload on Linux, see https://github.com/dotnet/maui/pull/18580 - $tfs = $IsMacos ? "$framework-android;$framework-ios;$framework-maccatalyst" : "$framework-android" + $tfs = $IsMacos ? "$framework-android$androidTpv;$framework-ios$iosTpv;$framework-maccatalyst$iosTpv" : "$framework-android$androidTpv" (Get-Content $name/$name.csproj) -replace '[^<]+', "$tfs" | Set-Content $name/$name.csproj dotnet remove $name/$name.csproj package 'Microsoft.Extensions.Logging.Debug' | ForEach-Object { Write-Host $_ } @@ -144,7 +147,7 @@ Describe 'MAUI' -ForEach @( if (Test-Path env:CI) { - dotnet build $name/$name.csproj -t:InstallAndroidDependencies -f:$framework-android -p:AcceptAndroidSDKLicenses=True -p:AndroidSdkPath="/usr/local/lib/android/sdk/" | ForEach-Object { Write-Host $_ } + dotnet build $name/$name.csproj -t:InstallAndroidDependencies -f:$framework-android$androidTpv -p:AcceptAndroidSDKLicenses=True -p:AndroidSdkPath="/usr/local/lib/android/sdk/" | ForEach-Object { Write-Host $_ } if ($LASTEXITCODE -ne 0) { throw "Failed to install android dependencies." @@ -153,7 +156,7 @@ Describe 'MAUI' -ForEach @( } It "uploads symbols and sources for an Android build" { - $result = RunDotnetWithSentryCLI 'build' 'maui-app' $True $True "$framework-android" + $result = RunDotnetWithSentryCLI 'build' 'maui-app' $True $True "$framework-android$androidTpv" $result.UploadedDebugFiles() | Sort-Object -Unique | Should -Be @( 'libsentry-android.so', 'libsentry.so', @@ -166,7 +169,7 @@ Describe 'MAUI' -ForEach @( } It "uploads symbols and sources for an iOS build" -Skip:(!$IsMacOS) { - $result = RunDotnetWithSentryCLI 'build' 'maui-app' $True $True "$framework-ios" + $result = RunDotnetWithSentryCLI 'build' 'maui-app' $True $True "$framework-ios$iosTpv" $result.UploadedDebugFiles() | Sort-Object -Unique | Should -Be @( 'libmono-component-debugger.dylib', 'libmono-component-diagnostics_tracing.dylib', diff --git a/samples/Sentry.Samples.Android/Sentry.Samples.Android.csproj b/samples/Sentry.Samples.Android/Sentry.Samples.Android.csproj index ace5748bae..ede53769c4 100644 --- a/samples/Sentry.Samples.Android/Sentry.Samples.Android.csproj +++ b/samples/Sentry.Samples.Android/Sentry.Samples.Android.csproj @@ -1,6 +1,6 @@ - net8.0-android + net8.0-android34.0 21 Exe enable diff --git a/samples/Sentry.Samples.Ios/Sentry.Samples.Ios.csproj b/samples/Sentry.Samples.Ios/Sentry.Samples.Ios.csproj index 1506de5f5e..9ecfce8114 100644 --- a/samples/Sentry.Samples.Ios/Sentry.Samples.Ios.csproj +++ b/samples/Sentry.Samples.Ios/Sentry.Samples.Ios.csproj @@ -1,7 +1,7 @@ - net8.0-ios + net8.0-ios17.0 Exe enable true diff --git a/samples/Sentry.Samples.MacCatalyst/Sentry.Samples.MacCatalyst.csproj b/samples/Sentry.Samples.MacCatalyst/Sentry.Samples.MacCatalyst.csproj index b738bd244b..deacfb2b37 100644 --- a/samples/Sentry.Samples.MacCatalyst/Sentry.Samples.MacCatalyst.csproj +++ b/samples/Sentry.Samples.MacCatalyst/Sentry.Samples.MacCatalyst.csproj @@ -1,7 +1,7 @@ - net8.0-maccatalyst + net8.0-maccatalyst17.0 Exe enable true diff --git a/samples/Sentry.Samples.Maui/Sentry.Samples.Maui.csproj b/samples/Sentry.Samples.Maui/Sentry.Samples.Maui.csproj index a8df32a874..98e93c4b6d 100644 --- a/samples/Sentry.Samples.Maui/Sentry.Samples.Maui.csproj +++ b/samples/Sentry.Samples.Maui/Sentry.Samples.Maui.csproj @@ -6,9 +6,9 @@ On Mac, we'll also build for iOS and MacCatalyst. On Windows, we'll also build for Windows 10. --> - $(TargetFrameworks);net9.0-android - $(TargetFrameworks);net9.0-windows10.0.19041.0;net9.0-ios;net9.0-maccatalyst - $(TargetFrameworks);net9.0-ios;net9.0-maccatalyst + $(TargetFrameworks);net9.0-android35.0 + $(TargetFrameworks);net9.0-windows10.0.19041.0;net9.0-ios18.0;net9.0-maccatalyst18.0 + $(TargetFrameworks);net9.0-ios18.0;net9.0-maccatalyst18.0 Exe Sentry.Samples.Maui true diff --git a/scripts/device-test.ps1 b/scripts/device-test.ps1 index 0d6ced343c..9a975bcdb6 100644 --- a/scripts/device-test.ps1 +++ b/scripts/device-test.ps1 @@ -25,7 +25,7 @@ try $arch = (!$IsWindows -and $(uname -m) -eq 'arm64') ? 'arm64' : 'x64' if ($Platform -eq 'android') { - $tfm += 'android' + $tfm += 'android34.0' $group = 'android' $buildDir = $CI ? 'bin' : "test/Sentry.Maui.Device.TestApp/bin/Release/$tfm/android-$arch" $arguments = @( @@ -43,7 +43,7 @@ try } elseif ($Platform -eq 'ios') { - $tfm += 'ios' + $tfm += 'ios17.0' $group = 'apple' # Always use x64 on iOS, since arm64 doesn't support JIT, which is required for tests using NSubstitute $arch = 'x64' diff --git a/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj b/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj index a437fc4975..b001209a47 100644 --- a/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj +++ b/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj @@ -1,6 +1,6 @@ - net8.0-android + net8.0-android34.0 $(NoWarn);BG8605;BG8606 7.18.0 diff --git a/src/Sentry.Bindings.Cocoa/Sentry.Bindings.Cocoa.csproj b/src/Sentry.Bindings.Cocoa/Sentry.Bindings.Cocoa.csproj index 563e5a3c37..60364aae34 100644 --- a/src/Sentry.Bindings.Cocoa/Sentry.Bindings.Cocoa.csproj +++ b/src/Sentry.Bindings.Cocoa/Sentry.Bindings.Cocoa.csproj @@ -1,9 +1,9 @@ - net8.0-ios;net8.0-maccatalyst - net8.0-ios - net8.0-maccatalyst + net8.0-ios17.0;net8.0-maccatalyst17.0 + net8.0-ios17.0 + net8.0-maccatalyst17.0 true true .NET Bindings for the Sentry Cocoa SDK diff --git a/src/Sentry.Maui/Sentry.Maui.csproj b/src/Sentry.Maui/Sentry.Maui.csproj index be26d74d91..272cfc1d0d 100644 --- a/src/Sentry.Maui/Sentry.Maui.csproj +++ b/src/Sentry.Maui/Sentry.Maui.csproj @@ -7,9 +7,9 @@ Target other platforms so we can include platform-specific code, and bundle native SDKs. --> net9.0;net8.0 - $(TargetFrameworks);net8.0-android;net9.0-android - $(TargetFrameworks);net8.0-ios;net9.0-ios - $(TargetFrameworks);net8.0-maccatalyst;net9.0-maccatalyst + $(TargetFrameworks);net8.0-android34.0;net9.0-android35.0 + $(TargetFrameworks);net8.0-ios17.0;net9.0-ios18.0 + $(TargetFrameworks);net8.0-maccatalyst17.0;net9.0-maccatalyst18.0 $(TargetFrameworks);net8.0-windows10.0.19041.0 - 2.39.0 + 2.39.1 $(MSBuildThisFileDirectory)tools\sentry-cli\$(SentryCLIVersion)\ diff --git a/src/Sentry/Sentry.csproj b/src/Sentry/Sentry.csproj index b2a273e7d3..649fd4853e 100644 --- a/src/Sentry/Sentry.csproj +++ b/src/Sentry/Sentry.csproj @@ -120,13 +120,13 @@ <_OSArchitecture>$([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture) - - - - - - - + + + + + + + From f2ca96c49439a85d5f2471c83a14e965dca8d913 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Sun, 1 Dec 2024 08:47:06 +1300 Subject: [PATCH 053/363] Workaround thread safety issues accessing Android device data (#3802) --- CHANGELOG.md | 3 ++ Directory.Build.props | 2 +- src/Sentry.Maui/Internal/MauiDeviceData.cs | 32 +++++++++++++++-- .../Internal/ScreenshotAttachment.cs | 21 ++++++++--- .../Internal/SentryMauiEventProcessor.cs | 2 +- src/Sentry/Internal/Http/CachingTransport.cs | 2 +- .../Internal/{Lock.cs => SentryLock.cs} | 4 +-- .../SentryMauiScreenshotTests.cs | 35 +++++++++++++++++++ 8 files changed, 88 insertions(+), 13 deletions(-) rename src/Sentry/Internal/{Lock.cs => SentryLock.cs} (78%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 48a1046870..645de6ab43 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## Unreleased +### Fixes +- Fixed JNI Error when accessing Android device data from multiple threads ([#3802](https://github.com/getsentry/sentry-dotnet/pull/3802)) + ### Dependencies - Bump CLI from v2.39.0 to v2.39.1 ([#3799](https://github.com/getsentry/sentry-dotnet/pull/3799)) diff --git a/Directory.Build.props b/Directory.Build.props index 8f7593a6e2..0ac7c083f7 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -2,7 +2,7 @@ 5.0.0-alpha.1 - 12 + 13 true true $(MSBuildThisFileDirectory).assets\Sentry.snk diff --git a/src/Sentry.Maui/Internal/MauiDeviceData.cs b/src/Sentry.Maui/Internal/MauiDeviceData.cs index 3e92772ffa..cffb3ebc11 100644 --- a/src/Sentry.Maui/Internal/MauiDeviceData.cs +++ b/src/Sentry.Maui/Internal/MauiDeviceData.cs @@ -6,7 +6,14 @@ namespace Sentry.Maui.Internal; internal static class MauiDeviceData { - public static void ApplyMauiDeviceData(this Device device, IDiagnosticLogger? logger) +#if NET9_0_OR_GREATER && ANDROID + private static readonly Lock JniLock = new(); +#elif ANDROID + private static readonly object JniLock = new(); +#endif + + public static void ApplyMauiDeviceData(this Device device, IDiagnosticLogger? logger, + INetworkStatusListener? networkStatusListener) { try { @@ -22,7 +29,17 @@ public static void ApplyMauiDeviceData(this Device device, IDiagnosticLogger? lo device.Name ??= deviceInfo.Name; device.Manufacturer ??= deviceInfo.Manufacturer; device.Model ??= deviceInfo.Model; +#if ANDROID + // DeviceInfo.Idiom is not threadsafe on Android + // See: https://github.com/getsentry/sentry-dotnet/issues/3627 + lock (JniLock) + { + device.DeviceType ??= deviceInfo.Idiom.ToString(); + } +#else device.DeviceType ??= deviceInfo.Idiom.ToString(); +#endif + device.Simulator ??= deviceInfo.DeviceType switch { DeviceType.Virtual => true, @@ -54,10 +71,12 @@ public static void ApplyMauiDeviceData(this Device device, IDiagnosticLogger? lo logger?.LogDebug("No permission to read battery state from the device."); } - // https://docs.microsoft.com/dotnet/maui/platform-integration/communication/networking#using-connectivity try { - device.IsOnline ??= Connectivity.NetworkAccess == NetworkAccess.Internet; + // Note: Connectivity.NetworkAccess is not threadsafe on Android. As we already have a network listener + // monitoring the status of the network, we get the online satus from there instead (on all platforms) + // See: https://github.com/getsentry/sentry-dotnet/issues/3627 + device.IsOnline ??= networkStatusListener?.Online; } catch (PermissionException) { @@ -79,6 +98,13 @@ public static void ApplyMauiDeviceData(this Device device, IDiagnosticLogger? lo MainThread.BeginInvokeOnMainThread(() => CaptureDisplayInfo(resetEvent)); resetEvent.Wait(); } +#elif ANDROID + // DeviceDisplay.Current is not threadsafe on Android. + // See: https://github.com/getsentry/sentry-dotnet/issues/3627 + lock (JniLock) + { + CaptureDisplayInfo(); + } #else CaptureDisplayInfo(); #endif diff --git a/src/Sentry.Maui/Internal/ScreenshotAttachment.cs b/src/Sentry.Maui/Internal/ScreenshotAttachment.cs index c867862f85..60dab1a1fa 100644 --- a/src/Sentry.Maui/Internal/ScreenshotAttachment.cs +++ b/src/Sentry.Maui/Internal/ScreenshotAttachment.cs @@ -27,6 +27,12 @@ internal class ScreenshotAttachmentContent : IAttachmentContent { private readonly SentryMauiOptions _options; +#if NET9_0_OR_GREATER && ANDROID + private static readonly Lock JniLock = new(); +#elif ANDROID + private static readonly object JniLock = new(); +#endif + public ScreenshotAttachmentContent(SentryMauiOptions options) { _options = options; @@ -36,7 +42,7 @@ public Stream GetStream() { var stream = Stream.Null; // Not including this on Windows specific build because on WinUI this can deadlock. -#if __ANDROID__ || __IOS__ +#if (__ANDROID__ || __IOS__) Stream CaptureScreenBlocking() { // This actually runs synchronously (returning Task.FromResult) on the following platforms: @@ -52,22 +58,27 @@ Stream CaptureScreenBlocking() return stream; } +#endif +#if __IOS__ if (MainThread.IsMainThread) { stream = CaptureScreenBlocking(); } else { -#if __ANDROID__ //Android does not require UI thread to capture screen but iOS does. - stream = CaptureScreenBlocking(); -#else + // Screenshots have to be captured from the UI thread on iOS stream = MainThread.InvokeOnMainThreadAsync(async () => { var screen = await Screenshot.Default.CaptureAsync().ConfigureAwait(true); return await screen.OpenReadAsync(ScreenshotFormat.Jpeg).ConfigureAwait(true); }).ConfigureAwait(false).GetAwaiter().GetResult(); -#endif + } +#elif __ANDROID__ + // Capturing screenshots is not threadsafe on Android + lock (JniLock) + { + stream = CaptureScreenBlocking(); } #endif return stream; diff --git a/src/Sentry.Maui/Internal/SentryMauiEventProcessor.cs b/src/Sentry.Maui/Internal/SentryMauiEventProcessor.cs index 0c8bceccb5..6048da72fc 100644 --- a/src/Sentry.Maui/Internal/SentryMauiEventProcessor.cs +++ b/src/Sentry.Maui/Internal/SentryMauiEventProcessor.cs @@ -17,7 +17,7 @@ public SentryEvent Process(SentryEvent @event) { @event.Sdk.Name = Constants.SdkName; @event.Sdk.Version = Constants.SdkVersion; - @event.Contexts.Device.ApplyMauiDeviceData(_options.DiagnosticLogger); + @event.Contexts.Device.ApplyMauiDeviceData(_options.DiagnosticLogger, _options.NetworkStatusListener); @event.Contexts.OperatingSystem.ApplyMauiOsData(_options.DiagnosticLogger); @event.Contexts.App.InForeground = InForeground; diff --git a/src/Sentry/Internal/Http/CachingTransport.cs b/src/Sentry/Internal/Http/CachingTransport.cs index d783d0dbd0..f6d4c9b856 100644 --- a/src/Sentry/Internal/Http/CachingTransport.cs +++ b/src/Sentry/Internal/Http/CachingTransport.cs @@ -41,7 +41,7 @@ internal class CachingTransport : ITransport, IDisposable // and write from/to the cache directory. // Lock usage is minimized by moving files that are being processed to a special directory // where collisions are not expected. - private readonly Lock _cacheDirectoryLock = new(); + private readonly SentryLock _cacheDirectoryLock = new(); private readonly CancellationTokenSource _workerCts = new(); private Task _worker = null!; diff --git a/src/Sentry/Internal/Lock.cs b/src/Sentry/Internal/SentryLock.cs similarity index 78% rename from src/Sentry/Internal/Lock.cs rename to src/Sentry/Internal/SentryLock.cs index f831c64104..6889484f6b 100644 --- a/src/Sentry/Internal/Lock.cs +++ b/src/Sentry/Internal/SentryLock.cs @@ -1,10 +1,10 @@ namespace Sentry.Internal; -internal class Lock : IDisposable +internal class SentryLock : IDisposable { private readonly Signal _signal; - public Lock() => _signal = new Signal(true); + public SentryLock() => _signal = new Signal(true); public async Task AcquireAsync(CancellationToken cancellationToken = default) { diff --git a/test/Sentry.Maui.Tests/SentryMauiScreenshotTests.cs b/test/Sentry.Maui.Tests/SentryMauiScreenshotTests.cs index e2b60f5354..601b042a1a 100644 --- a/test/Sentry.Maui.Tests/SentryMauiScreenshotTests.cs +++ b/test/Sentry.Maui.Tests/SentryMauiScreenshotTests.cs @@ -172,5 +172,40 @@ public async Task CaptureException_BeforeCaptureScreenshot_DefaultAsync() envelopeItem!.TryGetFileName().Should().Be("screenshot.jpg"); } } + + [Fact] + public async Task CaptureException_AttachScreenshot_Threadsafe() + { + // Arrange + var builder = _fixture.Builder.UseSentry(options => + { + options.AttachScreenshot = true; + }); + await using var app = builder.Build(); + var client = app.Services.GetRequiredService(); + + // Act + var tasks = new List>(); + for (var i = 0; i < 20; i++) + { + var j = i; + tasks.Add(Task.Run(() => + { + var exSample = new NotImplementedException("Sample Exception " + j); + var sentryId = client.CaptureException(exSample); + client.FlushAsync(); + return sentryId; + })); + } + + // Assert + while (tasks.Any()) + { + var finishedTask = await Task.WhenAny(tasks); + + finishedTask.Exception.Should().BeNull(); + tasks.Remove(finishedTask); + } + } #endif } From 84778947605adde3bee44be855cc0e66acf0ad35 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Sun, 1 Dec 2024 08:48:28 +1300 Subject: [PATCH 054/363] Remove icon and splash screen from device test app (#3793) --- .../Platforms/Android/AndroidManifest.xml | 3 +-- .../Sentry.Maui.Device.TestApp.csproj | 5 ----- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/test/Sentry.Maui.Device.TestApp/Platforms/Android/AndroidManifest.xml b/test/Sentry.Maui.Device.TestApp/Platforms/Android/AndroidManifest.xml index 00ad0780ed..67eea7a864 100644 --- a/test/Sentry.Maui.Device.TestApp/Platforms/Android/AndroidManifest.xml +++ b/test/Sentry.Maui.Device.TestApp/Platforms/Android/AndroidManifest.xml @@ -1,7 +1,6 @@  - + diff --git a/test/Sentry.Maui.Device.TestApp/Sentry.Maui.Device.TestApp.csproj b/test/Sentry.Maui.Device.TestApp/Sentry.Maui.Device.TestApp.csproj index 6ffabe9c3b..52071b0e55 100644 --- a/test/Sentry.Maui.Device.TestApp/Sentry.Maui.Device.TestApp.csproj +++ b/test/Sentry.Maui.Device.TestApp/Sentry.Maui.Device.TestApp.csproj @@ -72,11 +72,6 @@ - - - - - From cc87ba5547d0739956c6d1b10c4c61e328568e4d Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Sun, 1 Dec 2024 08:49:56 +1300 Subject: [PATCH 055/363] Updated Sentry.Samples.Maui to match latest MAUI template (#3797) --- Directory.Build.props | 4 +- .../Resources/Images/dotnet_bot.png | Bin 0 -> 93437 bytes .../Resources/Images/dotnet_bot.svg | 93 ------------------ .../Resources/Styles/Colors.xaml | 12 +++ .../Resources/Styles/Styles.xaml | 2 +- .../Sentry.Samples.Maui.csproj | 18 ++-- 6 files changed, 27 insertions(+), 102 deletions(-) create mode 100644 samples/Sentry.Samples.Maui/Resources/Images/dotnet_bot.png delete mode 100644 samples/Sentry.Samples.Maui/Resources/Images/dotnet_bot.svg diff --git a/Directory.Build.props b/Directory.Build.props index 0ac7c083f7..7fc23dfc16 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -15,10 +15,10 @@ $(NoWarn);CS8002 - $(NoWarn);NU1903 + $(NoWarn);NU1902;NU1903 - NU1903 + NU1902;NU1903 diff --git a/samples/Sentry.Samples.Maui/Resources/Images/dotnet_bot.png b/samples/Sentry.Samples.Maui/Resources/Images/dotnet_bot.png new file mode 100644 index 0000000000000000000000000000000000000000..1d1b981ee1ab520412db1299de0df05882d23d3e GIT binary patch literal 93437 zcmbSy=UWrq7cHO&gkBN^0qG?PN>?dLFQJ!IdgxV(bQO_=t`rGPk&ciA2vtOCAiOk{ zE>#2t6$F)D1TVk)2iy<$e3)nEJkQM8=ge7quUUIfvXzAqJ3tUXM@PqQVytgNM|Y7z zN5`hk9wu)dqsMJEOJY%Dxenj(h?Q8@?l|Q1Bjk9b z`9!e9WHNF!1H1m5?<4A3f3#y)A!yF?@{|YoGyK*YSG|sVtP=^hXQPE@3jNng`IlaW zofP>T2V1`_kJurzuU2uaRdcUYactzd9YsrjNLO1+Ra&iM+j?ey)ByNZ9`!5r_D5$} zezx{v#+|uF!0$w%iRc^0uQ-0#S-Dik{0Wvn4i+9F2rr}y&pfp{d35Kfj%~wGVWf#= zE5Z1 z$696F%Q41TkLD$QB*Qn@l712v7V*HHEGyWY=tZ?-Jtz;`Q|Fl_Uf!;V*bB0l3A3IG z3&fe}#5!q@=_|eTz=l0QEt)HjUI#Wlb^m%l`f-rO>$>QjDZp(UwV- z(fcu>Sx>R+Phv^86(ig&h_b}Rj#TDt8D`t5e{-2%Vg#UVSy`zO!owmX6QmC?IY(Or7FA8%L>{$t$!54aO& z`X_^KJI-{i;o`x4gEbue@9^8J@09l5X&oJH?RasLF`P+?~bwk8N0gE4?g^+ zyp!dz?xFN`QDXap_2Hn(@#O8j2>R_0MyFabL#YmH1@ve4^tV{(cEZ$-ny&6FYV3YD z+^M2JnK#?>kUyX~oG6LrImmvomfRVG{~L2ZOQ1W2((PZMOX9d#Piq~=UihQQ`|Bb@ z8Cq%9TKIEE-TP}?gCcBs#!}-${SzjPhnIPznkt4#xr1y>K9YQK`fQ(ylDmtTmNy3X z!WgGg13H%q4&)i8sjp7Ho@1c=o{fbq9g{dGyBzR}1VR&qx+0`3XJf6XsfpG_1I?B6 zwOvf5ZXmBgO?1u0T-AVTM`uPF23A_GX9!!vn6r4XK;!txbl2bLjBDRbWAFa`>wOk{ z8uawYH!Ye_?caYE==PM*NGU9ciz@D?MV`i;o$aJOc+gC9BQGzUo#xI~FS*B_5kA$V zwVa;4|8L>6w&R$#wm)@xL_L{1+dnAo{diXU-}IMJ)G6l#9lZ~;iN20qh*RGe77_LnQksdtlHs>e~+lnpc zlGiOaOa5XaT~vP(cAUQDt9hpP_xhX6Ciov%e{S7}!1)126_=_?lv##)v3H?e zdoam>`KV)usM+h==(NTAm68$DJj3JR%dIP-Kc#&pD_>8n&B*KE>rmTjVipT+`hJ@< zZSl(`CPwmgIrD%u@$tY)fUWL8%rE-T|1bk3D^`1qpbKbK_G@utEGP0?EW zO1FGb2}8PH34yk~5r11Lv+d1Q0IT0NOXac=Xn{uw59qDt-LiqKa?cBIiZ@^E_x5F7 zo!8TKdiiXJXf$wAT7m30&a{`{Y&XfS0zYBpympqEn2{^B_1L1p7Z$FOB=^gsJPi(R z`|rJ*-@_Ano77jjU;b6HL}~foqzE2av%ND3;|u5^jCXzvS#(uM*idQipMW-tL$f~$ zGe?OfJeZ<;eC}~t4zc+j2&!yt&7&FEwYw7XE&k!}nzSy5f5R+zaBu&;eDgL#scMGJ z^FkmH(?0E_;qw|&6LrNtX!RnH6J>1{^L4W%`cJc%nB}_%h(fOV$?xKhg)onGd zI8f;jM1rzlReM^)++MO5@x`bQ{tCp>_XVo^GW(zxEdC?wkUfUHPGhYGmf!~-Yrf`y zMTO!(<3q_6tHYxVJQFpV@5H|zC6yN#!ZqpN>!70a1a!h{i4o`ycrhbBrbbhm^eJsOt9Jcn6Lexgrc%jOyP^r zibuK(8jY_b+>%j@>W0fU{XDG- zrXtN3ue}1r5Oi{0kp>VJIG2dx&2^*r`+vYfc;PLn#c`u;xq9b9+H1J!XaVaEScgFr*e z^AbK8W4|mR#y6D12lu#wNnq59#Y}Ird)6aSF()Z{~ZydTqc^!q9+sBuT)<7B= zcO3|`bi`M_tijyBSVuDzEoWqU7ji_X0km)DslFnOG}{P!_Ovly z{nvF*_TJl}`k@abB`>&A#g?xS#$N?x;2vNdkBMS@?Vmu#VZ1OX(-2TTTz-aG9xmlx zQt{vKUxvyi_#~Q0696A*@#cAPu3GGM3ve@8a3(`a&pziUxk|kP=imge*R z2Tiu5Pt&A=CNMk!dNfe$LBzkXAqlo5T=pE;PqZy4k=0Vw??~-Ee@H-^IOnqNE*>7~_gP?PWeNsS`uQsX0{pi}cU5r-~?CtrItf|*eXF5ffrWI<+0 z2e6ZT+&iu)LvDLq26R||OeCz^y(DgvvuI!f$Yx@8w|s6QFAXU_0aQ11J80W^;2hvDl+yupBjDs+Y^s(YU)f`vVt1LEX9 zs9i{o0R&rkUqxMNe8h_ngB@t|QvLU`&h3`~z#|0CL)v}=wgjtA^qLw$_Jlz{-(LFU zI$9Nn%GhM~v$HQl6ttzmbA+3o4xupXsaS)cWJj9&bgAVH!IDm~V5(q~3Lhg;iRjALkw7)-as!k{FtPu*( z-GATr90=>Hdnl*d?_K37Q+TlQNCul@@fp}CYm`GH+v$RALF;vrWb{+lKiZo^2I`T_>Nc!Lfb0xQ!7o1hwKRpcw``-Bnvs6DEo%a%{i6(5HSVma`}L$*mZphp>N} zEI-P!Fur9qiJkZ!GR;$w2j^&_UJjYYzG%1zi3wqT4t1>%^D{<=vKV)R&q401{#%HDX$Pt`2H%tQKzyiriPr1G+%4XVe!x@DQFtJW5i=dh^1bZAo3yIP z`h=onTVO0dQGdm>O|(Tac-Rf(X_V77TOxRk>Q;WxfP-@T1zwlx_=l_5>S|WM!sm4$ z_Obc>Gz;P(rA^T(0rTROEzH?!N6NL z8#Qf?Fqljm7>dZ_A;y}MV+d7XUA1|y+XFzY?|!|Y_cj+Vy%bD^y)OSzHthj*<^vcIM!f=+RyIl&02Y9F4XSSwO2s%8vcr zuD}o9(Al}lq+;hf=8R4pZ6w1yC=JJt9`}F+S{WKd%oFbl*Q@Mz1PE@pyUmOduZ8_Q zn=N9q2@pXaS2_-a$YICayx~v}=(9g5d!Lj+Q6BszN7v^Bes8e&g3Vxupk59w@3^B0a7^!MJ4Bx1X2DjGI`hdwy+Qzp+8op{II{dvPY2>^^ z*M1q^OsIbOWA>@AP-8dZ^fcmwd)%YGSL>hte6jL|O zyLW%-^$d-SBXJ}gO*$2w`35LIq1dx?{aaD&EvDMFU+==czYn&Rx6;o^U#xIcWK|Y4 zyz4CMIp};)rJC^?f35k3DQdf48v6F&fYQ%`d3bFuQ0m{EX!Ba%&hFMs64qGRkDWaI z-74V`v~-=zAjYI{)GJXTQwrg#;KGRTf%-jFB5T(i-A`uxF#k;<8Q)e7f}u228-yBA zvmhSZw~G>JSus{1{Ccplwyz}gV2{PGp@#+Xdj3Ot^TCt7J@*r7%D=2Rvd!L14O zdFa6l_;e&icPw$;VP=tmQuTse9eHb;V_NWs2tsOR8s-uP=RL zjW%sd?3NoIRVVVmXiWCAuZsJT0s2?*i6p6zGXBwpVXg_M`e(As(FhFJh_1*j@OO4T zbhg$&mXuP~FIA<~gp<_+DKNJWBc<}SlZBKdNCK35V|Rol^&Q0~ml3jei=2?nXd@QD zf~ka87oaNzOz^Yj&Q}WHn|G#12SNr)UsdT06m(B)75)N4+q0aDC{67K)Pe;sDlzWa zl_?_`)nLK6H2xn2|D}(#%=gD83UbOfxlHv<1utcQ*bz_0hg^ql-!tMZF;==fbjg~Y zl_mjINWH!Ko?`dsey?>hM1U4l5p5Ymoif%jJ(()6A60$Bn2KBwEG^&8Co^eb=@H)F zqt^=`_&dvDt!?#4`g*q*xz(P7alOC4sY$rP|CMjI03B*Rz`tbro7WNtb65ALoS#>} zjSbS`lpm<_to1E{&EJNO>OsB?LgI&6$(JcOs4==0= zS<0qfKJvr$BJ}T)OQNJY$GZTUMQ^E**q_}5-J!C1xMsBI|^4gOYU$xzkLc~LEXis-Wd zMoOWgJnX{6$qfmi!H*zmjarh>aoDK(>Y&C8J?IW#lUbPC@2Rw14_T^1pBU0vYOUB@ zejI_X^=FNPfP@@Yg;K>xo`_`V8vyz;m5}{!YTz1jEzAoQrVid>*Zz|(NhW*C@#l|+ z;)!-8#;PKn?U;XcZKw2GCsS|JMMRu6JKEc#_kaEutW{Q9TYK&L^&%&sJ1V5W-3xKC z8ptT7?=xV>haOPF;6tDbbdqx2` z#97C|_w+%|n~7Nl5Ism=N}rq<1WtnvPOivK}S{$rz($ zgljBb)Pp@;>Fh%IYwk@x6~p{dgl1c1OPAf3co_DGhCcEwk$|>-ATQpBC;7aFq22k< zJ#Gm(`Q)PRv*R~nas6Hn87QEkPx&5qnUmg6C*J9s5B^L3YM>0RUYKJ#n3b#RA;j{} z-`Uug_d8A^6OE@vS>bXC*Q9FIQE>9OT_X_>769u`n&0c*HoGb!>MG_^qQ`-*Cm+Q!xM4f)@duMLJXk%yQfN{i91@b|Mno_`st8Xq@8xlGZcXxLWWUv8$Y<9h5WE%8Q_X7^b# z14b}BuOg)f3^e?dE5w4vf{@!*&ZHRI?{%zti$Qk#dkxT zfT((fstg!^fbc~)G=X%Xd!0@>QtD`?BQ~j`cV80v8TP{$-y3VeJ8@{ZX{LLwyyY` z35mqd7Th(s&?;8j!UBnAZ8{FfMo@rCIvA&K)QoMH+!NQ-w$mthOC0#dWX=9#-+T^5 zI%uJ@qMP+ANUnH|WU=q1s{+Vmgcaf?mH3(Hj^L+ICf!gErwf5v-#aEaCEAJI9_+e^ zug}P$k_+2X_CcNvQW0S|L6@GI59NailF+5VJEL2~F5a5K+^{^%o$p*LfgdQJQYtO4 z8Scj=+p49e`aMA%IPLNg2c1|kPaGuWi? z^b0p`#=6oM6r0>wFK$p|@q3rrQc9kW-QtQlO;&Q9a$o5Dc(kPw#fM=B>IZ{fCP`bi z7siz2+%X&Vi|;4Gw*Vf=ma2!s%7{)0{c4W@e}h%OhDm+sTmf4W;qSA>m+D?&sCOEjL4Ke6 zYJRHlPF#xnvMB?t3QlO=zYFE$ojUCKOZRx+k9T_Cgsd^Sd#I$*1Axok{X0W_wZZ@K z`i$aiE46uabso_Qm#$f&ui>%V)G9`=;wOs{-z8&Me6nyPt=r7--y%;>mMj zHlsdEsd?M?*@&-oK_Sz+1X;YdV|g__bm-j&)U3@_r)H;MqU_Dl?ZO9AebOs*vrUP3Juzt*?7$2_g`I}+z#OaJSXE>j)NG4^JX{KhfmAr z-;uiYC3f>gsao*A+YSjzw|+pF9G~Y7a`R0S7g6Jw*?b_aN{TOB5oXXD=ox#+{uUR%knj+PoLYiL~_{4tHN!h!B+G=-+7CrpoC0HoOY zhvq%d^nRl)r@C7|5%Eq1hB++U8mnEWC*uew^+5Mzlu%YOl%b+Umh=VV1Z(YT*IAtQ%fZmK{6UvirG%^kX9*JgzSW>*` z)3lZ@BW5tgVRgB)a-V*@KkJs#XS92YD%ki1&@!6n`NGaCL!S45!i%S4#i{Rs+47aS z`-X+Cz(6VT--LeWtC1vJ^Wx<*dGqVFhCv=q^@Fk%;78!{*N0sJB3sP)ut$7QkVo45Q9fFC>QKSnznuF90)$*u$ITIpeWF@j1XH$n!( zZVzoNygLa{1G^p}Cz#UarO8i5%Z8Wwqrz&Y11`h~jSdx;c9vN;-_zJ_cxifTb|op{ zT$yYFGazi4>PtNGoijz7|7+pIX2MUrydB$gpW(>)nY2fYT52^TU;(^!)Zk;U)%1-j zBi8B1a>I9j+2ic4MzSWk{-B1hMv-Ar>m6)p1U4tk)QK>!t#)w#+hxGot|L&|`{<4( z%06fjV_7>ua%OtjJ+~CbOW|| zB2f&?n!2610^cTJO8#qvkNG+dDYdBZG^M;S*bpLxmM}8aL^5uEmMP+`)veYBt2Im; zL7N$nr)jFo6!T|SZWYB3&Hsq6!M>EO;jFWYpWKoDbuagPkN+!i`whOEpxAKp@1cxO zg}=cJ4(>To)P!f$c+!wCLu|+?dPJ$%_)2zmVuCyyx&XvXk>|Wxs9vM&;7SO(sZc@+QUD>v-iuX|fquW8L|t^^t_8XQBp5(d9w#Ax+@^)9}uaYJtCp z_xCS?v(> zbv(rB1w31xNc7))CBP!o72h0rVb}N1jV`J3*GoKsl+K685>Zm9WZU|ycUskI7)aXd ztHZf>D84@`XW=fx_B3%n!bnIn2uzxyo@YLA9`E>7Zk0D`M&Gb>1%)k_kue z-+cRIqA+*(MZ;^W{S9-W8#9_p5YEYWmjTK}{=l*S7(*=m)bd%8k-0APO&t{Co2NMu zZCrT}&X437^8Ox*+K<-7G|tjo8#G=>hV|6C3u_kAGxjzkW&Vyhpc*_MaO zpda(PNv(SB+ABHUct#6HcY2bQsKjtz$knRV+t&7lFYlVvA6=?0t%xKR)wRD;obF0} zh!B9W-AI-TaLB;3rr13F7BH&vkTrrP@lZ1Y<%Oy9T@aao=%>|YY$M;5ca*>>u(ktE zLfLTO@Tjlyum$TNHmF2(x11P263R3LGQ73OV3tBXDU*^c{msymkdaNb>?6Slc`I19 z);_s_${d#pJH>_!h?&GjDX3;DB?zytu*fYgzl4o6tp94yac$&wDq+c=nbv$@Ve5Tr zAGoJWzGwJws>6CHbn-s6qGLYr5=q3>#cQ(b&{>^)tLxXZ%fVR0=xJv$+EpURcpmN0m;t!xDn0M!|wQTS+N> zN=?(0C)54f6Ooilv8uY&X{5h&`A#Lk8qsMQy{ziFKmbUba;tw@Aw{^IzDi;@oG#61 z{(OV=REqSH?UW=t-%E|vkuR1F5m$vw{81^H+?1#yLPzKwZHCS)YL* zkyD?CntZLAEwzP(e(Jp54_sWKinSOb`>uzuf~!5elhLOgCGPGWx%Npzr#8c=C90)!Yi*dQ3iD8u$Cv5DsHkMU?#pZNo zAYZ`oQ)jo08O?O0Cq5A%C>|$C#&-91oc!w~Pteqo5Ra|3D0f<+*y!Yc;3$YE1=dMi zqZ6S9>)$!c^&>&mLaMR!?_DocXr%DBhi_M3B^Mz32tHEI@J3kn zvC&Ysl>e-M^L%gM-I;$aVopCFTDZSq;;VY;8Fe=JD&t+duY(2zB>6`&R@EG6+b&qb zY|lKAQb7&$_P9+V0sjz7KsG7wB*^MeTh<;+U87|D&t?=+N8Vj5R`K5b5(`r*0UbuQ z7`7gnAaMEhO7IeI2_RrOhhNTDN)66QQCm6fQR#X7h^*52xC0OocH;&75Zt&f$WtHc z!CK!K#uz5pN)3>zoBF|V64UE{TwCFqgRUqp)D?S)bpbvQWw>IA;#$84XZAK1US?*z z0?kRusOTK+TaTZDJ%SBhZU93q7YT^t3=X{}rP{U}(+-yxf5{P`vLPMDTFri6N;lTG z&`xNtY``GlM&r<(paji}&o#)(l{C)NOJc<9Du>&IIh`+HZx4i{1S!0jRMAo!I7OT{ zNZl;0hTp6SzdN(ZrZrAYR~Z>M%FCqKL3Ih2fzQHf^PHORsk9KS?Oal}h&#{3~WDNH3$6bIa4wNu|=mcKsuv>{94vt z!~V&Bw>(k7o=M0rA3f-)gMat9;VlPO0$Mxg65Nchhb6hf2TpdY9>?f0R$CEMn0~4g z<@BGp?At>x6N6B<8uKdQ$u()HW0Y7|CN@fd)r7^SB4^yw2KWrR;>*JAyA+w;pBkk! zSr9U)B0E)QwDbKCd{sF{`5d1%yehc1FSWmWr{F%65mGn2bgJ3!W9|Jn@-=sV`TrvS zB0FzRfr`VLA0hf{cGXtIMwJ@>>gX8=;h;C-MB$T);NH9RBF=t2@^)=xSn{8#)|AfC zf$@d}z*mExa|vTS)6%B@>F^`o1D9c}>HX>ltw|L(9V!W&0%YCgIvm8Grh%7PuLpQ} zSO`}?bglpn{rqqqymSU#GT~QHhyP;B0+}vcTq6Vo-Qyml)&B$$oF*dmldsdP3?VZBj%{#j|ArIHnP+tOwb&$EaXrWtN=mmwg zz0pThh}ttqP1@&|Yw_A(T}b)$cW^8Co$$24MFRP2tH|2cKm83g>~ z%anw@&wm0JB&!#1B+aAbd%UF<+xA4uy981WIB8Hl(!5}soI8!%#C`di+;i=?q1BSF zM^iT)qrZ&`UrKF%+=UgWW%qTLGY4N(gX)^LinlV@8X{k$K^$eKc!hi&@JWyiz?HJH z)$Ostr!CTo4|8eQaM-_CVZOG4?5!koKG{f=-I2-udW`!V*arIY}|o z7b$s};3?H{u&7$MPC3zvj_9kxQ$;L*6M_g;z!`?D$5w|rq1Z{~LqdT&gqg7RW-tUP z8-k)E2<6bJ8a!;dfSjay;6Zyo6CNX!n^BAWTt^q2ETmC77DeT>8Bh`gG~2~^?M7UV zkB4Z`bs^Bk7{~C{GQ)Eq$&IDnoxVicVz%C-&^_{I9weRK+3zFy#X+R_&g&->*;*MY z`yjQbi+>2Vd~ zhQ4e~MGiV;A}Q9R96o!F-C&!p)=mq=BBfOdzQ;t+$-*9oqTY0zHSxKv`U zmQ&HK*OL;p@EM4^fz6YCy=k!mgNQggI@nr@hf69g^d?jZvgV*a^{99}<@gY0PU?5P z^)v`+vs_~)54|H|>OEj&(D){GN-G7fWFZX)FWmdCiNcmVAm{WR4^IY>#8cmZly0T84R2cVl*(?v4WU|K;dfzKoz|F!wjE%M>ovEE)W@tk4azRRl@S1}x> z*?)W);Z0Sss3|}g_wZQ{v?O?iULtK!eioEYlMA4jKXSgOHRqdK>exymLNrwZ>Uk4- zfz`5uc&@@EBLfhtg<>Ys7{}}PjNJ~;reAf1?fN`NzwRb4cs3`Z)zF~Ky{*W7i~$!? zJ&g!Igm{PP_c+p{lf*_GQ7gy{_Dd^fK9FDRsr> zFe?w}VMzQC)~9~u-$#K+=(y-F`m1lrk!h(-x@e`QDwBD>VY3Zl-`v#eewy6vR89Xy^2(A}1YsKd54~<}x9YR1?@fNM??bhEb zf4h21P3;g(EeHb zU7_6AM$uadzPhgz-7urWFC9hm?JK*)->4!NIsCaGwWji_S^m^y(Wuh9P47cvsj7cO zLO7Zx>6bfku%oPs> zu9BZMt=~G=(U%s1`Z3KJ0^}pMjqD%2-pnP(Ti%o*VKMgUxF~~U2cL*6&A%x+C)7`csW4e&3kpByY?IgwZ*pex;wf&a8Jo zmy3`YfjfA3+`B_6p4u!PcWuI2MutA45b=()j}crOF)SmG9~vZKyt#NAl%9t%4m?gH z`~SU$2%cvoi%udJ_(h&0WUI_NB#9%CEt`tVwbF^Ss#jZO1-2ZXk=3rrq({@C1K<29 zT*fA!0C?_d4|&@9!_!=A`1?hmA{8swwPUJ&sb2K#sxupzi*9n28F-e&0hyJ5PTh`q z__Jwl$hE@QRz?HNSr_6g6Kv!4Z6gQF+)AiF*Om>pC^Ot!GENU( zuuV%}IAJH3e`we-*Z-XYx7U$pr*C%oplk#S<4|C32!hKZPP;lQgL{{OE zsEEUd%XS^+n)NuG45KT7gM^8LR8s}F6?E-J6|TA`qpchS!{lz#DxMHpeW|0RZvnBu z{-OD03~X7V94we^?)vQ-sJAI3e~om=&*2JZmrH*8(B>w2W$3b<3u~qZxg7L@!Jf$N z3A8ovaT;?uiW)nI>fG+}b2U_Rqm3$Ml>3u`-sSev-qJTzagy5IV!nYk>;KlCJin1# zqg#sb?l{wPwsNLNKc04ucQ#gcy!N%_ls6~u-rAy67m#QV+X1oIdT%yYLif=}vxNTDEW9S%13kvTMPxt~D50sD6t2ZN;%IF_2 zmz_DmuZ;~UF&&q+vti=FC}1TR*@WXD{_CK3w?8MJ@1TkRfn^!$H!YVf@(6_6FOwHK zc$O0D+0Vi1LF_GXj`$}d0Y#Is33zT08={vq=dzgd7Ry~j$tdy>K;GE6*i^ZmusddS zb!y;von+Z*FzRj1fCy#neCg@v$KKdB5rG}zZ^%f}Xg~h+fw=Rgtf4Mtq#2V7ckyc` z0d9%EE`^l8W2j@kM(bYRLwu+vPM(T7=6p3RqY@AfzN6J2;Zpjyve)1rsiRoM`@msy zF*o%Z*+#JD3+)OvXTq+!+cEzIgY4H10hG#{Py;@4h*O1rYYMUn$)DAOHIEHT;4t;u zqMUjDa(D=RUaGKl`p{wB=6Je7whmSusgFy_Yc9#045(vq{7~BHMrNxVm0M*qfrb6KucMcH$)sf4=+G;~&cHkpL;u!0N%-)Hh#dYre=-r7?HLN~|A3%)BONjepGq>d@f7ufy@h$KBZbpTM|$~fdr;a6)_>kb+PNLtWn} zTot|BU6a+lbB@S%zj%+>sf*K`gDyH3r+ds{lm=Waw3sQ6#7Tw$8(WZY!e9JJ6+ed% zU2WBkCBFLG7wbg}KZw_5WG8vJpW28IKPdSH6#v}bmhj75O#o4`Ht{Q?r{G2dn|vEt z%tpL652~jcU-4RfqWZ&f-2zag=qK6d+DU1mf`zB+>JXPUUMrx2Q0pweYD!o5%D2_2qV^pTKog~ax^?%Mbq{%>>!o}v63Cr{gI?~-7u%=iEJd}6T`1l ztSf}wN!wpKy3-|`VQeqcjrC+XciK>pINk8JX_!9ZV8 zY#dVki`CP5zDRc_K_v@UaZ!+Iqzy6h(^Gw89Y`aXGgr|zqEd$tQ@irYCIji(IK%!% z#%`wb@V3i|q3A`D-7&avNU09`7V0Sf2j=@pS`w4@q<@MG5K?3#ZPf-j+dP2`#{Yf) zS}QFSC4MsLYVvQNd1GwB6;Fe$I8y5j68SR|oi%rbgh87GAyCky*cV>{eE>Po-~TH) z`g_Ud{8rc1_O`nh9fXa~*5y90SATT!Y~Nc>e9kGtP`?tV_uZB3d`PuXG`%)F;QQ%1 zS#$c^3Q$jNNa;`>&nhM#pK*-!J&YB?ZZm!>x;BM_`J{PV#KgZCaQ00m`X_6`{XzN z?0NT!us*{6(9dq5P5H^vyfnn(siAoB2ZflcsgT}5`Q?P^9?x>y%@Sa@Q z?7Od157ltjO@U-C%VdN&C14_-c7*S%#o;maj@$)nrLlf9Ssn`lwWv5xERA<*eLoQW@-UY5pf|L?amq5gk z;d-mt3joAzCa|3%FA>bxK>k!~=r_he@=68DFW)|sKbUoOwO-hqM;=Sxl`xHZ|emjknbLWlZ^1&xq; z4Cj{@ldOqSU{{FR6x`#(Hfux56@1C=@9TBa@T%y?3Gh7P@C%d&3LTCT9_49-dCPk` z`ePAaj$XJ_S%vYM-bj}QUv=3uZ4H6^yx?dK643wmw09*CP&p_;4oviLr$^mj07yIvJVO~A2&Bu`!+!-6Xt3G5(g7yUB8xI_)nV)g1B9x z*27UEjl2kpWmvvG<_GiwKmG|Z=f*RSbU5#DD^?KI9HCh4PrBa?&@CJwCh?$?vO7#Q zhp;!in}4yRPAvF{*V=zjmDec%q-wuQK2nj&Q+MUpl#OczED@;jETu?87?FBknlN-P zL6sZzOWMN2_W;*5=*o>sd}+@_nZ9wO$lbT}sT@!dI3QlRB}Kkm#!m zLa4nOP%}?^oBmfFLy!=nhx8yEHN&9B@5GF-p2qKW6V9JPO$fjHh$k1IRO7L}J?>anoSvd_VXC-z1Lx2wryV-Ho&dA%P zSzRp=mMprd;RJDBkTRoXrznM?+~W2U?sBNH=fHMx&L6v@w{EGE_x6TdwB!=QuJ-a& zTN!3$G%I7!=-DXUwj)n&$fIT@!H&utt5zEp#`@ys8g`+q8x4Y^>XF~wgjh+LtDU2b z5o0Omdbsv?z*4v9aI3D)oM~{n-47mgSdO=l~|`1pyx9>Ys$0>T`$o0QhSc z>jc7$nXM_;)oy@ON#tX^N~vb(P){Fc#f@#&H%*WwmAb6coOAlZ+D1*+_$FdH4BGgQ z4&;T}5CypyIB2~AJ4iig;=@kS+eKEL76bLVF_F3=rriwvh7z~P#f0F!Y~ktmn^&$} z{lmzaj2?;ybf^4b$)MeOeyqzg93ZBZW4v0gF_d|Ii^bN%sKWQTs?$1CGMV`|;5;|; z7;dC7;9|(#^wg-I3&O`l*|oB$Q*BB~JALU=L9Sk90<2c}m%@Gw+n>QWZ<_U}G#W!< z$fnsHc`i|FfrZBD@cfRKs4&H#us?XP$rj7O3}`)HXxK}VjqLuYX+o;Y7PbEn%>7AU z$?P_t#u*4~k;xy_P#E_Ni@HNY1ng=enUJVNUu z?%@^C9iQ?qPQQPj+eV&hKfvn9A-tN(8vu)G^)Gc};N`pvh)> zIEfwjQhrh-Cx(K?Yww|?)1o}z#He(HJYSol`@AdKdrjT&ein1pBRHQu`R>s|Ih4-NbL|BHrG%ow<1vR>tpFE~KJi9fCP)z@H~IrT zS_Qq%YHNQ;BN%CusaV%I)Q@gm1z1u3moPBDP)_Wn&Twt`X1%Dxvy}fr7Wfj_ARzGj zv~l#5kzg9JPyUq7+k-QCG!VHs`SOivoUG&+E^VCGs>$IUF$>YF)D5Y8t4DziP_xn? z5x85#XnHWH9_E8Z#|ooU*WkgINH-U+qD<&#gDMJPj|;l=W-RheYu-LFd)|_kB5&?& z5Iy-KZnb5!y@3XBk-`Qrpue+3;W`=+H?8qD-9sZQmst!RD;RMjMi0fYst3$QnGZR6`p*ZAy}Icrf%F&p(5udDz^E^!*GFug`#+R+%Y7 z%db!$ZWN!S1=DzXAr1v1!@TDe#*s@r`b|@|BF>w35~eW~YADUTVCgQH2Q%rT<(OV(i7;DHD?u8{UCV|(){}Uijr|*M<;H^@eAAvCb8?myO zI3?S687Y|S&Ss=m@V}zN0?T%#`g=Z$vYKWtjCBhGp^a8NOY&9w z5sKJhhtD*Q)Dm&e5?sPj{@6oT%3N=_29#xMNM7_5;eVvVQ)y*oE@x=NB43tbW;4l1 z`e5GGXU9~{d1^ISoSeTcGcc>nN1#`?-|Y9{94P4 zGVW;M{76UeORrL|_PvN^Kx9OmT?Xu6B@Fl+pZtWR@)jYUBlh7Op4Yh9>ic%@gs}{B zkp4X4Fjjrtet%DC@;`W!jlvU%GO5>%v5o?51Z3We9(E>N8h$v?gGE(5t4Odpb_=jr zz1cv&Q#bb^?1)(QYJ~di7Crg8m_76aVo#>a2{wpt{v#PI<)qcQeN&dv!Q@21MD08i zXEsfx@w{qvm5w?9c_e}1yZbd9{I*@_|r*)D4w}=#sCDNk-Hw;3Qb`p`hGyLuOa}ajM){v~$}w&r0r99X5If zT$`^c{|YkYL5n8(%5T}OgHjb@keKQ4R?_rCLb%8jOs|L0ncXhw;Ah5;aQeM;(Ma6i z3|@5+cL(ou7bqo$fO9BQ?mG^@Gu;K%a3IgzY(#OEJ_!@#74opFQ9xr2KL5_uAg7&}`*f637rV4qT0)+Ksyv&Un)P98*v0Jx85( zht~h~@6)rlIAloZ>iq_ z?-i~38j;|QXQTJj#aY=iONk1rgad#Sg46g6%U=4t zF&bsfVKw|>&6z&#qec$BPlTh!HyRP~co^{bITfA?efP|U%YcfE$KM8)*xN%@+q z5jId6q5x6n%Ko`}d?Hd4Ccebv6x6nt;(IHbUJ6I)~{D(f8x2mIM;_)LLA142c-tg1B>;MD2O z&&fo9T@a@i|ExX^zk=vpfh!?-5zzj5sT5c{w63c;!aS?;#TXWoo*oA!DN4D?zlR-z zIUJC;3g?cuR_4BKkqUwAE{y(@L0!2ZDc~+VAe(N`q^3hFO74!5Y@9R<^6~Zx!trF6A?x@&p^lA7 z`Z(K?05}U2$1pk2%9xTA)Yc`qk`JDfE-29jMtA29h1#1X>z+SbmKbH$vxN~}Hg_+O zTmMzxy`lcJTF_P5+8r#Z&7CpFW-b5@z>#3O`N)xM+v~Rpq1K(i5NFJvl?}wBH!&vcTCLp_(U&l#(fM6~RK&ayPBGz=QVdJ0l*cTAqW}WeK)% z&~hDpg@xMwQyhtAqOV@R`_XIu!jKW1e;0?WN^{8XQX`_NX~3s!79G#CY3S~hp~RqC z>ceN3MHBVstRK zlehO3@1Z+kD>bo5o=uBM;4??`*xf-G!y-#)S`kpqgD)<zAqvT8L@J|G~4!5Kmf~0nG!X;GVO*`m#f#JW2mApPvVF{!oJSR z*h=-|mAPA;4#yDf)I}coj}|ACor(TqUoy=-k<1E72d!&-=M3QFTLcp)!eGH5)cD)& zVvC9EAcvpEqdVsZUC%yvFK}M&=~RJojd(2(imvztlrp=WOd)1?o+qBkq>nv$BgC7j zyC9Yy>VD4Q7r~&PEh5Z#WTaC^EA)#&>z~I`o$r<(*@CdcPNdC~E2jY}%B2$1@#FdZ zgC%v|W(E3R+{Tp0cj$oTI%gNjWCBdAe^P~nOh*}%tp@Uwb{-82+jFh-FQzp3yh{zN z@^?@|yuLf&>+Huzoz?rQTRdDb?jWk>3s;dmr{b$Ke?}}P`=fDIar^HpLy+m(&OmM@=fYWn`!Z{FBn;0L;SNP?DGYEEMdl5~oSt)o!1ohWC!3<0leG&QqT(XC)!dN;9 zd}O@{@kvyio#khs$S zLhoolblY2?4Zb8TWKhyO53aqP3%0&MEx!`+*jvU!{eviQc9eOXn8}&Eqt!)d07?-P z{FWn29yJC|EkeZ{l^Xa>`;+;*3FypN{0e4H+zb|=glt))$HKd;x1g~m$#{Lo3MsS+ zwq7?eBZ71)1ko5-KlY)YYN$V(qfjK$?^N>a%&j<7D3JDBX0g`0)>xfLm}R2l!4{@4 zzBap?wmqg zI-SeY;SV~*tfNZ=h~KL-MI5j86nI%);C6X`oTj=760xV+vG|?|T)@n|9gc@!H?TJ2 z>M^&ujmu5vG6+AZw`iX}2`%oE{t#_bAj0ruyP~9mc(Fx^VYv!dC+~YOaB; zghua8YlXdPe~oSf<#3vjWLJ>8iSD#<(Xn2NIRH1!LW-0i0q3UyPhb9k>~3&Z+3E4% zwt`%pqXkn5$^B`q!()BXNwDvSpRW*QAzX9~E&3`AB)b9;wY-i z&kRXmp$ExaeXy)Fsa<5~Lpcc?lSjdTps>bO{~Lv?@MRjaMjKLbUh8e2UxMAug_Yp` z)96H&M*JKsA{GuuT2?4D=>&VFpi)L)0*_tp{CTtg(bPBr24#c`@x5O7oEs7L^ZhQc z4$8t9P%nxO^k#Q*(8VpS=`*5ex3OOxe>cjTK{-2eDTvQ>KdZaXhu;fEfidvC#EnTc ztmpQsb6I%LyR(n+eQ#2`PvcD?$DT+nB%bzWlY&^Zr5IR}Sl+u+!!}?Uq5>R}N{~>$ z1b^X;$9(KJ+eiz%G|)+&JbKXby!l}E=uqmHEeP76Pf+%6cN=!$#aygSq_F3*`*7}x zM8a|G$g8KhXZxv=cJ(TtK@13UCdEn50LP1@@T!wIsCK;=I`lhDpL)C2Qs4$eY(R*o z$}V9i<=cxXq}OTWI<4;L+7PXX*29&qO*gLmZguw6YLVXvxrb=`l>min_FFs`)4rzW zq_*dOjD`&*@O;~g`Q%i4tSfC`!uxhHyY~cRewp{>B2DQx?6`@OPr!eKy*v+67bAn} zcx#cYWAlzt(;sVW-Hy>!huqZ7>*T9^?7#+1X*A46-G3eAq&zyp*ed04BUNl44M8WD zg`j(1uAR?&T#=d3RHf|Y+mg;ycgB^Z!-6{dkmSC}fxSCXDX#Jh_#O0z@(u1n4 zb8;?K>0`mVs=5XiRe~T=n?i&%nmt<}7pXiWIOoloh& z!55Nk{SAz48wb67W+%&sov*jwGnoZs#!=B5#bCWfy}DMvVpc*9Tps9WGY69!+L=fhq@J^@w2N{MyEdhg?CCx1F|E#ecK8MA zy$oePWzQ75|4wUgQnBwupZsd7m}mTA#@;RnAwth_+CQatOG-Qf)!uO@qo2ORCqKGs z=HxEp>@SR%m&6Iip|jh%Gl!Qwc16%5Ja=c)c0qlBp(0xBg2sGn+Shii+|MwfFIhM3 zp6=K&nP?jx|EdLpmXZ)~qy};`t?~oSoP0a~Lijmgf%z>NS=nj$OB%T-DMdX|FSeqW z1xY3GLWoz8>U)4OQoAR+!@0!lD>H8Y@|nhc$``J(K}t9Anlrh4vtSul8H{E^{7#3y z%;K~&;X$Q5P6uj}h15;$^8omg_rorB0zd5NrXqY@d^f}5!c+T4+PEz{@iFKv>ndrd zaYctgsy^ZQ?tbqfukXKwyVvggQr%umxc+!OkY8v+vWF~q{W!Hi1J;SW!Ki3$zuM~j zQfPwvmVj*+Arx#GDpR*9r|B`gsHxTMiBw;sXHeZBicG!~INX4*!j9MJ=0!Ri6rN~B zgcCin`@HTG1THD+JDcOwx;<%P-8O!rFlo@;AdpWl^13d)I=MT03bKqYxq)QSef+Sb z_2*ZEXFnE3)EA|C<^%djMK)#!^drbGtqZEnhmIwtD0{!>qECrWdQ6jZyD=5~X1<)} z&l{WM#GBSUqF^7P#8kYjU@8X=9N*iKoR~}Z3>IFFlppakZ5B`5QvV=+~Y9fe$xRCs8WAkc@3#Cn>>xpWY-NGA;e-s=8} zddW)Qgvi4Z9G0_|rQk_=`1E=3qV@&gqAL&D-1O)_Xv7h`8~cIhAA~8DTFC6O$yI*`FOJ#3*I<~QHr^y z`iQ@DzIO-dkI$ShGt7+LaslM0OD0&&NX*f$^oqWOzpZF*Uf#y!|9ky@`65-heqlix zXana8GE@t1z_nlwiy$kwal4yVr@J$^n>&x-y*nynt!kx}&(IN(#a-9&Nn<*h_~+%2 z>bzZ`Q9tTX-jQl)?>!wlkRfNX6gOL(>|iESx;->wt0Y6!b+QoWv`+`LTeNHEmmI`y z;&MkMU26Tjy-y?@eW_k<_-#_QKe6On?ykX=PPTr5nDuQ8il_@}4C0B)JrGfJgTyyu z^(o?W)DBc1biP=-+C?4t5c=(#d%$-HUDx3jz3!n~iS-RRzK^0+Z7}70ghka4+#sV} zl%;522|Y7T*?z8Y@7d;Zs?F*}Dk-iSm@FqpFG&7ddBxGX>ilg+|VV6xgT@d4^rmRE1(m_bEm00B+N z?t8Es77}7UP>22f_#ox5ZQHkOxPs{`z=K+6^^@>ll?UgMJ=R32#rgY+?>2gbc252K zo$E%5=kcF#3z4egf2@!hGs{#}FizHul@7sunlho}q0BprD6rcolUw^X+b(JD>ixZp zDafM=tY0^xE>ZH$H({zaQ2^Dh-}#|jJX)3C9+t_;?x0%P zeF-JPHPDRJqA86)=a~;;;t`M*2_(0kkGu+C)~7PS>a1zVWnsnvu8nd?e3t_4mKUDd zUG}Q=bUXMhPaRFu%FP+i_WYbHek6%nIfbKZJ%iMgXYmLRaL{#nLhbX~Kx*ln8I8OS zh;}=me3UJXL!mNHG{iHC-R|gq4WA3$%|Qaiqv@A5Uf|O!WzGQ>!FP}P4z6Ae{iL^1 zLmZgpsE#G~tM+j?gxuG~C3vQD-V(ULg=xk9r*k7q;-UY`7dxFR$*#93Oq}B`%24$d z@=xLQ#!{HED0(Etha_Cr1|6|r*7e^&T3%bc^|2WmW1M%A`pyWJCq`jyM`KAR@SV`! z1ZpCBG`!~RYyaJKwXu=$emEpfH`@MA5@!zne9?U==Vi|8C+;S7PKpj{*ZiIh_8S9F zdT`ctQ3B+~J_G}@9?thq-rmEdfxEK1B?k|$UHff+*Y;WtOfjd9zd%J*T8aq?T1$XBASNTXqoaC#+A0=QC+RI0>fnRODb@nEg# zmt$g)o8ErN0NFk}GDCdBi4NWb3M+tj^;~X>`J@8WF-fa8C z=b(2rMY8W(Uv}>GeS5fa{!7#V=ZUGvAM0zV)woW(y05<4SifmjBy+pz@J=nvIH@yj zV{uS6t>~jD)z+UrNJ%y#tHz4vaJ;~9Jm7fqtEqX+`y*5f`3N%X&2E#CLZZ&9cOfkQdVgkz&Z2+*ddTswE6os(DE{|aP)zJ zX+`GW+^Hb5jZC6T(8pJx{dDn6cTZ0UOBFtGDPn)?O|fFKLH0+^j{_UcKN}wN0olQx zR@&b9h)OZwcrbhTm!j9)W7?VF_gp?H` z)8k+c1c%%8T(NaVfSoX!DH$2|spY>o7||H4u?fZ23FULvR?v5(T($@`f9Cey{_8$- ze+%59@>>bt#Q0vGc-sHUPlr1|NPD|r`gmF^?HY2Owc$D_o%K_2!U(b~%2XfE@ zk24-whRe*IG5!8}tzmzS1Nj?f+-8A~n=1I}YeBQi|4{$1m(l%t3NoNRS0%#oH36;u z2#rUjG;gwauD6MUfX}Hk(!=K~_dC4RNrW_aF!S}Xrf9Aqp>R#ISsbMypr|>D60Dq7 zS%7#!xB&i39!WMj30E51mPSaOIYzm^_r}%iZK8|N-ir(^!W>>9h!AHne1#W-DIdFl z%!6a#iZJi)(m`h;jadgMO3=55-((WnK<2!tGzj6S?#UW6=kt9v?)fdlDh?B2?{rWs0x)8&&_L4 zPp+Tp?i07L1$~2lP36N&kQc_Y57St7f+LFhG;qh`e-npD5*boi+>*RRjr-i>7-tHxo$l+kyR9Fc_haLoGy>k;%ay}wak2d z(-SD^a85;QwPIW*8t1gbVgo7;*mpb`W(H22MI2@UWCdf*XC4KFtp2&a%OM0U${Bv7 z2&xq9t8Yntc!ckA(*35M0i7BJyt+a< z8M|hBEd?eJ#C(2E!G|hr*Uiq0dI2_C@IB(X8~gSI_ zENr7p0Wyq-=78A zB}j3!kBAHD9<{JSLSyP_Jyd2ah3f`{Q{VZ{{KN^c@-22$9kBJYQen(hpUN$J;qIy! zw~%b}VOQ$6y_wrXdZz_t|NP$Va*O=>p|=j-#Tf9NXY!x@2G;pf2h0n?qB<@^O*~AH z?gRDZZKq2rpFdSckb9g)__gZ^wPamGW5||HOUxlPB5dPO>UXVecRcuJ9{~P&w;@3Z ze2yxhj}So`J7`G2wU#|q&rr0xUOZf(NZnLiUdggEsWX&7q#(5z*R{2A0`jAhf_?5< zhi#SgEG-3J<1Q=*ifRBe{tX-YOt-_BFR<`DIHy+cHTM^^_B7mEY-r%MA9Tw40 zrVzBUOF-e&c0&emp#uko6Z1J;-gkYRhWKrHM~PWBi_+GgEd_OhEn?IBcp42;_2X^( ztsBW7B#@5+TcMys1Cq`sZzuZRIKAb`DefBU(cxp--7w_kC1cBozJ;gcD%0MBv+znaY~&1#V9b%GH^C=^J)9F;S$7$Z5SVsn zj3jBs5v$$m-%kxiNG1cmJOMO*IQi+@cv*Xk{B*r!=6sdFnG?^kvEZl(m6egBLtots z@Ctb=*!iCs~DCd^iNzm{?)* z81bG;@ZAJmzzk^4hDG0t*!& z#NoF%FTs(z{%fo02TiuSd~^OdA#x|w#Fw$kP)Mb&#KFn(xUEP+iiw5!%c8U^I}$Hl z@61oCu*PDcxtO!(4d>`H0x=xTQ76{G5XCvLVOmpk40$G0(XYR^fSoZV_JMr*yD+RN zd0u`jAnXb^|MmG0f+=E-A}+ey{%301B`lERrgT-ZQ}TM_x6M_pC9&|g$GG@-%~({t zFarRqb&r{DSN0d4WQ030ML0Ax`-p|+ipO?KOBtmhW2$-$oGU`qWp8|UdxDVJwhYSCK7KP1?~9%5yt5nFbf&r7 zbgLn=s8%Q*c=`NqhW+z`mAcb~Q3p$Pz*n%i;6RsX4I7-A@cHF9moFHHDd{md3s-4JZAP@a34TJs=;7cfVh!|MRPyekq_o1;Xn2jD5(k_(|uL?~jyh_+bISUXE4b;DQt7-w|gqECe`*%LalD;n9 zpcZu=Jof(j;Rzoy4kiNALdzU>`1xKogDd!b+7vEoD6HQeeObg_cz4prCi3r3n`luu zaud`Q-{C$Td;Y+g8@Co9x-xxg=I;mfKlPpW?s;NLM3E?8KWlpVs2CT$uKeX!dHF08 zx2DtINA+|8S?ro@e*~D;(N5j2lp>$(Jj)kQXJ`)0$AaFw#luAW{?5`5Wg9nI!&AE_^-_jH7u7wNAEg=xi}@MF zFC(ANS$Ue@>ETn10qkyqD&CUCBt7v;?CU+sIG4~3KMSbj1|a%jWff;@WCa3uq><+Z z;%p!Q=L9Y?yI!2}yH-Rvv)&R@yYTEpP4niyJ{ZBqdgK#kC9~kxmpNO@VjF)q!5h(E zp1$%ofXEP=!A}I^#|T?rajx$S<2u|7J}+RvzFQ|A7(~E|K?r$F2xqPMyx870t29;yMVvzPP`o#HT=*uyn$O`bZzbDUm}o@( zleUAd@Drdi}9GFEzX7**`9pDuka1uLz?-Fm2u0K}5PZvV$ zAY3Gos%ix5{{qRULH-5l$VCwa%&;sEPID{!k4QkeN^UUe4TW*Kr2$1xrra%f5 zz_U^AG?i3bH5Q~?RZ`Fb+^fIFUw-1lTyjiY@-vNm9>-VOY0+w$cL{ECI+=85Z_zl; zI^IMtdB=Mxwf=mgkV76?sP@d%7af+)*zO?#*aouPhAGsc2Y-K}nsb^w>1CsSYExRSbXwT_$E>~{qc+_CLfcT zqI;NS=Tv`P**8oqa>wXZLJuKMIl*(n1>CM2PO9KQ*&$ftd$+(y|Dr0xq>C9E0T;1s z#5@ueIWO5o*}lBKbbNqy4A|I@xVsY`sK0B}PTHn*ZX>Uz1eG~_s@|HrKV^($A-#_| zKfcoy(pal3D%X?c=w|)OOC^^hcFccMggC1OrGJmUyc*UG{z`}?OlKs=SWFt&_YcOu zi!axur6k%i z^goLiX;3#(7g=f1u&?@}{?k@Nm!sk~%7z&`+=2^cLUhAp5n1hUTZ_@`cZLcnP_9b6 z6~-Ew1(>-(9b-;-QI`j%z@j22XqjU0rNWz1aQHtO>Y(7}7PRTR?5nA6%GdP6JWmhx z4X*rupr6#}>k*=IXP;WgAcs8g)?)Oo(}e3ua2tji(w2^VB05{1;knVnLgmO^4X{BfV zbsi#(NGa;U7AD0Yrz`+a1-+rtGtdJ*HMt=ImRkC^nL*L>k^A)TaCzg898%LV=dMF~@9WS|30?GD7*#kWjCn_;ML4~!-~_EyIV1m# zqv2($BO6uXkdliqv0LItl$(=V&aV*yF*n{AeBqo=B}q|pLBz2~nUH}lXdGk%7l5OE zZ)UV8knjIeWT^!Tv{v4PdbKdX5X|U>Y9yHg(T}eiZR#W?o}T>A za$3S{0(M+`jr+e(3Wp=!aY!CXsuyt<$^{VFJ;A(X>OQ{*FP#$UK8EtC^QdNFnA8u- z^t7l1JBymuC+WS}rRvR^u}o8Vg*o+DQ)kpEi=@GS>f;%#Zf&GarXYDpGBZ?J4xznkkM#m+tS>Uw@ zlh6m_6G7?^=|!EF%pdD=W5bMC1pEi;UgAKgBafRNp>3OgWm2lTg*|-g7>Wi&FVgs_ zUNugUtyYi}6KhszXgn1vp2Fg{EG$N(FFgI*-+XD}U0fU-?9k7Ze$$%zP!0@HZgjXQ>eA3_Q|C9&`#JiIUiewI=@;M;&_hhL`p z?NfHtyIx}-$Aazmeinr+^b>w-3)SuR$3nVeBtRl~+Y)b@3On`S*XZEr4O94Hn~rzK zlHEE@qluH{{k=Cd-KI^wXNInaSsZ(CxQSdqpwEs=AQr^ajEe0h-SFM7kB)e&g>j&= z)??gm+$W|U?6+*>puD*|U5-v_)8r(nR!`3viwhu+DJvL-^L2rB#c-nQ|!wl3p;$I~9fbPQT>0Y!~Jv}>kB7P$D(*6`VW4OqamgT>puXLT7S z&j=o7OJ>)-e8daEuUK?7^Q_lQt{k6beJ)^%+*eM{0KBg5EP08sG`0Fb9(hUl&*dhz zv3##I;2xjbCX_4(&OcL8T4S(s)kx(|!Yugj!l!8#lAPFm#i~c-7?U}UY$Hn}-iT*f z?C7_<$UIdqfSK&|JeD{$j_Yo!=rm&q#*@e==QPjxLS5`vD>m3%eWlK?j&F8f%S5@J zuSsF0S&H0aF8(0QPef--9YbFj!$Yh3d$8A3ZCgYuM5~}4DV_yxb0*t!)w4Ax^~ZRE zyVXyEY=?ujkLR$+_?id44W$l_QPbN!+7UXNi00R znEN_qhifk(QZ8MHztXz*^-M2jpPxmqtYFLpVX-@EQt*q^mtuep%KH8;^mvKqV<`ax8!-%O?49MP-%G$iu$mWkNqil9D6-dH?kfo(?pTismgZk-|CX# z-c7hKgv2t6_@lc$jrFj}^YhCMG;y1&jZOYS#POdtEN(%Je$tRwiHARJ4S#!NjV+v~ ztk=lb2;Mu;p9M|UdfCLGI3W3?xy!CMj=88AK*ib99P7!)uy#+fJ_?Ts3kN;4;0yV$ zkZZ-QM6vFzLiMP~43`_>5NdLnQU0Fdaj(fGY6FBmSDBWbc-3!>TMXc=F{b7VFi$8qLen*qkeMGMKQ(&%B)d3`xwy`eZT9hJ@rK4-ko2G z+UTL)8)K98-^EKW1|-*z71*e0SLey+%iG9H1p>r~dKzt9+Woy0%G}=D?f+>gS{k~# zKMvv)hx&Puw(h181HR*fyJ#6qk~D&I_=U%GmR}OtiPGru)K#L{dpN1M#N;&he{OfR z4>(H=x+lhE3G!xgAp1_HSAe{$evnUlgtz-<&sZpiAHrz9v>^XrYR&TxzW3N(7`C6& zq8mc|z^NqQp;P@M70a%uh7eDe3HK(M#5Qu{NNnyqR(etODcp)-9Vsr2Tylo>KP`P| z^Y^1f7C0=4#Va7n6p*aikN7V2H}LaG&aDu2T%nNLT)3)B?Y?>tdrh^TTLLF9$|^am zL3%7lI|tku{*c0uDia1cb!op}&T9hqZUB^_Hdkk7Hb`duNtwC%S1NDK7kP=7IzB%d zJa-(Iye9yHg`+_w+pL*U{n$5k}>yF2!*IT)eckhORIvPCe1MHKi95o*W zL9Ee4hRDw{Iz5FGGu^@V)M6yQbB}|O`4J{kHvUZz>A8Qh!X!u8N#K#tte3-K7M8fM z1``%u1sBi51Pox4Fa=mkJmJ3bX=%t$evWi^J+%8{lKEKKJk7x==diql;e%p#rQ^eF zeY;bi|GI)Cr{d3ZGA0$YBeCQ4XGc~2)KfX#0`3nK=>-GZEEDYD6erc|+5QVY2?OfS zW9V{lWcS`v1(;-0I`9yqlc>P52^H*3ort3-ue)*5?%&2;N+!sWmJ>MnkP|Mm%$du? zqWV)%DYUNT{a>dB4h<9rn(gcxD^;Ad&V0@t1{ts@zb^-`d)5{RUcfB)xJ%5hwWjt^ z5APg=*I2rsUrSVncJjPeqDNCF2W62KOKJbxzSVwT(~s?~(Q!wn5Hc0mM(UtH`X&YH zTzR^`>cve5p(Hb4VwM%64Q=Yvx4COH@4dPKXSY;I7m30N4s71EdYjWY$~N$$W1);2 z-SVYkqcqG_fu2VFs@-iy)p*Qre@kHdRjan!8hgc6nZM-sZqCQu^ms3cb;S=DI`YuZ z@MCn_pq2f{D_>ZY&Gg3Ap9!9SB&2S5ag2sHr*96=I^;^q8Qy% z2O(!u{j2JH&QnD(A!-Uf)ujT^An+z)i|xwq5fc zHa~^2Qszb!AN|Uc-U}V9(dj72uml#5yQV5*+g%Kfz6Aq10Ia8$jXYZ&>cWd0y`-KA zsti7#*NQAZioUon&KKWp*g)R#aeqXy|7(@3ykC~7cB}p?bQYR4E3 zJ&jh(Bxt9rDZi4guUq;EfNc&DtlX1TQZx;INFiGJ%b59hM(mipw?^oi@X-*b? z1a3S)p)K=|ECpU2Efhte08jRSS1lt!M$#Ic1Q~*r z!-10RJ3;L#4wP2+B}TNp81hprGJ|?=UTBsX-YW8#JC$yiI! zn7C>t-b`iv28^EwhnV>Dq8AX`b({3NKmgZ(vYzD?L)OP{VpIKg*=n8dJv8lQC@$<3 z`k{JI&yeM6U!nno6-&Js!C83PO}?1Vgj$QN0^X3|JUrr0S$?(k~R^Hi23%Q}=R20=hfc=Z)^R*Ug81N~v zG?pxs2sY!q4VGo41*KmdL;aJhxB-`@3@8xc9M>cb%`WYt;eD&_cHP*y&5KIE9+ zMb~8fzxAFh9(_M-rXXLlq;0jW;>l+1Dt)LU9CtNvPX^99F(lA!U`7g66TJ%z+RIud zdnz3a`FMC=Ba{iU4tL^c?}|6$g+le8{4D1CTlwgEO2bENJI*}+ZE5G^ls_+vNOp)_ zuH&mbd*Y z@7lk`$F1freXcE9Q-qyi7I8eK11u2IeCE$Aj=qP_f;3>>o>+A+aNcwfTDQUJdLc99RdiB9WLnTycwxi&5zV7)Jbb!3}e+%JX6WS1W5*f%u za0qAnm+xE@Q*9Gi82sx9qq3j4FSQ9d7KpN6rT53&M|Gj*SylW&KTchg*t{=PhQH2f;`!4BsH z<-wnyV^3T+Wu*mJD2d@;V1)2K6=pta_C9RH|8b85n$@1+GQlvkqg+ zQ7|rJN*n@)tignBk<;TSKMmB|EgCOH%NWVXyVZ}rD)NAtd4xMCre^`vMBs5lr}pzp z0F^_$zPmkIKRtRw-u=2B{V!B_#p;}c^7hVN_4(RC>c;ws+8OgQ*3DX|T$h<-uf!Aa zh(xBO&!+-shcFaNUuV@vviKrVd1i4;EH+~K`)T=ZsI7uG%^-we-DgHPitGjZX)`6XuK`M*D2 zS5*F7XJdV?fTy>K_#bd>M2Jqbu{W|?&>>_V)05jGncL36%8kP0oTX*hYBXGQNu^m!6@Qei@f_sO{zbkGA%3phic84o;rG5f-w04i%Xl^R!K9Tv;K9@ zG$naoN_M}p<@wM^+=ix7(Il59a zu%`X@X%}^J-GA^Qze&qqbo#V2_0whJU(4Qv1k5O^=J4;Ap~GsU!=+DQUxtpZY{X9* zicyH@wZjeNTHi*_Th1}$;;_jH&YIoNQSXetQ|^jTF!)nfja@>^mAoUfm0f(2!W?Vm zo4K1YoD(h+t_Jw_s1vVvzPMhFHeI6RDpSlT%BmbJew53yO_V<3Xm~!yp;foy2A5@X zylnZKkx^$B8wVt?6UQXtQE#o;Q3_*(14)^=|Ad(T_4@&H^gMT(0}nKnTEO=o$jj?1 zxnC1-6Mqke`h;C*2HQ0n_Pc0#*F@c&lmgd5-N4M-aMhzkvYiNxY>^7@JM+s(BfGQz z-^%0(0>oya_%4LLz--u4Kf%cUbNv=Fjx`mwUTpqhDyhCS^7F$tSoK}D7pr6oXNAI4 zO-9A-GgrbrFThz@xj)SJUzy{zl;?nR%0k*rokVTmNo3;(H_T-@qMRlk%}8wX-CmZG zU{Nu+UGUfCjbW(SJQLR0-vzSj#TJS;8g|DYCeJbpRBDKPw*TF3#=Hy}d&fo;7y~6% zr=nU-PF_-gUN#()F9$t}&}A*q2#{oy<$elQ?$W>0PnJ9wb?=V*GyMnZO$wUV>pd5C_lx=~ylyX6 z2}kll@>coRtvBtT3r&98hIwh3DV!D&7L(rjn!H4>201xfImyd6BzOdoZUsL=q@w)1 zstNUJH>mNf)@->5YOAu5Pzp*oNCYY9hx_g*;&)aZk@!{;m4sN~fx(IEVl1g=7W}EE zPMr)1-ru|3cBxedc##|XBkRp0_j&bjwHZ~hV|%uFFp`o3f@>vKLtLpS!qg6NImJb| zO>(^K?aNcWb`H~WDPgnLW=T;UYeM1-VX`vgcvVYYv;^vcNF}U}m2vxU`B7wD3ab}V zV&8W>3_^#L7I{38Lid0}?FXZ%0UU45z4xyRu%A9lwty6V+|eJ~F-7K}y(Zl`Yra=o z_;vGp=cAJ9jJ;F0EBZwC(#m2V)+WL?8JA(Bhzs`Spw@5$7`;x;h zOJ1};daYidB(;9GCm6W$`2O#?vwl6#3mk)7!?oDMw)VcDy3$0(66inHdB;HE_&!%P z%$?&2K28#t>`E|gd3kw;yRoB0@=)$Ix1=VZI!gd_W6Q>CxCQy__r^pZk4~w;^GM^@ zio1oYv%5R`%J#6QEC&SAH)#H}j4n}2Wy+;ujK1Rq6P!VxJN;QoP9_0kWRQOIKN%3F zUYlCYP0iziD0$COqzorvd6ZyO6+qcGADO||M($wM$h`Xd>+?za zv0I)KdflfVc#gZ145C@d5}(jKk0^I|F$jnAft zKyES_=L+s?zf6SK;*I9)Gw2pLd3o`zw3qd;@1rvBF#gE|^!$^(fIE7W|3t=oDJ33w zcx_)}=nEg@vVa&>v~Hc_F&jw|v&Dl}ffN{Ot=veuqnpa<)N&|oirP8P-A#$OevE7V zJM$p_&`t`VL*sKpe--|0JFPVK^4j!PmCV@r@cTQRZ)!%>?Up=l<+iTe7XM7163Rwd z@d7je(-Z$T>S=S->j|MiK;2DOU~B_nbkc8KNF-z}n&oG|zCPiu>>@9LB?g1_=8E0$ z<6gp_)-^6s0;Rk3uML8w3(azlHN*|_2fa=!zki42<;GNfdugTk|NouEb5aE!QmO2Zu0z=6AXOmd z%0b;831LHy!ypw*{TJDc`?dmegC{;s0=&o@htTUJ!mEs`*9pCn)xpYcG25L#T#wjt zlsPj>BZ$^=5-5|;$-W-U)_eGlZ}&Cfkh^s2CRxae{I>5j-_~!rhq2gf2LL1cQ+Q}k}}0ZX>fmbZFid@ z*7^Ki|0B&5-O!ck=MPw2JKDESfHRxoB6ayqf&R_m|KwAszBv zVFF*7xDWp0s}{u+n2bGa0cZhv*YGI7LGh3*tcdu*ccTwTfff&LY{yrCj_gmmU%*p)xGwZGl z$^yc67hzVCrZ#;c>G{Lwd4(+q#XYe$m?X8P_3%;GK?uLUkGyT7g->bbUtn}p(DWp# z9fpCcyJeyL$gf@{pxDOt3efd>rYE^pL@rUzdXU;}zT?sn4G95)EBu65Fcdi*ezmYM zyWMUzK@k_Wx2Gs|ln6;7NhM0UvO69-7RcvLyYow@B$MSl<;8N=cX(8q`SY8@)iOPO zS=rhe;U0oJBTvxpK9ca{!`b1dV-Rlg8K=N=m9X!uO9-l6;?LKnNhKpo$9}e^IJ!5! zKw@vIFrC{R`A9`U$pl65HMvO62i-}V>4$8v>r0fcW`<+^QZCi8JL&?&#H_>VFT8*& zU0o+>5(i9#5{mmPSec;&mM%ifA#<{_yL`8s5AI*`C&<3P*;1_oNP5V4de4(?LcT$5 zXoA6sxSHS&R*NpHckBMI1Ocu;h_dJZ?dsXG#^Pc0)^CBcQRdo@<{M~FdhKi$y?rm2 zmp(+Qa?*v_;Yyj^zlSbS*$`eS_4)x9@0i3cSCd?um_af zfY|8OZ9m63<)b|>d%J|Jx@`IWsYE{Dv~rbbj0&`Pel<{%y3nI)(`w{>Yd-ExR=t-T zY!Ga7A|8L_b^-nkG$9m+I(Aft;&n(=5SNPA!ELE4v_|Gwqj4(W5JcwWR=+9HX4_0cLK^PWhOIAxzX3ZPd{&L}z71W((Jlu4v zZ%|bjW6x_i_qMuV3M-_(zWE4kFP~cZx^~RXf4C3BSs5ABEA56!TxE(~`~x0)Tm>C} zH3}&Gu34BASf{5+qu zf6kp7{Zr`6S;jKBH(Ru};-Qu$X9?B%tb@`=UaXj^SGT3=UOZ=&g%f8@aV;_TC)8#6 z&i?p{KMybL@zj_7@7%{I+Gw#F|JG6Np5j9E*M7xt3>CfQJN@6i=QlQemu?Sf6h4z@ zoU^rmOO}W8BLyfK)YY0vA22QZjop%=3@!yBB2Z|W(3|G%w)|-ErbB?d?+)mi=l>`= z??9;kKaOW}w%mmxJMK<)!-~u5P}WWJtc-?~8M4ke$;e$Oo8slKe#HW&~q6W#4Wa*38*FQAYh5w#dwzm+VZRb?@IIbfw9#lOtF4(a&3qqJ!2yZr zNfvujc3F&JxuH^1ArAm0KKB=T&cBwZNx&#Vc88C@&BL^qVT~^~mAZCBX$e6vG9_w% z$Qw8K-*Ll~Y^FQuc^;vSI<)xqH~w7jx6r?bN#8k|^af@UI}(giF9Y1*)0G{oDs_r` zt0)iu$gIYJ72XBtEK<5I^2+G`m0^Y=!sX8VZtG|(!xTNfeU8G12jqT}NDv=UV*9ES zQb8J>c^v&5)2e^&Y)cfb3AodKQ20PiqUp7B+Ia$9@d~ijMw=9NfrshIGCiUCQ4>$A z>Q4f4)vQDWU%nBGhVBOANWju||1cx7o(|(Vfcn1|iYP1BA%LhDUO=V=aytexD_52hCc&t(gFmpsoJ`9j%GL z>80q4)*H|^9^Y;YcJ5CI3?{m|7cjiae4ml&kW7EDeiQg4c|W0ZLLkui0YARdI5~;- zh!eARwe>IJ<-Oyg^v`2|>e`H$pelKW9}!m5eEZYs$Oou=xP%(^lU8=#zkbIk8Wn$o zzX!${bjTgKn;S_K5!;VR3~szp-Rjo-R0|8!X;>9T%Xvt9!6X{E4ZWO_ z1a>FC{LrXGW77p09)`h_v5P)GNLS#-PrTA_NbYd!i`1K><%BXAJJs+6#?+iuA?c~X3o8P}0 zeHXe+DS5uoe4L2Dd!uoQM#n_{8`7@ULp6N=wfw1i=aE+2I(7&&o8k354vVrYUn9e&xr7WUTUP7lQ+=|ml4Z)iBJPw99^~CQqdJw=IjS~r8x=jBwUJznYi@(Q;!Xp0-184 z+a7AJ2}WClqIFjp}oJ zM~_m7_9YG1%0-AgF>xZLfJq)E1HhYrsNG7&?$5^1CW&e(F@r}Y*X~Q4knxN;+=X)5 zw1ims0|H{EW`l1C$kE}j6oN6(4%ST5pE=>lfo;HQbnovD9_<@}U7o$tJkl`OZSZoV zN2iif&5sjz)9ypSy9f*pliYN2N(U-8*}Aq7?m*umnF z$~~ZC`^YRb*^ivv=o8H6E)ujjb9O4@9UoR41@J7M$dlhOkY@$g8S-|_ZkkLA`PbaL zSy^$U<_-R>J15xxAr3qQeR%5cmw#eBRm>~RsO^625`Wr~A*dH9`t*{oy~5T>P2c1G z_~v8EHOuph!wr_V%0)%V3=j{EF6D{h`juUW5e^bgZ-2?{^BI7=ES&O;(D>eMzl-Z?J_6hS%vmbYif_7K+US*u(=G{sUCKXQy~7EzVwF{w~o^5WVvA?R_Kz`gSi zrso7DFvll^LFiMh;fi(?&&lv9f}iurdoKc4NMP8FmQ9LF>+6s6Ol>K8U|i^!MR84m zECld;0~`KQ(1b{WkgM}xr6lodK@6faqm24zx>BI$<9hLKN7wR8bcEVL`;Ull> z8(fC(`~C8*?9k`!)U8!2F8m4}D)2et+W*JLF&`yC&^P8HH`?Xxu*w*dVi2jBDI3R!j{(Q9dUUcEsrrqckm4s0`7PB`!_5J&n0GQcABx|6} z=nMa3SgJG6EovIC_siVmpim=M$YalZ7_~3D24NdkhJZE%R%KxiPK4v&G5q+Ro>>H* zfw{*DH>nGqaFBfVo9o#6$;D3MZ!YqxYFU%my~WOMY>+>O=avdK;jUQ#WCOae{x%lw}= zJ_m=^;5Di#rArQ`y;e{~3cC9k0Z1R!-2zg4|Lj!3DTqN4o7Hy&6ydhQ3HI~&u9v|- z!*y*O-htU`5zha%&izZrScbQ;T*hxi`C4joIvH0Ov~t(99;f4l(T8$PAYFdPk&DG> z{R>Fj>qne7AtiYLw0mVBrl&n7`f#Vd0jtQK*q-Kfz4h(YfNqM2@%efSAG@6t_$w+4 zk01y5dg2B=dh#9GLAYh=zpj2a8MFDc6U{DwDAzl@1f-7mc0&E=%R~21>m9JcrkB?Z zfq+?t6ns}i&_;)zw*eB&B<9G0pZ`iyQ@9tSX9!O$zmmdp7@0CD2jxoHebMOscI?zJ z9r^Lu@`0aUWY^mViEb6|e_$*UA-)RZtYuiyjy&iD>Qukb(3QW3{v``f`Fgt}`rwi8@JHbOt@KtLtx!#Ht< zN*VMC2>St32u9v`I(h|^O{m{~#N0kX>mexoN9qd91YSzq&F?27_nqtBhM2P&)|x=a zBuagfAYL3-UpgvMq{g9zsINBTgP%c}yosoMr_GpscYe7wfs$3L)n5z09yRQ96wClf zYXf8O{}BvD2QuzdG$;G>_g~|5Bg#HP$>tc7?@5U*)f^r`bP$ElVuaa(@{{us=BYGW zT01#FzpS$9otXfxgMMI2b79`-wm2W18FGC4Ohs-U@tPKHFnZZIi~Q!*#gWAD@Fa8; zI`8vF>Vx}7Z}@v!EON0ho+R1`X z%^A0t8@f6%-ud;$0rZ+5xy-5L&wI=fn(oy%_cI?gArK3;9N1F=fXmgKy?xss81?Y_ z6spu!eDQmJ=4=K%)f(((`116{Fw7o?lad?kd#|$1PBUfl_K~2eEBs8cF14<3r8L+p zI6J{Pb+iV#Oq%7SAE`dKd}ImI^77a~)gsc@Fal-ROZs|^eVI3}@#oXasTeg&bRftj zpFFRgyFHMcx^H=Fk@2r>#_X|gvG27W3J!ey;V}o+1FHeY|4un-Q)m>kuN>E-fIhMo#6erqf*f>=dtFD29qUbx7mgzdNu!~! zd4oRY##`XYEvzZU27-DbL*wevdu7f{I2fVtOe$ zKfXodeRFka5_AwgChB$>Ibt%Arcbw6dAz`hjhp;bA%s36*l!SdBXD^-Ev*dZYQpVl z);PTH_4#g)-|e5*js31RBF2jXtBEF7|0#99+7j%@N^niSfgOX-%!rqap#sL;T zQG*gt**UC3wS~buNTVR<(mWKJh5eI(c#+gg&PNq`t(d*qtVm+{H5$@QO7zdA>}LA+ zh3V3ST_zte#GS!-!sbbI8Q3R?9Nfc?7QkfX5fgT2>TiA|Ke>7;OyeO&g}JxM*`qoE zmW$nhr-6Yyv^({?F`NKy%z=$Dr+kvL*C$n;l)+d6@5pLSxAuFT``3B)8Jm?6FQ>8LT`@CR>=DG<$>Z0~4;Eeg`A9Y6Na zC3(a4TZkcioD|=ni=5P{p_(H<(=Mch%DD=kVDmf@HHgFgw&71e_m}8A54=?=GxnL5 z1-ev%=)`2WI4KGL`mI;qA5>{_Z)VGH|# zY-e@M(9-Uj_qix+IObrp&Cvk*SV7QF+_{OjL=oHp%>lVsJS9hxdp9&hY<&6CN|Ioz z%2>cTfbK={;9^?2<{rSEEYe{V2i4xSx{ zvI?;tAqwDy4ySC#HxBQhU4soIWDS5>h;8g0ulW-$!O-eS=<=#RH-gsG7VNm1#4a82 znwq@8N}`I?GO~J6nOz9Sd<=mhrX8MXn&Tqg&s?0mXsav8@R}iHs2TKgR@)2uGv&K* z(zu*Ra?0j?-iH73%2D7|G z{ILH$zcF?DQ-Fe?&ZC(kP@sMR!0-HO$RSv=yi7AS>KIk~2(_z&l%L*PJ=(n-bWnbZ zJL+;#Q71-buUb8+ui#;i4wDm)eK*%$V3K~)S`{5nPpfNJ9Lfth)jw$pdx5q+uH|I%M4pM1-L()d%10(mA-8I1dgPJ zW0}5w>YUhTlzys%vd61{M3i|hwANXXEzw{S`QM|H-7p#W5y9?)uVR7_;B!s}2+_?- zdT(CZQnFRd$*6#_MqM&SDlJVjVPsxBq49o=(M!bh-!y8M!L5XDFPJ~RQM7h^XOQbxm3AkqW8eHk z-;fHU_t(WIua4pxu=a~21s=w%ILTKc)^S}h4Q_9pi@24CLM4J{_*2LqIwF+#+6^MM zh+pF2PboDNYBXhbQc4y*8Yo5n7`{t11#?>pD`j2!6_Srps#ce*Qv^iav*KY{J;+2~ zA7USn2b6{RD#%0ud(R2zIJ?w$d8s~{xU6+sy>x_n{g_gTNxptC_CHeX+->er)+FeL zx*aX-o^QHEp;{_Y{U9=6#XdVw7NTPa|9k@QS{Aw&D*|+y8)*Q~(9;H6@J$D+dj=aC zqi_|ccg2tVse>Fq&R)S8I<-TUBV@T*Kq|pJulN(MFjXA38$755xhNt?@ASKi%D%PI z52113JLoe0yE=j@MMvi)N6R|AHuUPYmw4}8ecI<;StN$b_fn9^9?tVy+0guzoF68A zrE$s;xFg)Vg1L4fB)40qu|lZY(!M(Bax}MBVW&XyKTqPsU=yj5-)EY@)bc3ksdnH9 zBK0pnM97{jT(UWvL`mzJ(qlaJWzg&HX6?fCUWgW!o+mmgy&Dvv8NF@a4N-78Exwlh zx>WlrcY27>ocsvH>l~IRUBxRyex=qcC>SRcNu%?k&(qD}*8MU%^+_D6H>Wr6=6+?fET!cBvgP1 z5HY!(ay*Gj>T+yW&m(3vgVKq%0BU|{lr?_UFDh@cEdefl`On#J^Wo|RCJ&8(RZ$jg zy*6~iFW&{XAnjDeUgZR`WO1HksYwFmQ;h(cGz{gDh+5k+Fj+hcXstPz%fG+Qeit2j zc>6IAlOFJM@ZIkT2q$h4tarFJJKO!@xT=?gsxr$U+?Uj$#SEY3jBDYPtiC?t=Rx`d6b?|L$R@~23!4_r#E8MOj zR}oijUeRq0)_tM8B5Q4BHnkUmyC-y7@X>g&=%I_ykPdP{Za!acJ;n~$raMN=OSr0S1!2P`S zdzx2ylX{KHJEZv84r=2A%{<+jpztyfzFWKI6a{UF} zr+ld01x1C0g{pN>zNI-x?}?xw)zLFG1a?xsud6h655ocfhDzqKSEx)>5TjAQ3ZwzP z=e$)SS%~6>b~KqTDUhY?0X>}y8Dx<&-M10S*U`!3?8&~4WKiAdvo(L$$4oRRY43g? z{ES^!xJ^40)a6PE{n4;KF01F_IWBP8{1(v}d&-^N}B?BDq}PgVKG-g&BpdptL+m>#U@u8)Ur2R?si^bZ+OpyHS2n1=2qr|t3<^U2+v z&Z(1Ny-d~D^qL0tG2vK^e+`y-sq@h@?3lDcR^!88dmphPAEAX2$LJ@#D9^C|e3z&S zxsLD68~#q#nfwbec52A!F(~dDlv8)1(f@vo-kKvKpDpm+tNC}K^!g+Bkm)ebH8sXW z4L|{{0NYmz`GeUY@L_TG1YiAqy5fB1jjYE6)OC<|j=sCRja<-z=pF2r%L&+rx?msj zG2xbdguPN7;1 zaV+)(tP|P4diy~Syl>;;zc<5g^1KyP-p>8BA+CE|7puWmtcnfs(mecj>#hNr8nP6f zEAxE7ik9~b>H}kLUdS3y!5hot#nF>~VoiA6nte<5%-zk0PM#2US=Y9hjWqtRFR8!G zl-{L%iX52_{;&PkUVlcNe&N@B!BlPgDq&_(0Y$0`B7@jp^%#iLZGV z-L#J4W`owUnM+oKo}V`u%XMW>pO5|{<=;hEX)<$Bt`@(2xXL4}&n5Y;^fC^4WPc_7 z0e>|L-z>f;?Rt1+UAQXxNT=Bk58$qP&-TOV=IFl0_nYT~7u&rvO`>+bjy=O1Glg}4 zmJ)v7AKh<`#S#_nRxxU1)E;q41o--rPKl? ztB!|}Jvl46HBM!_VSOYU72e)vt7 zz))m5^Q(h(XmaStjd|nVRR*@)_T))BV+8`wJ>rpyEODu37`%+ScbQP&MbO96&0*ZP zM=fDjKD<6_BH^|&y>)s?no2v}jA7)Qag%%kUAq}@hG)RTSN1?RoM@9kk?E{#N1bd~ z&C`Fykx5Lfcpd(lk7W&yq_@^@UXp1B`bH0b|1zgPyQ8-oF$G&QBu0jAH~RX29uoP`;|Dg&P2?w3}qciSmjXy~wz2I>kEtHUyCr`{{~Xhroq4;YswOl(U6cN#I+ zMWDjlY4!2>z-90s3)Ub(x?e@F2U?FceFk?UMAq)Vn?Ab`e!<@tc`FUoH?_e-&+7|} z*hTHy9#~oPOjyyql_6a7$R<#^g{**AB}L0`lbbS3&gRM7{aJ2-`Ca}BF>I2$Ld?=uVP8m4-EvjmN|#jS3&VJcr|N7_)*w3Fcc7dj{4prndxEUf;CB51}VfKrm(tIz=-Kb2g*qLNQ1fV7w zcgn}?lh{-V0n9g3i0(&Z1IcmH|IlULssqL|-b4}rI+zKH%rY(GUgYMrem6n^LHlS! zG>cF&*cRyTPtKO5a3Qjj0m;wU^jTN0Am0wh$iN$Z9JtV;fj5I9K_awyuWvqG648Lm z$Y)7{rBh>c_dP$2L@z#hud^F@^H!D7qw-?T`l8gUrNs#yIt-(i614lYho>&M##Wm{ zjM&2p3a}F5?5rpYsZ9B#jkcXXFa-l1AE&Y4sD^K6mReiYDT8V`Rmc>kWh!m%eWq*6 zLDV9Y4oOnMvtfcR{AqP4cbflIcAe1~yagoQ*K1c- zu-f_;|L&B6h3Ah@vE!dn%3a-v*1hp<8_js!4WUP78l-iC4t5Xg1qMGht(-k^3GC_% zq@q|z;SavoH3c-Q-rxdJRQqaXsVQc{e4nV7(!7z^Mh*L_Pq2l)~Yya z^~Vv)6(3v~cu$CK)kAZ)ikPzV%%UP-+?GH^kQfyk$*UJ7H<9pm{G1?wEl0?sDL~=H z0NUjCYmsmu{Irw=r##=nS@)*iXQy&j$SBlUhIw2`M>(ttbP<3gfV57d+g4{TGmS#e zx*P1(3~kV_C!+}9wNXOhjcNSK1S^{vyh4rP~K>|o;Q0fuUv z(5Q>o;r)Nkv8YOM*0*`6Ap3n#k(Yf|@ekZf&n$5-~xYXrUc*^M^YerOulPC-f8PZ}GmU2TZq7@|8O_b{W6HNkkx(@F$p zkyT9LrSe8N7VPl#oA0=AH?k$fMQSWWd1!3Uv$VQV(@`d)2sq(~C0<%`)MSqtzxd8k zmw;6QaAQhJSW8L((bF#x+~W8-Q8<}g_W#nnJ4~%@!Z?xUYz&w$O1oyxPvhqfu%~M5 z$43Bfr9h+h5ZCVrgHvl)8#weFmIHq0fnHFBU=%-1?g^{P{RaTqU1CO!{cM zLBBYKZ@mwt%M{_MB?+qz8F2fJT_q?S9@@k~C~4%9$v(Jw8LGfr#nsIvYjs8@eI;?T z{H%I6w1*#Z3t;lP6Yhr|@<;Zh(yMw2`Yv#~JY{l^?VuYpI_LRM^NayKIeb`1mK`v9 zx~Os03pHU2d|d&&EZkWh78;$?cG*A`D~)>!5sa+ljK`TmSY+%PUkB_`zhYffHv)fQ zYv7C3(Yi9FPwMo|1hoBAvLPZMU(oE@fZs^)UlE;Y4{6{|t2@$wxYclZUbnVZ7{9cv zstihC^~nf3Gd1;U`l)Ms-X@huF$mQ8=4 zv`-CPZuVSk3kfc|fRcCnr>xd0%<$akHozs_QTqW3 z@Z)(#_ay7wr5g=+eKYy_#wbp&zTs<~vrqjsrv09{Q!n_D_no)>U-=&nBQlEYnkU%lia z>SHVr$+ozv^byKRp}u|e0M1i|M9VHYDj&Wa=Yph$CiF*{jz*b^UcBn3u)+-XgJq$= z@n2Gpq-A6zJ{(=jNI+AomGm0VXa`w#g>#SZAvyGg=F2T7E)MaonR4H1ELL!jw7BxM z!!Wp#s?|758xzkJFT5$0*18oHPDHS)=TGhYO!{Uwuv7YJj`z5Brhg02n;8dG{#-%dt@#CjcR-_E*>yJ{sGas=$FLYjYHq zx$w~7lFHdLd>R}Wp#%~Nug8j`$)mA!cY&0ibvGX8T&1=;dNxo!%dcr&hs3aNH6-s2 zk6T?~6#YGa0wTm-DhJ!`*;no>l)m#~4YGrKH<14+cWVQp%G_QuY6xBi?G&-G*`TVK zL!6~Flx5L%DoH~LoH#GGoP;1DaM~*kqn3;s`gq=ik6qtV zI2;!2>!V-XyP7%b?n$Z^Zjxv)8Yt@sX+G20q*J$NRt~M81XnhufE!+lv=7dVTs!wc zEYQGswTQ7&i2dUH_9cw>BJD=j^)Z41@HxAC2mG2m;51sjfcMRJgwNtG!D4##^C#`7 z%R6e;L?0Y?ZIXt&MN+*^Ri}rALNyRLRF}26*MU6Pu80FrtO(%Afs~DBocK=2yF+U1I)_HeUzc_v+__1N*BBIe(i{>&_B zf%kf*x%>bWuIvxUub;=}g4~vw9A*{tVk7bw&c%97wsG(}IxK@51aNo+MEUY93wIkI;tdI=l=UJhy#a8x zII$E?=+`jr)4JiAbH_K2LOC(e^_xGutzYSNuYL#dS)DcJjVoR;9k4gSZpu59d=q&5XBmOKN%TaxQWy+J|f^1o1!I=h$ znh+~K}E(H)%Tfl7JHJvL$3UO%5P5ox9ZEyf@ZMnWl^Yt&Ra_9IbAd9j4nq@jMTeu zilHeywI9J<+rWuJ&%%L!E#qro<)?%MTtjf(WF#4D{f;_Gm^>M#xiIDFU+D3LvCKGl z^ELM>Hg+TOxB1eUtyQJ5)%uIY4hfWeR3)*(v!d6`a)a{~aij`U3dPe{-cP}Z6A zxq+zEMCAU7|BE z)(a{=Yy&usUU6CG&ul_lYK)l3Q2dE7BG<%g52M%7sq`6Tvmige{Yp8O5#4JiG;X~h z-5d^baA?wiB3g8)(S*P>%;7%(AmajOty)h}2gPXpzzK!6^N4U9R%~qLV%pYpIYse) z`Kl}gxdsvMxnF4Jc?c-%5Jk8gBmww#`rXj3?~$lu*!T5iGqB!Ni;GnP6bjd{fjzh8 zFf{2^r)*}7yJ zY8C44)9P@_({LqPNp2SpBA^3RhMMRxa3mT97w?(}QRs+_<)YDYxNmV^)UmtrgP)dr zUj1x)j4cJR^2#WT-IZGb-XFIJZ$69uv#lBOQou=ZQ+D2`Ei6bf?g-haBQ>}i7?pos zb!#-(ORV9fpnxScy&J?+i$z{j&kGdF-@3vl_E zKgyJeV2Pohv_YXY&^bD(XPKL}a(YSr#($_^9J4CUz^yy(cxMjA1LllCuo0G&)35X} zyb`pg9MEkPOm}FaAR|MNDS-RQuM5*BZeyS_^^fQwt;f5!OW~HNQ{Z@w1Xxp}2wqzV z#;u$}e3Exgx}D{hLAU-$vf-t5c*SFQ=(*ciOUM)Q#!(5EeokEYca=Jk?>vBkjN0W@ zmWpYgh)oiPD7zC!%G#C@?90sD0YYVBc|O|iW{(8b`Hd9`HzFd)1W$)uPwxs5T1e54 za5Hz0Ink1{e9;oEHPl#$rq0Z9MQu9ElNbnyihm{4wOTC^?O+QB7tn!v!sfyJhf_Fe z3Xev7`r4=7-n2CEK25GZOGqJT`IATLQQFb30*?-!ZSYhnDhkl-J0}sB006VlJ))vC zP7hNhL@{KTv2a^hWk|NlwXtgg_)s?mjK4=3-IJ2(jhdJchdvKsl`atCao?oSt#(a8 zTUJ&tItaoV?DjkVy-VR%TkX2pcDUCi8i}%C?=+n^-kPRL?zsJs6v+ zqMDxCGty5gH8jVgc&-xLRecDN?@;&WGw9qJI)gg~1IQXKEUW{jI8<gXA>AY}s>s^kk(s!@&D)+^Ql$-a@vfS{Q*nH>D= z1bg~oioUg*qZA+(8ii)9v}S!-FuaOJp{s9Ps&@sgbCPAz-F~jqULz0jRaW*RP>T-e zV>pBdCF_%h&Bx&YBj(89{-^c6(&~ZTk$u4etTqE7XdOp#854K(y?svOj*3Q8a*rZa z66*axn-O@Q5pu=)Ze^zt+P8&qp-H>Byc(_mgK8HJX;ejNh|wZS+_X( zho{zl^yp`4cH39}6kYm%1smLeZpZ{5!xi)2VT#A8hm+ATEnUH86|}Z@KKdmdT5AU< z|0z>+2HXh%>A6G5^^!_ktec%Gtq^zgXwVV1^2v{p2kS59V-IRCMbKGFNobJgYd_D| zbY*5gvW$8jT1$we4 z4@HJr6l=Gq4oU4`FaVOv?%Qa+)O1k>0Id~9J@w~rTkiuyg5-x(1@!_9{8yAp(PtY> z_^oLs*b#;0#;A(| zhoBRTg23}I&|4BU64;w3iDpk<#dX2F5k_*PjKK)XIjf%=hS6JQu?no(c)_F3imV_@F}YVxPEn_;skshd3}- zL{V?2pN5)o&$44_PlyfH5r{>pZ8!p3;Sga?KrE@J%yr~*^9a?}VOZ^6(p6Bl04|c$ zW7}YWN(tqO>RkR>RjjI9i}-e3II-N}r{mndP|#NfZ%gnxC|_nm@5}7557C@V;X=53I4G)2_0F{J1l65j51$36Ukp zrw=8D50(GYi#z*b7{+PSc5P~ne<61BRp?@%?{x&%uOsY%BNR^_VAQxGG~}{K-A=u{ z0eW@|V#Df?5xmAzTk;xj@?Psf)__5rXr(`6Th~DD6(%dxX%`i1OKVdC?m1H%3@Cta zVBO&r?Adnfl+7ndBVAf-&fURs@ls?deuUFo*pJ)Md=eGv^ONGIH@M zbx6-1Hw5RU{&7yG+7(vWSFVZK$ zi=+*Ka=>q6ISdB^q1Oh0u(8{VHPKaOAs4?E$KC*z$yI0L7cy6|50-?2N?u;mWe5S7 zrGCWWXg4RTyHe*`^&PO5W7RpY@`ix_OictW)3puLcfKQWi-UNHiY%U{&Z_{)wHuo7 zCsrQz5ooDArL+?z#*1rguLXbphgLX8Z-mxyVgZqpoQpV)ax%HdQamrh_uh)23tV-M z>qTe=SH3)ngPX5hE;YXmPO-}F1PhkEApkDdcjeb_c2f=D*1rVi z#GV!fzuJ)aeEO}`-vd`G*)>VeC)#e0bT;z(pc+)|b}X^Hki#h?5Ae8gYNwwW zUYux`eo;a$PI9+T_A?68)azBt2Vb>{r~896T3Djqo;ax-;gR;Sjd!Hci5DJvLD55Xou9tG>aRY#wLyZa}QcXiUprQ`@ek0T4o zBbFL|#(V}RwW-B{t#epC67}1+=xU|J->2&8PYGo^ipvqkfQsMR4CX9%a%FaIcwEh< z-?90*K`$t!r>g36!`&_++6rIEPeDJ)&)|kX(@!i@8e@POq+cf|S(pB<^k!C=7SYHg~aFFoBJ*gbg^e`97r(m!~`&R&QI}_N}(4Y)_$!sKW#AuD9re}d$;C( zbrBlB9frql%CqtOeTZ~%_VJsNy7UZ`>|55>F-{6@g@e)-b;)8W%>J0hspHBn@V(?1 zr*IzKJFur4+TGhnfz5Ux;9nS|#ggj3b4LFhW_Rl&o5*0~2d22-)U8!`! zfc{@**Z@HQ=-_t1*6tp)xvtRZq&9J_>WTfe`kv4!sJmJg$l4-yNT+rfzDx>r-^%GD z<+g|#mOE;9LH)9?i~x=Hy(JjxQQ2tj5Huq+FnZKV1z4Dx(Er7>c+ zd{a=7OJC0@uEG6$ONFjtpEAgk^lt5M`Ov_fda1%-gEnlSW04^XMk^9 zGrOtu=c(FZQOf(p-qX6tlGn-v!QQKF#>lo(o}rN6|8~491ifK2Q0vUOR|=D>Q$|K zXUbh0CAq284&w>arsZSnE*FC8RFBX*y#it)%NEI%kCRaNkxqA}YKIr0`M5KDO{T)p zYXC4IKpX(#zHbBYKGF$)EKBoIkx#|Sx;ojJFtQ9#qUsMbBGIF<%*%Uxc0HteZV5gC0~ays5?H#MQn;7Oys~Xw)h_DP#@c$H5I1l^Q1~{yoT% zM05iVI|32x-j=6vVK<3w31j;qoGpQTY{cs}ughzl_2IW|N?1Mk2h3M?Y^*s4>M*#0y|AbKJ=eBiiCu#Br zxdinLqZRevaChK4*X3Lq6J<|2CJ1&V{D9Hsebw)%+$fbn14(YPlSa0XSup4EtVw#L zzgIC6CEJq8SJOM5fgrwp zpcwPKV;CWY6&5x7!o2$h7Y@6C%sT-IUO6R@-6(ot^Y1Ge;5&E)z|Gf{+=R8WULyf- zm{H_~&;curZvDiQ4#%Y8VcLNTP$yajKE9T}A|JI3J(KLOoTz`(zlW#+vH2iBbXH(Y zh=k#0N2NjE&R%EC5dE@@OHXfWAb!AWjl#~d6sd#ZAR@F?awvNtT?HkJe(8`WqCX)G zw;1BL3BXr=2^oeJAGhmcgl%+ussA_U)~V3ITN(4?bhz-`I-?-MBIaghBacs-4fFvn zddV*<$c*PvFL}{d+ED7qT_Ct+L20h zgSs9ELl8ioevH%42#WpQ0qxzgxZ?lkyuai}P&K1%+j~?^rOtk@ToE2dB)bs%Cm>70aN3O|Vg)FCQmb<&o)R1}eke2!emmoZy4-Zz*B!9O7_Q$BiEan}@1(}EFOCA< zJ)PkA?bE5AgB~KW_9WBw< zZh7hkQqzu2m}pOY-$wSx_<3$NxEx zkRMWl(+Bf;-vWz$oquvabWUYh8@N7f2vqLaUp8jwFeRsv`405{e|Z@fxo2?fdOF&F?SvwZ7;<9&`x7t5;hzbFdwB&P&$stcO+y9mrZZwW@v= zV-=`yxt$f{NBa3|UG6+djBLpPGF(BZa~eU(Z|H{x9imZRE7||5f_I_Zk3yGwlJ6$L z^l7~h$@+dI6bagMN@WOCA7OiHUBKy>pnk_71m;PEra7l!WBF%9V2!gi6J4wEsMziE zvnT;NO>C^4llAm0@yontnl)be+O7fpwH&*RW;6eL9zuIhnD)-c<{svQ7-gUL2L6q{?PHlTM1_4mdUtEdxsVL&%vNJ$gjg; zjcDytCn(FW-hVzjuhh^1a@Bb3ZXF=W1^zeKN)i-Jc6QUOJV>%c|9Z|aBKi99ya2gg zpC@R)daLi_U&#yM1ss)xcy#s34ZrKPiH%dxQy6;R%0#?sv&j}R)A|J!^ zQL=LUP=f0x%^ThQYKdXh_F*+xcD>ESzB82OJAGLWn&J;o`l&$|9*$7|e;m(@ zGqUbNlpS|RWO!IaA(i#?2??aLn@<)P-*-=zyE;G zy4U;ldcGbHEBfn7ZkwL7YEEFBY4C8-ukSp3z1)fwfnW;vSdVf`+W@qGDOgjG{{%Xn6g=eTE?rq#88&^k+dcLU8l<25&I)?tCF*V-TqX z3_04OuRne(EWlM~>;5}R|1?X|Cf`a5(2WeKd<(~4j2o;QxUG&X86{=y&mL`e9)9~? z^QxoQ?PV>vqae3O#DzRPD+t8`vJDyGig#>nn=bkAR72nyl?vYOMkc|zQvOindZ=^w zwbQ77Rq-|P?Io=VK}U3N^1G}4EmuPg9KS~B)A!B=G5wgD^_q;bb7;#)jbY}}(--Yd zXhEw!zA4crI^aZ}1VBVv-vQub z?lbe8s^*gF12k2f+BOAeNHd{TEkIF;Y*@(u85-+NA}S^fynfR+GU21I8-CLAetYVG z0HEpq1uF|9#RlaIOn=P#|HGC0_1;|M;Y;Jvav&GhzwtoSUXk5*=h~=zkU3r!%Jmdu zem3r1_BzpkVo@YQRcK_kD5f@a_S&d4rd|!Gd=!iMZSg1M)wgE!1Ny{ovD)eDzQ*Dm zc5M#ExlHL)6+1FwdKwUWc^mhKk8}gn#0R>%A1Uh z;AdQIduZuP^9jErr0MzzjgH@^4j08L&hj25g3}Hav_@b>yxtNvDRS8j4K3PE7?C2s zB0&wUrxqrSZX-`X(rHZ*VX+fwK-X6_H}}fF-+Pa$Ub#Lw=4)_z^e01UdiHxf<0H!f z>9yP#NLDE5I5QO`3f8bxNmi{;LcJB$1NvPVg{NuHu=<}Qzdo9s?;W|yxG7s*SrSD8 z=2UA>k36FPf5GynLM|}F`E&MTZ-G4=2N*+D0X9uGN>e5}Pt_~fSXARNb~~>B%8JA; zl7ulUVoGF-YIz+xCw)Kb?DczUFygoA_|K*VC1oi~}$ zl#uvJZ?sp!+UFS5mHL9G_g^0JURwHelfYHuks=^lf&e49eFt|&Dp#gHF%H}srNieT zv_3b|pT#Zaiv799G|gw}JS*Pd@&Sajv9`*E3FE%<2Vx~TU#7$>E#zuDPEPqrpUFZG zb|o6`u3mWs!pcFykjtR%-t&R?{rzX%s;ibxmjT3)j$S%4kH)kq@3r)`HO@wx`KD;q zSU6pW=yjFI}at zCdQz380nG_a~p!|B8w@78i5K*^jX?qOyYGMwWjn{Fy?6R?Tu6l=BuVEn0GKY9bCP`(HL8CJ z&e8<#?ZHOdg~%pg9=(6&=EYPoz}Ch3Q1npR7{{gpE4+%~i4LDsZ!l-Gr93FJ)vCzB zUc4YluR5I*NS4}sZ455CdGF)a%n0$%!JKbs_)g=Avwt`Glf2!k$5*8>vx0KI2v(9f zGNr2ez4$QsH8{`Cj(L`7+H;Gx)#fRIo^fvsS@5qt-+V@acF~V%0-Ku7)p>vfNI%$M zMjUKsWLE+{^ho&W;Va>scNT)9MYDG>bI@o0KU&`s)*Q)}NtkyhdU+n)aD8m;;ecMf z2bQ?*!>dxPdgec=BHLmO4n0k0I!VE8phQ^!<;PjyhdX$t3t@08OwYXe@5WQ5+UJj> zZm!f`e{wI-|H}#Xlm*(pRv!axC2-c*E$wML23$#yL_=Y9D4vEoe$77{HSLtcI3?g; z?-}}Q(Fj07J~6_tSbi1+`Z(FQL;#|d-A|{N&($%&%6I?I@Dn*;ILN;yq46;1LzHXt z{r4{-&eh&t?NpjJ=jg`iImmDLetf%q?~kBXgAbxw&i53_oK?_p_g;zJzviR71nU>^nDY0 z2I}0SA2;g8;y}bIw?ylr9JzEbAMAV#6!*Ega$>UBBZ0Mh@A|90xL4{~JsXQBKGuI7 zb3>-|Ap~NPi}I=R7;=pWaOqFc3nv0!jZ=XNrZxA}w%l~igS8hCGQONCKL%#`mndocxytaIv$(l)@Jb0D#Kb3$|s?Y1(j zS_NO@*=~gTJ`3IdRQtM3QgG9yJ#el3U(Yc(0o2pL4=aDyK=3#xNmR9_dnA-AKWh6= zzqptpm6O-VI!voL@v-6FInNwv_z~^eXyg)7oZK_pT&njqTE*4F75IKOx!!31_B5JR z{Ok)r^@lutWWVY&VuF8Q&qramk&+X0R}>EtPX(E;|Ar)&lz^e6h_%#`3*-0tzw>2v zk<_oGOg#P-W3wnR;&QF8l+q{(LQhCMaTclt=;JkSW6-}Z>i^i^HFpw`Z=4i?($A!d zAx!j{4kBHcojs@>uJw&sGg_Vur)0kg$lIR4cp!wLpj1@(1+Qot>}J2!~aTgrJKydm5ef_uq8bgP9P!l_TopbjjRq-o4NSDWp>Lp2oTM5K zRzZ2d3V6&|!nb~Lxe0nRXC{IAieGzr=}iNHgI0!IudN7!U&564EsAZ07} z++bb8qd!LGXE*WKPwANL;Ej{bOcVGwkHCErBRRcSsJL5+GmFNZZ%gHRD)ubX2KDpx zC!QJ$UOQV8Xey-Y+>Nx8P!}Xy_1->zEo!XbEaPJj&Hl6RvbCn$jf96h4zIhY2+2X2 z8ii$_@N*Z%3iIsFGEQI9hl{3Qzk-gWAe!4eXd<$ea^)1((kbD|J!>N^qGH&=Uo!5vIh#s zkg7Lis)TltAF@FXyFTTAy&L=R8kVSGcWf(}D}e=cUOOL&%n}p80o+e+)#@>h$0*WonNAPvv%oPn^xi(F;eWvLX z*`MlWf%C1)$$+Lpi*IO*sa&5vE9q*YZo48v_{K(`brlB4nukkKkCJa(el?1}wgP@Y z5kM(Hw0Tm%Q=mKKRD*W6ay=;lYgIisU^y$#Dy=>zjU3j8k5>BJ2FRk;Ep`Pcu_GgW)6k5D?cGr>II#V2=LLnE^+I*9>lO43RPBxK{Ic@*P+_3vT zj(+R;&xL+=dYPGp?OMtrpPF}dVy|#*=%d=zfY#J*yB8AO+Kty;{%h5^UA@geB$7smg!4H5=$I2_&y^ zMZ%1m2F^8}b(?3j0Z|onRtg0mEu+#I)PSI{^2nlTQto#9y(LmE2j+<|Jm1MetiLtL z@nqR%V#nEeX+dAl$>cU!U}lY$Lp3mSfS#Vc`AJ1{X|?AieWt&X-F_v1{cNk;a)C(LCJsK+j71dpkX)! z78E&~(oFz-|K;3JGCxKFnVAQ^<~@MHMSer&Hdw=pNb+W3t>&(}wt zWQbk4cPj)}QCHcuM-m~U|KfzRgy+PSu0a@QK_ zIAP<$LU=AO6$i;m*2hM%x#0N)ugUO}&+eZ1d%My7Z)8n`e&YU-Qghsuj-$Q{U7_~k zy{(*@*ZOwf1`5^_8ho}if*lm&kCmIs`0Aw(3L|nd=L|ThMI?`5ykczQ!wd2p0HooD z^Pj4l5#sP~!1Z@iw;VVoTF0YIurzHEp*Nfoqxa$mzVswM5Y6I)sy&uMjY`n(P8F7x zf#tJ9;)KVWd5-?DoTVClFwv+u0L6~us_8&f4tZGzrf8|2wD4~a@0=$8Yhtw(?e{j! zBF`?I8|yDL^!5DhYb{8OY*iurG4zVu>nlq`%B0xX^}mK5p9&3))GmMv6^xymv=FxB z{|Ut7ucu^dHl#~hXww=*O_YT{0_-RVCw*gl=mFR}=wQndL6MrC{S}AaaO0{rQRDJt zCJ#v#@$&5Zz&t9==>b%-iXOjMeSV!5J0b^{2azM?qY7XL>OZh zW#gJ>qefZ4;io>C1rr^+Z#jmLtv0Du@@s5v9IB4`qEt+sd}y&(({AbI>{Z)%=ODqW z^D%}OErI}zv%GA81?-c^^*}(lbwY^|_ZePp}pc!2Nju@+=Q}SYkZPZ5%91h~|#d6qL zhPrgeFMVE3kC7wFQ@tM2kdnI?bCVckc&5<0d+B-1kU=b2yC!`=RQE{YsP=jIKkEwc zMrj_7SYIww=hv*%Ky9HEQJ&-^?mx;~`|mhXv#((`61W{!r}{8kVvwbuAH|sAUi$(u z{mJK1GQJ~Tf$Aqp4=mGnedrQd7C`}0I+?zG)TfU`vCu`4+sQl#VJuEQt4EHZ97fB% zAHHt-gF0~;2w}!N96^&AD>{7aS~v8MS?Aemh$(=jA#bjl3I|RkYdpDpre%adXZ9KF=;{p1|KxUjSa*3u zij*ftVns){Zz1Ro_F~R-tD1x&@P@KxMbD7rkzKl7M#l(_n=S-h0b5$(>y!#Z<3i%> zrRf&`xz$`RH28C>0NeG=Ix-j&3&MyJjS6C%_zn4V^`LeG>758O{tN_V0c>aFcP+Yl z>h6spyAS&K_MY&HMlt|H3ne#9-g)Jbv%ng2KH8S6GKUwyuy9kF8Zhv0N&e2QVP|?l zxX)YBhYj#s!v8%%-6RhQxe-DkN5ikWepaYNd_gCa+}-r*H7Orrk$-;vk44|m7B z2JfpcQ#T>S0Xn6}YK6Kf3OHt{&s3=qtPFzn$Pv4~scF_y9T{mhDqU?2yP}&XokO_r zv`@2THUbk!N-VTr_-ob?+i>f~kQnp1Ac1VAQCk`Cc~EwT9F0sZmU~|wB;ywlN_Ewr za&BVARBx5iF}T6bNgH(}=YKL{B9_(zcXY1;)Ck6P<7k@by>{hj#e_ziZ^>35GnR&m z{BPTSb9_SMXYOesY54WEp5TfFBxNw+V^ZB4olZeY6eiSWacRuzu);4`jFzObaILn_>VvoXJ=VPA?_0Y639Ur)h8S zKi_t}U0%}rcrMIHY4g)*w_*G>pdgc%A8lEYp~hRq@bU?8eZ!kP0Onw@*BHVW zp!Ob#39pYi`G1cRQVue zE5*tvVI)_wTQO}`fnE>|6uH5ezazOFlp;kBS;R4*8|?;;=#7QmPv4Q90I}E9m;%Bg z3GRk}r~Ak5!^IS9w5`RRU*d$rg%PM!3|{QEin(-7q9u+AlVVc^`qXRk+SB1Rs=DCr!vJ-t33+T%XU zfJBEAkl-@|nLU{UBmFoK0&gZ!lW4y`9Z)98##N<$=?70rx1z&9s&7fM>2q#8zR)U% zN=jgX=%PiGEf#iOzE|8k)h{^xwS9dH}VM> zFuE~6#=&3AO9|v;kk%gEc;~1w4tsCG(ScYjZwdZ5n~XeE_P9uFPK0N|bZgF1a!&18 ztx}(6_;-drdjr5KlJ0bkPnI~x+x=~Az`W_7Rs6(AUu%qZ8a#iIUTy4qAkZn}D=Nr0 zV-8>Vo2>UCfbMXo5NZUo4mZfVunk48i4@BE517Gkh8Z*MB2tP7>)<3UF8IAI-KQfx z)d+y+fusM#ZymQ3u%h=mt5Ri5VcecM<jHm&7iH$sF^R!Ui_b3OFxh`y=u<2FE=6O z++%x-fx9El0=j0G|BEC4GRXee@+rRZ^lKWwKyB|QE9C(lLo&h%UqlSjr4Jj0EQ8SJ z*wE$@^lnikJRitY0~Lis4(E69f&kUpE>;Ua(2RfyTGbg1p}_0RV*}gK4Ln0xv(Wm@enHI$7)z-SRnE z#iM*&)?%FgV!SvEjASa#;KQbvu&*L3XYOAG?#%basBKOl%z~v`!Iwec*u8X`Y=smX zOx59d9^tCub)dhgp<&Jw)S55&A+S0QuH;@XxEUL?0Y5XUyU$wB+Hfy)s{J4!n;-x& zzT>!OA?u=AJYBib346x@@1yRe=-!c>Zo+;!}Z;wICk$4+LD^0EC$V#Cs<3=*!2C~=iUZHWTc z6LO#2?V89k3i>LYBm35~?rBu}p2zZu#=eq)-HM5R;FPAtZ@Dn~_5_d9MTNQaXdlHT zAHD!vcb4!E8Jrtin$8DCqN-Ua+`;NG_w>4bdH|P8q|k}W?aMB^$+1kMl>Pe*iwvmG zk&BJ%7l409=~mf%cSjWh*VaD&0X}Y>_U7FA}5^fyaup7T4V_%tRf#F52WKe2^lyZ72p^aU~ zaupoLO9B*A6mL3*kC*3WI#x>Y6u^yuSymdO~ zgIw=VcU}F3o;jaD0hkq_Njbuu<5Kr;`F6G^a1qD@b%V*Ct3o{$i363&9y!(KZ0h{M zi)47(#L!$V?}Zoeug^Igu5;DB#YZy!_WZiW(@@f-Cvh&|B$M+hK#BIju={V_5(MkXHNAL;k zv0^SO5@aA(U$sPFzH<%SVowOd7f8&lGA{pjB;KK|b17&!_EzcsBA)q@oAI0u@?&y$J~Q`6ffx694yjZk_zwH5bQUCnUma2t$0xa zdfm%>Woc;drp&JsDstrCF(zmZ6~rG!atw;~(TO6)qcB*zt#%tO@CZ?*rv z^rbF*4hvGb@~0!qW&G!(;!$ky%%V-qG0QKoZzJ5J)6j|^k8?B6 z^5Zfb#*}bXi71UjHp@JM04^|xpY};dO71sklh~Bt-9hHwFGc#{{>dW)Boqn^Q z7n(M)&(E#t6?ClghY6ggrME?*Bc_}7M_F7}ulk)72&;)nv_KmoAdQcP7m7NjWh$I&Y%%H)=z517UW+b>{_deru>uMXzX&_krd~9aH zI-GxmK3Q^gn?Y7YsR+Qf+5X!XQzx4c9c1lG3oGkHs=g9hU;|^CC3l5&@#1!D&X?{S z>w&r}TA9w@Q^W@;UJP2huL;X+NWSe2uD^|S2pZk7Q%E{p`H-C)Xxd6!e8$1|&7y#1)9WnDfqq(htPV7jSxRoZrP0!i}O9Vq$Q7(-(bU$*|6wd@D^$XdfpYR5M^ ztuet#AxV8PP2>3d+XqN16Q>V5Us6wJ?XPXCdIDhqD|(_~j!mPOhQq-2F>qkHSob}< zYkP7DAqf_lis|J*UPZ@Gpmf$#=L>If$YRfbb|>%-Ew?#fHlImaO(sxC^So?{g)Pq* zNE_H0sJO?4Ga6i+eNU6C)~%59U4`rf{-BDWyhX)Z+KxBK)y-mpN+$!_GO}_mpaNI- zC!#k`IHi~fVIf{Y@0;dsKL659QBLLnHw;0Qu9f%bT^hDbQssIxF}VWr6T;WZ%mGa* ztd`}M5G#_F@10A1x4jL(287&>Hw*tDWUgNqq;rIcUd7@#Cr^U+>ngr8QSIOM1%?U8 z|Cs9cq%wumC$+_zi1kVzp<@ul2LvXX9xZBg7yM4svOo&8Tvu6{`Jmi+xfnR=Zzyok z#+}IRlRHV6KbIccB^p!nc+M*5^kkVj6sMzp+zQ`HV@JL^DFDl&4x)vTq+6YS)knG? zBVv$WUQi8D>YHZ#jE!)33>vd{{5@y_kv}QW&GH8Xw{{DWd;VI@&Pn8ZId(&KVj^{U zyXVW@qM^Mp!gukm;1(AOzRMiN5Y1O>C_+m|+< zZhysndJ=hq6`2KH!bNA8md+^U3L-<=8l0@o?RqPa-SQp(+L;Ao zS{}}cYh&k}O&-+SHOIDb%m}}-@oDXr5xuh+Xk@G@O*9}R;wV^3B(fVSK7d=`d@3qy z$7U%>04w!cB$X$cJ}&3MWgtEAGFVR4We_L<%mE5e9dZl1Rrvq|59E8VST4?Ro|AKH9U%O(C?s1!j&xVzIQc@v`=KG_p4;HsW$%#_Tj}} zRkkS}Bhp2vH40T1Z+AgmgygO8y|8NN*e=9I3sTub4M5pXvey24#!GNkL#eW(E&Cn% z;FB9paa&-sx#5C~aAfiuM7^p4RmKW{aP`eUXdaiV{?M?Zu0kXPJ*FFrGNFPgtZ!X? zKsSX52!S};jV~8)SAAk{0KYrR7_qpz(!uAP1F4220jYx5$78B41 zItA$IsRJDs^`7m~ykoRpC>A!ERGD1-1LeN|y5YFPcRzMp3s;h75c5LxGOgEs;Cec> z9vVO%;kT02DV&$t{P5<6!6)KaKltv)m37aXuADVFLY``>UkE%nyYCEh)ueu0`Wqkj z=%Lx90o6iGR0)y%wW9z+Ro~#9Z+wdM8 zv^wi*d7u=4tVdF3e;ZMmpz5tI9dn(R=zNE4TW}f3b>J|L@KgdRTp|k1U?Db%W~FbW z|2y~ec}wKc%7Iq%In`*Sa=XJzAIzFeoi;nJx{m;d!=Ka{<~C-vYDuAlET^FOay2aSWb0;nht(xyjp7&fg&(?O@-9a5g4hg~ty)@P!JGuhN zLh~XkkYB5_Yz>jxZD zU?z+J;vwgAg6YpI!~^?H9c;pO>m`3aUx=mV6Z&qfx?@foRuTYy$kQ$#fA$#b4xfG= zb2+H-)YJ-R+&tKg{NqQ~Nlivc;lo)1X!rp?^nM%SwVQ-JVj_DB3Bn= zduTU^RASY`D1b40aglkASGkK zEq*|xu0w;LSbnnmQ)IDMxm}l~I1_ul?M%n%9B(hfM$^XAS_SmGiBlIqTFj?a%EeYq zP&tFj;m5s#^*wIiV#iCg#_i0D`a~ppK&pbu zK)284^g~|QXjzT{uJ4Ar@m*RSCMBUH-|;nG`u2nrirFwX8B_tj-7ru$-1hP7X~GPl zv7bHhx6DZDR$ZLx%RK0Y{zrAuT2A`sBvz>6qwA-unUe^k^VR){ zdE#^#2MQA9Q_X?ufvQQ~WL+aUXdnrv#48amKEBdB_~0&^9gl*iwcE*7LAa<}j9@qI zBod9Q46&zTVR5OSx7n*MI2k)+kP8-swbEaD25p?!{Lmiv?-G4R3bn;%E#)v2by1hT zD81s>_P*FPBC)X`39MOtz{)F^dqx@^uw@njmq zxeIvLn9iO=UBZS036BC%d=PP6Rty1()v$$A5%49w@C)I+@Txv; zFBtQ)KS8B@QmsQWF|d$sMZaYYmxUcsBZm@act7T#)cM@C8^Qy0)?437qu#u)Tlr5C zJ$ZH)n5&)4^QuFMPd97G!iA|V6+I)2wz@&|Ph^Dut%c{H-I0+71gnh*fN!U^{*`5- z=N>xi1$&>GMW=P*vilITZZpJTKt!XF3ImtvL&7mbcd!qM(GTyoJOpYB^D)Qy;YkqM3WKM{;Ckze-uVr16=7#8 zdJTfg9b@p*rkxrq5#m^>GxvWlC!Be{_mb~bz}z7JRWg{SIt^v7VjP??JPy+rdiR=9 z$CRsHF?c^#WXI^o^_mjEb#E~w7u8;T)q+|S7#M#rIq42y2`n97E1n^#KuH!F(54YK z{GLC?WyuS2oRvx+SdIr|0OZrtvd7odii;~GMYGcrpNQ70h5k4MI%fpi@0h?z$CRW2 zgqgX^A&$OVH<|owzecv^Z1=`Jy>vSrJ^@-0Qbl%CT7WB*{QCnX5{8Y*{vxJX@kjXh z>BldCioy*%xp?ETVJfu7kG5NANL#}0`^+(WSh0R7nGn+ zI|XJ7(o|nih)#jZ^Y>5IOsmsu2&kwNzFFPCO8EGfvWV8hJ+bvsi!plN_Rmji2+rvZ z9HDCAbR{Um`T!YS2XQp68=hpBWW_WCrbs?Zkf!%b=>6`3!uOW`n;_m0E?nbkVJ<_p zfmoT%rtt}qUY4J5jg~b z+LE=GIzA?^?_GuF!2!ld4k|OFT-iV80(m{qYneJZ1T?E3Z$Eza=__OYxkZ#RhxfxF zz0KtY)zqf;B=BG8=(7TBkk7TRo@qC`9pfpE7@Schmg{TyI7_V7z~p%;8zJ(?@T1Y! zBcw5KTQj*@TuQy#`_A+I{H|AVrAUEHg^J z)DSi5wEN)*DEqo`*~8oRp=GM5^N!BaKep=TBd%6vHv{&j>^DQRK)5Tt6+lbdCz!1! zld`Y3U!D8EoMqQ>-6QJ=TbU@JF*-pVR)yc8Zd4omsvMZ|l{{B$*6z$8tUVOE#x!U@ z5w{^MyH?aq8;md5Bp|ytdexCF4bbO4aGD5Jq^{B(JQ2*l3+kTa4f&vUiLM|H4~33# z=BVZvf3!$uz5Kw)mT)v4BFK>GUz=abD4tSX`zPqz_la?@OD^cIN9sP1DykmV+o>kG zq-6olfbX)t9{(XTLgIpeNw$Gnz}g-qK z-06xi)mtPUNkDeC-n6Om(tFD0qapi?W-7!d0rF7v8Yb{M4oM#q%k1+%lx{);vp-%( zFp(E$4r^rQtxvTl?RWJ_ZZcMqp=oG?6`M!jls1c(y3Spm24<<&YMdWT&2k-ajrKcO z@2i+)WJi9*HjjP9t+X?2B1kwR zHrVwFincY3hYM@2L}L?+y9O&Gxh$yR){e8!S<9d$P=DXG5RCM~Qz?`utg3vJaP{3` zm8LjL!Bg}_=jDf9?imj7CM=&7r&hg>`PB`kv_m|Jn2+I6aryCZO9T7UV;Q(@sePK% zS=)Iy{9_mnzS*&2E4{^A%}EdXdSZc6Ys{zXZaErBBUzE3+7=JN>XPMXF1|MVvsY{J zf&-mBhLAWeYs!a#cK9oTV`x#$(oN?%$7cjE^eucyLz-3=ZS7Xuqzhy&R(*syOrhT3 ztVeiWd&y5P*oP+`3gt(CP}vkq9RnCB@vL5-@hbY?TDD6uslB}|v-5(ZyY|{_<+gxN z+Xv)ly1wC##5P_hF5GX<*gtx;6p!*}epHmKmi1yi(qlAqOqLHhOj>xm04-bVNnZ=T zCy16Xl0y>eJQk+4vr#;%ctrh-4m%Fuu)a&up5Echv(chE)G;O9C|49~AXqWz1YwiG zU)=yRF;wmb^Ia^Dk*c)D_0mi*_WyEhIoqb_3u0VG~+t`ZMJpvUqP}{fo0Nh%^U9%rSEUv>bl8wUmd+Qu7kc%DEQ3yvE4K9bLNu|9L=x4XdfTEsu@I*SA(`{=niMEHP_>Z9YR z!=FF?d-Ii161hO1d~_n#wAhy{Rs-Uy&DNO1=A)Jwi;;&JS5LQcmPvZB_Qp~$4Vta* zTVH-%ICfh(7D;(XQ7ZN^!{l2BaWOXpM_cU5370D6h*bX6vWW&w%zPD^ zUcMEG0nupRo;OONP8h}LYW!FG!aM0C&t{yB4&qeYbkd`mzXyl4U4J3n0Q2raDgAf; z4q3cvQT~Lj53rUF8o?gPu)z}daaslshyH@{9SUgYuCP(d*c_av3_%<{eB@+>n+s8M z<7abUK=u8yFyQegh1G{?5BOQ}s-5VY`=H%>NCvOuIjY6k!^(eRf&JMPb z$B12M=gg}yy8_Hrsc3y!tp+Y>)U!{++l-;5_)^K}Cn~dJuL5eWUUmBUU|Cca~}n#bBqNT>Lv8-VW>^C3uy~ksb$ej)9XJrwRTl2|CjPV8`IjgoB~<0_(YK|A zkM197_2Rgg%HRHz6CGFHGc9y}JuzuMHDay$7|J$!)!}VH^Dc%X4iPCVYc7H0b=;O= z`#7UzEB#4 z)@R=BTAazr$VbXpGLpyq;gwtNRoAQhI!EzU{k5;AZ(~a5R)f^iLNI~R-jRA=Ho$L2 z`X^}*21n1swW-Y%Caaos?vGZl$p2aGf02w4ZrCm#y@zdnrnQlg1UqS4k-uKeiK53<~Y0U4Sz=0TU2|viV^*sLoWks=b6XN!0u1y!96-Z>+ z{k6z?8#DbUoRm4!WX~1GPzdNr`a@Jy?1lGJF{PzyMd1PGL=zzbGId&eNnx&~$y{|($-~q9=p|ufek*2<@I>1TppV+3 zZ)En!(K=4iCpIdSUm*K&L>&gANN$|k#;+ECPD%nh(l{uY2Q9~WU^|zrjl8@a^HisE z0x`et{$&eu4#Ip{OOpbZ^)*v_(X@JXfJ>M4~KkB)7 zVrERAj#2{O{<>_+!jJFKzj|M&p)2Ne4ftfoVAW!e1|mxvC$GVvi&Z<1@5(fhWgu>f zPE2)~nv39;wu8=TXUh1&Rt?57R?&%Uu38~X-b#vhjDe))yo+=5!R9_j*WZ12I5yo^ z*Kx+Aa;t^?^=4&eW>s=3?L&?DjH}9?b#DVqp(g(MuuEFVGW9oj79r@nKul%}V)HRM zkutd-AGg>Ztr(Q)^KWZjvtuLe*GyRB6D**)tyQ?icSY%$dI#7Fvbh>^I_wrTg#6)i z#;dx)^@)q&csXc&_6>grl=zUl<``VmmozhnkcEYUZQsF~X8mi$GOFIx zxLo4nM)6cAsm4_&dO7i02(M(D!G$1sL32;BA<^+*>h1Hbu_PbW@~1~?AHmUZMM;b~ zcRDf}60bBzpL7HB^;sx3&PN0y4M#^VfHGe9BbM}^y_hwr^)`q2R`*Hz)5V?-UbvD# z_BL+wBh>ZcWskuWma`j5Vv0azSm(mn|gP4$O`dr0+-QwiPLCQ%luP;?hi~3lJtpZ_GruRVL zZ@XfcP%qM#VNRd+tg(xDB#crM;_`NKZQ$a1WV<-3QQHK z;B5*_SbLZFvUFIJ`8!P=neUdae!k&GYKA$um=s&tq+g5Np|KN z(e6UGfFAvzK5acw9m`dtK3xoPl+1T#%_SN1l5Uh2bW#E+55S#=O&M$Y%Qp&oU1waZ zuRiOiK`ftr@wm4$bJM{TuGUICRIL8cPIzYij1)_L{{(A5g(4QrbZ5;gZ_*R%fuNCS_lez6zE=tJdT0kca?~9DGyb z%}nsgQ*hHZ(>(2+rxK#RX@k2@(-mhimE*UjD#*xS%w(k!Dns~G5N7ewEeB71A2;bl z8DRvDU0A+{>_DV}_g%A_I`?biZTHXB{qQ{uS>Cwt4f>cKgE^K1N6kz9)(Bnel!4VH zAfGwQ*|A8Wwxadmsbtaecrni#E&;w?t^>-a(3=WCLQ6x2^?(9O=W+qh86}jp*6c=m zn&docJrcxs^^7QuZuk>;QGqSR`VSkC;TWaiv3`qf-@M0txBYCcI_-t>!HA|ubKYjR zP0eR+YsiS9tiA~+dD@XM`RGC=+6?5rED@+ll|#WTlJf$Y$RAoECmG?}v4<{E+xr(> zTxlnScwHvqaG8?0c>7@IFVg{Y+SK!9jvW_W7zB%DVA%#4e-QU_-M^S1ZZ`nc7jl+_ zE0osBUoL)21it3h@Cf(}Org9@;uYR@7?0IzgViEGZ7{_5CcNKnDKgDco~Mv(N+_09 zYnQ9i(GqB92DN~G;);LRJ~I?43Adhc;ze_VA84mlHB03?n@w}#DyHx9{T*OO<}}3* zXk9qv%Mf+20D=1;qjL1@UfP>5ex%YHX->YnW{EYJ2nZ2*>Euo)4d(FHW{nn2KI&rxI=i12GZcDqX&N zPt*ZUe*l@Bi0QE#ahh>f;jeDP5hQSvfo0%9l^+JZ_LEhwZxX}S@F|o>9ULeNpzE#R z`3;NCRghb6eVC0yT4(IE-B9kSkjn5Gf!EB9Dc%hG4)dl@2y{>iPLSbb1Q z=RkU}n#zM&K!fHXng@LzWA4wRo2-u~W}B!0YfnjZdu5yC(thNCr^><*lo(si=!>~` zPy4>_`RV}+LD~kh|F^A!Pad3Pc_PP_mU|tCpWG8SD=hNl}CgJ2%KtFG@ZpQ*Gr& z>6-P)E~qagt&Bmgx(85szSq=nf&;5Q3~X`s>YK1bbUVb%yu_YfSi%nIF=6F&+^xsqA2Es9iXxdcE1`)Ce&P${v zw%8BJMu6=Js1p?q4~@BNAM%AL+C&54PlElsC=Z7|&4%Q2u_z-Rv{V>c*`>Ju+@i

KF((%H0yv@+x68RrR=i<-w|HbiKWA3uKMDDk3Z%F^X^)|1)06%EOV2$+ciY2>;}t+EgFDm5Kv^VtvcByyu1TIf!Gwv-~JmE<%Dm9TCxzQkDycQPC4U+Sl$ zkVQx}z`II}i+NRnmN6RY)$}C|ykqGwda0MpZ;HfEP8w3~S5+JbrvPCi`(a3b;u+4A zp5wvuX{Mr)*-ARZD;37suJwEg6#t#68%&T;w|2m6vtOfVV>|ktJc0S3zb-BZ2h{_b z0T374oT|vFkknzMC1~fA8>v71y>;atCHPnZ;mTxvDchCeEGNjF!O%|zx;it92 z%c~p>le8@Iz2AKud|ASmST%VuUfbL1d7Q@;bH#8%mgLTOiFR)X-R(amg8VUL4)iQx zB#DbRm>?hUesAx|#<`j=Xa5U#N_^T}_~qfk-jm8gUd_GV4>tB5ss;fDS2n5>-T=Sb zVh7y+9*6mkj7cVTi8$Ygefjc|ob=s>c&Cte9`gf3M2#3$Bw&SZHN5IfY-0Dp^hVy;0a@+xIkDkN?fn^!qT z@Fd79SuY&jWpFdNwJ${?JbL?~OC-u|E+?1A3kMG@uY&*X&*rB;jRWmvbgT?{;u~GR z`TTGARMTwxIYC7Gp}!ODUvzcbO2O-M)O%(De!JKukUl`VjftCd&wTL=;#h)ljH}HCNoO!eW*8n7<^CToG?d6JJ6!|L z@HkgFKGCj1F0SI0^r78S_pd0L3>k-+^2s*qB8)KWoky9RaH1CUUQ)BQvWOMlITeut zKaM<_j3XYQ0VzW7-dU)|Ag^Owt_!QR7ltJqX zwM65TnWb^ROG0+{{I79T*1ZxO+B1nkFVbyQmM*|_O43bS)uA$V1-(*<_++jQ=x>x& zs}yKJPx=Y>K1OA1gkM!sIE%dcpSFncFfC1x%7$@`zXI-+1p%!6H~;d1vaW+u{FtL2 z^KPzPu9H{pO*ewRD6ul;Tb~uSn|xeI%a)N&JOMKw)giCTmzxn(UNM+Aks~eO1iq@A zxtJLesL9RV%}pSbl2aw}7I7-sR>BRo8~<2p!(z>u6k2{g9}@2w6h5D=Bl}X6{*Au+ zPby3T_s(J;q4uNvW*VVmZ#;>QmvK1IYUjJu1*-TIpceVmF|ZL6s54xE;x(IE|{dlhR+Wx112IQ-LzEka-?!Cu@a%%4KeG_eYVW+6(9{Hg3LRA;`%F zMcfBEx4I+}{fIK2wuwqp*#7={#Aa)JNKLE0Exsha(CEHs{A3EUFE0$>7XM`6apVr| zi~l{vaY*wA7xtmIJXX%{+!C=bIBRx^x|Vt3v7be{NtX7D4JV%U(q4=pSvKdBaVD2v z)QU2DX}1B}rjxDz9QTNUj#oAWLD!yMk|vp*ovCG0r@-k>>_h?s*wh21ls}^*@_YRQvLr5M>15Ew; z!bbZ<&miRRPo2EgI(QuA$Np&jX<>e0OW3O?J_L;CYb8;u1IA18O;|44Paxj~(}qCq z5nrzx&8PiVv!Yd0JhJRg8b5whKPLc74(j)a{|ueAMJ7ckTsLwb#e z(vtdxazyAUNXvg!Ki6Z-P3NUCUDBUAKGQ`v#tMTT%1m*uHy7TO4TTzm%Q7pMBxol) z7XEW(0}#8|AJ%zykKpPd_Xom89Qw0<&g{r$_S6k9`y$2*YNRkyCWkvEzx0O9o`Ebfrj`|X}q{ev^iMgRD z0tGX%DZx%7qI`RI2!mSX)uYsl{(AH~#%Pmtp26iDT#m>QdQo_pYgVY}u0Ei7AIL=0@NFpE+w@ zYQsGQ@JBGuW}r?FEub3^)mS9-lLZmsFB0UOT*qFSGgib-*QwT8N*yJKva$p~1jjp9 z&<}?s)J($5*Iw&#lhJGFtrPvNr)_)kG(S3vh zW{=a}3DBgrTy2ZXxY_1v76g2lJqJI1k0ULPci;2I-;X+-6tfa!b$8gH zG_WFV^xMzjpZxW8zBk^kj;*fRZCbK3DoF3c4@N+F`1UJd%YI5C(aySQ-|t7MQ2(1x zwM+R{emy)+F45W(H5$e2PZVmjCh<<+=>t-}_mB=rIPpzLEMbynZ%8xY{|M2ZexQp- z!N%|&>VI~hJP34@k1u-$wDD6P8G?DE(F1*2uo3tmDA$4yw)ly|Q?fmVFf?BSJ>37j zFsgI1yPG}I^Qu|*b$M#z@kV=|t4E*~WhprYnZYJjqpP~+*PS0q*4;nYd0to@x?_Cx zk-`W<6p^uf7`EOX-!rCWi2GzWtxk%CM>Lko7UN99Q=@U4SnUelPZhZOXQWGH@8w9_ zxTX|#U33ox#C+hLJu!L>^iz(yRnjgbGy~F@uM4`iwOoe$@hr5&xVc)o#3qIq0!Jv-C^DD zZqOmE>?M{mMdg05ny1CC4>CKmIBD6vROl##_aV~bGKw6ALt)M>MRsLrikFwSoOe|K zhu&|wDEf#D5RedL0C~x|+Pi#ABfXxZOY}eHLGWf|j28MC1agv?h~+Tl#oaLV28zoa z015CrGyqpA`wuDn<*sILz{P}@lrFs8+P^yPA+6fg^wbw{zt73BUqFw=bf6HJg@&y5 zs&-6pujM~HY1|6p`o~k=MJ!P!z6~Se9TnDG+4x$sdC161h8(JBa8;LhvQb zxdT7KJTwk|*}m>8Sz5md`4``-EX)eK|FD-&$l;N*bmhq8>n*Rw9f|x8`^A z3BK+0nH7QUa3Aq~-+myvxV^pIwI9PbbL{k4Bo{_ErD&4v!|H_*ig7%*;P-b9TdQ8j zY2>H(L|RO}!wS|P60JMO^IPG&2)BQSVe*?v&0cQOpo^)zRraQOB$tQAqD6M$6VJbJj}>VA zCCu;k(cZyf`;GbP!L#l6?Y?t2i8VKp8=#teI!rAE3xwFmL7Jt&u4dz%y}>$50c7e` z8xpS*X~OWm>O70mNz%Zi!lNK&QQrbQf`WlWpdb8I$)7;|lpwS3?D+d-?(@X@dj2XV)dAXT5Bve+B))725_lHWiQFpl`L~1bN?UEK&|p1mZ!0WH0}B zhJFN-hF>;+wJ0Tb+2ed9E}| ztJ!NSYks5B>v~XvW=bB5zkY{mX!26V+zKc?gYlC6aL>w4^uz;_7PAiUBP_QRX6i9` zsMY~ziYL18+O_^;KHmiqvbVo#hB0-wTdtk{!>4xXoIrd~_qFZ|XwLJ6zjT6QTey&- zR`GML%_PC0Y@YA*86@nK?%9wKErf%wq>0D9 z>)ww%33keibB)QNQao48w2L88F0-dH@L(D=K(HAJCf0AxE%@kAet&m={=LSqT~oDZ z5NsO(y@)dl$7RuC%_b#Bk<|uu2=;@Vll~cgA;4!y{ z>;yBR!8<&j=mpbfNRON?B(+84MTJOD#TK{`pbsLxi z$y6-z?d(wH>5-+YHzAvjz9ymUD%n52oYOCdm!H$>mpK{?_vwIcjo-rJ(Wl_$||PcDK3&mxDMnXuhEtky1mgs zF=7tJNF_2yH~L@0Ez`e|n18|?1`v$;ML3B8hs5ipn5lrXu$65&=9Wdw(x<(1idEm} zb0KG{rHy9jiJGD?LB!)0;L=N}rz>g5$9}I?s*kL#Ec!eCSSUw+Z&bye62)HitsT`{(&nbh&*p4mNf9@*fH zr(t`td0}^7y@qSlJ@~C-6F+aCLo2_9_i2027BQLXi0|r$%k$Om0)HVq$Ee;Q&VCDW za#GU0A`-jE)TxxtNP@0Ye!FM3?GC(&MXvL_?%Blc{d5jVe1iHF@iFDZ`EQzf=f{oP zU(vSNosxS7g>s_o%@^QR?<##hp7X4mS^#yzYP+#j{K-K}>KSf(Ry7^`?_9M@)wQUf zC&k2$Wd{8a3nkJx3C2nU5#ZN1-`FKI9;9+N=C%igt)c%MPWz zkM?4@7);G$6v8nzLtgADC!U8MyNbW09{BOvbs;>ev_C6pOqI%zGt9h{LBDG1SrqBB zAm-UC3^$dqq%XVTBL6r8t&fD>@zpH|deOsdS<>os)~d?y*^dA7=FecnMCIY5u5`Z- z9P1j33dIRQ?1Ql6vA*N=s$%rCxIUdK3FIYMkS^dkFV#E4;SmMc>eKT1Ts`B1bdj)o zdIs&sR6l)@JAb`NY3kZVj1I2dX?nWv;KSi*TeFMV1?_N5FO=I$$BJ$*jGdH_#ZKip zrWIGDdd3sSDmz|L%-;FG?SIQn67xy<^Wog(q1xq|JG1lIyf3D>hc=2UUc}^k*??ep zgomm_rx#eUss4Ll|L#!7v*lm=eKuTu&$i+pPI!;ye$;O4$-nZ1%PL@@HzJ$$5CM^n z2EZ>OS>WlU3)S`wi!S;wPlLPSk8|N}vcCO%fk&`mo zFr=l=ftGIeX*|aDM(C!%hcUuv(zr0YPtW8}5XFP+9dx*FUGvK17ZBska*_^uuO!ZE8i%Q`R_(ig7FiyYwD*Q; zp<{1JPjhCk=yS(YVKzJu{~3ar7-t#18sRHAsw8Ge1>p=cf|WiETazHWp`DOtX&j%R zPv;G87bbXuxm>h#>)#JL(fts@Lx-Du-x>PQlbT{6Sm72}{ghl+cJA_Eh+;}$N+4*8 zu6WTSU9TMFERT!Re%|JGtI9F!3OpZ?y^iIPzcVP9tSpT&3OVJX9Q%y{zc%^9Mp^^J zmbDG5F4vG6rxJbNmLjzv0# z`hQh{1rQ~9ZSitP5Jdi|eHqt(-&DxXv8f&0` zfO1}>8G@Pk5SDjbp4LU-EZ0(Y)P{bTKfo10!qhP-GHWqu@F9D!m+eZhjyEUm<9vE4G6tU{0B^IHPTqU%vb2c<0FFAaJu}(YLx&ouAUG5$_1{S zVdM2Q~aaMtK-|HmWgE?b?esAAs`}De*s^1>;ZUXD+D3*7GwuVTqr;B&aw73!M zilB}^EI|LfeZ>_YOU>lglBe+&NCqVkoX+qKbv6UNV-2?bS=;S&PiF8&_5BSM%zfUR zL}&#{)LtSfTmJ|a`wr}LIMc1*@r%6;f% zc)$0f9@Nvw2f?R!VZM7BNB1m~&Hn|I$OE8xxZmEf(s+sOgzM3oUkAMGL&hPoaIW}e z8F0BRM?z|};r3J&ttk(iLWUFO#}a`j{757J@@O=XdW%%U>=G>iN@thce}RVNvaljv zcqx+XtqkDboS_Bs4Gqqkz`*v$BM8OQBloQ zAd;PxYd~B29Y{vhjGbBeGIXod{bOOvDpUC}Yt;hjqBhso3b6SGY{_0j75wM#$qv)y zj3Fx3g6hLB3}vwzOR#M-;3k3<5#Nww++^z1miEZOZli_Qde0{i(Z7!|JK=$Ep)(xK zzGX@k8Qo;B@N5BcqOwzfmWthha1_F#S1M3Wm-%=_O5Q3;qb5arn^Kkp93jn-E*Zuz zUn{hxEy;)HZ%AXH4LEJxHT`FIfn1ft!S@B+wT~Sy_a!Xcz~7Rr2#Ngw)tC|p5=V%v zQN@El8p;R8_B#Dk57DD@BDlt?f}z`kXHDzBL{Z5-_dk=!K3;ZKh7-b_&HA^wa)GFFxs7Z}6^HIfq3#GJbNgahR9>c~4r6IK?r!ln2K^xyWA48HS9elN_kSUNA5REh zO4cbQ%z0sQvG&PW%%(FrjfM_N`l(*>Z8bNR zXn&IQvjG$0|K@tF93->1C@BanzHO)S2nAY5_QlkNzv zv%uFB1TkgjTyBfpGxONj!Ryn0w5f7d4Y~uR=zz@84V5|R4drhjNqz$U&b=zR$7wvM z?P;0g7Az?Vdx`8nIxu?hA^AC3oJBP|LM75v&5#>>KbpAB6kw+xTA3#zGFGP<=|w__ zwuo;v*xTa@yZKtcC$uiZsXx!x-$SD@UHQ9<>uJV0ZH83g*N~pO9A0R59v2ZG*Gm3+ zZNx1;5v2BxdMsS*7h*UH;kY#zFU4Plluvs0$A0J@$Er8u#051)0rM*~h|}KO$F?HM$zzwq`q`=aWWgA0DSFI2;_uGOrKM*f z&cQ)o7+?xS^fv&tC~|sR;hcNZR`uhwOyJDI88^pVhw{ltW_aUpXCs3v-|QJ$!KxJE zbRz>jOb4Ds(!MisvzbL`J%V9&WkT6Ug>pz&kX3I&u&z7CRCrQi&LBvD%m9yeLKeBFSfaAwl+U^%nba@KkS)q!hoD(oxSC9|IXs9xl4pX>C0RzzTFtdy! zrD9LO^nkeb-{mGYgmucMlN&T2SJ4I17jAA@Za2kMMbw69cA!aiVp#e%9ChucZ>1oQnb7C@bfdTjxvot*ID(N+-z@d(nA?$X}ohU2GF_z&6g zya@^pZ=L>oU%TEH5_U^OCUj%w_w0N1b0wF;abblUPd++4n}j7(O>9!hmR~}6qjfMY z%pSrPxp07DjX;0-x9uEeEt49vj+%O6gfX)B_L6656udMvRCf~?#|zBj?SAi?UCYry z_(Pf`Y&w2_%zZcEl9mMsZo6@HM)Dmzbxtp~F-ts-f9HG$?M zQYXeRc_VSBN3hDd;RG8PD_s^Is$))w%y=YX?4P}eEfz%vJjBV62bNP03R~}NFnv>u zi*K_a&RYxTqn63H8?v|oM$ugrFMHTow*`XZi@f&|+sXbGgoqGd0^NybUJw(h?fZ;xWFp4%&iYD7x? z9b@E_N=!}%6+2AYX3}NQ6Ali*)s@+mJNm@>ZBmP`5mIwpO|VQ zR_R}r4k4Zxc|w~-nLCoF7w)*GS?uXipnotj_x*(g$bOhg+lvNn`LJ0D)C-!#{CfPV zIKZOy{!h0?WHVxy_{*q1my2M0Go57o?2)K%N<~t&kh*Evb>Z^cZzwBJZ9zUcyc2Z<1n_xJ8~4<0(3`^bm)m|N;n zAV*^T`3%xI+ng0nj{D+%eJ}(EmI$1Ya3Bf_2nv))>oCCIKEXT|DY~{&L|V`maalZe z{=xb48|C!RH)xMI6ds9&LgLL{C_5TNJ0hJe9!R-uz!xpeW{NKOU2K%Q zh>X=L6BSpcG|GKX%4ww{$BSv6uzOH$L)rD;9+cFbPzz4>cpGXUNQPQ7ca4N};%ANArHMDm9So5g$a7#_%GJ-kStu zm7Ohi9qp_o%x?a>8RxnDKvBYgw-|T=X_#RY`u@BnCSYG5 zCf?m|zyG1f;d!s%F@){%n%vZ8F$)B+@(oN0gRY(?1WkCz9f?B@STEuVUb2U4H9~Yu zkO7{q+RF@E%TQh?;qRn%STxoRrp5ZI+$?tdwzAQVsDqOQDLLX)CLsP?abv@Y zCGONId?Y2Zy24Ho>(fscsY}bHJvO_>6S_4h;=Od@t)22d%{7SFiEat?%qB+Gug)KN zP<#hX;D81CCeqYq&a582hfJ$i^EaVds>g3cX(%Et+Iybi&F+&yF)8mvEaS4@cy>;^ zXuQKO7Y&A=9v$wM3i~LdGAV}mLn;S&86Hd?sunIqCa?9&twrD4KuzkPD+35uB^PtK z$>bLn*4WTwIHx`x+8^TwA>wjF-5z#F^@h785E;roA~+RH3P|fR>f63S)B8@1l--S( z%Vj|$WLEsN_(|0+LJA_?rVB^~smri4u0c?GR>NX+ZCdBc!oGmvjM2lGi|*|l8I`)!cJ z6-G>j`S#^L^3Ml&KUe}ZIg?oVd+U1m{<6f+$?WSB3NZCML%EN} zkD6C(Vm~g@IcXXBpa)ifX0j4SFxd9lXEAE28o z(V<^DJvqTnGh)4D`$lF$RT0?}kpE5(K5&i!9d2b@=+`w#BA92g-OQ{wTO6poEqqO2Ohod_;qy{yo5ZQY9myJIBPqx<}1H}uQ;FQfzpz zV!5OuotmdeTXwwXb_!DFzHSZRqWtd~d6J)1_awP&b?mpVM5tb)-CG=-;d3MPkA0Hq zgs1SxkhyV&8F~dW_hg}rr)C}#-8zhOk*xchUAeg*M~6?oH+>(j7xFvzTG8wP+-Gl2 z++0ZZoW!A}AgD__ElHEQi=0KCMOrgNlXpxFS(ceW7?|$`ZeR6`q?@J+MW++6mag&3 zcRtNon(EYxQb`8YvB1ix3#fDT*lv?Pj_if1ZE4cV+n6=m{6oWoAN~IWAn>rtvlFFn z73%+dWU8NH!aLbbzcMZ$4P=+U*inSqvinOeB25vb#RnI*34%h8Vb?2$lDepA@^le%OYblH85m-?F<# zYo@c`v(4n$s*p+K3DQw=zN_6y4Zg{V{mUNcl<}&!vF!q8 zbT9__27-^@`Bq`e;vcxHo*|5!3J_WQv(E>9?}H!RTSs|mg%O7*ejt5>0{ss0N0N1E zNiVw)CE$)wW$_X6EGm6LRpis8$C%i**3{gS$Yn-LI-wZhGD;A+>^a-#a$b?1i-wRI zKT!T;o^UPEg4R2kP8Gp!!47&fJ%mcA)EB-S#hxNq0IcdPpFVt57$&$s(q97ohW^O# zVT)`!fZ_jEY|u4Pz{E?VqT4G@he=WBad&h=8As_GG5G8DY@_E6_$fL0dOm7{1u|`( z1BkG{jnfr66!Z>wVpec=Z~V2c?kZ1iK!n!QAw~zPQRC-6cj5`YUV?c-MR>U|jL$tp zqHmm>%Q_l(V5}>2%ifX32Cg7>ccxr4J+jt)7m=0hSF z*E*A`ZOh@&{Ymea3{5Rp%r?x^j3PWX_f0H%XNbh2K&U+7d&FO@1eORaa_8uhO<nT2{ee8_7eQ?@$KuD-Ifv0^M+T)qEE}mBBrU;0>+6wqKuq<#TXxb+RNX6dQp~XWw{rup5mANkf&Gw{j01VDy`akeK*e2xpLb5orn56DfkuI zqImp%UWl-7V1{V1&(|3jr77RvyXg-u7!?R5qlNj-x9M^GJ^Wn*YAx3`bahWkbJti# z5ZR06AFEi#5Ixg_L~2jO^e8YgCSgOy_}wf?Jz|Ir9}w`!CLAJKB?hOJlTfr(KgCiy zhQuY>A{UYbaMf{E=Uw%EG`2a@Imt4CQ3Sw;HR!)}AKtretnVgGw4^SdUYq3!UEol= zON@EdBNXNIZkRGaVfhZ%uD3DdAJoz}fs0U>F^rbjfHS)A+bOhhq79Q!SGM`U%c1nk!Y+vxM_dR%CujefG{H8gFXuxwG^!`-;Yc~ z!qQqefOFHry?1PW3521PqHit{_j~!Qf)g{~;-XJhH`c$I&qbmSklebtci7MtH@lGI zmS7R5F%`{iRflSnJw<0`hILadZ7SaDrt16G)Esd*-2Za_A^tO6C)Uo|p`LTUV|pgt zijeDcciI{`hZJKzMn1RsVR!{5P0my%%=28TYT2C$TFaPIb`=GlJ^O7gctX<&WvPis z;Bx!BdG;GL)C>R}tXyaoRh~Q97&9foM2OXw|GbF5H}`j_Y2#;B?(0-qo$5q@O}FLo z;4nD#(Yw^5M`QM8B4vS5GqP;F&LU6ybS0Z3PY3y}uCLHW2X$$#aRM-JQaW8p(bsH} zFRP}-JGv}p9m~x;)o$NZa)Z8lLQb!D^>|fB5CEyc;S<(G^_kRlR*8hL{*2w2C7kXeo360kYhD}05PO}i)`Jd2W82i&L0P9yar_;fWMx;E?0JP0MS;VmA86}%l1%WZ zyxOry{H+Mz(Jx&^li&N@z9umzT0>v>t(LJfk;#rLC;(F? zYpX$Sj%Ek!-6M#pkZ%g~OKh~aHV?KFBRzS6Na!}PCloF4kkrd(U6Vk|xLp=4-mz9I zR^y?PJX+EP@sP0&>rU#))97|b?#Ajk8qk_${v>{edtv&+m1-`kKaO^q{~L)8McyjU zPa#Ke(n8HMk7?*7bfcH(%@0CPNs}!PKi%Cy%*z`fJ8slPBREG$C1bUxR;@f?<1otS z4tiib$;f%yD7rq-LnA^t!84wfC2G|VYI5%+k16r>-bc6IhqNj%?%zKhSVwcVN<@R% zS*Ok!RktXBjdC8^MSbz~NR8ln;q*~6U9}iupp}JfCF&D2ktD=ak(6%k<6rx#y%)=l zVQJ}PGypwKRRlTrcP=<~@!X3j7WZ{%uYKku+=7*5(b39#BLaH-FLam$yDjCE*8vk;7XWZkv_UE@JuLbOkVBg==SOcoa!mU#Gzyn)m>KYw;R8{2& zt@zEuX+DAoX5a{)q;^HaZQ3eEUh$NA{LZh(k;_kQJ1O@jWZG)(cQ-eR2FwgsMmu_n zCkU7Q(%oAU+Dz)0NWUJ_oC^;I{^YZ88Z*et-{r94(O)i!fZL9l`ZV%{Th)bXm*k(l zGN2}xBy=v@#^?;Lp7VC)eOmq3me_%)xX)JeC)sdPt+VdmEA^2R9vNI*)QRKdJmNri zNK+}t$&NqU0!OB9y{LL#%fukB*|_ooHOBx|OSc=*pnP%lLqF%Ki*Bi~T7+XRQua7k z2^|%)35ex)V@uQn%_Uxcj>N9>b|l>uP_Ow$|9n8TURczSH&OJL~!CjQ}ET&8QQ?VnbHxM@_a~ zZjAMJyl73Y2jV9mq-~CQoNTu5oiV+7WB<;^dei*mtzsEWy&Lla4KKgu=`&tFyFPpG z#|XAe(5g>LiwtpF#G%&C7$T>sne29^rmxTOd5K^;p`>$!E{zYsDoHIEevdS zr?FbHzI)1?eBrZB6#%D~=r?5zpa?=_5@*YY}vmjrlJ+-|Da$8S! z1Xr38?o#&vLb$hCJ$)>Oc7dD-9VIwzJQO>zw*34GtryP=Xa`0Vv{JaI_HBxeJy^hT z^R=?mcfXI*{$`Zafs4~iqy|%tb3e|l%fm`z*2g9;7hqo%%GV1+BP{a;<=Qn?T_AWg>i@e^8d5y2UrtC~z{JUX!!a0NCK!#akO8Nw&Y4&?q-gRmd)_V=2)yhU#uf+_Odt=IFw6d^0IxRZEJO43{3$|vkf6ILQD8e zniRij=#;M?YED}p_nYZA%LHVO?~rO>)v!$;<&64ZN&7}Z_jM7h#mB*ul(p<~$< zbNI^{`osd<0Un?2bJ>0mo5;{^=FZN&G0%8=D0AeW3qz*2GzZJ)d?ua&YW3pp4X0i62 zXi`seTBm5Wy;~**p75DEchWi-z)jo*0wjq8%R}7%#_qbrm(A?WHSO=y!S(11FeQ*G z7l($YF~>}%k~&E;Q{h646k3V=m4te}lA-S9_Rw%!YA*@SsE^a6J`kI6M{W(C;pIZk ztNe87ZdElp-Z_Ye<|p?=j%5u&?8_R*KFk|&?A@ey<9V@8Av-@6k~gg$`LAm5Pg&>t zE7TYY4{H=bkyE?uxY_KgUPjh;<(g{hxrvO2MXvw$&IHs4P6h*d{8Q0_)OW&~P+WDD z5=;Z85X$n^_C51>c%kyOPY&$@28GmKTlLi7q7PR7u1pRkHN;*ExuwjI8$)ofXO;({K&5%z=Co%Vmn%mp(OKJUhu5Bc0CnRm_#fDon92_c0? zx&T&FoPrwJD0gk%ZizKuuAW6|V2F#KlOJGj8P$>w`;IrB-vT~}G0J2=fDQY6W998;atRj2m6`*zH25w57$cB{p@P(IWIIF`%zlYSv0N;sSt8G+l zbGvx&s({n2c3zRztWHED=&uNl{R{2p2I2aNxDmzm>{~xi=oIMqcvclf%xh6fY=0!j zVg$krcS{pkwuw&ju;!C>eax(t7Jrx}!fA@sDRo(6N>%%=jr7#ihz!*~wf%NNQk?DM z?n{a2Ss;<7{Q-W_>2g3hn-0DG=?@-6gbPQM-YqBf99Nnbx`ZRC@rp-0^tJeb*jqSf zy4~En$``j1ah;^k-ygCel)j-sWXF!eX%5ULRyd=c2%~cK!ZYe+RIFOFKGjUY8ktL+ zFngbHwJ$nnk^eHxjpHYc)?ID9n${#*y6E;DpcJ;M>sw#7D}0T3kf=gE;6#%23seAmX3F z4~)Q{`+2>1=v~@fg5xJwXuyHi`L&-vSNb7>eA4znJHdOP&Iqa47MJL#Tc9k zQh`c*LvXI@{WHix`;*f09Xe^W5dKbvY4UghLpf*^5tv}wjN83vv}I?rYLjZeTI~JDm`V{-j&{)Cr395 zI9A}j5zJ8gJ@7yI?-099QjfEbt2dr;0!i~j=W9vViebQ*qG zfkx|P|F_ouc#h+KvtUTs(b6c&RR-mH-KyWQ+62VJE}D7-o&}k8saQRlh`6CEni&}D zC_(e);iZ(?IANRZ00D#&JamBbFCkz&t?L912 zx9mHbHZZ9lhxm?45H0Ik`mE*XUL_?9@(Se(&()8+ zVW)5G61Co9_YyvK*&X(KpX_A5OXSY8uY$~@6)O(rnZWixhqNKFo*aXUw~w7k0bgYG z`t_fUrFwj=$8OI)W`gu2NcmAAf-=2t28!EB8$XzxR1)g4_no|LjgIP8^LAw7yHUuO zPhHcP9vtn%w9U^?n3t^TZ4chlC^8jvSZCb3JD1u~|lvrk9CpMtd9v zE9w1tboF{tajgUM!JhR8gQg-A@KiE;k7!*;p{d!TXc9X&tT9@8tVozUP244h2H$7t zV-^^*GM83YRrj@ujTxRqG=Uo+H3b@a$SrU6BUg*-o|Oz*P}X-772Rl(S%)QcLr?&12uk!fqi(@OgfI-g>7#hd`eC~WQiK} zlaI+)Vb6CXVgWipLQXxr2dl2FgHNG)7GWaF{pqSU-UGuxfoU}YTkTs#qSZjVIF+^c zv6J_RY$IYH>JgiVxHGE07Zd1BU%4;TRvjcGp4q^UI62Zm1VqeDCQB!VOgeGQY|=gB z%`m<$(zUu*l^sk&5a9%~dt+IaK2_T`Kb?P1jpAo?T(U$$CP9m2c3Rt8DZ=$O0{l0c z2m1OosJNMCa}&Gv2L#EscnKnhbtj>(Nw!)K)CQ)pm^5b{?wC=guJe9=Z$VItEGSQz zhef?(9+sLf4JJmyr#RY090|t!I%nWmA(6(;^oE1OgsBkQ!XqJ7!i8+qF_V_DFS6Y| zb;$3*;bHefH@2}*kyi1P7<$KfN3*Z>{ScbkrhYt+^OCn%u+CL*FHqiFKLW8sUyEvX z-#9K<0Ed39Zt=9TP$h-0Rj`@i^eiI~hSQjYIN~rq0~mF*AMhP!YD}rt_vFdrBjHDs z=&&+c&cPtC!#f>~)<7a!Sbs33m`vguVhClI+ar$S%x!JV~zd|`GZ!0jX~?q``tG9QuVKKOSL=%={*vtjiB@M`%bWV|jM5`Ay)SJWDc z!YbFz)FB_a!ZL{{iNcK`@hgPMZ7~VTHu&M93!^5q>1oA#dkQnyQLf*)?}q3qc(B|_$@}nnzr^wEzj4QS$I>_$R71~;KbFp|H=Ev=g7WHw zK^5z~&Ig3^ou}KzF%(D-h;C0LpRnkZX7ROd@rLhLHTT(8$gHkd`a1h;(ryJ@2F z<}1@{j5Q#;CaFVtdJ!8z)l@$4O-Gx{f#tT}2IjP8fpotC#6Ue{pRlnKk8!F9#GE=+ zSXfw3REs{1heJS^moYV!@|JzB_gX5IlxBE*W_WyfYGJSHlhIH~+{W~s8?{e5LUEo; z3;&HrW`Ee2UEKX^NvS!}Kop3=J9M0ccreR_uLou4alyk4I1NJ5vLk$gi;((IA7(>2 zXukxdg`B&ZXUg$9K1T7U}}2#s>3)F?lsTpgy!)NVG#Htdipmll&|jr8jXy7ox6rTRSj z?0!Yd-RIz60G+0;tnPG5wR}1MU@P&$=C(?1fS*wDM zy6SQPN_@u7R{IB4X{hbi=u6!|F(tgP3j#JNrC6UCM6M71}OR zYYyI*cZQPV3d0dp1ktYv5gK2!%@1q4&MqsYsunva8g$b_i&TV#onWVsyH4`(AV5F9 zq%P=C|Mg>5uK^&(>pnZv1K8}H;9C2O>#$VxfC7O_gk&U-Z*FG76CCr4e>vF%=(UBC z6A4WC7=bLuw?!B_Hf^Dbk=w5@^4s_KvVD;; zKD<`jJw<25Sd2ds7rbGSq3@gqkATceKj;Bk^y7v_1-Wjo=?jB}4+L1SVbv8+?w8Cc zD5jc^YZJyn4{cuntnb^JJlD6@{|zWT)XeCHaIFGJ%n<7Bo$egp7C>g7aJ6JOfXYxi zSPyGhR-FDQ#{4GMpz%|&;Fp_~?4tzxLloW)76a?j{kfX2rbbXFoVO3%e~P z>XB>emfQDFg&1eUMuhDHA14J2{5M5rsuaBn42VwAjG=k^M(5dMJQ11$-NYYjfoS-* z1q{Wu$%azyWF-#9Z#V~-t0gVL^eX&Et%Mu!L$Yf~@E^h1)4#pyGG6p)ckC&sf^>E$ zIv``Y(?YOZR2AaI7y*$y-!pY{$noGlx}!^&j^~h-YXmjn?mC6=Y2Y7;X%5g<>ANb& zcdn#-*#oF=dM|Vh?(^l@_)S%C=)58aD zm=d;r)>6oe4LJ38E00$QvA{TU#9LDPvxol`U902vL-LwgR)Z6-t&Z*=zkalZSyp9q z?5sJZ5gH+LnFC=4$Ffj_1RPwJx z1cIJOA@on#6=?kZVIgz(=+vSz?Ix6K!UCFR8QihpoB&pyPX=WGC=;F8jvf!pFb28) zSFHe>nM`K;V=XXy>igPl>ZU5hHEYOn-=;4Nx(TN2&2h|UP5^4& zaiX5W`I-FyVtU{AGJ@kHM{~dU6k}EV%k&ZI5()|_`>x2v&dHZB`RY?37H|%Jwn#+@ zC#}L1h(xj1=2BWmT=+D9L!*}{`gEM_!mAovpyE^94F>-)fIPmGxdFHI3Zhwwx)iRw#R%H@xyHdh@3t)m1aVL>8>U5)dO5nopm9;bBz&sOuoUODfsZ9j zGE<}D0IRanu0J&m@ZRo1#dl{y>e{h3CSEqZc7mE?4t6O9G*v7xHd(kf2r6yam@u=Q zdJ+K;1M`9h^@m1@zQLhACk;~!ytbNw(x7{WRw<@7r#5|mWB=d^#`AQ6ABR_o0vU@V zlv`4)DU&oIVnA0Tivps0DfEvV`Q|KS-rygpM%8Jc8*9_X1a3*d#Y3|?2%TX*JD3mZOOah~|Hjv4g%QtZ) zUB2cpu1g#zEYvhjtgCOM1ahen-^o)`e;N^S`|y7yFgoyf4<|wt_;jJv_llY1>&04o zDWZ!o3fnD75w?r&!4_=rq$jbK;QSs$JX+*faf9W4~cLMj0z-+lHma3N@;}zv{>ZuGxoyeFP7j<%~{UW1b4%B zl*?RenJG!>>@w0G&w#_`^yHR9R%li+cAK^GL6tf9Z}_L)%5dyvS&Y7Eo$z^}zaM`{ zkWuUOTrePHlu+L?+MATItprayB59QXNbh<=2Ddl}IGLWONi1&qnF8>)6^a)&eX|)( zJ8iSCQ2t-|M*Oe;6dGZr^t1_c`>dzyc9?&BmX-3P$tk|+NcS-#(o*)$;aV*XKh_^Cf|Vf zTU##7c8uinEO#~7P6UW-W8cnk`>MFNL|S%v3{7Hqq)V^!_!qjc=oh}x&$h;t=@XK{ zMMm1zPBtU9m(Tq!zJKM6*ImZ3!Q3XYlJbLh-{R$?`6+7q`?0=@bd^ z&Z-1cuM^x)BHNuyY?HwSt^HkG#cyv7PlLfDHy2`qf z|KqMh8@AR1&wbktH8pi6sK`1oyy`Zzzmn(mvj@22;+P*b6${jtNt4E@poMj-w)XKBgl2o^QKG-oIHdql2I`Y)XDC9lZh>^JGoaMdV_(1v&SR1b`opfz9gq(R2 zAwYq=uabq<$W!@`qMJV7T@0hHOU`wdFYC}Ev#^!z<47=74kMVYVqMK7TI6106DDt+ zsG#hE4fng=m2KJ}wq$1Kk+!xbZ3V>_3OM3^BFb>5j&+oJgd?Ohk%MuuV3Tp;VHeMu32SKvl0Tk<%?R7VFT^;rY|%4ItDX>8do2fyR|>=O5Sf z9h4Ne-Z&MTuc$Bo86Y@jR8Iy($}LjHGLFUv)$7|5G!zkuxOG)1Ejy~Eg_R{$8w{uI zS5?HZn8r)VC9Lkus>PW$4d3wAb^0~2!sMH*L4eId;;>#g+|)DVcb%|of5f@d|GrKBTS{xU$ z{xeKUC+u@yRNK3n@8|W3<7n;5w*&3p@|X;(ei9oo_V_nrxMjUDfBUS6{1p2MjJ@m9 z^x&H`3RAizW@#hdIjbP6sOdjUnuRuql+c>q$x+>Bi`F%a!rp|k!;Qoe^j}W#;`(u^ zX6qU&u&QSC)1*Rvpp$noe&mA{krlBqUXB4hdny2z~w56wIE;^H;&O6HyDHC zP2GIl>w1^jjct!3;oY(CL~GQd{4vm<-hP|=Ymctvf;LrttOm@xx3XB&ooQ$%Q!btO z_XGO5nW*itDr8VC5Pvt<6#87~-H0QN>)Q*5F5a^5B} z(Cmcurww-_ef=`Mvz%?iCqN7NTT2)d2GzAvxNchoGN&cP<85U+V1rmcXQr4LduFT& z7TvUWeej(v*^AI&+5DNs?Z3^g_k1hF5c@H?H6EITEs$nP`9%pJ;3?CKKi?fU4i}ZS z*Hx>Wf;x}u!=1e6JyF+9&$(&O#E`L4l1{X^K1;JhcpuhfUHa>p%|IF&2CPkda*{6!?3=HX~Ynwt>V(v8dHpxsJ`iq_+ zq*21J(m%Jh3m@&daWAlp8RO?)#6=>b;i8C0gkJZDbTUUvc>aq` zbyLG{Ws?EOZno^4R31IpkLx`NTr^Bq5#HfM#C-{`-Q8WyzPTlRquDO%=#q-Qo4K$k z7HKBZq|V>bH(meuRn!RlWz8h{dV39F4rXil;Tqk$5`3ErlG*;hbMsL{Lqno);!E;Z zigNETua)A7J^CTek_ozatb^4z$)%`(D)c6Ab4DGpzZXH_Dp^6tomUq>^94~*Jm)`; ze!#jr`MwkdT~&Db`5AKXQ+Vu!dt4Jh`JAivdNy;EV%)&NuM;9_=omGaJ`aqcQk*;* zbR%fXD15g9^o*3%Aw!y@U7aEj_unnGKB8i`$WuB|R)?2q*jWiyX9ZfA)E#ztQ|7;Q z#asy(2i{o~LOVTnt?`;F#MU;=6KnQ&>hHHV+aI2|y>W&gqtXmuEZA!e{ld2rn-jkj z+ewBcQ*By~5`%d2p{-!&*)I}Lie@zE9lh^My@ZJZ9j%&%IX`9Z5ic~{R2&BaHQzJO z&^UyXK;dpfvT^ z{D$n1weQux_HKsGC?yO3TV%4Ui2?J`X3-gO1cDRSKZW$}-998kS z>VZOZN61syi)k4xqE zZ5jo$V4gd!Lrih&M;t}u5CrO1@Bn%p3H>`sJcnox1o}oGnQxHBjK_kFY-w?+uX3*7 z8U+^@02)^~W1zxPxwwnfby$0L@!@l8HD+bwV$_sVaa&IpH_kIB ziCrIO_xqfuT%0h#C~Reh3)vXw=fwVS5Z0nLt0fu_Zd^dWY2LjDQu~$i5^7JDSNe&t z|8OB#Ef)p~B(3ks!5LK`|C&*6NC`5ZqRn_?%?oyFELq&z9#Nglnog#JLadW;Jg{m_ z5p&mBdUmJ*_f4Bl*4tA$9v(%a>^aqaPhAvWU8%65GlOm`dmbW`tvSo66z{Pn4s4Zi z+24*UQ|(srsbs3TP)1xiXs5E`%&Tj?^(YYqw4{BMI2KK@<|Z-DP?kIeUS8vqofgo` zlaQa7F4d9l{vggDf%Mz4n7-*3;II2P;N=#S5iJ0z-|{;Bx`yyUwwnq>x317~?EInT zyoLsN+%lco#6}?Mq2z0*Y}Kf(;8ldglRx$|3cKs(>(sEnd-XxJfxlZos)R$q8HIB^ zKbR9!NdmnC{Tq3O5>QOl7399B+4A4b4@R}j_$^|{QqQ*h36N93-_XEqWKr!qTXp$5 z4+U9Ad(`?AA+f z`}n`oEiI?J&#Tz*Dv?Nuk7xP$-WJdn-4--m#Gos5ENqiqtmJ9Oim;#W?H&{~6lTtB79h5oU>DH{X8lksaVaU9x zuFb<`omtd?ZL@h8$Xn{GT@D*RhOIy|d&u*;`IpK96f{rw9q`8%qq{P3&zir@EO_PI z`t9gUdEBwJX(X|kkpUp@(`P1wW(IIY%ipn0elAPzTq}T2j2nCtjmFyxPBS6EGR#Z3 zL~%oB^?|1E-$9)_`9b(_!VZ2b6nAFpe293f#yqFuf5>N2-4UKts{MY%_(c=yp4md` z7OA?)+NJY(kSAu|4ai_r=f-s1Nf%g!j)<@*C@k1@=Q27{BLocxl$j)F37Kt*Elr`5 z`HEJFT$yKbvPGC0+-DYJ3yPxCP?lfPru~-HJJt~l47221rv)l4a+H6LqR4iFTms%+ z{>+~uS-zAQFh9Cy$`XYM(R^R|_sch3!K^ICXpyJ~e=#O;PSgfWs-a}vRXq9fUGQF% zC4<#Q&#TIH%&Vruy;`647R}rZr+4Iy*qX~u(TQwUih|;eD-TS8m(ry5V$F7Z{S3gU z3VDID1-Y$F*qyaFT30D4{X_4$*T0U95Z(!MNA|EI*EIU_@jg_|s>9=vZ#ihSr!358 zjspdTaF4X>=mhL^5+2oub-j_f-E4#LHKY6V2vB7%AX6rC`uw}!bdBxOotloS7OQ=E zNE&O+F!6_GuVnSUUM%f02APSdCKtC9YA4RA707(}H0PV^|HmizB2a1yy5$y=b6|E9 zqFQ+OMGnX5qsuV9h$Tg3l^RUh#4SAl-rpU0ip}lUIUsl-dyheBXb(Yap*JQQkFq(e z5t3=%b4fUf1l)RTQ^W}{xC z&lrD=MWLsILy&;6E0KkiI~jmI_>!kmIak&306Q2X28En^u- z_0I(l5}6Y{LCgRE#zz_@DI}J*$1nY-|1gI{;$>! zCbpvoa)JEZpjkR5tiTL$AK|w;8tcEoFqt9Kly3d<=pon(+3F5|$;@cK|7E)XU3~Wt zVi|=dZxpe*Z4c)K#lmh_ZW|Gj$@gZ79bL%J?Ept=e zkD&t0oWMP;Wg=$X;9eQP4JhPfGgh|2GX`tdXQ`ZwE}u}Vmz3GuU7_E5)yjCE)Mu_B zV3*rbT!YSo6j%^~u4Jq2JTp5Dp7jdtBMLWK$)A+~7Y44;#I83lYxie5#G7}eBBt6i z(ay>nE5cF`6%xYrBzwv=$>+g5J`-BEYl-Hn( z&#g{-zQ6iXZwD6QgUSsnq`X2mNhnV_9yUe%efHDnTW$ZPEL@63{zB~g2eYu~i|wnt zaCfBS&r6|;y94bHD$l&>&d^B~KV8Vq7J;P-k{c>T6ub{ii% zFF&`*s4i)7;BSrt@-~5hMt*mYZ8uh#%?a>A7F@^%9h=3_9Gn_BOcVE=M+yC;G-ZO_ zH~M~HjUz>+m5@j7<0HB1{`lJJgeVu+AJENEg8UO#f>>|rvS}+ilT!b@ zYCS2?bzf~S4p`TTRT&xjE?CO*jyLA9Bw?`c_o0}jB2TW-0WgoKl zC-K!v8J4y$o@KUwzF)FWYvb|d#OLh9O#MURRrHKH?`+OVA!LVS-&xYyi|Q@t#YrCI z{dtRdtL@`xPr?lo<%Nef+-@7m{Ilr3EwxRQ!)>XTF=6fX#8EnG%1;kDu{ zW{9m~NeQ0J32_5`be#O$v>f$Ym1E5^3WNtDBwus#9Y{aCyWw%HI19r7xAtR;hyT~2STxP#Ni;0SXxWcYH(CbYvms!dbt^5U~-}hgL%*S zz~=Q`c~#flA~ySGbXiv3vsnH6F~8lX5)eA^G>i1xUMvj4rn@1YCiBR|Q+knE1<1{K z&4=#L)^Xz2Uik`#jw7*$2-zj+SW9C7pz>%o%2*c5P?z?z)NsnfAUj8u=&AgxZDD@K=iG66)=k?zP)S^> zHTp8Nq{%++agHUuoKT^OJ2F36S>g%wJhiIS%CUh2fd6v46tol>MKX}Z-+B&;rgPKvEhmP>2%QbvO*Uc8a{Hec2L za^EU#Uu}Oh^FcJ(HhT*62w6UKh4vu&Ncn^5w~MXTK~%81sqlT|r>9fw=dx&2&9$9O z3Bp-NcMLaY7CKbxVO5?4VSDNpBj$JXI-O3DdE&iUa3Mm|>PLimwU7v!xp3jmVDnWO zt6LED8xaNp`T<>R|2yoyzKLFqj$P#c0sOk1-~a#s literal 0 HcmV?d00001 diff --git a/samples/Sentry.Samples.Maui/Resources/Images/dotnet_bot.svg b/samples/Sentry.Samples.Maui/Resources/Images/dotnet_bot.svg deleted file mode 100644 index abfaff26a8..0000000000 --- a/samples/Sentry.Samples.Maui/Resources/Images/dotnet_bot.svg +++ /dev/null @@ -1,93 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/samples/Sentry.Samples.Maui/Resources/Styles/Colors.xaml b/samples/Sentry.Samples.Maui/Resources/Styles/Colors.xaml index 3cae0efe5c..47ba06c10c 100644 --- a/samples/Sentry.Samples.Maui/Resources/Styles/Colors.xaml +++ b/samples/Sentry.Samples.Maui/Resources/Styles/Colors.xaml @@ -4,11 +4,21 @@ xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"> + + #512BD4 + #ac99ea + #242424 #DFD8F7 + #9880e5 #2B0B98 + White Black + #D600AA + #190649 + #1f1f1f + #E1E1E1 #C8C8C8 #ACACAC @@ -17,11 +27,13 @@ #404040 #212121 #141414 + + diff --git a/samples/Sentry.Samples.Maui/Resources/Styles/Styles.xaml b/samples/Sentry.Samples.Maui/Resources/Styles/Styles.xaml index da5ceaf5aa..f30c96f297 100644 --- a/samples/Sentry.Samples.Maui/Resources/Styles/Styles.xaml +++ b/samples/Sentry.Samples.Maui/Resources/Styles/Styles.xaml @@ -357,7 +357,7 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/Sentry.MauiTrimTest/Sentry.MauiTrimTest.csproj b/test/Sentry.MauiTrimTest/Sentry.MauiTrimTest.csproj new file mode 100644 index 0000000000..510b462757 --- /dev/null +++ b/test/Sentry.MauiTrimTest/Sentry.MauiTrimTest.csproj @@ -0,0 +1,92 @@ + + + + net9.0-android35.0;net9.0-ios18.0 + $(TargetFrameworks);net9.0-windows10.0.19041.0 + + + + + + + Exe + Sentry.MauiTrimTest + true + true + enable + enable + + + Sentry.MauiTrimTest + + + com.companyname.sentry.mauitrimtest + + + 1.0 + 1 + + + None + + 15.0 + 15.0 + 21.0 + 10.0.17763.0 + 10.0.17763.0 + 6.5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + true + + false + + + + + true + + IL3050;IL3053 + + + + + + + + + + + + diff --git a/test/Sentry.OpenTelemetry.Tests/Sentry.OpenTelemetry.Tests.csproj b/test/Sentry.OpenTelemetry.Tests/Sentry.OpenTelemetry.Tests.csproj index 823005c757..4752071589 100644 --- a/test/Sentry.OpenTelemetry.Tests/Sentry.OpenTelemetry.Tests.csproj +++ b/test/Sentry.OpenTelemetry.Tests/Sentry.OpenTelemetry.Tests.csproj @@ -5,8 +5,8 @@ - - + + diff --git a/test/Sentry.TrimTest/Directory.Build.props b/test/Sentry.TrimTest/Directory.Build.props new file mode 100644 index 0000000000..bbb7413d29 --- /dev/null +++ b/test/Sentry.TrimTest/Directory.Build.props @@ -0,0 +1,2 @@ + + diff --git a/test/Sentry.TrimTest/Directory.Build.targets b/test/Sentry.TrimTest/Directory.Build.targets new file mode 100644 index 0000000000..bbb7413d29 --- /dev/null +++ b/test/Sentry.TrimTest/Directory.Build.targets @@ -0,0 +1,2 @@ + + diff --git a/test/Sentry.TrimTest/Program.cs b/test/Sentry.TrimTest/Program.cs new file mode 100644 index 0000000000..39946ea18e --- /dev/null +++ b/test/Sentry.TrimTest/Program.cs @@ -0,0 +1,2 @@ +// See https://aka.ms/new-console-template for more information +Console.WriteLine("Hello, World!"); diff --git a/test/Sentry.TrimTest/README.md b/test/Sentry.TrimTest/README.md new file mode 100644 index 0000000000..865f258928 --- /dev/null +++ b/test/Sentry.TrimTest/README.md @@ -0,0 +1,16 @@ +# Sentry Trimming Test App + +This project is used to ensure Sentry packages are trim compatible when targeting non-mobile targets. + +This project does not contain any unit or integration tests. However, if we are able to successfully publish it without +warning/error then Sentry should be trim compatible. + +To test this we need to publish the app by running `dotnet publish -c Release -r `. For example, on macOS: + +```bash +dotnet publish -c Release -r oxs-arm64 +``` + +See: +- [Show all warnings with test app](https://learn.microsoft.com/en-us/dotnet/core/deploying/trimming/prepare-libraries-for-trimming#show-all-warnings-with-test-app) +- [.NET RID Catalog](https://learn.microsoft.com/en-us/dotnet/core/rid-catalog) diff --git a/test/Sentry.TrimTest/Sentry.TrimTest.csproj b/test/Sentry.TrimTest/Sentry.TrimTest.csproj new file mode 100644 index 0000000000..5b671fa03e --- /dev/null +++ b/test/Sentry.TrimTest/Sentry.TrimTest.csproj @@ -0,0 +1,52 @@ + + + + Exe + net9.0 + enable + enable + + + + true + true + + false + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From e8fc892fe9e789a80ee3a473a46fd0c459e36b12 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Thu, 19 Dec 2024 13:44:19 +1300 Subject: [PATCH 074/363] Prepare 5.0.0 release (#3856) --- CHANGELOG.md | 60 +++++++------------ Directory.Build.props | 3 + src/Sentry/Debouncer.cs | 7 +++ src/Sentry/HeapDumpTriggers.cs | 7 +++ src/Sentry/Infrastructure/DiagnosticId.cs | 11 ++++ src/Sentry/Internal/HeapDumpOptions.cs | 4 ++ src/Sentry/SentryOptions.cs | 14 +++++ ...piApprovalTests.Run.DotNet8_0.verified.txt | 4 ++ ...piApprovalTests.Run.DotNet9_0.verified.txt | 4 ++ .../ApiApprovalTests.Run.Net4_8.verified.txt | 8 --- test/Sentry.Tests/DebouncerTests.cs | 4 ++ 11 files changed, 78 insertions(+), 48 deletions(-) create mode 100644 src/Sentry/Infrastructure/DiagnosticId.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index d5a51b0211..86962d0aa6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,66 +5,46 @@ ### API Changes - Removed net6.0 and net7.0 TFMs as Microsoft has stopped supporting both of these now. If you need to target net6.0 or net7.0 then we recommend using version 4.x of the .NET SDK for Sentry. ([#3807](https://github.com/getsentry/sentry-dotnet/pull/3807)) +- Sentry's Experimental Metrics feature has been deprecated and removed from the SDK. ([#3718](https://github.com/getsentry/sentry-dotnet/pull/3718)) +- `SentryOptions.EnableTracing` has been removed. Instead, tracing should be enabled or disabled by setting the `SentryOptions.TracesSampleRate` or by using `SentryOptions.TracesSampler` to configure a sampling function ([#3569](https://github.com/getsentry/sentry-dotnet/pull/3569)) - Temporarily removed experimental Session Replay support ([#3827](https://github.com/getsentry/sentry-dotnet/pull/3827)) - -### Fixes -- Fixed JNI Error when accessing Android device data from multiple threads ([#3802](https://github.com/getsentry/sentry-dotnet/pull/3802)) -- Android - fix bug that prevents logcat.log from getting attached to unhandled events (SIGSEGV Segfault) ([#3694](https://github.com/getsentry/sentry-dotnet/pull/3694)) -- Fix "System.ArgumentOutOfRangeException: Specified argument was out of the range of valid values. (Parameter 'idData')" error propagating OpenTelemetry span ids ([#3850](https://github.com/getsentry/sentry-dotnet/pull/3850)) -- Address Trim warnings so that MAUI applications can be compiled AOT ([#3841](https://github.com/getsentry/sentry-dotnet/pull/3841)) - -### Dependencies - -- Bump CLI from v2.39.0 to v2.39.1 ([#3799](https://github.com/getsentry/sentry-dotnet/pull/3799)) - - [changelog](https://github.com/getsentry/sentry-cli/blob/master/CHANGELOG.md#2391) - - [diff](https://github.com/getsentry/sentry-cli/compare/2.39.0...2.39.1) -- Bump Java SDK from v7.18.0 to v7.19.0 ([#3805](https://github.com/getsentry/sentry-dotnet/pull/3805), [#3844](https://github.com/getsentry/sentry-dotnet/pull/3844)) - - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#7190) - - [diff](https://github.com/getsentry/sentry-java/compare/7.18.0...7.19.0) -- Bump Native SDK from v0.7.15 to v0.7.16 ([#3825](https://github.com/getsentry/sentry-dotnet/pull/3825)) - - [changelog](https://github.com/getsentry/sentry-native/blob/master/CHANGELOG.md#0716) - - [diff](https://github.com/getsentry/sentry-native/compare/0.7.15...0.7.16) - -## 5.0.0-alpha.1 - -### API Changes - - You should no longer pass `AndroidContext` as an argument to `SentrySdk.Init` ([#3562](https://github.com/getsentry/sentry-dotnet/pull/3562)) - The `SentryUser.Segment` property has been deprecated. Consider sending this as a tag or additional data instead ([#3563](https://github.com/getsentry/sentry-dotnet/pull/3563)) - The ITraceContext now includes an [Origin](https://develop.sentry.dev/sdk/telemetry/traces/trace-origin/), which is set automatically and is primarily used internally by the Sentry server ([#3564](https://github.com/getsentry/sentry-dotnet/pull/3564)) - `Device.BatteryLevel` and `Device.ProcessorFrequency` are now stored as floats rather than ints, to align with the Cocoa and Java SDKs ([#3567](https://github.com/getsentry/sentry-dotnet/pull/3567)) -- `SentryOptions.EnableTracing` has been removed. Instead, tracing should be enabled or disabled by setting the `SentryOptions.TracesSampleRate` or by using `SentryOptions.TracesSampler` to configure a sampling function ([#3569](https://github.com/getsentry/sentry-dotnet/pull/3569)) - The `FailedRequestTargets`, `TagFilters` and `TracePropagationTargets` options have all been changed from `SubstringOrRegexPattern` to `IList` ([#3566](https://github.com/getsentry/sentry-dotnet/pull/3566)) - `Scope.Transaction` is now always stored as an `AsyncLocal` also in [Global Mode](https://docs.sentry.io/platforms/dotnet/configuration/options/#is-global-mode-enabled), to prevent auto-instrumented spans from the UI ending up parented to transactions from a background task (or vice versa). ([#3596](https://github.com/getsentry/sentry-dotnet/pull/3596)) -- Heap dumps can be captured automatically when memory usage exceeds a configurable threshold ([#3667](https://github.com/getsentry/sentry-dotnet/pull/3667)) -- Sentry's Experimental Metrics feature has been deprecated and removed from the SDK. ([#3718](https://github.com/getsentry/sentry-dotnet/pull/3718)) ### Features -- Added support for `.NET 9` (preview) ([#3699](https://github.com/getsentry/sentry-dotnet/pull/3699)) +- Added support for `.NET 9` ([#3699](https://github.com/getsentry/sentry-dotnet/pull/3699)) +- Heap dumps can be captured automatically when memory usage exceeds a configurable threshold. Note that this API is still experimental and may change based on user feedback. ([#3667](https://github.com/getsentry/sentry-dotnet/pull/3667)) - libsentrysupplemental.so now supports 16 KB page sizes on Android ([#3723](https://github.com/getsentry/sentry-dotnet/pull/3723)) - Added `SentryOptions` extension for profiling: `options.AddProfilingIntegration()` ([#3660](https://github.com/getsentry/sentry-dotnet/pull/3660)) ### Fixes - +- Address Trim warnings to enable AOT support, including support for MAUI specifically. ([#3841](https://github.com/getsentry/sentry-dotnet/pull/3841)) +- Fixed JNI Error when accessing Android device data from multiple threads ([#3802](https://github.com/getsentry/sentry-dotnet/pull/3802)) +- Android - fix bug that prevents logcat.log from getting attached to unhandled events (SIGSEGV Segfault) ([#3694](https://github.com/getsentry/sentry-dotnet/pull/3694)) - Fixed ArgumentNullException in FormRequestPayloadExtractor when handling invalid form data on ASP.NET ([#3734](https://github.com/getsentry/sentry-dotnet/pull/3734)) - Fixed NullReferenceException in SentryTraceHeader when parsing null or empty values ([#3757](https://github.com/getsentry/sentry-dotnet/pull/3757)) +- Fix "System.ArgumentOutOfRangeException: Specified argument was out of the range of valid values. (Parameter 'idData')" error propagating OpenTelemetry span ids ([#3850](https://github.com/getsentry/sentry-dotnet/pull/3850)) - ArgumentNullException in FormRequestPayloadExtractor when handling invalid form data on ASP.NET ([#3734](https://github.com/getsentry/sentry-dotnet/pull/3734)) -- Crash when using NLog with FailedRequestStatusCodes options in a Maui app with Trimming enabled ([#3743](https://github.com/getsentry/sentry-dotnet/pull/3743)) +- Fixed crash when using NLog with FailedRequestStatusCodes options in a Maui app with Trimming enabled ([#3743](https://github.com/getsentry/sentry-dotnet/pull/3743)) ### Dependencies +- Bump CLI from v2.38.2 to v2.39.1 ([#3782](https://github.com/getsentry/sentry-dotnet/pull/3782)) ([#3799](https://github.com/getsentry/sentry-dotnet/pull/3799)) + - [changelog](https://github.com/getsentry/sentry-cli/blob/master/CHANGELOG.md#2391) + - [diff](https://github.com/getsentry/sentry-cli/compare/2.38.2...2.39.1) +- Bump Java SDK from v7.16.0 to v7.19.0 ([#3749](https://github.com/getsentry/sentry-dotnet/pull/3749), [#3771](https://github.com/getsentry/sentry-dotnet/pull/3771)) ([#3805](https://github.com/getsentry/sentry-dotnet/pull/3805), [#3844](https://github.com/getsentry/sentry-dotnet/pull/3844)) + - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#7190) + - [diff](https://github.com/getsentry/sentry-java/compare/7.16.0...7.19.0) - Bump Cocoa SDK from v8.36.0 to v8.39.0 ([#3727](https://github.com/getsentry/sentry-dotnet/pull/3727)) - - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#8390) - - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.36.0...8.39.0) -- Bump Java SDK from v7.16.0 to v7.18.0 ([#3749](https://github.com/getsentry/sentry-dotnet/pull/3749), [#3771](https://github.com/getsentry/sentry-dotnet/pull/3771)) - - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#7180) - - [diff](https://github.com/getsentry/sentry-java/compare/7.16.0...7.18.0) -- Bump Native SDK from v0.7.11 to v0.7.15 ([#3731](https://github.com/getsentry/sentry-dotnet/pull/3731), [#3770](https://github.com/getsentry/sentry-dotnet/pull/3770), [#3775](https://github.com/getsentry/sentry-dotnet/pull/3775), [#3779](https://github.com/getsentry/sentry-dotnet/pull/3779)) - - [changelog](https://github.com/getsentry/sentry-native/blob/master/CHANGELOG.md#0715) - - [diff](https://github.com/getsentry/sentry-native/compare/0.7.11...0.7.15) -- Bump CLI from v2.38.2 to v2.39.0 ([#3782](https://github.com/getsentry/sentry-dotnet/pull/3782)) - - [changelog](https://github.com/getsentry/sentry-cli/blob/master/CHANGELOG.md#2390) - - [diff](https://github.com/getsentry/sentry-cli/compare/2.38.2...2.39.0) + - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#8390) + - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.36.0...8.39.0) +- Bump Native SDK from v0.7.11 to v0.7.16 ([#3731](https://github.com/getsentry/sentry-dotnet/pull/3731), [#3770](https://github.com/getsentry/sentry-dotnet/pull/3770), [#3775](https://github.com/getsentry/sentry-dotnet/pull/3775), [#3779](https://github.com/getsentry/sentry-dotnet/pull/3779)) ([#3825](https://github.com/getsentry/sentry-dotnet/pull/3825)) + - [changelog](https://github.com/getsentry/sentry-native/blob/master/CHANGELOG.md#0716) + - [diff](https://github.com/getsentry/sentry-native/compare/0.7.11...0.7.16) ## 4.13.0 diff --git a/Directory.Build.props b/Directory.Build.props index 7fc23dfc16..356a7abdca 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -11,6 +11,9 @@ strict true + + $(NoWarn);SENTRY0001 + $(NoWarn);CS8002 diff --git a/src/Sentry/Debouncer.cs b/src/Sentry/Debouncer.cs index 0bd4e68ae9..80726cfea0 100644 --- a/src/Sentry/Debouncer.cs +++ b/src/Sentry/Debouncer.cs @@ -1,9 +1,14 @@ +using Sentry.Infrastructure; + namespace Sentry; +#if MEMORY_DUMP_SUPPORTED + ///

/// A debouncer that can be used to limit the number of occurrences of an event within a given interval and optionally, /// enforce a minimum cooldown period between events. /// +[Experimental(DiagnosticId.ExperimentalFeature)] public class Debouncer { internal enum DebouncerInterval { Minute, Hour, Day, ApplicationLifetime } @@ -101,3 +106,5 @@ internal bool CanProcess(DateTimeOffset? timestamp = null) return _cooldown is not { } cooldown || _lastEvent + cooldown <= eventTime; } } + +#endif diff --git a/src/Sentry/HeapDumpTriggers.cs b/src/Sentry/HeapDumpTriggers.cs index 411270b6ef..211d974112 100644 --- a/src/Sentry/HeapDumpTriggers.cs +++ b/src/Sentry/HeapDumpTriggers.cs @@ -1,11 +1,16 @@ +using Sentry.Infrastructure; + namespace Sentry; +#if MEMORY_DUMP_SUPPORTED + /// /// Delegate that determines whether a heap dump should be triggered or not. /// /// Memory currently used by the process /// Total available memory /// if the heap dump should be triggered; otherwise, . +[Experimental(DiagnosticId.ExperimentalFeature)] public delegate bool HeapDumpTrigger(long usedMemory, long totalMemory); internal static class HeapDumpTriggers @@ -25,3 +30,5 @@ internal static HeapDumpTrigger MemoryPercentageThreshold(int memoryPercentageTh }; } } + +#endif diff --git a/src/Sentry/Infrastructure/DiagnosticId.cs b/src/Sentry/Infrastructure/DiagnosticId.cs new file mode 100644 index 0000000000..92703ddc87 --- /dev/null +++ b/src/Sentry/Infrastructure/DiagnosticId.cs @@ -0,0 +1,11 @@ +namespace Sentry.Infrastructure; + +internal static class DiagnosticId +{ +#if NET5_0_OR_GREATER + /// + /// Indicates that the feature is experimental and may be subject to change or removal in future versions. + /// + internal const string ExperimentalFeature = "SENTRY0001"; +#endif +} diff --git a/src/Sentry/Internal/HeapDumpOptions.cs b/src/Sentry/Internal/HeapDumpOptions.cs index 3d68d84d58..27b52a961f 100644 --- a/src/Sentry/Internal/HeapDumpOptions.cs +++ b/src/Sentry/Internal/HeapDumpOptions.cs @@ -1,3 +1,7 @@ namespace Sentry.Internal; +#if MEMORY_DUMP_SUPPORTED + internal record HeapDumpOptions(HeapDumpTrigger Trigger, Debouncer Debouncer, SentryLevel Level); + +#endif diff --git a/src/Sentry/SentryOptions.cs b/src/Sentry/SentryOptions.cs index b91e099b48..127cc4bf11 100644 --- a/src/Sentry/SentryOptions.cs +++ b/src/Sentry/SentryOptions.cs @@ -525,9 +525,18 @@ public int MaxQueueItems #if MEMORY_DUMP_SUPPORTED /// + /// /// Configures a heap dump to be captured if the percentage of memory used exceeds a certain threshold. /// This can be useful to diagnose memory leaks. + /// + /// + /// Note: This feature requires `dotnet-gcdump` to be installed globally on the machine or container where the heap + /// dumps will be captured. You can install this by running: `dotnet tool install --global dotnet-gcdump` + /// /// + /// + /// This API is experimental and may change in future versions. + /// /// /// The memory threshold at which to trigger a heap dump, as a percentage of total available memory. /// Must be a number between 1 and 99. @@ -538,6 +547,7 @@ public int MaxQueueItems /// /// Optional parameter controlling the event level associated with heap dumps. /// Defaults to . + [Experimental(DiagnosticId.ExperimentalFeature)] public void EnableHeapDumps(short memoryPercentageThreshold, Debouncer? debouncer = null, SentryLevel level = SentryLevel.Warning) => EnableHeapDumps(HeapDumpTriggers.MemoryPercentageThreshold(memoryPercentageThreshold), debouncer, level); @@ -550,6 +560,9 @@ public void EnableHeapDumps(short memoryPercentageThreshold, Debouncer? debounce /// dumps will be captured. You can install this by running: `dotnet tool install --global dotnet-gcdump` /// ///
+ /// + /// This API is experimental and may change in future versions. + /// /// /// A custom trigger function that accepts the current memory usage and total available memory as arguments and /// return true to indicate that a heap dump should be captured or false otherwise. @@ -560,6 +573,7 @@ public void EnableHeapDumps(short memoryPercentageThreshold, Debouncer? debounce /// /// Optional parameter controlling the event level associated with heap dumps. /// Defaults to . + [Experimental(DiagnosticId.ExperimentalFeature)] public void EnableHeapDumps(HeapDumpTrigger trigger, Debouncer? debouncer = null, SentryLevel level = SentryLevel.Warning) => HeapDumpOptions = new HeapDumpOptions(trigger, debouncer ?? Debouncer.PerApplicationLifetime(), level); diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt index a66f1c5ea4..6a7583141c 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt @@ -75,6 +75,7 @@ namespace Sentry ManagedBackgroundThread = 1, Native = 2, } + [System.Diagnostics.CodeAnalysis.Experimental("SENTRY0001")] public class Debouncer { public static Sentry.Debouncer PerApplicationLifetime(int eventMaximum = 1, System.TimeSpan? cooldown = default) { } @@ -132,6 +133,7 @@ namespace Sentry { public static void SetTags(this Sentry.IHasTags hasTags, System.Collections.Generic.IEnumerable> tags) { } } + [System.Diagnostics.CodeAnalysis.Experimental("SENTRY0001")] public delegate bool HeapDumpTrigger(long usedMemory, long totalMemory); public static class HintTypes { @@ -730,7 +732,9 @@ namespace Sentry public void DisableSystemDiagnosticsMetricsIntegration() { } public void DisableUnobservedTaskExceptionCapture() { } public void DisableWinUiUnhandledExceptionIntegration() { } + [System.Diagnostics.CodeAnalysis.Experimental("SENTRY0001")] public void EnableHeapDumps(Sentry.HeapDumpTrigger trigger, Sentry.Debouncer? debouncer = null, Sentry.SentryLevel level = 2) { } + [System.Diagnostics.CodeAnalysis.Experimental("SENTRY0001")] public void EnableHeapDumps(short memoryPercentageThreshold, Sentry.Debouncer? debouncer = null, Sentry.SentryLevel level = 2) { } public System.Collections.Generic.IEnumerable GetAllEventProcessors() { } public System.Collections.Generic.IEnumerable GetAllExceptionProcessors() { } diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt index a66f1c5ea4..6a7583141c 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt @@ -75,6 +75,7 @@ namespace Sentry ManagedBackgroundThread = 1, Native = 2, } + [System.Diagnostics.CodeAnalysis.Experimental("SENTRY0001")] public class Debouncer { public static Sentry.Debouncer PerApplicationLifetime(int eventMaximum = 1, System.TimeSpan? cooldown = default) { } @@ -132,6 +133,7 @@ namespace Sentry { public static void SetTags(this Sentry.IHasTags hasTags, System.Collections.Generic.IEnumerable> tags) { } } + [System.Diagnostics.CodeAnalysis.Experimental("SENTRY0001")] public delegate bool HeapDumpTrigger(long usedMemory, long totalMemory); public static class HintTypes { @@ -730,7 +732,9 @@ namespace Sentry public void DisableSystemDiagnosticsMetricsIntegration() { } public void DisableUnobservedTaskExceptionCapture() { } public void DisableWinUiUnhandledExceptionIntegration() { } + [System.Diagnostics.CodeAnalysis.Experimental("SENTRY0001")] public void EnableHeapDumps(Sentry.HeapDumpTrigger trigger, Sentry.Debouncer? debouncer = null, Sentry.SentryLevel level = 2) { } + [System.Diagnostics.CodeAnalysis.Experimental("SENTRY0001")] public void EnableHeapDumps(short memoryPercentageThreshold, Sentry.Debouncer? debouncer = null, Sentry.SentryLevel level = 2) { } public System.Collections.Generic.IEnumerable GetAllEventProcessors() { } public System.Collections.Generic.IEnumerable GetAllExceptionProcessors() { } diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt index d0a17a5b77..460ab5309d 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt @@ -74,13 +74,6 @@ namespace Sentry Managed = 0, ManagedBackgroundThread = 1, } - public class Debouncer - { - public static Sentry.Debouncer PerApplicationLifetime(int eventMaximum = 1, System.TimeSpan? cooldown = default) { } - public static Sentry.Debouncer PerDay(int eventMaximum = 1, System.TimeSpan? cooldown = default) { } - public static Sentry.Debouncer PerHour(int eventMaximum = 1, System.TimeSpan? cooldown = default) { } - public static Sentry.Debouncer PerMinute(int eventMaximum = 1, System.TimeSpan? cooldown = default) { } - } [System.Flags] public enum DeduplicateMode { @@ -130,7 +123,6 @@ namespace Sentry { public static void SetTags(this Sentry.IHasTags hasTags, System.Collections.Generic.IEnumerable> tags) { } } - public delegate bool HeapDumpTrigger(long usedMemory, long totalMemory); public static class HintTypes { public const string HttpResponseMessage = "http-response-message"; diff --git a/test/Sentry.Tests/DebouncerTests.cs b/test/Sentry.Tests/DebouncerTests.cs index 39807e9bd4..ef9f838838 100644 --- a/test/Sentry.Tests/DebouncerTests.cs +++ b/test/Sentry.Tests/DebouncerTests.cs @@ -1,5 +1,7 @@ namespace Sentry.Tests; +#if MEMORY_DUMP_SUPPORTED + public class DebouncerTests { [Fact] @@ -142,3 +144,5 @@ public void RecordOccurence_AfterInterval_ResetsInterval() debouncer._lastEvent.Should().Be(secondEventTime); } } + +#endif From a1a282078a96b2604941bc166b66f247941406bf Mon Sep 17 00:00:00 2001 From: getsentry-bot Date: Thu, 19 Dec 2024 00:45:49 +0000 Subject: [PATCH 075/363] release: 5.0.0 --- CHANGELOG.md | 2 +- Directory.Build.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 86962d0aa6..9e5034ec07 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## Unreleased +## 5.0.0 ### API Changes diff --git a/Directory.Build.props b/Directory.Build.props index 356a7abdca..77f4f80e04 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,7 +1,7 @@ - 5.0.0-alpha.1 + 5.0.0 13 true true From 9529c1dd839e159bc105fcca72c3167e6e66aeb8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 19 Dec 2024 20:10:03 +1300 Subject: [PATCH 076/363] chore: update modules/sentry-native to 0.7.17 (#3857) Co-authored-by: GitHub --- CHANGELOG.md | 8 ++++++++ modules/sentry-native | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e5034ec07..451793474d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## Unreleased + +### Dependencies + +- Bump Native SDK from v0.7.16 to v0.7.17 ([#3857](https://github.com/getsentry/sentry-dotnet/pull/3857)) + - [changelog](https://github.com/getsentry/sentry-native/blob/master/CHANGELOG.md#0717) + - [diff](https://github.com/getsentry/sentry-native/compare/0.7.16...0.7.17) + ## 5.0.0 ### API Changes diff --git a/modules/sentry-native b/modules/sentry-native index 39445fe251..4072538dfd 160000 --- a/modules/sentry-native +++ b/modules/sentry-native @@ -1 +1 @@ -Subproject commit 39445fe2512a83e6e8f8e031a64e0c5d90457263 +Subproject commit 4072538dfdbcafb3974318fe08798e51f62786d9 From 4817f4936fb445130d533fb924118ae4cafa6e6d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 26 Dec 2024 00:46:27 +1300 Subject: [PATCH 077/363] build(deps): bump gradle/actions from 4.2.1 to 4.2.2 (#3862) Bumps [gradle/actions](https://github.com/gradle/actions) from 4.2.1 to 4.2.2. - [Release notes](https://github.com/gradle/actions/releases) - [Commits](https://github.com/gradle/actions/compare/cc4fc85e6b35bafd578d5ffbc76a5518407e1af0...0bdd871935719febd78681f197cd39af5b6e16a6) --- updated-dependencies: - dependency-name: gradle/actions dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/device-tests-android.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/device-tests-android.yml b/.github/workflows/device-tests-android.yml index bcc222c94f..16e4070b0c 100644 --- a/.github/workflows/device-tests-android.yml +++ b/.github/workflows/device-tests-android.yml @@ -74,7 +74,7 @@ jobs: path: bin - name: Setup Gradle - uses: gradle/actions/setup-gradle@cc4fc85e6b35bafd578d5ffbc76a5518407e1af0 # pin@v3 + uses: gradle/actions/setup-gradle@0bdd871935719febd78681f197cd39af5b6e16a6 # pin@v3 # Cached AVD setup per https://github.com/ReactiveCircus/android-emulator-runner/blob/main/README.md From 1c23f7257fa5adc62bf208d1957d682331994f85 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Jan 2025 10:42:16 +1300 Subject: [PATCH 078/363] build(deps): bump actions/create-github-app-token from 1.11.0 to 1.11.1 (#3863) Bumps [actions/create-github-app-token](https://github.com/actions/create-github-app-token) from 1.11.0 to 1.11.1. - [Release notes](https://github.com/actions/create-github-app-token/releases) - [Commits](https://github.com/actions/create-github-app-token/compare/5d869da34e18e7287c1daad50e0b8ea0f506ce69...c1a285145b9d317df6ced56c09f525b5c2b6f755) --- updated-dependencies: - dependency-name: actions/create-github-app-token dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0bcbfbb2e2..9117f5f517 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -20,7 +20,7 @@ jobs: steps: - name: Get auth token id: token - uses: actions/create-github-app-token@5d869da34e18e7287c1daad50e0b8ea0f506ce69 # v1.11.0 + uses: actions/create-github-app-token@c1a285145b9d317df6ced56c09f525b5c2b6f755 # v1.11.1 with: app-id: ${{ vars.SENTRY_RELEASE_BOT_CLIENT_ID }} private-key: ${{ secrets.SENTRY_RELEASE_BOT_PRIVATE_KEY }} From 327899c718a2fd3aa65d4da965d58cdef3fdca46 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Jan 2025 10:42:49 +1300 Subject: [PATCH 079/363] build(deps): bump github/codeql-action from 3.27.9 to 3.28.0 (#3864) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.27.9 to 3.28.0. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/df409f7d9260372bd5f19e5b04e83cb3c43714ae...48ab28a6f5dbc2a99bf1e0131198dd8f1df78169) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 479439f8c1..ef53f66517 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -35,7 +35,7 @@ jobs: uses: ./.github/actions/environment - name: Initialize CodeQL - uses: github/codeql-action/init@df409f7d9260372bd5f19e5b04e83cb3c43714ae # pin@v2 + uses: github/codeql-action/init@48ab28a6f5dbc2a99bf1e0131198dd8f1df78169 # pin@v2 with: languages: csharp @@ -49,6 +49,6 @@ jobs: run: dotnet build Sentry-CI-CodeQL.slnf --no-restore --nologo - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@df409f7d9260372bd5f19e5b04e83cb3c43714ae # pin@v2 + uses: github/codeql-action/analyze@48ab28a6f5dbc2a99bf1e0131198dd8f1df78169 # pin@v2 with: category: '/language:csharp' From 233ed480e38973eda98e753a65a661919ca75f2b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Jan 2025 10:43:16 +1300 Subject: [PATCH 080/363] build(deps): bump codecov/codecov-action from 5.1.1 to 5.1.2 (#3865) Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 5.1.1 to 5.1.2. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/7f8b4b4bde536c465e797be725718b88c5d95e0e...1e68e06f1dbfde0e4cefc87efeba9e4643565303) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7ce2ff8dc0..d45f1c3dab 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -121,7 +121,7 @@ jobs: run: dotnet test Sentry-CI-Build-${{ runner.os }}.slnf -c Release --no-build --nologo -l GitHubActions -l "trx;LogFilePrefix=testresults_${{ runner.os }}" --collect "XPlat Code Coverage" - name: Upload code coverage - uses: codecov/codecov-action@7f8b4b4bde536c465e797be725718b88c5d95e0e + uses: codecov/codecov-action@1e68e06f1dbfde0e4cefc87efeba9e4643565303 - name: Upload build and test outputs if: failure() From 1aa4a78fcd2f1a9ed81a8551dcf64610cb33f45f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 6 Jan 2025 10:55:02 +1300 Subject: [PATCH 081/363] chore: update scripts/update-cli.ps1 to 2.40.0 (#3869) Co-authored-by: GitHub --- CHANGELOG.md | 3 +++ Directory.Build.props | 2 +- src/Sentry/Sentry.csproj | 14 +++++++------- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 451793474d..3830c7a52b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ - Bump Native SDK from v0.7.16 to v0.7.17 ([#3857](https://github.com/getsentry/sentry-dotnet/pull/3857)) - [changelog](https://github.com/getsentry/sentry-native/blob/master/CHANGELOG.md#0717) - [diff](https://github.com/getsentry/sentry-native/compare/0.7.16...0.7.17) +- Bump CLI from v2.39.1 to v2.40.0 ([#3869](https://github.com/getsentry/sentry-dotnet/pull/3869)) + - [changelog](https://github.com/getsentry/sentry-cli/blob/master/CHANGELOG.md#2400) + - [diff](https://github.com/getsentry/sentry-cli/compare/2.39.1...2.40.0) ## 5.0.0 diff --git a/Directory.Build.props b/Directory.Build.props index 77f4f80e04..2cafe8a2ba 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -83,7 +83,7 @@ - 2.39.1 + 2.40.0 $(MSBuildThisFileDirectory)tools\sentry-cli\$(SentryCLIVersion)\ diff --git a/src/Sentry/Sentry.csproj b/src/Sentry/Sentry.csproj index 1162096802..9a6d9a3afc 100644 --- a/src/Sentry/Sentry.csproj +++ b/src/Sentry/Sentry.csproj @@ -120,13 +120,13 @@ <_OSArchitecture>$([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture) - - - - - - - + + + + + + + From f752d26e7c49a991c9680f61900403a2b0ff858d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 6 Jan 2025 16:27:09 +1300 Subject: [PATCH 082/363] chore(deps): update Java SDK to v7.20.0 (#3866) * chore: update scripts/update-java.ps1 to 7.20.0 * pin Ubuntu 22.04 --------- Co-authored-by: GitHub Co-authored-by: James Crosswell --- .github/workflows/build.yml | 11 +++++++---- CHANGELOG.md | 3 +++ .../Sentry.Bindings.Android.csproj | 2 +- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d45f1c3dab..0947f636db 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,8 +16,9 @@ jobs: strategy: fail-fast: false matrix: + # Pin ubuntu to ensure mono is installed # Pin macos to get the version of Xcode that we need: https://github.com/actions/runner-images/issues/10703 - os: [ubuntu-latest, windows-latest, macos-15] + os: [ubuntu-22.04, windows-latest, macos-15] steps: - name: Checkout @@ -53,8 +54,9 @@ jobs: strategy: fail-fast: false matrix: + # Pin ubuntu to ensure mono is installed # Pin macos to get the version of Xcode that we need: https://github.com/actions/runner-images/issues/10703 - os: [ubuntu-latest, windows-latest, macos-15] + os: [ubuntu-22.04, windows-latest, macos-15] steps: - name: Cancel Previous Runs @@ -154,8 +156,9 @@ jobs: strategy: fail-fast: false matrix: + # Pin ubuntu to ensure mono is installed # Pin macos to get the version of Xcode that we need: https://github.com/actions/runner-images/issues/10703 - os: [ubuntu-latest, windows-latest, macos-15] + os: [ubuntu-22.04, windows-latest, macos-15] steps: - uses: actions/checkout@v4 @@ -234,7 +237,7 @@ jobs: # run: dotnet publish test/Sentry.MauiTrimTest/Sentry.MauiTrimTest.csproj -c Release -f net9.0-ios18.0 -r ios-arm64 test-solution-filters: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 if: ${{ !startsWith(github.ref_name, 'release/') }} steps: diff --git a/CHANGELOG.md b/CHANGELOG.md index 3830c7a52b..605bb89d1f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ - Bump Native SDK from v0.7.16 to v0.7.17 ([#3857](https://github.com/getsentry/sentry-dotnet/pull/3857)) - [changelog](https://github.com/getsentry/sentry-native/blob/master/CHANGELOG.md#0717) - [diff](https://github.com/getsentry/sentry-native/compare/0.7.16...0.7.17) +- Bump Java SDK from v7.19.0 to v7.20.0 ([#3866](https://github.com/getsentry/sentry-dotnet/pull/3866)) + - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#7200) + - [diff](https://github.com/getsentry/sentry-java/compare/7.19.0...7.20.0) - Bump CLI from v2.39.1 to v2.40.0 ([#3869](https://github.com/getsentry/sentry-dotnet/pull/3869)) - [changelog](https://github.com/getsentry/sentry-cli/blob/master/CHANGELOG.md#2400) - [diff](https://github.com/getsentry/sentry-cli/compare/2.39.1...2.40.0) diff --git a/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj b/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj index 024a4a93d1..46cc0077ab 100644 --- a/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj +++ b/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj @@ -3,7 +3,7 @@ net8.0-android34.0 $(NoWarn);BG8605;BG8606 - 7.19.0 + 7.20.0 $(BaseIntermediateOutputPath)sdks\Sentry\Android\$(SentryAndroidSdkVersion)\ .NET Bindings for the Sentry Android SDK From 7024f37efa663bbf054913b5a4659b9b407ab47d Mon Sep 17 00:00:00 2001 From: Bruno Garcia Date: Mon, 6 Jan 2025 02:51:39 -0500 Subject: [PATCH 083/363] Disable and obsolete iOS Watchdog termination (#3867) * Disable and obsolete iOS Watchdog termination * changelog --- CHANGELOG.md | 5 +++++ src/Sentry/Platforms/Cocoa/BindableSentryOptions.cs | 2 ++ src/Sentry/Platforms/Cocoa/SentryOptions.cs | 11 +++++++---- src/Sentry/Platforms/Cocoa/SentrySdk.cs | 2 ++ 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 605bb89d1f..627316d4d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ ## Unreleased +### Fixes + +- .NET Mobile: Disable and made obsolete the iOS Watchdog termination feature which is based on heuristics that don't work in .NET ([#3867](https://github.com/getsentry/sentry-dotnet/pull/3867)) + + ### Dependencies - Bump Native SDK from v0.7.16 to v0.7.17 ([#3857](https://github.com/getsentry/sentry-dotnet/pull/3857)) diff --git a/src/Sentry/Platforms/Cocoa/BindableSentryOptions.cs b/src/Sentry/Platforms/Cocoa/BindableSentryOptions.cs index 5697ccf8cb..af7b318302 100644 --- a/src/Sentry/Platforms/Cocoa/BindableSentryOptions.cs +++ b/src/Sentry/Platforms/Cocoa/BindableSentryOptions.cs @@ -38,7 +38,9 @@ public void ApplyTo(SentryOptions.NativeOptions options) options.EnableFileIOTracing = EnableFileIOTracing ?? options.EnableFileIOTracing; options.EnableNetworkBreadcrumbs = EnableNetworkBreadcrumbs ?? options.EnableNetworkBreadcrumbs; options.EnableNetworkTracking = EnableNetworkTracking ?? options.EnableNetworkTracking; +#pragma warning disable CS0618 // Type or member is obsolete options.EnableWatchdogTerminationTracking = EnableWatchdogTerminationTracking ?? options.EnableWatchdogTerminationTracking; +#pragma warning restore CS0618 // Type or member is obsolete options.EnableSwizzling = EnableSwizzling ?? options.EnableSwizzling; options.EnableUIViewControllerTracing = EnableUIViewControllerTracing ?? options.EnableUIViewControllerTracing; options.EnableUserInteractionTracing = EnableUserInteractionTracing ?? options.EnableUserInteractionTracing; diff --git a/src/Sentry/Platforms/Cocoa/SentryOptions.cs b/src/Sentry/Platforms/Cocoa/SentryOptions.cs index ae28a38afb..f72f37e08b 100644 --- a/src/Sentry/Platforms/Cocoa/SentryOptions.cs +++ b/src/Sentry/Platforms/Cocoa/SentryOptions.cs @@ -128,13 +128,16 @@ internal NativeOptions(SentryOptions options) public bool EnableNetworkTracking { get; set; } = true; /// - /// Whether to enable watchdog termination tracking or not. - /// The default value is true (enabled). + /// Whether to enable watchdog termination tracking or not. NOT advised. + /// The default value is false (disabled). /// /// - /// https://docs.sentry.io/platforms/apple/configuration/watchdog-terminations/ + /// This feature is prone to false positives on .NET since it relies on heuristics that don't work in this environment. /// - public bool EnableWatchdogTerminationTracking { get; set; } = true; + /// + /// + [Obsolete("See: https://github.com/getsentry/sentry-dotnet/issues/3860")] + public bool EnableWatchdogTerminationTracking { get; set; } = false; /// /// Whether the SDK should use swizzling or not. diff --git a/src/Sentry/Platforms/Cocoa/SentrySdk.cs b/src/Sentry/Platforms/Cocoa/SentrySdk.cs index 812733c7c9..c10e22938c 100644 --- a/src/Sentry/Platforms/Cocoa/SentrySdk.cs +++ b/src/Sentry/Platforms/Cocoa/SentrySdk.cs @@ -127,7 +127,9 @@ private static void InitSentryCocoaSdk(SentryOptions options) nativeOptions.EnableFileIOTracing = options.Native.EnableFileIOTracing; nativeOptions.EnableNetworkBreadcrumbs = options.Native.EnableNetworkBreadcrumbs; nativeOptions.EnableNetworkTracking = options.Native.EnableNetworkTracking; +#pragma warning disable CS0618 // Type or member is obsolete nativeOptions.EnableWatchdogTerminationTracking = options.Native.EnableWatchdogTerminationTracking; +#pragma warning restore CS0618 // Type or member is obsolete nativeOptions.EnableSwizzling = options.Native.EnableSwizzling; nativeOptions.EnableUIViewControllerTracing = options.Native.EnableUIViewControllerTracing; nativeOptions.EnableUserInteractionTracing = options.Native.EnableUserInteractionTracing; From 3f63cb6a9c71d37d061ca0f7a706b2a5e0a2cc26 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Wed, 8 Jan 2025 12:25:06 +1300 Subject: [PATCH 084/363] Revert signal handler chaining order (PR #3694) (#3871) --- CHANGELOG.md | 2 +- src/Sentry/Platforms/Android/SentrySdk.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 627316d4d4..394f4bfcfe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ ### Fixes - .NET Mobile: Disable and made obsolete the iOS Watchdog termination feature which is based on heuristics that don't work in .NET ([#3867](https://github.com/getsentry/sentry-dotnet/pull/3867)) - +- NullReferenceExceptions causing application crashes in AOT compiled applications (PR #3694) ([#3871](https://github.com/getsentry/sentry-dotnet/pull/3871)) ### Dependencies diff --git a/src/Sentry/Platforms/Android/SentrySdk.cs b/src/Sentry/Platforms/Android/SentrySdk.cs index f7a5b9f686..607f4dee59 100644 --- a/src/Sentry/Platforms/Android/SentrySdk.cs +++ b/src/Sentry/Platforms/Android/SentrySdk.cs @@ -60,7 +60,7 @@ private static void InitSentryAndroidSdk(SentryOptions options) o.ServerName = options.ServerName; o.SessionTrackingIntervalMillis = (long)options.AutoSessionTrackingInterval.TotalMilliseconds; o.ShutdownTimeoutMillis = (long)options.ShutdownTimeout.TotalMilliseconds; - o.SetNativeHandlerStrategy(JavaSdk.Android.Core.NdkHandlerStrategy.SentryHandlerStrategyChainAtStart); + o.SetNativeHandlerStrategy(JavaSdk.Android.Core.NdkHandlerStrategy.SentryHandlerStrategyDefault); if (options.CacheDirectoryPath is { } cacheDirectoryPath) { From 6b81458c7159701f843e6ddd3469a539a429a02a Mon Sep 17 00:00:00 2001 From: Bruno Garcia Date: Tue, 7 Jan 2025 18:46:19 -0500 Subject: [PATCH 085/363] improve changelog for android fix (#3874) --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 394f4bfcfe..fc100bf5ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ ### Fixes - .NET Mobile: Disable and made obsolete the iOS Watchdog termination feature which is based on heuristics that don't work in .NET ([#3867](https://github.com/getsentry/sentry-dotnet/pull/3867)) -- NullReferenceExceptions causing application crashes in AOT compiled applications (PR #3694) ([#3871](https://github.com/getsentry/sentry-dotnet/pull/3871)) +- .NET on Android: NullReferenceException handled by Mono cause the app to crash (PR #3694) ([#3871](https://github.com/getsentry/sentry-dotnet/pull/3871)) ### Dependencies From 980c6c81880bfb13a5b1642d098a09b37d9c52b7 Mon Sep 17 00:00:00 2001 From: getsentry-bot Date: Tue, 7 Jan 2025 23:48:39 +0000 Subject: [PATCH 086/363] release: 5.0.1 --- CHANGELOG.md | 2 +- Directory.Build.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fc100bf5ee..54ed60add3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## Unreleased +## 5.0.1 ### Fixes diff --git a/Directory.Build.props b/Directory.Build.props index 2cafe8a2ba..b9a20c6d18 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,7 +1,7 @@ - 5.0.0 + 5.0.1 13 true true From c2c737b37882ae5a9a34e17de9386eb620f00ba4 Mon Sep 17 00:00:00 2001 From: phunkeler <69320109+phunkeler@users.noreply.github.com> Date: Wed, 8 Jan 2025 23:04:11 -0500 Subject: [PATCH 087/363] [net9.0] Use [GeneratedRegex] props (#3870) --- src/Sentry/BuiltInSystemDiagnosticsMeters.cs | 108 ++++++++++++++----- src/Sentry/Internal/OriginHelper.cs | 13 ++- src/Sentry/MetricHelper.cs | 57 ++++++---- src/Sentry/SentryMonitorOptions.cs | 33 +++--- 4 files changed, 142 insertions(+), 69 deletions(-) diff --git a/src/Sentry/BuiltInSystemDiagnosticsMeters.cs b/src/Sentry/BuiltInSystemDiagnosticsMeters.cs index d631557531..145546897f 100644 --- a/src/Sentry/BuiltInSystemDiagnosticsMeters.cs +++ b/src/Sentry/BuiltInSystemDiagnosticsMeters.cs @@ -24,10 +24,15 @@ public static partial class BuiltInSystemDiagnosticsMeters /// /// Matches the built-in Microsoft.AspNetCore.Hosting metrics /// -#if NET8_0_OR_GREATER +#if NET9_0_OR_GREATER + public static readonly StringOrRegex MicrosoftAspNetCoreHosting = MicrosoftAspNetCoreHostingRegex; + + [GeneratedRegex(MicrosoftAspNetCoreHostingPattern)] + private static partial Regex MicrosoftAspNetCoreHostingRegex { get; } +#elif NET8_0 public static readonly StringOrRegex MicrosoftAspNetCoreHosting = MicrosoftAspNetCoreHostingRegex(); - [GeneratedRegex(MicrosoftAspNetCoreHostingPattern, RegexOptions.Compiled)] + [GeneratedRegex(MicrosoftAspNetCoreHostingPattern)] private static partial Regex MicrosoftAspNetCoreHostingRegex(); #else public static readonly StringOrRegex MicrosoftAspNetCoreHosting = new Regex(MicrosoftAspNetCoreHostingPattern, RegexOptions.Compiled); @@ -36,10 +41,15 @@ public static partial class BuiltInSystemDiagnosticsMeters /// /// Matches the built-in Microsoft.AspNetCore.Routing metrics /// -#if NET8_0_OR_GREATER +#if NET9_0_OR_GREATER + public static readonly StringOrRegex MicrosoftAspNetCoreRouting = MicrosoftAspNetCoreRoutingRegex; + + [GeneratedRegex(MicrosoftAspNetCoreRoutingPattern)] + private static partial Regex MicrosoftAspNetCoreRoutingRegex { get; } +#elif NET8_0 public static readonly StringOrRegex MicrosoftAspNetCoreRouting = MicrosoftAspNetCoreRoutingRegex(); - [GeneratedRegex(MicrosoftAspNetCoreRoutingPattern, RegexOptions.Compiled)] + [GeneratedRegex(MicrosoftAspNetCoreRoutingPattern)] private static partial Regex MicrosoftAspNetCoreRoutingRegex(); #else public static readonly StringOrRegex MicrosoftAspNetCoreRouting = new Regex(MicrosoftAspNetCoreRoutingPattern, RegexOptions.Compiled); @@ -48,10 +58,15 @@ public static partial class BuiltInSystemDiagnosticsMeters /// /// Matches the built-in Microsoft.AspNetCore.Diagnostics metrics /// -#if NET8_0_OR_GREATER +#if NET9_0_OR_GREATER + public static readonly StringOrRegex MicrosoftAspNetCoreDiagnostics = MicrosoftAspNetCoreDiagnosticsRegex; + + [GeneratedRegex(MicrosoftAspNetCoreDiagnosticsPattern)] + private static partial Regex MicrosoftAspNetCoreDiagnosticsRegex { get; } +#elif NET8_0 public static readonly StringOrRegex MicrosoftAspNetCoreDiagnostics = MicrosoftAspNetCoreDiagnosticsRegex(); - [GeneratedRegex(MicrosoftAspNetCoreDiagnosticsPattern, RegexOptions.Compiled)] + [GeneratedRegex(MicrosoftAspNetCoreDiagnosticsPattern)] private static partial Regex MicrosoftAspNetCoreDiagnosticsRegex(); #else public static readonly StringOrRegex MicrosoftAspNetCoreDiagnostics = new Regex(MicrosoftAspNetCoreDiagnosticsPattern, RegexOptions.Compiled); @@ -60,10 +75,15 @@ public static partial class BuiltInSystemDiagnosticsMeters /// /// Matches the built-in Microsoft.AspNetCore.RateLimiting metrics /// -#if NET8_0_OR_GREATER +#if NET9_0_OR_GREATER + public static readonly StringOrRegex MicrosoftAspNetCoreRateLimiting = MicrosoftAspNetCoreRateLimitingRegex; + + [GeneratedRegex(MicrosoftAspNetCoreRateLimitingPattern)] + private static partial Regex MicrosoftAspNetCoreRateLimitingRegex { get; } +#elif NET8_0 public static readonly StringOrRegex MicrosoftAspNetCoreRateLimiting = MicrosoftAspNetCoreRateLimitingRegex(); - [GeneratedRegex(MicrosoftAspNetCoreRateLimitingPattern, RegexOptions.Compiled)] + [GeneratedRegex(MicrosoftAspNetCoreRateLimitingPattern)] private static partial Regex MicrosoftAspNetCoreRateLimitingRegex(); #else public static readonly StringOrRegex MicrosoftAspNetCoreRateLimiting = new Regex(MicrosoftAspNetCoreRateLimitingPattern, RegexOptions.Compiled); @@ -72,10 +92,15 @@ public static partial class BuiltInSystemDiagnosticsMeters /// /// Matches the built-in Microsoft.AspNetCore.HeaderParsing metrics /// -#if NET8_0_OR_GREATER +#if NET9_0_OR_GREATER + public static readonly StringOrRegex MicrosoftAspNetCoreHeaderParsing = MicrosoftAspNetCoreHeaderParsingRegex; + + [GeneratedRegex(MicrosoftAspNetCoreHeaderParsingPattern)] + private static partial Regex MicrosoftAspNetCoreHeaderParsingRegex { get; } +#elif NET8_0 public static readonly StringOrRegex MicrosoftAspNetCoreHeaderParsing = MicrosoftAspNetCoreHeaderParsingRegex(); - [GeneratedRegex(MicrosoftAspNetCoreHeaderParsingPattern, RegexOptions.Compiled)] + [GeneratedRegex(MicrosoftAspNetCoreHeaderParsingPattern)] private static partial Regex MicrosoftAspNetCoreHeaderParsingRegex(); #else public static readonly StringOrRegex MicrosoftAspNetCoreHeaderParsing = new Regex(MicrosoftAspNetCoreHeaderParsingPattern, RegexOptions.Compiled); @@ -84,10 +109,15 @@ public static partial class BuiltInSystemDiagnosticsMeters /// /// Matches the built-in Microsoft.AspNetCore.Server.Kestrel metrics /// -#if NET8_0_OR_GREATER +#if NET9_0_OR_GREATER + public static readonly StringOrRegex MicrosoftAspNetCoreServerKestrel = MicrosoftAspNetCoreServerKestrelRegex; + + [GeneratedRegex(MicrosoftAspNetCoreServerKestrelPattern)] + private static partial Regex MicrosoftAspNetCoreServerKestrelRegex { get; } +#elif NET8_0 public static readonly StringOrRegex MicrosoftAspNetCoreServerKestrel = MicrosoftAspNetCoreServerKestrelRegex(); - [GeneratedRegex(MicrosoftAspNetCoreServerKestrelPattern, RegexOptions.Compiled)] + [GeneratedRegex(MicrosoftAspNetCoreServerKestrelPattern)] private static partial Regex MicrosoftAspNetCoreServerKestrelRegex(); #else public static readonly StringOrRegex MicrosoftAspNetCoreServerKestrel = new Regex(MicrosoftAspNetCoreServerKestrelPattern, RegexOptions.Compiled); @@ -96,10 +126,15 @@ public static partial class BuiltInSystemDiagnosticsMeters /// /// Matches the built-in Microsoft.AspNetCore.Http.Connections metrics /// -#if NET8_0_OR_GREATER +#if NET9_0_OR_GREATER + public static readonly StringOrRegex MicrosoftAspNetCoreHttpConnections = MicrosoftAspNetCoreHttpConnectionsRegex; + + [GeneratedRegex(MicrosoftAspNetCoreHttpConnectionsPattern)] + private static partial Regex MicrosoftAspNetCoreHttpConnectionsRegex { get; } +#elif NET8_0 public static readonly StringOrRegex MicrosoftAspNetCoreHttpConnections = MicrosoftAspNetCoreHttpConnectionsRegex(); - [GeneratedRegex(MicrosoftAspNetCoreHttpConnectionsPattern, RegexOptions.Compiled)] + [GeneratedRegex(MicrosoftAspNetCoreHttpConnectionsPattern)] private static partial Regex MicrosoftAspNetCoreHttpConnectionsRegex(); #else public static readonly StringOrRegex MicrosoftAspNetCoreHttpConnections = new Regex(MicrosoftAspNetCoreHttpConnectionsPattern, RegexOptions.Compiled); @@ -108,10 +143,15 @@ public static partial class BuiltInSystemDiagnosticsMeters /// /// Matches the built-in Microsoft.Extensions.Diagnostics.HealthChecks metrics /// -#if NET8_0_OR_GREATER +#if NET9_0_OR_GREATER + public static readonly StringOrRegex MicrosoftExtensionsDiagnosticsHealthChecks = MicrosoftExtensionsDiagnosticsHealthChecksRegex; + + [GeneratedRegex(MicrosoftExtensionsDiagnosticsHealthChecksPattern)] + private static partial Regex MicrosoftExtensionsDiagnosticsHealthChecksRegex { get; } +#elif NET8_0 public static readonly StringOrRegex MicrosoftExtensionsDiagnosticsHealthChecks = MicrosoftExtensionsDiagnosticsHealthChecksRegex(); - [GeneratedRegex(MicrosoftExtensionsDiagnosticsHealthChecksPattern, RegexOptions.Compiled)] + [GeneratedRegex(MicrosoftExtensionsDiagnosticsHealthChecksPattern)] private static partial Regex MicrosoftExtensionsDiagnosticsHealthChecksRegex(); #else public static readonly StringOrRegex MicrosoftExtensionsDiagnosticsHealthChecks = new Regex(MicrosoftExtensionsDiagnosticsHealthChecksPattern, RegexOptions.Compiled); @@ -120,10 +160,15 @@ public static partial class BuiltInSystemDiagnosticsMeters /// /// Matches the built-in Microsoft.Extensions.Diagnostics.ResourceMonitoring metrics /// -#if NET8_0_OR_GREATER +#if NET9_0_OR_GREATER + public static readonly StringOrRegex MicrosoftExtensionsDiagnosticsResourceMonitoring = MicrosoftExtensionsDiagnosticsResourceMonitoringRegex; + + [GeneratedRegex(MicrosoftExtensionsDiagnosticsResourceMonitoringPattern)] + private static partial Regex MicrosoftExtensionsDiagnosticsResourceMonitoringRegex { get; } +#elif NET8_0 public static readonly StringOrRegex MicrosoftExtensionsDiagnosticsResourceMonitoring = MicrosoftExtensionsDiagnosticsResourceMonitoringRegex(); - [GeneratedRegex(MicrosoftExtensionsDiagnosticsResourceMonitoringPattern, RegexOptions.Compiled)] + [GeneratedRegex(MicrosoftExtensionsDiagnosticsResourceMonitoringPattern)] private static partial Regex MicrosoftExtensionsDiagnosticsResourceMonitoringRegex(); #else public static readonly StringOrRegex MicrosoftExtensionsDiagnosticsResourceMonitoring = new Regex(MicrosoftExtensionsDiagnosticsResourceMonitoringPattern, RegexOptions.Compiled); @@ -132,10 +177,15 @@ public static partial class BuiltInSystemDiagnosticsMeters /// /// Matches the built-in System.Net.NameResolution metrics /// -#if NET8_0_OR_GREATER +#if NET9_0_OR_GREATER + public static readonly StringOrRegex OpenTelemetryInstrumentationRuntime = OpenTelemetryInstrumentationRuntimeRegex; + + [GeneratedRegex(OpenTelemetryInstrumentationRuntimePattern)] + private static partial Regex OpenTelemetryInstrumentationRuntimeRegex { get; } +#elif NET8_0 public static readonly StringOrRegex OpenTelemetryInstrumentationRuntime = OpenTelemetryInstrumentationRuntimeRegex(); - [GeneratedRegex(OpenTelemetryInstrumentationRuntimePattern, RegexOptions.Compiled)] + [GeneratedRegex(OpenTelemetryInstrumentationRuntimePattern)] private static partial Regex OpenTelemetryInstrumentationRuntimeRegex(); #else public static readonly StringOrRegex OpenTelemetryInstrumentationRuntime = new Regex(OpenTelemetryInstrumentationRuntimePattern, RegexOptions.Compiled); @@ -144,10 +194,15 @@ public static partial class BuiltInSystemDiagnosticsMeters /// /// Matches the built-in System.Net.NameResolution metrics /// -#if NET8_0_OR_GREATER +#if NET9_0_OR_GREATER + public static readonly StringOrRegex SystemNetNameResolution = SystemNetNameResolutionRegex; + + [GeneratedRegex(SystemNetNameResolutionPattern)] + private static partial Regex SystemNetNameResolutionRegex { get; } +#elif NET8_0 public static readonly StringOrRegex SystemNetNameResolution = SystemNetNameResolutionRegex(); - [GeneratedRegex(SystemNetNameResolutionPattern, RegexOptions.Compiled)] + [GeneratedRegex(SystemNetNameResolutionPattern)] private static partial Regex SystemNetNameResolutionRegex(); #else public static readonly StringOrRegex SystemNetNameResolution = new Regex(SystemNetNameResolutionPattern, RegexOptions.Compiled); @@ -156,10 +211,15 @@ public static partial class BuiltInSystemDiagnosticsMeters /// /// Matches the built-in metrics /// -#if NET8_0_OR_GREATER +#if NET9_0_OR_GREATER + public static readonly StringOrRegex SystemNetHttp = SystemNetHttpRegex; + + [GeneratedRegex(SystemNetHttpPattern)] + private static partial Regex SystemNetHttpRegex { get; } +#elif NET8_0 public static readonly StringOrRegex SystemNetHttp = SystemNetHttpRegex(); - [GeneratedRegex(SystemNetHttpPattern, RegexOptions.Compiled)] + [GeneratedRegex(SystemNetHttpPattern)] private static partial Regex SystemNetHttpRegex(); #else public static readonly StringOrRegex SystemNetHttp = new Regex(SystemNetHttpPattern, RegexOptions.Compiled); diff --git a/src/Sentry/Internal/OriginHelper.cs b/src/Sentry/Internal/OriginHelper.cs index c3be3e95c9..0bd0aa0957 100644 --- a/src/Sentry/Internal/OriginHelper.cs +++ b/src/Sentry/Internal/OriginHelper.cs @@ -5,12 +5,15 @@ internal static partial class OriginHelper internal const string Manual = "manual"; private const string ValidOriginPattern = @"^(auto|manual)(\.[\w]+){0,3}$"; -#if NET8_0_OR_GREATER - [GeneratedRegex(ValidOriginPattern, RegexOptions.Compiled)] - private static partial Regex ValidOriginRegEx(); - private static readonly Regex ValidOrigin = ValidOriginRegEx(); +#if NET9_0_OR_GREATER + [GeneratedRegex(ValidOriginPattern)] + private static partial Regex ValidOrigin { get; } +#elif NET8_0 + [GeneratedRegex(ValidOriginPattern)] + private static partial Regex ValidOriginRegex(); + private static readonly Regex ValidOrigin = ValidOriginRegex(); #else - private static readonly Regex ValidOrigin = new (ValidOriginPattern, RegexOptions.Compiled); + private static readonly Regex ValidOrigin = new(ValidOriginPattern, RegexOptions.Compiled); #endif public static bool IsValidOrigin(string? value) => value == null || ValidOrigin.IsMatch(value); diff --git a/src/Sentry/MetricHelper.cs b/src/Sentry/MetricHelper.cs index d9d2d0743e..f05b20e6c6 100644 --- a/src/Sentry/MetricHelper.cs +++ b/src/Sentry/MetricHelper.cs @@ -18,6 +18,40 @@ internal static partial class MetricHelper private static readonly DateTimeOffset UnixEpoch = new DateTimeOffset(1970, 1, 1, 0, 0, 0, 0, TimeSpan.Zero); #endif +#if NET9_0_OR_GREATER + [GeneratedRegex(InvalidMetricKeyOrNameCharactersPattern)] + private static partial Regex InvalidMetricKeyOrNameCharacters { get; } + + [GeneratedRegex(InvalidTagKeyCharactersPattern)] + private static partial Regex InvalidTagKeyCharacters { get; } + + [GeneratedRegex(InvalidMetricUnitCharactersPattern)] + private static partial Regex InvalidMetricUnitCharacters { get; } +#elif NET8_0 + [GeneratedRegex(InvalidMetricKeyOrNameCharactersPattern)] + private static partial Regex InvalidMetricKeyOrNameCharacters(); + + [GeneratedRegex(InvalidTagKeyCharactersPattern)] + private static partial Regex InvalidTagKeyCharacters(); + + [GeneratedRegex(InvalidMetricUnitCharactersPattern)] + private static partial Regex InvalidMetricUnitCharacters(); +#else + private static readonly Regex InvalidMetricKeyOrNameCharacters = new(InvalidMetricKeyOrNameCharactersPattern, RegexOptions.Compiled); + private static readonly Regex InvalidTagKeyCharacters = new(InvalidTagKeyCharactersPattern, RegexOptions.Compiled); + private static readonly Regex InvalidMetricUnitCharacters = new(InvalidMetricUnitCharactersPattern, RegexOptions.Compiled); +#endif + +#if NET8_0 + internal static string SanitizeMetricKeyOrName(string input) => InvalidMetricKeyOrNameCharacters().Replace(input, "_"); + internal static string SanitizeTagKey(string input) => InvalidTagKeyCharacters().Replace(input, ""); + internal static string SanitizeMetricUnit(string input) => InvalidMetricUnitCharacters().Replace(input, ""); +#else + internal static string SanitizeMetricKeyOrName(string input) => InvalidMetricKeyOrNameCharacters.Replace(input, "_"); + internal static string SanitizeTagKey(string input) => InvalidTagKeyCharacters.Replace(input, ""); + internal static string SanitizeMetricUnit(string input) => InvalidMetricUnitCharacters.Replace(input, ""); +#endif + internal static long GetDayBucketKey(this DateTimeOffset timestamp) { var utc = timestamp.ToUniversalTime(); @@ -43,29 +77,6 @@ internal static DateTimeOffset GetCutoff() => DateTimeOffset.UtcNow .Subtract(TimeSpan.FromSeconds(RollupInSeconds)) .Subtract(TimeSpan.FromMilliseconds(FlushShift)); -#if NET7_0_OR_GREATER - [GeneratedRegex(InvalidMetricKeyOrNameCharactersPattern, RegexOptions.Compiled)] - private static partial Regex InvalidMetricKeyOrNameCharacters(); - internal static string SanitizeMetricKeyOrName(string input) => InvalidMetricKeyOrNameCharacters().Replace(input, "_"); - - [GeneratedRegex(InvalidTagKeyCharactersPattern, RegexOptions.Compiled)] - private static partial Regex InvalidTagKeyCharacters(); - internal static string SanitizeTagKey(string input) => InvalidTagKeyCharacters().Replace(input, ""); - - [GeneratedRegex(InvalidMetricUnitCharactersPattern, RegexOptions.Compiled)] - private static partial Regex InvalidMetricUnitCharacters(); - internal static string SanitizeMetricUnit(string input) => InvalidMetricUnitCharacters().Replace(input, ""); -#else - private static readonly Regex InvalidMetricKeyOrNameCharacters = new(InvalidMetricKeyOrNameCharactersPattern, RegexOptions.Compiled); - internal static string SanitizeMetricKeyOrName(string input) => InvalidMetricKeyOrNameCharacters.Replace(input, "_"); - - private static readonly Regex InvalidTagKeyCharacters = new(InvalidTagKeyCharactersPattern, RegexOptions.Compiled); - internal static string SanitizeTagKey(string input) => InvalidTagKeyCharacters.Replace(input, ""); - - private static readonly Regex InvalidMetricUnitCharacters = new(InvalidMetricUnitCharactersPattern, RegexOptions.Compiled); - internal static string SanitizeMetricUnit(string input) => InvalidMetricUnitCharacters.Replace(input, ""); -#endif - private static readonly Lazy[]> LazyTagValueReplacements = new(() => [ new KeyValuePair("\n", @"\n"), diff --git a/src/Sentry/SentryMonitorOptions.cs b/src/Sentry/SentryMonitorOptions.cs index 91d6d7dbeb..c61aa7e4ab 100644 --- a/src/Sentry/SentryMonitorOptions.cs +++ b/src/Sentry/SentryMonitorOptions.cs @@ -51,11 +51,6 @@ public enum SentryMonitorInterval /// public partial class SentryMonitorOptions : ISentryJsonSerializable { - private SentryMonitorScheduleType _type = SentryMonitorScheduleType.None; - private string? _crontab; - private int? _interval; - private SentryMonitorInterval? _unit; - // Breakdown of the validation // ^(\*|([0-5]?\d)) Minute 0 - 59 // (\s+)(\*|([01]?\d|2[0-3])) Hour 0 - 23 @@ -63,18 +58,22 @@ public partial class SentryMonitorOptions : ISentryJsonSerializable // (\s+)(\*|([1-9]|1[0-2])) Month 1 - 12 // (\s+)(\*|([0-7])) Weekday 0 - 7 // $ End of string -#if NET7_0_OR_GREATER - [GeneratedRegex(@"^(\*|([0-5]?\d))(\s+)(\*|([01]?\d|2[0-3]))(\s+)(\*|([1-9]|[12]\d|3[01]))(\s+)(\*|([1-9]|1[0-2]))(\s+)(\*|([0-7]))$", RegexOptions.IgnoreCase)] - private static partial Regex CrontabValidation(); -#else - private static Regex? CrontabValidationInstance; + private const string ValidCrontabPattern = @"^(\*|([0-5]?\d))(\s+)(\*|([01]?\d|2[0-3]))(\s+)(\*|([1-9]|[12]\d|3[01]))(\s+)(\*|([1-9]|1[0-2]))(\s+)(\*|([0-7]))$"; - private static Regex CrontabValidation() - { - return CrontabValidationInstance ??= new Regex( - @"^(\*|([0-5]?\d))(\s+)(\*|([01]?\d|2[0-3]))(\s+)(\*|([1-9]|[12]\d|3[01]))(\s+)(\*|([1-9]|1[0-2]))(\s+)(\*|([0-7]))$", - RegexOptions.Compiled | RegexOptions.CultureInvariant); - } + private SentryMonitorScheduleType _type = SentryMonitorScheduleType.None; + private string? _crontab; + private int? _interval; + private SentryMonitorInterval? _unit; + +#if NET9_0_OR_GREATER + [GeneratedRegex(ValidCrontabPattern, RegexOptions.IgnoreCase)] + private static partial Regex ValidCrontab { get; } +#elif NET8_0 + [GeneratedRegex(ValidCrontabPattern, RegexOptions.IgnoreCase)] + private static partial Regex ValidCrontabRegex(); + private static readonly Regex ValidCrontab = ValidCrontabRegex(); +#else + private static readonly Regex ValidCrontab = new(ValidCrontabPattern, RegexOptions.Compiled | RegexOptions.CultureInvariant); #endif /// @@ -88,7 +87,7 @@ public void Interval(string crontab) throw new ArgumentException("You tried to set the interval twice. The Check-Ins interval is supposed to be set only once."); } - if (!CrontabValidation().IsMatch(crontab)) + if (!ValidCrontab.IsMatch(crontab)) { throw new ArgumentException("The provided crontab does not match the expected format of '* * * * *' " + "translating to 'minute', 'hour', 'day of the month', 'month', and 'day of the week'."); From c20d16245c7e2d2f9056a49780593a391e59eda1 Mon Sep 17 00:00:00 2001 From: thisisthekap Date: Thu, 9 Jan 2025 16:48:49 +0100 Subject: [PATCH 088/363] Add iOS experimental EnableAppHangTrackingV2 binding (#3877) * Add experimental EnableAppHangTrackingV2 configuration flag to the dotnet SDK * Update CHANGELOG.md * reformatted BindableSentryOptions * Apply suggestions from code review --------- Co-authored-by: Bruno Garcia --- CHANGELOG.md | 4 ++++ .../Platforms/Cocoa/BindableSentryOptions.cs | 2 ++ src/Sentry/Platforms/Cocoa/SentryOptions.cs | 17 +++++++++++++++++ src/Sentry/Platforms/Cocoa/SentrySdk.cs | 1 + 4 files changed, 24 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 54ed60add3..0c055ddd6a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## 5.0.1 +### Features + +- .NET on iOS: Add experimental EnableAppHangTrackingV2 configuration flag to the options binding SDK ([#3877](https://github.com/getsentry/sentry-dotnet/pull/3877)) + ### Fixes - .NET Mobile: Disable and made obsolete the iOS Watchdog termination feature which is based on heuristics that don't work in .NET ([#3867](https://github.com/getsentry/sentry-dotnet/pull/3867)) diff --git a/src/Sentry/Platforms/Cocoa/BindableSentryOptions.cs b/src/Sentry/Platforms/Cocoa/BindableSentryOptions.cs index af7b318302..f8dae26213 100644 --- a/src/Sentry/Platforms/Cocoa/BindableSentryOptions.cs +++ b/src/Sentry/Platforms/Cocoa/BindableSentryOptions.cs @@ -14,6 +14,7 @@ public class NativeOptions public TimeSpan? AppHangTimeoutInterval { get; set; } public TimeSpan? IdleTimeout { get; set; } public bool? EnableAppHangTracking { get; set; } + public bool? EnableAppHangTrackingV2 { get; set; } public bool? EnableAutoBreadcrumbTracking { get; set; } public bool? EnableAutoPerformanceTracing { get; set; } public bool? EnableCoreDataTracing { get; set; } @@ -32,6 +33,7 @@ public void ApplyTo(SentryOptions.NativeOptions options) options.AppHangTimeoutInterval = AppHangTimeoutInterval ?? options.AppHangTimeoutInterval; options.IdleTimeout = IdleTimeout ?? options.IdleTimeout; options.EnableAppHangTracking = EnableAppHangTracking ?? options.EnableAppHangTracking; + options.EnableAppHangTrackingV2 = EnableAppHangTrackingV2 ?? options.EnableAppHangTrackingV2; options.EnableAutoBreadcrumbTracking = EnableAutoBreadcrumbTracking ?? options.EnableAutoBreadcrumbTracking; options.EnableAutoPerformanceTracing = EnableAutoPerformanceTracing ?? options.EnableAutoPerformanceTracing; options.EnableCoreDataTracing = EnableCoreDataTracing ?? options.EnableCoreDataTracing; diff --git a/src/Sentry/Platforms/Cocoa/SentryOptions.cs b/src/Sentry/Platforms/Cocoa/SentryOptions.cs index f72f37e08b..6b0e9587e6 100644 --- a/src/Sentry/Platforms/Cocoa/SentryOptions.cs +++ b/src/Sentry/Platforms/Cocoa/SentryOptions.cs @@ -68,6 +68,23 @@ internal NativeOptions(SentryOptions options) /// public bool EnableAppHangTracking { get; set; } = true; + /// + /// IMPORTANT: This feature is experimental and may have bugs. + ///
+ /// As of version 8.39.0-beta.1 of the sentry-cocoa SDK, you can enable AppHangsV2, which is available on iOS and tvOS. + /// The main difference is that AppHangsV2 differentiates between fully-blocking and non-fully-blocking + /// app hangs, which you might choose to ignore. A fully-blocking app hang is when the main thread is stuck + /// completely, and the app can't render a single frame. + /// A non-fully-blocking app hang is when the app appears stuck to the user, but can still render a few frames. + /// Fully-blocking app hangs are more actionable because the stacktrace shows the exact blocking location on + /// the main thread. Non-fully-blocking app hangs can have a stacktrace that doesn't highlight the exact + /// blocking location, since the main thread isn't completely blocked. + ///
+ /// + /// See https://docs.sentry.io/platforms/apple/configuration/app-hangs/#app-hangs-v2 + /// + public bool EnableAppHangTrackingV2 { get; set; } = true; + /// /// When enabled, the SDK adds breadcrumbs for various system events. /// The default value is true (enabled). diff --git a/src/Sentry/Platforms/Cocoa/SentrySdk.cs b/src/Sentry/Platforms/Cocoa/SentrySdk.cs index c10e22938c..c7b438f12d 100644 --- a/src/Sentry/Platforms/Cocoa/SentrySdk.cs +++ b/src/Sentry/Platforms/Cocoa/SentrySdk.cs @@ -121,6 +121,7 @@ private static void InitSentryCocoaSdk(SentryOptions options) nativeOptions.IdleTimeout = options.Native.IdleTimeout.TotalSeconds; nativeOptions.Dist = options.Distribution; nativeOptions.EnableAppHangTracking = options.Native.EnableAppHangTracking; + nativeOptions.EnableAppHangTrackingV2 = options.Native.EnableAppHangTrackingV2; nativeOptions.EnableAutoBreadcrumbTracking = options.Native.EnableAutoBreadcrumbTracking; nativeOptions.EnableAutoPerformanceTracing = options.Native.EnableAutoPerformanceTracing; nativeOptions.EnableCoreDataTracing = options.Native.EnableCoreDataTracing; From dc3c0dd39fe5f593e9d43ed0d6fbc7a7b42436ce Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Mon, 13 Jan 2025 09:12:47 +1300 Subject: [PATCH 089/363] Add SentryOptions.DisableSentryHttpMessageHandler (#3879) --- CHANGELOG.md | 4 ++ .../SentryHttpMessageHandlerBuilderFilter.cs | 3 +- src/Sentry/BindableSentryOptions.cs | 2 + src/Sentry/SentryHttpMessageHandler.cs | 3 +- src/Sentry/SentryOptions.cs | 8 +++ ...tryHttpMessageHandlerBuilderFilterTests.cs | 54 +++++++++++++++++++ ...piApprovalTests.Run.DotNet8_0.verified.txt | 1 + ...piApprovalTests.Run.DotNet9_0.verified.txt | 1 + .../ApiApprovalTests.Run.Net4_8.verified.txt | 1 + 9 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 test/Sentry.Extensions.Logging.Tests/SentryHttpMessageHandlerBuilderFilterTests.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c055ddd6a..ef60cb5644 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## Unreleased + +- Added `SentryOptions.DisableSentryHttpMessageHandler`. Useful if you're using `OpenTelemetry.Instrumentation.Http` and ending up with duplicate spans. ([#3879](https://github.com/getsentry/sentry-dotnet/pull/3879)) + ## 5.0.1 ### Features diff --git a/src/Sentry.Extensions.Logging/SentryHttpMessageHandlerBuilderFilter.cs b/src/Sentry.Extensions.Logging/SentryHttpMessageHandlerBuilderFilter.cs index f35cad05d9..7cbc7ebc54 100644 --- a/src/Sentry.Extensions.Logging/SentryHttpMessageHandlerBuilderFilter.cs +++ b/src/Sentry.Extensions.Logging/SentryHttpMessageHandlerBuilderFilter.cs @@ -14,7 +14,8 @@ public Action Configure(Action { var hub = _getHub(); - if (!handlerBuilder.AdditionalHandlers.Any(h => h is SentryHttpMessageHandler)) + var enableHandler = hub.GetSentryOptions()?.DisableSentryHttpMessageHandler == false; + if (enableHandler && !handlerBuilder.AdditionalHandlers.Any(h => h is SentryHttpMessageHandler)) { handlerBuilder.AdditionalHandlers.Add( new SentryHttpMessageHandler(hub) diff --git a/src/Sentry/BindableSentryOptions.cs b/src/Sentry/BindableSentryOptions.cs index 46d3ec7711..cd9e5cc8d8 100644 --- a/src/Sentry/BindableSentryOptions.cs +++ b/src/Sentry/BindableSentryOptions.cs @@ -48,6 +48,7 @@ internal partial class BindableSentryOptions public TimeSpan? AutoSessionTrackingInterval { get; set; } public bool? AutoSessionTracking { get; set; } public bool? UseAsyncFileIO { get; set; } + public bool? DisableSentryHttpMessageHandler { get; set; } public bool? JsonPreserveReferences { get; set; } public bool? EnableSpotlight { get; set; } public string? SpotlightUrl { get; set; } @@ -94,6 +95,7 @@ public void ApplyTo(SentryOptions options) options.AutoSessionTrackingInterval = AutoSessionTrackingInterval ?? options.AutoSessionTrackingInterval; options.AutoSessionTracking = AutoSessionTracking ?? options.AutoSessionTracking; options.UseAsyncFileIO = UseAsyncFileIO ?? options.UseAsyncFileIO; + options.DisableSentryHttpMessageHandler = DisableSentryHttpMessageHandler ?? options.DisableSentryHttpMessageHandler; options.JsonPreserveReferences = JsonPreserveReferences ?? options.JsonPreserveReferences; options.EnableSpotlight = EnableSpotlight ?? options.EnableSpotlight; options.SpotlightUrl = SpotlightUrl ?? options.SpotlightUrl; diff --git a/src/Sentry/SentryHttpMessageHandler.cs b/src/Sentry/SentryHttpMessageHandler.cs index db409c9132..f75f958b8a 100644 --- a/src/Sentry/SentryHttpMessageHandler.cs +++ b/src/Sentry/SentryHttpMessageHandler.cs @@ -5,7 +5,8 @@ namespace Sentry; /// -/// Special HTTP message handler that can be used to propagate Sentry headers and other contextual information. +/// Special HTTP message handler that can be used to propagate Sentry headers and other contextual information. Will +/// also create events for failed requests if is enabled. /// public class SentryHttpMessageHandler : SentryMessageHandler { diff --git a/src/Sentry/SentryOptions.cs b/src/Sentry/SentryOptions.cs index 127cc4bf11..be6d9eaa57 100644 --- a/src/Sentry/SentryOptions.cs +++ b/src/Sentry/SentryOptions.cs @@ -1066,6 +1066,14 @@ public StackTraceMode StackTraceMode /// internal Instrumenter Instrumenter { get; set; } = Instrumenter.Sentry; + /// + /// + /// Set to `true` to prevents Sentry from automatically registering . + /// + /// Defaults to `false`. Should be set to `true` when using the OpenTelemetry.Instrumentation.Http. + /// + public bool DisableSentryHttpMessageHandler { get; set; } = false; + /// /// Adds a to be used when serializing or deserializing /// objects to JSON with this SDK. For example, when custom context data might use diff --git a/test/Sentry.Extensions.Logging.Tests/SentryHttpMessageHandlerBuilderFilterTests.cs b/test/Sentry.Extensions.Logging.Tests/SentryHttpMessageHandlerBuilderFilterTests.cs new file mode 100644 index 0000000000..ee3e59fff6 --- /dev/null +++ b/test/Sentry.Extensions.Logging.Tests/SentryHttpMessageHandlerBuilderFilterTests.cs @@ -0,0 +1,54 @@ +using Microsoft.Extensions.Http; + +namespace Sentry.Extensions.Logging.Tests; + +public class SentryHttpMessageHandlerBuilderFilterTests +{ + [SkippableFact] + public void Configure_HandlerEnabled_ShouldAddSentryHttpMessageHandler() + { +#if __ANDROID__ + Skip.If(true, "Can't create proxies for classes without parameterless constructors on Android"); +#endif + + // Arrange + var hub = Substitute.For(); + SentryClientExtensions.SentryOptionsForTestingOnly = new SentryOptions { DisableSentryHttpMessageHandler = false }; + + var filter = new SentryHttpMessageHandlerBuilderFilter(() => hub); + var handlerBuilder = Substitute.For(); + handlerBuilder.AdditionalHandlers.Returns(new List()); + Action next = _ => { }; + + // Act + var configure = filter.Configure(next); + configure(handlerBuilder); + + // Assert + handlerBuilder.AdditionalHandlers.Should().ContainSingle(h => h is SentryHttpMessageHandler); + } + + [SkippableFact] + public void Configure_HandlerDisabled_ShouldNotAddSentryHttpMessageHandler() + { +#if __ANDROID__ + Skip.If(true, "Can't create proxies for classes without parameterless constructors on Android"); +#endif + + // Arrange + var hub = Substitute.For(); + SentryClientExtensions.SentryOptionsForTestingOnly = new SentryOptions { DisableSentryHttpMessageHandler = true }; + + var filter = new SentryHttpMessageHandlerBuilderFilter(() => hub); + var handlerBuilder = Substitute.For(); + handlerBuilder.AdditionalHandlers.Returns(new List()); + Action next = _ => { }; + + // Act + var configure = filter.Configure(next); + configure(handlerBuilder); + + // Assert + handlerBuilder.AdditionalHandlers.Should().NotContain(h => h is SentryHttpMessageHandler); + } +} diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt index 6a7583141c..843b28f7e4 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt @@ -664,6 +664,7 @@ namespace Sentry public Sentry.SentryLevel DiagnosticLevel { get; set; } public Sentry.Extensibility.IDiagnosticLogger? DiagnosticLogger { get; set; } public bool DisableFileWrite { get; set; } + public bool DisableSentryHttpMessageHandler { get; set; } public string? Distribution { get; set; } public string? Dsn { get; set; } public bool EnableScopeSync { get; set; } diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt index 6a7583141c..843b28f7e4 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt @@ -664,6 +664,7 @@ namespace Sentry public Sentry.SentryLevel DiagnosticLevel { get; set; } public Sentry.Extensibility.IDiagnosticLogger? DiagnosticLogger { get; set; } public bool DisableFileWrite { get; set; } + public bool DisableSentryHttpMessageHandler { get; set; } public string? Distribution { get; set; } public string? Dsn { get; set; } public bool EnableScopeSync { get; set; } diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt index 460ab5309d..a14cf232b7 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt @@ -651,6 +651,7 @@ namespace Sentry public Sentry.SentryLevel DiagnosticLevel { get; set; } public Sentry.Extensibility.IDiagnosticLogger? DiagnosticLogger { get; set; } public bool DisableFileWrite { get; set; } + public bool DisableSentryHttpMessageHandler { get; set; } public string? Distribution { get; set; } public string? Dsn { get; set; } public bool EnableScopeSync { get; set; } From 6ec897a0d8763d4e75d30adab8042f84618667dc Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Mon, 13 Jan 2025 21:21:48 +1300 Subject: [PATCH 090/363] Fix changelog (#3884) --- CHANGELOG.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ef60cb5644..72385a3017 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,13 +2,12 @@ ## Unreleased -- Added `SentryOptions.DisableSentryHttpMessageHandler`. Useful if you're using `OpenTelemetry.Instrumentation.Http` and ending up with duplicate spans. ([#3879](https://github.com/getsentry/sentry-dotnet/pull/3879)) - -## 5.0.1 - ### Features - .NET on iOS: Add experimental EnableAppHangTrackingV2 configuration flag to the options binding SDK ([#3877](https://github.com/getsentry/sentry-dotnet/pull/3877)) +- Added `SentryOptions.DisableSentryHttpMessageHandler`. Useful if you're using `OpenTelemetry.Instrumentation.Http` and ending up with duplicate spans. ([#3879](https://github.com/getsentry/sentry-dotnet/pull/3879)) + +## 5.0.1 ### Fixes From 5a8331359278f73853be4445ea5d10da33f1a6be Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Jan 2025 23:35:51 +1300 Subject: [PATCH 091/363] build(deps): bump github/codeql-action from 3.28.0 to 3.28.1 (#3886) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.28.0 to 3.28.1. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/48ab28a6f5dbc2a99bf1e0131198dd8f1df78169...b6a472f63d85b9c78a3ac5e89422239fc15e9b3c) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index ef53f66517..acc9055ab3 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -35,7 +35,7 @@ jobs: uses: ./.github/actions/environment - name: Initialize CodeQL - uses: github/codeql-action/init@48ab28a6f5dbc2a99bf1e0131198dd8f1df78169 # pin@v2 + uses: github/codeql-action/init@b6a472f63d85b9c78a3ac5e89422239fc15e9b3c # pin@v2 with: languages: csharp @@ -49,6 +49,6 @@ jobs: run: dotnet build Sentry-CI-CodeQL.slnf --no-restore --nologo - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@48ab28a6f5dbc2a99bf1e0131198dd8f1df78169 # pin@v2 + uses: github/codeql-action/analyze@b6a472f63d85b9c78a3ac5e89422239fc15e9b3c # pin@v2 with: category: '/language:csharp' From 730ef3a833fa3e8d3bc58f6373420779a707d63b Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Thu, 16 Jan 2025 20:43:33 +1300 Subject: [PATCH 092/363] Prevent package downgrade for Maui packages (#3892) --- .../Sentry.Maui.Device.TestApp.csproj | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/Sentry.Maui.Device.TestApp/Sentry.Maui.Device.TestApp.csproj b/test/Sentry.Maui.Device.TestApp/Sentry.Maui.Device.TestApp.csproj index adfd1f11de..24a25093a6 100644 --- a/test/Sentry.Maui.Device.TestApp/Sentry.Maui.Device.TestApp.csproj +++ b/test/Sentry.Maui.Device.TestApp/Sentry.Maui.Device.TestApp.csproj @@ -4,6 +4,7 @@ $(TargetFrameworks);net8.0-android34.0 $(TargetFrameworks);net8.0-ios17.0 + true From 9a11d779833a82e2ee235c65e75631d8735dfe5f Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Fri, 17 Jan 2025 19:42:53 +1300 Subject: [PATCH 093/363] Removed obsolete verify files (#3897) --- ...piApprovalTests.Run.DotNet6_0.verified.txt | 12 - ...piApprovalTests.Run.DotNet7_0.verified.txt | 12 - ...piApprovalTests.Run.DotNet6_0.verified.txt | 50 - ...piApprovalTests.Run.DotNet7_0.verified.txt | 50 - ...piApprovalTests.Run.DotNet6_0.verified.txt | 83 - ...piApprovalTests.Run.DotNet7_0.verified.txt | 83 - ...ionTests.Versioning.DotNet6_0.verified.txt | 92 - ...ionTests.Versioning.DotNet7_0.verified.txt | 92 - ...piApprovalTests.Run.DotNet6_0.verified.txt | 13 - ...piApprovalTests.Run.DotNet7_0.verified.txt | 13 - ...rTests.LoggingAsync.DotNet6_0.verified.txt | 118 -- ...rTests.LoggingAsync.DotNet7_0.verified.txt | 118 -- ...ests.RecordsEfAsync.DotNet6_0.verified.txt | 124 -- ...ests.RecordsEfAsync.DotNet7_0.verified.txt | 124 -- ...piApprovalTests.Run.DotNet6_0.verified.txt | 39 - ...piApprovalTests.Run.DotNet7_0.verified.txt | 39 - ...piApprovalTests.Run.DotNet6_0.verified.txt | 59 - ...piApprovalTests.Run.DotNet7_0.verified.txt | 59 - ...piApprovalTests.Run.DotNet6_0.verified.txt | 10 - ...piApprovalTests.Run.DotNet7_0.verified.txt | 10 - ...piApprovalTests.Run.DotNet6_0.verified.txt | 14 - ...piApprovalTests.Run.DotNet7_0.verified.txt | 14 - ...piApprovalTests.Run.DotNet7_0.verified.txt | 22 - ...piApprovalTests.Run.DotNet6_0.verified.txt | 81 - ...piApprovalTests.Run.DotNet7_0.verified.txt | 81 - ...grationTests.Simple.DotNet6_0.verified.txt | 106 - ...grationTests.Simple.DotNet7_0.verified.txt | 106 - ...piApprovalTests.Run.DotNet6_0.verified.txt | 46 - ...piApprovalTests.Run.DotNet7_0.verified.txt | 46 - ...grationTests.Simple.DotNet6_0.verified.txt | 280 --- ...grationTests.Simple.DotNet7_0.verified.txt | 280 --- ...piApprovalTests.Run.DotNet6_0.verified.txt | 1812 ----------------- ...piApprovalTests.Run.DotNet7_0.verified.txt | 1812 ----------------- ...ctionEndedAsCrashed.DotNet6_0.verified.txt | 181 -- ...ctionEndedAsCrashed.DotNet7_0.verified.txt | 181 -- ...ctionEndedAsCrashed.DotNet6_0.verified.txt | 190 -- ...ctionEndedAsCrashed.DotNet7_0.verified.txt | 190 -- ...ryInfoTests.WriteTo.DotNet6_0.verified.txt | 19 - ...ryInfoTests.WriteTo.DotNet7_0.verified.txt | 19 - ...orToEventBreadcrumb.DotNet6_0.verified.txt | 15 - ...orToEventBreadcrumb.DotNet7_0.verified.txt | 15 - 41 files changed, 6710 deletions(-) delete mode 100644 test/Sentry.Android.AssemblyReader.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt delete mode 100644 test/Sentry.Android.AssemblyReader.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt delete mode 100644 test/Sentry.AspNetCore.Grpc.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt delete mode 100644 test/Sentry.AspNetCore.Grpc.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt delete mode 100644 test/Sentry.AspNetCore.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt delete mode 100644 test/Sentry.AspNetCore.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt delete mode 100644 test/Sentry.AspNetCore.Tests/WebIntegrationTests.Versioning.DotNet6_0.verified.txt delete mode 100644 test/Sentry.AspNetCore.Tests/WebIntegrationTests.Versioning.DotNet7_0.verified.txt delete mode 100644 test/Sentry.Azure.Functions.Worker.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt delete mode 100644 test/Sentry.Azure.Functions.Worker.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt delete mode 100644 test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.LoggingAsync.DotNet6_0.verified.txt delete mode 100644 test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.LoggingAsync.DotNet7_0.verified.txt delete mode 100644 test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.DotNet6_0.verified.txt delete mode 100644 test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.DotNet7_0.verified.txt delete mode 100644 test/Sentry.EntityFramework.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt delete mode 100644 test/Sentry.EntityFramework.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt delete mode 100644 test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt delete mode 100644 test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt delete mode 100644 test/Sentry.Google.Cloud.Functions.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt delete mode 100644 test/Sentry.Google.Cloud.Functions.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt delete mode 100644 test/Sentry.Log4Net.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt delete mode 100644 test/Sentry.Log4Net.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt delete mode 100644 test/Sentry.Maui.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt delete mode 100644 test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt delete mode 100644 test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt delete mode 100644 test/Sentry.NLog.Tests/IntegrationTests.Simple.DotNet6_0.verified.txt delete mode 100644 test/Sentry.NLog.Tests/IntegrationTests.Simple.DotNet7_0.verified.txt delete mode 100644 test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt delete mode 100644 test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt delete mode 100644 test/Sentry.Serilog.Tests/IntegrationTests.Simple.DotNet6_0.verified.txt delete mode 100644 test/Sentry.Serilog.Tests/IntegrationTests.Simple.DotNet7_0.verified.txt delete mode 100644 test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt delete mode 100644 test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt delete mode 100644 test/Sentry.Tests/HintTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.DotNet6_0.verified.txt delete mode 100644 test/Sentry.Tests/HintTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.DotNet7_0.verified.txt delete mode 100644 test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.DotNet6_0.verified.txt delete mode 100644 test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.DotNet7_0.verified.txt delete mode 100644 test/Sentry.Tests/Internals/MemoryInfoTests.WriteTo.DotNet6_0.verified.txt delete mode 100644 test/Sentry.Tests/Internals/MemoryInfoTests.WriteTo.DotNet7_0.verified.txt delete mode 100644 test/Sentry.Tests/SentryClientTests.CaptureEvent_BeforeEventThrows_ErrorToEventBreadcrumb.DotNet6_0.verified.txt delete mode 100644 test/Sentry.Tests/SentryClientTests.CaptureEvent_BeforeEventThrows_ErrorToEventBreadcrumb.DotNet7_0.verified.txt diff --git a/test/Sentry.Android.AssemblyReader.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt b/test/Sentry.Android.AssemblyReader.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt deleted file mode 100644 index 42c0fac236..0000000000 --- a/test/Sentry.Android.AssemblyReader.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt +++ /dev/null @@ -1,12 +0,0 @@ -namespace Sentry.Android.AssemblyReader -{ - public static class AndroidAssemblyReaderFactory - { - public static Sentry.Android.AssemblyReader.IAndroidAssemblyReader Open(string apkPath, System.Collections.Generic.IList supportedAbis, Sentry.Android.AssemblyReader.DebugLogger? logger = null) { } - } - public delegate void DebugLogger(string message, params object?[] args); - public interface IAndroidAssemblyReader : System.IDisposable - { - System.Reflection.PortableExecutable.PEReader? TryReadAssembly(string name); - } -} \ No newline at end of file diff --git a/test/Sentry.Android.AssemblyReader.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt b/test/Sentry.Android.AssemblyReader.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt deleted file mode 100644 index 42c0fac236..0000000000 --- a/test/Sentry.Android.AssemblyReader.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt +++ /dev/null @@ -1,12 +0,0 @@ -namespace Sentry.Android.AssemblyReader -{ - public static class AndroidAssemblyReaderFactory - { - public static Sentry.Android.AssemblyReader.IAndroidAssemblyReader Open(string apkPath, System.Collections.Generic.IList supportedAbis, Sentry.Android.AssemblyReader.DebugLogger? logger = null) { } - } - public delegate void DebugLogger(string message, params object?[] args); - public interface IAndroidAssemblyReader : System.IDisposable - { - System.Reflection.PortableExecutable.PEReader? TryReadAssembly(string name); - } -} \ No newline at end of file diff --git a/test/Sentry.AspNetCore.Grpc.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt b/test/Sentry.AspNetCore.Grpc.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt deleted file mode 100644 index 60439aee0e..0000000000 --- a/test/Sentry.AspNetCore.Grpc.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt +++ /dev/null @@ -1,50 +0,0 @@ -namespace Sentry.AspNetCore.Grpc -{ - public class DefaultProtobufRequestPayloadExtractor : Sentry.AspNetCore.Grpc.IProtobufRequestPayloadExtractor - { - public DefaultProtobufRequestPayloadExtractor() { } - public Google.Protobuf.IMessage ExtractPayload(Sentry.AspNetCore.Grpc.IProtobufRequest request) - where TRequest : class, Google.Protobuf.IMessage { } - } - public interface IProtobufRequestPayloadExtractor - { - Google.Protobuf.IMessage? ExtractPayload(Sentry.AspNetCore.Grpc.IProtobufRequest request) - where TRequest : class, Google.Protobuf.IMessage; - } - public interface IProtobufRequest - { - long? ContentLength { get; } - TRequest Request { get; } - } - public class ProtobufRequestExtractionDispatcher : Sentry.AspNetCore.Grpc.IProtobufRequestPayloadExtractor - { - public ProtobufRequestExtractionDispatcher(System.Collections.Generic.IEnumerable extractors, Sentry.SentryOptions options, System.Func sizeSwitch) { } - public Google.Protobuf.IMessage? ExtractPayload(Sentry.AspNetCore.Grpc.IProtobufRequest request) - where TRequest : class, Google.Protobuf.IMessage { } - } - public static class ScopeExtensions - { - public static void Populate(this Sentry.Scope scope, Grpc.Core.ServerCallContext context, TRequest? request, Sentry.AspNetCore.SentryAspNetCoreOptions options) - where TRequest : class { } - } - public static class SentryBuilderExtensions - { - public static Sentry.AspNetCore.ISentryBuilder AddGrpc(this Sentry.AspNetCore.ISentryBuilder builder) { } - } - public class SentryGrpcInterceptor : Grpc.Core.Interceptors.Interceptor - { - public SentryGrpcInterceptor(System.Func hubAccessor, Microsoft.Extensions.Options.IOptions options) { } - public override System.Threading.Tasks.Task ClientStreamingServerHandler(Grpc.Core.IAsyncStreamReader requestStream, Grpc.Core.ServerCallContext context, Grpc.Core.ClientStreamingServerMethod continuation) - where TRequest : class - where TResponse : class { } - public override System.Threading.Tasks.Task DuplexStreamingServerHandler(Grpc.Core.IAsyncStreamReader requestStream, Grpc.Core.IServerStreamWriter responseStream, Grpc.Core.ServerCallContext context, Grpc.Core.DuplexStreamingServerMethod continuation) - where TRequest : class - where TResponse : class { } - public override System.Threading.Tasks.Task ServerStreamingServerHandler(TRequest request, Grpc.Core.IServerStreamWriter responseStream, Grpc.Core.ServerCallContext context, Grpc.Core.ServerStreamingServerMethod continuation) - where TRequest : class - where TResponse : class { } - public override System.Threading.Tasks.Task UnaryServerHandler(TRequest request, Grpc.Core.ServerCallContext context, Grpc.Core.UnaryServerMethod continuation) - where TRequest : class - where TResponse : class { } - } -} \ No newline at end of file diff --git a/test/Sentry.AspNetCore.Grpc.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt b/test/Sentry.AspNetCore.Grpc.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt deleted file mode 100644 index 60439aee0e..0000000000 --- a/test/Sentry.AspNetCore.Grpc.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt +++ /dev/null @@ -1,50 +0,0 @@ -namespace Sentry.AspNetCore.Grpc -{ - public class DefaultProtobufRequestPayloadExtractor : Sentry.AspNetCore.Grpc.IProtobufRequestPayloadExtractor - { - public DefaultProtobufRequestPayloadExtractor() { } - public Google.Protobuf.IMessage ExtractPayload(Sentry.AspNetCore.Grpc.IProtobufRequest request) - where TRequest : class, Google.Protobuf.IMessage { } - } - public interface IProtobufRequestPayloadExtractor - { - Google.Protobuf.IMessage? ExtractPayload(Sentry.AspNetCore.Grpc.IProtobufRequest request) - where TRequest : class, Google.Protobuf.IMessage; - } - public interface IProtobufRequest - { - long? ContentLength { get; } - TRequest Request { get; } - } - public class ProtobufRequestExtractionDispatcher : Sentry.AspNetCore.Grpc.IProtobufRequestPayloadExtractor - { - public ProtobufRequestExtractionDispatcher(System.Collections.Generic.IEnumerable extractors, Sentry.SentryOptions options, System.Func sizeSwitch) { } - public Google.Protobuf.IMessage? ExtractPayload(Sentry.AspNetCore.Grpc.IProtobufRequest request) - where TRequest : class, Google.Protobuf.IMessage { } - } - public static class ScopeExtensions - { - public static void Populate(this Sentry.Scope scope, Grpc.Core.ServerCallContext context, TRequest? request, Sentry.AspNetCore.SentryAspNetCoreOptions options) - where TRequest : class { } - } - public static class SentryBuilderExtensions - { - public static Sentry.AspNetCore.ISentryBuilder AddGrpc(this Sentry.AspNetCore.ISentryBuilder builder) { } - } - public class SentryGrpcInterceptor : Grpc.Core.Interceptors.Interceptor - { - public SentryGrpcInterceptor(System.Func hubAccessor, Microsoft.Extensions.Options.IOptions options) { } - public override System.Threading.Tasks.Task ClientStreamingServerHandler(Grpc.Core.IAsyncStreamReader requestStream, Grpc.Core.ServerCallContext context, Grpc.Core.ClientStreamingServerMethod continuation) - where TRequest : class - where TResponse : class { } - public override System.Threading.Tasks.Task DuplexStreamingServerHandler(Grpc.Core.IAsyncStreamReader requestStream, Grpc.Core.IServerStreamWriter responseStream, Grpc.Core.ServerCallContext context, Grpc.Core.DuplexStreamingServerMethod continuation) - where TRequest : class - where TResponse : class { } - public override System.Threading.Tasks.Task ServerStreamingServerHandler(TRequest request, Grpc.Core.IServerStreamWriter responseStream, Grpc.Core.ServerCallContext context, Grpc.Core.ServerStreamingServerMethod continuation) - where TRequest : class - where TResponse : class { } - public override System.Threading.Tasks.Task UnaryServerHandler(TRequest request, Grpc.Core.ServerCallContext context, Grpc.Core.UnaryServerMethod continuation) - where TRequest : class - where TResponse : class { } - } -} \ No newline at end of file diff --git a/test/Sentry.AspNetCore.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt b/test/Sentry.AspNetCore.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt deleted file mode 100644 index 5f9eab70e9..0000000000 --- a/test/Sentry.AspNetCore.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt +++ /dev/null @@ -1,83 +0,0 @@ -namespace Microsoft.AspNetCore.Builder -{ - public static class SentryTracingMiddlewareExtensions - { - public static Microsoft.AspNetCore.Builder.IApplicationBuilder UseSentryTracing(this Microsoft.AspNetCore.Builder.IApplicationBuilder builder) { } - } -} -namespace Microsoft.AspNetCore.Hosting -{ - public static class SentryWebHostBuilderExtensions - { - public static void AddSentryTunneling(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, params string[] hostnames) { } - public static Microsoft.AspNetCore.Hosting.IWebHostBuilder UseSentry(this Microsoft.AspNetCore.Hosting.IWebHostBuilder builder) { } - public static Microsoft.AspNetCore.Hosting.IWebHostBuilder UseSentry(this Microsoft.AspNetCore.Hosting.IWebHostBuilder builder, System.Action? configureSentry) { } - public static Microsoft.AspNetCore.Hosting.IWebHostBuilder UseSentry(this Microsoft.AspNetCore.Hosting.IWebHostBuilder builder, System.Action? configureOptions) { } - public static Microsoft.AspNetCore.Hosting.IWebHostBuilder UseSentry(this Microsoft.AspNetCore.Hosting.IWebHostBuilder builder, System.Action? configureSentry) { } - public static Microsoft.AspNetCore.Hosting.IWebHostBuilder UseSentry(this Microsoft.AspNetCore.Hosting.IWebHostBuilder builder, System.Action? configureOptions) { } - public static Microsoft.AspNetCore.Hosting.IWebHostBuilder UseSentry(this Microsoft.AspNetCore.Hosting.IWebHostBuilder builder, string dsn) { } - public static void UseSentryTunneling(this Microsoft.AspNetCore.Builder.IApplicationBuilder builder, string path = "/tunnel") { } - } -} -namespace Microsoft.Extensions.DependencyInjection -{ - public static class ServiceCollectionExtensions - { - public static Sentry.AspNetCore.ISentryBuilder AddSentry(this Microsoft.Extensions.DependencyInjection.IServiceCollection services) { } - } -} -namespace Sentry.AspNetCore -{ - public interface ISentryBuilder - { - Microsoft.Extensions.DependencyInjection.IServiceCollection Services { get; } - } - public static class SamplingExtensions - { - public static Microsoft.AspNetCore.Http.HttpContext? TryGetHttpContext(this Sentry.TransactionSamplingContext samplingContext) { } - public static string? TryGetHttpMethod(this Sentry.TransactionSamplingContext samplingContext) { } - public static string? TryGetHttpPath(this Sentry.TransactionSamplingContext samplingContext) { } - public static string? TryGetHttpRoute(this Sentry.TransactionSamplingContext samplingContext) { } - } - public static class ScopeExtensions - { - public static void Populate(this Sentry.Scope scope, System.Diagnostics.Activity activity) { } - public static void Populate(this Sentry.Scope scope, Microsoft.AspNetCore.Http.HttpContext context, Sentry.AspNetCore.SentryAspNetCoreOptions options) { } - } - [Microsoft.Extensions.Logging.ProviderAlias("Sentry")] - public class SentryAspNetCoreLoggerProvider : Sentry.Extensions.Logging.SentryLoggerProvider - { - public SentryAspNetCoreLoggerProvider(Microsoft.Extensions.Options.IOptions options, Sentry.IHub hub) { } - } - public class SentryAspNetCoreOptions : Sentry.Extensions.Logging.SentryLoggingOptions - { - public SentryAspNetCoreOptions() { } - public bool AdjustStandardEnvironmentNameCasing { get; set; } - public bool AutoRegisterTracing { get; set; } - public bool CaptureBlockingCalls { get; set; } - public bool FlushOnCompletedRequest { get; set; } - public bool IncludeActivityData { get; set; } - public Sentry.Extensibility.RequestSize MaxRequestBodySize { get; set; } - public Sentry.AspNetCore.TransactionNameProvider? TransactionNameProvider { get; set; } - } - public class SentryAspNetCoreOptionsSetup : Microsoft.Extensions.Options.IConfigureOptions - { - public SentryAspNetCoreOptionsSetup(Microsoft.Extensions.Logging.Configuration.ILoggerProviderConfiguration providerConfiguration) { } - public void Configure(Sentry.AspNetCore.SentryAspNetCoreOptions options) { } - } - public static class SentryBuilderExtensions - { - public static Sentry.AspNetCore.ISentryBuilder AddSentryOptions(this Sentry.AspNetCore.ISentryBuilder builder, System.Action? configureOptions) { } - } - public class SentryStartupFilter : Microsoft.AspNetCore.Hosting.IStartupFilter - { - public SentryStartupFilter() { } - public System.Action Configure(System.Action next) { } - } - public class SentryTunnelMiddleware : Microsoft.AspNetCore.Http.IMiddleware - { - public SentryTunnelMiddleware(string[] allowedHosts) { } - public System.Threading.Tasks.Task InvokeAsync(Microsoft.AspNetCore.Http.HttpContext context, Microsoft.AspNetCore.Http.RequestDelegate next) { } - } - public delegate string? TransactionNameProvider(Microsoft.AspNetCore.Http.HttpContext context); -} \ No newline at end of file diff --git a/test/Sentry.AspNetCore.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt b/test/Sentry.AspNetCore.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt deleted file mode 100644 index 5f9eab70e9..0000000000 --- a/test/Sentry.AspNetCore.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt +++ /dev/null @@ -1,83 +0,0 @@ -namespace Microsoft.AspNetCore.Builder -{ - public static class SentryTracingMiddlewareExtensions - { - public static Microsoft.AspNetCore.Builder.IApplicationBuilder UseSentryTracing(this Microsoft.AspNetCore.Builder.IApplicationBuilder builder) { } - } -} -namespace Microsoft.AspNetCore.Hosting -{ - public static class SentryWebHostBuilderExtensions - { - public static void AddSentryTunneling(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, params string[] hostnames) { } - public static Microsoft.AspNetCore.Hosting.IWebHostBuilder UseSentry(this Microsoft.AspNetCore.Hosting.IWebHostBuilder builder) { } - public static Microsoft.AspNetCore.Hosting.IWebHostBuilder UseSentry(this Microsoft.AspNetCore.Hosting.IWebHostBuilder builder, System.Action? configureSentry) { } - public static Microsoft.AspNetCore.Hosting.IWebHostBuilder UseSentry(this Microsoft.AspNetCore.Hosting.IWebHostBuilder builder, System.Action? configureOptions) { } - public static Microsoft.AspNetCore.Hosting.IWebHostBuilder UseSentry(this Microsoft.AspNetCore.Hosting.IWebHostBuilder builder, System.Action? configureSentry) { } - public static Microsoft.AspNetCore.Hosting.IWebHostBuilder UseSentry(this Microsoft.AspNetCore.Hosting.IWebHostBuilder builder, System.Action? configureOptions) { } - public static Microsoft.AspNetCore.Hosting.IWebHostBuilder UseSentry(this Microsoft.AspNetCore.Hosting.IWebHostBuilder builder, string dsn) { } - public static void UseSentryTunneling(this Microsoft.AspNetCore.Builder.IApplicationBuilder builder, string path = "/tunnel") { } - } -} -namespace Microsoft.Extensions.DependencyInjection -{ - public static class ServiceCollectionExtensions - { - public static Sentry.AspNetCore.ISentryBuilder AddSentry(this Microsoft.Extensions.DependencyInjection.IServiceCollection services) { } - } -} -namespace Sentry.AspNetCore -{ - public interface ISentryBuilder - { - Microsoft.Extensions.DependencyInjection.IServiceCollection Services { get; } - } - public static class SamplingExtensions - { - public static Microsoft.AspNetCore.Http.HttpContext? TryGetHttpContext(this Sentry.TransactionSamplingContext samplingContext) { } - public static string? TryGetHttpMethod(this Sentry.TransactionSamplingContext samplingContext) { } - public static string? TryGetHttpPath(this Sentry.TransactionSamplingContext samplingContext) { } - public static string? TryGetHttpRoute(this Sentry.TransactionSamplingContext samplingContext) { } - } - public static class ScopeExtensions - { - public static void Populate(this Sentry.Scope scope, System.Diagnostics.Activity activity) { } - public static void Populate(this Sentry.Scope scope, Microsoft.AspNetCore.Http.HttpContext context, Sentry.AspNetCore.SentryAspNetCoreOptions options) { } - } - [Microsoft.Extensions.Logging.ProviderAlias("Sentry")] - public class SentryAspNetCoreLoggerProvider : Sentry.Extensions.Logging.SentryLoggerProvider - { - public SentryAspNetCoreLoggerProvider(Microsoft.Extensions.Options.IOptions options, Sentry.IHub hub) { } - } - public class SentryAspNetCoreOptions : Sentry.Extensions.Logging.SentryLoggingOptions - { - public SentryAspNetCoreOptions() { } - public bool AdjustStandardEnvironmentNameCasing { get; set; } - public bool AutoRegisterTracing { get; set; } - public bool CaptureBlockingCalls { get; set; } - public bool FlushOnCompletedRequest { get; set; } - public bool IncludeActivityData { get; set; } - public Sentry.Extensibility.RequestSize MaxRequestBodySize { get; set; } - public Sentry.AspNetCore.TransactionNameProvider? TransactionNameProvider { get; set; } - } - public class SentryAspNetCoreOptionsSetup : Microsoft.Extensions.Options.IConfigureOptions - { - public SentryAspNetCoreOptionsSetup(Microsoft.Extensions.Logging.Configuration.ILoggerProviderConfiguration providerConfiguration) { } - public void Configure(Sentry.AspNetCore.SentryAspNetCoreOptions options) { } - } - public static class SentryBuilderExtensions - { - public static Sentry.AspNetCore.ISentryBuilder AddSentryOptions(this Sentry.AspNetCore.ISentryBuilder builder, System.Action? configureOptions) { } - } - public class SentryStartupFilter : Microsoft.AspNetCore.Hosting.IStartupFilter - { - public SentryStartupFilter() { } - public System.Action Configure(System.Action next) { } - } - public class SentryTunnelMiddleware : Microsoft.AspNetCore.Http.IMiddleware - { - public SentryTunnelMiddleware(string[] allowedHosts) { } - public System.Threading.Tasks.Task InvokeAsync(Microsoft.AspNetCore.Http.HttpContext context, Microsoft.AspNetCore.Http.RequestDelegate next) { } - } - public delegate string? TransactionNameProvider(Microsoft.AspNetCore.Http.HttpContext context); -} \ No newline at end of file diff --git a/test/Sentry.AspNetCore.Tests/WebIntegrationTests.Versioning.DotNet6_0.verified.txt b/test/Sentry.AspNetCore.Tests/WebIntegrationTests.Versioning.DotNet6_0.verified.txt deleted file mode 100644 index 0da3599287..0000000000 --- a/test/Sentry.AspNetCore.Tests/WebIntegrationTests.Versioning.DotNet6_0.verified.txt +++ /dev/null @@ -1,92 +0,0 @@ -{ - result: Hello world, - Payloads: [ - { - Source: { - Origin: auto.http.aspnetcore, - Name: GET /v1.1/Target, - NameSource: Route, - Platform: csharp, - Operation: http.server, - Description: , - Status: Ok, - IsSampled: true, - SampleRate: 1.0, - Request: { - Method: GET, - QueryString: - }, - Contexts: { - trace: { - Operation: http.server, - Origin: auto.http.aspnetcore, - Description: , - Status: Ok, - IsSampled: true - } - }, - User: { - Id: Guid_1, - IpAddress: {{auto}} - }, - Environment: production, - Breadcrumbs: [ - { - Message: Request starting HTTP/1.1 GET http://localhost/v1.1/Target - -, - Data: { - eventId: 1 - }, - Category: Microsoft.AspNetCore.Hosting.Diagnostics - }, - { - Message: Executing endpoint 'Sentry.AspNetCore.Tests.WebIntegrationTests+VersionController.Method (Sentry.AspNetCore.Tests)', - Data: { - eventId: ExecutingEndpoint - }, - Category: Microsoft.AspNetCore.Routing.EndpointMiddleware - }, - { - Message: Route matched with {action = "Method", controller = "Version"}. Executing controller action with signature System.String Method() on controller Sentry.AspNetCore.Tests.WebIntegrationTests+VersionController (Sentry.AspNetCore.Tests)., - Data: { - eventId: ControllerActionExecuting - }, - Category: Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker - }, - { - Message: Executing ObjectResult, writing value of type 'System.String'., - Data: { - eventId: ObjectResultExecuting - }, - Category: Microsoft.AspNetCore.Mvc.Infrastructure.ObjectResultExecutor - }, - { - Message: Executed action Sentry.AspNetCore.Tests.WebIntegrationTests+VersionController.Method, - Data: { - eventId: ActionExecuted - }, - Category: Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker - }, - { - Message: Executed endpoint 'Sentry.AspNetCore.Tests.WebIntegrationTests+VersionController.Method (Sentry.AspNetCore.Tests)', - Data: { - eventId: ExecutedEndpoint - }, - Category: Microsoft.AspNetCore.Routing.EndpointMiddleware - } - ], - Extra: { - http.request.method: GET, - http.response.status_code: 200 - }, - Tags: { - ActionId: Guid_2, - ActionName: Sentry.AspNetCore.Tests.WebIntegrationTests+VersionController.Method (Sentry.AspNetCore.Tests), - route.action: Method, - route.controller: Version, - route.version: 1.1 - }, - IsFinished: true - } - } - ] -} \ No newline at end of file diff --git a/test/Sentry.AspNetCore.Tests/WebIntegrationTests.Versioning.DotNet7_0.verified.txt b/test/Sentry.AspNetCore.Tests/WebIntegrationTests.Versioning.DotNet7_0.verified.txt deleted file mode 100644 index 0da3599287..0000000000 --- a/test/Sentry.AspNetCore.Tests/WebIntegrationTests.Versioning.DotNet7_0.verified.txt +++ /dev/null @@ -1,92 +0,0 @@ -{ - result: Hello world, - Payloads: [ - { - Source: { - Origin: auto.http.aspnetcore, - Name: GET /v1.1/Target, - NameSource: Route, - Platform: csharp, - Operation: http.server, - Description: , - Status: Ok, - IsSampled: true, - SampleRate: 1.0, - Request: { - Method: GET, - QueryString: - }, - Contexts: { - trace: { - Operation: http.server, - Origin: auto.http.aspnetcore, - Description: , - Status: Ok, - IsSampled: true - } - }, - User: { - Id: Guid_1, - IpAddress: {{auto}} - }, - Environment: production, - Breadcrumbs: [ - { - Message: Request starting HTTP/1.1 GET http://localhost/v1.1/Target - -, - Data: { - eventId: 1 - }, - Category: Microsoft.AspNetCore.Hosting.Diagnostics - }, - { - Message: Executing endpoint 'Sentry.AspNetCore.Tests.WebIntegrationTests+VersionController.Method (Sentry.AspNetCore.Tests)', - Data: { - eventId: ExecutingEndpoint - }, - Category: Microsoft.AspNetCore.Routing.EndpointMiddleware - }, - { - Message: Route matched with {action = "Method", controller = "Version"}. Executing controller action with signature System.String Method() on controller Sentry.AspNetCore.Tests.WebIntegrationTests+VersionController (Sentry.AspNetCore.Tests)., - Data: { - eventId: ControllerActionExecuting - }, - Category: Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker - }, - { - Message: Executing ObjectResult, writing value of type 'System.String'., - Data: { - eventId: ObjectResultExecuting - }, - Category: Microsoft.AspNetCore.Mvc.Infrastructure.ObjectResultExecutor - }, - { - Message: Executed action Sentry.AspNetCore.Tests.WebIntegrationTests+VersionController.Method, - Data: { - eventId: ActionExecuted - }, - Category: Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker - }, - { - Message: Executed endpoint 'Sentry.AspNetCore.Tests.WebIntegrationTests+VersionController.Method (Sentry.AspNetCore.Tests)', - Data: { - eventId: ExecutedEndpoint - }, - Category: Microsoft.AspNetCore.Routing.EndpointMiddleware - } - ], - Extra: { - http.request.method: GET, - http.response.status_code: 200 - }, - Tags: { - ActionId: Guid_2, - ActionName: Sentry.AspNetCore.Tests.WebIntegrationTests+VersionController.Method (Sentry.AspNetCore.Tests), - route.action: Method, - route.controller: Version, - route.version: 1.1 - }, - IsFinished: true - } - } - ] -} \ No newline at end of file diff --git a/test/Sentry.Azure.Functions.Worker.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt b/test/Sentry.Azure.Functions.Worker.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt deleted file mode 100644 index f6955863c0..0000000000 --- a/test/Sentry.Azure.Functions.Worker.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt +++ /dev/null @@ -1,13 +0,0 @@ -namespace Sentry.Azure.Functions.Worker -{ - public class SentryAzureFunctionsOptions : Sentry.Extensions.Logging.SentryLoggingOptions - { - public SentryAzureFunctionsOptions() { } - } - public static class SentryFunctionsWorkerApplicationBuilderExtensions - { - public static Microsoft.Azure.Functions.Worker.IFunctionsWorkerApplicationBuilder UseSentry(this Microsoft.Azure.Functions.Worker.IFunctionsWorkerApplicationBuilder builder, Microsoft.Extensions.Hosting.HostBuilderContext context) { } - public static Microsoft.Azure.Functions.Worker.IFunctionsWorkerApplicationBuilder UseSentry(this Microsoft.Azure.Functions.Worker.IFunctionsWorkerApplicationBuilder builder, Microsoft.Extensions.Hosting.HostBuilderContext context, System.Action? optionsConfiguration) { } - public static Microsoft.Azure.Functions.Worker.IFunctionsWorkerApplicationBuilder UseSentry(this Microsoft.Azure.Functions.Worker.IFunctionsWorkerApplicationBuilder builder, Microsoft.Extensions.Hosting.HostBuilderContext context, string dsn) { } - } -} \ No newline at end of file diff --git a/test/Sentry.Azure.Functions.Worker.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt b/test/Sentry.Azure.Functions.Worker.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt deleted file mode 100644 index f6955863c0..0000000000 --- a/test/Sentry.Azure.Functions.Worker.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt +++ /dev/null @@ -1,13 +0,0 @@ -namespace Sentry.Azure.Functions.Worker -{ - public class SentryAzureFunctionsOptions : Sentry.Extensions.Logging.SentryLoggingOptions - { - public SentryAzureFunctionsOptions() { } - } - public static class SentryFunctionsWorkerApplicationBuilderExtensions - { - public static Microsoft.Azure.Functions.Worker.IFunctionsWorkerApplicationBuilder UseSentry(this Microsoft.Azure.Functions.Worker.IFunctionsWorkerApplicationBuilder builder, Microsoft.Extensions.Hosting.HostBuilderContext context) { } - public static Microsoft.Azure.Functions.Worker.IFunctionsWorkerApplicationBuilder UseSentry(this Microsoft.Azure.Functions.Worker.IFunctionsWorkerApplicationBuilder builder, Microsoft.Extensions.Hosting.HostBuilderContext context, System.Action? optionsConfiguration) { } - public static Microsoft.Azure.Functions.Worker.IFunctionsWorkerApplicationBuilder UseSentry(this Microsoft.Azure.Functions.Worker.IFunctionsWorkerApplicationBuilder builder, Microsoft.Extensions.Hosting.HostBuilderContext context, string dsn) { } - } -} \ No newline at end of file diff --git a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.LoggingAsync.DotNet6_0.verified.txt b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.LoggingAsync.DotNet6_0.verified.txt deleted file mode 100644 index 4a7f4cdba6..0000000000 --- a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.LoggingAsync.DotNet6_0.verified.txt +++ /dev/null @@ -1,118 +0,0 @@ -[ - { - Source: { - Message: { - Message: Failed executing DbCommand, - Formatted: -Failed executing DbCommand -SET NOCOUNT ON; -INSERT INTO [TestEntities] ([Property]) -VALUES (@p0); -SELECT [Id] -FROM [TestEntities] -WHERE @@ROWCOUNT = 1 AND [Id] = scope_identity(); - }, - Logger: Microsoft.EntityFrameworkCore.Database.Command, - Platform: csharp, - Level: error, - Request: {}, - Contexts: { - trace: { - Operation: - } - }, - User: { - Id: Guid_1, - IpAddress: {{auto}} - }, - Breadcrumbs: [ - { - Message: Entity Framework Core initialized 'TestDbContext' using provider 'Microsoft.EntityFrameworkCore.SqlServer:' with options: NoTracking , - Data: { - eventId: Microsoft.EntityFrameworkCore.Infrastructure.ContextInitialized - }, - Category: Microsoft.EntityFrameworkCore.Infrastructure - } - ], - Tags: { - commandText: -SET NOCOUNT ON; -INSERT INTO [TestEntities] ([Property]) -VALUES (@p0); -SELECT [Id] -FROM [TestEntities] -WHERE @@ROWCOUNT = 1 AND [Id] = scope_identity();, - commandTimeout: 30, - commandType: Text, - eventId: Microsoft.EntityFrameworkCore.Database.Command.CommandError, - newLine: -, - parameters: @p0='?' (Size = 450) - } - } - }, - { - Source: { - Name: my transaction, - Platform: csharp, - Operation: my operation, - Description: , - Status: Ok, - IsSampled: true, - SampleRate: 1.0, - Request: {}, - Contexts: { - trace: { - Operation: my operation, - Description: , - Status: Ok, - IsSampled: true - } - }, - User: { - Id: Guid_1, - IpAddress: {{auto}} - }, - Spans: [ - { - IsFinished: true, - Operation: db.connection, - Description: SqlListenerTests.verify_LoggingAsync, - Status: Ok, - IsSampled: true, - Extra: { - bytes_sent : 510, - db.connection_id: Guid_2, - db.name: SqlListenerTests.verify_LoggingAsync, - db.operation_id: Guid_3, - db.server: (LocalDb)\SqlListenerTests, - db.system: sql, - rows_sent: 0 - } - }, - { - IsFinished: true, - Operation: db.query, - Description: -SET NOCOUNT ON; -INSERT INTO [TestEntities] ([Property]) -VALUES (@p0); -SELECT [Id] -FROM [TestEntities] -WHERE @@ROWCOUNT = 1 AND [Id] = scope_identity(); - -, - Status: InternalError, - IsSampled: true, - Extra: { - db.connection_id: Guid_2, - db.name: SqlListenerTests.verify_LoggingAsync, - db.operation_id: Guid_4, - db.system: sql - } - } - ], - IsFinished: true - } - } -] \ No newline at end of file diff --git a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.LoggingAsync.DotNet7_0.verified.txt b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.LoggingAsync.DotNet7_0.verified.txt deleted file mode 100644 index 4a7f4cdba6..0000000000 --- a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.LoggingAsync.DotNet7_0.verified.txt +++ /dev/null @@ -1,118 +0,0 @@ -[ - { - Source: { - Message: { - Message: Failed executing DbCommand, - Formatted: -Failed executing DbCommand -SET NOCOUNT ON; -INSERT INTO [TestEntities] ([Property]) -VALUES (@p0); -SELECT [Id] -FROM [TestEntities] -WHERE @@ROWCOUNT = 1 AND [Id] = scope_identity(); - }, - Logger: Microsoft.EntityFrameworkCore.Database.Command, - Platform: csharp, - Level: error, - Request: {}, - Contexts: { - trace: { - Operation: - } - }, - User: { - Id: Guid_1, - IpAddress: {{auto}} - }, - Breadcrumbs: [ - { - Message: Entity Framework Core initialized 'TestDbContext' using provider 'Microsoft.EntityFrameworkCore.SqlServer:' with options: NoTracking , - Data: { - eventId: Microsoft.EntityFrameworkCore.Infrastructure.ContextInitialized - }, - Category: Microsoft.EntityFrameworkCore.Infrastructure - } - ], - Tags: { - commandText: -SET NOCOUNT ON; -INSERT INTO [TestEntities] ([Property]) -VALUES (@p0); -SELECT [Id] -FROM [TestEntities] -WHERE @@ROWCOUNT = 1 AND [Id] = scope_identity();, - commandTimeout: 30, - commandType: Text, - eventId: Microsoft.EntityFrameworkCore.Database.Command.CommandError, - newLine: -, - parameters: @p0='?' (Size = 450) - } - } - }, - { - Source: { - Name: my transaction, - Platform: csharp, - Operation: my operation, - Description: , - Status: Ok, - IsSampled: true, - SampleRate: 1.0, - Request: {}, - Contexts: { - trace: { - Operation: my operation, - Description: , - Status: Ok, - IsSampled: true - } - }, - User: { - Id: Guid_1, - IpAddress: {{auto}} - }, - Spans: [ - { - IsFinished: true, - Operation: db.connection, - Description: SqlListenerTests.verify_LoggingAsync, - Status: Ok, - IsSampled: true, - Extra: { - bytes_sent : 510, - db.connection_id: Guid_2, - db.name: SqlListenerTests.verify_LoggingAsync, - db.operation_id: Guid_3, - db.server: (LocalDb)\SqlListenerTests, - db.system: sql, - rows_sent: 0 - } - }, - { - IsFinished: true, - Operation: db.query, - Description: -SET NOCOUNT ON; -INSERT INTO [TestEntities] ([Property]) -VALUES (@p0); -SELECT [Id] -FROM [TestEntities] -WHERE @@ROWCOUNT = 1 AND [Id] = scope_identity(); - -, - Status: InternalError, - IsSampled: true, - Extra: { - db.connection_id: Guid_2, - db.name: SqlListenerTests.verify_LoggingAsync, - db.operation_id: Guid_4, - db.system: sql - } - } - ], - IsFinished: true - } - } -] \ No newline at end of file diff --git a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.DotNet6_0.verified.txt b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.DotNet6_0.verified.txt deleted file mode 100644 index 4830a78537..0000000000 --- a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.DotNet6_0.verified.txt +++ /dev/null @@ -1,124 +0,0 @@ -[ - { - Source: { - Exception: { - $type: Exception, - Type: Exception, - Message: my exception - }, - Platform: csharp, - SentryExceptions: [ - { - Type: System.Exception, - Value: my exception - } - ], - Level: error, - TransactionName: my transaction, - Request: {}, - Contexts: { - trace: { - Operation: - } - }, - User: { - Id: Guid_1, - IpAddress: {{auto}} - } - } - }, - { - Source: { - Name: my transaction, - Platform: csharp, - Operation: my operation, - Description: , - Status: Ok, - IsSampled: true, - SampleRate: 1.0, - Request: {}, - Contexts: { - trace: { - Operation: my operation, - Description: , - Status: Ok, - IsSampled: true - } - }, - User: { - Id: Guid_1, - IpAddress: {{auto}} - }, - Breadcrumbs: [ - { - Message: my exception, - Category: Exception, - Level: critical - } - ], - Spans: [ - { - IsFinished: true, - Operation: db.connection, - Description: SqlListenerTests.verify_RecordsEfAsync, - Status: Ok, - IsSampled: true, - Extra: { - bytes_received: 304, - bytes_sent : 704, - db.connection_id: Guid_2, - db.name: SqlListenerTests.verify_RecordsEfAsync, - db.operation_id: Guid_3, - db.server: (LocalDb)\SqlListenerTests, - db.system: sql, - rows_sent: 1 - } - }, - { - IsFinished: true, - Operation: db.query, - Description: -SET NOCOUNT ON; -INSERT INTO [TestEntities] ([Property]) -VALUES (@p0); -SELECT [Id] -FROM [TestEntities] -WHERE @@ROWCOUNT = 1 AND [Id] = scope_identity(); - -, - Status: Ok, - IsSampled: true, - Extra: { - db.connection_id: Guid_2, - db.name: SqlListenerTests.verify_RecordsEfAsync, - db.operation_id: Guid_4, - db.system: sql - } - }, - { - IsFinished: true, - Operation: db.query.compile, - Description: 'DbSet()', - Status: Ok, - IsSampled: true - }, - { - IsFinished: true, - Operation: db.query, - Description: -SELECT [t].[Id], [t].[Property] -FROM [TestEntities] AS [t], - Status: Ok, - IsSampled: true, - Extra: { - db.connection_id: Guid_2, - db.name: SqlListenerTests.verify_RecordsEfAsync, - db.operation_id: Guid_5, - db.system: sql - } - } - ], - IsFinished: true - } - } -] \ No newline at end of file diff --git a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.DotNet7_0.verified.txt b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.DotNet7_0.verified.txt deleted file mode 100644 index 4830a78537..0000000000 --- a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.DotNet7_0.verified.txt +++ /dev/null @@ -1,124 +0,0 @@ -[ - { - Source: { - Exception: { - $type: Exception, - Type: Exception, - Message: my exception - }, - Platform: csharp, - SentryExceptions: [ - { - Type: System.Exception, - Value: my exception - } - ], - Level: error, - TransactionName: my transaction, - Request: {}, - Contexts: { - trace: { - Operation: - } - }, - User: { - Id: Guid_1, - IpAddress: {{auto}} - } - } - }, - { - Source: { - Name: my transaction, - Platform: csharp, - Operation: my operation, - Description: , - Status: Ok, - IsSampled: true, - SampleRate: 1.0, - Request: {}, - Contexts: { - trace: { - Operation: my operation, - Description: , - Status: Ok, - IsSampled: true - } - }, - User: { - Id: Guid_1, - IpAddress: {{auto}} - }, - Breadcrumbs: [ - { - Message: my exception, - Category: Exception, - Level: critical - } - ], - Spans: [ - { - IsFinished: true, - Operation: db.connection, - Description: SqlListenerTests.verify_RecordsEfAsync, - Status: Ok, - IsSampled: true, - Extra: { - bytes_received: 304, - bytes_sent : 704, - db.connection_id: Guid_2, - db.name: SqlListenerTests.verify_RecordsEfAsync, - db.operation_id: Guid_3, - db.server: (LocalDb)\SqlListenerTests, - db.system: sql, - rows_sent: 1 - } - }, - { - IsFinished: true, - Operation: db.query, - Description: -SET NOCOUNT ON; -INSERT INTO [TestEntities] ([Property]) -VALUES (@p0); -SELECT [Id] -FROM [TestEntities] -WHERE @@ROWCOUNT = 1 AND [Id] = scope_identity(); - -, - Status: Ok, - IsSampled: true, - Extra: { - db.connection_id: Guid_2, - db.name: SqlListenerTests.verify_RecordsEfAsync, - db.operation_id: Guid_4, - db.system: sql - } - }, - { - IsFinished: true, - Operation: db.query.compile, - Description: 'DbSet()', - Status: Ok, - IsSampled: true - }, - { - IsFinished: true, - Operation: db.query, - Description: -SELECT [t].[Id], [t].[Property] -FROM [TestEntities] AS [t], - Status: Ok, - IsSampled: true, - Extra: { - db.connection_id: Guid_2, - db.name: SqlListenerTests.verify_RecordsEfAsync, - db.operation_id: Guid_5, - db.system: sql - } - } - ], - IsFinished: true - } - } -] \ No newline at end of file diff --git a/test/Sentry.EntityFramework.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt b/test/Sentry.EntityFramework.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt deleted file mode 100644 index 53cafe92cc..0000000000 --- a/test/Sentry.EntityFramework.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt +++ /dev/null @@ -1,39 +0,0 @@ -namespace Sentry.EntityFramework.ErrorProcessors -{ - public class DbConcurrencyExceptionProcessor : Sentry.Extensibility.SentryEventExceptionProcessor - { - public DbConcurrencyExceptionProcessor() { } - protected override void ProcessException(System.Data.DBConcurrencyException exception, Sentry.SentryEvent sentryEvent) { } - } - public class DbEntityValidationExceptionProcessor : Sentry.Extensibility.SentryEventExceptionProcessor - { - public DbEntityValidationExceptionProcessor() { } - protected override void ProcessException(System.Data.Entity.Validation.DbEntityValidationException exception, Sentry.SentryEvent sentryEvent) { } - } -} -namespace Sentry.EntityFramework -{ - public interface IQueryLogger - { - void Log(string text, Sentry.BreadcrumbLevel level = -1); - } - public class SentryCommandInterceptor : System.Data.Entity.Infrastructure.Interception.IDbCommandInterceptor, System.Data.Entity.Infrastructure.Interception.IDbInterceptor - { - public SentryCommandInterceptor(Sentry.EntityFramework.IQueryLogger queryLogger) { } - public virtual void Log(System.Data.Common.DbCommand command, System.Data.Entity.Infrastructure.Interception.DbCommandInterceptionContext interceptionContext) { } - public void NonQueryExecuted(System.Data.Common.DbCommand command, System.Data.Entity.Infrastructure.Interception.DbCommandInterceptionContext interceptionContext) { } - public void NonQueryExecuting(System.Data.Common.DbCommand command, System.Data.Entity.Infrastructure.Interception.DbCommandInterceptionContext interceptionContext) { } - public void ReaderExecuted(System.Data.Common.DbCommand command, System.Data.Entity.Infrastructure.Interception.DbCommandInterceptionContext interceptionContext) { } - public void ReaderExecuting(System.Data.Common.DbCommand command, System.Data.Entity.Infrastructure.Interception.DbCommandInterceptionContext interceptionContext) { } - public void ScalarExecuted(System.Data.Common.DbCommand command, System.Data.Entity.Infrastructure.Interception.DbCommandInterceptionContext interceptionContext) { } - public void ScalarExecuting(System.Data.Common.DbCommand command, System.Data.Entity.Infrastructure.Interception.DbCommandInterceptionContext interceptionContext) { } - } -} -namespace Sentry -{ - public static class SentryOptionsExtensions - { - public static Sentry.SentryOptions AddEntityFramework(this Sentry.SentryOptions sentryOptions) { } - public static void DisableDbInterceptionIntegration(this Sentry.SentryOptions options) { } - } -} \ No newline at end of file diff --git a/test/Sentry.EntityFramework.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt b/test/Sentry.EntityFramework.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt deleted file mode 100644 index 53cafe92cc..0000000000 --- a/test/Sentry.EntityFramework.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt +++ /dev/null @@ -1,39 +0,0 @@ -namespace Sentry.EntityFramework.ErrorProcessors -{ - public class DbConcurrencyExceptionProcessor : Sentry.Extensibility.SentryEventExceptionProcessor - { - public DbConcurrencyExceptionProcessor() { } - protected override void ProcessException(System.Data.DBConcurrencyException exception, Sentry.SentryEvent sentryEvent) { } - } - public class DbEntityValidationExceptionProcessor : Sentry.Extensibility.SentryEventExceptionProcessor - { - public DbEntityValidationExceptionProcessor() { } - protected override void ProcessException(System.Data.Entity.Validation.DbEntityValidationException exception, Sentry.SentryEvent sentryEvent) { } - } -} -namespace Sentry.EntityFramework -{ - public interface IQueryLogger - { - void Log(string text, Sentry.BreadcrumbLevel level = -1); - } - public class SentryCommandInterceptor : System.Data.Entity.Infrastructure.Interception.IDbCommandInterceptor, System.Data.Entity.Infrastructure.Interception.IDbInterceptor - { - public SentryCommandInterceptor(Sentry.EntityFramework.IQueryLogger queryLogger) { } - public virtual void Log(System.Data.Common.DbCommand command, System.Data.Entity.Infrastructure.Interception.DbCommandInterceptionContext interceptionContext) { } - public void NonQueryExecuted(System.Data.Common.DbCommand command, System.Data.Entity.Infrastructure.Interception.DbCommandInterceptionContext interceptionContext) { } - public void NonQueryExecuting(System.Data.Common.DbCommand command, System.Data.Entity.Infrastructure.Interception.DbCommandInterceptionContext interceptionContext) { } - public void ReaderExecuted(System.Data.Common.DbCommand command, System.Data.Entity.Infrastructure.Interception.DbCommandInterceptionContext interceptionContext) { } - public void ReaderExecuting(System.Data.Common.DbCommand command, System.Data.Entity.Infrastructure.Interception.DbCommandInterceptionContext interceptionContext) { } - public void ScalarExecuted(System.Data.Common.DbCommand command, System.Data.Entity.Infrastructure.Interception.DbCommandInterceptionContext interceptionContext) { } - public void ScalarExecuting(System.Data.Common.DbCommand command, System.Data.Entity.Infrastructure.Interception.DbCommandInterceptionContext interceptionContext) { } - } -} -namespace Sentry -{ - public static class SentryOptionsExtensions - { - public static Sentry.SentryOptions AddEntityFramework(this Sentry.SentryOptions sentryOptions) { } - public static void DisableDbInterceptionIntegration(this Sentry.SentryOptions options) { } - } -} \ No newline at end of file diff --git a/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt b/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt deleted file mode 100644 index b438b0af45..0000000000 --- a/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt +++ /dev/null @@ -1,59 +0,0 @@ -namespace Microsoft.Extensions.Logging -{ - public static class LoggingBuilderExtensions - { - public static Microsoft.Extensions.Logging.ILoggingBuilder AddSentry(this Microsoft.Extensions.Logging.ILoggingBuilder builder) { } - public static Microsoft.Extensions.Logging.ILoggingBuilder AddSentry(this Microsoft.Extensions.Logging.ILoggingBuilder builder, System.Action? optionsConfiguration) { } - public static Microsoft.Extensions.Logging.ILoggingBuilder AddSentry(this Microsoft.Extensions.Logging.ILoggingBuilder builder, string dsn) { } - } - public static class SentryLoggerFactoryExtensions - { - public static Microsoft.Extensions.Logging.ILoggerFactory AddSentry(this Microsoft.Extensions.Logging.ILoggerFactory factory, System.Action? optionsConfiguration = null) { } - } -} -namespace Sentry.Extensions.Logging -{ - public class DelegateLogEntryFilter : Sentry.Extensions.Logging.ILogEntryFilter - { - public DelegateLogEntryFilter(System.Func filter) { } - public bool Filter(string categoryName, Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, System.Exception? exception) { } - } - public interface ILogEntryFilter - { - bool Filter(string categoryName, Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, System.Exception? exception); - } - public class MelDiagnosticLogger : Sentry.Extensibility.IDiagnosticLogger - { - public MelDiagnosticLogger(Microsoft.Extensions.Logging.ILogger logger, Sentry.SentryLevel level) { } - public bool IsEnabled(Sentry.SentryLevel level) { } - public void Log(Sentry.SentryLevel logLevel, string message, System.Exception? exception = null, params object?[] args) { } - } - [Microsoft.Extensions.Logging.ProviderAlias("Sentry")] - public class SentryLoggerProvider : Microsoft.Extensions.Logging.ILoggerProvider, System.IDisposable - { - public SentryLoggerProvider(Microsoft.Extensions.Options.IOptions options, Sentry.IHub hub) { } - public Microsoft.Extensions.Logging.ILogger CreateLogger(string categoryName) { } - public void Dispose() { } - } - public class SentryLoggingOptions : Sentry.SentryOptions - { - public SentryLoggingOptions() { } - public bool InitializeSdk { get; set; } - public Microsoft.Extensions.Logging.LogLevel MinimumBreadcrumbLevel { get; set; } - public Microsoft.Extensions.Logging.LogLevel MinimumEventLevel { get; set; } - public void ConfigureScope(System.Action action) { } - } - public static class SentryLoggingOptionsExtensions - { - public static void AddLogEntryFilter(this Sentry.Extensions.Logging.SentryLoggingOptions options, Sentry.Extensions.Logging.ILogEntryFilter filter) { } - public static void AddLogEntryFilter(this Sentry.Extensions.Logging.SentryLoggingOptions options, System.Func filter) { } - } -} -namespace Sentry.Extensions.Logging.Extensions.DependencyInjection -{ - public static class ServiceCollectionExtensions - { - public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddSentry(this Microsoft.Extensions.DependencyInjection.IServiceCollection services) - where TOptions : Sentry.Extensions.Logging.SentryLoggingOptions, new () { } - } -} \ No newline at end of file diff --git a/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt b/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt deleted file mode 100644 index b438b0af45..0000000000 --- a/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt +++ /dev/null @@ -1,59 +0,0 @@ -namespace Microsoft.Extensions.Logging -{ - public static class LoggingBuilderExtensions - { - public static Microsoft.Extensions.Logging.ILoggingBuilder AddSentry(this Microsoft.Extensions.Logging.ILoggingBuilder builder) { } - public static Microsoft.Extensions.Logging.ILoggingBuilder AddSentry(this Microsoft.Extensions.Logging.ILoggingBuilder builder, System.Action? optionsConfiguration) { } - public static Microsoft.Extensions.Logging.ILoggingBuilder AddSentry(this Microsoft.Extensions.Logging.ILoggingBuilder builder, string dsn) { } - } - public static class SentryLoggerFactoryExtensions - { - public static Microsoft.Extensions.Logging.ILoggerFactory AddSentry(this Microsoft.Extensions.Logging.ILoggerFactory factory, System.Action? optionsConfiguration = null) { } - } -} -namespace Sentry.Extensions.Logging -{ - public class DelegateLogEntryFilter : Sentry.Extensions.Logging.ILogEntryFilter - { - public DelegateLogEntryFilter(System.Func filter) { } - public bool Filter(string categoryName, Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, System.Exception? exception) { } - } - public interface ILogEntryFilter - { - bool Filter(string categoryName, Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, System.Exception? exception); - } - public class MelDiagnosticLogger : Sentry.Extensibility.IDiagnosticLogger - { - public MelDiagnosticLogger(Microsoft.Extensions.Logging.ILogger logger, Sentry.SentryLevel level) { } - public bool IsEnabled(Sentry.SentryLevel level) { } - public void Log(Sentry.SentryLevel logLevel, string message, System.Exception? exception = null, params object?[] args) { } - } - [Microsoft.Extensions.Logging.ProviderAlias("Sentry")] - public class SentryLoggerProvider : Microsoft.Extensions.Logging.ILoggerProvider, System.IDisposable - { - public SentryLoggerProvider(Microsoft.Extensions.Options.IOptions options, Sentry.IHub hub) { } - public Microsoft.Extensions.Logging.ILogger CreateLogger(string categoryName) { } - public void Dispose() { } - } - public class SentryLoggingOptions : Sentry.SentryOptions - { - public SentryLoggingOptions() { } - public bool InitializeSdk { get; set; } - public Microsoft.Extensions.Logging.LogLevel MinimumBreadcrumbLevel { get; set; } - public Microsoft.Extensions.Logging.LogLevel MinimumEventLevel { get; set; } - public void ConfigureScope(System.Action action) { } - } - public static class SentryLoggingOptionsExtensions - { - public static void AddLogEntryFilter(this Sentry.Extensions.Logging.SentryLoggingOptions options, Sentry.Extensions.Logging.ILogEntryFilter filter) { } - public static void AddLogEntryFilter(this Sentry.Extensions.Logging.SentryLoggingOptions options, System.Func filter) { } - } -} -namespace Sentry.Extensions.Logging.Extensions.DependencyInjection -{ - public static class ServiceCollectionExtensions - { - public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddSentry(this Microsoft.Extensions.DependencyInjection.IServiceCollection services) - where TOptions : Sentry.Extensions.Logging.SentryLoggingOptions, new () { } - } -} \ No newline at end of file diff --git a/test/Sentry.Google.Cloud.Functions.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt b/test/Sentry.Google.Cloud.Functions.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt deleted file mode 100644 index acc2519014..0000000000 --- a/test/Sentry.Google.Cloud.Functions.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt +++ /dev/null @@ -1,10 +0,0 @@ -namespace Google.Cloud.Functions.Framework -{ - public class SentryStartup : Google.Cloud.Functions.Hosting.FunctionsStartup - { - public SentryStartup() { } - public override void Configure(Microsoft.AspNetCore.Hosting.WebHostBuilderContext context, Microsoft.AspNetCore.Builder.IApplicationBuilder app) { } - public override void ConfigureLogging(Microsoft.AspNetCore.Hosting.WebHostBuilderContext context, Microsoft.Extensions.Logging.ILoggingBuilder logging) { } - public override void ConfigureServices(Microsoft.AspNetCore.Hosting.WebHostBuilderContext context, Microsoft.Extensions.DependencyInjection.IServiceCollection services) { } - } -} \ No newline at end of file diff --git a/test/Sentry.Google.Cloud.Functions.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt b/test/Sentry.Google.Cloud.Functions.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt deleted file mode 100644 index acc2519014..0000000000 --- a/test/Sentry.Google.Cloud.Functions.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt +++ /dev/null @@ -1,10 +0,0 @@ -namespace Google.Cloud.Functions.Framework -{ - public class SentryStartup : Google.Cloud.Functions.Hosting.FunctionsStartup - { - public SentryStartup() { } - public override void Configure(Microsoft.AspNetCore.Hosting.WebHostBuilderContext context, Microsoft.AspNetCore.Builder.IApplicationBuilder app) { } - public override void ConfigureLogging(Microsoft.AspNetCore.Hosting.WebHostBuilderContext context, Microsoft.Extensions.Logging.ILoggingBuilder logging) { } - public override void ConfigureServices(Microsoft.AspNetCore.Hosting.WebHostBuilderContext context, Microsoft.Extensions.DependencyInjection.IServiceCollection services) { } - } -} \ No newline at end of file diff --git a/test/Sentry.Log4Net.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt b/test/Sentry.Log4Net.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt deleted file mode 100644 index 117659776a..0000000000 --- a/test/Sentry.Log4Net.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt +++ /dev/null @@ -1,14 +0,0 @@ -[assembly: System.CLSCompliant(true)] -namespace Sentry.Log4Net -{ - public class SentryAppender : log4net.Appender.AppenderSkeleton - { - public SentryAppender() { } - public string? Dsn { get; set; } - public string? Environment { get; set; } - public log4net.Core.Level? MinimumEventLevel { get; set; } - public bool SendIdentity { get; set; } - protected override void Append(log4net.Core.LoggingEvent loggingEvent) { } - protected override void OnClose() { } - } -} \ No newline at end of file diff --git a/test/Sentry.Log4Net.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt b/test/Sentry.Log4Net.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt deleted file mode 100644 index 117659776a..0000000000 --- a/test/Sentry.Log4Net.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt +++ /dev/null @@ -1,14 +0,0 @@ -[assembly: System.CLSCompliant(true)] -namespace Sentry.Log4Net -{ - public class SentryAppender : log4net.Appender.AppenderSkeleton - { - public SentryAppender() { } - public string? Dsn { get; set; } - public string? Environment { get; set; } - public log4net.Core.Level? MinimumEventLevel { get; set; } - public bool SendIdentity { get; set; } - protected override void Append(log4net.Core.LoggingEvent loggingEvent) { } - protected override void OnClose() { } - } -} \ No newline at end of file diff --git a/test/Sentry.Maui.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt b/test/Sentry.Maui.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt deleted file mode 100644 index 2113579db2..0000000000 --- a/test/Sentry.Maui.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt +++ /dev/null @@ -1,22 +0,0 @@ -namespace Microsoft.Maui.Hosting -{ - public static class SentryMauiAppBuilderExtensions - { - public static Microsoft.Maui.Hosting.MauiAppBuilder UseSentry(this Microsoft.Maui.Hosting.MauiAppBuilder builder) { } - public static Microsoft.Maui.Hosting.MauiAppBuilder UseSentry(this Microsoft.Maui.Hosting.MauiAppBuilder builder, System.Action? configureOptions) { } - public static Microsoft.Maui.Hosting.MauiAppBuilder UseSentry(this Microsoft.Maui.Hosting.MauiAppBuilder builder, string dsn) { } - } -} -namespace Sentry.Maui -{ - public class SentryMauiOptions : Sentry.Extensions.Logging.SentryLoggingOptions - { - public SentryMauiOptions() { } - public bool AttachScreenshot { get; set; } - public bool CreateElementEventsBreadcrumbs { get; set; } - public bool IncludeBackgroundingStateInBreadcrumbs { get; set; } - public bool IncludeTextInBreadcrumbs { get; set; } - public bool IncludeTitleInBreadcrumbs { get; set; } - public void SetBeforeScreenshotCapture(System.Func beforeCapture) { } - } -} \ No newline at end of file diff --git a/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt b/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt deleted file mode 100644 index 00a36bc53b..0000000000 --- a/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt +++ /dev/null @@ -1,81 +0,0 @@ -[assembly: System.CLSCompliant(true)] -namespace NLog -{ - public static class ConfigurationExtensions - { - public static NLog.Config.LoggingConfiguration AddSentry(this NLog.Config.LoggingConfiguration configuration, System.Action? optionsConfig = null) { } - public static NLog.Config.LoggingConfiguration AddSentry(this NLog.Config.LoggingConfiguration configuration, string? dsn, System.Action? optionsConfig = null) { } - public static NLog.Config.LoggingConfiguration AddSentry(this NLog.Config.LoggingConfiguration configuration, string? dsn, string targetName, System.Action? optionsConfig = null) { } - public static void AddTag(this Sentry.NLog.SentryNLogOptions options, string name, NLog.Layouts.Layout layout) { } - } -} -namespace Sentry.NLog -{ - [NLog.Config.NLogConfigurationItem] - public class SentryNLogOptions : Sentry.SentryOptions - { - public SentryNLogOptions() { } - [NLog.Config.NLogConfigurationIgnoreProperty] - public NLog.Layouts.Layout? BreadcrumbCategoryLayout { get; set; } - [NLog.Config.NLogConfigurationIgnoreProperty] - public NLog.Layouts.Layout? BreadcrumbLayout { get; set; } - [NLog.Config.NLogConfigurationIgnoreProperty] - public NLog.Layouts.Layout? DsnLayout { get; set; } - [NLog.Config.NLogConfigurationIgnoreProperty] - public NLog.Layouts.Layout? EnvironmentLayout { get; set; } - public bool IgnoreEventsWithNoException { get; set; } - public bool IncludeEventDataOnBreadcrumbs { get; set; } - public bool IncludeEventPropertiesAsTags { get; set; } - public bool InitializeSdk { get; set; } - [NLog.Config.NLogConfigurationIgnoreProperty] - public NLog.Layouts.Layout? Layout { get; set; } - public NLog.LogLevel? MinimumBreadcrumbLevel { get; set; } - public NLog.LogLevel? MinimumEventLevel { get; set; } - [NLog.Config.NLogConfigurationIgnoreProperty] - public NLog.Layouts.Layout? ReleaseLayout { get; set; } - public int ShutdownTimeoutSeconds { get; set; } - [NLog.Config.NLogConfigurationIgnoreProperty] - public System.Collections.Generic.IList Tags { get; } - [NLog.Config.NLogConfigurationIgnoreProperty] - public Sentry.NLog.SentryNLogUser? User { get; set; } - } - [NLog.Config.NLogConfigurationItem] - public class SentryNLogUser - { - public SentryNLogUser() { } - public NLog.Layouts.Layout? Email { get; set; } - public NLog.Layouts.Layout? Id { get; set; } - public NLog.Layouts.Layout? IpAddress { get; set; } - [NLog.Config.ArrayParameter(typeof(NLog.Targets.TargetPropertyWithContext?), "other")] - public System.Collections.Generic.IList? Other { get; } - public NLog.Layouts.Layout? Segment { get; set; } - public NLog.Layouts.Layout? Username { get; set; } - } - [NLog.Targets.Target("Sentry")] - public sealed class SentryTarget : NLog.Targets.TargetWithContext - { - public SentryTarget() { } - public SentryTarget(Sentry.NLog.SentryNLogOptions options) { } - public NLog.Layouts.Layout? BreadcrumbCategory { get; set; } - public NLog.Layouts.Layout? BreadcrumbLayout { get; set; } - public NLog.Layouts.Layout? Dsn { get; set; } - public NLog.Layouts.Layout? Environment { get; set; } - public int FlushTimeoutSeconds { get; set; } - public bool IgnoreEventsWithNoException { get; set; } - public bool IncludeEventDataOnBreadcrumbs { get; set; } - public bool IncludeEventPropertiesAsTags { get; set; } - public bool InitializeSdk { get; set; } - public string MinimumBreadcrumbLevel { get; set; } - public string MinimumEventLevel { get; set; } - public Sentry.NLog.SentryNLogOptions Options { get; } - public NLog.Layouts.Layout? Release { get; set; } - public int ShutdownTimeoutSeconds { get; set; } - [NLog.Config.ArrayParameter(typeof(NLog.Targets.TargetPropertyWithContext), "tag")] - public System.Collections.Generic.IList Tags { get; } - public Sentry.NLog.SentryNLogUser? User { get; set; } - protected override void CloseTarget() { } - protected override void FlushAsync(NLog.Common.AsyncContinuation asyncContinuation) { } - protected override void InitializeTarget() { } - protected override void Write(NLog.LogEventInfo logEvent) { } - } -} \ No newline at end of file diff --git a/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt b/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt deleted file mode 100644 index 00a36bc53b..0000000000 --- a/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt +++ /dev/null @@ -1,81 +0,0 @@ -[assembly: System.CLSCompliant(true)] -namespace NLog -{ - public static class ConfigurationExtensions - { - public static NLog.Config.LoggingConfiguration AddSentry(this NLog.Config.LoggingConfiguration configuration, System.Action? optionsConfig = null) { } - public static NLog.Config.LoggingConfiguration AddSentry(this NLog.Config.LoggingConfiguration configuration, string? dsn, System.Action? optionsConfig = null) { } - public static NLog.Config.LoggingConfiguration AddSentry(this NLog.Config.LoggingConfiguration configuration, string? dsn, string targetName, System.Action? optionsConfig = null) { } - public static void AddTag(this Sentry.NLog.SentryNLogOptions options, string name, NLog.Layouts.Layout layout) { } - } -} -namespace Sentry.NLog -{ - [NLog.Config.NLogConfigurationItem] - public class SentryNLogOptions : Sentry.SentryOptions - { - public SentryNLogOptions() { } - [NLog.Config.NLogConfigurationIgnoreProperty] - public NLog.Layouts.Layout? BreadcrumbCategoryLayout { get; set; } - [NLog.Config.NLogConfigurationIgnoreProperty] - public NLog.Layouts.Layout? BreadcrumbLayout { get; set; } - [NLog.Config.NLogConfigurationIgnoreProperty] - public NLog.Layouts.Layout? DsnLayout { get; set; } - [NLog.Config.NLogConfigurationIgnoreProperty] - public NLog.Layouts.Layout? EnvironmentLayout { get; set; } - public bool IgnoreEventsWithNoException { get; set; } - public bool IncludeEventDataOnBreadcrumbs { get; set; } - public bool IncludeEventPropertiesAsTags { get; set; } - public bool InitializeSdk { get; set; } - [NLog.Config.NLogConfigurationIgnoreProperty] - public NLog.Layouts.Layout? Layout { get; set; } - public NLog.LogLevel? MinimumBreadcrumbLevel { get; set; } - public NLog.LogLevel? MinimumEventLevel { get; set; } - [NLog.Config.NLogConfigurationIgnoreProperty] - public NLog.Layouts.Layout? ReleaseLayout { get; set; } - public int ShutdownTimeoutSeconds { get; set; } - [NLog.Config.NLogConfigurationIgnoreProperty] - public System.Collections.Generic.IList Tags { get; } - [NLog.Config.NLogConfigurationIgnoreProperty] - public Sentry.NLog.SentryNLogUser? User { get; set; } - } - [NLog.Config.NLogConfigurationItem] - public class SentryNLogUser - { - public SentryNLogUser() { } - public NLog.Layouts.Layout? Email { get; set; } - public NLog.Layouts.Layout? Id { get; set; } - public NLog.Layouts.Layout? IpAddress { get; set; } - [NLog.Config.ArrayParameter(typeof(NLog.Targets.TargetPropertyWithContext?), "other")] - public System.Collections.Generic.IList? Other { get; } - public NLog.Layouts.Layout? Segment { get; set; } - public NLog.Layouts.Layout? Username { get; set; } - } - [NLog.Targets.Target("Sentry")] - public sealed class SentryTarget : NLog.Targets.TargetWithContext - { - public SentryTarget() { } - public SentryTarget(Sentry.NLog.SentryNLogOptions options) { } - public NLog.Layouts.Layout? BreadcrumbCategory { get; set; } - public NLog.Layouts.Layout? BreadcrumbLayout { get; set; } - public NLog.Layouts.Layout? Dsn { get; set; } - public NLog.Layouts.Layout? Environment { get; set; } - public int FlushTimeoutSeconds { get; set; } - public bool IgnoreEventsWithNoException { get; set; } - public bool IncludeEventDataOnBreadcrumbs { get; set; } - public bool IncludeEventPropertiesAsTags { get; set; } - public bool InitializeSdk { get; set; } - public string MinimumBreadcrumbLevel { get; set; } - public string MinimumEventLevel { get; set; } - public Sentry.NLog.SentryNLogOptions Options { get; } - public NLog.Layouts.Layout? Release { get; set; } - public int ShutdownTimeoutSeconds { get; set; } - [NLog.Config.ArrayParameter(typeof(NLog.Targets.TargetPropertyWithContext), "tag")] - public System.Collections.Generic.IList Tags { get; } - public Sentry.NLog.SentryNLogUser? User { get; set; } - protected override void CloseTarget() { } - protected override void FlushAsync(NLog.Common.AsyncContinuation asyncContinuation) { } - protected override void InitializeTarget() { } - protected override void Write(NLog.LogEventInfo logEvent) { } - } -} \ No newline at end of file diff --git a/test/Sentry.NLog.Tests/IntegrationTests.Simple.DotNet6_0.verified.txt b/test/Sentry.NLog.Tests/IntegrationTests.Simple.DotNet6_0.verified.txt deleted file mode 100644 index 0546a4fe28..0000000000 --- a/test/Sentry.NLog.Tests/IntegrationTests.Simple.DotNet6_0.verified.txt +++ /dev/null @@ -1,106 +0,0 @@ -[ - { - Header: { - event_id: Guid_1, - sdk: { - name: sentry.dotnet - }, - trace: { - environment: production, - public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, - release: test-release, - trace_id: Guid_2 - } - }, - Items: [ - { - Header: { - type: event - }, - Payload: { - Source: { - Exception: { - $type: Exception, - Type: Exception, - Message: Exception message - }, - Message: { - Message: message = {arg}, - Formatted: message = "arg value" - }, - Logger: Sentry.NLog.Tests.IntegrationTests, - Platform: csharp, - SentryExceptions: [ - { - Type: System.Exception, - Value: Exception message, - Stacktrace: { - Frames: [ - { - FileName: IntegrationTests.verify.cs, - Function: Task IntegrationTests.Simple(), - Module: null, - LineNumber: 52, - ColumnNumber: 17, - AbsolutePath: {ProjectDirectory}IntegrationTests.verify.cs, - ContextLine: null, - InApp: false, - Package: Sentry.NLog.Tests, Version=SCRUBBED, Culture=SCRUBBED, PublicKeyToken=SCRUBBED, - Platform: null, - ImageAddress: null, - SymbolAddress: null, - InstructionAddress: 2, - AddressMode: rel:0, - FunctionId: 1 - } - ] - }, - Mechanism: { - Type: generic, - Handled: true, - Synthetic: false, - IsExceptionGroup: false - } - } - ], - DebugImages: [ - { - Type: pe_dotnet, - ImageAddress: null, - ImageSize: null, - DebugId: ________-____-____-____-____________-________, - DebugChecksum: ______:________________________________________________________________, - DebugFile: .../Sentry.NLog.Tests.pdb, - CodeId: ______________, - CodeFile: .../Sentry.NLog.Tests.dll - } - ], - Level: error, - Request: {}, - Contexts: { - trace: { - Operation: - } - }, - User: { - Id: myId, - Username: , - Email: , - IpAddress: , - Other: { - mood: joyous - } - }, - Environment: production, - Extra: { - arg: arg value - }, - Tags: { - logger: Sentry.NLog.Tests.IntegrationTests - } - } - } - } - ] - } -] \ No newline at end of file diff --git a/test/Sentry.NLog.Tests/IntegrationTests.Simple.DotNet7_0.verified.txt b/test/Sentry.NLog.Tests/IntegrationTests.Simple.DotNet7_0.verified.txt deleted file mode 100644 index 0546a4fe28..0000000000 --- a/test/Sentry.NLog.Tests/IntegrationTests.Simple.DotNet7_0.verified.txt +++ /dev/null @@ -1,106 +0,0 @@ -[ - { - Header: { - event_id: Guid_1, - sdk: { - name: sentry.dotnet - }, - trace: { - environment: production, - public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, - release: test-release, - trace_id: Guid_2 - } - }, - Items: [ - { - Header: { - type: event - }, - Payload: { - Source: { - Exception: { - $type: Exception, - Type: Exception, - Message: Exception message - }, - Message: { - Message: message = {arg}, - Formatted: message = "arg value" - }, - Logger: Sentry.NLog.Tests.IntegrationTests, - Platform: csharp, - SentryExceptions: [ - { - Type: System.Exception, - Value: Exception message, - Stacktrace: { - Frames: [ - { - FileName: IntegrationTests.verify.cs, - Function: Task IntegrationTests.Simple(), - Module: null, - LineNumber: 52, - ColumnNumber: 17, - AbsolutePath: {ProjectDirectory}IntegrationTests.verify.cs, - ContextLine: null, - InApp: false, - Package: Sentry.NLog.Tests, Version=SCRUBBED, Culture=SCRUBBED, PublicKeyToken=SCRUBBED, - Platform: null, - ImageAddress: null, - SymbolAddress: null, - InstructionAddress: 2, - AddressMode: rel:0, - FunctionId: 1 - } - ] - }, - Mechanism: { - Type: generic, - Handled: true, - Synthetic: false, - IsExceptionGroup: false - } - } - ], - DebugImages: [ - { - Type: pe_dotnet, - ImageAddress: null, - ImageSize: null, - DebugId: ________-____-____-____-____________-________, - DebugChecksum: ______:________________________________________________________________, - DebugFile: .../Sentry.NLog.Tests.pdb, - CodeId: ______________, - CodeFile: .../Sentry.NLog.Tests.dll - } - ], - Level: error, - Request: {}, - Contexts: { - trace: { - Operation: - } - }, - User: { - Id: myId, - Username: , - Email: , - IpAddress: , - Other: { - mood: joyous - } - }, - Environment: production, - Extra: { - arg: arg value - }, - Tags: { - logger: Sentry.NLog.Tests.IntegrationTests - } - } - } - } - ] - } -] \ No newline at end of file diff --git a/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt b/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt deleted file mode 100644 index 893bc8a67e..0000000000 --- a/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt +++ /dev/null @@ -1,46 +0,0 @@ -[assembly: System.CLSCompliant(true)] -namespace Sentry.Serilog -{ - public class SentrySerilogOptions : Sentry.SentryOptions - { - public SentrySerilogOptions() { } - public System.IFormatProvider? FormatProvider { get; set; } - public bool InitializeSdk { get; set; } - public Serilog.Events.LogEventLevel MinimumBreadcrumbLevel { get; set; } - public Serilog.Events.LogEventLevel MinimumEventLevel { get; set; } - public Serilog.Formatting.ITextFormatter? TextFormatter { get; set; } - } -} -namespace Serilog -{ - public static class SentrySinkExtensions - { - public static Serilog.LoggerConfiguration Sentry(this Serilog.Configuration.LoggerSinkConfiguration loggerConfiguration, System.Action configureOptions) { } - public static Serilog.LoggerConfiguration Sentry(this Serilog.Configuration.LoggerSinkConfiguration loggerConfiguration, Serilog.Events.LogEventLevel? minimumEventLevel = default, Serilog.Events.LogEventLevel? minimumBreadcrumbLevel = default, System.IFormatProvider? formatProvider = null, Serilog.Formatting.ITextFormatter? textFormatter = null) { } - public static Serilog.LoggerConfiguration Sentry( - this Serilog.Configuration.LoggerSinkConfiguration loggerConfiguration, - string dsn, - Serilog.Events.LogEventLevel? minimumBreadcrumbLevel = default, - Serilog.Events.LogEventLevel? minimumEventLevel = default, - System.IFormatProvider? formatProvider = null, - Serilog.Formatting.ITextFormatter? textFormatter = null, - bool? sendDefaultPii = default, - bool? isEnvironmentUser = default, - string? serverName = null, - bool? attachStackTrace = default, - int? maxBreadcrumbs = default, - float? sampleRate = default, - string? release = null, - string? environment = null, - int? maxQueueItems = default, - System.TimeSpan? shutdownTimeout = default, - System.Net.DecompressionMethods? decompressionMethods = default, - System.IO.Compression.CompressionLevel? requestBodyCompressionLevel = default, - bool? requestBodyCompressionBuffered = default, - bool? debug = default, - Sentry.SentryLevel? diagnosticLevel = default, - Sentry.ReportAssembliesMode? reportAssembliesMode = default, - Sentry.DeduplicateMode? deduplicateMode = default, - System.Collections.Generic.Dictionary? defaultTags = null) { } - } -} \ No newline at end of file diff --git a/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt b/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt deleted file mode 100644 index 893bc8a67e..0000000000 --- a/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt +++ /dev/null @@ -1,46 +0,0 @@ -[assembly: System.CLSCompliant(true)] -namespace Sentry.Serilog -{ - public class SentrySerilogOptions : Sentry.SentryOptions - { - public SentrySerilogOptions() { } - public System.IFormatProvider? FormatProvider { get; set; } - public bool InitializeSdk { get; set; } - public Serilog.Events.LogEventLevel MinimumBreadcrumbLevel { get; set; } - public Serilog.Events.LogEventLevel MinimumEventLevel { get; set; } - public Serilog.Formatting.ITextFormatter? TextFormatter { get; set; } - } -} -namespace Serilog -{ - public static class SentrySinkExtensions - { - public static Serilog.LoggerConfiguration Sentry(this Serilog.Configuration.LoggerSinkConfiguration loggerConfiguration, System.Action configureOptions) { } - public static Serilog.LoggerConfiguration Sentry(this Serilog.Configuration.LoggerSinkConfiguration loggerConfiguration, Serilog.Events.LogEventLevel? minimumEventLevel = default, Serilog.Events.LogEventLevel? minimumBreadcrumbLevel = default, System.IFormatProvider? formatProvider = null, Serilog.Formatting.ITextFormatter? textFormatter = null) { } - public static Serilog.LoggerConfiguration Sentry( - this Serilog.Configuration.LoggerSinkConfiguration loggerConfiguration, - string dsn, - Serilog.Events.LogEventLevel? minimumBreadcrumbLevel = default, - Serilog.Events.LogEventLevel? minimumEventLevel = default, - System.IFormatProvider? formatProvider = null, - Serilog.Formatting.ITextFormatter? textFormatter = null, - bool? sendDefaultPii = default, - bool? isEnvironmentUser = default, - string? serverName = null, - bool? attachStackTrace = default, - int? maxBreadcrumbs = default, - float? sampleRate = default, - string? release = null, - string? environment = null, - int? maxQueueItems = default, - System.TimeSpan? shutdownTimeout = default, - System.Net.DecompressionMethods? decompressionMethods = default, - System.IO.Compression.CompressionLevel? requestBodyCompressionLevel = default, - bool? requestBodyCompressionBuffered = default, - bool? debug = default, - Sentry.SentryLevel? diagnosticLevel = default, - Sentry.ReportAssembliesMode? reportAssembliesMode = default, - Sentry.DeduplicateMode? deduplicateMode = default, - System.Collections.Generic.Dictionary? defaultTags = null) { } - } -} \ No newline at end of file diff --git a/test/Sentry.Serilog.Tests/IntegrationTests.Simple.DotNet6_0.verified.txt b/test/Sentry.Serilog.Tests/IntegrationTests.Simple.DotNet6_0.verified.txt deleted file mode 100644 index 1f894bd0ef..0000000000 --- a/test/Sentry.Serilog.Tests/IntegrationTests.Simple.DotNet6_0.verified.txt +++ /dev/null @@ -1,280 +0,0 @@ -[ - { - Header: { - event_id: Guid_1, - sdk: { - name: sentry.dotnet - }, - trace: { - environment: production, - public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, - release: test-release, - trace_id: Guid_2 - } - }, - Items: [ - { - Header: { - type: event - }, - Payload: { - Source: { - Message: { - Message: Debug message stored as breadcrumb., - Formatted: [42] Debug message stored as breadcrumb. - }, - Platform: csharp, - ServerName: TheMachineName, - Level: debug, - Request: {}, - Contexts: { - trace: { - Operation: - } - }, - User: { - Id: Guid_3, - Username: TheUserName, - IpAddress: {{auto}} - }, - Environment: production, - Extra: { - inventory: { SmallPotion = 3, BigPotion = 0, CheeseWheels = 512 }, - MyTaskId: 42 - } - } - } - } - ] - }, - { - Header: { - event_id: Guid_4, - sdk: { - name: sentry.dotnet - }, - trace: { - environment: production, - public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, - release: test-release, - trace_id: Guid_2 - } - }, - Items: [ - { - Header: { - type: event - }, - Payload: { - Source: { - Message: { - Message: Message with a different MyTaskId, - Formatted: [65] Message with a different MyTaskId - }, - Platform: csharp, - ServerName: TheMachineName, - Level: debug, - Request: {}, - Contexts: { - trace: { - Operation: - } - }, - User: { - Id: Guid_3, - Username: TheUserName, - IpAddress: {{auto}} - }, - Environment: production, - Breadcrumbs: [ - { - Message: [42] Debug message stored as breadcrumb., - Level: debug - } - ], - Extra: { - inventory: { SmallPotion = 3, BigPotion = 0, CheeseWheels = 512 }, - MyTaskId: 65 - } - } - } - } - ] - }, - { - Header: { - event_id: Guid_5, - sdk: { - name: sentry.dotnet - }, - trace: { - environment: production, - public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, - release: test-release, - trace_id: Guid_2 - } - }, - Items: [ - { - Header: { - type: event - }, - Payload: { - Source: { - Message: { - Message: Some event that includes the previous breadcrumbs, - Formatted: [42] Some event that includes the previous breadcrumbs - }, - Platform: csharp, - ServerName: TheMachineName, - Level: error, - Request: {}, - Contexts: { - trace: { - Operation: - } - }, - User: { - Id: Guid_3, - Username: TheUserName, - IpAddress: {{auto}} - }, - Environment: production, - Breadcrumbs: [ - { - Message: [42] Debug message stored as breadcrumb., - Level: debug - }, - { - Message: [65] Message with a different MyTaskId, - Level: debug - } - ], - Extra: { - inventory: { SmallPotion = 3, BigPotion = 0, CheeseWheels = 512 }, - MyTaskId: 42 - } - } - } - } - ] - }, - { - Header: { - event_id: Guid_6, - sdk: { - name: sentry.dotnet - }, - trace: { - environment: production, - public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, - release: test-release, - trace_id: Guid_2 - } - }, - Items: [ - { - Header: { - type: event - }, - Payload: { - Source: { - Exception: { - $type: Exception, - Type: Exception, - Message: Exception message, - Data: { - details: Do work always throws. - } - }, - Message: { - Message: Error: with exception, - Formatted: [42] Error: with exception - }, - Platform: csharp, - ServerName: TheMachineName, - SentryExceptions: [ - { - Type: System.Exception, - Value: Exception message, - Stacktrace: { - Frames: [ - { - FileName: IntegrationTests.verify.cs, - Function: Task IntegrationTests.Simple(), - Module: null, - LineNumber: 47, - ColumnNumber: 17, - AbsolutePath: {ProjectDirectory}IntegrationTests.verify.cs, - ContextLine: null, - InApp: false, - Package: Sentry.Serilog.Tests, Version=SCRUBBED, Culture=SCRUBBED, PublicKeyToken=SCRUBBED, - Platform: null, - ImageAddress: null, - SymbolAddress: null, - InstructionAddress: 2, - AddressMode: rel:0, - FunctionId: 1 - } - ] - }, - Mechanism: { - Type: generic, - Handled: true, - Synthetic: false, - IsExceptionGroup: false, - Data: { - details: Do work always throws. - } - } - } - ], - DebugImages: [ - { - Type: pe_dotnet, - ImageAddress: null, - ImageSize: null, - DebugId: ________-____-____-____-____________-________, - DebugChecksum: ______:________________________________________________________________, - DebugFile: .../Sentry.Serilog.Tests.pdb, - CodeId: ______________, - CodeFile: .../Sentry.Serilog.Tests.dll - } - ], - Level: fatal, - Request: {}, - Contexts: { - trace: { - Operation: - } - }, - User: { - Id: Guid_3, - Username: TheUserName, - IpAddress: {{auto}} - }, - Environment: production, - Breadcrumbs: [ - { - Message: [42] Debug message stored as breadcrumb., - Level: debug - }, - { - Message: [65] Message with a different MyTaskId, - Level: debug - }, - { - Message: [42] Some event that includes the previous breadcrumbs, - Level: error - } - ], - Extra: { - inventory: { SmallPotion = 3, BigPotion = 0, CheeseWheels = 512 }, - MyTaskId: 42 - } - } - } - } - ] - } -] \ No newline at end of file diff --git a/test/Sentry.Serilog.Tests/IntegrationTests.Simple.DotNet7_0.verified.txt b/test/Sentry.Serilog.Tests/IntegrationTests.Simple.DotNet7_0.verified.txt deleted file mode 100644 index 1f894bd0ef..0000000000 --- a/test/Sentry.Serilog.Tests/IntegrationTests.Simple.DotNet7_0.verified.txt +++ /dev/null @@ -1,280 +0,0 @@ -[ - { - Header: { - event_id: Guid_1, - sdk: { - name: sentry.dotnet - }, - trace: { - environment: production, - public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, - release: test-release, - trace_id: Guid_2 - } - }, - Items: [ - { - Header: { - type: event - }, - Payload: { - Source: { - Message: { - Message: Debug message stored as breadcrumb., - Formatted: [42] Debug message stored as breadcrumb. - }, - Platform: csharp, - ServerName: TheMachineName, - Level: debug, - Request: {}, - Contexts: { - trace: { - Operation: - } - }, - User: { - Id: Guid_3, - Username: TheUserName, - IpAddress: {{auto}} - }, - Environment: production, - Extra: { - inventory: { SmallPotion = 3, BigPotion = 0, CheeseWheels = 512 }, - MyTaskId: 42 - } - } - } - } - ] - }, - { - Header: { - event_id: Guid_4, - sdk: { - name: sentry.dotnet - }, - trace: { - environment: production, - public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, - release: test-release, - trace_id: Guid_2 - } - }, - Items: [ - { - Header: { - type: event - }, - Payload: { - Source: { - Message: { - Message: Message with a different MyTaskId, - Formatted: [65] Message with a different MyTaskId - }, - Platform: csharp, - ServerName: TheMachineName, - Level: debug, - Request: {}, - Contexts: { - trace: { - Operation: - } - }, - User: { - Id: Guid_3, - Username: TheUserName, - IpAddress: {{auto}} - }, - Environment: production, - Breadcrumbs: [ - { - Message: [42] Debug message stored as breadcrumb., - Level: debug - } - ], - Extra: { - inventory: { SmallPotion = 3, BigPotion = 0, CheeseWheels = 512 }, - MyTaskId: 65 - } - } - } - } - ] - }, - { - Header: { - event_id: Guid_5, - sdk: { - name: sentry.dotnet - }, - trace: { - environment: production, - public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, - release: test-release, - trace_id: Guid_2 - } - }, - Items: [ - { - Header: { - type: event - }, - Payload: { - Source: { - Message: { - Message: Some event that includes the previous breadcrumbs, - Formatted: [42] Some event that includes the previous breadcrumbs - }, - Platform: csharp, - ServerName: TheMachineName, - Level: error, - Request: {}, - Contexts: { - trace: { - Operation: - } - }, - User: { - Id: Guid_3, - Username: TheUserName, - IpAddress: {{auto}} - }, - Environment: production, - Breadcrumbs: [ - { - Message: [42] Debug message stored as breadcrumb., - Level: debug - }, - { - Message: [65] Message with a different MyTaskId, - Level: debug - } - ], - Extra: { - inventory: { SmallPotion = 3, BigPotion = 0, CheeseWheels = 512 }, - MyTaskId: 42 - } - } - } - } - ] - }, - { - Header: { - event_id: Guid_6, - sdk: { - name: sentry.dotnet - }, - trace: { - environment: production, - public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, - release: test-release, - trace_id: Guid_2 - } - }, - Items: [ - { - Header: { - type: event - }, - Payload: { - Source: { - Exception: { - $type: Exception, - Type: Exception, - Message: Exception message, - Data: { - details: Do work always throws. - } - }, - Message: { - Message: Error: with exception, - Formatted: [42] Error: with exception - }, - Platform: csharp, - ServerName: TheMachineName, - SentryExceptions: [ - { - Type: System.Exception, - Value: Exception message, - Stacktrace: { - Frames: [ - { - FileName: IntegrationTests.verify.cs, - Function: Task IntegrationTests.Simple(), - Module: null, - LineNumber: 47, - ColumnNumber: 17, - AbsolutePath: {ProjectDirectory}IntegrationTests.verify.cs, - ContextLine: null, - InApp: false, - Package: Sentry.Serilog.Tests, Version=SCRUBBED, Culture=SCRUBBED, PublicKeyToken=SCRUBBED, - Platform: null, - ImageAddress: null, - SymbolAddress: null, - InstructionAddress: 2, - AddressMode: rel:0, - FunctionId: 1 - } - ] - }, - Mechanism: { - Type: generic, - Handled: true, - Synthetic: false, - IsExceptionGroup: false, - Data: { - details: Do work always throws. - } - } - } - ], - DebugImages: [ - { - Type: pe_dotnet, - ImageAddress: null, - ImageSize: null, - DebugId: ________-____-____-____-____________-________, - DebugChecksum: ______:________________________________________________________________, - DebugFile: .../Sentry.Serilog.Tests.pdb, - CodeId: ______________, - CodeFile: .../Sentry.Serilog.Tests.dll - } - ], - Level: fatal, - Request: {}, - Contexts: { - trace: { - Operation: - } - }, - User: { - Id: Guid_3, - Username: TheUserName, - IpAddress: {{auto}} - }, - Environment: production, - Breadcrumbs: [ - { - Message: [42] Debug message stored as breadcrumb., - Level: debug - }, - { - Message: [65] Message with a different MyTaskId, - Level: debug - }, - { - Message: [42] Some event that includes the previous breadcrumbs, - Level: error - } - ], - Extra: { - inventory: { SmallPotion = 3, BigPotion = 0, CheeseWheels = 512 }, - MyTaskId: 42 - } - } - } - } - ] - } -] \ No newline at end of file diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt deleted file mode 100644 index bb10750b76..0000000000 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt +++ /dev/null @@ -1,1812 +0,0 @@ -[assembly: System.CLSCompliant(true)] -namespace Sentry -{ - public enum AttachmentType - { - Default = 0, - Minidump = 1, - AppleCrashReport = 2, - UnrealContext = 3, - UnrealLogs = 4, - ViewHierarchy = 5, - } - public class BaggageHeader - { - public override string ToString() { } - } - [System.Diagnostics.DebuggerDisplay("Message: {Message}, Type: {Type}")] - public sealed class Breadcrumb : Sentry.ISentryJsonSerializable - { - public Breadcrumb(string message, string type, System.Collections.Generic.IReadOnlyDictionary? data = null, string? category = null, Sentry.BreadcrumbLevel level = 0) { } - public string? Category { get; } - public System.Collections.Generic.IReadOnlyDictionary? Data { get; } - public Sentry.BreadcrumbLevel Level { get; } - public string? Message { get; } - public System.DateTimeOffset Timestamp { get; } - public string? Type { get; } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } - public static Sentry.Breadcrumb FromJson(System.Text.Json.JsonElement json) { } - } - public enum BreadcrumbLevel - { - [System.Runtime.Serialization.EnumMember(Value="debug")] - Debug = -1, - [System.Runtime.Serialization.EnumMember(Value="info")] - Info = 0, - [System.Runtime.Serialization.EnumMember(Value="warning")] - Warning = 1, - [System.Runtime.Serialization.EnumMember(Value="error")] - Error = 2, - [System.Runtime.Serialization.EnumMember(Value="critical")] - Critical = 3, - } - public static class BuiltInSystemDiagnosticsMeters - { - public static readonly Sentry.StringOrRegex MicrosoftAspNetCoreDiagnostics; - public static readonly Sentry.StringOrRegex MicrosoftAspNetCoreHeaderParsing; - public static readonly Sentry.StringOrRegex MicrosoftAspNetCoreHosting; - public static readonly Sentry.StringOrRegex MicrosoftAspNetCoreHttpConnections; - public static readonly Sentry.StringOrRegex MicrosoftAspNetCoreRateLimiting; - public static readonly Sentry.StringOrRegex MicrosoftAspNetCoreRouting; - public static readonly Sentry.StringOrRegex MicrosoftAspNetCoreServerKestrel; - public static readonly Sentry.StringOrRegex MicrosoftExtensionsDiagnosticsHealthChecks; - public static readonly Sentry.StringOrRegex MicrosoftExtensionsDiagnosticsResourceMonitoring; - public static readonly Sentry.StringOrRegex OpenTelemetryInstrumentationRuntime; - public static readonly Sentry.StringOrRegex SystemNetHttp; - public static readonly Sentry.StringOrRegex SystemNetNameResolution; - public static System.Collections.Generic.IList All { get; } - } - public class ByteAttachmentContent : Sentry.IAttachmentContent - { - public ByteAttachmentContent(byte[] bytes) { } - public System.IO.Stream GetStream() { } - } - public enum CheckInStatus - { - InProgress = 0, - Ok = 1, - Error = 2, - } - [System.Obsolete("WARNING: This method deliberately causes a crash, and should not be used in a rea" + - "l application.")] - public enum CrashType - { - Managed = 0, - ManagedBackgroundThread = 1, - } - public class Debouncer - { - public static Sentry.Debouncer PerApplicationLifetime(int eventMaximum = 1, System.TimeSpan? cooldown = default) { } - public static Sentry.Debouncer PerDay(int eventMaximum = 1, System.TimeSpan? cooldown = default) { } - public static Sentry.Debouncer PerHour(int eventMaximum = 1, System.TimeSpan? cooldown = default) { } - public static Sentry.Debouncer PerMinute(int eventMaximum = 1, System.TimeSpan? cooldown = default) { } - } - [System.Flags] - public enum DeduplicateMode - { - SameEvent = 1, - SameExceptionInstance = 2, - InnerException = 4, - AggregateException = 8, - All = 2147483647, - } - public class DefaultSentryScopeStateProcessor : Sentry.ISentryScopeStateProcessor - { - public DefaultSentryScopeStateProcessor() { } - public void Apply(Sentry.Scope scope, object state) { } - } - [System.AttributeUsage(System.AttributeTargets.Assembly)] - public class DsnAttribute : System.Attribute - { - public DsnAttribute(string dsn) { } - public string Dsn { get; } - } - public static class EventLikeExtensions - { - public static void AddBreadcrumb(this Sentry.IEventLike eventLike, string message, string? category, string? type, System.ValueTuple? dataPair = default, Sentry.BreadcrumbLevel level = 0) { } - public static void AddBreadcrumb(this Sentry.IEventLike eventLike, string message, string? category = null, string? type = null, System.Collections.Generic.IReadOnlyDictionary? data = null, Sentry.BreadcrumbLevel level = 0) { } - public static void AddBreadcrumb(this Sentry.IEventLike eventLike, System.DateTimeOffset? timestamp, string message, string? category = null, string? type = null, System.Collections.Generic.IReadOnlyDictionary? data = null, Sentry.BreadcrumbLevel level = 0) { } - public static bool HasUser(this Sentry.IEventLike eventLike) { } - public static void SetFingerprint(this Sentry.IEventLike eventLike, System.Collections.Generic.IEnumerable fingerprint) { } - public static void SetFingerprint(this Sentry.IEventLike eventLike, params string[] fingerprint) { } - } - public class ExperimentalMetricsOptions - { - public ExperimentalMetricsOptions() { } - public System.Collections.Generic.IList CaptureSystemDiagnosticsInstruments { get; set; } - public System.Collections.Generic.IList CaptureSystemDiagnosticsMeters { get; set; } - public bool EnableCodeLocations { get; set; } - } - public class FileAttachmentContent : Sentry.IAttachmentContent - { - public FileAttachmentContent(string filePath) { } - public FileAttachmentContent(string filePath, bool readFileAsynchronously) { } - public System.IO.Stream GetStream() { } - } - public static class HasExtraExtensions - { - public static void SetExtras(this Sentry.IHasExtra hasExtra, System.Collections.Generic.IEnumerable> values) { } - } - public static class HasTagsExtensions - { - public static void SetTags(this Sentry.IHasTags hasTags, System.Collections.Generic.IEnumerable> tags) { } - } - public delegate bool HeapDumpTrigger(long usedMemory, long totalMemory); - public static class HintTypes - { - public const string HttpResponseMessage = "http-response-message"; - } - public readonly struct HttpStatusCodeRange : System.IEquatable - { - public HttpStatusCodeRange(int statusCode) { } - public HttpStatusCodeRange(int start, int end) { } - public int End { get; init; } - public int Start { get; init; } - public bool Contains(int statusCode) { } - public bool Contains(System.Net.HttpStatusCode statusCode) { } - public static Sentry.HttpStatusCodeRange op_Implicit(int statusCode) { } - public static Sentry.HttpStatusCodeRange op_Implicit(System.Net.HttpStatusCode statusCode) { } - public static Sentry.HttpStatusCodeRange op_Implicit([System.Runtime.CompilerServices.TupleElementNames(new string[] { - "Start", - "End"})] System.ValueTuple range) { } - public static Sentry.HttpStatusCodeRange op_Implicit([System.Runtime.CompilerServices.TupleElementNames(new string[] { - "start", - "end"})] System.ValueTuple range) { } - } - public static class HubExtensions - { - public static void AddBreadcrumb(this Sentry.IHub hub, Sentry.Breadcrumb breadcrumb, Sentry.SentryHint? hint = null) { } - public static void AddBreadcrumb(this Sentry.IHub hub, string message, string? category = null, string? type = null, System.Collections.Generic.IDictionary? data = null, Sentry.BreadcrumbLevel level = 0) { } - public static void AddBreadcrumb(this Sentry.IHub hub, Sentry.Infrastructure.ISystemClock? clock, string message, string? category = null, string? type = null, System.Collections.Generic.IDictionary? data = null, Sentry.BreadcrumbLevel level = 0) { } - public static Sentry.SentryId CaptureException(this Sentry.IHub hub, System.Exception ex, System.Action configureScope) { } - public static Sentry.SentryId CaptureMessage(this Sentry.IHub hub, string message, System.Action configureScope, Sentry.SentryLevel level = 1) { } - public static void LockScope(this Sentry.IHub hub) { } - public static System.IDisposable PushAndLockScope(this Sentry.IHub hub) { } - public static Sentry.ITransactionTracer StartTransaction(this Sentry.IHub hub, Sentry.ITransactionContext context) { } - public static Sentry.ITransactionTracer StartTransaction(this Sentry.IHub hub, string name, string operation) { } - public static Sentry.ITransactionTracer StartTransaction(this Sentry.IHub hub, string name, string operation, Sentry.SentryTraceHeader traceHeader) { } - public static Sentry.ITransactionTracer StartTransaction(this Sentry.IHub hub, string name, string operation, string? description) { } - public static void UnlockScope(this Sentry.IHub hub) { } - } - public interface IAttachmentContent - { - System.IO.Stream GetStream(); - } - public interface IEventLike : Sentry.IHasExtra, Sentry.IHasTags - { - System.Collections.Generic.IReadOnlyCollection Breadcrumbs { get; } - Sentry.SentryContexts Contexts { get; set; } - string? Distribution { get; set; } - string? Environment { get; set; } - System.Collections.Generic.IReadOnlyList Fingerprint { get; set; } - Sentry.SentryLevel? Level { get; set; } - string? Release { get; set; } - Sentry.SentryRequest Request { get; set; } - Sentry.SdkVersion Sdk { get; } - string? TransactionName { get; set; } - Sentry.SentryUser User { get; set; } - void AddBreadcrumb(Sentry.Breadcrumb breadcrumb); - } - public interface IHasExtra - { - System.Collections.Generic.IReadOnlyDictionary Extra { get; } - void SetExtra(string key, object? value); - } - public interface IHasTags - { - System.Collections.Generic.IReadOnlyDictionary Tags { get; } - void SetTag(string key, string value); - void UnsetTag(string key); - } - public interface IHub : Sentry.ISentryClient, Sentry.ISentryScopeManager - { - Sentry.SentryId LastEventId { get; } - void BindException(System.Exception exception, Sentry.ISpan span); - Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, System.Action configureScope); - Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, Sentry.SentryHint? hint, System.Action configureScope); - Sentry.TransactionContext ContinueTrace(Sentry.SentryTraceHeader? traceHeader, Sentry.BaggageHeader? baggageHeader, string? name = null, string? operation = null); - Sentry.TransactionContext ContinueTrace(string? traceHeader, string? baggageHeader, string? name = null, string? operation = null); - void EndSession(Sentry.SessionEndStatus status = 0); - Sentry.BaggageHeader? GetBaggage(); - Sentry.ISpan? GetSpan(); - Sentry.SentryTraceHeader? GetTraceHeader(); - void PauseSession(); - void ResumeSession(); - void StartSession(); - Sentry.ITransactionTracer StartTransaction(Sentry.ITransactionContext context, System.Collections.Generic.IReadOnlyDictionary customSamplingContext); - } - public interface IScopeObserver - { - void AddBreadcrumb(Sentry.Breadcrumb breadcrumb); - void SetExtra(string key, object? value); - void SetTag(string key, string value); - void SetUser(Sentry.SentryUser? user); - void UnsetTag(string key); - } - public interface ISentryClient - { - bool IsEnabled { get; } - Sentry.SentryId CaptureCheckIn(string monitorSlug, Sentry.CheckInStatus status, Sentry.SentryId? sentryId = default, System.TimeSpan? duration = default, Sentry.Scope? scope = null, System.Action? configureMonitorOptions = null); - bool CaptureEnvelope(Sentry.Protocol.Envelopes.Envelope envelope); - Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, Sentry.Scope? scope = null, Sentry.SentryHint? hint = null); - void CaptureSession(Sentry.SessionUpdate sessionUpdate); - void CaptureTransaction(Sentry.SentryTransaction transaction); - void CaptureTransaction(Sentry.SentryTransaction transaction, Sentry.Scope? scope, Sentry.SentryHint? hint); - void CaptureUserFeedback(Sentry.UserFeedback userFeedback); - System.Threading.Tasks.Task FlushAsync(System.TimeSpan timeout); - } - public interface ISentryJsonSerializable - { - void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger); - } - public interface ISentryScopeManager - { - void BindClient(Sentry.ISentryClient client); - void ConfigureScope(System.Action configureScope); - System.Threading.Tasks.Task ConfigureScopeAsync(System.Func configureScope); - System.IDisposable PushScope(); - System.IDisposable PushScope(TState state); - } - public interface ISentryScopeStateProcessor - { - void Apply(Sentry.Scope scope, object state); - } - public interface ISentrySession - { - string? DistinctId { get; } - string? Environment { get; } - int ErrorCount { get; } - Sentry.SentryId Id { get; } - string? IpAddress { get; } - string Release { get; } - System.DateTimeOffset StartTimestamp { get; } - string? UserAgent { get; } - } - public interface ISentryUserFactory - { - Sentry.SentryUser? Create(); - } - public interface ISpan : Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpanData, Sentry.Protocol.ITraceContext - { - new string? Description { get; set; } - new string Operation { get; set; } - new Sentry.SpanStatus? Status { get; set; } - void Finish(); - void Finish(Sentry.SpanStatus status); - void Finish(System.Exception exception); - void Finish(System.Exception exception, Sentry.SpanStatus status); - Sentry.ISpan StartChild(string operation); - } - public interface ISpanData : Sentry.IHasExtra, Sentry.IHasTags, Sentry.Protocol.ITraceContext - { - System.DateTimeOffset? EndTimestamp { get; } - bool IsFinished { get; } - System.Collections.Generic.IReadOnlyDictionary Measurements { get; } - System.DateTimeOffset StartTimestamp { get; } - Sentry.SentryTraceHeader GetTraceHeader(); - void SetMeasurement(string name, Sentry.Protocol.Measurement measurement); - } - public interface ITransactionContext : Sentry.Protocol.ITraceContext - { - bool? IsParentSampled { get; } - string Name { get; } - Sentry.TransactionNameSource NameSource { get; } - } - public interface ITransactionData : Sentry.IEventLike, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.Protocol.ITraceContext - { - string? Platform { get; set; } - } - public interface ITransactionTracer : Sentry.IEventLike, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpan, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.Protocol.ITraceContext - { - new bool? IsParentSampled { get; set; } - new string Name { get; set; } - System.Collections.Generic.IReadOnlyCollection Spans { get; } - Sentry.ISpan? GetLastActiveSpan(); - } - public enum InstructionAddressAdjustment - { - Auto = 0, - All = 1, - AllButFirst = 2, - None = 3, - } - public enum Instrumenter - { - Sentry = 0, - OpenTelemetry = 1, - } - public readonly struct MeasurementUnit : System.IEquatable - { - public static Sentry.MeasurementUnit None; - public bool Equals(Sentry.MeasurementUnit other) { } - public override bool Equals(object? obj) { } - public override int GetHashCode() { } - public override string ToString() { } - public static Sentry.MeasurementUnit Custom(string name) { } - public static Sentry.MeasurementUnit op_Implicit(Sentry.MeasurementUnit.Duration unit) { } - public static Sentry.MeasurementUnit op_Implicit(Sentry.MeasurementUnit.Fraction unit) { } - public static Sentry.MeasurementUnit op_Implicit(Sentry.MeasurementUnit.Information unit) { } - public static bool operator !=(Sentry.MeasurementUnit left, Sentry.MeasurementUnit right) { } - public static bool operator ==(Sentry.MeasurementUnit left, Sentry.MeasurementUnit right) { } - public enum Duration - { - Nanosecond = 0, - Microsecond = 1, - Millisecond = 2, - Second = 3, - Minute = 4, - Hour = 5, - Day = 6, - Week = 7, - } - public enum Fraction - { - Ratio = 0, - Percent = 1, - } - public enum Information - { - Bit = 0, - Byte = 1, - Kilobyte = 2, - Kibibyte = 3, - Megabyte = 4, - Mebibyte = 5, - Gigabyte = 6, - Gibibyte = 7, - Terabyte = 8, - Tebibyte = 9, - Petabyte = 10, - Pebibyte = 11, - Exabyte = 12, - Exbibyte = 13, - } - } - public enum ReportAssembliesMode - { - None = 0, - Version = 1, - InformationalVersion = 2, - } - public class Scope : Sentry.IEventLike, Sentry.IHasExtra, Sentry.IHasTags - { - public Scope(Sentry.SentryOptions? options) { } - public System.Collections.Generic.IReadOnlyCollection Attachments { get; } - public System.Collections.Generic.IReadOnlyCollection Breadcrumbs { get; } - public Sentry.SentryContexts Contexts { get; set; } - public string? Distribution { get; set; } - public string? Environment { get; set; } - public System.Collections.Generic.IReadOnlyDictionary Extra { get; } - public System.Collections.Generic.IReadOnlyList Fingerprint { get; set; } - public Sentry.SentryLevel? Level { get; set; } - public string? Release { get; set; } - public Sentry.SentryRequest Request { get; set; } - public Sentry.SdkVersion Sdk { get; } - public Sentry.ISpan? Span { get; set; } - public System.Collections.Generic.IReadOnlyDictionary Tags { get; } - public Sentry.ITransactionTracer? Transaction { get; set; } - public string? TransactionName { get; set; } - public Sentry.SentryUser User { get; set; } - public void AddAttachment(Sentry.SentryAttachment attachment) { } - public void AddAttachment(string filePath, Sentry.AttachmentType type = 0, string? contentType = null) { } - public void AddAttachment(byte[] data, string fileName, Sentry.AttachmentType type = 0, string? contentType = null) { } - public void AddAttachment(System.IO.Stream stream, string fileName, Sentry.AttachmentType type = 0, string? contentType = null) { } - public void AddBreadcrumb(Sentry.Breadcrumb breadcrumb) { } - public void AddBreadcrumb(Sentry.Breadcrumb breadcrumb, Sentry.SentryHint hint) { } - public void AddEventProcessor(Sentry.Extensibility.ISentryEventProcessor processor) { } - public void AddEventProcessor(System.Func processor) { } - public void AddEventProcessors(System.Collections.Generic.IEnumerable processors) { } - public void AddExceptionProcessor(Sentry.Extensibility.ISentryEventExceptionProcessor processor) { } - public void AddExceptionProcessors(System.Collections.Generic.IEnumerable processors) { } - public void AddTransactionProcessor(Sentry.Extensibility.ISentryTransactionProcessor processor) { } - public void AddTransactionProcessor(System.Func processor) { } - public void AddTransactionProcessors(System.Collections.Generic.IEnumerable processors) { } - public void Apply(Sentry.IEventLike other) { } - public void Apply(Sentry.Scope other) { } - public void Apply(object state) { } - public void Clear() { } - public void ClearAttachments() { } - public void ClearBreadcrumbs() { } - public Sentry.Scope Clone() { } - public System.Collections.Generic.IEnumerable GetAllEventProcessors() { } - public System.Collections.Generic.IEnumerable GetAllExceptionProcessors() { } - public System.Collections.Generic.IEnumerable GetAllTransactionProcessors() { } - public void SetExtra(string key, object? value) { } - public void SetTag(string key, string value) { } - public void UnsetTag(string key) { } - } - public sealed class SdkVersion : Sentry.ISentryJsonSerializable - { - public SdkVersion() { } - public string? Name { get; set; } - public System.Collections.Generic.IEnumerable Packages { get; } - public string? Version { get; set; } - public void AddIntegration(string integration) { } - public void AddPackage(string name, string version) { } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } - public static Sentry.SdkVersion FromJson(System.Text.Json.JsonElement json) { } - } - [System.Diagnostics.DebuggerDisplay("{FileName}")] - public class SentryAttachment - { - public SentryAttachment(Sentry.AttachmentType type, Sentry.IAttachmentContent content, string fileName, string? contentType) { } - public Sentry.IAttachmentContent Content { get; } - public string? ContentType { get; } - public string FileName { get; } - public Sentry.AttachmentType Type { get; } - } - public class SentryCheckIn : Sentry.ISentryJsonSerializable - { - public SentryCheckIn(string monitorSlug, Sentry.CheckInStatus status, Sentry.SentryId? sentryId = default) { } - public System.TimeSpan? Duration { get; set; } - public string? Environment { get; set; } - public Sentry.SentryId Id { get; } - public string MonitorSlug { get; } - public string? Release { get; set; } - public Sentry.CheckInStatus Status { get; } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } - } - public class SentryClient : Sentry.ISentryClient, System.IDisposable - { - public SentryClient(Sentry.SentryOptions options) { } - public bool IsEnabled { get; } - public Sentry.SentryId CaptureCheckIn(string monitorSlug, Sentry.CheckInStatus status, Sentry.SentryId? sentryId = default, System.TimeSpan? duration = default, Sentry.Scope? scope = null, System.Action? configureMonitorOptions = null) { } - public bool CaptureEnvelope(Sentry.Protocol.Envelopes.Envelope envelope) { } - public Sentry.SentryId CaptureEvent(Sentry.SentryEvent? @event, Sentry.Scope? scope = null, Sentry.SentryHint? hint = null) { } - public void CaptureSession(Sentry.SessionUpdate sessionUpdate) { } - public void CaptureTransaction(Sentry.SentryTransaction transaction) { } - public void CaptureTransaction(Sentry.SentryTransaction transaction, Sentry.Scope? scope, Sentry.SentryHint? hint) { } - public void CaptureUserFeedback(Sentry.UserFeedback userFeedback) { } - public void Dispose() { } - public System.Threading.Tasks.Task FlushAsync(System.TimeSpan timeout) { } - } - public static class SentryClientExtensions - { - public static Sentry.SentryId CaptureException(this Sentry.ISentryClient client, System.Exception ex) { } - public static Sentry.SentryId CaptureMessage(this Sentry.ISentryClient client, string message, Sentry.SentryLevel level = 1) { } - public static void CaptureUserFeedback(this Sentry.ISentryClient client, Sentry.SentryId eventId, string email, string comments, string? name = null) { } - public static void Flush(this Sentry.ISentryClient client) { } - public static void Flush(this Sentry.ISentryClient client, System.TimeSpan timeout) { } - public static System.Threading.Tasks.Task FlushAsync(this Sentry.ISentryClient client) { } - } - public static class SentryConstants - { - public const int DefaultMaxBreadcrumbs = 100; - public const string DisableSdkDsnValue = ""; - public const string Platform = "csharp"; - public const int ProtocolVersion = 7; - } - public sealed class SentryContexts : Sentry.ISentryJsonSerializable, System.Collections.Generic.ICollection>, System.Collections.Generic.IDictionary, System.Collections.Generic.IEnumerable>, System.Collections.IEnumerable - { - public SentryContexts() { } - public Sentry.Protocol.App App { get; } - public Sentry.Protocol.Browser Browser { get; } - public int Count { get; } - public Sentry.Protocol.Device Device { get; } - public Sentry.Protocol.Gpu Gpu { get; } - public bool IsReadOnly { get; } - public object this[string key] { get; set; } - public System.Collections.Generic.ICollection Keys { get; } - public Sentry.Protocol.OperatingSystem OperatingSystem { get; } - public Sentry.Protocol.Response Response { get; } - public Sentry.Protocol.Runtime Runtime { get; } - public Sentry.Protocol.Trace Trace { get; } - public System.Collections.Generic.ICollection Values { get; } - public void Add(System.Collections.Generic.KeyValuePair item) { } - public void Add(string key, object value) { } - public void Clear() { } - public bool Contains(System.Collections.Generic.KeyValuePair item) { } - public bool ContainsKey(string key) { } - public void CopyTo(System.Collections.Generic.KeyValuePair[] array, int arrayIndex) { } - public System.Collections.Generic.IEnumerator> GetEnumerator() { } - public bool Remove(System.Collections.Generic.KeyValuePair item) { } - public bool Remove(string key) { } - public bool TryGetValue(string key, out object value) { } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } - public static Sentry.SentryContexts FromJson(System.Text.Json.JsonElement json) { } - } - [System.Diagnostics.DebuggerDisplay("{GetType().Name,nq}: {EventId,nq}")] - public sealed class SentryEvent : Sentry.IEventLike, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISentryJsonSerializable - { - public SentryEvent() { } - public SentryEvent(System.Exception? exception) { } - public System.Collections.Generic.IReadOnlyCollection Breadcrumbs { get; } - public Sentry.SentryContexts Contexts { get; set; } - public System.Collections.Generic.List? DebugImages { get; set; } - public string? Distribution { get; set; } - public string? Environment { get; set; } - public Sentry.SentryId EventId { get; } - public System.Exception? Exception { get; } - public System.Collections.Generic.IReadOnlyDictionary Extra { get; } - public System.Collections.Generic.IReadOnlyList Fingerprint { get; set; } - public Sentry.SentryLevel? Level { get; set; } - public string? Logger { get; set; } - public Sentry.SentryMessage? Message { get; set; } - public System.Collections.Generic.IDictionary Modules { get; } - public string? Platform { get; set; } - public string? Release { get; set; } - public Sentry.SentryRequest Request { get; set; } - public Sentry.SdkVersion Sdk { get; } - public System.Collections.Generic.IEnumerable? SentryExceptions { get; set; } - public System.Collections.Generic.IEnumerable? SentryThreads { get; set; } - public string? ServerName { get; set; } - public System.Collections.Generic.IReadOnlyDictionary Tags { get; } - public System.DateTimeOffset Timestamp { get; } - public string? TransactionName { get; set; } - public Sentry.SentryUser User { get; set; } - public void AddBreadcrumb(Sentry.Breadcrumb breadcrumb) { } - public void SetExtra(string key, object? value) { } - public void SetTag(string key, string value) { } - public void UnsetTag(string key) { } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } - public static Sentry.SentryEvent FromJson(System.Text.Json.JsonElement json) { } - } - public class SentryGraphQLHttpMessageHandler : Sentry.SentryMessageHandler - { - public SentryGraphQLHttpMessageHandler(System.Net.Http.HttpMessageHandler? innerHandler = null, Sentry.IHub? hub = null) { } - protected override void HandleResponse(System.Net.Http.HttpResponseMessage response, Sentry.ISpan? span, string method, string url) { } - protected override Sentry.ISpan? ProcessRequest(System.Net.Http.HttpRequestMessage request, string method, string url) { } - } - public class SentryHint - { - public SentryHint() { } - public SentryHint(string key, object? value) { } - public System.Collections.Generic.ICollection Attachments { get; } - public System.Collections.Generic.IDictionary Items { get; } - public void AddAttachment(string filePath, Sentry.AttachmentType type = 0, string? contentType = null) { } - public void AddAttachment(byte[] data, string fileName, Sentry.AttachmentType type = 0, string? contentType = null) { } - public static Sentry.SentryHint WithAttachments(params Sentry.SentryAttachment[] attachments) { } - public static Sentry.SentryHint WithAttachments(System.Collections.Generic.IEnumerable attachments) { } - } - public class SentryHttpMessageHandler : Sentry.SentryMessageHandler - { - public SentryHttpMessageHandler() { } - public SentryHttpMessageHandler(Sentry.IHub hub) { } - public SentryHttpMessageHandler(System.Net.Http.HttpMessageHandler innerHandler) { } - public SentryHttpMessageHandler(System.Net.Http.HttpMessageHandler innerHandler, Sentry.IHub hub) { } - protected override void HandleResponse(System.Net.Http.HttpResponseMessage response, Sentry.ISpan? span, string method, string url) { } - protected override Sentry.ISpan? ProcessRequest(System.Net.Http.HttpRequestMessage request, string method, string url) { } - } - public readonly struct SentryId : Sentry.ISentryJsonSerializable, System.IEquatable - { - public static readonly Sentry.SentryId Empty; - public SentryId(System.Guid guid) { } - public bool Equals(Sentry.SentryId other) { } - public override bool Equals(object? obj) { } - public override int GetHashCode() { } - public override string ToString() { } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } - public static Sentry.SentryId Create() { } - public static Sentry.SentryId FromJson(System.Text.Json.JsonElement json) { } - public static Sentry.SentryId Parse(string value) { } - public static System.Guid op_Implicit(Sentry.SentryId sentryId) { } - public static Sentry.SentryId op_Implicit(System.Guid guid) { } - public static bool operator !=(Sentry.SentryId left, Sentry.SentryId right) { } - public static bool operator ==(Sentry.SentryId left, Sentry.SentryId right) { } - } - public enum SentryLevel : short - { - [System.Runtime.Serialization.EnumMember(Value="debug")] - Debug = 0, - [System.Runtime.Serialization.EnumMember(Value="info")] - Info = 1, - [System.Runtime.Serialization.EnumMember(Value="warning")] - Warning = 2, - [System.Runtime.Serialization.EnumMember(Value="error")] - Error = 3, - [System.Runtime.Serialization.EnumMember(Value="fatal")] - Fatal = 4, - } - public sealed class SentryMessage : Sentry.ISentryJsonSerializable - { - public SentryMessage() { } - public string? Formatted { get; set; } - public string? Message { get; set; } - public System.Collections.Generic.IEnumerable? Params { get; set; } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } - public static Sentry.SentryMessage FromJson(System.Text.Json.JsonElement json) { } - public static Sentry.SentryMessage op_Implicit(string? message) { } - } - public abstract class SentryMessageHandler : System.Net.Http.DelegatingHandler - { - protected SentryMessageHandler() { } - protected SentryMessageHandler(Sentry.IHub hub) { } - protected SentryMessageHandler(System.Net.Http.HttpMessageHandler innerHandler) { } - protected SentryMessageHandler(System.Net.Http.HttpMessageHandler innerHandler, Sentry.IHub hub) { } - protected abstract void HandleResponse(System.Net.Http.HttpResponseMessage response, Sentry.ISpan? span, string method, string url); - protected abstract Sentry.ISpan? ProcessRequest(System.Net.Http.HttpRequestMessage request, string method, string url); - protected override System.Net.Http.HttpResponseMessage Send(System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) { } - protected override System.Threading.Tasks.Task SendAsync(System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) { } - } - public enum SentryMonitorInterval - { - Year = 0, - Month = 1, - Week = 2, - Day = 3, - Hour = 4, - Minute = 5, - } - public class SentryMonitorOptions : Sentry.ISentryJsonSerializable - { - public System.TimeSpan? CheckInMargin { get; set; } - public int? FailureIssueThreshold { get; set; } - public System.TimeSpan? MaxRuntime { get; set; } - public string? Owner { get; set; } - public int? RecoveryThreshold { get; set; } - public string? TimeZone { get; set; } - public void Interval(string crontab) { } - public void Interval(int interval, Sentry.SentryMonitorInterval unit) { } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } - } - public class SentryOptions - { - public SentryOptions() { } - [System.CLSCompliant(false)] - public System.Func? AssemblyReader { get; set; } - public bool AttachStacktrace { get; set; } - public bool AutoSessionTracking { get; set; } - public System.TimeSpan AutoSessionTrackingInterval { get; set; } - public Sentry.Extensibility.IBackgroundWorker? BackgroundWorker { get; set; } - public string? CacheDirectoryPath { get; set; } - public bool CaptureFailedRequests { get; set; } - public System.Action? ConfigureClient { get; set; } - public System.Func? CrashedLastRun { get; set; } - public System.Func? CreateHttpMessageHandler { get; set; } - public bool Debug { get; set; } - public System.Net.DecompressionMethods DecompressionMethods { get; set; } - public Sentry.DeduplicateMode DeduplicateMode { get; set; } - public System.Collections.Generic.Dictionary DefaultTags { get; } - public Sentry.StartupTimeDetectionMode DetectStartupTime { get; set; } - public Sentry.SentryLevel DiagnosticLevel { get; set; } - public Sentry.Extensibility.IDiagnosticLogger? DiagnosticLogger { get; set; } - public bool DisableFileWrite { get; set; } - public string? Distribution { get; set; } - public string? Dsn { get; set; } - public bool EnableScopeSync { get; set; } - public bool EnableSpotlight { get; set; } - public string? Environment { get; set; } - public System.Collections.Generic.IList FailedRequestStatusCodes { get; set; } - public System.Collections.Generic.IList FailedRequestTargets { get; set; } - public System.TimeSpan FlushTimeout { get; set; } - public System.Net.IWebProxy? HttpProxy { get; set; } - public System.TimeSpan InitCacheFlushTimeout { get; set; } - public bool IsEnvironmentUser { get; set; } - public bool IsGlobalModeEnabled { get; set; } - public bool JsonPreserveReferences { get; set; } - public long MaxAttachmentSize { get; set; } - public int MaxBreadcrumbs { get; set; } - public int MaxCacheItems { get; set; } - public int MaxQueueItems { get; set; } - public Sentry.Extensibility.INetworkStatusListener? NetworkStatusListener { get; set; } - public double? ProfilesSampleRate { get; set; } - public string? Release { get; set; } - public Sentry.ReportAssembliesMode ReportAssembliesMode { get; set; } - public bool RequestBodyCompressionBuffered { get; set; } - public System.IO.Compression.CompressionLevel RequestBodyCompressionLevel { get; set; } - public float? SampleRate { get; set; } - public Sentry.IScopeObserver? ScopeObserver { get; set; } - public bool SendClientReports { get; set; } - public bool SendDefaultPii { get; set; } - public Sentry.ISentryScopeStateProcessor SentryScopeStateProcessor { get; set; } - public string? ServerName { get; set; } - public System.TimeSpan ShutdownTimeout { get; set; } - public string SpotlightUrl { get; set; } - public Sentry.StackTraceMode StackTraceMode { get; set; } - public System.Collections.Generic.IList TagFilters { get; set; } - public System.Collections.Generic.IList TracePropagationTargets { get; set; } - public double? TracesSampleRate { get; set; } - public System.Func? TracesSampler { get; set; } - public Sentry.Extensibility.ITransport? Transport { get; set; } - public bool UseAsyncFileIO { get; set; } - public void AddEventProcessor(Sentry.Extensibility.ISentryEventProcessor processor) { } - public void AddEventProcessorProvider(System.Func> processorProvider) { } - public void AddEventProcessors(System.Collections.Generic.IEnumerable processors) { } - public void AddExceptionFilter(Sentry.Extensibility.IExceptionFilter exceptionFilter) { } - public void AddExceptionFilterForType() - where TException : System.Exception { } - public void AddExceptionProcessor(Sentry.Extensibility.ISentryEventExceptionProcessor processor) { } - public void AddExceptionProcessorProvider(System.Func> processorProvider) { } - public void AddExceptionProcessors(System.Collections.Generic.IEnumerable processors) { } - public void AddInAppExclude(string prefix) { } - public void AddInAppExclude(System.Text.RegularExpressions.Regex regex) { } - public void AddInAppExcludeRegex(string pattern) { } - public void AddInAppInclude(string prefix) { } - public void AddInAppInclude(System.Text.RegularExpressions.Regex regex) { } - public void AddInAppIncludeRegex(string pattern) { } - public void AddIntegration(Sentry.Integrations.ISdkIntegration integration) { } - public void AddJsonConverter(System.Text.Json.Serialization.JsonConverter converter) { } - public void AddJsonSerializerContext(System.Func contextBuilder) - where T : System.Text.Json.Serialization.JsonSerializerContext { } - public void AddTransactionProcessor(Sentry.Extensibility.ISentryTransactionProcessor processor) { } - public void AddTransactionProcessorProvider(System.Func> processorProvider) { } - public void AddTransactionProcessors(System.Collections.Generic.IEnumerable processors) { } - public void ApplyDefaultTags(Sentry.IHasTags hasTags) { } - public void DisableAppDomainProcessExitFlush() { } - public void DisableAppDomainUnhandledExceptionCapture() { } - public void DisableDiagnosticSourceIntegration() { } - public void DisableDuplicateEventDetection() { } - public void DisableUnobservedTaskExceptionCapture() { } - public void DisableWinUiUnhandledExceptionIntegration() { } - public void EnableHeapDumps(Sentry.HeapDumpTrigger trigger, Sentry.Debouncer? debouncer = null, Sentry.SentryLevel level = 2) { } - public void EnableHeapDumps(short memoryPercentageThreshold, Sentry.Debouncer? debouncer = null, Sentry.SentryLevel level = 2) { } - public System.Collections.Generic.IEnumerable GetAllEventProcessors() { } - public System.Collections.Generic.IEnumerable GetAllExceptionProcessors() { } - public System.Collections.Generic.IEnumerable GetAllTransactionProcessors() { } - public void RemoveEventProcessor() - where TProcessor : Sentry.Extensibility.ISentryEventProcessor { } - public void RemoveExceptionFilter() - where TFilter : Sentry.Extensibility.IExceptionFilter { } - public void RemoveIntegration() - where TIntegration : Sentry.Integrations.ISdkIntegration { } - public void RemoveTransactionProcessor() - where TProcessor : Sentry.Extensibility.ISentryTransactionProcessor { } - public void SetBeforeBreadcrumb(System.Func beforeBreadcrumb) { } - public void SetBeforeBreadcrumb(System.Func beforeBreadcrumb) { } - public void SetBeforeSend(System.Func beforeSend) { } - public void SetBeforeSend(System.Func beforeSend) { } - public void SetBeforeSendTransaction(System.Func beforeSendTransaction) { } - public void SetBeforeSendTransaction(System.Func beforeSendTransaction) { } - public Sentry.SentryOptions UseStackTraceFactory(Sentry.Extensibility.ISentryStackTraceFactory sentryStackTraceFactory) { } - } - public sealed class SentryPackage : Sentry.ISentryJsonSerializable - { - public SentryPackage(string name, string version) { } - public string Name { get; } - public string Version { get; } - public override bool Equals(object? obj) { } - public override int GetHashCode() { } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } - public static Sentry.SentryPackage FromJson(System.Text.Json.JsonElement json) { } - } - public sealed class SentryRequest : Sentry.ISentryJsonSerializable - { - public SentryRequest() { } - public string? ApiTarget { get; set; } - public string? Cookies { get; set; } - public object? Data { get; set; } - public System.Collections.Generic.IDictionary Env { get; } - public System.Collections.Generic.IDictionary Headers { get; } - public string? Method { get; set; } - public System.Collections.Generic.IDictionary Other { get; } - public string? QueryString { get; set; } - public string? Url { get; set; } - public Sentry.SentryRequest Clone() { } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } - public static Sentry.SentryRequest FromJson(System.Text.Json.JsonElement json) { } - } - public static class SentrySdk - { - public static bool IsEnabled { get; } - public static Sentry.SentryId LastEventId { get; } - public static void AddBreadcrumb(Sentry.Breadcrumb breadcrumb, Sentry.SentryHint? hint = null) { } - public static void AddBreadcrumb(string message, string? category = null, string? type = null, System.Collections.Generic.IDictionary? data = null, Sentry.BreadcrumbLevel level = 0) { } - public static void AddBreadcrumb(Sentry.Infrastructure.ISystemClock? clock, string message, string? category = null, string? type = null, System.Collections.Generic.IDictionary? data = null, Sentry.BreadcrumbLevel level = 0) { } - public static void BindClient(Sentry.ISentryClient client) { } - public static void BindException(System.Exception exception, Sentry.ISpan span) { } - public static Sentry.SentryId CaptureCheckIn(string monitorSlug, Sentry.CheckInStatus status, Sentry.SentryId? sentryId = default, System.TimeSpan? duration = default, Sentry.Scope? scope = null, System.Action? configureMonitorOptions = null) { } - public static bool CaptureEnvelope(Sentry.Protocol.Envelopes.Envelope envelope) { } - public static Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, System.Action configureScope) { } - public static Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, Sentry.Scope? scope = null, Sentry.SentryHint? hint = null) { } - public static Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, Sentry.SentryHint? hint, System.Action configureScope) { } - public static Sentry.SentryId CaptureException(System.Exception exception) { } - public static Sentry.SentryId CaptureException(System.Exception exception, System.Action configureScope) { } - public static Sentry.SentryId CaptureMessage(string message, Sentry.SentryLevel level = 1) { } - public static Sentry.SentryId CaptureMessage(string message, System.Action configureScope, Sentry.SentryLevel level = 1) { } - public static void CaptureSession(Sentry.SessionUpdate sessionUpdate) { } - public static void CaptureTransaction(Sentry.SentryTransaction transaction) { } - public static void CaptureTransaction(Sentry.SentryTransaction transaction, Sentry.Scope? scope, Sentry.SentryHint? hint) { } - public static void CaptureUserFeedback(Sentry.UserFeedback userFeedback) { } - public static void CaptureUserFeedback(Sentry.SentryId eventId, string email, string comments, string? name = null) { } - [System.Obsolete("WARNING: This method deliberately causes a crash, and should not be used in a rea" + - "l application.")] - public static void CauseCrash(Sentry.CrashType crashType) { } - public static void Close() { } - public static void ConfigureScope(System.Action configureScope) { } - public static System.Threading.Tasks.Task ConfigureScopeAsync(System.Func configureScope) { } - public static Sentry.TransactionContext ContinueTrace(Sentry.SentryTraceHeader? traceHeader, Sentry.BaggageHeader? baggageHeader, string? name = null, string? operation = null) { } - public static Sentry.TransactionContext ContinueTrace(string? traceHeader, string? baggageHeader, string? name = null, string? operation = null) { } - public static void EndSession(Sentry.SessionEndStatus status = 0) { } - public static void Flush() { } - public static void Flush(System.TimeSpan timeout) { } - public static System.Threading.Tasks.Task FlushAsync() { } - public static System.Threading.Tasks.Task FlushAsync(System.TimeSpan timeout) { } - public static Sentry.BaggageHeader? GetBaggage() { } - public static Sentry.ISpan? GetSpan() { } - public static Sentry.SentryTraceHeader? GetTraceHeader() { } - public static System.IDisposable Init() { } - public static System.IDisposable Init(Sentry.SentryOptions options) { } - public static System.IDisposable Init(System.Action? configureOptions) { } - public static System.IDisposable Init(string? dsn) { } - public static void PauseSession() { } - public static System.IDisposable PushScope() { } - public static System.IDisposable PushScope(TState state) { } - public static void ResumeSession() { } - public static void StartSession() { } - public static Sentry.ITransactionTracer StartTransaction(Sentry.ITransactionContext context) { } - public static Sentry.ITransactionTracer StartTransaction(Sentry.ITransactionContext context, System.Collections.Generic.IReadOnlyDictionary customSamplingContext) { } - public static Sentry.ITransactionTracer StartTransaction(string name, string operation) { } - public static Sentry.ITransactionTracer StartTransaction(string name, string operation, Sentry.SentryTraceHeader traceHeader) { } - public static Sentry.ITransactionTracer StartTransaction(string name, string operation, string? description) { } - } - public class SentrySession : Sentry.ISentrySession - { - public SentrySession(string? distinctId, string release, string? environment) { } - public string? DistinctId { get; } - public string? Environment { get; } - public int ErrorCount { get; } - public Sentry.SentryId Id { get; } - public string? IpAddress { get; } - public string Release { get; } - public System.DateTimeOffset StartTimestamp { get; } - public string? UserAgent { get; } - public void ReportError() { } - } - public class SentrySpan : Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISentryJsonSerializable, Sentry.ISpanData, Sentry.Protocol.ITraceContext - { - public SentrySpan(Sentry.ISpan tracer) { } - public SentrySpan(Sentry.SpanId? parentSpanId, string operation) { } - public string? Description { get; set; } - public System.DateTimeOffset? EndTimestamp { get; } - public System.Collections.Generic.IReadOnlyDictionary Extra { get; } - public bool IsFinished { get; } - public bool? IsSampled { get; } - public System.Collections.Generic.IReadOnlyDictionary Measurements { get; } - public string Operation { get; set; } - public string? Origin { get; } - public Sentry.SpanId? ParentSpanId { get; } - public Sentry.SpanId SpanId { get; } - public System.DateTimeOffset StartTimestamp { get; } - public Sentry.SpanStatus? Status { get; set; } - public System.Collections.Generic.IReadOnlyDictionary Tags { get; } - public Sentry.SentryId TraceId { get; } - public Sentry.SentryTraceHeader GetTraceHeader() { } - public void SetExtra(string key, object? value) { } - public void SetMeasurement(string name, Sentry.Protocol.Measurement measurement) { } - public void SetTag(string key, string value) { } - public void UnsetTag(string key) { } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } - public static Sentry.SentrySpan FromJson(System.Text.Json.JsonElement json) { } - } - [System.Diagnostics.DebuggerDisplay("{Function}")] - public sealed class SentryStackFrame : Sentry.ISentryJsonSerializable - { - public SentryStackFrame() { } - public string? AbsolutePath { get; set; } - public string? AddressMode { get; set; } - public int? ColumnNumber { get; set; } - public string? ContextLine { get; set; } - public string? FileName { get; set; } - public System.Collections.Generic.IList FramesOmitted { get; } - public string? Function { get; set; } - public long? FunctionId { get; set; } - public long? ImageAddress { get; set; } - public bool? InApp { get; set; } - public long? InstructionAddress { get; set; } - public int? LineNumber { get; set; } - public string? Module { get; set; } - public string? Package { get; set; } - public string? Platform { get; set; } - public System.Collections.Generic.IList PostContext { get; } - public System.Collections.Generic.IList PreContext { get; } - public long? SymbolAddress { get; set; } - public System.Collections.Generic.IDictionary Vars { get; } - public void ConfigureAppFrame(Sentry.SentryOptions options) { } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } - public static Sentry.SentryStackFrame FromJson(System.Text.Json.JsonElement json) { } - } - public class SentryStackTrace : Sentry.ISentryJsonSerializable - { - public SentryStackTrace() { } - public Sentry.InstructionAddressAdjustment? AddressAdjustment { get; set; } - public System.Collections.Generic.IList Frames { get; set; } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } - public static Sentry.SentryStackTrace FromJson(System.Text.Json.JsonElement json) { } - } - public sealed class SentryThread : Sentry.ISentryJsonSerializable - { - public SentryThread() { } - public bool? Crashed { get; set; } - public bool? Current { get; set; } - public int? Id { get; set; } - public string? Name { get; set; } - public Sentry.SentryStackTrace? Stacktrace { get; set; } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } - public static Sentry.SentryThread FromJson(System.Text.Json.JsonElement json) { } - } - public class SentryTraceHeader - { - public SentryTraceHeader(Sentry.SentryId traceId, Sentry.SpanId spanSpanId, bool? isSampled) { } - public bool? IsSampled { get; } - public Sentry.SpanId SpanId { get; } - public Sentry.SentryId TraceId { get; } - public override string ToString() { } - public static Sentry.SentryTraceHeader? Parse(string value) { } - } - public class SentryTransaction : Sentry.IEventLike, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISentryJsonSerializable, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.Protocol.ITraceContext - { - public SentryTransaction(Sentry.ITransactionTracer tracer) { } - public SentryTransaction(string name, string operation) { } - public SentryTransaction(string name, string operation, Sentry.TransactionNameSource nameSource) { } - public System.Collections.Generic.IReadOnlyCollection Breadcrumbs { get; } - public Sentry.SentryContexts Contexts { get; set; } - public string? Description { get; set; } - public string? Distribution { get; set; } - public System.DateTimeOffset? EndTimestamp { get; } - public string? Environment { get; set; } - public Sentry.SentryId EventId { get; } - public System.Collections.Generic.IReadOnlyDictionary Extra { get; } - public System.Collections.Generic.IReadOnlyList Fingerprint { get; set; } - public bool IsFinished { get; } - public bool? IsParentSampled { get; set; } - public bool? IsSampled { get; } - public Sentry.SentryLevel? Level { get; set; } - public System.Collections.Generic.IReadOnlyDictionary Measurements { get; } - public string Name { get; } - public Sentry.TransactionNameSource NameSource { get; } - public string Operation { get; } - public string? Origin { get; } - public Sentry.SpanId? ParentSpanId { get; } - public string? Platform { get; set; } - public string? Release { get; set; } - public Sentry.SentryRequest Request { get; set; } - public double? SampleRate { get; } - public Sentry.SdkVersion Sdk { get; } - public Sentry.SpanId SpanId { get; } - public System.Collections.Generic.IReadOnlyCollection Spans { get; } - public System.DateTimeOffset StartTimestamp { get; } - public Sentry.SpanStatus? Status { get; } - public System.Collections.Generic.IReadOnlyDictionary Tags { get; } - public Sentry.SentryId TraceId { get; } - public Sentry.SentryUser User { get; set; } - public void AddBreadcrumb(Sentry.Breadcrumb breadcrumb) { } - public Sentry.SentryTraceHeader GetTraceHeader() { } - public void SetExtra(string key, object? value) { } - public void SetMeasurement(string name, Sentry.Protocol.Measurement measurement) { } - public void SetTag(string key, string value) { } - public void UnsetTag(string key) { } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } - public static Sentry.SentryTransaction FromJson(System.Text.Json.JsonElement json) { } - } - public sealed class SentryUser : Sentry.ISentryJsonSerializable - { - public SentryUser() { } - public string? Email { get; set; } - public string? Id { get; set; } - public string? IpAddress { get; set; } - public System.Collections.Generic.IDictionary Other { get; set; } - public string? Username { get; set; } - public Sentry.SentryUser Clone() { } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? _) { } - public static Sentry.SentryUser FromJson(System.Text.Json.JsonElement json) { } - } - public enum SessionEndStatus - { - Exited = 0, - Crashed = 1, - Abnormal = 2, - } - public class SessionUpdate : Sentry.ISentryJsonSerializable, Sentry.ISentrySession - { - public SessionUpdate(Sentry.SessionUpdate sessionUpdate, bool isInitial) { } - public SessionUpdate(Sentry.SessionUpdate sessionUpdate, bool isInitial, Sentry.SessionEndStatus? endStatus) { } - public SessionUpdate(Sentry.ISentrySession session, bool isInitial, System.DateTimeOffset timestamp, int sequenceNumber, Sentry.SessionEndStatus? endStatus) { } - public SessionUpdate(Sentry.SentryId id, string? distinctId, System.DateTimeOffset startTimestamp, string release, string? environment, string? ipAddress, string? userAgent, int errorCount, bool isInitial, System.DateTimeOffset timestamp, int sequenceNumber, Sentry.SessionEndStatus? endStatus) { } - public string? DistinctId { get; } - public System.TimeSpan Duration { get; } - public Sentry.SessionEndStatus? EndStatus { get; } - public string? Environment { get; } - public int ErrorCount { get; } - public Sentry.SentryId Id { get; } - public string? IpAddress { get; } - public bool IsInitial { get; } - public string Release { get; } - public int SequenceNumber { get; } - public System.DateTimeOffset StartTimestamp { get; } - public System.DateTimeOffset Timestamp { get; } - public string? UserAgent { get; } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } - public static Sentry.SessionUpdate FromJson(System.Text.Json.JsonElement json) { } - } - public class SpanContext : Sentry.Protocol.ITraceContext - { - public SpanContext(string operation, Sentry.SpanId? spanId = default, Sentry.SpanId? parentSpanId = default, Sentry.SentryId? traceId = default, string? description = null, Sentry.SpanStatus? status = default, bool? isSampled = default) { } - public string? Description { get; } - public Sentry.Instrumenter Instrumenter { get; } - public bool? IsSampled { get; } - public string Operation { get; set; } - public string? Origin { get; } - public Sentry.SpanId? ParentSpanId { get; } - public Sentry.SpanId SpanId { get; } - public Sentry.SpanStatus? Status { get; } - public Sentry.SentryId TraceId { get; } - } - public static class SpanDataExtensions - { - public static void SetMeasurement(this Sentry.ISpanData spanData, string name, double value, Sentry.MeasurementUnit unit = default) { } - public static void SetMeasurement(this Sentry.ISpanData spanData, string name, int value, Sentry.MeasurementUnit unit = default) { } - public static void SetMeasurement(this Sentry.ISpanData spanData, string name, long value, Sentry.MeasurementUnit unit = default) { } - [System.CLSCompliant(false)] - public static void SetMeasurement(this Sentry.ISpanData spanData, string name, ulong value, Sentry.MeasurementUnit unit = default) { } - } - public static class SpanExtensions - { - public static Sentry.ITransactionTracer GetTransaction(this Sentry.ISpan span) { } - public static Sentry.ISpan StartChild(this Sentry.ISpan span, string operation, string? description) { } - } - public readonly struct SpanId : Sentry.ISentryJsonSerializable, System.IEquatable - { - public static readonly Sentry.SpanId Empty; - public SpanId(long value) { } - public SpanId(string value) { } - public bool Equals(Sentry.SpanId other) { } - public override bool Equals(object? obj) { } - public override int GetHashCode() { } - public override string ToString() { } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? _) { } - public static Sentry.SpanId Create() { } - public static Sentry.SpanId FromJson(System.Text.Json.JsonElement json) { } - public static Sentry.SpanId Parse(string value) { } - public static string op_Implicit(Sentry.SpanId id) { } - public static bool operator !=(Sentry.SpanId left, Sentry.SpanId right) { } - public static bool operator ==(Sentry.SpanId left, Sentry.SpanId right) { } - } - public enum SpanStatus - { - Ok = 0, - DeadlineExceeded = 1, - Unauthenticated = 2, - PermissionDenied = 3, - NotFound = 4, - ResourceExhausted = 5, - InvalidArgument = 6, - Unimplemented = 7, - Unavailable = 8, - InternalError = 9, - UnknownError = 10, - Cancelled = 11, - AlreadyExists = 12, - FailedPrecondition = 13, - Aborted = 14, - OutOfRange = 15, - DataLoss = 16, - } - public class SpanTracer : Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpan, Sentry.ISpanData, Sentry.Protocol.ITraceContext - { - public SpanTracer(Sentry.IHub hub, Sentry.TransactionTracer transaction, Sentry.SpanId? parentSpanId, Sentry.SentryId traceId, string operation) { } - public string? Description { get; set; } - public System.DateTimeOffset? EndTimestamp { get; } - public System.Collections.Generic.IReadOnlyDictionary Extra { get; } - public bool IsFinished { get; } - public bool? IsSampled { get; } - public System.Collections.Generic.IReadOnlyDictionary Measurements { get; } - public string Operation { get; set; } - public string? Origin { get; } - public Sentry.SpanId? ParentSpanId { get; } - public Sentry.SpanId SpanId { get; } - public System.DateTimeOffset StartTimestamp { get; } - public Sentry.SpanStatus? Status { get; set; } - public System.Collections.Generic.IReadOnlyDictionary Tags { get; } - public Sentry.SentryId TraceId { get; } - public void Finish() { } - public void Finish(Sentry.SpanStatus status) { } - public void Finish(System.Exception exception) { } - public void Finish(System.Exception exception, Sentry.SpanStatus status) { } - public Sentry.SentryTraceHeader GetTraceHeader() { } - public void SetExtra(string key, object? value) { } - public void SetMeasurement(string name, Sentry.Protocol.Measurement measurement) { } - public void SetTag(string key, string value) { } - public Sentry.ISpan StartChild(string operation) { } - public void UnsetTag(string key) { } - } - public enum StackTraceMode - { - Original = 0, - Enhanced = 1, - } - public enum StartupTimeDetectionMode - { - None = 0, - Fast = 1, - Best = 2, - } - public class StreamAttachmentContent : Sentry.IAttachmentContent - { - public StreamAttachmentContent(System.IO.Stream stream) { } - public System.IO.Stream GetStream() { } - } - [System.ComponentModel.TypeConverter(typeof(Sentry.StringOrRegexTypeConverter))] - public class StringOrRegex - { - public StringOrRegex(string stringOrRegex) { } - public StringOrRegex(System.Text.RegularExpressions.Regex regex) { } - public override bool Equals(object? obj) { } - public override int GetHashCode() { } - public override string ToString() { } - public static Sentry.StringOrRegex op_Implicit(string stringOrRegex) { } - public static Sentry.StringOrRegex op_Implicit(System.Text.RegularExpressions.Regex regex) { } - } - public class TransactionContext : Sentry.SpanContext, Sentry.ITransactionContext, Sentry.Protocol.ITraceContext - { - public TransactionContext(string name, string operation, Sentry.SpanId? spanId = default, Sentry.SpanId? parentSpanId = default, Sentry.SentryId? traceId = default, string? description = "", Sentry.SpanStatus? status = default, bool? isSampled = default, bool? isParentSampled = default, Sentry.TransactionNameSource nameSource = 0) { } - public bool? IsParentSampled { get; } - public string Name { get; set; } - public Sentry.TransactionNameSource NameSource { get; set; } - } - public enum TransactionNameSource - { - Custom = 0, - Url = 1, - Route = 2, - View = 3, - Component = 4, - Task = 5, - } - public class TransactionSamplingContext - { - public TransactionSamplingContext(Sentry.ITransactionContext transactionContext, System.Collections.Generic.IReadOnlyDictionary customSamplingContext) { } - public System.Collections.Generic.IReadOnlyDictionary CustomSamplingContext { get; } - public Sentry.ITransactionContext TransactionContext { get; } - } - public class TransactionTracer : Sentry.IEventLike, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpan, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.ITransactionTracer, Sentry.Protocol.ITraceContext - { - public TransactionTracer(Sentry.IHub hub, Sentry.ITransactionContext context) { } - public System.Collections.Generic.IReadOnlyCollection Breadcrumbs { get; } - public Sentry.SentryContexts Contexts { get; set; } - public string? Description { get; set; } - public string? Distribution { get; set; } - public System.DateTimeOffset? EndTimestamp { get; } - public string? Environment { get; set; } - public System.Collections.Generic.IReadOnlyDictionary Extra { get; } - public System.Collections.Generic.IReadOnlyList Fingerprint { get; set; } - public bool IsFinished { get; } - public bool? IsParentSampled { get; set; } - public bool? IsSampled { get; } - public Sentry.SentryLevel? Level { get; set; } - public System.Collections.Generic.IReadOnlyDictionary Measurements { get; } - public string Name { get; set; } - public Sentry.TransactionNameSource NameSource { get; set; } - public string Operation { get; set; } - public string? Origin { get; } - public Sentry.SpanId? ParentSpanId { get; } - public string? Platform { get; set; } - public string? Release { get; set; } - public Sentry.SentryRequest Request { get; set; } - public double? SampleRate { get; } - public Sentry.SdkVersion Sdk { get; } - public Sentry.SpanId SpanId { get; } - public System.Collections.Generic.IReadOnlyCollection Spans { get; } - public System.DateTimeOffset StartTimestamp { get; } - public Sentry.SpanStatus? Status { get; set; } - public System.Collections.Generic.IReadOnlyDictionary Tags { get; } - public Sentry.SentryId TraceId { get; } - public Sentry.SentryUser User { get; set; } - public void AddBreadcrumb(Sentry.Breadcrumb breadcrumb) { } - public void Finish() { } - public void Finish(Sentry.SpanStatus status) { } - public void Finish(System.Exception exception) { } - public void Finish(System.Exception exception, Sentry.SpanStatus status) { } - public Sentry.ISpan? GetLastActiveSpan() { } - public Sentry.SentryTraceHeader GetTraceHeader() { } - public void SetExtra(string key, object? value) { } - public void SetMeasurement(string name, Sentry.Protocol.Measurement measurement) { } - public void SetTag(string key, string value) { } - public Sentry.ISpan StartChild(string operation) { } - public void UnsetTag(string key) { } - } - public sealed class UserFeedback : Sentry.ISentryJsonSerializable - { - public UserFeedback(Sentry.SentryId eventId, string? name, string? email, string? comments) { } - public string? Comments { get; } - public string? Email { get; } - public Sentry.SentryId EventId { get; } - public string? Name { get; } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } - public static Sentry.UserFeedback FromJson(System.Text.Json.JsonElement json) { } - } - public sealed class ViewHierarchy : Sentry.ISentryJsonSerializable - { - public ViewHierarchy(string renderingSystem) { } - public string RenderingSystem { get; set; } - public System.Collections.Generic.List Windows { get; } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } - } - public class ViewHierarchyAttachment : Sentry.SentryAttachment - { - public ViewHierarchyAttachment(Sentry.IAttachmentContent content) { } - } - public abstract class ViewHierarchyNode : Sentry.ISentryJsonSerializable - { - protected ViewHierarchyNode(string type) { } - public System.Collections.Generic.List Children { get; set; } - public string Type { get; set; } - protected abstract void WriteAdditionalProperties(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger); - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } - } -} -namespace Sentry.Ben.BlockingDetector -{ - public class SuppressBlockingDetection : System.IDisposable - { - public SuppressBlockingDetection() { } - public void Dispose() { } - } -} -namespace Sentry.Extensibility -{ - public abstract class BaseRequestPayloadExtractor : Sentry.Extensibility.IRequestPayloadExtractor - { - protected BaseRequestPayloadExtractor() { } - protected abstract object? DoExtractPayLoad(Sentry.Extensibility.IHttpRequest request); - public object? ExtractPayload(Sentry.Extensibility.IHttpRequest request) { } - protected abstract bool IsSupported(Sentry.Extensibility.IHttpRequest request); - } - public class DefaultRequestPayloadExtractor : Sentry.Extensibility.BaseRequestPayloadExtractor - { - public DefaultRequestPayloadExtractor() { } - protected override object? DoExtractPayLoad(Sentry.Extensibility.IHttpRequest request) { } - protected override bool IsSupported(Sentry.Extensibility.IHttpRequest request) { } - } - public static class DiagnosticLoggerExtensions - { - public static void LogDebug(this Sentry.Extensibility.IDiagnosticLogger logger, string message) { } - public static void LogDebug(this Sentry.Extensibility.IDiagnosticLogger logger, string message, TArg arg) { } - public static void LogDebug(this Sentry.Extensibility.IDiagnosticLogger logger, string message, TArg arg, TArg2 arg2) { } - public static void LogError(this Sentry.Extensibility.IDiagnosticLogger logger, string message) { } - public static void LogError(this Sentry.Extensibility.IDiagnosticLogger logger, System.Exception exception, string message) { } - public static void LogError(this Sentry.Extensibility.IDiagnosticLogger logger, string message, TArg arg) { } - public static void LogError(this Sentry.Extensibility.IDiagnosticLogger logger, System.Exception exception, string message, TArg arg) { } - public static void LogError(this Sentry.Extensibility.IDiagnosticLogger logger, System.Exception exception, string message, TArg arg, TArg2 arg2) { } - public static void LogError(this Sentry.Extensibility.IDiagnosticLogger logger, System.Exception exception, string message, TArg arg, TArg2 arg2, TArg3 arg3) { } - public static void LogError(this Sentry.Extensibility.IDiagnosticLogger logger, System.Exception exception, string message, TArg arg, TArg2 arg2, TArg3 arg3, TArg4 arg4) { } - public static void LogFatal(this Sentry.Extensibility.IDiagnosticLogger logger, string message) { } - public static void LogFatal(this Sentry.Extensibility.IDiagnosticLogger logger, System.Exception exception, string message) { } - public static void LogInfo(this Sentry.Extensibility.IDiagnosticLogger logger, string message) { } - public static void LogInfo(this Sentry.Extensibility.IDiagnosticLogger logger, string message, TArg arg) { } - public static void LogInfo(this Sentry.Extensibility.IDiagnosticLogger logger, string message, TArg arg, TArg2 arg2) { } - public static void LogInfo(this Sentry.Extensibility.IDiagnosticLogger logger, string message, TArg arg, TArg2 arg2, TArg3 arg3) { } - public static void LogWarning(this Sentry.Extensibility.IDiagnosticLogger logger, string message) { } - public static void LogWarning(this Sentry.Extensibility.IDiagnosticLogger logger, System.Exception exception, string message) { } - public static void LogWarning(this Sentry.Extensibility.IDiagnosticLogger logger, string message, TArg arg) { } - public static void LogWarning(this Sentry.Extensibility.IDiagnosticLogger logger, string message, TArg arg, TArg2 arg2) { } - } - public class DisabledHub : Sentry.IHub, Sentry.ISentryClient, Sentry.ISentryScopeManager, System.IDisposable - { - public static readonly Sentry.Extensibility.DisabledHub Instance; - public bool IsEnabled { get; } - public Sentry.SentryId LastEventId { get; } - public void BindClient(Sentry.ISentryClient client) { } - public void BindException(System.Exception exception, Sentry.ISpan span) { } - public Sentry.SentryId CaptureCheckIn(string monitorSlug, Sentry.CheckInStatus status, Sentry.SentryId? sentryId = default, System.TimeSpan? duration = default, Sentry.Scope? scope = null, System.Action? configureMonitorOptions = null) { } - public bool CaptureEnvelope(Sentry.Protocol.Envelopes.Envelope envelope) { } - public Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, System.Action configureScope) { } - public Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, Sentry.Scope? scope = null, Sentry.SentryHint? hint = null) { } - public Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, Sentry.SentryHint? hint, System.Action configureScope) { } - public void CaptureSession(Sentry.SessionUpdate sessionUpdate) { } - public void CaptureTransaction(Sentry.SentryTransaction transaction) { } - public void CaptureTransaction(Sentry.SentryTransaction transaction, Sentry.Scope? scope, Sentry.SentryHint? hint) { } - public void CaptureUserFeedback(Sentry.UserFeedback userFeedback) { } - public void ConfigureScope(System.Action configureScope) { } - public System.Threading.Tasks.Task ConfigureScopeAsync(System.Func configureScope) { } - public Sentry.TransactionContext ContinueTrace(Sentry.SentryTraceHeader? traceHeader, Sentry.BaggageHeader? baggageHeader, string? name = null, string? operation = null) { } - public Sentry.TransactionContext ContinueTrace(string? traceHeader, string? baggageHeader, string? name = null, string? operation = null) { } - public void Dispose() { } - public void EndSession(Sentry.SessionEndStatus status = 0) { } - public System.Threading.Tasks.Task FlushAsync(System.TimeSpan timeout) { } - public Sentry.BaggageHeader? GetBaggage() { } - public Sentry.ISpan? GetSpan() { } - public Sentry.SentryTraceHeader? GetTraceHeader() { } - public void PauseSession() { } - public System.IDisposable PushScope() { } - public System.IDisposable PushScope(TState state) { } - public void ResumeSession() { } - public void StartSession() { } - public Sentry.ITransactionTracer StartTransaction(Sentry.ITransactionContext context, System.Collections.Generic.IReadOnlyDictionary customSamplingContext) { } - } - public class FormRequestPayloadExtractor : Sentry.Extensibility.BaseRequestPayloadExtractor - { - public FormRequestPayloadExtractor() { } - protected override object? DoExtractPayLoad(Sentry.Extensibility.IHttpRequest request) { } - protected override bool IsSupported(Sentry.Extensibility.IHttpRequest request) { } - } - public sealed class HubAdapter : Sentry.IHub, Sentry.ISentryClient, Sentry.ISentryScopeManager - { - public static readonly Sentry.Extensibility.HubAdapter Instance; - public bool IsEnabled { get; } - public Sentry.SentryId LastEventId { get; } - public void AddBreadcrumb(string message, string? category = null, string? type = null, System.Collections.Generic.IDictionary? data = null, Sentry.BreadcrumbLevel level = 0) { } - public void AddBreadcrumb(Sentry.Infrastructure.ISystemClock clock, string message, string? category = null, string? type = null, System.Collections.Generic.IDictionary? data = null, Sentry.BreadcrumbLevel level = 0) { } - public void BindClient(Sentry.ISentryClient client) { } - public void BindException(System.Exception exception, Sentry.ISpan span) { } - public Sentry.SentryId CaptureCheckIn(string monitorSlug, Sentry.CheckInStatus status, Sentry.SentryId? sentryId = default, System.TimeSpan? duration = default, Sentry.Scope? scope = null, System.Action? monitorOptions = null) { } - public bool CaptureEnvelope(Sentry.Protocol.Envelopes.Envelope envelope) { } - public Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt) { } - public Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, Sentry.Scope? scope) { } - public Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, System.Action configureScope) { } - public Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, Sentry.Scope? scope, Sentry.SentryHint? hint = null) { } - public Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, Sentry.SentryHint? hint, System.Action configureScope) { } - public Sentry.SentryId CaptureException(System.Exception exception) { } - public void CaptureSession(Sentry.SessionUpdate sessionUpdate) { } - public void CaptureTransaction(Sentry.SentryTransaction transaction) { } - public void CaptureTransaction(Sentry.SentryTransaction transaction, Sentry.Scope? scope, Sentry.SentryHint? hint) { } - public void CaptureUserFeedback(Sentry.UserFeedback sentryUserFeedback) { } - public void ConfigureScope(System.Action configureScope) { } - public System.Threading.Tasks.Task ConfigureScopeAsync(System.Func configureScope) { } - public Sentry.TransactionContext ContinueTrace(Sentry.SentryTraceHeader? traceHeader, Sentry.BaggageHeader? baggageHeader, string? name = null, string? operation = null) { } - public Sentry.TransactionContext ContinueTrace(string? traceHeader, string? baggageHeader, string? name = null, string? operation = null) { } - public void EndSession(Sentry.SessionEndStatus status = 0) { } - public System.Threading.Tasks.Task FlushAsync(System.TimeSpan timeout) { } - public Sentry.BaggageHeader? GetBaggage() { } - public Sentry.ISpan? GetSpan() { } - public Sentry.SentryTraceHeader? GetTraceHeader() { } - public void PauseSession() { } - public System.IDisposable PushScope() { } - public System.IDisposable PushScope(TState state) { } - public void ResumeSession() { } - public void StartSession() { } - public Sentry.ITransactionTracer StartTransaction(Sentry.ITransactionContext context, System.Collections.Generic.IReadOnlyDictionary customSamplingContext) { } - } - public interface IBackgroundWorker - { - int QueuedItems { get; } - bool EnqueueEnvelope(Sentry.Protocol.Envelopes.Envelope envelope); - System.Threading.Tasks.Task FlushAsync(System.TimeSpan timeout); - } - public interface IDiagnosticLogger - { - bool IsEnabled(Sentry.SentryLevel level); - void Log(Sentry.SentryLevel logLevel, string message, System.Exception? exception = null, params object?[] args); - } - public interface IExceptionFilter - { - bool Filter(System.Exception ex); - } - public interface IHttpRequest - { - System.IO.Stream? Body { get; } - long? ContentLength { get; } - string? ContentType { get; } - System.Collections.Generic.IEnumerable>>? Form { get; } - } - public interface INetworkStatusListener - { - bool Online { get; } - System.Threading.Tasks.Task WaitForNetworkOnlineAsync(System.Threading.CancellationToken cancellationToken); - } - public interface IRequestPayloadExtractor - { - object? ExtractPayload(Sentry.Extensibility.IHttpRequest request); - } - public interface ISentryEventExceptionProcessor - { - void Process(System.Exception exception, Sentry.SentryEvent sentryEvent); - } - public interface ISentryEventProcessor - { - Sentry.SentryEvent? Process(Sentry.SentryEvent @event); - } - public interface ISentryEventProcessorWithHint : Sentry.Extensibility.ISentryEventProcessor - { - Sentry.SentryEvent? Process(Sentry.SentryEvent @event, Sentry.SentryHint hint); - } - public interface ISentryStackTraceFactory - { - Sentry.SentryStackTrace? Create(System.Exception? exception = null); - } - public interface ISentryTransactionProcessor - { - Sentry.SentryTransaction? Process(Sentry.SentryTransaction transaction); - } - public interface ISentryTransactionProcessorWithHint : Sentry.Extensibility.ISentryTransactionProcessor - { - Sentry.SentryTransaction? Process(Sentry.SentryTransaction transaction, Sentry.SentryHint hint); - } - public interface ITransport - { - System.Threading.Tasks.Task SendEnvelopeAsync(Sentry.Protocol.Envelopes.Envelope envelope, System.Threading.CancellationToken cancellationToken = default); - } - public class RequestBodyExtractionDispatcher : Sentry.Extensibility.IRequestPayloadExtractor - { - public RequestBodyExtractionDispatcher(System.Collections.Generic.IEnumerable extractors, Sentry.SentryOptions options, System.Func sizeSwitch) { } - public object? ExtractPayload(Sentry.Extensibility.IHttpRequest request) { } - } - public enum RequestSize - { - None = 0, - Small = 1, - Medium = 2, - Always = 3, - } - public abstract class SentryEventExceptionProcessor : Sentry.Extensibility.ISentryEventExceptionProcessor - where TException : System.Exception - { - protected SentryEventExceptionProcessor() { } - public void Process(System.Exception? exception, Sentry.SentryEvent sentryEvent) { } - protected abstract void ProcessException(TException exception, Sentry.SentryEvent sentryEvent); - } - public sealed class SentryStackTraceFactory : Sentry.Extensibility.ISentryStackTraceFactory - { - public SentryStackTraceFactory(Sentry.SentryOptions options) { } - public Sentry.SentryStackTrace? Create(System.Exception? exception = null) { } - } -} -namespace Sentry.Http -{ - public abstract class HttpTransportBase - { - protected HttpTransportBase(Sentry.SentryOptions options, System.Func? getEnvironmentVariable = null, Sentry.Infrastructure.ISystemClock? clock = null) { } - protected virtual System.Net.Http.HttpRequestMessage CreateRequest(Sentry.Protocol.Envelopes.Envelope envelope) { } - protected void HandleResponse(System.Net.Http.HttpResponseMessage response, Sentry.Protocol.Envelopes.Envelope envelope) { } - protected System.Threading.Tasks.Task HandleResponseAsync(System.Net.Http.HttpResponseMessage response, Sentry.Protocol.Envelopes.Envelope envelope, System.Threading.CancellationToken cancellationToken) { } - protected Sentry.Protocol.Envelopes.Envelope ProcessEnvelope(Sentry.Protocol.Envelopes.Envelope envelope) { } - protected System.IO.Stream ReadStreamFromHttpContent(System.Net.Http.HttpContent content) { } - } - public interface ISentryHttpClientFactory - { - System.Net.Http.HttpClient Create(Sentry.SentryOptions options); - } -} -namespace Sentry.Infrastructure -{ - public class ConsoleAndTraceDiagnosticLogger : Sentry.Infrastructure.DiagnosticLogger - { - public ConsoleAndTraceDiagnosticLogger(Sentry.SentryLevel minimalLevel) { } - protected override void LogMessage(string message) { } - } - public class ConsoleDiagnosticLogger : Sentry.Infrastructure.DiagnosticLogger - { - public ConsoleDiagnosticLogger(Sentry.SentryLevel minimalLevel) { } - protected override void LogMessage(string message) { } - } - public abstract class DiagnosticLogger : Sentry.Extensibility.IDiagnosticLogger - { - protected DiagnosticLogger(Sentry.SentryLevel minimalLevel) { } - public bool IsEnabled(Sentry.SentryLevel level) { } - public void Log(Sentry.SentryLevel logLevel, string message, System.Exception? exception = null, params object?[] args) { } - protected abstract void LogMessage(string message); - } - public class FileDiagnosticLogger : Sentry.Infrastructure.DiagnosticLogger - { - public FileDiagnosticLogger(string path, bool alsoWriteToConsole = false) { } - public FileDiagnosticLogger(string path, Sentry.SentryLevel minimalLevel, bool alsoWriteToConsole = false) { } - protected override void LogMessage(string message) { } - } - public interface ISystemClock - { - System.DateTimeOffset GetUtcNow(); - } - public sealed class SystemClock : Sentry.Infrastructure.ISystemClock - { - public static readonly Sentry.Infrastructure.SystemClock Clock; - public System.DateTimeOffset GetUtcNow() { } - } - public class TraceDiagnosticLogger : Sentry.Infrastructure.DiagnosticLogger - { - public TraceDiagnosticLogger(Sentry.SentryLevel minimalLevel) { } - protected override void LogMessage(string message) { } - } -} -namespace Sentry.Integrations -{ - public interface ISdkIntegration - { - void Register(Sentry.IHub hub, Sentry.SentryOptions options); - } -} -namespace Sentry.PlatformAbstractions -{ - public static class FrameworkInfo - { - public static System.Collections.Generic.IReadOnlyDictionary NetFxReleaseVersionMap { get; } - public static System.Collections.Generic.IEnumerable GetInstallations() { } - public static Sentry.PlatformAbstractions.FrameworkInstallation? GetLatest(int clr) { } - } - public class FrameworkInstallation - { - public FrameworkInstallation() { } - public Sentry.PlatformAbstractions.FrameworkProfile? Profile { get; set; } - public int? Release { get; set; } - public int? ServicePack { get; set; } - public string? ShortName { get; set; } - public System.Version? Version { get; set; } - public override string ToString() { } - } - public enum FrameworkProfile - { - Client = 0, - Full = 1, - } - public class SentryRuntime : System.IEquatable - { - public SentryRuntime(string? name = null, string? version = null, string? raw = null, string? identifier = null) { } - public string? Identifier { get; } - public string? Name { get; } - public string? Raw { get; } - public string? Version { get; } - public static Sentry.PlatformAbstractions.SentryRuntime Current { get; } - public bool Equals(Sentry.PlatformAbstractions.SentryRuntime? other) { } - public override bool Equals(object? obj) { } - public override int GetHashCode() { } - public override string? ToString() { } - } - public static class SentryRuntimeExtensions - { - public static bool IsMono(this Sentry.PlatformAbstractions.SentryRuntime runtime) { } - public static bool IsNetCore(this Sentry.PlatformAbstractions.SentryRuntime runtime) { } - public static bool IsNetFx(this Sentry.PlatformAbstractions.SentryRuntime runtime) { } - } -} -namespace Sentry.Protocol -{ - public sealed class App : Sentry.ISentryJsonSerializable - { - public const string Type = "app"; - public App() { } - public string? Build { get; set; } - public string? BuildType { get; set; } - public string? Hash { get; set; } - public string? Identifier { get; set; } - public bool? InForeground { get; set; } - public string? Name { get; set; } - public System.DateTimeOffset? StartTime { get; set; } - public string? Version { get; set; } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? _) { } - public static Sentry.Protocol.App FromJson(System.Text.Json.JsonElement json) { } - } - public sealed class Browser : Sentry.ISentryJsonSerializable - { - public const string Type = "browser"; - public Browser() { } - public string? Name { get; set; } - public string? Version { get; set; } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? _) { } - public static Sentry.Protocol.Browser FromJson(System.Text.Json.JsonElement json) { } - } - public sealed class DebugImage : Sentry.ISentryJsonSerializable - { - public DebugImage() { } - public string? CodeFile { get; set; } - public string? CodeId { get; set; } - public string? DebugChecksum { get; set; } - public string? DebugFile { get; set; } - public string? DebugId { get; set; } - public long? ImageAddress { get; set; } - public long? ImageSize { get; set; } - public string? Type { get; set; } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } - public static Sentry.Protocol.DebugImage FromJson(System.Text.Json.JsonElement json) { } - } - public sealed class Device : Sentry.ISentryJsonSerializable - { - public const string Type = "device"; - public Device() { } - public string? Architecture { get; set; } - public float? BatteryLevel { get; set; } - public string? BatteryStatus { get; set; } - public System.DateTimeOffset? BootTime { get; set; } - public string? Brand { get; set; } - public string? CpuDescription { get; set; } - public string? DeviceType { get; set; } - public string? DeviceUniqueIdentifier { get; set; } - public long? ExternalFreeStorage { get; set; } - public long? ExternalStorageSize { get; set; } - public string? Family { get; set; } - public long? FreeMemory { get; set; } - public long? FreeStorage { get; set; } - public bool? IsCharging { get; set; } - public bool? IsOnline { get; set; } - public bool? LowMemory { get; set; } - public string? Manufacturer { get; set; } - public long? MemorySize { get; set; } - public string? Model { get; set; } - public string? ModelId { get; set; } - public string? Name { get; set; } - public Sentry.Protocol.DeviceOrientation? Orientation { get; set; } - public int? ProcessorCount { get; set; } - public float? ProcessorFrequency { get; set; } - public float? ScreenDensity { get; set; } - public int? ScreenDpi { get; set; } - public string? ScreenResolution { get; set; } - public bool? Simulator { get; set; } - public long? StorageSize { get; set; } - public bool? SupportsAccelerometer { get; set; } - public bool? SupportsAudio { get; set; } - public bool? SupportsGyroscope { get; set; } - public bool? SupportsLocationService { get; set; } - public bool? SupportsVibration { get; set; } - public System.TimeZoneInfo? Timezone { get; set; } - public long? UsableMemory { get; set; } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? _) { } - public static Sentry.Protocol.Device FromJson(System.Text.Json.JsonElement json) { } - } - public enum DeviceOrientation - { - [System.Runtime.Serialization.EnumMember(Value="portrait")] - Portrait = 0, - [System.Runtime.Serialization.EnumMember(Value="landscape")] - Landscape = 1, - } - public sealed class Gpu : Sentry.ISentryJsonSerializable - { - public const string Type = "gpu"; - public Gpu() { } - public string? ApiType { get; set; } - public string? GraphicsShaderLevel { get; set; } - public int? Id { get; set; } - public int? MaxTextureSize { get; set; } - public int? MemorySize { get; set; } - public bool? MultiThreadedRendering { get; set; } - public string? Name { get; set; } - public string? NpotSupport { get; set; } - public bool? SupportsComputeShaders { get; set; } - public bool? SupportsDrawCallInstancing { get; set; } - public bool? SupportsGeometryShaders { get; set; } - public bool? SupportsRayTracing { get; set; } - public string? VendorId { get; set; } - public string? VendorName { get; set; } - public string? Version { get; set; } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? _) { } - public static Sentry.Protocol.Gpu FromJson(System.Text.Json.JsonElement json) { } - } - public interface ITraceContext - { - string? Description { get; } - bool? IsSampled { get; } - string Operation { get; } - string? Origin { get; } - Sentry.SpanId? ParentSpanId { get; } - Sentry.SpanId SpanId { get; } - Sentry.SpanStatus? Status { get; } - Sentry.SentryId TraceId { get; } - } - public sealed class Measurement : Sentry.ISentryJsonSerializable - { - public Sentry.MeasurementUnit Unit { get; } - public object Value { get; } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } - public static Sentry.Protocol.Measurement FromJson(System.Text.Json.JsonElement json) { } - } - public sealed class Mechanism : Sentry.ISentryJsonSerializable - { - public static readonly string DescriptionKey; - public static readonly string HandledKey; - public static readonly string MechanismKey; - public Mechanism() { } - public System.Collections.Generic.IDictionary Data { get; } - public string? Description { get; set; } - public int? ExceptionId { get; set; } - public bool? Handled { get; set; } - public string? HelpLink { get; set; } - public bool IsExceptionGroup { get; set; } - public System.Collections.Generic.IDictionary Meta { get; } - public int? ParentId { get; set; } - public string? Source { get; set; } - public bool Synthetic { get; set; } - public string Type { get; set; } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } - public static Sentry.Protocol.Mechanism FromJson(System.Text.Json.JsonElement json) { } - } - public sealed class OperatingSystem : Sentry.ISentryJsonSerializable - { - public const string Type = "os"; - public OperatingSystem() { } - public string? Build { get; set; } - public string? KernelVersion { get; set; } - public string? Name { get; set; } - public string? RawDescription { get; set; } - public bool? Rooted { get; set; } - public string? Version { get; set; } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? _) { } - public static Sentry.Protocol.OperatingSystem FromJson(System.Text.Json.JsonElement json) { } - } - public sealed class Response : Sentry.ISentryJsonSerializable - { - public const string Type = "response"; - public Response() { } - public long? BodySize { get; set; } - public string? Cookies { get; set; } - public object? Data { get; set; } - public System.Collections.Generic.IDictionary Headers { get; } - public short? StatusCode { get; set; } - public Sentry.Protocol.Response Clone() { } - public void UpdateFrom(Sentry.Protocol.Response source) { } - public void UpdateFrom(object source) { } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } - public static Sentry.Protocol.Response FromJson(System.Text.Json.JsonElement json) { } - } - public sealed class Runtime : Sentry.ISentryJsonSerializable - { - public const string Type = "runtime"; - public Runtime() { } - public string? Build { get; set; } - public string? Identifier { get; set; } - public string? Name { get; set; } - public string? RawDescription { get; set; } - public string? Version { get; set; } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? _) { } - public static Sentry.Protocol.Runtime FromJson(System.Text.Json.JsonElement json) { } - } - public sealed class SentryException : Sentry.ISentryJsonSerializable - { - public SentryException() { } - public Sentry.Protocol.Mechanism? Mechanism { get; set; } - public string? Module { get; set; } - public Sentry.SentryStackTrace? Stacktrace { get; set; } - public int ThreadId { get; set; } - public string? Type { get; set; } - public string? Value { get; set; } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } - public static Sentry.Protocol.SentryException FromJson(System.Text.Json.JsonElement json) { } - } - public class Trace : Sentry.ISentryJsonSerializable, Sentry.Protocol.ITraceContext - { - public const string Type = "trace"; - public Trace() { } - public string? Description { get; set; } - public bool? IsSampled { get; } - public string Operation { get; set; } - public string? Origin { get; } - public Sentry.SpanId? ParentSpanId { get; set; } - public Sentry.SpanId SpanId { get; set; } - public Sentry.SpanStatus? Status { get; set; } - public Sentry.SentryId TraceId { get; set; } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } - public static Sentry.Protocol.Trace FromJson(System.Text.Json.JsonElement json) { } - } -} -namespace Sentry.Protocol.Envelopes -{ - public sealed class Envelope : Sentry.Protocol.Envelopes.ISerializable, System.IDisposable - { - public Envelope(System.Collections.Generic.IReadOnlyDictionary header, System.Collections.Generic.IReadOnlyList items) { } - public System.Collections.Generic.IReadOnlyDictionary Header { get; } - public System.Collections.Generic.IReadOnlyList Items { get; } - public void Dispose() { } - public void Serialize(System.IO.Stream stream, Sentry.Extensibility.IDiagnosticLogger? logger) { } - public System.Threading.Tasks.Task SerializeAsync(System.IO.Stream stream, Sentry.Extensibility.IDiagnosticLogger? logger, System.Threading.CancellationToken cancellationToken = default) { } - public Sentry.SentryId? TryGetEventId() { } - public static System.Threading.Tasks.Task DeserializeAsync(System.IO.Stream stream, System.Threading.CancellationToken cancellationToken = default) { } - public static Sentry.Protocol.Envelopes.Envelope FromCheckIn(Sentry.SentryCheckIn checkIn) { } - public static Sentry.Protocol.Envelopes.Envelope FromEvent(Sentry.SentryEvent @event, Sentry.Extensibility.IDiagnosticLogger? logger = null, System.Collections.Generic.IReadOnlyCollection? attachments = null, Sentry.SessionUpdate? sessionUpdate = null) { } - public static Sentry.Protocol.Envelopes.Envelope FromSession(Sentry.SessionUpdate sessionUpdate) { } - public static Sentry.Protocol.Envelopes.Envelope FromTransaction(Sentry.SentryTransaction transaction) { } - public static Sentry.Protocol.Envelopes.Envelope FromUserFeedback(Sentry.UserFeedback sentryUserFeedback) { } - } - public sealed class EnvelopeItem : Sentry.Protocol.Envelopes.ISerializable, System.IDisposable - { - public EnvelopeItem(System.Collections.Generic.IReadOnlyDictionary header, Sentry.Protocol.Envelopes.ISerializable payload) { } - public System.Collections.Generic.IReadOnlyDictionary Header { get; } - public Sentry.Protocol.Envelopes.ISerializable Payload { get; } - public void Dispose() { } - public void Serialize(System.IO.Stream stream, Sentry.Extensibility.IDiagnosticLogger? logger) { } - public System.Threading.Tasks.Task SerializeAsync(System.IO.Stream stream, Sentry.Extensibility.IDiagnosticLogger? logger, System.Threading.CancellationToken cancellationToken = default) { } - public string? TryGetFileName() { } - public long? TryGetLength() { } - public string? TryGetType() { } - public static System.Threading.Tasks.Task DeserializeAsync(System.IO.Stream stream, System.Threading.CancellationToken cancellationToken = default) { } - public static Sentry.Protocol.Envelopes.EnvelopeItem FromAttachment(Sentry.SentryAttachment attachment) { } - public static Sentry.Protocol.Envelopes.EnvelopeItem FromCheckIn(Sentry.SentryCheckIn checkIn) { } - public static Sentry.Protocol.Envelopes.EnvelopeItem FromEvent(Sentry.SentryEvent @event) { } - public static Sentry.Protocol.Envelopes.EnvelopeItem FromSession(Sentry.SessionUpdate sessionUpdate) { } - public static Sentry.Protocol.Envelopes.EnvelopeItem FromTransaction(Sentry.SentryTransaction transaction) { } - public static Sentry.Protocol.Envelopes.EnvelopeItem FromUserFeedback(Sentry.UserFeedback sentryUserFeedback) { } - } - public interface ISerializable - { - void Serialize(System.IO.Stream stream, Sentry.Extensibility.IDiagnosticLogger? logger); - System.Threading.Tasks.Task SerializeAsync(System.IO.Stream stream, Sentry.Extensibility.IDiagnosticLogger? logger, System.Threading.CancellationToken cancellationToken = default); - } -} -namespace Sentry.Reflection -{ - public static class AssemblyExtensions - { - public static Sentry.SdkVersion GetNameAndVersion(this System.Reflection.Assembly asm) { } - } -} -public static class SentryExceptionExtensions -{ - public static void AddSentryContext(this System.Exception ex, string name, System.Collections.Generic.IReadOnlyDictionary data) { } - public static void AddSentryTag(this System.Exception ex, string name, string value) { } - public static void SetSentryMechanism(this System.Exception ex, string type, string? description = null, bool? handled = default) { } -} \ No newline at end of file diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt deleted file mode 100644 index bb10750b76..0000000000 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt +++ /dev/null @@ -1,1812 +0,0 @@ -[assembly: System.CLSCompliant(true)] -namespace Sentry -{ - public enum AttachmentType - { - Default = 0, - Minidump = 1, - AppleCrashReport = 2, - UnrealContext = 3, - UnrealLogs = 4, - ViewHierarchy = 5, - } - public class BaggageHeader - { - public override string ToString() { } - } - [System.Diagnostics.DebuggerDisplay("Message: {Message}, Type: {Type}")] - public sealed class Breadcrumb : Sentry.ISentryJsonSerializable - { - public Breadcrumb(string message, string type, System.Collections.Generic.IReadOnlyDictionary? data = null, string? category = null, Sentry.BreadcrumbLevel level = 0) { } - public string? Category { get; } - public System.Collections.Generic.IReadOnlyDictionary? Data { get; } - public Sentry.BreadcrumbLevel Level { get; } - public string? Message { get; } - public System.DateTimeOffset Timestamp { get; } - public string? Type { get; } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } - public static Sentry.Breadcrumb FromJson(System.Text.Json.JsonElement json) { } - } - public enum BreadcrumbLevel - { - [System.Runtime.Serialization.EnumMember(Value="debug")] - Debug = -1, - [System.Runtime.Serialization.EnumMember(Value="info")] - Info = 0, - [System.Runtime.Serialization.EnumMember(Value="warning")] - Warning = 1, - [System.Runtime.Serialization.EnumMember(Value="error")] - Error = 2, - [System.Runtime.Serialization.EnumMember(Value="critical")] - Critical = 3, - } - public static class BuiltInSystemDiagnosticsMeters - { - public static readonly Sentry.StringOrRegex MicrosoftAspNetCoreDiagnostics; - public static readonly Sentry.StringOrRegex MicrosoftAspNetCoreHeaderParsing; - public static readonly Sentry.StringOrRegex MicrosoftAspNetCoreHosting; - public static readonly Sentry.StringOrRegex MicrosoftAspNetCoreHttpConnections; - public static readonly Sentry.StringOrRegex MicrosoftAspNetCoreRateLimiting; - public static readonly Sentry.StringOrRegex MicrosoftAspNetCoreRouting; - public static readonly Sentry.StringOrRegex MicrosoftAspNetCoreServerKestrel; - public static readonly Sentry.StringOrRegex MicrosoftExtensionsDiagnosticsHealthChecks; - public static readonly Sentry.StringOrRegex MicrosoftExtensionsDiagnosticsResourceMonitoring; - public static readonly Sentry.StringOrRegex OpenTelemetryInstrumentationRuntime; - public static readonly Sentry.StringOrRegex SystemNetHttp; - public static readonly Sentry.StringOrRegex SystemNetNameResolution; - public static System.Collections.Generic.IList All { get; } - } - public class ByteAttachmentContent : Sentry.IAttachmentContent - { - public ByteAttachmentContent(byte[] bytes) { } - public System.IO.Stream GetStream() { } - } - public enum CheckInStatus - { - InProgress = 0, - Ok = 1, - Error = 2, - } - [System.Obsolete("WARNING: This method deliberately causes a crash, and should not be used in a rea" + - "l application.")] - public enum CrashType - { - Managed = 0, - ManagedBackgroundThread = 1, - } - public class Debouncer - { - public static Sentry.Debouncer PerApplicationLifetime(int eventMaximum = 1, System.TimeSpan? cooldown = default) { } - public static Sentry.Debouncer PerDay(int eventMaximum = 1, System.TimeSpan? cooldown = default) { } - public static Sentry.Debouncer PerHour(int eventMaximum = 1, System.TimeSpan? cooldown = default) { } - public static Sentry.Debouncer PerMinute(int eventMaximum = 1, System.TimeSpan? cooldown = default) { } - } - [System.Flags] - public enum DeduplicateMode - { - SameEvent = 1, - SameExceptionInstance = 2, - InnerException = 4, - AggregateException = 8, - All = 2147483647, - } - public class DefaultSentryScopeStateProcessor : Sentry.ISentryScopeStateProcessor - { - public DefaultSentryScopeStateProcessor() { } - public void Apply(Sentry.Scope scope, object state) { } - } - [System.AttributeUsage(System.AttributeTargets.Assembly)] - public class DsnAttribute : System.Attribute - { - public DsnAttribute(string dsn) { } - public string Dsn { get; } - } - public static class EventLikeExtensions - { - public static void AddBreadcrumb(this Sentry.IEventLike eventLike, string message, string? category, string? type, System.ValueTuple? dataPair = default, Sentry.BreadcrumbLevel level = 0) { } - public static void AddBreadcrumb(this Sentry.IEventLike eventLike, string message, string? category = null, string? type = null, System.Collections.Generic.IReadOnlyDictionary? data = null, Sentry.BreadcrumbLevel level = 0) { } - public static void AddBreadcrumb(this Sentry.IEventLike eventLike, System.DateTimeOffset? timestamp, string message, string? category = null, string? type = null, System.Collections.Generic.IReadOnlyDictionary? data = null, Sentry.BreadcrumbLevel level = 0) { } - public static bool HasUser(this Sentry.IEventLike eventLike) { } - public static void SetFingerprint(this Sentry.IEventLike eventLike, System.Collections.Generic.IEnumerable fingerprint) { } - public static void SetFingerprint(this Sentry.IEventLike eventLike, params string[] fingerprint) { } - } - public class ExperimentalMetricsOptions - { - public ExperimentalMetricsOptions() { } - public System.Collections.Generic.IList CaptureSystemDiagnosticsInstruments { get; set; } - public System.Collections.Generic.IList CaptureSystemDiagnosticsMeters { get; set; } - public bool EnableCodeLocations { get; set; } - } - public class FileAttachmentContent : Sentry.IAttachmentContent - { - public FileAttachmentContent(string filePath) { } - public FileAttachmentContent(string filePath, bool readFileAsynchronously) { } - public System.IO.Stream GetStream() { } - } - public static class HasExtraExtensions - { - public static void SetExtras(this Sentry.IHasExtra hasExtra, System.Collections.Generic.IEnumerable> values) { } - } - public static class HasTagsExtensions - { - public static void SetTags(this Sentry.IHasTags hasTags, System.Collections.Generic.IEnumerable> tags) { } - } - public delegate bool HeapDumpTrigger(long usedMemory, long totalMemory); - public static class HintTypes - { - public const string HttpResponseMessage = "http-response-message"; - } - public readonly struct HttpStatusCodeRange : System.IEquatable - { - public HttpStatusCodeRange(int statusCode) { } - public HttpStatusCodeRange(int start, int end) { } - public int End { get; init; } - public int Start { get; init; } - public bool Contains(int statusCode) { } - public bool Contains(System.Net.HttpStatusCode statusCode) { } - public static Sentry.HttpStatusCodeRange op_Implicit(int statusCode) { } - public static Sentry.HttpStatusCodeRange op_Implicit(System.Net.HttpStatusCode statusCode) { } - public static Sentry.HttpStatusCodeRange op_Implicit([System.Runtime.CompilerServices.TupleElementNames(new string[] { - "Start", - "End"})] System.ValueTuple range) { } - public static Sentry.HttpStatusCodeRange op_Implicit([System.Runtime.CompilerServices.TupleElementNames(new string[] { - "start", - "end"})] System.ValueTuple range) { } - } - public static class HubExtensions - { - public static void AddBreadcrumb(this Sentry.IHub hub, Sentry.Breadcrumb breadcrumb, Sentry.SentryHint? hint = null) { } - public static void AddBreadcrumb(this Sentry.IHub hub, string message, string? category = null, string? type = null, System.Collections.Generic.IDictionary? data = null, Sentry.BreadcrumbLevel level = 0) { } - public static void AddBreadcrumb(this Sentry.IHub hub, Sentry.Infrastructure.ISystemClock? clock, string message, string? category = null, string? type = null, System.Collections.Generic.IDictionary? data = null, Sentry.BreadcrumbLevel level = 0) { } - public static Sentry.SentryId CaptureException(this Sentry.IHub hub, System.Exception ex, System.Action configureScope) { } - public static Sentry.SentryId CaptureMessage(this Sentry.IHub hub, string message, System.Action configureScope, Sentry.SentryLevel level = 1) { } - public static void LockScope(this Sentry.IHub hub) { } - public static System.IDisposable PushAndLockScope(this Sentry.IHub hub) { } - public static Sentry.ITransactionTracer StartTransaction(this Sentry.IHub hub, Sentry.ITransactionContext context) { } - public static Sentry.ITransactionTracer StartTransaction(this Sentry.IHub hub, string name, string operation) { } - public static Sentry.ITransactionTracer StartTransaction(this Sentry.IHub hub, string name, string operation, Sentry.SentryTraceHeader traceHeader) { } - public static Sentry.ITransactionTracer StartTransaction(this Sentry.IHub hub, string name, string operation, string? description) { } - public static void UnlockScope(this Sentry.IHub hub) { } - } - public interface IAttachmentContent - { - System.IO.Stream GetStream(); - } - public interface IEventLike : Sentry.IHasExtra, Sentry.IHasTags - { - System.Collections.Generic.IReadOnlyCollection Breadcrumbs { get; } - Sentry.SentryContexts Contexts { get; set; } - string? Distribution { get; set; } - string? Environment { get; set; } - System.Collections.Generic.IReadOnlyList Fingerprint { get; set; } - Sentry.SentryLevel? Level { get; set; } - string? Release { get; set; } - Sentry.SentryRequest Request { get; set; } - Sentry.SdkVersion Sdk { get; } - string? TransactionName { get; set; } - Sentry.SentryUser User { get; set; } - void AddBreadcrumb(Sentry.Breadcrumb breadcrumb); - } - public interface IHasExtra - { - System.Collections.Generic.IReadOnlyDictionary Extra { get; } - void SetExtra(string key, object? value); - } - public interface IHasTags - { - System.Collections.Generic.IReadOnlyDictionary Tags { get; } - void SetTag(string key, string value); - void UnsetTag(string key); - } - public interface IHub : Sentry.ISentryClient, Sentry.ISentryScopeManager - { - Sentry.SentryId LastEventId { get; } - void BindException(System.Exception exception, Sentry.ISpan span); - Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, System.Action configureScope); - Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, Sentry.SentryHint? hint, System.Action configureScope); - Sentry.TransactionContext ContinueTrace(Sentry.SentryTraceHeader? traceHeader, Sentry.BaggageHeader? baggageHeader, string? name = null, string? operation = null); - Sentry.TransactionContext ContinueTrace(string? traceHeader, string? baggageHeader, string? name = null, string? operation = null); - void EndSession(Sentry.SessionEndStatus status = 0); - Sentry.BaggageHeader? GetBaggage(); - Sentry.ISpan? GetSpan(); - Sentry.SentryTraceHeader? GetTraceHeader(); - void PauseSession(); - void ResumeSession(); - void StartSession(); - Sentry.ITransactionTracer StartTransaction(Sentry.ITransactionContext context, System.Collections.Generic.IReadOnlyDictionary customSamplingContext); - } - public interface IScopeObserver - { - void AddBreadcrumb(Sentry.Breadcrumb breadcrumb); - void SetExtra(string key, object? value); - void SetTag(string key, string value); - void SetUser(Sentry.SentryUser? user); - void UnsetTag(string key); - } - public interface ISentryClient - { - bool IsEnabled { get; } - Sentry.SentryId CaptureCheckIn(string monitorSlug, Sentry.CheckInStatus status, Sentry.SentryId? sentryId = default, System.TimeSpan? duration = default, Sentry.Scope? scope = null, System.Action? configureMonitorOptions = null); - bool CaptureEnvelope(Sentry.Protocol.Envelopes.Envelope envelope); - Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, Sentry.Scope? scope = null, Sentry.SentryHint? hint = null); - void CaptureSession(Sentry.SessionUpdate sessionUpdate); - void CaptureTransaction(Sentry.SentryTransaction transaction); - void CaptureTransaction(Sentry.SentryTransaction transaction, Sentry.Scope? scope, Sentry.SentryHint? hint); - void CaptureUserFeedback(Sentry.UserFeedback userFeedback); - System.Threading.Tasks.Task FlushAsync(System.TimeSpan timeout); - } - public interface ISentryJsonSerializable - { - void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger); - } - public interface ISentryScopeManager - { - void BindClient(Sentry.ISentryClient client); - void ConfigureScope(System.Action configureScope); - System.Threading.Tasks.Task ConfigureScopeAsync(System.Func configureScope); - System.IDisposable PushScope(); - System.IDisposable PushScope(TState state); - } - public interface ISentryScopeStateProcessor - { - void Apply(Sentry.Scope scope, object state); - } - public interface ISentrySession - { - string? DistinctId { get; } - string? Environment { get; } - int ErrorCount { get; } - Sentry.SentryId Id { get; } - string? IpAddress { get; } - string Release { get; } - System.DateTimeOffset StartTimestamp { get; } - string? UserAgent { get; } - } - public interface ISentryUserFactory - { - Sentry.SentryUser? Create(); - } - public interface ISpan : Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpanData, Sentry.Protocol.ITraceContext - { - new string? Description { get; set; } - new string Operation { get; set; } - new Sentry.SpanStatus? Status { get; set; } - void Finish(); - void Finish(Sentry.SpanStatus status); - void Finish(System.Exception exception); - void Finish(System.Exception exception, Sentry.SpanStatus status); - Sentry.ISpan StartChild(string operation); - } - public interface ISpanData : Sentry.IHasExtra, Sentry.IHasTags, Sentry.Protocol.ITraceContext - { - System.DateTimeOffset? EndTimestamp { get; } - bool IsFinished { get; } - System.Collections.Generic.IReadOnlyDictionary Measurements { get; } - System.DateTimeOffset StartTimestamp { get; } - Sentry.SentryTraceHeader GetTraceHeader(); - void SetMeasurement(string name, Sentry.Protocol.Measurement measurement); - } - public interface ITransactionContext : Sentry.Protocol.ITraceContext - { - bool? IsParentSampled { get; } - string Name { get; } - Sentry.TransactionNameSource NameSource { get; } - } - public interface ITransactionData : Sentry.IEventLike, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.Protocol.ITraceContext - { - string? Platform { get; set; } - } - public interface ITransactionTracer : Sentry.IEventLike, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpan, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.Protocol.ITraceContext - { - new bool? IsParentSampled { get; set; } - new string Name { get; set; } - System.Collections.Generic.IReadOnlyCollection Spans { get; } - Sentry.ISpan? GetLastActiveSpan(); - } - public enum InstructionAddressAdjustment - { - Auto = 0, - All = 1, - AllButFirst = 2, - None = 3, - } - public enum Instrumenter - { - Sentry = 0, - OpenTelemetry = 1, - } - public readonly struct MeasurementUnit : System.IEquatable - { - public static Sentry.MeasurementUnit None; - public bool Equals(Sentry.MeasurementUnit other) { } - public override bool Equals(object? obj) { } - public override int GetHashCode() { } - public override string ToString() { } - public static Sentry.MeasurementUnit Custom(string name) { } - public static Sentry.MeasurementUnit op_Implicit(Sentry.MeasurementUnit.Duration unit) { } - public static Sentry.MeasurementUnit op_Implicit(Sentry.MeasurementUnit.Fraction unit) { } - public static Sentry.MeasurementUnit op_Implicit(Sentry.MeasurementUnit.Information unit) { } - public static bool operator !=(Sentry.MeasurementUnit left, Sentry.MeasurementUnit right) { } - public static bool operator ==(Sentry.MeasurementUnit left, Sentry.MeasurementUnit right) { } - public enum Duration - { - Nanosecond = 0, - Microsecond = 1, - Millisecond = 2, - Second = 3, - Minute = 4, - Hour = 5, - Day = 6, - Week = 7, - } - public enum Fraction - { - Ratio = 0, - Percent = 1, - } - public enum Information - { - Bit = 0, - Byte = 1, - Kilobyte = 2, - Kibibyte = 3, - Megabyte = 4, - Mebibyte = 5, - Gigabyte = 6, - Gibibyte = 7, - Terabyte = 8, - Tebibyte = 9, - Petabyte = 10, - Pebibyte = 11, - Exabyte = 12, - Exbibyte = 13, - } - } - public enum ReportAssembliesMode - { - None = 0, - Version = 1, - InformationalVersion = 2, - } - public class Scope : Sentry.IEventLike, Sentry.IHasExtra, Sentry.IHasTags - { - public Scope(Sentry.SentryOptions? options) { } - public System.Collections.Generic.IReadOnlyCollection Attachments { get; } - public System.Collections.Generic.IReadOnlyCollection Breadcrumbs { get; } - public Sentry.SentryContexts Contexts { get; set; } - public string? Distribution { get; set; } - public string? Environment { get; set; } - public System.Collections.Generic.IReadOnlyDictionary Extra { get; } - public System.Collections.Generic.IReadOnlyList Fingerprint { get; set; } - public Sentry.SentryLevel? Level { get; set; } - public string? Release { get; set; } - public Sentry.SentryRequest Request { get; set; } - public Sentry.SdkVersion Sdk { get; } - public Sentry.ISpan? Span { get; set; } - public System.Collections.Generic.IReadOnlyDictionary Tags { get; } - public Sentry.ITransactionTracer? Transaction { get; set; } - public string? TransactionName { get; set; } - public Sentry.SentryUser User { get; set; } - public void AddAttachment(Sentry.SentryAttachment attachment) { } - public void AddAttachment(string filePath, Sentry.AttachmentType type = 0, string? contentType = null) { } - public void AddAttachment(byte[] data, string fileName, Sentry.AttachmentType type = 0, string? contentType = null) { } - public void AddAttachment(System.IO.Stream stream, string fileName, Sentry.AttachmentType type = 0, string? contentType = null) { } - public void AddBreadcrumb(Sentry.Breadcrumb breadcrumb) { } - public void AddBreadcrumb(Sentry.Breadcrumb breadcrumb, Sentry.SentryHint hint) { } - public void AddEventProcessor(Sentry.Extensibility.ISentryEventProcessor processor) { } - public void AddEventProcessor(System.Func processor) { } - public void AddEventProcessors(System.Collections.Generic.IEnumerable processors) { } - public void AddExceptionProcessor(Sentry.Extensibility.ISentryEventExceptionProcessor processor) { } - public void AddExceptionProcessors(System.Collections.Generic.IEnumerable processors) { } - public void AddTransactionProcessor(Sentry.Extensibility.ISentryTransactionProcessor processor) { } - public void AddTransactionProcessor(System.Func processor) { } - public void AddTransactionProcessors(System.Collections.Generic.IEnumerable processors) { } - public void Apply(Sentry.IEventLike other) { } - public void Apply(Sentry.Scope other) { } - public void Apply(object state) { } - public void Clear() { } - public void ClearAttachments() { } - public void ClearBreadcrumbs() { } - public Sentry.Scope Clone() { } - public System.Collections.Generic.IEnumerable GetAllEventProcessors() { } - public System.Collections.Generic.IEnumerable GetAllExceptionProcessors() { } - public System.Collections.Generic.IEnumerable GetAllTransactionProcessors() { } - public void SetExtra(string key, object? value) { } - public void SetTag(string key, string value) { } - public void UnsetTag(string key) { } - } - public sealed class SdkVersion : Sentry.ISentryJsonSerializable - { - public SdkVersion() { } - public string? Name { get; set; } - public System.Collections.Generic.IEnumerable Packages { get; } - public string? Version { get; set; } - public void AddIntegration(string integration) { } - public void AddPackage(string name, string version) { } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } - public static Sentry.SdkVersion FromJson(System.Text.Json.JsonElement json) { } - } - [System.Diagnostics.DebuggerDisplay("{FileName}")] - public class SentryAttachment - { - public SentryAttachment(Sentry.AttachmentType type, Sentry.IAttachmentContent content, string fileName, string? contentType) { } - public Sentry.IAttachmentContent Content { get; } - public string? ContentType { get; } - public string FileName { get; } - public Sentry.AttachmentType Type { get; } - } - public class SentryCheckIn : Sentry.ISentryJsonSerializable - { - public SentryCheckIn(string monitorSlug, Sentry.CheckInStatus status, Sentry.SentryId? sentryId = default) { } - public System.TimeSpan? Duration { get; set; } - public string? Environment { get; set; } - public Sentry.SentryId Id { get; } - public string MonitorSlug { get; } - public string? Release { get; set; } - public Sentry.CheckInStatus Status { get; } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } - } - public class SentryClient : Sentry.ISentryClient, System.IDisposable - { - public SentryClient(Sentry.SentryOptions options) { } - public bool IsEnabled { get; } - public Sentry.SentryId CaptureCheckIn(string monitorSlug, Sentry.CheckInStatus status, Sentry.SentryId? sentryId = default, System.TimeSpan? duration = default, Sentry.Scope? scope = null, System.Action? configureMonitorOptions = null) { } - public bool CaptureEnvelope(Sentry.Protocol.Envelopes.Envelope envelope) { } - public Sentry.SentryId CaptureEvent(Sentry.SentryEvent? @event, Sentry.Scope? scope = null, Sentry.SentryHint? hint = null) { } - public void CaptureSession(Sentry.SessionUpdate sessionUpdate) { } - public void CaptureTransaction(Sentry.SentryTransaction transaction) { } - public void CaptureTransaction(Sentry.SentryTransaction transaction, Sentry.Scope? scope, Sentry.SentryHint? hint) { } - public void CaptureUserFeedback(Sentry.UserFeedback userFeedback) { } - public void Dispose() { } - public System.Threading.Tasks.Task FlushAsync(System.TimeSpan timeout) { } - } - public static class SentryClientExtensions - { - public static Sentry.SentryId CaptureException(this Sentry.ISentryClient client, System.Exception ex) { } - public static Sentry.SentryId CaptureMessage(this Sentry.ISentryClient client, string message, Sentry.SentryLevel level = 1) { } - public static void CaptureUserFeedback(this Sentry.ISentryClient client, Sentry.SentryId eventId, string email, string comments, string? name = null) { } - public static void Flush(this Sentry.ISentryClient client) { } - public static void Flush(this Sentry.ISentryClient client, System.TimeSpan timeout) { } - public static System.Threading.Tasks.Task FlushAsync(this Sentry.ISentryClient client) { } - } - public static class SentryConstants - { - public const int DefaultMaxBreadcrumbs = 100; - public const string DisableSdkDsnValue = ""; - public const string Platform = "csharp"; - public const int ProtocolVersion = 7; - } - public sealed class SentryContexts : Sentry.ISentryJsonSerializable, System.Collections.Generic.ICollection>, System.Collections.Generic.IDictionary, System.Collections.Generic.IEnumerable>, System.Collections.IEnumerable - { - public SentryContexts() { } - public Sentry.Protocol.App App { get; } - public Sentry.Protocol.Browser Browser { get; } - public int Count { get; } - public Sentry.Protocol.Device Device { get; } - public Sentry.Protocol.Gpu Gpu { get; } - public bool IsReadOnly { get; } - public object this[string key] { get; set; } - public System.Collections.Generic.ICollection Keys { get; } - public Sentry.Protocol.OperatingSystem OperatingSystem { get; } - public Sentry.Protocol.Response Response { get; } - public Sentry.Protocol.Runtime Runtime { get; } - public Sentry.Protocol.Trace Trace { get; } - public System.Collections.Generic.ICollection Values { get; } - public void Add(System.Collections.Generic.KeyValuePair item) { } - public void Add(string key, object value) { } - public void Clear() { } - public bool Contains(System.Collections.Generic.KeyValuePair item) { } - public bool ContainsKey(string key) { } - public void CopyTo(System.Collections.Generic.KeyValuePair[] array, int arrayIndex) { } - public System.Collections.Generic.IEnumerator> GetEnumerator() { } - public bool Remove(System.Collections.Generic.KeyValuePair item) { } - public bool Remove(string key) { } - public bool TryGetValue(string key, out object value) { } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } - public static Sentry.SentryContexts FromJson(System.Text.Json.JsonElement json) { } - } - [System.Diagnostics.DebuggerDisplay("{GetType().Name,nq}: {EventId,nq}")] - public sealed class SentryEvent : Sentry.IEventLike, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISentryJsonSerializable - { - public SentryEvent() { } - public SentryEvent(System.Exception? exception) { } - public System.Collections.Generic.IReadOnlyCollection Breadcrumbs { get; } - public Sentry.SentryContexts Contexts { get; set; } - public System.Collections.Generic.List? DebugImages { get; set; } - public string? Distribution { get; set; } - public string? Environment { get; set; } - public Sentry.SentryId EventId { get; } - public System.Exception? Exception { get; } - public System.Collections.Generic.IReadOnlyDictionary Extra { get; } - public System.Collections.Generic.IReadOnlyList Fingerprint { get; set; } - public Sentry.SentryLevel? Level { get; set; } - public string? Logger { get; set; } - public Sentry.SentryMessage? Message { get; set; } - public System.Collections.Generic.IDictionary Modules { get; } - public string? Platform { get; set; } - public string? Release { get; set; } - public Sentry.SentryRequest Request { get; set; } - public Sentry.SdkVersion Sdk { get; } - public System.Collections.Generic.IEnumerable? SentryExceptions { get; set; } - public System.Collections.Generic.IEnumerable? SentryThreads { get; set; } - public string? ServerName { get; set; } - public System.Collections.Generic.IReadOnlyDictionary Tags { get; } - public System.DateTimeOffset Timestamp { get; } - public string? TransactionName { get; set; } - public Sentry.SentryUser User { get; set; } - public void AddBreadcrumb(Sentry.Breadcrumb breadcrumb) { } - public void SetExtra(string key, object? value) { } - public void SetTag(string key, string value) { } - public void UnsetTag(string key) { } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } - public static Sentry.SentryEvent FromJson(System.Text.Json.JsonElement json) { } - } - public class SentryGraphQLHttpMessageHandler : Sentry.SentryMessageHandler - { - public SentryGraphQLHttpMessageHandler(System.Net.Http.HttpMessageHandler? innerHandler = null, Sentry.IHub? hub = null) { } - protected override void HandleResponse(System.Net.Http.HttpResponseMessage response, Sentry.ISpan? span, string method, string url) { } - protected override Sentry.ISpan? ProcessRequest(System.Net.Http.HttpRequestMessage request, string method, string url) { } - } - public class SentryHint - { - public SentryHint() { } - public SentryHint(string key, object? value) { } - public System.Collections.Generic.ICollection Attachments { get; } - public System.Collections.Generic.IDictionary Items { get; } - public void AddAttachment(string filePath, Sentry.AttachmentType type = 0, string? contentType = null) { } - public void AddAttachment(byte[] data, string fileName, Sentry.AttachmentType type = 0, string? contentType = null) { } - public static Sentry.SentryHint WithAttachments(params Sentry.SentryAttachment[] attachments) { } - public static Sentry.SentryHint WithAttachments(System.Collections.Generic.IEnumerable attachments) { } - } - public class SentryHttpMessageHandler : Sentry.SentryMessageHandler - { - public SentryHttpMessageHandler() { } - public SentryHttpMessageHandler(Sentry.IHub hub) { } - public SentryHttpMessageHandler(System.Net.Http.HttpMessageHandler innerHandler) { } - public SentryHttpMessageHandler(System.Net.Http.HttpMessageHandler innerHandler, Sentry.IHub hub) { } - protected override void HandleResponse(System.Net.Http.HttpResponseMessage response, Sentry.ISpan? span, string method, string url) { } - protected override Sentry.ISpan? ProcessRequest(System.Net.Http.HttpRequestMessage request, string method, string url) { } - } - public readonly struct SentryId : Sentry.ISentryJsonSerializable, System.IEquatable - { - public static readonly Sentry.SentryId Empty; - public SentryId(System.Guid guid) { } - public bool Equals(Sentry.SentryId other) { } - public override bool Equals(object? obj) { } - public override int GetHashCode() { } - public override string ToString() { } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } - public static Sentry.SentryId Create() { } - public static Sentry.SentryId FromJson(System.Text.Json.JsonElement json) { } - public static Sentry.SentryId Parse(string value) { } - public static System.Guid op_Implicit(Sentry.SentryId sentryId) { } - public static Sentry.SentryId op_Implicit(System.Guid guid) { } - public static bool operator !=(Sentry.SentryId left, Sentry.SentryId right) { } - public static bool operator ==(Sentry.SentryId left, Sentry.SentryId right) { } - } - public enum SentryLevel : short - { - [System.Runtime.Serialization.EnumMember(Value="debug")] - Debug = 0, - [System.Runtime.Serialization.EnumMember(Value="info")] - Info = 1, - [System.Runtime.Serialization.EnumMember(Value="warning")] - Warning = 2, - [System.Runtime.Serialization.EnumMember(Value="error")] - Error = 3, - [System.Runtime.Serialization.EnumMember(Value="fatal")] - Fatal = 4, - } - public sealed class SentryMessage : Sentry.ISentryJsonSerializable - { - public SentryMessage() { } - public string? Formatted { get; set; } - public string? Message { get; set; } - public System.Collections.Generic.IEnumerable? Params { get; set; } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } - public static Sentry.SentryMessage FromJson(System.Text.Json.JsonElement json) { } - public static Sentry.SentryMessage op_Implicit(string? message) { } - } - public abstract class SentryMessageHandler : System.Net.Http.DelegatingHandler - { - protected SentryMessageHandler() { } - protected SentryMessageHandler(Sentry.IHub hub) { } - protected SentryMessageHandler(System.Net.Http.HttpMessageHandler innerHandler) { } - protected SentryMessageHandler(System.Net.Http.HttpMessageHandler innerHandler, Sentry.IHub hub) { } - protected abstract void HandleResponse(System.Net.Http.HttpResponseMessage response, Sentry.ISpan? span, string method, string url); - protected abstract Sentry.ISpan? ProcessRequest(System.Net.Http.HttpRequestMessage request, string method, string url); - protected override System.Net.Http.HttpResponseMessage Send(System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) { } - protected override System.Threading.Tasks.Task SendAsync(System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) { } - } - public enum SentryMonitorInterval - { - Year = 0, - Month = 1, - Week = 2, - Day = 3, - Hour = 4, - Minute = 5, - } - public class SentryMonitorOptions : Sentry.ISentryJsonSerializable - { - public System.TimeSpan? CheckInMargin { get; set; } - public int? FailureIssueThreshold { get; set; } - public System.TimeSpan? MaxRuntime { get; set; } - public string? Owner { get; set; } - public int? RecoveryThreshold { get; set; } - public string? TimeZone { get; set; } - public void Interval(string crontab) { } - public void Interval(int interval, Sentry.SentryMonitorInterval unit) { } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } - } - public class SentryOptions - { - public SentryOptions() { } - [System.CLSCompliant(false)] - public System.Func? AssemblyReader { get; set; } - public bool AttachStacktrace { get; set; } - public bool AutoSessionTracking { get; set; } - public System.TimeSpan AutoSessionTrackingInterval { get; set; } - public Sentry.Extensibility.IBackgroundWorker? BackgroundWorker { get; set; } - public string? CacheDirectoryPath { get; set; } - public bool CaptureFailedRequests { get; set; } - public System.Action? ConfigureClient { get; set; } - public System.Func? CrashedLastRun { get; set; } - public System.Func? CreateHttpMessageHandler { get; set; } - public bool Debug { get; set; } - public System.Net.DecompressionMethods DecompressionMethods { get; set; } - public Sentry.DeduplicateMode DeduplicateMode { get; set; } - public System.Collections.Generic.Dictionary DefaultTags { get; } - public Sentry.StartupTimeDetectionMode DetectStartupTime { get; set; } - public Sentry.SentryLevel DiagnosticLevel { get; set; } - public Sentry.Extensibility.IDiagnosticLogger? DiagnosticLogger { get; set; } - public bool DisableFileWrite { get; set; } - public string? Distribution { get; set; } - public string? Dsn { get; set; } - public bool EnableScopeSync { get; set; } - public bool EnableSpotlight { get; set; } - public string? Environment { get; set; } - public System.Collections.Generic.IList FailedRequestStatusCodes { get; set; } - public System.Collections.Generic.IList FailedRequestTargets { get; set; } - public System.TimeSpan FlushTimeout { get; set; } - public System.Net.IWebProxy? HttpProxy { get; set; } - public System.TimeSpan InitCacheFlushTimeout { get; set; } - public bool IsEnvironmentUser { get; set; } - public bool IsGlobalModeEnabled { get; set; } - public bool JsonPreserveReferences { get; set; } - public long MaxAttachmentSize { get; set; } - public int MaxBreadcrumbs { get; set; } - public int MaxCacheItems { get; set; } - public int MaxQueueItems { get; set; } - public Sentry.Extensibility.INetworkStatusListener? NetworkStatusListener { get; set; } - public double? ProfilesSampleRate { get; set; } - public string? Release { get; set; } - public Sentry.ReportAssembliesMode ReportAssembliesMode { get; set; } - public bool RequestBodyCompressionBuffered { get; set; } - public System.IO.Compression.CompressionLevel RequestBodyCompressionLevel { get; set; } - public float? SampleRate { get; set; } - public Sentry.IScopeObserver? ScopeObserver { get; set; } - public bool SendClientReports { get; set; } - public bool SendDefaultPii { get; set; } - public Sentry.ISentryScopeStateProcessor SentryScopeStateProcessor { get; set; } - public string? ServerName { get; set; } - public System.TimeSpan ShutdownTimeout { get; set; } - public string SpotlightUrl { get; set; } - public Sentry.StackTraceMode StackTraceMode { get; set; } - public System.Collections.Generic.IList TagFilters { get; set; } - public System.Collections.Generic.IList TracePropagationTargets { get; set; } - public double? TracesSampleRate { get; set; } - public System.Func? TracesSampler { get; set; } - public Sentry.Extensibility.ITransport? Transport { get; set; } - public bool UseAsyncFileIO { get; set; } - public void AddEventProcessor(Sentry.Extensibility.ISentryEventProcessor processor) { } - public void AddEventProcessorProvider(System.Func> processorProvider) { } - public void AddEventProcessors(System.Collections.Generic.IEnumerable processors) { } - public void AddExceptionFilter(Sentry.Extensibility.IExceptionFilter exceptionFilter) { } - public void AddExceptionFilterForType() - where TException : System.Exception { } - public void AddExceptionProcessor(Sentry.Extensibility.ISentryEventExceptionProcessor processor) { } - public void AddExceptionProcessorProvider(System.Func> processorProvider) { } - public void AddExceptionProcessors(System.Collections.Generic.IEnumerable processors) { } - public void AddInAppExclude(string prefix) { } - public void AddInAppExclude(System.Text.RegularExpressions.Regex regex) { } - public void AddInAppExcludeRegex(string pattern) { } - public void AddInAppInclude(string prefix) { } - public void AddInAppInclude(System.Text.RegularExpressions.Regex regex) { } - public void AddInAppIncludeRegex(string pattern) { } - public void AddIntegration(Sentry.Integrations.ISdkIntegration integration) { } - public void AddJsonConverter(System.Text.Json.Serialization.JsonConverter converter) { } - public void AddJsonSerializerContext(System.Func contextBuilder) - where T : System.Text.Json.Serialization.JsonSerializerContext { } - public void AddTransactionProcessor(Sentry.Extensibility.ISentryTransactionProcessor processor) { } - public void AddTransactionProcessorProvider(System.Func> processorProvider) { } - public void AddTransactionProcessors(System.Collections.Generic.IEnumerable processors) { } - public void ApplyDefaultTags(Sentry.IHasTags hasTags) { } - public void DisableAppDomainProcessExitFlush() { } - public void DisableAppDomainUnhandledExceptionCapture() { } - public void DisableDiagnosticSourceIntegration() { } - public void DisableDuplicateEventDetection() { } - public void DisableUnobservedTaskExceptionCapture() { } - public void DisableWinUiUnhandledExceptionIntegration() { } - public void EnableHeapDumps(Sentry.HeapDumpTrigger trigger, Sentry.Debouncer? debouncer = null, Sentry.SentryLevel level = 2) { } - public void EnableHeapDumps(short memoryPercentageThreshold, Sentry.Debouncer? debouncer = null, Sentry.SentryLevel level = 2) { } - public System.Collections.Generic.IEnumerable GetAllEventProcessors() { } - public System.Collections.Generic.IEnumerable GetAllExceptionProcessors() { } - public System.Collections.Generic.IEnumerable GetAllTransactionProcessors() { } - public void RemoveEventProcessor() - where TProcessor : Sentry.Extensibility.ISentryEventProcessor { } - public void RemoveExceptionFilter() - where TFilter : Sentry.Extensibility.IExceptionFilter { } - public void RemoveIntegration() - where TIntegration : Sentry.Integrations.ISdkIntegration { } - public void RemoveTransactionProcessor() - where TProcessor : Sentry.Extensibility.ISentryTransactionProcessor { } - public void SetBeforeBreadcrumb(System.Func beforeBreadcrumb) { } - public void SetBeforeBreadcrumb(System.Func beforeBreadcrumb) { } - public void SetBeforeSend(System.Func beforeSend) { } - public void SetBeforeSend(System.Func beforeSend) { } - public void SetBeforeSendTransaction(System.Func beforeSendTransaction) { } - public void SetBeforeSendTransaction(System.Func beforeSendTransaction) { } - public Sentry.SentryOptions UseStackTraceFactory(Sentry.Extensibility.ISentryStackTraceFactory sentryStackTraceFactory) { } - } - public sealed class SentryPackage : Sentry.ISentryJsonSerializable - { - public SentryPackage(string name, string version) { } - public string Name { get; } - public string Version { get; } - public override bool Equals(object? obj) { } - public override int GetHashCode() { } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } - public static Sentry.SentryPackage FromJson(System.Text.Json.JsonElement json) { } - } - public sealed class SentryRequest : Sentry.ISentryJsonSerializable - { - public SentryRequest() { } - public string? ApiTarget { get; set; } - public string? Cookies { get; set; } - public object? Data { get; set; } - public System.Collections.Generic.IDictionary Env { get; } - public System.Collections.Generic.IDictionary Headers { get; } - public string? Method { get; set; } - public System.Collections.Generic.IDictionary Other { get; } - public string? QueryString { get; set; } - public string? Url { get; set; } - public Sentry.SentryRequest Clone() { } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } - public static Sentry.SentryRequest FromJson(System.Text.Json.JsonElement json) { } - } - public static class SentrySdk - { - public static bool IsEnabled { get; } - public static Sentry.SentryId LastEventId { get; } - public static void AddBreadcrumb(Sentry.Breadcrumb breadcrumb, Sentry.SentryHint? hint = null) { } - public static void AddBreadcrumb(string message, string? category = null, string? type = null, System.Collections.Generic.IDictionary? data = null, Sentry.BreadcrumbLevel level = 0) { } - public static void AddBreadcrumb(Sentry.Infrastructure.ISystemClock? clock, string message, string? category = null, string? type = null, System.Collections.Generic.IDictionary? data = null, Sentry.BreadcrumbLevel level = 0) { } - public static void BindClient(Sentry.ISentryClient client) { } - public static void BindException(System.Exception exception, Sentry.ISpan span) { } - public static Sentry.SentryId CaptureCheckIn(string monitorSlug, Sentry.CheckInStatus status, Sentry.SentryId? sentryId = default, System.TimeSpan? duration = default, Sentry.Scope? scope = null, System.Action? configureMonitorOptions = null) { } - public static bool CaptureEnvelope(Sentry.Protocol.Envelopes.Envelope envelope) { } - public static Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, System.Action configureScope) { } - public static Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, Sentry.Scope? scope = null, Sentry.SentryHint? hint = null) { } - public static Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, Sentry.SentryHint? hint, System.Action configureScope) { } - public static Sentry.SentryId CaptureException(System.Exception exception) { } - public static Sentry.SentryId CaptureException(System.Exception exception, System.Action configureScope) { } - public static Sentry.SentryId CaptureMessage(string message, Sentry.SentryLevel level = 1) { } - public static Sentry.SentryId CaptureMessage(string message, System.Action configureScope, Sentry.SentryLevel level = 1) { } - public static void CaptureSession(Sentry.SessionUpdate sessionUpdate) { } - public static void CaptureTransaction(Sentry.SentryTransaction transaction) { } - public static void CaptureTransaction(Sentry.SentryTransaction transaction, Sentry.Scope? scope, Sentry.SentryHint? hint) { } - public static void CaptureUserFeedback(Sentry.UserFeedback userFeedback) { } - public static void CaptureUserFeedback(Sentry.SentryId eventId, string email, string comments, string? name = null) { } - [System.Obsolete("WARNING: This method deliberately causes a crash, and should not be used in a rea" + - "l application.")] - public static void CauseCrash(Sentry.CrashType crashType) { } - public static void Close() { } - public static void ConfigureScope(System.Action configureScope) { } - public static System.Threading.Tasks.Task ConfigureScopeAsync(System.Func configureScope) { } - public static Sentry.TransactionContext ContinueTrace(Sentry.SentryTraceHeader? traceHeader, Sentry.BaggageHeader? baggageHeader, string? name = null, string? operation = null) { } - public static Sentry.TransactionContext ContinueTrace(string? traceHeader, string? baggageHeader, string? name = null, string? operation = null) { } - public static void EndSession(Sentry.SessionEndStatus status = 0) { } - public static void Flush() { } - public static void Flush(System.TimeSpan timeout) { } - public static System.Threading.Tasks.Task FlushAsync() { } - public static System.Threading.Tasks.Task FlushAsync(System.TimeSpan timeout) { } - public static Sentry.BaggageHeader? GetBaggage() { } - public static Sentry.ISpan? GetSpan() { } - public static Sentry.SentryTraceHeader? GetTraceHeader() { } - public static System.IDisposable Init() { } - public static System.IDisposable Init(Sentry.SentryOptions options) { } - public static System.IDisposable Init(System.Action? configureOptions) { } - public static System.IDisposable Init(string? dsn) { } - public static void PauseSession() { } - public static System.IDisposable PushScope() { } - public static System.IDisposable PushScope(TState state) { } - public static void ResumeSession() { } - public static void StartSession() { } - public static Sentry.ITransactionTracer StartTransaction(Sentry.ITransactionContext context) { } - public static Sentry.ITransactionTracer StartTransaction(Sentry.ITransactionContext context, System.Collections.Generic.IReadOnlyDictionary customSamplingContext) { } - public static Sentry.ITransactionTracer StartTransaction(string name, string operation) { } - public static Sentry.ITransactionTracer StartTransaction(string name, string operation, Sentry.SentryTraceHeader traceHeader) { } - public static Sentry.ITransactionTracer StartTransaction(string name, string operation, string? description) { } - } - public class SentrySession : Sentry.ISentrySession - { - public SentrySession(string? distinctId, string release, string? environment) { } - public string? DistinctId { get; } - public string? Environment { get; } - public int ErrorCount { get; } - public Sentry.SentryId Id { get; } - public string? IpAddress { get; } - public string Release { get; } - public System.DateTimeOffset StartTimestamp { get; } - public string? UserAgent { get; } - public void ReportError() { } - } - public class SentrySpan : Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISentryJsonSerializable, Sentry.ISpanData, Sentry.Protocol.ITraceContext - { - public SentrySpan(Sentry.ISpan tracer) { } - public SentrySpan(Sentry.SpanId? parentSpanId, string operation) { } - public string? Description { get; set; } - public System.DateTimeOffset? EndTimestamp { get; } - public System.Collections.Generic.IReadOnlyDictionary Extra { get; } - public bool IsFinished { get; } - public bool? IsSampled { get; } - public System.Collections.Generic.IReadOnlyDictionary Measurements { get; } - public string Operation { get; set; } - public string? Origin { get; } - public Sentry.SpanId? ParentSpanId { get; } - public Sentry.SpanId SpanId { get; } - public System.DateTimeOffset StartTimestamp { get; } - public Sentry.SpanStatus? Status { get; set; } - public System.Collections.Generic.IReadOnlyDictionary Tags { get; } - public Sentry.SentryId TraceId { get; } - public Sentry.SentryTraceHeader GetTraceHeader() { } - public void SetExtra(string key, object? value) { } - public void SetMeasurement(string name, Sentry.Protocol.Measurement measurement) { } - public void SetTag(string key, string value) { } - public void UnsetTag(string key) { } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } - public static Sentry.SentrySpan FromJson(System.Text.Json.JsonElement json) { } - } - [System.Diagnostics.DebuggerDisplay("{Function}")] - public sealed class SentryStackFrame : Sentry.ISentryJsonSerializable - { - public SentryStackFrame() { } - public string? AbsolutePath { get; set; } - public string? AddressMode { get; set; } - public int? ColumnNumber { get; set; } - public string? ContextLine { get; set; } - public string? FileName { get; set; } - public System.Collections.Generic.IList FramesOmitted { get; } - public string? Function { get; set; } - public long? FunctionId { get; set; } - public long? ImageAddress { get; set; } - public bool? InApp { get; set; } - public long? InstructionAddress { get; set; } - public int? LineNumber { get; set; } - public string? Module { get; set; } - public string? Package { get; set; } - public string? Platform { get; set; } - public System.Collections.Generic.IList PostContext { get; } - public System.Collections.Generic.IList PreContext { get; } - public long? SymbolAddress { get; set; } - public System.Collections.Generic.IDictionary Vars { get; } - public void ConfigureAppFrame(Sentry.SentryOptions options) { } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } - public static Sentry.SentryStackFrame FromJson(System.Text.Json.JsonElement json) { } - } - public class SentryStackTrace : Sentry.ISentryJsonSerializable - { - public SentryStackTrace() { } - public Sentry.InstructionAddressAdjustment? AddressAdjustment { get; set; } - public System.Collections.Generic.IList Frames { get; set; } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } - public static Sentry.SentryStackTrace FromJson(System.Text.Json.JsonElement json) { } - } - public sealed class SentryThread : Sentry.ISentryJsonSerializable - { - public SentryThread() { } - public bool? Crashed { get; set; } - public bool? Current { get; set; } - public int? Id { get; set; } - public string? Name { get; set; } - public Sentry.SentryStackTrace? Stacktrace { get; set; } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } - public static Sentry.SentryThread FromJson(System.Text.Json.JsonElement json) { } - } - public class SentryTraceHeader - { - public SentryTraceHeader(Sentry.SentryId traceId, Sentry.SpanId spanSpanId, bool? isSampled) { } - public bool? IsSampled { get; } - public Sentry.SpanId SpanId { get; } - public Sentry.SentryId TraceId { get; } - public override string ToString() { } - public static Sentry.SentryTraceHeader? Parse(string value) { } - } - public class SentryTransaction : Sentry.IEventLike, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISentryJsonSerializable, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.Protocol.ITraceContext - { - public SentryTransaction(Sentry.ITransactionTracer tracer) { } - public SentryTransaction(string name, string operation) { } - public SentryTransaction(string name, string operation, Sentry.TransactionNameSource nameSource) { } - public System.Collections.Generic.IReadOnlyCollection Breadcrumbs { get; } - public Sentry.SentryContexts Contexts { get; set; } - public string? Description { get; set; } - public string? Distribution { get; set; } - public System.DateTimeOffset? EndTimestamp { get; } - public string? Environment { get; set; } - public Sentry.SentryId EventId { get; } - public System.Collections.Generic.IReadOnlyDictionary Extra { get; } - public System.Collections.Generic.IReadOnlyList Fingerprint { get; set; } - public bool IsFinished { get; } - public bool? IsParentSampled { get; set; } - public bool? IsSampled { get; } - public Sentry.SentryLevel? Level { get; set; } - public System.Collections.Generic.IReadOnlyDictionary Measurements { get; } - public string Name { get; } - public Sentry.TransactionNameSource NameSource { get; } - public string Operation { get; } - public string? Origin { get; } - public Sentry.SpanId? ParentSpanId { get; } - public string? Platform { get; set; } - public string? Release { get; set; } - public Sentry.SentryRequest Request { get; set; } - public double? SampleRate { get; } - public Sentry.SdkVersion Sdk { get; } - public Sentry.SpanId SpanId { get; } - public System.Collections.Generic.IReadOnlyCollection Spans { get; } - public System.DateTimeOffset StartTimestamp { get; } - public Sentry.SpanStatus? Status { get; } - public System.Collections.Generic.IReadOnlyDictionary Tags { get; } - public Sentry.SentryId TraceId { get; } - public Sentry.SentryUser User { get; set; } - public void AddBreadcrumb(Sentry.Breadcrumb breadcrumb) { } - public Sentry.SentryTraceHeader GetTraceHeader() { } - public void SetExtra(string key, object? value) { } - public void SetMeasurement(string name, Sentry.Protocol.Measurement measurement) { } - public void SetTag(string key, string value) { } - public void UnsetTag(string key) { } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } - public static Sentry.SentryTransaction FromJson(System.Text.Json.JsonElement json) { } - } - public sealed class SentryUser : Sentry.ISentryJsonSerializable - { - public SentryUser() { } - public string? Email { get; set; } - public string? Id { get; set; } - public string? IpAddress { get; set; } - public System.Collections.Generic.IDictionary Other { get; set; } - public string? Username { get; set; } - public Sentry.SentryUser Clone() { } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? _) { } - public static Sentry.SentryUser FromJson(System.Text.Json.JsonElement json) { } - } - public enum SessionEndStatus - { - Exited = 0, - Crashed = 1, - Abnormal = 2, - } - public class SessionUpdate : Sentry.ISentryJsonSerializable, Sentry.ISentrySession - { - public SessionUpdate(Sentry.SessionUpdate sessionUpdate, bool isInitial) { } - public SessionUpdate(Sentry.SessionUpdate sessionUpdate, bool isInitial, Sentry.SessionEndStatus? endStatus) { } - public SessionUpdate(Sentry.ISentrySession session, bool isInitial, System.DateTimeOffset timestamp, int sequenceNumber, Sentry.SessionEndStatus? endStatus) { } - public SessionUpdate(Sentry.SentryId id, string? distinctId, System.DateTimeOffset startTimestamp, string release, string? environment, string? ipAddress, string? userAgent, int errorCount, bool isInitial, System.DateTimeOffset timestamp, int sequenceNumber, Sentry.SessionEndStatus? endStatus) { } - public string? DistinctId { get; } - public System.TimeSpan Duration { get; } - public Sentry.SessionEndStatus? EndStatus { get; } - public string? Environment { get; } - public int ErrorCount { get; } - public Sentry.SentryId Id { get; } - public string? IpAddress { get; } - public bool IsInitial { get; } - public string Release { get; } - public int SequenceNumber { get; } - public System.DateTimeOffset StartTimestamp { get; } - public System.DateTimeOffset Timestamp { get; } - public string? UserAgent { get; } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } - public static Sentry.SessionUpdate FromJson(System.Text.Json.JsonElement json) { } - } - public class SpanContext : Sentry.Protocol.ITraceContext - { - public SpanContext(string operation, Sentry.SpanId? spanId = default, Sentry.SpanId? parentSpanId = default, Sentry.SentryId? traceId = default, string? description = null, Sentry.SpanStatus? status = default, bool? isSampled = default) { } - public string? Description { get; } - public Sentry.Instrumenter Instrumenter { get; } - public bool? IsSampled { get; } - public string Operation { get; set; } - public string? Origin { get; } - public Sentry.SpanId? ParentSpanId { get; } - public Sentry.SpanId SpanId { get; } - public Sentry.SpanStatus? Status { get; } - public Sentry.SentryId TraceId { get; } - } - public static class SpanDataExtensions - { - public static void SetMeasurement(this Sentry.ISpanData spanData, string name, double value, Sentry.MeasurementUnit unit = default) { } - public static void SetMeasurement(this Sentry.ISpanData spanData, string name, int value, Sentry.MeasurementUnit unit = default) { } - public static void SetMeasurement(this Sentry.ISpanData spanData, string name, long value, Sentry.MeasurementUnit unit = default) { } - [System.CLSCompliant(false)] - public static void SetMeasurement(this Sentry.ISpanData spanData, string name, ulong value, Sentry.MeasurementUnit unit = default) { } - } - public static class SpanExtensions - { - public static Sentry.ITransactionTracer GetTransaction(this Sentry.ISpan span) { } - public static Sentry.ISpan StartChild(this Sentry.ISpan span, string operation, string? description) { } - } - public readonly struct SpanId : Sentry.ISentryJsonSerializable, System.IEquatable - { - public static readonly Sentry.SpanId Empty; - public SpanId(long value) { } - public SpanId(string value) { } - public bool Equals(Sentry.SpanId other) { } - public override bool Equals(object? obj) { } - public override int GetHashCode() { } - public override string ToString() { } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? _) { } - public static Sentry.SpanId Create() { } - public static Sentry.SpanId FromJson(System.Text.Json.JsonElement json) { } - public static Sentry.SpanId Parse(string value) { } - public static string op_Implicit(Sentry.SpanId id) { } - public static bool operator !=(Sentry.SpanId left, Sentry.SpanId right) { } - public static bool operator ==(Sentry.SpanId left, Sentry.SpanId right) { } - } - public enum SpanStatus - { - Ok = 0, - DeadlineExceeded = 1, - Unauthenticated = 2, - PermissionDenied = 3, - NotFound = 4, - ResourceExhausted = 5, - InvalidArgument = 6, - Unimplemented = 7, - Unavailable = 8, - InternalError = 9, - UnknownError = 10, - Cancelled = 11, - AlreadyExists = 12, - FailedPrecondition = 13, - Aborted = 14, - OutOfRange = 15, - DataLoss = 16, - } - public class SpanTracer : Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpan, Sentry.ISpanData, Sentry.Protocol.ITraceContext - { - public SpanTracer(Sentry.IHub hub, Sentry.TransactionTracer transaction, Sentry.SpanId? parentSpanId, Sentry.SentryId traceId, string operation) { } - public string? Description { get; set; } - public System.DateTimeOffset? EndTimestamp { get; } - public System.Collections.Generic.IReadOnlyDictionary Extra { get; } - public bool IsFinished { get; } - public bool? IsSampled { get; } - public System.Collections.Generic.IReadOnlyDictionary Measurements { get; } - public string Operation { get; set; } - public string? Origin { get; } - public Sentry.SpanId? ParentSpanId { get; } - public Sentry.SpanId SpanId { get; } - public System.DateTimeOffset StartTimestamp { get; } - public Sentry.SpanStatus? Status { get; set; } - public System.Collections.Generic.IReadOnlyDictionary Tags { get; } - public Sentry.SentryId TraceId { get; } - public void Finish() { } - public void Finish(Sentry.SpanStatus status) { } - public void Finish(System.Exception exception) { } - public void Finish(System.Exception exception, Sentry.SpanStatus status) { } - public Sentry.SentryTraceHeader GetTraceHeader() { } - public void SetExtra(string key, object? value) { } - public void SetMeasurement(string name, Sentry.Protocol.Measurement measurement) { } - public void SetTag(string key, string value) { } - public Sentry.ISpan StartChild(string operation) { } - public void UnsetTag(string key) { } - } - public enum StackTraceMode - { - Original = 0, - Enhanced = 1, - } - public enum StartupTimeDetectionMode - { - None = 0, - Fast = 1, - Best = 2, - } - public class StreamAttachmentContent : Sentry.IAttachmentContent - { - public StreamAttachmentContent(System.IO.Stream stream) { } - public System.IO.Stream GetStream() { } - } - [System.ComponentModel.TypeConverter(typeof(Sentry.StringOrRegexTypeConverter))] - public class StringOrRegex - { - public StringOrRegex(string stringOrRegex) { } - public StringOrRegex(System.Text.RegularExpressions.Regex regex) { } - public override bool Equals(object? obj) { } - public override int GetHashCode() { } - public override string ToString() { } - public static Sentry.StringOrRegex op_Implicit(string stringOrRegex) { } - public static Sentry.StringOrRegex op_Implicit(System.Text.RegularExpressions.Regex regex) { } - } - public class TransactionContext : Sentry.SpanContext, Sentry.ITransactionContext, Sentry.Protocol.ITraceContext - { - public TransactionContext(string name, string operation, Sentry.SpanId? spanId = default, Sentry.SpanId? parentSpanId = default, Sentry.SentryId? traceId = default, string? description = "", Sentry.SpanStatus? status = default, bool? isSampled = default, bool? isParentSampled = default, Sentry.TransactionNameSource nameSource = 0) { } - public bool? IsParentSampled { get; } - public string Name { get; set; } - public Sentry.TransactionNameSource NameSource { get; set; } - } - public enum TransactionNameSource - { - Custom = 0, - Url = 1, - Route = 2, - View = 3, - Component = 4, - Task = 5, - } - public class TransactionSamplingContext - { - public TransactionSamplingContext(Sentry.ITransactionContext transactionContext, System.Collections.Generic.IReadOnlyDictionary customSamplingContext) { } - public System.Collections.Generic.IReadOnlyDictionary CustomSamplingContext { get; } - public Sentry.ITransactionContext TransactionContext { get; } - } - public class TransactionTracer : Sentry.IEventLike, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpan, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.ITransactionTracer, Sentry.Protocol.ITraceContext - { - public TransactionTracer(Sentry.IHub hub, Sentry.ITransactionContext context) { } - public System.Collections.Generic.IReadOnlyCollection Breadcrumbs { get; } - public Sentry.SentryContexts Contexts { get; set; } - public string? Description { get; set; } - public string? Distribution { get; set; } - public System.DateTimeOffset? EndTimestamp { get; } - public string? Environment { get; set; } - public System.Collections.Generic.IReadOnlyDictionary Extra { get; } - public System.Collections.Generic.IReadOnlyList Fingerprint { get; set; } - public bool IsFinished { get; } - public bool? IsParentSampled { get; set; } - public bool? IsSampled { get; } - public Sentry.SentryLevel? Level { get; set; } - public System.Collections.Generic.IReadOnlyDictionary Measurements { get; } - public string Name { get; set; } - public Sentry.TransactionNameSource NameSource { get; set; } - public string Operation { get; set; } - public string? Origin { get; } - public Sentry.SpanId? ParentSpanId { get; } - public string? Platform { get; set; } - public string? Release { get; set; } - public Sentry.SentryRequest Request { get; set; } - public double? SampleRate { get; } - public Sentry.SdkVersion Sdk { get; } - public Sentry.SpanId SpanId { get; } - public System.Collections.Generic.IReadOnlyCollection Spans { get; } - public System.DateTimeOffset StartTimestamp { get; } - public Sentry.SpanStatus? Status { get; set; } - public System.Collections.Generic.IReadOnlyDictionary Tags { get; } - public Sentry.SentryId TraceId { get; } - public Sentry.SentryUser User { get; set; } - public void AddBreadcrumb(Sentry.Breadcrumb breadcrumb) { } - public void Finish() { } - public void Finish(Sentry.SpanStatus status) { } - public void Finish(System.Exception exception) { } - public void Finish(System.Exception exception, Sentry.SpanStatus status) { } - public Sentry.ISpan? GetLastActiveSpan() { } - public Sentry.SentryTraceHeader GetTraceHeader() { } - public void SetExtra(string key, object? value) { } - public void SetMeasurement(string name, Sentry.Protocol.Measurement measurement) { } - public void SetTag(string key, string value) { } - public Sentry.ISpan StartChild(string operation) { } - public void UnsetTag(string key) { } - } - public sealed class UserFeedback : Sentry.ISentryJsonSerializable - { - public UserFeedback(Sentry.SentryId eventId, string? name, string? email, string? comments) { } - public string? Comments { get; } - public string? Email { get; } - public Sentry.SentryId EventId { get; } - public string? Name { get; } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } - public static Sentry.UserFeedback FromJson(System.Text.Json.JsonElement json) { } - } - public sealed class ViewHierarchy : Sentry.ISentryJsonSerializable - { - public ViewHierarchy(string renderingSystem) { } - public string RenderingSystem { get; set; } - public System.Collections.Generic.List Windows { get; } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } - } - public class ViewHierarchyAttachment : Sentry.SentryAttachment - { - public ViewHierarchyAttachment(Sentry.IAttachmentContent content) { } - } - public abstract class ViewHierarchyNode : Sentry.ISentryJsonSerializable - { - protected ViewHierarchyNode(string type) { } - public System.Collections.Generic.List Children { get; set; } - public string Type { get; set; } - protected abstract void WriteAdditionalProperties(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger); - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } - } -} -namespace Sentry.Ben.BlockingDetector -{ - public class SuppressBlockingDetection : System.IDisposable - { - public SuppressBlockingDetection() { } - public void Dispose() { } - } -} -namespace Sentry.Extensibility -{ - public abstract class BaseRequestPayloadExtractor : Sentry.Extensibility.IRequestPayloadExtractor - { - protected BaseRequestPayloadExtractor() { } - protected abstract object? DoExtractPayLoad(Sentry.Extensibility.IHttpRequest request); - public object? ExtractPayload(Sentry.Extensibility.IHttpRequest request) { } - protected abstract bool IsSupported(Sentry.Extensibility.IHttpRequest request); - } - public class DefaultRequestPayloadExtractor : Sentry.Extensibility.BaseRequestPayloadExtractor - { - public DefaultRequestPayloadExtractor() { } - protected override object? DoExtractPayLoad(Sentry.Extensibility.IHttpRequest request) { } - protected override bool IsSupported(Sentry.Extensibility.IHttpRequest request) { } - } - public static class DiagnosticLoggerExtensions - { - public static void LogDebug(this Sentry.Extensibility.IDiagnosticLogger logger, string message) { } - public static void LogDebug(this Sentry.Extensibility.IDiagnosticLogger logger, string message, TArg arg) { } - public static void LogDebug(this Sentry.Extensibility.IDiagnosticLogger logger, string message, TArg arg, TArg2 arg2) { } - public static void LogError(this Sentry.Extensibility.IDiagnosticLogger logger, string message) { } - public static void LogError(this Sentry.Extensibility.IDiagnosticLogger logger, System.Exception exception, string message) { } - public static void LogError(this Sentry.Extensibility.IDiagnosticLogger logger, string message, TArg arg) { } - public static void LogError(this Sentry.Extensibility.IDiagnosticLogger logger, System.Exception exception, string message, TArg arg) { } - public static void LogError(this Sentry.Extensibility.IDiagnosticLogger logger, System.Exception exception, string message, TArg arg, TArg2 arg2) { } - public static void LogError(this Sentry.Extensibility.IDiagnosticLogger logger, System.Exception exception, string message, TArg arg, TArg2 arg2, TArg3 arg3) { } - public static void LogError(this Sentry.Extensibility.IDiagnosticLogger logger, System.Exception exception, string message, TArg arg, TArg2 arg2, TArg3 arg3, TArg4 arg4) { } - public static void LogFatal(this Sentry.Extensibility.IDiagnosticLogger logger, string message) { } - public static void LogFatal(this Sentry.Extensibility.IDiagnosticLogger logger, System.Exception exception, string message) { } - public static void LogInfo(this Sentry.Extensibility.IDiagnosticLogger logger, string message) { } - public static void LogInfo(this Sentry.Extensibility.IDiagnosticLogger logger, string message, TArg arg) { } - public static void LogInfo(this Sentry.Extensibility.IDiagnosticLogger logger, string message, TArg arg, TArg2 arg2) { } - public static void LogInfo(this Sentry.Extensibility.IDiagnosticLogger logger, string message, TArg arg, TArg2 arg2, TArg3 arg3) { } - public static void LogWarning(this Sentry.Extensibility.IDiagnosticLogger logger, string message) { } - public static void LogWarning(this Sentry.Extensibility.IDiagnosticLogger logger, System.Exception exception, string message) { } - public static void LogWarning(this Sentry.Extensibility.IDiagnosticLogger logger, string message, TArg arg) { } - public static void LogWarning(this Sentry.Extensibility.IDiagnosticLogger logger, string message, TArg arg, TArg2 arg2) { } - } - public class DisabledHub : Sentry.IHub, Sentry.ISentryClient, Sentry.ISentryScopeManager, System.IDisposable - { - public static readonly Sentry.Extensibility.DisabledHub Instance; - public bool IsEnabled { get; } - public Sentry.SentryId LastEventId { get; } - public void BindClient(Sentry.ISentryClient client) { } - public void BindException(System.Exception exception, Sentry.ISpan span) { } - public Sentry.SentryId CaptureCheckIn(string monitorSlug, Sentry.CheckInStatus status, Sentry.SentryId? sentryId = default, System.TimeSpan? duration = default, Sentry.Scope? scope = null, System.Action? configureMonitorOptions = null) { } - public bool CaptureEnvelope(Sentry.Protocol.Envelopes.Envelope envelope) { } - public Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, System.Action configureScope) { } - public Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, Sentry.Scope? scope = null, Sentry.SentryHint? hint = null) { } - public Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, Sentry.SentryHint? hint, System.Action configureScope) { } - public void CaptureSession(Sentry.SessionUpdate sessionUpdate) { } - public void CaptureTransaction(Sentry.SentryTransaction transaction) { } - public void CaptureTransaction(Sentry.SentryTransaction transaction, Sentry.Scope? scope, Sentry.SentryHint? hint) { } - public void CaptureUserFeedback(Sentry.UserFeedback userFeedback) { } - public void ConfigureScope(System.Action configureScope) { } - public System.Threading.Tasks.Task ConfigureScopeAsync(System.Func configureScope) { } - public Sentry.TransactionContext ContinueTrace(Sentry.SentryTraceHeader? traceHeader, Sentry.BaggageHeader? baggageHeader, string? name = null, string? operation = null) { } - public Sentry.TransactionContext ContinueTrace(string? traceHeader, string? baggageHeader, string? name = null, string? operation = null) { } - public void Dispose() { } - public void EndSession(Sentry.SessionEndStatus status = 0) { } - public System.Threading.Tasks.Task FlushAsync(System.TimeSpan timeout) { } - public Sentry.BaggageHeader? GetBaggage() { } - public Sentry.ISpan? GetSpan() { } - public Sentry.SentryTraceHeader? GetTraceHeader() { } - public void PauseSession() { } - public System.IDisposable PushScope() { } - public System.IDisposable PushScope(TState state) { } - public void ResumeSession() { } - public void StartSession() { } - public Sentry.ITransactionTracer StartTransaction(Sentry.ITransactionContext context, System.Collections.Generic.IReadOnlyDictionary customSamplingContext) { } - } - public class FormRequestPayloadExtractor : Sentry.Extensibility.BaseRequestPayloadExtractor - { - public FormRequestPayloadExtractor() { } - protected override object? DoExtractPayLoad(Sentry.Extensibility.IHttpRequest request) { } - protected override bool IsSupported(Sentry.Extensibility.IHttpRequest request) { } - } - public sealed class HubAdapter : Sentry.IHub, Sentry.ISentryClient, Sentry.ISentryScopeManager - { - public static readonly Sentry.Extensibility.HubAdapter Instance; - public bool IsEnabled { get; } - public Sentry.SentryId LastEventId { get; } - public void AddBreadcrumb(string message, string? category = null, string? type = null, System.Collections.Generic.IDictionary? data = null, Sentry.BreadcrumbLevel level = 0) { } - public void AddBreadcrumb(Sentry.Infrastructure.ISystemClock clock, string message, string? category = null, string? type = null, System.Collections.Generic.IDictionary? data = null, Sentry.BreadcrumbLevel level = 0) { } - public void BindClient(Sentry.ISentryClient client) { } - public void BindException(System.Exception exception, Sentry.ISpan span) { } - public Sentry.SentryId CaptureCheckIn(string monitorSlug, Sentry.CheckInStatus status, Sentry.SentryId? sentryId = default, System.TimeSpan? duration = default, Sentry.Scope? scope = null, System.Action? monitorOptions = null) { } - public bool CaptureEnvelope(Sentry.Protocol.Envelopes.Envelope envelope) { } - public Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt) { } - public Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, Sentry.Scope? scope) { } - public Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, System.Action configureScope) { } - public Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, Sentry.Scope? scope, Sentry.SentryHint? hint = null) { } - public Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, Sentry.SentryHint? hint, System.Action configureScope) { } - public Sentry.SentryId CaptureException(System.Exception exception) { } - public void CaptureSession(Sentry.SessionUpdate sessionUpdate) { } - public void CaptureTransaction(Sentry.SentryTransaction transaction) { } - public void CaptureTransaction(Sentry.SentryTransaction transaction, Sentry.Scope? scope, Sentry.SentryHint? hint) { } - public void CaptureUserFeedback(Sentry.UserFeedback sentryUserFeedback) { } - public void ConfigureScope(System.Action configureScope) { } - public System.Threading.Tasks.Task ConfigureScopeAsync(System.Func configureScope) { } - public Sentry.TransactionContext ContinueTrace(Sentry.SentryTraceHeader? traceHeader, Sentry.BaggageHeader? baggageHeader, string? name = null, string? operation = null) { } - public Sentry.TransactionContext ContinueTrace(string? traceHeader, string? baggageHeader, string? name = null, string? operation = null) { } - public void EndSession(Sentry.SessionEndStatus status = 0) { } - public System.Threading.Tasks.Task FlushAsync(System.TimeSpan timeout) { } - public Sentry.BaggageHeader? GetBaggage() { } - public Sentry.ISpan? GetSpan() { } - public Sentry.SentryTraceHeader? GetTraceHeader() { } - public void PauseSession() { } - public System.IDisposable PushScope() { } - public System.IDisposable PushScope(TState state) { } - public void ResumeSession() { } - public void StartSession() { } - public Sentry.ITransactionTracer StartTransaction(Sentry.ITransactionContext context, System.Collections.Generic.IReadOnlyDictionary customSamplingContext) { } - } - public interface IBackgroundWorker - { - int QueuedItems { get; } - bool EnqueueEnvelope(Sentry.Protocol.Envelopes.Envelope envelope); - System.Threading.Tasks.Task FlushAsync(System.TimeSpan timeout); - } - public interface IDiagnosticLogger - { - bool IsEnabled(Sentry.SentryLevel level); - void Log(Sentry.SentryLevel logLevel, string message, System.Exception? exception = null, params object?[] args); - } - public interface IExceptionFilter - { - bool Filter(System.Exception ex); - } - public interface IHttpRequest - { - System.IO.Stream? Body { get; } - long? ContentLength { get; } - string? ContentType { get; } - System.Collections.Generic.IEnumerable>>? Form { get; } - } - public interface INetworkStatusListener - { - bool Online { get; } - System.Threading.Tasks.Task WaitForNetworkOnlineAsync(System.Threading.CancellationToken cancellationToken); - } - public interface IRequestPayloadExtractor - { - object? ExtractPayload(Sentry.Extensibility.IHttpRequest request); - } - public interface ISentryEventExceptionProcessor - { - void Process(System.Exception exception, Sentry.SentryEvent sentryEvent); - } - public interface ISentryEventProcessor - { - Sentry.SentryEvent? Process(Sentry.SentryEvent @event); - } - public interface ISentryEventProcessorWithHint : Sentry.Extensibility.ISentryEventProcessor - { - Sentry.SentryEvent? Process(Sentry.SentryEvent @event, Sentry.SentryHint hint); - } - public interface ISentryStackTraceFactory - { - Sentry.SentryStackTrace? Create(System.Exception? exception = null); - } - public interface ISentryTransactionProcessor - { - Sentry.SentryTransaction? Process(Sentry.SentryTransaction transaction); - } - public interface ISentryTransactionProcessorWithHint : Sentry.Extensibility.ISentryTransactionProcessor - { - Sentry.SentryTransaction? Process(Sentry.SentryTransaction transaction, Sentry.SentryHint hint); - } - public interface ITransport - { - System.Threading.Tasks.Task SendEnvelopeAsync(Sentry.Protocol.Envelopes.Envelope envelope, System.Threading.CancellationToken cancellationToken = default); - } - public class RequestBodyExtractionDispatcher : Sentry.Extensibility.IRequestPayloadExtractor - { - public RequestBodyExtractionDispatcher(System.Collections.Generic.IEnumerable extractors, Sentry.SentryOptions options, System.Func sizeSwitch) { } - public object? ExtractPayload(Sentry.Extensibility.IHttpRequest request) { } - } - public enum RequestSize - { - None = 0, - Small = 1, - Medium = 2, - Always = 3, - } - public abstract class SentryEventExceptionProcessor : Sentry.Extensibility.ISentryEventExceptionProcessor - where TException : System.Exception - { - protected SentryEventExceptionProcessor() { } - public void Process(System.Exception? exception, Sentry.SentryEvent sentryEvent) { } - protected abstract void ProcessException(TException exception, Sentry.SentryEvent sentryEvent); - } - public sealed class SentryStackTraceFactory : Sentry.Extensibility.ISentryStackTraceFactory - { - public SentryStackTraceFactory(Sentry.SentryOptions options) { } - public Sentry.SentryStackTrace? Create(System.Exception? exception = null) { } - } -} -namespace Sentry.Http -{ - public abstract class HttpTransportBase - { - protected HttpTransportBase(Sentry.SentryOptions options, System.Func? getEnvironmentVariable = null, Sentry.Infrastructure.ISystemClock? clock = null) { } - protected virtual System.Net.Http.HttpRequestMessage CreateRequest(Sentry.Protocol.Envelopes.Envelope envelope) { } - protected void HandleResponse(System.Net.Http.HttpResponseMessage response, Sentry.Protocol.Envelopes.Envelope envelope) { } - protected System.Threading.Tasks.Task HandleResponseAsync(System.Net.Http.HttpResponseMessage response, Sentry.Protocol.Envelopes.Envelope envelope, System.Threading.CancellationToken cancellationToken) { } - protected Sentry.Protocol.Envelopes.Envelope ProcessEnvelope(Sentry.Protocol.Envelopes.Envelope envelope) { } - protected System.IO.Stream ReadStreamFromHttpContent(System.Net.Http.HttpContent content) { } - } - public interface ISentryHttpClientFactory - { - System.Net.Http.HttpClient Create(Sentry.SentryOptions options); - } -} -namespace Sentry.Infrastructure -{ - public class ConsoleAndTraceDiagnosticLogger : Sentry.Infrastructure.DiagnosticLogger - { - public ConsoleAndTraceDiagnosticLogger(Sentry.SentryLevel minimalLevel) { } - protected override void LogMessage(string message) { } - } - public class ConsoleDiagnosticLogger : Sentry.Infrastructure.DiagnosticLogger - { - public ConsoleDiagnosticLogger(Sentry.SentryLevel minimalLevel) { } - protected override void LogMessage(string message) { } - } - public abstract class DiagnosticLogger : Sentry.Extensibility.IDiagnosticLogger - { - protected DiagnosticLogger(Sentry.SentryLevel minimalLevel) { } - public bool IsEnabled(Sentry.SentryLevel level) { } - public void Log(Sentry.SentryLevel logLevel, string message, System.Exception? exception = null, params object?[] args) { } - protected abstract void LogMessage(string message); - } - public class FileDiagnosticLogger : Sentry.Infrastructure.DiagnosticLogger - { - public FileDiagnosticLogger(string path, bool alsoWriteToConsole = false) { } - public FileDiagnosticLogger(string path, Sentry.SentryLevel minimalLevel, bool alsoWriteToConsole = false) { } - protected override void LogMessage(string message) { } - } - public interface ISystemClock - { - System.DateTimeOffset GetUtcNow(); - } - public sealed class SystemClock : Sentry.Infrastructure.ISystemClock - { - public static readonly Sentry.Infrastructure.SystemClock Clock; - public System.DateTimeOffset GetUtcNow() { } - } - public class TraceDiagnosticLogger : Sentry.Infrastructure.DiagnosticLogger - { - public TraceDiagnosticLogger(Sentry.SentryLevel minimalLevel) { } - protected override void LogMessage(string message) { } - } -} -namespace Sentry.Integrations -{ - public interface ISdkIntegration - { - void Register(Sentry.IHub hub, Sentry.SentryOptions options); - } -} -namespace Sentry.PlatformAbstractions -{ - public static class FrameworkInfo - { - public static System.Collections.Generic.IReadOnlyDictionary NetFxReleaseVersionMap { get; } - public static System.Collections.Generic.IEnumerable GetInstallations() { } - public static Sentry.PlatformAbstractions.FrameworkInstallation? GetLatest(int clr) { } - } - public class FrameworkInstallation - { - public FrameworkInstallation() { } - public Sentry.PlatformAbstractions.FrameworkProfile? Profile { get; set; } - public int? Release { get; set; } - public int? ServicePack { get; set; } - public string? ShortName { get; set; } - public System.Version? Version { get; set; } - public override string ToString() { } - } - public enum FrameworkProfile - { - Client = 0, - Full = 1, - } - public class SentryRuntime : System.IEquatable - { - public SentryRuntime(string? name = null, string? version = null, string? raw = null, string? identifier = null) { } - public string? Identifier { get; } - public string? Name { get; } - public string? Raw { get; } - public string? Version { get; } - public static Sentry.PlatformAbstractions.SentryRuntime Current { get; } - public bool Equals(Sentry.PlatformAbstractions.SentryRuntime? other) { } - public override bool Equals(object? obj) { } - public override int GetHashCode() { } - public override string? ToString() { } - } - public static class SentryRuntimeExtensions - { - public static bool IsMono(this Sentry.PlatformAbstractions.SentryRuntime runtime) { } - public static bool IsNetCore(this Sentry.PlatformAbstractions.SentryRuntime runtime) { } - public static bool IsNetFx(this Sentry.PlatformAbstractions.SentryRuntime runtime) { } - } -} -namespace Sentry.Protocol -{ - public sealed class App : Sentry.ISentryJsonSerializable - { - public const string Type = "app"; - public App() { } - public string? Build { get; set; } - public string? BuildType { get; set; } - public string? Hash { get; set; } - public string? Identifier { get; set; } - public bool? InForeground { get; set; } - public string? Name { get; set; } - public System.DateTimeOffset? StartTime { get; set; } - public string? Version { get; set; } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? _) { } - public static Sentry.Protocol.App FromJson(System.Text.Json.JsonElement json) { } - } - public sealed class Browser : Sentry.ISentryJsonSerializable - { - public const string Type = "browser"; - public Browser() { } - public string? Name { get; set; } - public string? Version { get; set; } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? _) { } - public static Sentry.Protocol.Browser FromJson(System.Text.Json.JsonElement json) { } - } - public sealed class DebugImage : Sentry.ISentryJsonSerializable - { - public DebugImage() { } - public string? CodeFile { get; set; } - public string? CodeId { get; set; } - public string? DebugChecksum { get; set; } - public string? DebugFile { get; set; } - public string? DebugId { get; set; } - public long? ImageAddress { get; set; } - public long? ImageSize { get; set; } - public string? Type { get; set; } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } - public static Sentry.Protocol.DebugImage FromJson(System.Text.Json.JsonElement json) { } - } - public sealed class Device : Sentry.ISentryJsonSerializable - { - public const string Type = "device"; - public Device() { } - public string? Architecture { get; set; } - public float? BatteryLevel { get; set; } - public string? BatteryStatus { get; set; } - public System.DateTimeOffset? BootTime { get; set; } - public string? Brand { get; set; } - public string? CpuDescription { get; set; } - public string? DeviceType { get; set; } - public string? DeviceUniqueIdentifier { get; set; } - public long? ExternalFreeStorage { get; set; } - public long? ExternalStorageSize { get; set; } - public string? Family { get; set; } - public long? FreeMemory { get; set; } - public long? FreeStorage { get; set; } - public bool? IsCharging { get; set; } - public bool? IsOnline { get; set; } - public bool? LowMemory { get; set; } - public string? Manufacturer { get; set; } - public long? MemorySize { get; set; } - public string? Model { get; set; } - public string? ModelId { get; set; } - public string? Name { get; set; } - public Sentry.Protocol.DeviceOrientation? Orientation { get; set; } - public int? ProcessorCount { get; set; } - public float? ProcessorFrequency { get; set; } - public float? ScreenDensity { get; set; } - public int? ScreenDpi { get; set; } - public string? ScreenResolution { get; set; } - public bool? Simulator { get; set; } - public long? StorageSize { get; set; } - public bool? SupportsAccelerometer { get; set; } - public bool? SupportsAudio { get; set; } - public bool? SupportsGyroscope { get; set; } - public bool? SupportsLocationService { get; set; } - public bool? SupportsVibration { get; set; } - public System.TimeZoneInfo? Timezone { get; set; } - public long? UsableMemory { get; set; } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? _) { } - public static Sentry.Protocol.Device FromJson(System.Text.Json.JsonElement json) { } - } - public enum DeviceOrientation - { - [System.Runtime.Serialization.EnumMember(Value="portrait")] - Portrait = 0, - [System.Runtime.Serialization.EnumMember(Value="landscape")] - Landscape = 1, - } - public sealed class Gpu : Sentry.ISentryJsonSerializable - { - public const string Type = "gpu"; - public Gpu() { } - public string? ApiType { get; set; } - public string? GraphicsShaderLevel { get; set; } - public int? Id { get; set; } - public int? MaxTextureSize { get; set; } - public int? MemorySize { get; set; } - public bool? MultiThreadedRendering { get; set; } - public string? Name { get; set; } - public string? NpotSupport { get; set; } - public bool? SupportsComputeShaders { get; set; } - public bool? SupportsDrawCallInstancing { get; set; } - public bool? SupportsGeometryShaders { get; set; } - public bool? SupportsRayTracing { get; set; } - public string? VendorId { get; set; } - public string? VendorName { get; set; } - public string? Version { get; set; } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? _) { } - public static Sentry.Protocol.Gpu FromJson(System.Text.Json.JsonElement json) { } - } - public interface ITraceContext - { - string? Description { get; } - bool? IsSampled { get; } - string Operation { get; } - string? Origin { get; } - Sentry.SpanId? ParentSpanId { get; } - Sentry.SpanId SpanId { get; } - Sentry.SpanStatus? Status { get; } - Sentry.SentryId TraceId { get; } - } - public sealed class Measurement : Sentry.ISentryJsonSerializable - { - public Sentry.MeasurementUnit Unit { get; } - public object Value { get; } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } - public static Sentry.Protocol.Measurement FromJson(System.Text.Json.JsonElement json) { } - } - public sealed class Mechanism : Sentry.ISentryJsonSerializable - { - public static readonly string DescriptionKey; - public static readonly string HandledKey; - public static readonly string MechanismKey; - public Mechanism() { } - public System.Collections.Generic.IDictionary Data { get; } - public string? Description { get; set; } - public int? ExceptionId { get; set; } - public bool? Handled { get; set; } - public string? HelpLink { get; set; } - public bool IsExceptionGroup { get; set; } - public System.Collections.Generic.IDictionary Meta { get; } - public int? ParentId { get; set; } - public string? Source { get; set; } - public bool Synthetic { get; set; } - public string Type { get; set; } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } - public static Sentry.Protocol.Mechanism FromJson(System.Text.Json.JsonElement json) { } - } - public sealed class OperatingSystem : Sentry.ISentryJsonSerializable - { - public const string Type = "os"; - public OperatingSystem() { } - public string? Build { get; set; } - public string? KernelVersion { get; set; } - public string? Name { get; set; } - public string? RawDescription { get; set; } - public bool? Rooted { get; set; } - public string? Version { get; set; } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? _) { } - public static Sentry.Protocol.OperatingSystem FromJson(System.Text.Json.JsonElement json) { } - } - public sealed class Response : Sentry.ISentryJsonSerializable - { - public const string Type = "response"; - public Response() { } - public long? BodySize { get; set; } - public string? Cookies { get; set; } - public object? Data { get; set; } - public System.Collections.Generic.IDictionary Headers { get; } - public short? StatusCode { get; set; } - public Sentry.Protocol.Response Clone() { } - public void UpdateFrom(Sentry.Protocol.Response source) { } - public void UpdateFrom(object source) { } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } - public static Sentry.Protocol.Response FromJson(System.Text.Json.JsonElement json) { } - } - public sealed class Runtime : Sentry.ISentryJsonSerializable - { - public const string Type = "runtime"; - public Runtime() { } - public string? Build { get; set; } - public string? Identifier { get; set; } - public string? Name { get; set; } - public string? RawDescription { get; set; } - public string? Version { get; set; } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? _) { } - public static Sentry.Protocol.Runtime FromJson(System.Text.Json.JsonElement json) { } - } - public sealed class SentryException : Sentry.ISentryJsonSerializable - { - public SentryException() { } - public Sentry.Protocol.Mechanism? Mechanism { get; set; } - public string? Module { get; set; } - public Sentry.SentryStackTrace? Stacktrace { get; set; } - public int ThreadId { get; set; } - public string? Type { get; set; } - public string? Value { get; set; } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } - public static Sentry.Protocol.SentryException FromJson(System.Text.Json.JsonElement json) { } - } - public class Trace : Sentry.ISentryJsonSerializable, Sentry.Protocol.ITraceContext - { - public const string Type = "trace"; - public Trace() { } - public string? Description { get; set; } - public bool? IsSampled { get; } - public string Operation { get; set; } - public string? Origin { get; } - public Sentry.SpanId? ParentSpanId { get; set; } - public Sentry.SpanId SpanId { get; set; } - public Sentry.SpanStatus? Status { get; set; } - public Sentry.SentryId TraceId { get; set; } - public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } - public static Sentry.Protocol.Trace FromJson(System.Text.Json.JsonElement json) { } - } -} -namespace Sentry.Protocol.Envelopes -{ - public sealed class Envelope : Sentry.Protocol.Envelopes.ISerializable, System.IDisposable - { - public Envelope(System.Collections.Generic.IReadOnlyDictionary header, System.Collections.Generic.IReadOnlyList items) { } - public System.Collections.Generic.IReadOnlyDictionary Header { get; } - public System.Collections.Generic.IReadOnlyList Items { get; } - public void Dispose() { } - public void Serialize(System.IO.Stream stream, Sentry.Extensibility.IDiagnosticLogger? logger) { } - public System.Threading.Tasks.Task SerializeAsync(System.IO.Stream stream, Sentry.Extensibility.IDiagnosticLogger? logger, System.Threading.CancellationToken cancellationToken = default) { } - public Sentry.SentryId? TryGetEventId() { } - public static System.Threading.Tasks.Task DeserializeAsync(System.IO.Stream stream, System.Threading.CancellationToken cancellationToken = default) { } - public static Sentry.Protocol.Envelopes.Envelope FromCheckIn(Sentry.SentryCheckIn checkIn) { } - public static Sentry.Protocol.Envelopes.Envelope FromEvent(Sentry.SentryEvent @event, Sentry.Extensibility.IDiagnosticLogger? logger = null, System.Collections.Generic.IReadOnlyCollection? attachments = null, Sentry.SessionUpdate? sessionUpdate = null) { } - public static Sentry.Protocol.Envelopes.Envelope FromSession(Sentry.SessionUpdate sessionUpdate) { } - public static Sentry.Protocol.Envelopes.Envelope FromTransaction(Sentry.SentryTransaction transaction) { } - public static Sentry.Protocol.Envelopes.Envelope FromUserFeedback(Sentry.UserFeedback sentryUserFeedback) { } - } - public sealed class EnvelopeItem : Sentry.Protocol.Envelopes.ISerializable, System.IDisposable - { - public EnvelopeItem(System.Collections.Generic.IReadOnlyDictionary header, Sentry.Protocol.Envelopes.ISerializable payload) { } - public System.Collections.Generic.IReadOnlyDictionary Header { get; } - public Sentry.Protocol.Envelopes.ISerializable Payload { get; } - public void Dispose() { } - public void Serialize(System.IO.Stream stream, Sentry.Extensibility.IDiagnosticLogger? logger) { } - public System.Threading.Tasks.Task SerializeAsync(System.IO.Stream stream, Sentry.Extensibility.IDiagnosticLogger? logger, System.Threading.CancellationToken cancellationToken = default) { } - public string? TryGetFileName() { } - public long? TryGetLength() { } - public string? TryGetType() { } - public static System.Threading.Tasks.Task DeserializeAsync(System.IO.Stream stream, System.Threading.CancellationToken cancellationToken = default) { } - public static Sentry.Protocol.Envelopes.EnvelopeItem FromAttachment(Sentry.SentryAttachment attachment) { } - public static Sentry.Protocol.Envelopes.EnvelopeItem FromCheckIn(Sentry.SentryCheckIn checkIn) { } - public static Sentry.Protocol.Envelopes.EnvelopeItem FromEvent(Sentry.SentryEvent @event) { } - public static Sentry.Protocol.Envelopes.EnvelopeItem FromSession(Sentry.SessionUpdate sessionUpdate) { } - public static Sentry.Protocol.Envelopes.EnvelopeItem FromTransaction(Sentry.SentryTransaction transaction) { } - public static Sentry.Protocol.Envelopes.EnvelopeItem FromUserFeedback(Sentry.UserFeedback sentryUserFeedback) { } - } - public interface ISerializable - { - void Serialize(System.IO.Stream stream, Sentry.Extensibility.IDiagnosticLogger? logger); - System.Threading.Tasks.Task SerializeAsync(System.IO.Stream stream, Sentry.Extensibility.IDiagnosticLogger? logger, System.Threading.CancellationToken cancellationToken = default); - } -} -namespace Sentry.Reflection -{ - public static class AssemblyExtensions - { - public static Sentry.SdkVersion GetNameAndVersion(this System.Reflection.Assembly asm) { } - } -} -public static class SentryExceptionExtensions -{ - public static void AddSentryContext(this System.Exception ex, string name, System.Collections.Generic.IReadOnlyDictionary data) { } - public static void AddSentryTag(this System.Exception ex, string name, string value) { } - public static void SetSentryMechanism(this System.Exception ex, string type, string? description = null, bool? handled = default) { } -} \ No newline at end of file diff --git a/test/Sentry.Tests/HintTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.DotNet6_0.verified.txt b/test/Sentry.Tests/HintTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.DotNet6_0.verified.txt deleted file mode 100644 index ae69b386e8..0000000000 --- a/test/Sentry.Tests/HintTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.DotNet6_0.verified.txt +++ /dev/null @@ -1,181 +0,0 @@ -[ - { - Header: { - sdk: { - name: sentry.dotnet - } - }, - Items: [ - { - Header: { - type: session - }, - Payload: { - Source: { - DistinctId: Guid_1, - Release: release, - Environment: production, - IsInitial: true - } - } - } - ] - }, - { - Header: { - event_id: Guid_2, - sdk: { - name: sentry.dotnet - }, - trace: { - environment: production, - public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, - release: release, - sample_rate: 1, - trace_id: Guid_3, - transaction: my transaction - } - }, - Items: [ - { - Header: { - type: event - }, - Payload: { - Source: { - Platform: csharp, - SentryExceptions: [ - { - Mechanism: { - Type: generic, - Handled: false, - Synthetic: false - } - } - ], - SentryThreads: [ - { - Crashed: false, - Current: true - } - ], - DebugImages: [ - { - Type: pe_dotnet, - ImageAddress: null, - ImageSize: null, - DebugId: ________-____-____-____-____________-________, - DebugChecksum: null, - DebugFile: .../System.Private.CoreLib.pdb, - CodeId: ______________, - CodeFile: .../System.Private.CoreLib.dll - }, - { - Type: pe_dotnet, - ImageAddress: null, - ImageSize: null, - DebugId: ________-____-____-____-____________-________, - DebugChecksum: ______:________________________________________________________________, - DebugFile: .../Sentry.Tests.pdb, - CodeId: _____________, - CodeFile: .../Sentry.Tests.dll - }, - { - Type: pe_dotnet, - ImageAddress: null, - ImageSize: null, - DebugId: ________-____-____-____-____________-________, - DebugChecksum: ______:________________________________________________________________, - DebugFile: xunit.execution.dotnet.pdb, - CodeId: _____________, - CodeFile: .../xunit.execution.dotnet.dll - }, - { - Type: pe_dotnet, - ImageAddress: null, - ImageSize: null, - DebugId: ________-____-____-____-____________-________, - DebugChecksum: ______:________________________________________________________________, - DebugFile: xunit.core.pdb, - CodeId: _____________, - CodeFile: .../xunit.core.dll - } - ], - Level: error, - TransactionName: my transaction, - Request: {}, - Contexts: { - trace: { - Operation: - } - }, - User: {}, - Environment: production - } - } - }, - { - Header: { - type: session - }, - Payload: { - Source: { - DistinctId: Guid_1, - Release: release, - Environment: production, - ErrorCount: 1, - IsInitial: false, - SequenceNumber: 1, - EndStatus: Crashed - } - } - } - ] - }, - { - Header: { - event_id: Guid_4, - sdk: { - name: sentry.dotnet - }, - trace: { - environment: production, - public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, - release: release, - sample_rate: 1, - trace_id: Guid_3, - transaction: my transaction - } - }, - Items: [ - { - Header: { - type: transaction - }, - Payload: { - Source: { - Name: my transaction, - Platform: csharp, - Operation: my operation, - Description: , - Status: Aborted, - IsSampled: true, - SampleRate: 1.0, - Request: {}, - Contexts: { - trace: { - Operation: my operation, - Description: , - Status: Aborted, - IsSampled: true - } - }, - User: {}, - Environment: production, - IsFinished: true - } - } - } - ] - } -] \ No newline at end of file diff --git a/test/Sentry.Tests/HintTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.DotNet7_0.verified.txt b/test/Sentry.Tests/HintTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.DotNet7_0.verified.txt deleted file mode 100644 index 2824ce63df..0000000000 --- a/test/Sentry.Tests/HintTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.DotNet7_0.verified.txt +++ /dev/null @@ -1,181 +0,0 @@ -[ - { - Header: { - sdk: { - name: sentry.dotnet - } - }, - Items: [ - { - Header: { - type: session - }, - Payload: { - Source: { - DistinctId: Guid_1, - Release: release, - Environment: production, - IsInitial: true - } - } - } - ] - }, - { - Header: { - event_id: Guid_2, - sdk: { - name: sentry.dotnet - }, - trace: { - environment: production, - public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, - release: release, - sample_rate: 1, - trace_id: Guid_3, - transaction: my transaction - } - }, - Items: [ - { - Header: { - type: event - }, - Payload: { - Source: { - Platform: csharp, - SentryExceptions: [ - { - Mechanism: { - Type: generic, - Handled: false, - Synthetic: false - } - } - ], - SentryThreads: [ - { - Crashed: false, - Current: true - } - ], - DebugImages: [ - { - Type: pe_dotnet, - ImageAddress: null, - ImageSize: null, - DebugId: ________-____-____-____-____________-________, - DebugChecksum: ______:________________________________________________________________, - DebugFile: .../System.Private.CoreLib.pdb, - CodeId: ______________, - CodeFile: .../System.Private.CoreLib.dll - }, - { - Type: pe_dotnet, - ImageAddress: null, - ImageSize: null, - DebugId: ________-____-____-____-____________-________, - DebugChecksum: ______:________________________________________________________________, - DebugFile: .../Sentry.Tests.pdb, - CodeId: _____________, - CodeFile: .../Sentry.Tests.dll - }, - { - Type: pe_dotnet, - ImageAddress: null, - ImageSize: null, - DebugId: ________-____-____-____-____________-________, - DebugChecksum: ______:________________________________________________________________, - DebugFile: xunit.execution.dotnet.pdb, - CodeId: _____________, - CodeFile: .../xunit.execution.dotnet.dll - }, - { - Type: pe_dotnet, - ImageAddress: null, - ImageSize: null, - DebugId: ________-____-____-____-____________-________, - DebugChecksum: ______:________________________________________________________________, - DebugFile: xunit.core.pdb, - CodeId: _____________, - CodeFile: .../xunit.core.dll - } - ], - Level: error, - TransactionName: my transaction, - Request: {}, - Contexts: { - trace: { - Operation: - } - }, - User: {}, - Environment: production - } - } - }, - { - Header: { - type: session - }, - Payload: { - Source: { - DistinctId: Guid_1, - Release: release, - Environment: production, - ErrorCount: 1, - IsInitial: false, - SequenceNumber: 1, - EndStatus: Crashed - } - } - } - ] - }, - { - Header: { - event_id: Guid_4, - sdk: { - name: sentry.dotnet - }, - trace: { - environment: production, - public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, - release: release, - sample_rate: 1, - trace_id: Guid_3, - transaction: my transaction - } - }, - Items: [ - { - Header: { - type: transaction - }, - Payload: { - Source: { - Name: my transaction, - Platform: csharp, - Operation: my operation, - Description: , - Status: Aborted, - IsSampled: true, - SampleRate: 1.0, - Request: {}, - Contexts: { - trace: { - Operation: my operation, - Description: , - Status: Aborted, - IsSampled: true - } - }, - User: {}, - Environment: production, - IsFinished: true - } - } - } - ] - } -] \ No newline at end of file diff --git a/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.DotNet6_0.verified.txt b/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.DotNet6_0.verified.txt deleted file mode 100644 index a6d89f3137..0000000000 --- a/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.DotNet6_0.verified.txt +++ /dev/null @@ -1,190 +0,0 @@ -[ - { - Header: { - sdk: { - name: sentry.dotnet - } - }, - Items: [ - { - Header: { - type: session - }, - Payload: { - Source: { - DistinctId: Guid_1, - Release: release, - Environment: production, - IsInitial: true - } - } - } - ] - }, - { - Header: { - event_id: Guid_2, - sdk: { - name: sentry.dotnet - }, - trace: { - environment: production, - public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, - release: release, - sample_rate: 1, - sampled: true, - trace_id: Guid_3, - transaction: my transaction - } - }, - Items: [ - { - Header: { - type: event - }, - Payload: { - Source: { - Platform: csharp, - SentryExceptions: [ - { - Mechanism: { - Type: generic, - Handled: false, - Synthetic: false, - IsExceptionGroup: false - } - } - ], - SentryThreads: [ - { - Crashed: false, - Current: true - } - ], - DebugImages: [ - { - Type: pe_dotnet, - ImageAddress: null, - ImageSize: null, - DebugId: ________-____-____-____-____________-________, - DebugChecksum: null, - DebugFile: .../System.Private.CoreLib.pdb, - CodeId: ______________, - CodeFile: .../System.Private.CoreLib.dll - }, - { - Type: pe_dotnet, - ImageAddress: null, - ImageSize: null, - DebugId: ________-____-____-____-____________-________, - DebugChecksum: ______:________________________________________________________________, - DebugFile: .../Sentry.Tests.pdb, - CodeId: ______________, - CodeFile: .../Sentry.Tests.dll - }, - { - Type: pe_dotnet, - ImageAddress: null, - ImageSize: null, - DebugId: ________-____-____-____-____________-________, - DebugChecksum: ______:________________________________________________________________, - DebugFile: xunit.execution.dotnet.pdb, - CodeId: ______________, - CodeFile: .../xunit.execution.dotnet.dll - }, - { - Type: pe_dotnet, - ImageAddress: null, - ImageSize: null, - DebugId: ________-____-____-____-____________-________, - DebugChecksum: ______:________________________________________________________________, - DebugFile: xunit.core.pdb, - CodeId: ______________, - CodeFile: .../xunit.core.dll - } - ], - Level: error, - TransactionName: my transaction, - Request: {}, - Contexts: { - trace: { - Operation: - } - }, - User: { - Id: Guid_1, - IpAddress: {{auto}} - }, - Environment: production - } - } - }, - { - Header: { - type: session - }, - Payload: { - Source: { - DistinctId: Guid_1, - Release: release, - Environment: production, - ErrorCount: 1, - IsInitial: false, - SequenceNumber: 1, - EndStatus: Crashed - } - } - } - ] - }, - { - Header: { - event_id: Guid_4, - sdk: { - name: sentry.dotnet - }, - trace: { - environment: production, - public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, - release: release, - sample_rate: 1, - sampled: true, - trace_id: Guid_3, - transaction: my transaction - } - }, - Items: [ - { - Header: { - type: transaction - }, - Payload: { - Source: { - Name: my transaction, - Platform: csharp, - Operation: my operation, - Description: , - Status: Aborted, - IsSampled: true, - SampleRate: 1.0, - Request: {}, - Contexts: { - trace: { - Operation: my operation, - Description: , - Status: Aborted, - IsSampled: true - } - }, - User: { - Id: Guid_1, - IpAddress: {{auto}} - }, - Environment: production, - IsFinished: true - } - } - } - ] - } -] \ No newline at end of file diff --git a/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.DotNet7_0.verified.txt b/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.DotNet7_0.verified.txt deleted file mode 100644 index e2f6a68211..0000000000 --- a/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.DotNet7_0.verified.txt +++ /dev/null @@ -1,190 +0,0 @@ -[ - { - Header: { - sdk: { - name: sentry.dotnet - } - }, - Items: [ - { - Header: { - type: session - }, - Payload: { - Source: { - DistinctId: Guid_1, - Release: release, - Environment: production, - IsInitial: true - } - } - } - ] - }, - { - Header: { - event_id: Guid_2, - sdk: { - name: sentry.dotnet - }, - trace: { - environment: production, - public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, - release: release, - sample_rate: 1, - sampled: true, - trace_id: Guid_3, - transaction: my transaction - } - }, - Items: [ - { - Header: { - type: event - }, - Payload: { - Source: { - Platform: csharp, - SentryExceptions: [ - { - Mechanism: { - Type: generic, - Handled: false, - Synthetic: false, - IsExceptionGroup: false - } - } - ], - SentryThreads: [ - { - Crashed: false, - Current: true - } - ], - DebugImages: [ - { - Type: pe_dotnet, - ImageAddress: null, - ImageSize: null, - DebugId: ________-____-____-____-____________-________, - DebugChecksum: ______:________________________________________________________________, - DebugFile: .../System.Private.CoreLib.pdb, - CodeId: ______________, - CodeFile: .../System.Private.CoreLib.dll - }, - { - Type: pe_dotnet, - ImageAddress: null, - ImageSize: null, - DebugId: ________-____-____-____-____________-________, - DebugChecksum: ______:________________________________________________________________, - DebugFile: .../Sentry.Tests.pdb, - CodeId: ______________, - CodeFile: .../Sentry.Tests.dll - }, - { - Type: pe_dotnet, - ImageAddress: null, - ImageSize: null, - DebugId: ________-____-____-____-____________-________, - DebugChecksum: ______:________________________________________________________________, - DebugFile: xunit.execution.dotnet.pdb, - CodeId: ______________, - CodeFile: .../xunit.execution.dotnet.dll - }, - { - Type: pe_dotnet, - ImageAddress: null, - ImageSize: null, - DebugId: ________-____-____-____-____________-________, - DebugChecksum: ______:________________________________________________________________, - DebugFile: xunit.core.pdb, - CodeId: ______________, - CodeFile: .../xunit.core.dll - } - ], - Level: error, - TransactionName: my transaction, - Request: {}, - Contexts: { - trace: { - Operation: - } - }, - User: { - Id: Guid_1, - IpAddress: {{auto}} - }, - Environment: production - } - } - }, - { - Header: { - type: session - }, - Payload: { - Source: { - DistinctId: Guid_1, - Release: release, - Environment: production, - ErrorCount: 1, - IsInitial: false, - SequenceNumber: 1, - EndStatus: Crashed - } - } - } - ] - }, - { - Header: { - event_id: Guid_4, - sdk: { - name: sentry.dotnet - }, - trace: { - environment: production, - public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, - release: release, - sample_rate: 1, - sampled: true, - trace_id: Guid_3, - transaction: my transaction - } - }, - Items: [ - { - Header: { - type: transaction - }, - Payload: { - Source: { - Name: my transaction, - Platform: csharp, - Operation: my operation, - Description: , - Status: Aborted, - IsSampled: true, - SampleRate: 1.0, - Request: {}, - Contexts: { - trace: { - Operation: my operation, - Description: , - Status: Aborted, - IsSampled: true - } - }, - User: { - Id: Guid_1, - IpAddress: {{auto}} - }, - Environment: production, - IsFinished: true - } - } - } - ] - } -] \ No newline at end of file diff --git a/test/Sentry.Tests/Internals/MemoryInfoTests.WriteTo.DotNet6_0.verified.txt b/test/Sentry.Tests/Internals/MemoryInfoTests.WriteTo.DotNet6_0.verified.txt deleted file mode 100644 index faa8bc7db0..0000000000 --- a/test/Sentry.Tests/Internals/MemoryInfoTests.WriteTo.DotNet6_0.verified.txt +++ /dev/null @@ -1,19 +0,0 @@ -{ - allocated_bytes: 1, - fragmented_bytes: 2, - heap_size_bytes: 3, - high_memory_load_threshold_bytes: 4, - total_available_memory_bytes: 5, - memory_load_bytes: 6, - total_committed_bytes: 7, - promoted_bytes: 8, - pinned_objects_count: 9, - pause_time_percentage: 10, - index: 11, - finalization_pending_count: 12, - compacted: true, - concurrent: false, - pause_durations: [ - 1000 - ] -} \ No newline at end of file diff --git a/test/Sentry.Tests/Internals/MemoryInfoTests.WriteTo.DotNet7_0.verified.txt b/test/Sentry.Tests/Internals/MemoryInfoTests.WriteTo.DotNet7_0.verified.txt deleted file mode 100644 index 05e978d7b7..0000000000 --- a/test/Sentry.Tests/Internals/MemoryInfoTests.WriteTo.DotNet7_0.verified.txt +++ /dev/null @@ -1,19 +0,0 @@ -{ - allocated_bytes: 1, - fragmented_bytes: 2, - heap_size_bytes: 3, - high_memory_load_threshold_bytes: 4, - total_available_memory_bytes: 5, - memory_load_bytes: 6, - total_committed_bytes: 7, - promoted_bytes: 8, - pinned_objects_count: 9, - pause_time_percentage: 10, - index: 11, - finalization_pending_count: 12, - compacted: true, - concurrent: false, - pause_durations: [ - 1000 - ] -} \ No newline at end of file diff --git a/test/Sentry.Tests/SentryClientTests.CaptureEvent_BeforeEventThrows_ErrorToEventBreadcrumb.DotNet6_0.verified.txt b/test/Sentry.Tests/SentryClientTests.CaptureEvent_BeforeEventThrows_ErrorToEventBreadcrumb.DotNet6_0.verified.txt deleted file mode 100644 index 6dcf0fd76a..0000000000 --- a/test/Sentry.Tests/SentryClientTests.CaptureEvent_BeforeEventThrows_ErrorToEventBreadcrumb.DotNet6_0.verified.txt +++ /dev/null @@ -1,15 +0,0 @@ -[ - { - Timestamp: DateTimeOffset_1, - Message: BeforeSend callback failed., - Data: { - message: Exception message!, - stackTrace: -at Task Sentry.Tests.SentryClientTests.CaptureEvent_BeforeEventThrows_ErrorToEventBreadcrumb() -at void Sentry.SentryOptions.SetBeforeSend(...) -at SentryEvent Sentry.SentryClient.BeforeSend(...) - }, - Category: SentryClient, - Level: error - } -] \ No newline at end of file diff --git a/test/Sentry.Tests/SentryClientTests.CaptureEvent_BeforeEventThrows_ErrorToEventBreadcrumb.DotNet7_0.verified.txt b/test/Sentry.Tests/SentryClientTests.CaptureEvent_BeforeEventThrows_ErrorToEventBreadcrumb.DotNet7_0.verified.txt deleted file mode 100644 index 6dcf0fd76a..0000000000 --- a/test/Sentry.Tests/SentryClientTests.CaptureEvent_BeforeEventThrows_ErrorToEventBreadcrumb.DotNet7_0.verified.txt +++ /dev/null @@ -1,15 +0,0 @@ -[ - { - Timestamp: DateTimeOffset_1, - Message: BeforeSend callback failed., - Data: { - message: Exception message!, - stackTrace: -at Task Sentry.Tests.SentryClientTests.CaptureEvent_BeforeEventThrows_ErrorToEventBreadcrumb() -at void Sentry.SentryOptions.SetBeforeSend(...) -at SentryEvent Sentry.SentryClient.BeforeSend(...) - }, - Category: SentryClient, - Level: error - } -] \ No newline at end of file From f020492207f229ab65216385c004082cfaf74639 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos <6349682+vaind@users.noreply.github.com> Date: Fri, 17 Jan 2025 07:44:09 +0100 Subject: [PATCH 094/363] chore: move HTTPClientFactory logs (#3901) * chore:move logs * Update SdkComposer.cs --------- Co-authored-by: James Crosswell --- src/Sentry/Internal/SdkComposer.cs | 14 +------------- src/Sentry/SentryOptions.cs | 7 +++++++ 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/src/Sentry/Internal/SdkComposer.cs b/src/Sentry/Internal/SdkComposer.cs index 1a8aac88f6..dab9a08986 100644 --- a/src/Sentry/Internal/SdkComposer.cs +++ b/src/Sentry/Internal/SdkComposer.cs @@ -23,7 +23,7 @@ private ITransport CreateTransport() _options.LogDebug("Creating transport."); // Start from either the transport given on options, or create a new HTTP transport. - var transport = _options.Transport ?? CreateHttpTransport(); + var transport = _options.Transport ?? new LazyHttpTransport(_options); // When a cache directory path is given, wrap the transport in a caching transport. if (!string.IsNullOrWhiteSpace(_options.CacheDirectoryPath)) @@ -75,18 +75,6 @@ You can set a different environment via SENTRY_ENVIRONMENT env var or programati return transport; } - private LazyHttpTransport CreateHttpTransport() - { - if (_options.SentryHttpClientFactory is not null) - { - _options.LogDebug( - "Using ISentryHttpClientFactory set through options: {0}.", - _options.SentryHttpClientFactory.GetType().Name); - } - - return new LazyHttpTransport(_options); - } - public IBackgroundWorker CreateBackgroundWorker() { if (_options.BackgroundWorker is { } worker) diff --git a/src/Sentry/SentryOptions.cs b/src/Sentry/SentryOptions.cs index be6d9eaa57..de8d930066 100644 --- a/src/Sentry/SentryOptions.cs +++ b/src/Sentry/SentryOptions.cs @@ -222,6 +222,13 @@ internal IEnumerable Integrations internal HttpClient GetHttpClient() { + if (SentryHttpClientFactory is not null) + { + DiagnosticLogger?.LogDebug( + "Using ISentryHttpClientFactory set through options: {0}.", + SentryHttpClientFactory.GetType().Name); + } + var factory = SentryHttpClientFactory ?? new DefaultSentryHttpClientFactory(); return factory.Create(this); } From b73a294389f6efb4e7ef141c36de37e77e315000 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 17 Jan 2025 21:56:49 +1300 Subject: [PATCH 095/363] chore: update modules/sentry-native to 0.7.18 (#3891) Co-authored-by: GitHub --- CHANGELOG.md | 6 ++++++ modules/sentry-native | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 72385a3017..cf2cf291e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ - .NET on iOS: Add experimental EnableAppHangTrackingV2 configuration flag to the options binding SDK ([#3877](https://github.com/getsentry/sentry-dotnet/pull/3877)) - Added `SentryOptions.DisableSentryHttpMessageHandler`. Useful if you're using `OpenTelemetry.Instrumentation.Http` and ending up with duplicate spans. ([#3879](https://github.com/getsentry/sentry-dotnet/pull/3879)) +### Dependencies + +- Bump Native SDK from v0.7.17 to v0.7.18 ([#3891](https://github.com/getsentry/sentry-dotnet/pull/3891)) + - [changelog](https://github.com/getsentry/sentry-native/blob/master/CHANGELOG.md#0718) + - [diff](https://github.com/getsentry/sentry-native/compare/0.7.17...0.7.18) + ## 5.0.1 ### Fixes diff --git a/modules/sentry-native b/modules/sentry-native index 4072538dfd..52ee37c24c 160000 --- a/modules/sentry-native +++ b/modules/sentry-native @@ -1 +1 @@ -Subproject commit 4072538dfdbcafb3974318fe08798e51f62786d9 +Subproject commit 52ee37c24c1ec5a6f24aa8a5057bc6273f2e1162 From b22155fc964dbbdd852636ee9b2854558e28b455 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Tue, 21 Jan 2025 09:17:27 +1300 Subject: [PATCH 096/363] Workaround flaky SSL connection installing Android SDKs (#3904) --- .github/workflows/build.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0947f636db..187de670ef 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -220,10 +220,17 @@ jobs: uses: ./.github/actions/buildnative - name: Install Android SDKs + id: installandroidsdks + continue-on-error: true if: runner.os == 'macOS' run: | dotnet build src/Sentry/Sentry.csproj -t:InstallAndroidDependencies -f:net8.0-android34.0 -p:AcceptAndroidSDKLicenses=True -p:AndroidSdkPath="/usr/local/lib/android/sdk/" + - name: Install Android SDKs (retry) + if: steps.installandroidsdks.outcome=='failure' && runner.os == 'macOS' + run: | + dotnet build src/Sentry/Sentry.csproj -t:InstallAndroidDependencies -f:net8.0-android34.0 -p:AcceptAndroidSDKLicenses=True -p:AndroidSdkPath="/usr/local/lib/android/sdk/" + - name: Publish Test app (macOS) run: dotnet publish test/Sentry.TrimTest/Sentry.TrimTest.csproj -c Release -r osx-arm64 From 3e4af3007eeba086c03a049511b38b1404be8d9c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 21 Jan 2025 21:52:03 +1300 Subject: [PATCH 097/363] chore: update scripts/update-java.ps1 to 7.20.1 (#3907) Co-authored-by: GitHub --- CHANGELOG.md | 3 +++ src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cf2cf291e5..f47b834bdc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,9 @@ - Bump Native SDK from v0.7.17 to v0.7.18 ([#3891](https://github.com/getsentry/sentry-dotnet/pull/3891)) - [changelog](https://github.com/getsentry/sentry-native/blob/master/CHANGELOG.md#0718) - [diff](https://github.com/getsentry/sentry-native/compare/0.7.17...0.7.18) +- Bump Java SDK from v7.20.0 to v7.20.1 ([#3907](https://github.com/getsentry/sentry-dotnet/pull/3907)) + - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#7201) + - [diff](https://github.com/getsentry/sentry-java/compare/7.20.0...7.20.1) ## 5.0.1 diff --git a/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj b/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj index 46cc0077ab..b9aec99266 100644 --- a/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj +++ b/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj @@ -3,7 +3,7 @@ net8.0-android34.0 $(NoWarn);BG8605;BG8606 - 7.20.0 + 7.20.1 $(BaseIntermediateOutputPath)sdks\Sentry\Android\$(SentryAndroidSdkVersion)\ .NET Bindings for the Sentry Android SDK From dfd3f2de7bb48c17f16c5828b30960ac35cabefe Mon Sep 17 00:00:00 2001 From: Philipp Hofmann Date: Thu, 23 Jan 2025 01:49:03 +0100 Subject: [PATCH 098/363] chore: Increase date range for MIT licence (#3914) It's 2025 now. --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 63e0ed9e4d..a7f4292ba5 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2018-2024 Sentry +Copyright (c) 2018-2025 Sentry Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From 8348561a168fa41b824d8b71367e039f56b8f6cf Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 23 Jan 2025 13:53:10 +1300 Subject: [PATCH 099/363] chore: update scripts/update-cli.ps1 to 2.41.1 (#3910) Co-authored-by: GitHub --- CHANGELOG.md | 3 +++ Directory.Build.props | 2 +- src/Sentry/Sentry.csproj | 14 +++++++------- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f47b834bdc..ed29453183 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,9 @@ - Bump Java SDK from v7.20.0 to v7.20.1 ([#3907](https://github.com/getsentry/sentry-dotnet/pull/3907)) - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#7201) - [diff](https://github.com/getsentry/sentry-java/compare/7.20.0...7.20.1) +- Bump CLI from v2.40.0 to v2.41.1 ([#3910](https://github.com/getsentry/sentry-dotnet/pull/3910)) + - [changelog](https://github.com/getsentry/sentry-cli/blob/master/CHANGELOG.md#2411) + - [diff](https://github.com/getsentry/sentry-cli/compare/2.40.0...2.41.1) ## 5.0.1 diff --git a/Directory.Build.props b/Directory.Build.props index b9a20c6d18..4cc66fb411 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -83,7 +83,7 @@ - 2.40.0 + 2.41.1 $(MSBuildThisFileDirectory)tools\sentry-cli\$(SentryCLIVersion)\ diff --git a/src/Sentry/Sentry.csproj b/src/Sentry/Sentry.csproj index 9a6d9a3afc..2a12b41a38 100644 --- a/src/Sentry/Sentry.csproj +++ b/src/Sentry/Sentry.csproj @@ -120,13 +120,13 @@ <_OSArchitecture>$([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture) - - - - - - - + + + + + + + From b6ee85db678cdbe1b5340bd8ae00aca82dcc4419 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 23 Jan 2025 13:53:55 +1300 Subject: [PATCH 100/363] chore: update modules/sentry-native to 0.7.19 (#3908) Co-authored-by: GitHub --- CHANGELOG.md | 6 +++--- modules/sentry-native | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ed29453183..6354eb4f46 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,9 +9,9 @@ ### Dependencies -- Bump Native SDK from v0.7.17 to v0.7.18 ([#3891](https://github.com/getsentry/sentry-dotnet/pull/3891)) - - [changelog](https://github.com/getsentry/sentry-native/blob/master/CHANGELOG.md#0718) - - [diff](https://github.com/getsentry/sentry-native/compare/0.7.17...0.7.18) +- Bump Native SDK from v0.7.17 to v0.7.19 ([#3891](https://github.com/getsentry/sentry-dotnet/pull/3891), [#3908](https://github.com/getsentry/sentry-dotnet/pull/3908)) + - [changelog](https://github.com/getsentry/sentry-native/blob/master/CHANGELOG.md#0719) + - [diff](https://github.com/getsentry/sentry-native/compare/0.7.17...0.7.19) - Bump Java SDK from v7.20.0 to v7.20.1 ([#3907](https://github.com/getsentry/sentry-dotnet/pull/3907)) - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#7201) - [diff](https://github.com/getsentry/sentry-java/compare/7.20.0...7.20.1) diff --git a/modules/sentry-native b/modules/sentry-native index 52ee37c24c..62b966c487 160000 --- a/modules/sentry-native +++ b/modules/sentry-native @@ -1 +1 @@ -Subproject commit 52ee37c24c1ec5a6f24aa8a5057bc6273f2e1162 +Subproject commit 62b966c487f08773dc23cfb17b991bdd3f170ae4 From 2b8ff9ec4da4b1e62f81fdcb1d1ad06342836fe3 Mon Sep 17 00:00:00 2001 From: Philipp Hofmann Date: Thu, 23 Jan 2025 22:20:23 +0100 Subject: [PATCH 101/363] Remove date range for LICENSE (#3916) In our internal Open Source Legal Policy, we decided that licenses don't require a data range. This also has the advantage of not updating the date range yearly. --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index a7f4292ba5..3b3d6e5e27 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2018-2025 Sentry +Copyright (c) 2018 Sentry Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From aa2caa1de05ccfe3d849acf55fa597d6c3ea1be8 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos <6349682+vaind@users.noreply.github.com> Date: Tue, 28 Jan 2025 01:57:04 +0100 Subject: [PATCH 102/363] chore: fixup solution file (#3913) --- Sentry.sln | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sentry.sln b/Sentry.sln index 62290abdc9..3ef6fd24aa 100644 --- a/Sentry.sln +++ b/Sentry.sln @@ -170,7 +170,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Hangfire", "src\Sent EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Hangfire.Tests", "test\Sentry.Hangfire.Tests\Sentry.Hangfire.Tests.csproj", "{46E40BE8-1AB0-4846-B0A2-A40AD0272C64}" EndProject -Project("{00000000-0000-0000-0000-000000000000}") = "Sentry.Samples.AspNetCore.WebAPI.Profiling", "samples\Sentry.Samples.AspNetCore.WebAPI.Profiling\Sentry.Samples.AspNetCore.WebAPI.Profiling.csproj", "{A5B26C14-7313-4EDC-91E3-287F9374AB75}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Samples.AspNetCore.WebAPI.Profiling", "samples\Sentry.Samples.AspNetCore.WebAPI.Profiling\Sentry.Samples.AspNetCore.WebAPI.Profiling.csproj", "{A5B26C14-7313-4EDC-91E3-287F9374AB75}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "root", "root", "{233D34AB-970E-4913-AA1E-172E833FB5B2}" ProjectSection(SolutionItems) = preProject From b31e4db47bab1bf8433118c42ecd23a473d5025e Mon Sep 17 00:00:00 2001 From: Stefan Jandl Date: Tue, 28 Jan 2025 02:02:12 +0100 Subject: [PATCH 103/363] fix: Guard `user.IpAddress = "{{ auto }}"` by `SendDefaultPii` (#3893) Co-authored-by: James Crosswell --- CHANGELOG.md | 4 ++++ src/Sentry/Internal/Enricher.cs | 10 +++++++--- ...bIntegrationTests.Versioning.DotNet8_0.verified.txt | 3 +-- ...bIntegrationTests.Versioning.DotNet9_0.verified.txt | 3 +-- ...qlListenerTests.LoggingAsync.DotNet8_0.verified.txt | 3 +-- ...qlListenerTests.LoggingAsync.DotNet9_0.verified.txt | 3 +-- ...ListenerTests.RecordsEfAsync.DotNet8_0.verified.txt | 6 ++---- ...ListenerTests.RecordsEfAsync.DotNet9_0.verified.txt | 6 ++---- ...SqlListenerTests.RecordsEfAsync.Net4_8.verified.txt | 6 ++---- .../SqlListenerTests.RecordsSqlAsync.verified.txt | 6 ++---- .../IntegrationTests.Simple.verified.txt | 6 ++---- ...Tests.LoggingInsideTheContextOfLogging.verified.txt | 6 ++---- .../IntegrationTests.Simple.verified.txt | 3 +-- ...Tests.LoggingInsideTheContextOfLogging.verified.txt | 3 +-- ...Tests.LoggingInsideTheContextOfLogging.verified.txt | 3 +-- .../EventProcessorTests.Simple.verified.txt | 3 +-- .../EventProcessorTests.WithTransaction.verified.txt | 3 +-- ...ionTransactionEndedAsCrashed.DotNet8_0.verified.txt | 6 ++---- ...ionTransactionEndedAsCrashed.DotNet9_0.verified.txt | 6 ++---- ...eptionTransactionEndedAsCrashed.Net4_8.verified.txt | 6 ++---- .../Internals/MainSentryEventProcessorTests.cs | 4 ++-- .../TransactionProcessorTests.Discard.verified.txt | 3 +-- .../TransactionProcessorTests.Simple.verified.txt | 6 ++---- 23 files changed, 43 insertions(+), 65 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6354eb4f46..9426f0ec6b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Significant change in behavior + +- The User.IpAddress is now only set to `{{auto}}` when `SendDefaultPii` is enabled. This change gives you control over IP address collection directly on the client ([#3893](https://github.com/getsentry/sentry-dotnet/pull/3893)) + ### Features - .NET on iOS: Add experimental EnableAppHangTrackingV2 configuration flag to the options binding SDK ([#3877](https://github.com/getsentry/sentry-dotnet/pull/3877)) diff --git a/src/Sentry/Internal/Enricher.cs b/src/Sentry/Internal/Enricher.cs index df28a50e32..54829dca86 100644 --- a/src/Sentry/Internal/Enricher.cs +++ b/src/Sentry/Internal/Enricher.cs @@ -76,12 +76,16 @@ public void Apply(IEventLike eventLike) // User // Report local user if opt-in PII, no user was already set to event and feature not opted-out: - if (_options is { SendDefaultPii: true, IsEnvironmentUser: true } && !eventLike.HasUser()) + if (_options.SendDefaultPii) { - eventLike.User.Username = Environment.UserName; + if (_options.IsEnvironmentUser && !eventLike.HasUser()) + { + eventLike.User.Username = Environment.UserName; + } + + eventLike.User.IpAddress ??= DefaultIpAddress; } eventLike.User.Id ??= _options.InstallationId; - eventLike.User.IpAddress ??= DefaultIpAddress; //Apply App startup and Boot time eventLike.Contexts.App.StartTime ??= ProcessInfo.Instance?.StartupTime; diff --git a/test/Sentry.AspNetCore.Tests/WebIntegrationTests.Versioning.DotNet8_0.verified.txt b/test/Sentry.AspNetCore.Tests/WebIntegrationTests.Versioning.DotNet8_0.verified.txt index a75596e368..f60140240c 100644 --- a/test/Sentry.AspNetCore.Tests/WebIntegrationTests.Versioning.DotNet8_0.verified.txt +++ b/test/Sentry.AspNetCore.Tests/WebIntegrationTests.Versioning.DotNet8_0.verified.txt @@ -26,8 +26,7 @@ } }, User: { - Id: Guid_1, - IpAddress: {{auto}} + Id: Guid_1 }, Environment: production, Breadcrumbs: [ diff --git a/test/Sentry.AspNetCore.Tests/WebIntegrationTests.Versioning.DotNet9_0.verified.txt b/test/Sentry.AspNetCore.Tests/WebIntegrationTests.Versioning.DotNet9_0.verified.txt index a75596e368..f60140240c 100644 --- a/test/Sentry.AspNetCore.Tests/WebIntegrationTests.Versioning.DotNet9_0.verified.txt +++ b/test/Sentry.AspNetCore.Tests/WebIntegrationTests.Versioning.DotNet9_0.verified.txt @@ -26,8 +26,7 @@ } }, User: { - Id: Guid_1, - IpAddress: {{auto}} + Id: Guid_1 }, Environment: production, Breadcrumbs: [ diff --git a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.LoggingAsync.DotNet8_0.verified.txt b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.LoggingAsync.DotNet8_0.verified.txt index 2eebc2dae0..298823718c 100644 --- a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.LoggingAsync.DotNet8_0.verified.txt +++ b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.LoggingAsync.DotNet8_0.verified.txt @@ -18,8 +18,7 @@ } }, User: { - Id: Guid_1, - IpAddress: {{auto}} + Id: Guid_1 }, Spans: [ { diff --git a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.LoggingAsync.DotNet9_0.verified.txt b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.LoggingAsync.DotNet9_0.verified.txt index 2eebc2dae0..298823718c 100644 --- a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.LoggingAsync.DotNet9_0.verified.txt +++ b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.LoggingAsync.DotNet9_0.verified.txt @@ -18,8 +18,7 @@ } }, User: { - Id: Guid_1, - IpAddress: {{auto}} + Id: Guid_1 }, Spans: [ { diff --git a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.DotNet8_0.verified.txt b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.DotNet8_0.verified.txt index 5c1985dbc1..aa6dedca8c 100644 --- a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.DotNet8_0.verified.txt +++ b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.DotNet8_0.verified.txt @@ -22,8 +22,7 @@ } }, User: { - Id: Guid_1, - IpAddress: {{auto}} + Id: Guid_1 } } }, @@ -46,8 +45,7 @@ } }, User: { - Id: Guid_1, - IpAddress: {{auto}} + Id: Guid_1 }, Breadcrumbs: [ { diff --git a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.DotNet9_0.verified.txt b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.DotNet9_0.verified.txt index 5c1985dbc1..aa6dedca8c 100644 --- a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.DotNet9_0.verified.txt +++ b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.DotNet9_0.verified.txt @@ -22,8 +22,7 @@ } }, User: { - Id: Guid_1, - IpAddress: {{auto}} + Id: Guid_1 } } }, @@ -46,8 +45,7 @@ } }, User: { - Id: Guid_1, - IpAddress: {{auto}} + Id: Guid_1 }, Breadcrumbs: [ { diff --git a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.Net4_8.verified.txt b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.Net4_8.verified.txt index ce2f1f8ffa..96826b04ae 100644 --- a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.Net4_8.verified.txt +++ b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.Net4_8.verified.txt @@ -22,8 +22,7 @@ } }, User: { - Id: Guid_1, - IpAddress: {{auto}} + Id: Guid_1 } } }, @@ -46,8 +45,7 @@ } }, User: { - Id: Guid_1, - IpAddress: {{auto}} + Id: Guid_1 }, Breadcrumbs: [ { diff --git a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsSqlAsync.verified.txt b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsSqlAsync.verified.txt index 37f2475d22..7d7654f67d 100644 --- a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsSqlAsync.verified.txt +++ b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsSqlAsync.verified.txt @@ -22,8 +22,7 @@ } }, User: { - Id: Guid_1, - IpAddress: {{auto}} + Id: Guid_1 } } }, @@ -46,8 +45,7 @@ } }, User: { - Id: Guid_1, - IpAddress: {{auto}} + Id: Guid_1 }, Breadcrumbs: [ { diff --git a/test/Sentry.EntityFramework.Tests/IntegrationTests.Simple.verified.txt b/test/Sentry.EntityFramework.Tests/IntegrationTests.Simple.verified.txt index bc260895fb..e24261c17c 100644 --- a/test/Sentry.EntityFramework.Tests/IntegrationTests.Simple.verified.txt +++ b/test/Sentry.EntityFramework.Tests/IntegrationTests.Simple.verified.txt @@ -22,8 +22,7 @@ } }, User: { - Id: Guid_1, - IpAddress: {{auto}} + Id: Guid_1 }, Environment: production } @@ -47,8 +46,7 @@ } }, User: { - Id: Guid_1, - IpAddress: {{auto}} + Id: Guid_1 }, Environment: production, Breadcrumbs: [ diff --git a/test/Sentry.Log4Net.Tests/IntegrationTests.LoggingInsideTheContextOfLogging.verified.txt b/test/Sentry.Log4Net.Tests/IntegrationTests.LoggingInsideTheContextOfLogging.verified.txt index 92bf1b5798..35fe2365e2 100644 --- a/test/Sentry.Log4Net.Tests/IntegrationTests.LoggingInsideTheContextOfLogging.verified.txt +++ b/test/Sentry.Log4Net.Tests/IntegrationTests.LoggingInsideTheContextOfLogging.verified.txt @@ -33,8 +33,7 @@ } }, User: { - Id: Guid_3, - IpAddress: {{auto}} + Id: Guid_3 }, Environment: production } @@ -75,8 +74,7 @@ } }, User: { - Id: Guid_3, - IpAddress: {{auto}} + Id: Guid_3 }, Environment: production } diff --git a/test/Sentry.Log4Net.Tests/IntegrationTests.Simple.verified.txt b/test/Sentry.Log4Net.Tests/IntegrationTests.Simple.verified.txt index 65415cde47..4ec1830811 100644 --- a/test/Sentry.Log4Net.Tests/IntegrationTests.Simple.verified.txt +++ b/test/Sentry.Log4Net.Tests/IntegrationTests.Simple.verified.txt @@ -32,8 +32,7 @@ } }, User: { - Id: Guid_3, - IpAddress: {{auto}} + Id: Guid_3 }, Environment: production } diff --git a/test/Sentry.NLog.Tests/IntegrationTests.LoggingInsideTheContextOfLogging.verified.txt b/test/Sentry.NLog.Tests/IntegrationTests.LoggingInsideTheContextOfLogging.verified.txt index d333fa1efb..016d53f540 100644 --- a/test/Sentry.NLog.Tests/IntegrationTests.LoggingInsideTheContextOfLogging.verified.txt +++ b/test/Sentry.NLog.Tests/IntegrationTests.LoggingInsideTheContextOfLogging.verified.txt @@ -40,8 +40,7 @@ } }, User: { - Id: Guid_3, - IpAddress: {{auto}} + Id: Guid_3 }, Environment: production } diff --git a/test/Sentry.Serilog.Tests/IntegrationTests.LoggingInsideTheContextOfLogging.verified.txt b/test/Sentry.Serilog.Tests/IntegrationTests.LoggingInsideTheContextOfLogging.verified.txt index c5df4578af..d917dd9d91 100644 --- a/test/Sentry.Serilog.Tests/IntegrationTests.LoggingInsideTheContextOfLogging.verified.txt +++ b/test/Sentry.Serilog.Tests/IntegrationTests.LoggingInsideTheContextOfLogging.verified.txt @@ -39,8 +39,7 @@ } }, User: { - Id: Guid_3, - IpAddress: {{auto}} + Id: Guid_3 }, Environment: production } diff --git a/test/Sentry.Tests/EventProcessorTests.Simple.verified.txt b/test/Sentry.Tests/EventProcessorTests.Simple.verified.txt index f4d20928c3..b63e99dd53 100644 --- a/test/Sentry.Tests/EventProcessorTests.Simple.verified.txt +++ b/test/Sentry.Tests/EventProcessorTests.Simple.verified.txt @@ -32,8 +32,7 @@ } }, User: { - Id: Guid_3, - IpAddress: {{auto}} + Id: Guid_3 }, Environment: production } diff --git a/test/Sentry.Tests/EventProcessorTests.WithTransaction.verified.txt b/test/Sentry.Tests/EventProcessorTests.WithTransaction.verified.txt index 798424284e..af9d84138f 100644 --- a/test/Sentry.Tests/EventProcessorTests.WithTransaction.verified.txt +++ b/test/Sentry.Tests/EventProcessorTests.WithTransaction.verified.txt @@ -36,8 +36,7 @@ } }, User: { - Id: Guid_3, - IpAddress: {{auto}} + Id: Guid_3 }, Environment: production } diff --git a/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.DotNet8_0.verified.txt b/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.DotNet8_0.verified.txt index e2f6a68211..34439cfbcc 100644 --- a/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.DotNet8_0.verified.txt +++ b/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.DotNet8_0.verified.txt @@ -112,8 +112,7 @@ } }, User: { - Id: Guid_1, - IpAddress: {{auto}} + Id: Guid_1 }, Environment: production } @@ -177,8 +176,7 @@ } }, User: { - Id: Guid_1, - IpAddress: {{auto}} + Id: Guid_1 }, Environment: production, IsFinished: true diff --git a/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.DotNet9_0.verified.txt b/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.DotNet9_0.verified.txt index e2f6a68211..34439cfbcc 100644 --- a/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.DotNet9_0.verified.txt +++ b/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.DotNet9_0.verified.txt @@ -112,8 +112,7 @@ } }, User: { - Id: Guid_1, - IpAddress: {{auto}} + Id: Guid_1 }, Environment: production } @@ -177,8 +176,7 @@ } }, User: { - Id: Guid_1, - IpAddress: {{auto}} + Id: Guid_1 }, Environment: production, IsFinished: true diff --git a/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.Net4_8.verified.txt b/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.Net4_8.verified.txt index 800647416d..b453712534 100644 --- a/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.Net4_8.verified.txt +++ b/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.Net4_8.verified.txt @@ -112,8 +112,7 @@ } }, User: { - Id: Guid_1, - IpAddress: {{auto}} + Id: Guid_1 }, Environment: production } @@ -177,8 +176,7 @@ } }, User: { - Id: Guid_1, - IpAddress: {{auto}} + Id: Guid_1 }, Environment: production, IsFinished: true diff --git a/test/Sentry.Tests/Internals/MainSentryEventProcessorTests.cs b/test/Sentry.Tests/Internals/MainSentryEventProcessorTests.cs index 26956f6ad3..a34d28625f 100644 --- a/test/Sentry.Tests/Internals/MainSentryEventProcessorTests.cs +++ b/test/Sentry.Tests/Internals/MainSentryEventProcessorTests.cs @@ -132,7 +132,7 @@ public void Process_SendDefaultPiiTrueAndUserIpSet_UserIpIgnoreServerInferredIp( } [Fact] - public void Process_SendDefaultPiiFalse_UserIpAuto() + public void Process_SendDefaultPiiFalse_UserIpUserIpNotSet() { //Arrange var evt = new SentryEvent(); @@ -143,7 +143,7 @@ public void Process_SendDefaultPiiFalse_UserIpAuto() _ = sut.Process(evt); //Assert - evt.User.IpAddress.Should().Be(Enricher.DefaultIpAddress); + evt.User.IpAddress.Should().Be(null); } [Fact] diff --git a/test/Sentry.Tests/TransactionProcessorTests.Discard.verified.txt b/test/Sentry.Tests/TransactionProcessorTests.Discard.verified.txt index 75f069a373..6ffc1f77f4 100644 --- a/test/Sentry.Tests/TransactionProcessorTests.Discard.verified.txt +++ b/test/Sentry.Tests/TransactionProcessorTests.Discard.verified.txt @@ -35,8 +35,7 @@ } }, User: { - Id: Guid_3, - IpAddress: {{auto}} + Id: Guid_3 }, Environment: production } diff --git a/test/Sentry.Tests/TransactionProcessorTests.Simple.verified.txt b/test/Sentry.Tests/TransactionProcessorTests.Simple.verified.txt index e97eedb4cd..b8a5f7a62b 100644 --- a/test/Sentry.Tests/TransactionProcessorTests.Simple.verified.txt +++ b/test/Sentry.Tests/TransactionProcessorTests.Simple.verified.txt @@ -35,8 +35,7 @@ } }, User: { - Id: Guid_3, - IpAddress: {{auto}} + Id: Guid_3 }, Environment: production } @@ -85,8 +84,7 @@ } }, User: { - Id: Guid_3, - IpAddress: {{auto}} + Id: Guid_3 }, Environment: production, IsFinished: true From 6b75631f99ee9e0791318b47c606766db7d95e1f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 Jan 2025 14:17:53 +1300 Subject: [PATCH 104/363] build(deps): bump github/codeql-action from 3.28.1 to 3.28.5 (#3918) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.28.1 to 3.28.5. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/b6a472f63d85b9c78a3ac5e89422239fc15e9b3c...f6091c0113d1dcf9b98e269ee48e8a7e51b7bdd4) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index acc9055ab3..843fc886c4 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -35,7 +35,7 @@ jobs: uses: ./.github/actions/environment - name: Initialize CodeQL - uses: github/codeql-action/init@b6a472f63d85b9c78a3ac5e89422239fc15e9b3c # pin@v2 + uses: github/codeql-action/init@f6091c0113d1dcf9b98e269ee48e8a7e51b7bdd4 # pin@v2 with: languages: csharp @@ -49,6 +49,6 @@ jobs: run: dotnet build Sentry-CI-CodeQL.slnf --no-restore --nologo - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@b6a472f63d85b9c78a3ac5e89422239fc15e9b3c # pin@v2 + uses: github/codeql-action/analyze@f6091c0113d1dcf9b98e269ee48e8a7e51b7bdd4 # pin@v2 with: category: '/language:csharp' From a4fc2fe7304ae3aaa79006d507b0c5e8f9b67bfc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 Jan 2025 14:18:23 +1300 Subject: [PATCH 105/363] build(deps): bump codecov/codecov-action from 5.1.2 to 5.3.1 (#3919) Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 5.1.2 to 5.3.1. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/1e68e06f1dbfde0e4cefc87efeba9e4643565303...13ce06bfc6bbe3ecf90edbbf1bc32fe5978ca1d3) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 187de670ef..04bbd7a4a5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -123,7 +123,7 @@ jobs: run: dotnet test Sentry-CI-Build-${{ runner.os }}.slnf -c Release --no-build --nologo -l GitHubActions -l "trx;LogFilePrefix=testresults_${{ runner.os }}" --collect "XPlat Code Coverage" - name: Upload code coverage - uses: codecov/codecov-action@1e68e06f1dbfde0e4cefc87efeba9e4643565303 + uses: codecov/codecov-action@13ce06bfc6bbe3ecf90edbbf1bc32fe5978ca1d3 - name: Upload build and test outputs if: failure() From 81452c164d25925f4e796849f844e46ea9d3adfc Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Tue, 28 Jan 2025 16:46:30 +1300 Subject: [PATCH 106/363] Fixed duplicate SentryMauiEventProcessors (#3905) --- CHANGELOG.md | 4 ++++ .../Internal/SentryMauiOptionsSetup.cs | 2 +- .../SentryMauiAppBuilderExtensions.cs | 3 --- .../SentryMauiAppBuilderExtensionsTests.cs | 19 +++++++++++++++++++ 4 files changed, 24 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9426f0ec6b..ae68fdbfdc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,10 @@ - .NET on iOS: Add experimental EnableAppHangTrackingV2 configuration flag to the options binding SDK ([#3877](https://github.com/getsentry/sentry-dotnet/pull/3877)) - Added `SentryOptions.DisableSentryHttpMessageHandler`. Useful if you're using `OpenTelemetry.Instrumentation.Http` and ending up with duplicate spans. ([#3879](https://github.com/getsentry/sentry-dotnet/pull/3879)) +### Fixes + +- Fixed duplicate SentryMauiEventProcessors ([#3905](https://github.com/getsentry/sentry-dotnet/pull/3905)) + ### Dependencies - Bump Native SDK from v0.7.17 to v0.7.19 ([#3891](https://github.com/getsentry/sentry-dotnet/pull/3891), [#3908](https://github.com/getsentry/sentry-dotnet/pull/3908)) diff --git a/src/Sentry.Maui/Internal/SentryMauiOptionsSetup.cs b/src/Sentry.Maui/Internal/SentryMauiOptionsSetup.cs index d13b9731d8..7436d0720e 100644 --- a/src/Sentry.Maui/Internal/SentryMauiOptionsSetup.cs +++ b/src/Sentry.Maui/Internal/SentryMauiOptionsSetup.cs @@ -15,7 +15,7 @@ internal class SentryMauiOptionsSetup : IConfigureOptions public SentryMauiOptionsSetup(IConfiguration config) { ArgumentNullException.ThrowIfNull(config); - _config = config; + _config = config.GetSection("Sentry"); } public void Configure(SentryMauiOptions options) diff --git a/src/Sentry.Maui/SentryMauiAppBuilderExtensions.cs b/src/Sentry.Maui/SentryMauiAppBuilderExtensions.cs index da2a569926..1cbefb19bf 100644 --- a/src/Sentry.Maui/SentryMauiAppBuilderExtensions.cs +++ b/src/Sentry.Maui/SentryMauiAppBuilderExtensions.cs @@ -44,9 +44,6 @@ public static MauiAppBuilder UseSentry(this MauiAppBuilder builder, { var services = builder.Services; - var section = builder.Configuration.GetSection("Sentry"); - services.AddSingleton>(_ => new SentryMauiOptionsSetup(section)); - if (configureOptions != null) { services.Configure(configureOptions); diff --git a/test/Sentry.Maui.Tests/SentryMauiAppBuilderExtensionsTests.cs b/test/Sentry.Maui.Tests/SentryMauiAppBuilderExtensionsTests.cs index 2bbd4b493e..c3fac5e100 100644 --- a/test/Sentry.Maui.Tests/SentryMauiAppBuilderExtensionsTests.cs +++ b/test/Sentry.Maui.Tests/SentryMauiAppBuilderExtensionsTests.cs @@ -1,5 +1,6 @@ using Microsoft.Extensions.Options; using Sentry.Internal.Http; +using Sentry.Maui.Internal; using MauiConstants = Sentry.Maui.Internal.Constants; namespace Sentry.Maui.Tests; @@ -29,6 +30,24 @@ public Fixture() private readonly Fixture _fixture = new(); + [Fact] + public void UseSentry_RegistersEventProcessorOnlyOnce() + { + // Arrange + var builder = _fixture.Builder; + builder.Services.Configure(options => + { + options.Dsn = ValidDsn; + }); + + // Act + using var app = builder.UseSentry().Build(); + + // Assert + var options = app.Services.GetRequiredService>().Value; + options.EventProcessors.Should().ContainSingle(t => t.Type == typeof(SentryMauiEventProcessor)); + } + [Fact] public void CanUseSentry_WithConfigurationOnly() { From 2f818b2ef1452a2b8e84c179bef171bdfba6c505 Mon Sep 17 00:00:00 2001 From: jeevan wijerathna Date: Fri, 31 Jan 2025 13:53:31 +1300 Subject: [PATCH 107/363] Fixed Invalid index used in Debug logs (#3923) * fix: correct log warning format for LogWarning * Update CHANGELOG.md --------- Co-authored-by: James Crosswell --- CHANGELOG.md | 1 + .../DiagnosticSource/EFCommandDiagnosticSourceHelper.cs | 4 ++-- .../DiagnosticSource/EFConnectionDiagnosticSourceHelper.cs | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ae68fdbfdc..70ffca7d3b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ ### Fixes - Fixed duplicate SentryMauiEventProcessors ([#3905](https://github.com/getsentry/sentry-dotnet/pull/3905)) +- Fixed invalid string.Format index in Debug logs for the DiagnosticSource integration ([#3923](https://github.com/getsentry/sentry-dotnet/pull/3923)) ### Dependencies diff --git a/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/EFCommandDiagnosticSourceHelper.cs b/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/EFCommandDiagnosticSourceHelper.cs index baccd1366d..930c4a0d4a 100644 --- a/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/EFCommandDiagnosticSourceHelper.cs +++ b/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/EFCommandDiagnosticSourceHelper.cs @@ -34,7 +34,7 @@ private static void SetCommandId(ISpan span, Guid? commandId) span.Operation == Operation && TryGetCommandId(span) == commandId); } - Options.LogWarning("No correlation id found for {1}.", Operation); + Options.LogWarning("No correlation id found for {0}.", Operation); return null; } @@ -49,6 +49,6 @@ protected override void SetSpanReference(ISpan span, object? diagnosticSourceVal } return; } - Options.LogWarning("No correlation id can be set for {1}.", Operation); + Options.LogWarning("No correlation id can be set for {0}.", Operation); } } diff --git a/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/EFConnectionDiagnosticSourceHelper.cs b/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/EFConnectionDiagnosticSourceHelper.cs index 6f27286505..ba16b04ce2 100644 --- a/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/EFConnectionDiagnosticSourceHelper.cs +++ b/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/EFConnectionDiagnosticSourceHelper.cs @@ -22,7 +22,7 @@ internal EFConnectionDiagnosticSourceHelper(IHub hub, SentryOptions options) : b span.Operation == Operation && TryGetConnectionId(span) == connectionId); } - Options.LogWarning("No correlation id found for {1}.", Operation); + Options.LogWarning("No correlation id found for {0}.", Operation); return null; } From 44c48f69976df52b5cf15adca2e34f34b4329f66 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Mon, 3 Feb 2025 14:03:04 +1300 Subject: [PATCH 108/363] Prevent Native EXC_BAD_ACCESS signal for NullRefrenceExceptions (#3909) --- CHANGELOG.md | 1 + samples/Sentry.Samples.Ios/AppDelegate.cs | 134 ++++++++++++------ samples/Sentry.Samples.Ios/Info.plist | 76 +++++----- .../Sentry.Samples.Ios.csproj | 6 +- src/Sentry/Platforms/Cocoa/RuntimeAdapter.cs | 29 ++++ ...ntimeMarshalManagedExceptionIntegration.cs | 48 +++++++ src/Sentry/Platforms/Cocoa/SentrySdk.cs | 26 ++-- src/Sentry/SentryOptions.cs | 28 ++++ ...MarshalManagedExceptionIntegrationTests.cs | 70 +++++++++ test/Sentry.Tests/SentryOptionsTests.cs | 22 +++ 10 files changed, 343 insertions(+), 97 deletions(-) create mode 100644 src/Sentry/Platforms/Cocoa/RuntimeAdapter.cs create mode 100644 src/Sentry/Platforms/Cocoa/RuntimeMarshalManagedExceptionIntegration.cs create mode 100644 test/Sentry.Tests/Platforms/iOS/RuntimeMarshalManagedExceptionIntegrationTests.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 70ffca7d3b..abf54b4d10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ ### Fixes +- Prevent Native EXC_BAD_ACCESS signal errors from being captured when managed NullRefrenceExceptions occur ([#3909](https://github.com/getsentry/sentry-dotnet/pull/3909)) - Fixed duplicate SentryMauiEventProcessors ([#3905](https://github.com/getsentry/sentry-dotnet/pull/3905)) - Fixed invalid string.Format index in Debug logs for the DiagnosticSource integration ([#3923](https://github.com/getsentry/sentry-dotnet/pull/3923)) diff --git a/samples/Sentry.Samples.Ios/AppDelegate.cs b/samples/Sentry.Samples.Ios/AppDelegate.cs index 8ba822292f..80e9ea09a5 100644 --- a/samples/Sentry.Samples.Ios/AppDelegate.cs +++ b/samples/Sentry.Samples.Ios/AppDelegate.cs @@ -1,3 +1,5 @@ +using ObjCRuntime; + namespace Sentry.Samples.Ios; [Register("AppDelegate")] @@ -16,6 +18,7 @@ public override bool FinishedLaunching(UIApplication application, NSDictionary l { options.Dsn = "https://eb18e953812b41c3aeb042e666fd3b5c@o447951.ingest.sentry.io/5428537"; options.Debug = true; + options.SampleRate = 1.0F; options.TracesSampleRate = 1.0; options.ProfilesSampleRate = 1.0; @@ -23,77 +26,114 @@ public override bool FinishedLaunching(UIApplication application, NSDictionary l // https://docs.sentry.io/platforms/apple/guides/ios/configuration/ // Enable Native iOS SDK App Hangs detection options.Native.EnableAppHangTracking = true; + + options.CacheDirectoryPath = Path.GetTempPath(); }); // create a new window instance based on the screen size Window = new UIWindow(UIScreen.MainScreen.Bounds); - // determine the background color for the view (SystemBackground requires iOS >= 13.0) + // determine control colours (SystemBackground requires iOS >= 13.0) var backgroundColor = UIDevice.CurrentDevice.CheckSystemVersion(13, 0) #pragma warning disable CA1416 ? UIColor.SystemBackground #pragma warning restore CA1416 : UIColor.White; + var buttonConfig = UIButtonConfiguration.TintedButtonConfiguration; + var terminalButtonConfig = UIButtonConfiguration.TintedButtonConfiguration; + terminalButtonConfig.BaseBackgroundColor = UIColor.SystemRed; - // create a UIViewController with a single UILabel var vc = new UIViewController(); - vc.View!.AddSubview(new UILabel(Window!.Frame) + + var label = new UILabel { BackgroundColor = backgroundColor, TextAlignment = UITextAlignment.Center, Text = "Hello, iOS!", + AutoresizingMask = UIViewAutoresizing.All + }; + + // UIButton for a managed exception that we'll catch and handle (won't crash the app) + var managedCrashButton = new UIButton(UIButtonType.RoundedRect) + { AutoresizingMask = UIViewAutoresizing.All, - }); - Window.RootViewController = vc; + Configuration = buttonConfig + }; + managedCrashButton.SetTitle("Managed Crash", UIControlState.Normal); + managedCrashButton.TouchUpInside += delegate + { + Console.WriteLine("Managed Crash button clicked!"); + try + { + throw new Exception("Catch this!"); + } + catch (Exception e) + { + SentrySdk.CaptureException(e); + } + }; - // make the window visible - Window.MakeKeyAndVisible(); + // UIButton for unhandled managed exception + var unhandledCrashButton = new UIButton(UIButtonType.RoundedRect) + { + AutoresizingMask = UIViewAutoresizing.All, + Configuration = terminalButtonConfig + }; + unhandledCrashButton.SetTitle("Unhandled Crash", UIControlState.Normal); + unhandledCrashButton.TouchUpInside += delegate + { + Console.WriteLine("Unhandled Crash button clicked!"); + string s = null!; + // This will cause a NullReferenceException that will crash the app before Sentry can send the event. + // Since we're using a caching transport though, the exception will be written to disk and sent the + // next time the app is launched. + Console.WriteLine("Length: {0}", s.Length); + }; + // UIButton for native crash + var nativeCrashButton = new UIButton(UIButtonType.System) + { + Configuration = terminalButtonConfig + }; + nativeCrashButton.SetTitle("Native Crash", UIControlState.Normal); + nativeCrashButton.TouchUpInside += delegate + { + Console.WriteLine("Native Crash button clicked!"); +#pragma warning disable CS0618 // Type or member is obsolete + // This will cause a native crash that will crash the application before + // Sentry gets a chance to send the event. Since we've enabled caching however, + // the event will be written to disk and sent the next time the app is launched. + SentrySdk.CauseCrash(CrashType.Native); +#pragma warning restore CS0618 // Type or member is obsolete + }; - // Try out the Sentry SDK - SentrySdk.CaptureMessage("From iOS"); + // create a UIStackView to hold the label and buttons + var stackView = new UIStackView(new UIView[] { label, managedCrashButton, unhandledCrashButton, nativeCrashButton }) + { + Axis = UILayoutConstraintAxis.Vertical, + Distribution = UIStackViewDistribution.FillEqually, + Alignment = UIStackViewAlignment.Center, + Spacing = 10, + TranslatesAutoresizingMaskIntoConstraints = false, + }; - // Uncomment to try these - // throw new Exception("Test Unhandled Managed Exception"); - // SentrySdk.CauseCrash(CrashType.Native); + // add the stack view to the view controller's view + vc.View!.BackgroundColor = backgroundColor; + vc.View.AddSubview(stackView); - { - var tx = SentrySdk.StartTransaction("app", "run"); - var count = 10; - for (var i = 0; i < count; i++) - { - FindPrimeNumber(100000); - } + // set constraints for the stack view + NSLayoutConstraint.ActivateConstraints([ + stackView.CenterXAnchor.ConstraintEqualTo(vc.View.CenterXAnchor), + stackView.CenterYAnchor.ConstraintEqualTo(vc.View.CenterYAnchor), + stackView.WidthAnchor.ConstraintEqualTo(vc.View.WidthAnchor, 0.8f), + stackView.HeightAnchor.ConstraintEqualTo(vc.View.HeightAnchor, 0.5f) + ]); - tx.Finish(); - } + Window.RootViewController = vc; - return true; - } + // make the window visible + Window.MakeKeyAndVisible(); - private static long FindPrimeNumber(int n) - { - int count = 0; - long a = 2; - while (count < n) - { - long b = 2; - int prime = 1;// to check if found a prime - while (b * b <= a) - { - if (a % b == 0) - { - prime = 0; - break; - } - b++; - } - if (prime > 0) - { - count++; - } - a++; - } - return (--a); + return true; } } diff --git a/samples/Sentry.Samples.Ios/Info.plist b/samples/Sentry.Samples.Ios/Info.plist index fb0738075a..38f2e8adc8 100644 --- a/samples/Sentry.Samples.Ios/Info.plist +++ b/samples/Sentry.Samples.Ios/Info.plist @@ -2,43 +2,43 @@ - CFBundleDisplayName - Sentry.Samples.Ios - CFBundleIdentifier - io.sentry.dotnet.samples.ios - CFBundleShortVersionString - 1.0 - MinimumOSVersion - 11.0 - CFBundleVersion - 1.0 - LSRequiresIPhoneOS - - UIDeviceFamily - - 1 - 2 - - UILaunchStoryboardName - LaunchScreen - UIRequiredDeviceCapabilities - - armv7 - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - XSAppIconAssets - Assets.xcassets/AppIcon.appiconset + CFBundleDisplayName + Sentry.Samples.Ios + CFBundleIdentifier + io.sentry.dotnet.samples.ios + CFBundleShortVersionString + 1.0 + MinimumOSVersion + 15.0 + CFBundleVersion + 1.0 + LSRequiresIPhoneOS + + UIDeviceFamily + + 1 + 2 + + UILaunchStoryboardName + LaunchScreen + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + XSAppIconAssets + Assets.xcassets/AppIcon.appiconset diff --git a/samples/Sentry.Samples.Ios/Sentry.Samples.Ios.csproj b/samples/Sentry.Samples.Ios/Sentry.Samples.Ios.csproj index 9ecfce8114..76b122d042 100644 --- a/samples/Sentry.Samples.Ios/Sentry.Samples.Ios.csproj +++ b/samples/Sentry.Samples.Ios/Sentry.Samples.Ios.csproj @@ -1,13 +1,15 @@ - net8.0-ios17.0 + net9.0-ios18.0 Exe enable true - 11.0 + 15.0 true true + + IL3050;IL3053 diff --git a/src/Sentry/Platforms/Cocoa/RuntimeAdapter.cs b/src/Sentry/Platforms/Cocoa/RuntimeAdapter.cs new file mode 100644 index 0000000000..9bc7551cd4 --- /dev/null +++ b/src/Sentry/Platforms/Cocoa/RuntimeAdapter.cs @@ -0,0 +1,29 @@ +using ObjCRuntime; + +namespace Sentry.Cocoa; + +internal interface IRuntime +{ + internal event MarshalManagedExceptionHandler MarshalManagedException; + internal event MarshalObjectiveCExceptionHandler MarshalObjectiveCException; +} + +internal sealed class RuntimeAdapter : IRuntime +{ + public static RuntimeAdapter Instance { get; } = new(); + + private RuntimeAdapter() + { + Runtime.MarshalManagedException += OnMarshalManagedException; + Runtime.MarshalObjectiveCException += OnMarshalObjectiveCException; + } + + public event MarshalManagedExceptionHandler? MarshalManagedException; + public event MarshalObjectiveCExceptionHandler? MarshalObjectiveCException; + + [SecurityCritical] + private void OnMarshalManagedException(object sender, MarshalManagedExceptionEventArgs e) => MarshalManagedException?.Invoke(this, e); + + [SecurityCritical] + private void OnMarshalObjectiveCException(object sender, MarshalObjectiveCExceptionEventArgs e) => MarshalObjectiveCException?.Invoke(this, e); +} diff --git a/src/Sentry/Platforms/Cocoa/RuntimeMarshalManagedExceptionIntegration.cs b/src/Sentry/Platforms/Cocoa/RuntimeMarshalManagedExceptionIntegration.cs new file mode 100644 index 0000000000..396221cb79 --- /dev/null +++ b/src/Sentry/Platforms/Cocoa/RuntimeMarshalManagedExceptionIntegration.cs @@ -0,0 +1,48 @@ +using ObjCRuntime; +using Sentry.Extensibility; +using Sentry.Integrations; + +namespace Sentry.Cocoa; + +/// +/// When AOT Compiling iOS applications, the AppDomain UnhandledExceptionHandler doesn't fire. So instead we intercept +/// the Runtime.RuntimeMarshalManagedException event. +/// +internal class RuntimeMarshalManagedExceptionIntegration : ISdkIntegration +{ + private readonly IRuntime _runtime; + private IHub? _hub; + private SentryOptions? _options; + + internal RuntimeMarshalManagedExceptionIntegration(IRuntime? runtime = null) + => _runtime = runtime ?? RuntimeAdapter.Instance; + + public void Register(IHub hub, SentryOptions options) + { + _hub = hub; + _options = options; + _runtime.MarshalManagedException += Handle; + } + + // Internal for testability + [SecurityCritical] + internal void Handle(object sender, MarshalManagedExceptionEventArgs e) + { + _options?.LogDebug("Runtime Marshal Managed Exception mode {0}", e.ExceptionMode.ToString("G")); + + if (e.Exception is { } ex) + { + ex.SetSentryMechanism( + "Runtime.MarshalManagedException", + "This exception was caught by the .NET Runtime Marshal Managed Exception global error handler. " + + "The application may have crashed as a result of this exception.", + handled: false); + + // Call the internal implementation, so that we still capture even if the hub has been disabled. + _hub?.CaptureExceptionInternal(ex); + + // This is likely a terminal exception so try to send the crash report before shutting down + _hub?.Flush(); + } + } +} diff --git a/src/Sentry/Platforms/Cocoa/SentrySdk.cs b/src/Sentry/Platforms/Cocoa/SentrySdk.cs index c7b438f12d..dcc8e48006 100644 --- a/src/Sentry/Platforms/Cocoa/SentrySdk.cs +++ b/src/Sentry/Platforms/Cocoa/SentrySdk.cs @@ -10,11 +10,6 @@ public static partial class SentrySdk private static void InitSentryCocoaSdk(SentryOptions options) { options.LogDebug("Initializing native SDK"); - // Workaround for https://github.com/xamarin/xamarin-macios/issues/15252 - ObjCRuntime.Runtime.MarshalManagedException += (_, args) => - { - args.ExceptionMode = ObjCRuntime.MarshalManagedExceptionMode.UnwindNativeCode; - }; // Set default release and distribution options.Release ??= GetDefaultReleaseString(); @@ -153,10 +148,6 @@ private static void InitSentryCocoaSdk(SentryOptions options) // 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 nativeOptions.BeforeSend = evt => { // There should only be one exception on the event in this case @@ -164,10 +155,25 @@ private static void InitSentryCocoaSdk(SentryOptions options) { // It will match the following characteristics var ex = evt.Exceptions[0]; + + // Thankfully, sometimes 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 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 + // Don't send it + options.LogDebug("Discarded {0} error ({1}). Captured as managed exception instead.", ex.Type, ex.Value); + return null!; + } + + // Similar workaround for NullReferenceExceptions. We don't have any easy way to know whether the + // exception is managed code (compiled to native) or original native code though. + // See: https://github.com/getsentry/sentry-dotnet/issues/3776 + if (ex.Type == "EXC_BAD_ACCESS") + { + // Don't send it + options.LogDebug("Discarded {0} error ({1}). Captured as managed exception instead.", ex.Type, ex.Value); return null!; } } diff --git a/src/Sentry/SentryOptions.cs b/src/Sentry/SentryOptions.cs index de8d930066..7c99988acf 100644 --- a/src/Sentry/SentryOptions.cs +++ b/src/Sentry/SentryOptions.cs @@ -18,6 +18,11 @@ using Sentry.Android.AssemblyReader; #endif +#if IOS || MACCATALYST +using ObjCRuntime; +using Sentry.Cocoa; +#endif + namespace Sentry; /// @@ -162,10 +167,17 @@ internal IEnumerable Integrations yield return new AutoSessionTrackingIntegration(); } +#if IOS || MACCATALYST + if ((_defaultIntegrations & DefaultIntegrations.RuntimeMarshalManagedExceptionIntegration) != 0) + { + yield return new RuntimeMarshalManagedExceptionIntegration(); + } +#else if ((_defaultIntegrations & DefaultIntegrations.AppDomainUnhandledExceptionIntegration) != 0) { yield return new AppDomainUnhandledExceptionIntegration(); } +#endif if ((_defaultIntegrations & DefaultIntegrations.AppDomainProcessExitIntegration) != 0) { @@ -1239,7 +1251,11 @@ public SentryOptions() _integrations = new(); _defaultIntegrations = DefaultIntegrations.AutoSessionTrackingIntegration | +#if IOS || MACCATALYST + DefaultIntegrations.RuntimeMarshalManagedExceptionIntegration | +#else DefaultIntegrations.AppDomainUnhandledExceptionIntegration | +#endif DefaultIntegrations.AppDomainProcessExitIntegration | DefaultIntegrations.AutoSessionTrackingIntegration | DefaultIntegrations.UnobservedTaskExceptionIntegration @@ -1663,11 +1679,19 @@ public void ApplyDefaultTags(IHasTags hasTags) public void DisableDuplicateEventDetection() => RemoveEventProcessor(); +#if IOS || MACCATALYST + /// + /// Disables the capture of errors through . + /// + public void DisableRuntimeMarshalManagedExceptionCapture() => + RemoveDefaultIntegration(DefaultIntegrations.RuntimeMarshalManagedExceptionIntegration); +#else /// /// Disables the capture of errors through . /// public void DisableAppDomainUnhandledExceptionCapture() => RemoveDefaultIntegration(DefaultIntegrations.AppDomainUnhandledExceptionIntegration); +#endif #if HAS_DIAGNOSTIC_INTEGRATION /// @@ -1725,7 +1749,11 @@ public void DisableSystemDiagnosticsMetricsIntegration() internal enum DefaultIntegrations { AutoSessionTrackingIntegration = 1 << 0, +#if IOS || MACCATALYST + RuntimeMarshalManagedExceptionIntegration = 1 << 1, +#else AppDomainUnhandledExceptionIntegration = 1 << 1, +#endif AppDomainProcessExitIntegration = 1 << 2, UnobservedTaskExceptionIntegration = 1 << 3, #if NETFRAMEWORK diff --git a/test/Sentry.Tests/Platforms/iOS/RuntimeMarshalManagedExceptionIntegrationTests.cs b/test/Sentry.Tests/Platforms/iOS/RuntimeMarshalManagedExceptionIntegrationTests.cs new file mode 100644 index 0000000000..5ba83b0c28 --- /dev/null +++ b/test/Sentry.Tests/Platforms/iOS/RuntimeMarshalManagedExceptionIntegrationTests.cs @@ -0,0 +1,70 @@ +#if IOS +using ObjCRuntime; +using Sentry.Cocoa; + +namespace Sentry.Tests.Platforms.iOS; + +public class RuntimeMarshalManagedExceptionIntegrationTests +{ + private class Fixture + { + public IHub Hub { get; } = Substitute.For(); + public IRuntime Runtime { get; } = Substitute.For(); + + public RuntimeMarshalManagedExceptionIntegration GetSut() => new(Runtime); + } + + private readonly Fixture _fixture = new(); + private SentryOptions SentryOptions { get; } = new(); + + [Fact] + public void Handle_WithException_CaptureEvent() + { + var sut = _fixture.GetSut(); + sut.Register(_fixture.Hub, SentryOptions); + + sut.Handle(this, new MarshalManagedExceptionEventArgs { Exception = new Exception() }); + + _fixture.Hub.Received(1).CaptureEvent(Arg.Any()); + } + + [Fact] + public void Handle_WithException_IsHandledFalse() + { + var sut = _fixture.GetSut(); + sut.Register(_fixture.Hub, SentryOptions); + + var exception = new Exception(); + sut.Handle(this, new MarshalManagedExceptionEventArgs { Exception = exception }); + Assert.Equal(false, exception.Data[Mechanism.HandledKey]); + Assert.True(exception.Data.Contains(Mechanism.MechanismKey)); + + var stackTraceFactory = Substitute.For(); + var exceptionProcessor = new MainExceptionProcessor(SentryOptions, () => stackTraceFactory); + var @event = new SentryEvent(exception); + + exceptionProcessor.Process(exception, @event); + Assert.NotNull(@event.SentryExceptions?.ToList().Single(p => p.Mechanism?.Handled == false)); + } + + [Fact] + public void Handle_NoException_NoCaptureEvent() + { + var sut = _fixture.GetSut(); + sut.Register(_fixture.Hub, SentryOptions); + + sut.Handle(this, new MarshalManagedExceptionEventArgs()); + + _fixture.Hub.DidNotReceive().CaptureEvent(Arg.Any()); + } + + [Fact] + public void Register_UnhandledException_Subscribes() + { + var sut = _fixture.GetSut(); + sut.Register(_fixture.Hub, SentryOptions); + + _fixture.Runtime.Received().MarshalManagedException += sut.Handle; + } +} +#endif diff --git a/test/Sentry.Tests/SentryOptionsTests.cs b/test/Sentry.Tests/SentryOptionsTests.cs index cd20a5c7d3..7a12849443 100644 --- a/test/Sentry.Tests/SentryOptionsTests.cs +++ b/test/Sentry.Tests/SentryOptionsTests.cs @@ -1,6 +1,8 @@ namespace Sentry.Tests; #if NETFRAMEWORK using Sentry.PlatformAbstractions; +#elif IOS || MACCATALYST +using Sentry.Cocoa; #endif public partial class SentryOptionsTests { @@ -285,6 +287,16 @@ public void DisableNetFxInstallationsEventProcessor_RemovesDisableNetFxInstallat } #endif +#if IOS || MACCATALYST + [Fact] + public void DisableRuntimeMarshalManagedExceptionCapture_RemovesRuntimeMarshalManagedExceptionIntegration() + { + var sut = new SentryOptions(); + sut.DisableRuntimeMarshalManagedExceptionCapture(); + Assert.DoesNotContain(sut.Integrations, + p => p is RuntimeMarshalManagedExceptionIntegration); + } +#else [Fact] public void DisableAppDomainUnhandledExceptionCapture_RemovesAppDomainUnhandledExceptionIntegration() { @@ -293,6 +305,7 @@ public void DisableAppDomainUnhandledExceptionCapture_RemovesAppDomainUnhandledE Assert.DoesNotContain(sut.Integrations, p => p is AppDomainUnhandledExceptionIntegration); } +#endif [Fact] public void DisableTaskUnobservedTaskExceptionCapture_UnobservedTaskExceptionIntegration() @@ -581,12 +594,21 @@ public void GetAllEventProcessors_NoAdding_FirstReturned_DuplicateDetectionProce _ = Assert.IsType(sut.GetAllEventProcessors().First()); } +#if IOS || MACCATALYST + [Fact] + public void Integrations_Includes_RuntimeMarshalManagedExceptionIntegration() + { + var sut = new SentryOptions(); + Assert.Contains(sut.Integrations, i => i.GetType() == typeof(RuntimeMarshalManagedExceptionIntegration)); + } +#else [Fact] public void Integrations_Includes_AppDomainUnhandledExceptionIntegration() { var sut = new SentryOptions(); Assert.Contains(sut.Integrations, i => i.GetType() == typeof(AppDomainUnhandledExceptionIntegration)); } +#endif [Fact] public void Integrations_Includes_AppDomainProcessExitIntegration() From 7faf221b6fbf3c293052c30429b38b75564380c4 Mon Sep 17 00:00:00 2001 From: getsentry-bot Date: Mon, 3 Feb 2025 02:45:01 +0000 Subject: [PATCH 109/363] release: 5.1.0 --- CHANGELOG.md | 2 +- Directory.Build.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index abf54b4d10..faf55bd577 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## Unreleased +## 5.1.0 ### Significant change in behavior diff --git a/Directory.Build.props b/Directory.Build.props index 4cc66fb411..485fbcc7a4 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,7 +1,7 @@ - 5.0.1 + 5.1.0 13 true true From 3f450c204963a442a66a089d2242d0853ee8fedc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Feb 2025 23:12:01 +1300 Subject: [PATCH 110/363] build(deps): bump github/codeql-action from 3.28.5 to 3.28.8 (#3934) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.28.5 to 3.28.8. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/f6091c0113d1dcf9b98e269ee48e8a7e51b7bdd4...dd746615b3b9d728a6a37ca2045b68ca76d4841a) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 843fc886c4..0547bafd88 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -35,7 +35,7 @@ jobs: uses: ./.github/actions/environment - name: Initialize CodeQL - uses: github/codeql-action/init@f6091c0113d1dcf9b98e269ee48e8a7e51b7bdd4 # pin@v2 + uses: github/codeql-action/init@dd746615b3b9d728a6a37ca2045b68ca76d4841a # pin@v2 with: languages: csharp @@ -49,6 +49,6 @@ jobs: run: dotnet build Sentry-CI-CodeQL.slnf --no-restore --nologo - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@f6091c0113d1dcf9b98e269ee48e8a7e51b7bdd4 # pin@v2 + uses: github/codeql-action/analyze@dd746615b3b9d728a6a37ca2045b68ca76d4841a # pin@v2 with: category: '/language:csharp' From 4901e2fd92d23ed7fbc1b9ad250bf3b16eda6753 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Feb 2025 23:12:27 +1300 Subject: [PATCH 111/363] build(deps): bump actions/create-github-app-token from 1.11.1 to 1.11.2 (#3932) Bumps [actions/create-github-app-token](https://github.com/actions/create-github-app-token) from 1.11.1 to 1.11.2. - [Release notes](https://github.com/actions/create-github-app-token/releases) - [Commits](https://github.com/actions/create-github-app-token/compare/c1a285145b9d317df6ced56c09f525b5c2b6f755...136412a57a7081aa63c935a2cc2918f76c34f514) --- updated-dependencies: - dependency-name: actions/create-github-app-token dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9117f5f517..9058afdcd7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -20,7 +20,7 @@ jobs: steps: - name: Get auth token id: token - uses: actions/create-github-app-token@c1a285145b9d317df6ced56c09f525b5c2b6f755 # v1.11.1 + uses: actions/create-github-app-token@136412a57a7081aa63c935a2cc2918f76c34f514 # v1.11.2 with: app-id: ${{ vars.SENTRY_RELEASE_BOT_CLIENT_ID }} private-key: ${{ secrets.SENTRY_RELEASE_BOT_PRIVATE_KEY }} From bcfde368a451144b1955a2acc9e3add7fb9e2b05 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 3 Feb 2025 23:13:35 +1300 Subject: [PATCH 112/363] chore: update modules/sentry-native to 0.7.20 (#3929) Co-authored-by: GitHub --- CHANGELOG.md | 6 +++--- modules/sentry-native | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index faf55bd577..4fec059921 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,9 +19,9 @@ ### Dependencies -- Bump Native SDK from v0.7.17 to v0.7.19 ([#3891](https://github.com/getsentry/sentry-dotnet/pull/3891), [#3908](https://github.com/getsentry/sentry-dotnet/pull/3908)) - - [changelog](https://github.com/getsentry/sentry-native/blob/master/CHANGELOG.md#0719) - - [diff](https://github.com/getsentry/sentry-native/compare/0.7.17...0.7.19) +- Bump Native SDK from v0.7.17 to v0.7.20 ([#3891](https://github.com/getsentry/sentry-dotnet/pull/3891), [#3908](https://github.com/getsentry/sentry-dotnet/pull/3908), [#3929](https://github.com/getsentry/sentry-dotnet/pull/3929)) + - [changelog](https://github.com/getsentry/sentry-native/blob/master/CHANGELOG.md#0720) + - [diff](https://github.com/getsentry/sentry-native/compare/0.7.17...0.7.20) - Bump Java SDK from v7.20.0 to v7.20.1 ([#3907](https://github.com/getsentry/sentry-dotnet/pull/3907)) - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#7201) - [diff](https://github.com/getsentry/sentry-java/compare/7.20.0...7.20.1) diff --git a/modules/sentry-native b/modules/sentry-native index 62b966c487..69569b5eca 160000 --- a/modules/sentry-native +++ b/modules/sentry-native @@ -1 +1 @@ -Subproject commit 62b966c487f08773dc23cfb17b991bdd3f170ae4 +Subproject commit 69569b5eca87b5c2afd1ce5a0df7b01ded85023b From 9b29d1f7b6fe1f2638d344c5cbcb60281e16739c Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Wed, 5 Feb 2025 10:09:10 +1300 Subject: [PATCH 113/363] Filtered OTel activities should not be sent to Sentry (#3890) --- CHANGELOG.md | 6 ++ .../SentrySpanProcessor.cs | 8 ++ .../SentrySpanProcessorTests.cs | 82 +++++++++++++++++++ 3 files changed, 96 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4fec059921..af09754d95 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +### Fixes + +- OTel activities that are marked as not recorded are no longer sent to Sentry ([#3890](https://github.com/getsentry/sentry-dotnet/pull/3890)) + ## 5.1.0 ### Significant change in behavior diff --git a/src/Sentry.OpenTelemetry/SentrySpanProcessor.cs b/src/Sentry.OpenTelemetry/SentrySpanProcessor.cs index 09172c0636..39230143a8 100644 --- a/src/Sentry.OpenTelemetry/SentrySpanProcessor.cs +++ b/src/Sentry.OpenTelemetry/SentrySpanProcessor.cs @@ -181,6 +181,14 @@ public override void OnEnd(Activity data) return; } + // Skip any activities that are not recorded. + if (data is { Recorded: false }) + { + _options?.DiagnosticLogger?.LogDebug("Ignoring unrecorded Activity {0}.", data.SpanId); + _map.TryRemove(data.SpanId, out _); + return; + } + // Make a dictionary of the attributes (aka "tags") for faster lookup when used throughout the processor. var attributes = data.TagObjects.ToDict(); diff --git a/test/Sentry.OpenTelemetry.Tests/SentrySpanProcessorTests.cs b/test/Sentry.OpenTelemetry.Tests/SentrySpanProcessorTests.cs index 92839b2f56..1b199c9fc4 100644 --- a/test/Sentry.OpenTelemetry.Tests/SentrySpanProcessorTests.cs +++ b/test/Sentry.OpenTelemetry.Tests/SentrySpanProcessorTests.cs @@ -459,6 +459,88 @@ public void OnEnd_FinishesTransaction() } } + [Fact] + public void OnEnd_FilteredTransaction_DoesNotFinishTransaction() + { + // Arrange + _fixture.Options.Instrumenter = Instrumenter.OpenTelemetry; + var sut = _fixture.GetSut(); + + var parent = Tracer.StartActivity("transaction")!; + sut.OnStart(parent); + + var data = Tracer.StartActivity("test operation", kind: ActivityKind.Internal)!; + data.DisplayName = "test display name"; + sut.OnStart(data); + + FilterActivity(parent); + + sut._map.TryGetValue(parent.SpanId, out var span); + + // Act + sut.OnEnd(data); + sut.OnEnd(parent); + + // Assert + if (span is not TransactionTracer transaction) + { + Assert.Fail("Span is not a transaction tracer"); + return; + } + + using (new AssertionScope()) + { + transaction.EndTimestamp.Should().BeNull(); + transaction.Status.Should().BeNull(); + } + } + + [Fact] + public void OnEnd_FilteredSpan_RemovesSpan() + { + // Arrange + _fixture.Options.Instrumenter = Instrumenter.OpenTelemetry; + var sut = _fixture.GetSut(); + + var parent = Tracer.StartActivity("transaction")!; + sut.OnStart(parent); + + var data = Tracer.StartActivity("test operation", kind: ActivityKind.Internal)!; + data.DisplayName = "test display name"; + sut.OnStart(data); + + FilterActivity(data); + + sut._map.TryGetValue(parent.SpanId, out var parentSpan); + sut._map.TryGetValue(data.SpanId, out var childSpan); + + // Act + sut.OnEnd(data); + sut.OnEnd(parent); + + // Assert + if (parentSpan is not TransactionTracer transaction) + { + Assert.Fail("parentSpan is not a transaction tracer"); + return; + } + if (childSpan is not SpanTracer span) + { + Assert.Fail("span is not a span tracer"); + return; + } + + using (new AssertionScope()) + { + span.EndTimestamp.Should().BeNull(); + span.Status.Should().BeNull(); + + transaction.EndTimestamp.Should().NotBeNull(); + transaction.Status.Should().Be(SpanStatus.Ok); + transaction.Spans.Should().BeEmpty(); + } + } + [Theory] [InlineData(OtelSemanticConventions.AttributeUrlFull)] [InlineData(OtelSemanticConventions.AttributeHttpUrl)] From 04ba5e3f42b61afc2cecb914a0863ba4c988033d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 6 Feb 2025 13:13:02 +1300 Subject: [PATCH 114/363] build(deps): bump gradle/actions from 4.2.2 to 4.3.0 (#3933) Bumps [gradle/actions](https://github.com/gradle/actions) from 4.2.2 to 4.3.0. - [Release notes](https://github.com/gradle/actions/releases) - [Commits](https://github.com/gradle/actions/compare/0bdd871935719febd78681f197cd39af5b6e16a6...94baf225fe0a508e581a564467443d0e2379123b) --- updated-dependencies: - dependency-name: gradle/actions dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/device-tests-android.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/device-tests-android.yml b/.github/workflows/device-tests-android.yml index 16e4070b0c..64749e1b8f 100644 --- a/.github/workflows/device-tests-android.yml +++ b/.github/workflows/device-tests-android.yml @@ -74,7 +74,7 @@ jobs: path: bin - name: Setup Gradle - uses: gradle/actions/setup-gradle@0bdd871935719febd78681f197cd39af5b6e16a6 # pin@v3 + uses: gradle/actions/setup-gradle@94baf225fe0a508e581a564467443d0e2379123b # pin@v3 # Cached AVD setup per https://github.com/ReactiveCircus/android-emulator-runner/blob/main/README.md From bea672ac57a1f5379f5b893d53a936492e2c80c3 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos <6349682+vaind@users.noreply.github.com> Date: Fri, 7 Feb 2025 07:16:46 +0100 Subject: [PATCH 115/363] chore: profiling improvements (#3941) * feat: deduplicate profile frames * lint: prefer TryGetValue * chore: update Microsoft.Diagnostics.NETCore.Client * chore: changelog * chore: fix changelog --- CHANGELOG.md | 1 + src/Sentry.Profiling/SampleProfileBuilder.cs | 118 +++++++------ src/Sentry.Profiling/Sentry.Profiling.csproj | 2 +- ...ofileInfo_Serialization_Works.verified.txt | 162 ++++++------------ ...s.Profile_Serialization_Works.verified.txt | 162 ++++++------------ .../TraceLogProcessorTests.verify.cs | 16 +- 6 files changed, 197 insertions(+), 264 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index af09754d95..7433c719c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Fixes - OTel activities that are marked as not recorded are no longer sent to Sentry ([#3890](https://github.com/getsentry/sentry-dotnet/pull/3890)) +- Deduplicate profiling stack frames ([#3941](https://github.com/getsentry/sentry-dotnet/pull/3941)) ## 5.1.0 diff --git a/src/Sentry.Profiling/SampleProfileBuilder.cs b/src/Sentry.Profiling/SampleProfileBuilder.cs index bdef768d85..56927be4a9 100644 --- a/src/Sentry.Profiling/SampleProfileBuilder.cs +++ b/src/Sentry.Profiling/SampleProfileBuilder.cs @@ -16,8 +16,12 @@ internal class SampleProfileBuilder // Output profile being built. public readonly SampleProfile Profile = new(); - // A sparse array that maps from StackSourceFrameIndex to an index in the output Profile.frames. - private readonly Dictionary _frameIndexes = new(); + // A sparse array that maps from CodeAddressIndex to an index in the output Profile.frames. + private readonly Dictionary _frameIndexesByCodeAddressIndex = new(); + + // A sparse array that maps from MethodIndex to an index in the output Profile.frames. + // This deduplicates frames that map to the same method but have a different CodeAddressIndex. + private readonly Dictionary _frameIndexesByMethodIndex = new(); // A dictionary from a CallStackIndex to an index in the output Profile.stacks. private readonly Dictionary _stackIndexes = new(); @@ -85,13 +89,14 @@ private int AddStackTrace(CallStackIndex callstackIndex) { var key = (int)callstackIndex; - if (!_stackIndexes.ContainsKey(key)) + if (!_stackIndexes.TryGetValue(key, out var value)) { Profile.Stacks.Add(CreateStackTrace(callstackIndex)); - _stackIndexes[key] = Profile.Stacks.Count - 1; + value = Profile.Stacks.Count - 1; + _stackIndexes[key] = value; } - return _stackIndexes[key]; + return value; } private Internal.GrowableArray CreateStackTrace(CallStackIndex callstackIndex) @@ -116,21 +121,71 @@ private Internal.GrowableArray CreateStackTrace(CallStackIndex callstackInd return stackTrace; } + private int PushNewFrame(SentryStackFrame frame) + { + Profile.Frames.Add(frame); + return Profile.Frames.Count - 1; + } + /// /// Check if the frame is already stored in the output Profile, or adds it. /// /// The index to the output Profile frames array. private int AddStackFrame(CodeAddressIndex codeAddressIndex) { - var key = (int)codeAddressIndex; + if (_frameIndexesByCodeAddressIndex.TryGetValue((int)codeAddressIndex, out var value)) + { + return value; + } - if (!_frameIndexes.ContainsKey(key)) + var methodIndex = _traceLog.CodeAddresses.MethodIndex(codeAddressIndex); + if (methodIndex != MethodIndex.Invalid) + { + value = AddStackFrame(methodIndex); + _frameIndexesByCodeAddressIndex[(int)codeAddressIndex] = value; + return value; + } + + // Fall back if the method info is unknown, see more info on Symbol resolution in + // https://github.com/getsentry/perfview/blob/031250ffb4f9fcadb9263525d6c9f274be19ca51/src/PerfView/SupportFiles/UsersGuide.htm#L7745-L7784 + if (_traceLog.CodeAddresses[codeAddressIndex] is { } codeAddressInfo) { - Profile.Frames.Add(CreateStackFrame(codeAddressIndex)); - _frameIndexes[key] = Profile.Frames.Count - 1; + var frame = new SentryStackFrame + { + InstructionAddress = (long?)codeAddressInfo.Address, + Module = codeAddressInfo.ModuleFile?.Name, + }; + frame.ConfigureAppFrame(_options); + + return _frameIndexesByCodeAddressIndex[(int)codeAddressIndex] = PushNewFrame(frame); } - return _frameIndexes[key]; + // If all else fails, it's a completely unknown frame. + // TODO check this - maybe we would be able to resolve it later in the future? + return PushNewFrame(new SentryStackFrame { InApp = false }); + } + + /// + /// Check if the frame is already stored in the output Profile, or adds it. + /// + /// The index to the output Profile frames array. + private int AddStackFrame(MethodIndex methodIndex) + { + if (_frameIndexesByMethodIndex.TryGetValue((int)methodIndex, out var value)) + { + return value; + } + + var method = _traceLog.CodeAddresses.Methods[methodIndex]; + + var frame = new SentryStackFrame + { + Function = method.FullMethodName, + Module = method.MethodModuleFile?.Name + }; + frame.ConfigureAppFrame(_options); + + return _frameIndexesByMethodIndex[(int)methodIndex] = PushNewFrame(frame); } /// @@ -141,52 +196,17 @@ private int AddThread(TraceThread thread) { var key = (int)thread.ThreadIndex; - if (!_threadIndexes.ContainsKey(key)) + if (!_threadIndexes.TryGetValue(key, out var value)) { Profile.Threads.Add(new() { Name = thread.ThreadInfo ?? $"Thread {thread.ThreadID}", }); - _threadIndexes[key] = Profile.Threads.Count - 1; + value = Profile.Threads.Count - 1; + _threadIndexes[key] = value; _downsampler.NewThreadAdded(_threadIndexes[key]); } - return _threadIndexes[key]; - } - - private SentryStackFrame CreateStackFrame(CodeAddressIndex codeAddressIndex) - { - var frame = new SentryStackFrame(); - - var methodIndex = _traceLog.CodeAddresses.MethodIndex(codeAddressIndex); - if (_traceLog.CodeAddresses.Methods[methodIndex] is { } method) - { - frame.Function = method.FullMethodName; - - if (method.MethodModuleFile is { } moduleFile) - { - frame.Module = moduleFile.Name; - } - - frame.ConfigureAppFrame(_options); - } - else - { - // Fall back if the method info is unknown, see more info on Symbol resolution in - // https://github.com/getsentry/perfview/blob/031250ffb4f9fcadb9263525d6c9f274be19ca51/src/PerfView/SupportFiles/UsersGuide.htm#L7745-L7784 - frame.InstructionAddress = (long?)_traceLog.CodeAddresses.Address(codeAddressIndex); - - if (_traceLog.CodeAddresses.ModuleFile(codeAddressIndex) is { } moduleFile) - { - frame.Module = moduleFile.Name; - frame.ConfigureAppFrame(_options); - } - else - { - frame.InApp = false; - } - } - - return frame; + return value; } } diff --git a/src/Sentry.Profiling/Sentry.Profiling.csproj b/src/Sentry.Profiling/Sentry.Profiling.csproj index c453876242..9413ef51f0 100644 --- a/src/Sentry.Profiling/Sentry.Profiling.csproj +++ b/src/Sentry.Profiling/Sentry.Profiling.csproj @@ -14,7 +14,7 @@ - + diff --git a/test/Sentry.Profiling.Tests/TraceLogProcessorTests.ProfileInfo_Serialization_Works.verified.txt b/test/Sentry.Profiling.Tests/TraceLogProcessorTests.ProfileInfo_Serialization_Works.verified.txt index 714859c86d..481fcf2e42 100644 --- a/test/Sentry.Profiling.Tests/TraceLogProcessorTests.ProfileInfo_Serialization_Works.verified.txt +++ b/test/Sentry.Profiling.Tests/TraceLogProcessorTests.ProfileInfo_Serialization_Works.verified.txt @@ -107,11 +107,12 @@ 36 ], [ - 37 + 17 ], [ 0, 1, + 37, 38, 39, 40, @@ -119,35 +120,37 @@ 42, 43, 44, - 45, - 46, + 21, 22 ], [ + 45, + 46, 47, 48, 49, 50, - 51, - 52, - 37 + 17 ], [ + 51, + 52, 53, 54, 55, - 56, - 57, - 37 + 17 ], [ + 56, + 57, 58, 59, - 60, - 61, - 62 + 17 ], [ + 60, + 61, + 62, 63, 64, 65, @@ -155,48 +158,55 @@ 67, 68, 69, + 56, + 57, + 58, + 59, + 17 + ], + [ 70, 71, 72, + 73, + 74, + 69, + 56, + 57, 58, 59, - 60, - 61, - 62 + 17 ], [ - 73, - 74, 75, - 76, - 77, - 78, + 74, + 69, + 56, + 57, 58, 59, - 60, - 61, - 62 + 17 ], [ - 79, - 80, - 78, + 56, + 57, 58, 59, - 60, - 61, - 62 + 17 ], [ + 76, + 77, + 78, + 79, + 80, 81, + 82, + 83, 59, - 60, - 61, - 62 + 17 ], [ - 82, - 83, 84, 85, 86, @@ -204,31 +214,22 @@ 88, 89, 90, - 62 - ], - [ 91, 92, - 93, - 94, - 95, - 96, - 97, - 98, - 99, - 100, - 84, - 85, - 86, - 87, - 88, - 89, - 90, - 62 + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 59, + 17 ], [ 0, 1, + 37, 38, 39, 40, @@ -236,15 +237,14 @@ 42, 43, 44, - 45, - 46, + 21, 22 ], [ 18, 19, 20, - 101, + 21, 22 ] ], @@ -340,7 +340,6 @@ in_app: true }, { - in_app: false, instruction_addr: 0x7ff947fd8378 }, { @@ -433,11 +432,6 @@ module: System.Private.CoreLib.il, in_app: false }, - { - function: Aura.UI.Gallery.NetCore.Program.Main(class System.String[]), - module: Aura.UI.Gallery.NetCore, - in_app: true - }, { function: System.IO.Stream+<>c.b__40_0(class System.Object), module: System.Private.CoreLib.il, @@ -478,11 +472,6 @@ module: System.Private.CoreLib.il, in_app: false }, - { - function: System.Threading.PortableThreadPool+WorkerThread.WorkerThreadStart(), - module: System.Private.CoreLib.il, - in_app: false - }, { function: System.Collections.Concurrent.ConcurrentDictionary`2[System.__Canon,System.IntPtr].TryAddInternal(!0,value class System.Nullable`1,!1,bool,bool,!1&), module: System.Collections.Concurrent.il, @@ -509,7 +498,6 @@ in_app: true }, { - in_app: false, instruction_addr: 0x7ff94be7aed3 }, { @@ -557,11 +545,6 @@ module: Avalonia.Controls, in_app: true }, - { - function: Aura.UI.Gallery.NetCore.Program.Main(class System.String[]), - module: Aura.UI.Gallery.NetCore, - in_app: true - }, { function: Avalonia.Animation.Animation..cctor(), module: Avalonia.Animation, @@ -637,26 +620,11 @@ module: Avalonia.Controls, in_app: true }, - { - function: Avalonia.Controls.Primitives.TemplatedControl..cctor(), - module: Avalonia.Controls, - in_app: true - }, { function: System.Reactive.Linq.Observable.Merge(class System.IObservable`1[]), module: system.reactive, in_app: false }, - { - function: Avalonia.Controls.TextBlock..cctor(), - module: Avalonia.Controls, - in_app: true - }, - { - function: Avalonia.Controls.Window..cctor(), - module: Avalonia.Controls, - in_app: true - }, { function: Avalonia.Win32.Win32Platform.SetDpiAwareness(), module: avalonia.win32, @@ -697,11 +665,6 @@ module: Avalonia.Controls, in_app: true }, - { - function: Avalonia.ClassicDesktopStyleApplicationLifetimeExtensions.StartWithClassicDesktopLifetime(!!0,class System.String[],value class Avalonia.Controls.ShutdownMode), - module: Avalonia.Controls, - in_app: true - }, { function: System.Drawing.SafeNativeMethods+Gdip..cctor(), module: system.drawing.common, @@ -738,23 +701,12 @@ in_app: true }, { - in_app: false, instruction_addr: 0x7ffa0d468281 }, { function: Avalonia.Win32.Win32Platform.CreateMessageWindow(), module: avalonia.win32, in_app: true - }, - { - function: Avalonia.Win32.Win32Platform..ctor(), - module: avalonia.win32, - in_app: true - }, - { - function: System.Threading.PortableThreadPool+WorkerThread.WorkerThreadStart(), - module: System.Private.CoreLib.il, - in_app: false } ], samples: [ @@ -1070,4 +1022,4 @@ } ] } -} \ No newline at end of file +} diff --git a/test/Sentry.Profiling.Tests/TraceLogProcessorTests.Profile_Serialization_Works.verified.txt b/test/Sentry.Profiling.Tests/TraceLogProcessorTests.Profile_Serialization_Works.verified.txt index 6eb35f3c22..7720ab6755 100644 --- a/test/Sentry.Profiling.Tests/TraceLogProcessorTests.Profile_Serialization_Works.verified.txt +++ b/test/Sentry.Profiling.Tests/TraceLogProcessorTests.Profile_Serialization_Works.verified.txt @@ -84,11 +84,12 @@ 36 ], [ - 37 + 17 ], [ 0, 1, + 37, 38, 39, 40, @@ -96,35 +97,37 @@ 42, 43, 44, - 45, - 46, + 21, 22 ], [ + 45, + 46, 47, 48, 49, 50, - 51, - 52, - 37 + 17 ], [ + 51, + 52, 53, 54, 55, - 56, - 57, - 37 + 17 ], [ + 56, + 57, 58, 59, - 60, - 61, - 62 + 17 ], [ + 60, + 61, + 62, 63, 64, 65, @@ -132,48 +135,55 @@ 67, 68, 69, + 56, + 57, + 58, + 59, + 17 + ], + [ 70, 71, 72, + 73, + 74, + 69, + 56, + 57, 58, 59, - 60, - 61, - 62 + 17 ], [ - 73, - 74, 75, - 76, - 77, - 78, + 74, + 69, + 56, + 57, 58, 59, - 60, - 61, - 62 + 17 ], [ - 79, - 80, - 78, + 56, + 57, 58, 59, - 60, - 61, - 62 + 17 ], [ + 76, + 77, + 78, + 79, + 80, 81, + 82, + 83, 59, - 60, - 61, - 62 + 17 ], [ - 82, - 83, 84, 85, 86, @@ -181,31 +191,22 @@ 88, 89, 90, - 62 - ], - [ 91, 92, - 93, - 94, - 95, - 96, - 97, - 98, - 99, - 100, - 84, - 85, - 86, - 87, - 88, - 89, - 90, - 62 + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 59, + 17 ], [ 0, 1, + 37, 38, 39, 40, @@ -213,15 +214,14 @@ 42, 43, 44, - 45, - 46, + 21, 22 ], [ 18, 19, 20, - 101, + 21, 22 ] ], @@ -317,7 +317,6 @@ in_app: true }, { - in_app: false, instruction_addr: 0x7ff947fd8378 }, { @@ -410,11 +409,6 @@ module: System.Private.CoreLib.il, in_app: false }, - { - function: Aura.UI.Gallery.NetCore.Program.Main(class System.String[]), - module: Aura.UI.Gallery.NetCore, - in_app: true - }, { function: System.IO.Stream+<>c.b__40_0(class System.Object), module: System.Private.CoreLib.il, @@ -455,11 +449,6 @@ module: System.Private.CoreLib.il, in_app: false }, - { - function: System.Threading.PortableThreadPool+WorkerThread.WorkerThreadStart(), - module: System.Private.CoreLib.il, - in_app: false - }, { function: System.Collections.Concurrent.ConcurrentDictionary`2[System.__Canon,System.IntPtr].TryAddInternal(!0,value class System.Nullable`1,!1,bool,bool,!1&), module: System.Collections.Concurrent.il, @@ -486,7 +475,6 @@ in_app: true }, { - in_app: false, instruction_addr: 0x7ff94be7aed3 }, { @@ -534,11 +522,6 @@ module: Avalonia.Controls, in_app: true }, - { - function: Aura.UI.Gallery.NetCore.Program.Main(class System.String[]), - module: Aura.UI.Gallery.NetCore, - in_app: true - }, { function: Avalonia.Animation.Animation..cctor(), module: Avalonia.Animation, @@ -614,26 +597,11 @@ module: Avalonia.Controls, in_app: true }, - { - function: Avalonia.Controls.Primitives.TemplatedControl..cctor(), - module: Avalonia.Controls, - in_app: true - }, { function: System.Reactive.Linq.Observable.Merge(class System.IObservable`1[]), module: system.reactive, in_app: false }, - { - function: Avalonia.Controls.TextBlock..cctor(), - module: Avalonia.Controls, - in_app: true - }, - { - function: Avalonia.Controls.Window..cctor(), - module: Avalonia.Controls, - in_app: true - }, { function: Avalonia.Win32.Win32Platform.SetDpiAwareness(), module: avalonia.win32, @@ -674,11 +642,6 @@ module: Avalonia.Controls, in_app: true }, - { - function: Avalonia.ClassicDesktopStyleApplicationLifetimeExtensions.StartWithClassicDesktopLifetime(!!0,class System.String[],value class Avalonia.Controls.ShutdownMode), - module: Avalonia.Controls, - in_app: true - }, { function: System.Drawing.SafeNativeMethods+Gdip..cctor(), module: system.drawing.common, @@ -715,23 +678,12 @@ in_app: true }, { - in_app: false, instruction_addr: 0x7ffa0d468281 }, { function: Avalonia.Win32.Win32Platform.CreateMessageWindow(), module: avalonia.win32, in_app: true - }, - { - function: Avalonia.Win32.Win32Platform..ctor(), - module: avalonia.win32, - in_app: true - }, - { - function: System.Threading.PortableThreadPool+WorkerThread.WorkerThreadStart(), - module: System.Private.CoreLib.il, - in_app: false } ], samples: [ @@ -1046,4 +998,4 @@ stack_id: 18 } ] -} \ No newline at end of file +} diff --git a/test/Sentry.Profiling.Tests/TraceLogProcessorTests.verify.cs b/test/Sentry.Profiling.Tests/TraceLogProcessorTests.verify.cs index 5b4307e951..7a01150aec 100644 --- a/test/Sentry.Profiling.Tests/TraceLogProcessorTests.verify.cs +++ b/test/Sentry.Profiling.Tests/TraceLogProcessorTests.verify.cs @@ -20,16 +20,24 @@ public TraceLogProcessorTests(ITestOutputHelper output) // [Fact] // public void ManualDebugging() // { - // var etlFilePath = "C:/dev/Aura.UI/profile.nettrace"; + // var etlFilePath = "T:/src/sentry-dotnet/samples/Sentry.Samples.Console.Profiling/Sentry.Samples.Console.Profiling.exe_20250124_144756.nettrace"; // var etlxFilePath = Path.ChangeExtension(etlFilePath, ".etlx"); // if (!File.Exists(etlxFilePath)) // { // TraceLog.CreateFromEventTraceLogFile(etlFilePath, etlxFilePath); // } // using var eventLog = new TraceLog(etlxFilePath); - // var processor = new TraceLogProcessor(new(), eventLog); - // var profile = processor.Process(CancellationToken.None); - // var json = profile.ToJsonString(_testOutputLogger); + // var builder = new SampleProfileBuilder(new(), eventLog); + // foreach (var event_ in eventLog.Events) + // { + // if (event_.ProviderGuid == SampleProfilerTraceEventParser.ProviderGuid) + // { + // builder.AddSample(event_, event_.TimeStampRelativeMSec); + // } + // } + + // var json = builder.Profile.ToJsonString(_testOutputLogger); + // VerifyJson(json); // } private SampleProfile BuilProfile(TraceLogEventSource eventSource) From d197cb2f95c63f25992440b3f3e6947d4adbc11e Mon Sep 17 00:00:00 2001 From: Ivan Dlugos <6349682+vaind@users.noreply.github.com> Date: Fri, 7 Feb 2025 07:37:04 +0100 Subject: [PATCH 116/363] fix: net8 unknown stack trace methods for JIT methods (#3942) * fix: net8 unknown stack trace methods for JIT methods * chore: changelog * chore: fix changelog * remove problematic assertion * fix: dispose before processing is done --- CHANGELOG.md | 1 + src/Sentry.Profiling/SampleProfilerSession.cs | 39 +++++++++++-------- .../SamplingTransactionProfilerTests.cs | 37 +++++++++++++++++- 3 files changed, 59 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7433c719c8..a415874c1f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Fixes - OTel activities that are marked as not recorded are no longer sent to Sentry ([#3890](https://github.com/getsentry/sentry-dotnet/pull/3890)) +- Unknown stack frames in profiles on .NET 8+ ([#3942](https://github.com/getsentry/sentry-dotnet/pull/3942)) - Deduplicate profiling stack frames ([#3941](https://github.com/getsentry/sentry-dotnet/pull/3941)) ## 5.1.0 diff --git a/src/Sentry.Profiling/SampleProfilerSession.cs b/src/Sentry.Profiling/SampleProfilerSession.cs index 3bb606a960..6cd9a9242a 100644 --- a/src/Sentry.Profiling/SampleProfilerSession.cs +++ b/src/Sentry.Profiling/SampleProfilerSession.cs @@ -12,31 +12,32 @@ namespace Sentry.Profiling; internal class SampleProfilerSession : IDisposable { private readonly EventPipeSession _session; - private readonly TraceLogEventSource _eventSource; private readonly SampleProfilerTraceEventParser _sampleEventParser; private readonly IDiagnosticLogger? _logger; private readonly SentryStopwatch _stopwatch; private bool _stopped = false; + private Task _processing; - private SampleProfilerSession(SentryStopwatch stopwatch, EventPipeSession session, TraceLogEventSource eventSource, IDiagnosticLogger? logger) + private SampleProfilerSession(SentryStopwatch stopwatch, EventPipeSession session, TraceLogEventSource eventSource, Task processing, IDiagnosticLogger? logger) { _session = session; _logger = logger; - _eventSource = eventSource; - _sampleEventParser = new SampleProfilerTraceEventParser(_eventSource); + EventSource = eventSource; + _sampleEventParser = new SampleProfilerTraceEventParser(EventSource); _stopwatch = stopwatch; + _processing = processing; } // Exposed only for benchmarks. internal static EventPipeProvider[] Providers = new[] { - // Note: all events we need issued by "DotNETRuntime" provider are at "EventLevel.Informational" - // see https://learn.microsoft.com/en-us/dotnet/fundamentals/diagnostics/runtime-events - // TODO replace Keywords.Default with a subset. Currently it is: - // Default = GC | Type | GCHeapSurvivalAndMovement | Binder | Loader | Jit | NGen | SupressNGen - // | StopEnumeration | Security | AppDomainResourceManagement | Exception | Threading | Contention | Stack | JittedMethodILToNativeMap - // | ThreadTransfer | GCHeapAndTypeNames | Codesymbols | Compilation, - new EventPipeProvider(ClrTraceEventParser.ProviderName, EventLevel.Informational, (long) ClrTraceEventParser.Keywords.Default), + new EventPipeProvider(ClrTraceEventParser.ProviderName, EventLevel.Verbose, (long) ( + ClrTraceEventParser.Keywords.Jit + | ClrTraceEventParser.Keywords.NGen + | ClrTraceEventParser.Keywords.Loader + | ClrTraceEventParser.Keywords.Binder + | ClrTraceEventParser.Keywords.JittedMethodILToNativeMap + )), new EventPipeProvider(SampleProfilerTraceEventParser.ProviderName, EventLevel.Informational), // new EventPipeProvider(TplEtwProviderTraceEventParser.ProviderName, EventLevel.Informational, (long) TplEtwProviderTraceEventParser.Keywords.Default) }; @@ -46,11 +47,14 @@ private SampleProfilerSession(SentryStopwatch stopwatch, EventPipeSession sessio // need a large buffer if we're connecting righ away. Leaving it too large increases app memory usage. internal static int CircularBufferMB = 16; + // Exposed for tests + internal TraceLogEventSource EventSource { get; } + public SampleProfilerTraceEventParser SampleEventParser => _sampleEventParser; public TimeSpan Elapsed => _stopwatch.Elapsed; - public TraceLog TraceLog => _eventSource.TraceLog; + public TraceLog TraceLog => EventSource.TraceLog; // default is false, set 1 for true. private static int _throwOnNextStartupForTests = 0; @@ -86,7 +90,7 @@ public static SampleProfilerSession StartNew(IDiagnosticLogger? logger = null) var eventSource = TraceLog.CreateFromEventPipeSession(session, TraceLog.EventPipeRundownConfiguration.Enable(client)); // Process() blocks until the session is stopped so we need to run it on a separate thread. - Task.Factory.StartNew(eventSource.Process, TaskCreationOptions.LongRunning) + var processing = Task.Factory.StartNew(eventSource.Process, TaskCreationOptions.LongRunning) .ContinueWith(_ => { if (_.Exception?.InnerException is { } e) @@ -95,7 +99,7 @@ public static SampleProfilerSession StartNew(IDiagnosticLogger? logger = null) } }, TaskContinuationOptions.OnlyOnFaulted); - return new SampleProfilerSession(stopWatch, session, eventSource, logger); + return new SampleProfilerSession(stopWatch, session, eventSource, processing, logger); } catch (Exception ex) { @@ -108,7 +112,7 @@ public async Task WaitForFirstEventAsync(CancellationToken cancellationToken = d { var tcs = new TaskCompletionSource(); var cb = (TraceEvent _) => { tcs.TrySetResult(); }; - _eventSource.AllEvents += cb; + EventSource.AllEvents += cb; try { // Wait for the first event to be processed. @@ -116,7 +120,7 @@ public async Task WaitForFirstEventAsync(CancellationToken cancellationToken = d } finally { - _eventSource.AllEvents -= cb; + EventSource.AllEvents -= cb; } } @@ -128,8 +132,9 @@ public void Stop() { _stopped = true; _session.Stop(); + _processing.Wait(); _session.Dispose(); - _eventSource.Dispose(); + EventSource.Dispose(); } catch (Exception ex) { diff --git a/test/Sentry.Profiling.Tests/SamplingTransactionProfilerTests.cs b/test/Sentry.Profiling.Tests/SamplingTransactionProfilerTests.cs index 12bdcc5995..ac1855c6be 100644 --- a/test/Sentry.Profiling.Tests/SamplingTransactionProfilerTests.cs +++ b/test/Sentry.Profiling.Tests/SamplingTransactionProfilerTests.cs @@ -1,4 +1,5 @@ -using System.IO.Abstractions.TestingHelpers; +using Microsoft.Diagnostics.Tracing; +using Microsoft.Diagnostics.Tracing.Parsers.Clr; using Sentry.Internal.Http; namespace Sentry.Profiling.Tests; @@ -180,6 +181,40 @@ public async Task Profiler_AfterTimeout_Stops() } } + [SkippableFact] + public async Task EventPipeSession_ReceivesExpectedCLREvents() + { + SampleProfilerSession? session = null; + SkipIfFailsInCI(() => session = SampleProfilerSession.StartNew(_testOutputLogger)); + using (session) + { + var eventsReceived = new HashSet(); + session!.EventSource.Clr.All += (TraceEvent ev) => eventsReceived.Add(ev.EventName); + + var loadedMethods = new HashSet(); + session!.EventSource.Clr.MethodLoadVerbose += (MethodLoadUnloadVerboseTraceData ev) => loadedMethods.Add(ev.MethodName); + + + await session.WaitForFirstEventAsync(CancellationToken.None); + var limitMs = 50; + var sut = new SamplingTransactionProfiler(_testSentryOptions, session, limitMs, CancellationToken.None); + RunForMs(limitMs * 5); + MethodToBeLoaded(100); + RunForMs(limitMs * 5); + sut.Finish(); + + Assert.Contains("Method/LoadVerbose", eventsReceived); + Assert.Contains("Method/ILToNativeMap", eventsReceived); + + Assert.Contains("MethodToBeLoaded", loadedMethods); + } + } + + private static long MethodToBeLoaded(int n) + { + return -n; + } + [SkippableTheory] [InlineData(true)] [InlineData(false)] From 337faa713c63dbf9626c913975a8310fb77ba4e1 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Mon, 10 Feb 2025 09:36:17 +1300 Subject: [PATCH 117/363] Don't resend envelopes that get rejected by the server (#3938) --- CHANGELOG.md | 1 + src/Sentry/Internal/DiscardReason.cs | 1 + src/Sentry/Internal/Http/CachingTransport.cs | 20 +++++++++- .../Internals/Http/CachingTransportTests.cs | 37 +++++++++++++++++++ 4 files changed, 57 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a415874c1f..78d8179867 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Fixes - OTel activities that are marked as not recorded are no longer sent to Sentry ([#3890](https://github.com/getsentry/sentry-dotnet/pull/3890)) +- Fixed envelopes with oversized attachments getting stuck in __processing ([#3938](https://github.com/getsentry/sentry-dotnet/pull/3938)) - Unknown stack frames in profiles on .NET 8+ ([#3942](https://github.com/getsentry/sentry-dotnet/pull/3942)) - Deduplicate profiling stack frames ([#3941](https://github.com/getsentry/sentry-dotnet/pull/3941)) diff --git a/src/Sentry/Internal/DiscardReason.cs b/src/Sentry/Internal/DiscardReason.cs index d132f761ba..11a35fa2a3 100644 --- a/src/Sentry/Internal/DiscardReason.cs +++ b/src/Sentry/Internal/DiscardReason.cs @@ -4,6 +4,7 @@ namespace Sentry.Internal; { // See https://develop.sentry.dev/sdk/client-reports/ for list public static DiscardReason BeforeSend = new("before_send"); + public static DiscardReason BufferOverflow = new("buffer_overflow"); public static DiscardReason CacheOverflow = new("cache_overflow"); public static DiscardReason EventProcessor = new("event_processor"); public static DiscardReason NetworkError = new("network_error"); diff --git a/src/Sentry/Internal/Http/CachingTransport.cs b/src/Sentry/Internal/Http/CachingTransport.cs index f6d4c9b856..b5ed2cb34a 100644 --- a/src/Sentry/Internal/Http/CachingTransport.cs +++ b/src/Sentry/Internal/Http/CachingTransport.cs @@ -300,13 +300,22 @@ private async Task ProcessCacheAsync(CancellationToken cancellation) } } - private static bool IsNetworkError(Exception exception) => + private static bool IsNetworkUnavailableError(Exception exception) => exception switch { + // TODO: Could we make this more specific? Lots of these errors are unrelated to network availability. HttpRequestException or WebException or IOException or SocketException => true, _ => false }; + private static bool IsRejectedByServer(Exception ex) + { + // When envelopes are too big, the server will reset the connection as soon as the maximum size is exceeded + // (it doesn't wait for us to finish sending the whole envelope). + return ex is SocketException { ErrorCode: 32 /* Broken pipe */ } + || (ex.InnerException is { } innerException && IsRejectedByServer(innerException)); + } + private async Task InnerProcessCacheAsync(string file, CancellationToken cancellation) { if (_options.NetworkStatusListener is { Online: false } listener) @@ -346,8 +355,15 @@ private async Task InnerProcessCacheAsync(string file, CancellationToken cancell // Let the worker catch, log, wait a bit and retry. throw; } - catch (Exception ex) when (IsNetworkError(ex)) + catch (Exception ex) when (IsRejectedByServer(ex)) + { + _options.ClientReportRecorder.RecordDiscardedEvents(DiscardReason.BufferOverflow, envelope); + _options.LogError(ex, "Failed to send cached envelope: {0}. The envelope is likely too big and will be discarded.", file); + } + catch (Exception ex) when (IsNetworkUnavailableError(ex)) { + // TODO: Envelopes could end up in an infinite loop here. We should consider implementing some + // kind backoff strategy and a retry limit... then drop the envelopes if the limit is exceeded. if (_options.NetworkStatusListener is PollingNetworkStatusListener pollingListener) { pollingListener.Online = false; diff --git a/test/Sentry.Tests/Internals/Http/CachingTransportTests.cs b/test/Sentry.Tests/Internals/Http/CachingTransportTests.cs index 05df93f02b..e903b7f3ca 100644 --- a/test/Sentry.Tests/Internals/Http/CachingTransportTests.cs +++ b/test/Sentry.Tests/Internals/Http/CachingTransportTests.cs @@ -711,6 +711,43 @@ public async Task TransportResumesWhenNetworkComesBackOnline() envelopes.Should().NotBeEmpty(); } + [Fact] + public async Task FlushAsync_RejectedByServer_DiscardsEnvelope() + { + // Arrange + var listener = Substitute.For(); + listener.Online.Returns(_ => true); + + using var cacheDirectory = new TempDirectory(); + var options = new SentryOptions + { + Dsn = ValidDsn, + DiagnosticLogger = _logger, + Debug = true, + CacheDirectoryPath = cacheDirectory.Path, + NetworkStatusListener = listener, + ClientReportRecorder = Substitute.For() + }; + + using var envelope = Envelope.FromEvent(new SentryEvent()); + + var innerTransport = Substitute.For(); + innerTransport.SendEnvelopeAsync(Arg.Any(), Arg.Any()) + .Returns(_ => throw new SocketException(32 /* Bad pipe exception */)); + await using var transport = CachingTransport.Create(innerTransport, options, startWorker: false); + + // Act + await transport.SendEnvelopeAsync(envelope); + await transport.FlushAsync(); + + // Assert + foreach (var item in envelope.Items) + { + options.ClientReportRecorder.Received(1) + .RecordDiscardedEvent(DiscardReason.BufferOverflow, item.DataCategory); + } + } + [Fact] public async Task DoesntWriteSentAtHeaderToCacheFile() { From 50e5232600e93e9723cc3c8937a2630bbd196171 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Mon, 10 Feb 2025 23:22:54 +1300 Subject: [PATCH 118/363] Skip flaky test (EventPipeSession_ReceivesExpectedCLREvents) (#3949) --- .../SamplingTransactionProfilerTests.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/Sentry.Profiling.Tests/SamplingTransactionProfilerTests.cs b/test/Sentry.Profiling.Tests/SamplingTransactionProfilerTests.cs index ac1855c6be..6f9ff84191 100644 --- a/test/Sentry.Profiling.Tests/SamplingTransactionProfilerTests.cs +++ b/test/Sentry.Profiling.Tests/SamplingTransactionProfilerTests.cs @@ -184,8 +184,9 @@ public async Task Profiler_AfterTimeout_Stops() [SkippableFact] public async Task EventPipeSession_ReceivesExpectedCLREvents() { - SampleProfilerSession? session = null; - SkipIfFailsInCI(() => session = SampleProfilerSession.StartNew(_testOutputLogger)); + Skip.If(TestEnvironment.IsGitHubActions, "Flaky on CI"); + + var session = SampleProfilerSession.StartNew(_testOutputLogger); using (session) { var eventsReceived = new HashSet(); @@ -194,7 +195,6 @@ public async Task EventPipeSession_ReceivesExpectedCLREvents() var loadedMethods = new HashSet(); session!.EventSource.Clr.MethodLoadVerbose += (MethodLoadUnloadVerboseTraceData ev) => loadedMethods.Add(ev.MethodName); - await session.WaitForFirstEventAsync(CancellationToken.None); var limitMs = 50; var sut = new SamplingTransactionProfiler(_testSentryOptions, session, limitMs, CancellationToken.None); From e3425c921b5c6bbc52e3f1d34a562ef2ebd33e5c Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Tue, 11 Feb 2025 09:30:32 +1300 Subject: [PATCH 119/363] Log rate limit headers (#3950) --- src/Sentry/Http/HttpTransportBase.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Sentry/Http/HttpTransportBase.cs b/src/Sentry/Http/HttpTransportBase.cs index 49147645b6..9379980163 100644 --- a/src/Sentry/Http/HttpTransportBase.cs +++ b/src/Sentry/Http/HttpTransportBase.cs @@ -271,6 +271,7 @@ private void ExtractRateLimits(HttpHeaders responseHeaders) // Join to a string to handle both single-header and multi-header cases var rateLimitsEncoded = string.Join(",", rateLimitHeaderValues); + _options.LogDebug("Parsing rate limit headers: {0}", rateLimitsEncoded); // Parse and order by retry-after so that the longer rate limits are set last (and not overwritten) var rateLimits = RateLimit.ParseMany(rateLimitsEncoded).OrderBy(rl => rl.RetryAfter); From 4e7c3e44d7055041a4840be01993cee56701ed04 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Tue, 11 Feb 2025 10:10:49 +1300 Subject: [PATCH 120/363] Suppress Native SIGSEGV signal errors on Android (#3903) --- CHANGELOG.md | 2 +- .../Platforms/Android/AndroidOptions.cs | 16 +++ .../Android/BindableAndroidSentryOptions.cs | 2 + src/Sentry/Platforms/Android/SentrySdk.cs | 26 +++- .../Platforms/Android/SentrySdkTests.cs | 116 ++++++++++++++++++ 5 files changed, 157 insertions(+), 5 deletions(-) create mode 100644 test/Sentry.Tests/Platforms/Android/SentrySdkTests.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 78d8179867..674e18a1f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Fixes +- Native SIGSEGV errors resulting from managed NullReferenceExceptions are now suppressed on Android ([#3903](https://github.com/getsentry/sentry-dotnet/pull/3903)) - OTel activities that are marked as not recorded are no longer sent to Sentry ([#3890](https://github.com/getsentry/sentry-dotnet/pull/3890)) - Fixed envelopes with oversized attachments getting stuck in __processing ([#3938](https://github.com/getsentry/sentry-dotnet/pull/3938)) - Unknown stack frames in profiles on .NET 8+ ([#3942](https://github.com/getsentry/sentry-dotnet/pull/3942)) @@ -12,7 +13,6 @@ ## 5.1.0 ### Significant change in behavior - - The User.IpAddress is now only set to `{{auto}}` when `SendDefaultPii` is enabled. This change gives you control over IP address collection directly on the client ([#3893](https://github.com/getsentry/sentry-dotnet/pull/3893)) ### Features diff --git a/src/Sentry/Platforms/Android/AndroidOptions.cs b/src/Sentry/Platforms/Android/AndroidOptions.cs index f6dd210aaa..73e38ae20a 100644 --- a/src/Sentry/Platforms/Android/AndroidOptions.cs +++ b/src/Sentry/Platforms/Android/AndroidOptions.cs @@ -25,5 +25,21 @@ public class AndroidOptions /// /// public int LogCatMaxLines { get; set; } = 1000; + + /// + /// + /// Whether to suppress capturing SIGSEGV (Segfault) errors in the Native SDK. + /// + /// + /// When managed code results in a NullReferenceException, this also causes a SIGSEGV (Segfault). Duplicate + /// events (one managed and one native) can be prevented by suppressing native Segfaults, which may be + /// convenient. + /// + /// + /// Enabling this may prevent the capture of Segfault originating from native (not managed) code... so it may + /// prevent the capture of genuine native Segfault errors. + /// + /// + public bool SuppressSegfaults { get; set; } = false; } } diff --git a/src/Sentry/Platforms/Android/BindableAndroidSentryOptions.cs b/src/Sentry/Platforms/Android/BindableAndroidSentryOptions.cs index ea721bb7d4..0fa8264cb1 100644 --- a/src/Sentry/Platforms/Android/BindableAndroidSentryOptions.cs +++ b/src/Sentry/Platforms/Android/BindableAndroidSentryOptions.cs @@ -14,11 +14,13 @@ public class AndroidOptions { public LogCatIntegrationType? LogCatIntegration { get; set; } public int? LogCatMaxLines { get; set; } + public bool? SuppressSegfaults { get; set; } public void ApplyTo(SentryOptions.AndroidOptions options) { options.LogCatIntegration = LogCatIntegration ?? options.LogCatIntegration; options.LogCatMaxLines = LogCatMaxLines ?? options.LogCatMaxLines; + options.SuppressSegfaults = SuppressSegfaults ?? options.SuppressSegfaults; } } } diff --git a/src/Sentry/Platforms/Android/SentrySdk.cs b/src/Sentry/Platforms/Android/SentrySdk.cs index 607f4dee59..3cba5dac2c 100644 --- a/src/Sentry/Platforms/Android/SentrySdk.cs +++ b/src/Sentry/Platforms/Android/SentrySdk.cs @@ -3,6 +3,7 @@ using Sentry.Android; using Sentry.Android.Callbacks; using Sentry.Android.Extensions; +using Sentry.Extensibility; using Sentry.JavaSdk.Android.Core; // Don't let the Sentry Android SDK auto-init, as we do that manually in SentrySdk.Init @@ -99,10 +100,7 @@ private static void InitSentryAndroidSdk(SentryOptions options) } } - if (options.Native.EnableBeforeSend && options.BeforeSendInternal is { } beforeSend) - { - o.BeforeSend = new BeforeSendCallback(beforeSend, options, o); - } + o.BeforeSend = new BeforeSendCallback(BeforeSendWrapper(options), options, o); // These options are from SentryAndroidOptions o.AttachScreenshot = options.Native.AttachScreenshot; @@ -172,6 +170,26 @@ private static void InitSentryAndroidSdk(SentryOptions options) // TODO: Pause/Resume } + internal static Func BeforeSendWrapper(SentryOptions options) + { + return (evt, hint) => + { + // Suppress SIGSEGV errors. + // See: https://github.com/getsentry/sentry-dotnet/pull/3903 + if (options.Android.SuppressSegfaults + && evt.SentryExceptions?.FirstOrDefault() is { Type: "SIGSEGV", Value: "Segfault" }) + { + options.LogDebug("Suppressing SIGSEGV (this will be thrown as a managed exception instead)"); + return null; + } + + // Call the user defined BeforeSend callback, if it's defined - otherwise return the event as-is + return (options.Native.EnableBeforeSend && options.BeforeSendInternal is { } beforeSend) + ? beforeSend(evt, hint) + : evt; + }; + } + private static void AndroidEnvironment_UnhandledExceptionRaiser(object? _, RaiseThrowableEventArgs e) { var description = "This exception was caught by the Android global error handler."; diff --git a/test/Sentry.Tests/Platforms/Android/SentrySdkTests.cs b/test/Sentry.Tests/Platforms/Android/SentrySdkTests.cs new file mode 100644 index 0000000000..96bde8e62b --- /dev/null +++ b/test/Sentry.Tests/Platforms/Android/SentrySdkTests.cs @@ -0,0 +1,116 @@ +#if ANDROID +namespace Sentry.Tests.Platforms.Android; + +public class SentrySdkTests +{ + [Fact] + public void BeforeSendWrapper_Suppress_SIGSEGV_ReturnsNull() + { + // Arrange + var options = new SentryOptions(); + options.Android.SuppressSegfaults = true; + var evt = new SentryEvent + { + SentryExceptions = new[] + { + new SentryException + { + Type = "SIGSEGV", + Value = "Segfault" + } + } + }; + var hint = new SentryHint(); + + // Act + var result = SentrySdk.BeforeSendWrapper(options).Invoke(evt, hint); + + // Assert + result.Should().BeNull(); + } + + [Fact] + public void BeforeSendWrapper_DontSupress_SIGSEGV_ReturnsEvent() + { + // Arrange + var options = new SentryOptions(); + options.Android.SuppressSegfaults = false; + var evt = new SentryEvent + { + SentryExceptions = new[] + { + new SentryException + { + Type = "SIGSEGV", + Value = "Segfault" + } + } + }; + var hint = new SentryHint(); + + // Act + var result = SentrySdk.BeforeSendWrapper(options).Invoke(evt, hint); + + // Assert + result.Should().Be(evt); + } + + [Fact] + public void BeforeSendWrapper_BeforeSendCallbackDefined_CallsBeforeSend() + { + // Arrange + var beforeSend = Substitute.For>(); + beforeSend.Invoke(Arg.Any(), Arg.Any()).Returns(callInfo => callInfo.Arg()); + + var options = new SentryOptions(); + options.Native.EnableBeforeSend = true; + options.SetBeforeSend(beforeSend); + var evt = new SentryEvent(); + var hint = new SentryHint(); + + // Act + var result = SentrySdk.BeforeSendWrapper(options).Invoke(evt, hint); + + // Assert + beforeSend.Received(1).Invoke(Arg.Any(), Arg.Any()); + result.Should().Be(evt); + } + + [Fact] + public void BeforeSendWrapper_NoBeforeSendCallback_ReturnsEvent() + { + // Arrange + var options = new SentryOptions(); + options.Native.EnableBeforeSend = true; + var evt = new SentryEvent(); + var hint = new SentryHint(); + + // Act + var result = SentrySdk.BeforeSendWrapper(options).Invoke(evt, hint); + + // Assert + result.Should().Be(evt); + } + + [Fact] + public void BeforeSendWrapper_NativeBeforeSendDisabled_ReturnsEvent() + { + // Arrange + var beforeSend = Substitute.For>(); + beforeSend.Invoke(Arg.Any(), Arg.Any()).Returns(_ => null); + + var options = new SentryOptions(); + options.SetBeforeSend(beforeSend); + options.Native.EnableBeforeSend = false; + var evt = new SentryEvent(); + var hint = new SentryHint(); + + // Act + var result = SentrySdk.BeforeSendWrapper(options).Invoke(evt, hint); + + // Assert + beforeSend.DidNotReceive().Invoke(Arg.Any(), Arg.Any()); + result.Should().Be(evt); + } +} +#endif From 7a632e30b78be13d1f5c4015639a1bd65392ba2b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Feb 2025 13:59:12 +1300 Subject: [PATCH 121/363] build(deps): bump github/codeql-action from 3.28.8 to 3.28.9 (#3952) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.28.8 to 3.28.9. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/dd746615b3b9d728a6a37ca2045b68ca76d4841a...9e8d0789d4a0fa9ceb6b1738f7e269594bdd67f0) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: James Crosswell --- .github/workflows/codeql-analysis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 0547bafd88..b8110bb8a9 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -35,7 +35,7 @@ jobs: uses: ./.github/actions/environment - name: Initialize CodeQL - uses: github/codeql-action/init@dd746615b3b9d728a6a37ca2045b68ca76d4841a # pin@v2 + uses: github/codeql-action/init@9e8d0789d4a0fa9ceb6b1738f7e269594bdd67f0 # pin@v2 with: languages: csharp @@ -49,6 +49,6 @@ jobs: run: dotnet build Sentry-CI-CodeQL.slnf --no-restore --nologo - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@dd746615b3b9d728a6a37ca2045b68ca76d4841a # pin@v2 + uses: github/codeql-action/analyze@9e8d0789d4a0fa9ceb6b1738f7e269594bdd67f0 # pin@v2 with: category: '/language:csharp' From aa5c43274cabc3e5171c77ab21bc519026ad80b3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Feb 2025 16:29:52 +1300 Subject: [PATCH 122/363] build(deps): bump actions/create-github-app-token from 1.11.2 to 1.11.3 (#3953) Bumps [actions/create-github-app-token](https://github.com/actions/create-github-app-token) from 1.11.2 to 1.11.3. - [Release notes](https://github.com/actions/create-github-app-token/releases) - [Commits](https://github.com/actions/create-github-app-token/compare/136412a57a7081aa63c935a2cc2918f76c34f514...67e27a7eb7db372a1c61a7f9bdab8699e9ee57f7) --- updated-dependencies: - dependency-name: actions/create-github-app-token dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: James Crosswell --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9058afdcd7..8049860b19 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -20,7 +20,7 @@ jobs: steps: - name: Get auth token id: token - uses: actions/create-github-app-token@136412a57a7081aa63c935a2cc2918f76c34f514 # v1.11.2 + uses: actions/create-github-app-token@67e27a7eb7db372a1c61a7f9bdab8699e9ee57f7 # v1.11.3 with: app-id: ${{ vars.SENTRY_RELEASE_BOT_CLIENT_ID }} private-key: ${{ secrets.SENTRY_RELEASE_BOT_PRIVATE_KEY }} From 19b5a7eb76ced4820b6de3398ac77c11aff939d8 Mon Sep 17 00:00:00 2001 From: Tomasz Cielecki <249719+Cheesebaron@users.noreply.github.com> Date: Tue, 11 Feb 2025 23:14:23 +0100 Subject: [PATCH 123/363] Ignore null value on CocoaScopeObserver.SetTag (#3948) --- CHANGELOG.md | 1 + .../Platforms/Cocoa/CocoaScopeObserver.cs | 6 ++++++ test/Sentry.Tests/ScopeTests.cs | 18 ++++++++++++++++++ 3 files changed, 25 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 674e18a1f8..844ba4106e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - Fixed envelopes with oversized attachments getting stuck in __processing ([#3938](https://github.com/getsentry/sentry-dotnet/pull/3938)) - Unknown stack frames in profiles on .NET 8+ ([#3942](https://github.com/getsentry/sentry-dotnet/pull/3942)) - Deduplicate profiling stack frames ([#3941](https://github.com/getsentry/sentry-dotnet/pull/3941)) +- Ignore null value on CocoaScopeObserver.SetTag ([#3948](https://github.com/getsentry/sentry-dotnet/pull/3948)) ## 5.1.0 diff --git a/src/Sentry/Platforms/Cocoa/CocoaScopeObserver.cs b/src/Sentry/Platforms/Cocoa/CocoaScopeObserver.cs index 2e96952516..f36271f320 100644 --- a/src/Sentry/Platforms/Cocoa/CocoaScopeObserver.cs +++ b/src/Sentry/Platforms/Cocoa/CocoaScopeObserver.cs @@ -67,6 +67,12 @@ public void SetExtra(string key, object? value) public void SetTag(string key, string value) { + if (value is null) + { + _options.LogDebug("Tag with key '{0}' was null. Use Unset to remove tags instead.", key); + return; + } + try { SentryCocoaSdk.ConfigureScope(scope => scope.SetTagValue(value, key)); diff --git a/test/Sentry.Tests/ScopeTests.cs b/test/Sentry.Tests/ScopeTests.cs index 5815cdb68c..ece48d5170 100644 --- a/test/Sentry.Tests/ScopeTests.cs +++ b/test/Sentry.Tests/ScopeTests.cs @@ -615,6 +615,24 @@ public void Filtered_tags_are_not_set() scope.Tags.Should().OnlyContain(pair => pair.Key == "AzFunctions" && pair.Value == "rule"); } + + [Fact] + public void SetTag_NullValue_DoesNotThrowArgumentNullException() + { + var scope = new Scope(); + + ArgumentNullException exception = null; + try + { + scope.SetTag("IAmNull", null); + } + catch (ArgumentNullException e) + { + exception = e; + } + + Assert.Null(exception); + } } public static class ScopeTestExtensions From ba2b968c7ce04faf2c7f7a76a76c148383cbf48b Mon Sep 17 00:00:00 2001 From: Bruno Garcia Date: Wed, 12 Feb 2025 14:57:53 -0500 Subject: [PATCH 124/363] codeowners: remove bruno add allan (#3961) --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 3d362c4857..392cfefbbb 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1 @@ -* @bruno-garcia @jamescrosswell +* @aritchie @jamescrosswell From 4169cb739534e28b64a322a3a3938ee116cf7e6b Mon Sep 17 00:00:00 2001 From: Allan Ritchie Date: Wed, 12 Feb 2025 15:29:41 -0500 Subject: [PATCH 125/363] Emit transaction.data inside contexts.trace.data (#3936) --- CHANGELOG.md | 1 + src/Sentry/IHasData.cs | 17 +++++++ src/Sentry/ISpanData.cs | 2 +- src/Sentry/Internal/NoOpSpan.cs | 5 ++ src/Sentry/Protocol/Trace.cs | 23 ++++++++- src/Sentry/SentrySpan.cs | 25 +++++++--- src/Sentry/SentryTransaction.cs | 23 +++++---- src/Sentry/SpanTracer.cs | 11 +++- src/Sentry/TransactionTracer.cs | 15 ++++-- .../Protocol/SentryTransactionTests.cs | 2 +- test/Sentry.Tests/SerializationTests.cs | 50 +++++++++++++++++++ .../Sentry.Tests/SerializationTests.verify.cs | 9 +--- 12 files changed, 149 insertions(+), 34 deletions(-) create mode 100644 src/Sentry/IHasData.cs create mode 100644 test/Sentry.Tests/SerializationTests.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 844ba4106e..654821f034 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Fixes +- Emit transaction.data inside contexts.trace.data ([#3936](https://github.com/getsentry/sentry-dotnet/pull/3936)) - Native SIGSEGV errors resulting from managed NullReferenceExceptions are now suppressed on Android ([#3903](https://github.com/getsentry/sentry-dotnet/pull/3903)) - OTel activities that are marked as not recorded are no longer sent to Sentry ([#3890](https://github.com/getsentry/sentry-dotnet/pull/3890)) - Fixed envelopes with oversized attachments getting stuck in __processing ([#3938](https://github.com/getsentry/sentry-dotnet/pull/3938)) diff --git a/src/Sentry/IHasData.cs b/src/Sentry/IHasData.cs new file mode 100644 index 0000000000..980f403731 --- /dev/null +++ b/src/Sentry/IHasData.cs @@ -0,0 +1,17 @@ +namespace Sentry; + +/// +/// Implemented by objects that contain a map of untyped additional metadata. +/// +public interface IHasData +{ + /// + /// An arbitrary mapping of additional metadata to store with the event. + /// + IReadOnlyDictionary Data { get; } + + /// + /// Sets an extra. + /// + void SetData(string key, object? value); +} diff --git a/src/Sentry/ISpanData.cs b/src/Sentry/ISpanData.cs index 373ff75c34..3a475102b8 100644 --- a/src/Sentry/ISpanData.cs +++ b/src/Sentry/ISpanData.cs @@ -5,7 +5,7 @@ namespace Sentry; /// /// Immutable data belonging to a span. /// -public interface ISpanData : ITraceContext, IHasTags, IHasExtra +public interface ISpanData : ITraceContext, IHasData, IHasTags, IHasExtra { /// /// Start timestamp. diff --git a/src/Sentry/Internal/NoOpSpan.cs b/src/Sentry/Internal/NoOpSpan.cs index babecc4a4d..f6e49e5a2c 100644 --- a/src/Sentry/Internal/NoOpSpan.cs +++ b/src/Sentry/Internal/NoOpSpan.cs @@ -19,6 +19,7 @@ protected NoOpSpan() public bool? IsSampled => default; public IReadOnlyDictionary Tags => ImmutableDictionary.Empty; public IReadOnlyDictionary Extra => ImmutableDictionary.Empty; + public IReadOnlyDictionary Data => ImmutableDictionary.Empty; public DateTimeOffset StartTimestamp => default; public DateTimeOffset? EndTimestamp => default; public bool IsFinished => default; @@ -71,6 +72,10 @@ public void SetExtra(string key, object? value) { } + public void SetData(string key, object? value) + { + } + public SentryTraceHeader GetTraceHeader() => SentryTraceHeader.Empty; public IReadOnlyDictionary Measurements => ImmutableDictionary.Empty; diff --git a/src/Sentry/Protocol/Trace.cs b/src/Sentry/Protocol/Trace.cs index 289b2a4b08..8878aab170 100644 --- a/src/Sentry/Protocol/Trace.cs +++ b/src/Sentry/Protocol/Trace.cs @@ -50,6 +50,21 @@ internal set /// public bool? IsSampled { get; internal set; } + private Dictionary _data = new(); + + /// + /// Get the metadata + /// + public IReadOnlyDictionary Data => _data; + + /// + /// Adds metadata to the trace + /// + /// + /// + public void SetData(string key, object? value) + => _data[key] = value; + /// /// Clones this instance. /// @@ -63,7 +78,8 @@ internal set Operation = Operation, Origin = Origin, Status = Status, - IsSampled = IsSampled + IsSampled = IsSampled, + _data = _data.ToDict() }; /// @@ -103,6 +119,7 @@ public void WriteTo(Utf8JsonWriter writer, IDiagnosticLogger? logger) writer.WriteString("origin", Origin ?? Internal.OriginHelper.Manual); writer.WriteStringIfNotWhiteSpace("description", Description); writer.WriteStringIfNotWhiteSpace("status", Status?.ToString().ToSnakeCase()); + writer.WriteDictionaryIfNotEmpty("data", _data, logger); writer.WriteEndObject(); } @@ -120,6 +137,7 @@ public static Trace FromJson(JsonElement json) var description = json.GetPropertyOrNull("description")?.GetString(); var status = json.GetPropertyOrNull("status")?.GetString()?.Replace("_", "").ParseEnum(); var isSampled = json.GetPropertyOrNull("sampled")?.GetBoolean(); + var data = json.GetPropertyOrNull("data")?.GetDictionaryOrNull() ?? new(); return new Trace { @@ -130,7 +148,8 @@ public static Trace FromJson(JsonElement json) Origin = origin, Description = description, Status = status, - IsSampled = isSampled + IsSampled = isSampled, + _data = data }; } } diff --git a/src/Sentry/SentrySpan.cs b/src/Sentry/SentrySpan.cs index 9031556c7d..e94ca99074 100644 --- a/src/Sentry/SentrySpan.cs +++ b/src/Sentry/SentrySpan.cs @@ -66,15 +66,26 @@ public void UnsetTag(string key) => (_tags ??= new Dictionary()).Remove(key); // Aka 'data' - private Dictionary? _extra; private readonly MetricsSummary? _metricsSummary; + + private Dictionary? _data; + + /// + public IReadOnlyDictionary Data => + _data ??= new Dictionary(); + + /// + public void SetData(string key, object? value) => + (_data ??= new Dictionary())[key] = value; + /// - public IReadOnlyDictionary Extra => _extra ??= new Dictionary(); + [Obsolete("Use SetData")] + public IReadOnlyDictionary Extra => Data; /// - public void SetExtra(string key, object? value) => - (_extra ??= new Dictionary())[key] = value; + [Obsolete("Use Data")] + public void SetExtra(string key, object? value) => SetData(key, value); /// /// Initializes an instance of . @@ -100,7 +111,7 @@ public SentrySpan(ISpan tracer) Description = tracer.Description; Status = tracer.Status; IsSampled = tracer.IsSampled; - _extra = tracer.Extra.ToDict(); + _data = tracer.Data.ToDict(); if (tracer is SpanTracer spanTracer) { @@ -138,7 +149,7 @@ public void WriteTo(Utf8JsonWriter writer, IDiagnosticLogger? logger) writer.WriteString("start_timestamp", StartTimestamp); writer.WriteStringIfNotNull("timestamp", EndTimestamp); writer.WriteStringDictionaryIfNotEmpty("tags", _tags!); - writer.WriteDictionaryIfNotEmpty("data", _extra!, logger); + writer.WriteDictionaryIfNotEmpty("data", _data!, logger); writer.WriteDictionaryIfNotEmpty("measurements", _measurements, logger); writer.WriteSerializableIfNotNull("_metrics_summary", _metricsSummary, logger); @@ -173,7 +184,7 @@ public static SentrySpan FromJson(JsonElement json) Status = status, IsSampled = isSampled, _tags = tags!, - _extra = data!, + _data = data!, _measurements = measurements, }; } diff --git a/src/Sentry/SentryTransaction.cs b/src/Sentry/SentryTransaction.cs index b69acd3f52..386891ec01 100644 --- a/src/Sentry/SentryTransaction.cs +++ b/src/Sentry/SentryTransaction.cs @@ -179,12 +179,6 @@ public IReadOnlyList Fingerprint /// public IReadOnlyCollection Breadcrumbs => _breadcrumbs; - // Not readonly because of deserialization - private Dictionary _extra = new(); - - /// - public IReadOnlyDictionary Extra => _extra; - // Not readonly because of deserialization private Dictionary _tags = new(); @@ -270,7 +264,6 @@ public SentryTransaction(ITransactionTracer tracer) Sdk = tracer.Sdk; Fingerprint = tracer.Fingerprint; _breadcrumbs = tracer.Breadcrumbs.ToList(); - _extra = tracer.Extra.ToDict(); _tags = tracer.Tags.ToDict(); _spans = FromTracerSpans(tracer); @@ -339,8 +332,20 @@ public void AddBreadcrumb(Breadcrumb breadcrumb) => _breadcrumbs.Add(breadcrumb); /// + public IReadOnlyDictionary Data => _contexts.Trace.Data; + + /// + [Obsolete("Use Data")] + public IReadOnlyDictionary Extra => _contexts.Trace.Data; + + /// + [Obsolete("Use SetData")] public void SetExtra(string key, object? value) => - _extra[key] = value; + SetData(key, value); + + /// + public void SetData(string key, object? value) => + _contexts.Trace.SetData(key, value); /// public void SetTag(string key, string value) => @@ -401,7 +406,6 @@ public void WriteTo(Utf8JsonWriter writer, IDiagnosticLogger? logger) writer.WriteSerializable("sdk", Sdk, logger); writer.WriteStringArrayIfNotEmpty("fingerprint", _fingerprint); writer.WriteArrayIfNotEmpty("breadcrumbs", _breadcrumbs, logger); - writer.WriteDictionaryIfNotEmpty("extra", _extra, logger); writer.WriteStringDictionaryIfNotEmpty("tags", _tags!); writer.WriteArrayIfNotEmpty("spans", _spans, logger); writer.WriteDictionaryIfNotEmpty("measurements", _measurements, logger); @@ -459,7 +463,6 @@ public static SentryTransaction FromJson(JsonElement json) Sdk = sdk, _fingerprint = fingerprint, _breadcrumbs = breadcrumbs, - _extra = extra, _tags = tags, _measurements = measurements, _spans = spans diff --git a/src/Sentry/SpanTracer.cs b/src/Sentry/SpanTracer.cs index 9dbbbc4b96..ed51d48eaa 100644 --- a/src/Sentry/SpanTracer.cs +++ b/src/Sentry/SpanTracer.cs @@ -85,10 +85,17 @@ public void UnsetTag(string key) => private readonly ConcurrentDictionary _data = new(); /// - public IReadOnlyDictionary Extra => _data; + public IReadOnlyDictionary Data => _data; /// - public void SetExtra(string key, object? value) => _data[key] = value; + public void SetData(string key, object? value) => + _data[key] = value; + + /// + public IReadOnlyDictionary Extra => Data; + + /// + public void SetExtra(string key, object? value) => SetData(key, value); internal Func? IsFiltered { get; set; } diff --git a/src/Sentry/TransactionTracer.cs b/src/Sentry/TransactionTracer.cs index 69376ad71f..e8059a2d00 100644 --- a/src/Sentry/TransactionTracer.cs +++ b/src/Sentry/TransactionTracer.cs @@ -157,10 +157,15 @@ public IReadOnlyList Fingerprint /// public IReadOnlyCollection Breadcrumbs => _breadcrumbs; - private readonly ConcurrentDictionary _extra = new(); + private readonly ConcurrentDictionary _data = new(); /// - public IReadOnlyDictionary Extra => _extra; + [Obsolete("Use Data")] + public IReadOnlyDictionary Extra => _data; + + /// + public IReadOnlyDictionary Data => _data; + private readonly ConcurrentDictionary _tags = new(); @@ -270,7 +275,11 @@ internal TransactionTracer(IHub hub, ITransactionContext context, TimeSpan? idle public void AddBreadcrumb(Breadcrumb breadcrumb) => _breadcrumbs.Add(breadcrumb); /// - public void SetExtra(string key, object? value) => _extra[key] = value; + [Obsolete("Use SetData")] + public void SetExtra(string key, object? value) => _data[key] = value; + + /// + public void SetData(string key, object? value) => _data[key] = value; /// public void SetTag(string key, string value) => _tags[key] = value; diff --git a/test/Sentry.Tests/Protocol/SentryTransactionTests.cs b/test/Sentry.Tests/Protocol/SentryTransactionTests.cs index 597e275c15..4e0872ada1 100644 --- a/test/Sentry.Tests/Protocol/SentryTransactionTests.cs +++ b/test/Sentry.Tests/Protocol/SentryTransactionTests.cs @@ -207,7 +207,7 @@ public void SerializeObject_AllPropertiesSetToNonDefault_SerializesValidObject() "category", BreadcrumbLevel.Warning)); - transaction.SetExtra("extra_key", "extra_value"); + transaction.SetData("extra_key", "extra_value"); transaction.Fingerprint = new[] { "fingerprint" }; transaction.SetTag("tag_key", "tag_value"); transaction.SetMeasurement("measurement_1", 111); diff --git a/test/Sentry.Tests/SerializationTests.cs b/test/Sentry.Tests/SerializationTests.cs new file mode 100644 index 0000000000..51a57bdd34 --- /dev/null +++ b/test/Sentry.Tests/SerializationTests.cs @@ -0,0 +1,50 @@ +using System.Text.Json.Nodes; + +namespace Sentry.Tests; + +public partial class SerializationTests +{ + private readonly IDiagnosticLogger _testOutputLogger; + + public SerializationTests(ITestOutputHelper output) + { + _testOutputLogger = new TestOutputDiagnosticLogger(output); + } + + [Fact] + public void Serialization_TransactionAndSpanData() + { + var hub = Substitute.For(); + var context = new TransactionContext("name", "operation", new SentryTraceHeader(SentryId.Empty, SpanId.Empty, false)); + var transactionTracer = new TransactionTracer(hub, context); + var span = transactionTracer.StartChild("childop"); + span.SetData("span1", "value1"); + + var transaction = new SentryTransaction(transactionTracer) + { + IsSampled = false + }; + transaction.SetData("transaction1", "transaction_value"); + var json = transaction.ToJsonString(_testOutputLogger); + _testOutputLogger.LogDebug(json); + + var node = JsonNode.Parse(json); + var dataNode = node?["contexts"]?["trace"]?["data"]?["transaction1"]?.GetValue(); + dataNode.Should().NotBeNull("contexts.trace.data.transaction1 not found"); + dataNode.Should().Be("transaction_value"); + + var spansNode = node?["spans"]?.AsArray(); + spansNode.Should().NotBeNull("spans not found"); + var spanDataNode = spansNode!.FirstOrDefault()?["data"]?["span1"]?.GetValue(); + spanDataNode.Should().NotBeNull("spans.data not found"); + spanDataNode.Should().Be("value1"); + + // verify deserialization + var reader = new Utf8JsonReader(Encoding.UTF8.GetBytes(json)); + var el = JsonElement.ParseValue(ref reader); + var backTransaction = SentryTransaction.FromJson(el); + + backTransaction.Spans.First().Data["span1"].Should().Be("value1", "Span value missing"); + backTransaction.Contexts.Trace.Data["transaction1"].Should().Be("transaction_value", "Transaction value missing"); + } +} diff --git a/test/Sentry.Tests/SerializationTests.verify.cs b/test/Sentry.Tests/SerializationTests.verify.cs index 86a671cb3c..65b08857fa 100644 --- a/test/Sentry.Tests/SerializationTests.verify.cs +++ b/test/Sentry.Tests/SerializationTests.verify.cs @@ -4,15 +4,8 @@ namespace Sentry.Tests; -public class SerializationTests +public partial class SerializationTests { - private readonly IDiagnosticLogger _testOutputLogger; - - public SerializationTests(ITestOutputHelper output) - { - _testOutputLogger = new TestOutputDiagnosticLogger(output); - } - [Theory] [MemberData(nameof(GetData))] public async Task Serialization(string name, object target) From ee62bb942b6bebc6b835130a5192aa68a31b6f54 Mon Sep 17 00:00:00 2001 From: Allan Ritchie Date: Wed, 12 Feb 2025 16:35:57 -0500 Subject: [PATCH 126/363] Fix #2710 - Return macOS instead of 'Darwin' and return proper version (#3956) * Initial commit * Finish * Update CHANGELOG.md * Update CHANGELOG.md * Update Sentry.Maui.Device.TestApp.csproj * Update EnricherTests.cs * Update CHANGELOG.md --- CHANGELOG.md | 1 + src/Sentry/Internal/Enricher.cs | 7 +++++ .../Sentry.Maui.Device.TestApp.csproj | 30 +++++++++---------- test/Sentry.Tests/EnricherTests.cs | 24 +++++++++++++++ test/Sentry.Tests/PlatformFact.cs | 25 ++++++++++++++++ 5 files changed, 72 insertions(+), 15 deletions(-) create mode 100644 test/Sentry.Tests/EnricherTests.cs create mode 100644 test/Sentry.Tests/PlatformFact.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 654821f034..49ac701258 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ - Fixed envelopes with oversized attachments getting stuck in __processing ([#3938](https://github.com/getsentry/sentry-dotnet/pull/3938)) - Unknown stack frames in profiles on .NET 8+ ([#3942](https://github.com/getsentry/sentry-dotnet/pull/3942)) - Deduplicate profiling stack frames ([#3941](https://github.com/getsentry/sentry-dotnet/pull/3941)) +- OperatingSystem will now return macOS as OS name instead of 'Darwin' as well as the proper version. ([#2710](https://github.com/getsentry/sentry-dotnet/pull/3956)) - Ignore null value on CocoaScopeObserver.SetTag ([#3948](https://github.com/getsentry/sentry-dotnet/pull/3948)) ## 5.1.0 diff --git a/src/Sentry/Internal/Enricher.cs b/src/Sentry/Internal/Enricher.cs index 54829dca86..3d3629934a 100644 --- a/src/Sentry/Internal/Enricher.cs +++ b/src/Sentry/Internal/Enricher.cs @@ -45,8 +45,15 @@ public void Apply(IEventLike eventLike) } catch { eventLike.Contexts.OperatingSystem.RawDescription = Environment.OSVersion.VersionString; } + #else eventLike.Contexts.OperatingSystem.RawDescription = RuntimeInformation.OSDescription; + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + // works for catalyst and net9 base + eventLike.Contexts.OperatingSystem.Name = "macOS"; + eventLike.Contexts.OperatingSystem.Version = Environment.OSVersion.Version.ToString(); // reports macOS version (ie. 15.3.0) + } #endif } } diff --git a/test/Sentry.Maui.Device.TestApp/Sentry.Maui.Device.TestApp.csproj b/test/Sentry.Maui.Device.TestApp/Sentry.Maui.Device.TestApp.csproj index 24a25093a6..5953b65d70 100644 --- a/test/Sentry.Maui.Device.TestApp/Sentry.Maui.Device.TestApp.csproj +++ b/test/Sentry.Maui.Device.TestApp/Sentry.Maui.Device.TestApp.csproj @@ -1,7 +1,7 @@  - + $(TargetFrameworks);net8.0-android34.0 $(TargetFrameworks);net8.0-ios17.0 true @@ -65,33 +65,33 @@ - - - + + + - + - - + + - - + + - - + + - - - - + + + + diff --git a/test/Sentry.Tests/EnricherTests.cs b/test/Sentry.Tests/EnricherTests.cs new file mode 100644 index 0000000000..5fd49cdf02 --- /dev/null +++ b/test/Sentry.Tests/EnricherTests.cs @@ -0,0 +1,24 @@ +#if NET6_0_OR_GREATER +using Sentry.Tests; + +namespace Sentry.Maui.Tests; + +public class EnricherTests +{ + [PlatformFact(Platform.MacOS)] + public void macOS_Platform_Version() + { + var elike = Substitute.For(); + elike.Sdk.Returns(new SdkVersion()); + elike.User = new SentryUser(); + elike.Contexts = new SentryContexts(); + + var enricher = new Enricher(new SentryOptions()); + enricher.Apply(elike); + + var os = elike.Contexts.OperatingSystem; + os.Name.Should().Be("macOS"); + os.Version.Should().Be(Environment.OSVersion.Version.ToString()); + } +} +#endif diff --git a/test/Sentry.Tests/PlatformFact.cs b/test/Sentry.Tests/PlatformFact.cs new file mode 100644 index 0000000000..a43fcb5522 --- /dev/null +++ b/test/Sentry.Tests/PlatformFact.cs @@ -0,0 +1,25 @@ +namespace Sentry.Tests; + +public enum Platform +{ + Windows, + Linux, + MacOS +} + +public class PlatformFact : FactAttribute +{ + public PlatformFact(Platform platform) + { + var actual = platform switch + { + Platform.Windows => OSPlatform.Windows, + Platform.Linux => OSPlatform.Linux, + Platform.MacOS => OSPlatform.OSX, + _ => throw new NotSupportedException() + }; + + if (!RuntimeInformation.IsOSPlatform(actual)) + Skip = "Ignored - Not Platform: " + actual; + } +} From bb10659822ff2450fd2ce9365bce4f60d887dafb Mon Sep 17 00:00:00 2001 From: Ivan Dlugos <6349682+vaind@users.noreply.github.com> Date: Thu, 13 Feb 2025 08:14:49 +0100 Subject: [PATCH 127/363] revert: profiling changes (#3962) * Revert "Skip flaky test (EventPipeSession_ReceivesExpectedCLREvents) (#3949)" This reverts commit 50e5232600e93e9723cc3c8937a2630bbd196171. * Revert "fix: net8 unknown stack trace methods for JIT methods (#3942)" This reverts commit d197cb2f95c63f25992440b3f3e6947d4adbc11e. * Revert "chore: profiling improvements (#3941)" This reverts commit bea672ac57a1f5379f5b893d53a936492e2c80c3. * chore: revert changelog * Verify tests * Windows verify tests --------- Co-authored-by: James Crosswell --- src/Sentry.Profiling/SampleProfileBuilder.cs | 118 ++++++------- src/Sentry.Profiling/SampleProfilerSession.cs | 39 ++--- src/Sentry.Profiling/Sentry.Profiling.csproj | 2 +- ...ionTests.Versioning.DotNet8_0.verified.txt | 4 - ...ionTests.Versioning.DotNet9_0.verified.txt | 4 - ...rTests.LoggingAsync.DotNet8_0.verified.txt | 4 +- ...rTests.LoggingAsync.DotNet9_0.verified.txt | 4 +- ...ests.RecordsEfAsync.DotNet8_0.verified.txt | 8 +- ...ests.RecordsEfAsync.DotNet9_0.verified.txt | 8 +- ...erTests.RecordsEfAsync.Net4_8.verified.txt | 4 +- ...ListenerTests.RecordsSqlAsync.verified.txt | 6 +- .../SamplingTransactionProfilerTests.cs | 37 +--- ...ofileInfo_Serialization_Works.verified.txt | 162 ++++++++++++------ ...s.Profile_Serialization_Works.verified.txt | 162 ++++++++++++------ .../TraceLogProcessorTests.verify.cs | 16 +- ...piApprovalTests.Run.DotNet8_0.verified.txt | 37 +++- ...piApprovalTests.Run.DotNet9_0.verified.txt | 37 +++- .../ApiApprovalTests.Run.Net4_8.verified.txt | 37 +++- 18 files changed, 386 insertions(+), 303 deletions(-) diff --git a/src/Sentry.Profiling/SampleProfileBuilder.cs b/src/Sentry.Profiling/SampleProfileBuilder.cs index 56927be4a9..bdef768d85 100644 --- a/src/Sentry.Profiling/SampleProfileBuilder.cs +++ b/src/Sentry.Profiling/SampleProfileBuilder.cs @@ -16,12 +16,8 @@ internal class SampleProfileBuilder // Output profile being built. public readonly SampleProfile Profile = new(); - // A sparse array that maps from CodeAddressIndex to an index in the output Profile.frames. - private readonly Dictionary _frameIndexesByCodeAddressIndex = new(); - - // A sparse array that maps from MethodIndex to an index in the output Profile.frames. - // This deduplicates frames that map to the same method but have a different CodeAddressIndex. - private readonly Dictionary _frameIndexesByMethodIndex = new(); + // A sparse array that maps from StackSourceFrameIndex to an index in the output Profile.frames. + private readonly Dictionary _frameIndexes = new(); // A dictionary from a CallStackIndex to an index in the output Profile.stacks. private readonly Dictionary _stackIndexes = new(); @@ -89,14 +85,13 @@ private int AddStackTrace(CallStackIndex callstackIndex) { var key = (int)callstackIndex; - if (!_stackIndexes.TryGetValue(key, out var value)) + if (!_stackIndexes.ContainsKey(key)) { Profile.Stacks.Add(CreateStackTrace(callstackIndex)); - value = Profile.Stacks.Count - 1; - _stackIndexes[key] = value; + _stackIndexes[key] = Profile.Stacks.Count - 1; } - return value; + return _stackIndexes[key]; } private Internal.GrowableArray CreateStackTrace(CallStackIndex callstackIndex) @@ -121,71 +116,21 @@ private Internal.GrowableArray CreateStackTrace(CallStackIndex callstackInd return stackTrace; } - private int PushNewFrame(SentryStackFrame frame) - { - Profile.Frames.Add(frame); - return Profile.Frames.Count - 1; - } - /// /// Check if the frame is already stored in the output Profile, or adds it. /// /// The index to the output Profile frames array. private int AddStackFrame(CodeAddressIndex codeAddressIndex) { - if (_frameIndexesByCodeAddressIndex.TryGetValue((int)codeAddressIndex, out var value)) - { - return value; - } + var key = (int)codeAddressIndex; - var methodIndex = _traceLog.CodeAddresses.MethodIndex(codeAddressIndex); - if (methodIndex != MethodIndex.Invalid) - { - value = AddStackFrame(methodIndex); - _frameIndexesByCodeAddressIndex[(int)codeAddressIndex] = value; - return value; - } - - // Fall back if the method info is unknown, see more info on Symbol resolution in - // https://github.com/getsentry/perfview/blob/031250ffb4f9fcadb9263525d6c9f274be19ca51/src/PerfView/SupportFiles/UsersGuide.htm#L7745-L7784 - if (_traceLog.CodeAddresses[codeAddressIndex] is { } codeAddressInfo) + if (!_frameIndexes.ContainsKey(key)) { - var frame = new SentryStackFrame - { - InstructionAddress = (long?)codeAddressInfo.Address, - Module = codeAddressInfo.ModuleFile?.Name, - }; - frame.ConfigureAppFrame(_options); - - return _frameIndexesByCodeAddressIndex[(int)codeAddressIndex] = PushNewFrame(frame); + Profile.Frames.Add(CreateStackFrame(codeAddressIndex)); + _frameIndexes[key] = Profile.Frames.Count - 1; } - // If all else fails, it's a completely unknown frame. - // TODO check this - maybe we would be able to resolve it later in the future? - return PushNewFrame(new SentryStackFrame { InApp = false }); - } - - /// - /// Check if the frame is already stored in the output Profile, or adds it. - /// - /// The index to the output Profile frames array. - private int AddStackFrame(MethodIndex methodIndex) - { - if (_frameIndexesByMethodIndex.TryGetValue((int)methodIndex, out var value)) - { - return value; - } - - var method = _traceLog.CodeAddresses.Methods[methodIndex]; - - var frame = new SentryStackFrame - { - Function = method.FullMethodName, - Module = method.MethodModuleFile?.Name - }; - frame.ConfigureAppFrame(_options); - - return _frameIndexesByMethodIndex[(int)methodIndex] = PushNewFrame(frame); + return _frameIndexes[key]; } /// @@ -196,17 +141,52 @@ private int AddThread(TraceThread thread) { var key = (int)thread.ThreadIndex; - if (!_threadIndexes.TryGetValue(key, out var value)) + if (!_threadIndexes.ContainsKey(key)) { Profile.Threads.Add(new() { Name = thread.ThreadInfo ?? $"Thread {thread.ThreadID}", }); - value = Profile.Threads.Count - 1; - _threadIndexes[key] = value; + _threadIndexes[key] = Profile.Threads.Count - 1; _downsampler.NewThreadAdded(_threadIndexes[key]); } - return value; + return _threadIndexes[key]; + } + + private SentryStackFrame CreateStackFrame(CodeAddressIndex codeAddressIndex) + { + var frame = new SentryStackFrame(); + + var methodIndex = _traceLog.CodeAddresses.MethodIndex(codeAddressIndex); + if (_traceLog.CodeAddresses.Methods[methodIndex] is { } method) + { + frame.Function = method.FullMethodName; + + if (method.MethodModuleFile is { } moduleFile) + { + frame.Module = moduleFile.Name; + } + + frame.ConfigureAppFrame(_options); + } + else + { + // Fall back if the method info is unknown, see more info on Symbol resolution in + // https://github.com/getsentry/perfview/blob/031250ffb4f9fcadb9263525d6c9f274be19ca51/src/PerfView/SupportFiles/UsersGuide.htm#L7745-L7784 + frame.InstructionAddress = (long?)_traceLog.CodeAddresses.Address(codeAddressIndex); + + if (_traceLog.CodeAddresses.ModuleFile(codeAddressIndex) is { } moduleFile) + { + frame.Module = moduleFile.Name; + frame.ConfigureAppFrame(_options); + } + else + { + frame.InApp = false; + } + } + + return frame; } } diff --git a/src/Sentry.Profiling/SampleProfilerSession.cs b/src/Sentry.Profiling/SampleProfilerSession.cs index 6cd9a9242a..3bb606a960 100644 --- a/src/Sentry.Profiling/SampleProfilerSession.cs +++ b/src/Sentry.Profiling/SampleProfilerSession.cs @@ -12,32 +12,31 @@ namespace Sentry.Profiling; internal class SampleProfilerSession : IDisposable { private readonly EventPipeSession _session; + private readonly TraceLogEventSource _eventSource; private readonly SampleProfilerTraceEventParser _sampleEventParser; private readonly IDiagnosticLogger? _logger; private readonly SentryStopwatch _stopwatch; private bool _stopped = false; - private Task _processing; - private SampleProfilerSession(SentryStopwatch stopwatch, EventPipeSession session, TraceLogEventSource eventSource, Task processing, IDiagnosticLogger? logger) + private SampleProfilerSession(SentryStopwatch stopwatch, EventPipeSession session, TraceLogEventSource eventSource, IDiagnosticLogger? logger) { _session = session; _logger = logger; - EventSource = eventSource; - _sampleEventParser = new SampleProfilerTraceEventParser(EventSource); + _eventSource = eventSource; + _sampleEventParser = new SampleProfilerTraceEventParser(_eventSource); _stopwatch = stopwatch; - _processing = processing; } // Exposed only for benchmarks. internal static EventPipeProvider[] Providers = new[] { - new EventPipeProvider(ClrTraceEventParser.ProviderName, EventLevel.Verbose, (long) ( - ClrTraceEventParser.Keywords.Jit - | ClrTraceEventParser.Keywords.NGen - | ClrTraceEventParser.Keywords.Loader - | ClrTraceEventParser.Keywords.Binder - | ClrTraceEventParser.Keywords.JittedMethodILToNativeMap - )), + // Note: all events we need issued by "DotNETRuntime" provider are at "EventLevel.Informational" + // see https://learn.microsoft.com/en-us/dotnet/fundamentals/diagnostics/runtime-events + // TODO replace Keywords.Default with a subset. Currently it is: + // Default = GC | Type | GCHeapSurvivalAndMovement | Binder | Loader | Jit | NGen | SupressNGen + // | StopEnumeration | Security | AppDomainResourceManagement | Exception | Threading | Contention | Stack | JittedMethodILToNativeMap + // | ThreadTransfer | GCHeapAndTypeNames | Codesymbols | Compilation, + new EventPipeProvider(ClrTraceEventParser.ProviderName, EventLevel.Informational, (long) ClrTraceEventParser.Keywords.Default), new EventPipeProvider(SampleProfilerTraceEventParser.ProviderName, EventLevel.Informational), // new EventPipeProvider(TplEtwProviderTraceEventParser.ProviderName, EventLevel.Informational, (long) TplEtwProviderTraceEventParser.Keywords.Default) }; @@ -47,14 +46,11 @@ private SampleProfilerSession(SentryStopwatch stopwatch, EventPipeSession sessio // need a large buffer if we're connecting righ away. Leaving it too large increases app memory usage. internal static int CircularBufferMB = 16; - // Exposed for tests - internal TraceLogEventSource EventSource { get; } - public SampleProfilerTraceEventParser SampleEventParser => _sampleEventParser; public TimeSpan Elapsed => _stopwatch.Elapsed; - public TraceLog TraceLog => EventSource.TraceLog; + public TraceLog TraceLog => _eventSource.TraceLog; // default is false, set 1 for true. private static int _throwOnNextStartupForTests = 0; @@ -90,7 +86,7 @@ public static SampleProfilerSession StartNew(IDiagnosticLogger? logger = null) var eventSource = TraceLog.CreateFromEventPipeSession(session, TraceLog.EventPipeRundownConfiguration.Enable(client)); // Process() blocks until the session is stopped so we need to run it on a separate thread. - var processing = Task.Factory.StartNew(eventSource.Process, TaskCreationOptions.LongRunning) + Task.Factory.StartNew(eventSource.Process, TaskCreationOptions.LongRunning) .ContinueWith(_ => { if (_.Exception?.InnerException is { } e) @@ -99,7 +95,7 @@ public static SampleProfilerSession StartNew(IDiagnosticLogger? logger = null) } }, TaskContinuationOptions.OnlyOnFaulted); - return new SampleProfilerSession(stopWatch, session, eventSource, processing, logger); + return new SampleProfilerSession(stopWatch, session, eventSource, logger); } catch (Exception ex) { @@ -112,7 +108,7 @@ public async Task WaitForFirstEventAsync(CancellationToken cancellationToken = d { var tcs = new TaskCompletionSource(); var cb = (TraceEvent _) => { tcs.TrySetResult(); }; - EventSource.AllEvents += cb; + _eventSource.AllEvents += cb; try { // Wait for the first event to be processed. @@ -120,7 +116,7 @@ public async Task WaitForFirstEventAsync(CancellationToken cancellationToken = d } finally { - EventSource.AllEvents -= cb; + _eventSource.AllEvents -= cb; } } @@ -132,9 +128,8 @@ public void Stop() { _stopped = true; _session.Stop(); - _processing.Wait(); _session.Dispose(); - EventSource.Dispose(); + _eventSource.Dispose(); } catch (Exception ex) { diff --git a/src/Sentry.Profiling/Sentry.Profiling.csproj b/src/Sentry.Profiling/Sentry.Profiling.csproj index 9413ef51f0..c453876242 100644 --- a/src/Sentry.Profiling/Sentry.Profiling.csproj +++ b/src/Sentry.Profiling/Sentry.Profiling.csproj @@ -14,7 +14,7 @@ - + diff --git a/test/Sentry.AspNetCore.Tests/WebIntegrationTests.Versioning.DotNet8_0.verified.txt b/test/Sentry.AspNetCore.Tests/WebIntegrationTests.Versioning.DotNet8_0.verified.txt index f60140240c..019cc1fb2d 100644 --- a/test/Sentry.AspNetCore.Tests/WebIntegrationTests.Versioning.DotNet8_0.verified.txt +++ b/test/Sentry.AspNetCore.Tests/WebIntegrationTests.Versioning.DotNet8_0.verified.txt @@ -73,10 +73,6 @@ Category: Microsoft.AspNetCore.Routing.EndpointMiddleware } ], - Extra: { - http.request.method: GET, - http.response.status_code: 200 - }, Tags: { ActionId: Guid_2, ActionName: Sentry.AspNetCore.Tests.WebIntegrationTests+VersionController.Method (Sentry.AspNetCore.Tests), diff --git a/test/Sentry.AspNetCore.Tests/WebIntegrationTests.Versioning.DotNet9_0.verified.txt b/test/Sentry.AspNetCore.Tests/WebIntegrationTests.Versioning.DotNet9_0.verified.txt index f60140240c..019cc1fb2d 100644 --- a/test/Sentry.AspNetCore.Tests/WebIntegrationTests.Versioning.DotNet9_0.verified.txt +++ b/test/Sentry.AspNetCore.Tests/WebIntegrationTests.Versioning.DotNet9_0.verified.txt @@ -73,10 +73,6 @@ Category: Microsoft.AspNetCore.Routing.EndpointMiddleware } ], - Extra: { - http.request.method: GET, - http.response.status_code: 200 - }, Tags: { ActionId: Guid_2, ActionName: Sentry.AspNetCore.Tests.WebIntegrationTests+VersionController.Method (Sentry.AspNetCore.Tests), diff --git a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.LoggingAsync.DotNet8_0.verified.txt b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.LoggingAsync.DotNet8_0.verified.txt index 298823718c..348b185b6f 100644 --- a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.LoggingAsync.DotNet8_0.verified.txt +++ b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.LoggingAsync.DotNet8_0.verified.txt @@ -27,7 +27,7 @@ Description: SqlListenerTests.verify_LoggingAsync, Status: Ok, IsSampled: true, - Extra: { + Data: { bytes_sent : 376, db.connection_id: Guid_2, db.name: SqlListenerTests.verify_LoggingAsync, @@ -49,7 +49,7 @@ VALUES (@p0); , Status: Ok, IsSampled: true, - Extra: { + Data: { db.connection_id: Guid_2, db.name: SqlListenerTests.verify_LoggingAsync, db.operation_id: Guid_4, diff --git a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.LoggingAsync.DotNet9_0.verified.txt b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.LoggingAsync.DotNet9_0.verified.txt index 298823718c..348b185b6f 100644 --- a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.LoggingAsync.DotNet9_0.verified.txt +++ b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.LoggingAsync.DotNet9_0.verified.txt @@ -27,7 +27,7 @@ Description: SqlListenerTests.verify_LoggingAsync, Status: Ok, IsSampled: true, - Extra: { + Data: { bytes_sent : 376, db.connection_id: Guid_2, db.name: SqlListenerTests.verify_LoggingAsync, @@ -49,7 +49,7 @@ VALUES (@p0); , Status: Ok, IsSampled: true, - Extra: { + Data: { db.connection_id: Guid_2, db.name: SqlListenerTests.verify_LoggingAsync, db.operation_id: Guid_4, diff --git a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.DotNet8_0.verified.txt b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.DotNet8_0.verified.txt index aa6dedca8c..58b52da963 100644 --- a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.DotNet8_0.verified.txt +++ b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.DotNet8_0.verified.txt @@ -61,7 +61,7 @@ Description: SqlListenerTests.verify_RecordsEfAsync, Status: Ok, IsSampled: true, - Extra: { + Data: { bytes_received: 225, bytes_sent : 570, db.connection_id: Guid_2, @@ -84,7 +84,7 @@ VALUES (@p0); , Status: Ok, IsSampled: true, - Extra: { + Data: { db.connection_id: Guid_2, db.name: SqlListenerTests.verify_RecordsEfAsync, db.operation_id: Guid_4, @@ -97,7 +97,7 @@ VALUES (@p0); Description: 'DbSet()', Status: Ok, IsSampled: true, - Extra: { + Data: { db.system: mssql } }, @@ -109,7 +109,7 @@ SELECT [t].[Id], [t].[Property] FROM [TestEntities] AS [t], Status: Ok, IsSampled: true, - Extra: { + Data: { db.connection_id: Guid_2, db.name: SqlListenerTests.verify_RecordsEfAsync, db.operation_id: Guid_5, diff --git a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.DotNet9_0.verified.txt b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.DotNet9_0.verified.txt index aa6dedca8c..58b52da963 100644 --- a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.DotNet9_0.verified.txt +++ b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.DotNet9_0.verified.txt @@ -61,7 +61,7 @@ Description: SqlListenerTests.verify_RecordsEfAsync, Status: Ok, IsSampled: true, - Extra: { + Data: { bytes_received: 225, bytes_sent : 570, db.connection_id: Guid_2, @@ -84,7 +84,7 @@ VALUES (@p0); , Status: Ok, IsSampled: true, - Extra: { + Data: { db.connection_id: Guid_2, db.name: SqlListenerTests.verify_RecordsEfAsync, db.operation_id: Guid_4, @@ -97,7 +97,7 @@ VALUES (@p0); Description: 'DbSet()', Status: Ok, IsSampled: true, - Extra: { + Data: { db.system: mssql } }, @@ -109,7 +109,7 @@ SELECT [t].[Id], [t].[Property] FROM [TestEntities] AS [t], Status: Ok, IsSampled: true, - Extra: { + Data: { db.connection_id: Guid_2, db.name: SqlListenerTests.verify_RecordsEfAsync, db.operation_id: Guid_5, diff --git a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.Net4_8.verified.txt b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.Net4_8.verified.txt index 96826b04ae..57227d296e 100644 --- a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.Net4_8.verified.txt +++ b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.Net4_8.verified.txt @@ -67,7 +67,7 @@ FROM [TestEntities] WHERE @@ROWCOUNT = 1 AND [Id] = scope_identity();, Status: Ok, IsSampled: true, - Extra: { + Data: { db.command_id: Guid_2, db.connection_id: Guid_3, db.name: SqlListenerTests.verify_RecordsEfAsync, @@ -83,7 +83,7 @@ SELECT [t].[Id], [t].[Property] FROM [TestEntities] AS [t], Status: Ok, IsSampled: true, - Extra: { + Data: { db.command_id: Guid_4, db.connection_id: Guid_5, db.name: SqlListenerTests.verify_RecordsEfAsync, diff --git a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsSqlAsync.verified.txt b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsSqlAsync.verified.txt index 7d7654f67d..0172e27d4e 100644 --- a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsSqlAsync.verified.txt +++ b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsSqlAsync.verified.txt @@ -61,7 +61,7 @@ Description: SqlListenerTests.verify_RecordsSqlAsync, Status: Ok, IsSampled: true, - Extra: { + Data: { bytes_received: 167, bytes_sent : 536, db.connection_id: Guid_2, @@ -78,7 +78,7 @@ Description: insert into MyTable (Value) values (@value);, Status: Ok, IsSampled: true, - Extra: { + Data: { db.connection_id: Guid_2, db.name: SqlListenerTests.verify_RecordsSqlAsync, db.operation_id: Guid_4, @@ -91,7 +91,7 @@ Description: select Value from MyTable where Value = @value, Status: Ok, IsSampled: true, - Extra: { + Data: { db.connection_id: Guid_2, db.name: SqlListenerTests.verify_RecordsSqlAsync, db.operation_id: Guid_5, diff --git a/test/Sentry.Profiling.Tests/SamplingTransactionProfilerTests.cs b/test/Sentry.Profiling.Tests/SamplingTransactionProfilerTests.cs index 6f9ff84191..12bdcc5995 100644 --- a/test/Sentry.Profiling.Tests/SamplingTransactionProfilerTests.cs +++ b/test/Sentry.Profiling.Tests/SamplingTransactionProfilerTests.cs @@ -1,5 +1,4 @@ -using Microsoft.Diagnostics.Tracing; -using Microsoft.Diagnostics.Tracing.Parsers.Clr; +using System.IO.Abstractions.TestingHelpers; using Sentry.Internal.Http; namespace Sentry.Profiling.Tests; @@ -181,40 +180,6 @@ public async Task Profiler_AfterTimeout_Stops() } } - [SkippableFact] - public async Task EventPipeSession_ReceivesExpectedCLREvents() - { - Skip.If(TestEnvironment.IsGitHubActions, "Flaky on CI"); - - var session = SampleProfilerSession.StartNew(_testOutputLogger); - using (session) - { - var eventsReceived = new HashSet(); - session!.EventSource.Clr.All += (TraceEvent ev) => eventsReceived.Add(ev.EventName); - - var loadedMethods = new HashSet(); - session!.EventSource.Clr.MethodLoadVerbose += (MethodLoadUnloadVerboseTraceData ev) => loadedMethods.Add(ev.MethodName); - - await session.WaitForFirstEventAsync(CancellationToken.None); - var limitMs = 50; - var sut = new SamplingTransactionProfiler(_testSentryOptions, session, limitMs, CancellationToken.None); - RunForMs(limitMs * 5); - MethodToBeLoaded(100); - RunForMs(limitMs * 5); - sut.Finish(); - - Assert.Contains("Method/LoadVerbose", eventsReceived); - Assert.Contains("Method/ILToNativeMap", eventsReceived); - - Assert.Contains("MethodToBeLoaded", loadedMethods); - } - } - - private static long MethodToBeLoaded(int n) - { - return -n; - } - [SkippableTheory] [InlineData(true)] [InlineData(false)] diff --git a/test/Sentry.Profiling.Tests/TraceLogProcessorTests.ProfileInfo_Serialization_Works.verified.txt b/test/Sentry.Profiling.Tests/TraceLogProcessorTests.ProfileInfo_Serialization_Works.verified.txt index 481fcf2e42..714859c86d 100644 --- a/test/Sentry.Profiling.Tests/TraceLogProcessorTests.ProfileInfo_Serialization_Works.verified.txt +++ b/test/Sentry.Profiling.Tests/TraceLogProcessorTests.ProfileInfo_Serialization_Works.verified.txt @@ -107,12 +107,11 @@ 36 ], [ - 17 + 37 ], [ 0, 1, - 37, 38, 39, 40, @@ -120,37 +119,35 @@ 42, 43, 44, - 21, + 45, + 46, 22 ], [ - 45, - 46, 47, 48, 49, 50, - 17 - ], - [ 51, 52, + 37 + ], + [ 53, 54, 55, - 17 - ], - [ 56, 57, - 58, - 59, - 17 + 37 ], [ + 58, + 59, 60, 61, - 62, + 62 + ], + [ 63, 64, 65, @@ -158,55 +155,48 @@ 67, 68, 69, - 56, - 57, - 58, - 59, - 17 - ], - [ 70, 71, 72, - 73, - 74, - 69, - 56, - 57, 58, 59, - 17 + 60, + 61, + 62 ], [ - 75, + 73, 74, - 69, - 56, - 57, + 75, + 76, + 77, + 78, 58, 59, - 17 + 60, + 61, + 62 ], [ - 56, - 57, + 79, + 80, + 78, 58, 59, - 17 + 60, + 61, + 62 ], [ - 76, - 77, - 78, - 79, - 80, 81, - 82, - 83, 59, - 17 + 60, + 61, + 62 ], [ + 82, + 83, 84, 85, 86, @@ -214,22 +204,31 @@ 88, 89, 90, + 62 + ], + [ 91, 92, - 77, - 78, - 79, - 80, - 81, - 82, - 83, - 59, - 17 + 93, + 94, + 95, + 96, + 97, + 98, + 99, + 100, + 84, + 85, + 86, + 87, + 88, + 89, + 90, + 62 ], [ 0, 1, - 37, 38, 39, 40, @@ -237,14 +236,15 @@ 42, 43, 44, - 21, + 45, + 46, 22 ], [ 18, 19, 20, - 21, + 101, 22 ] ], @@ -340,6 +340,7 @@ in_app: true }, { + in_app: false, instruction_addr: 0x7ff947fd8378 }, { @@ -432,6 +433,11 @@ module: System.Private.CoreLib.il, in_app: false }, + { + function: Aura.UI.Gallery.NetCore.Program.Main(class System.String[]), + module: Aura.UI.Gallery.NetCore, + in_app: true + }, { function: System.IO.Stream+<>c.b__40_0(class System.Object), module: System.Private.CoreLib.il, @@ -472,6 +478,11 @@ module: System.Private.CoreLib.il, in_app: false }, + { + function: System.Threading.PortableThreadPool+WorkerThread.WorkerThreadStart(), + module: System.Private.CoreLib.il, + in_app: false + }, { function: System.Collections.Concurrent.ConcurrentDictionary`2[System.__Canon,System.IntPtr].TryAddInternal(!0,value class System.Nullable`1,!1,bool,bool,!1&), module: System.Collections.Concurrent.il, @@ -498,6 +509,7 @@ in_app: true }, { + in_app: false, instruction_addr: 0x7ff94be7aed3 }, { @@ -545,6 +557,11 @@ module: Avalonia.Controls, in_app: true }, + { + function: Aura.UI.Gallery.NetCore.Program.Main(class System.String[]), + module: Aura.UI.Gallery.NetCore, + in_app: true + }, { function: Avalonia.Animation.Animation..cctor(), module: Avalonia.Animation, @@ -620,11 +637,26 @@ module: Avalonia.Controls, in_app: true }, + { + function: Avalonia.Controls.Primitives.TemplatedControl..cctor(), + module: Avalonia.Controls, + in_app: true + }, { function: System.Reactive.Linq.Observable.Merge(class System.IObservable`1[]), module: system.reactive, in_app: false }, + { + function: Avalonia.Controls.TextBlock..cctor(), + module: Avalonia.Controls, + in_app: true + }, + { + function: Avalonia.Controls.Window..cctor(), + module: Avalonia.Controls, + in_app: true + }, { function: Avalonia.Win32.Win32Platform.SetDpiAwareness(), module: avalonia.win32, @@ -665,6 +697,11 @@ module: Avalonia.Controls, in_app: true }, + { + function: Avalonia.ClassicDesktopStyleApplicationLifetimeExtensions.StartWithClassicDesktopLifetime(!!0,class System.String[],value class Avalonia.Controls.ShutdownMode), + module: Avalonia.Controls, + in_app: true + }, { function: System.Drawing.SafeNativeMethods+Gdip..cctor(), module: system.drawing.common, @@ -701,12 +738,23 @@ in_app: true }, { + in_app: false, instruction_addr: 0x7ffa0d468281 }, { function: Avalonia.Win32.Win32Platform.CreateMessageWindow(), module: avalonia.win32, in_app: true + }, + { + function: Avalonia.Win32.Win32Platform..ctor(), + module: avalonia.win32, + in_app: true + }, + { + function: System.Threading.PortableThreadPool+WorkerThread.WorkerThreadStart(), + module: System.Private.CoreLib.il, + in_app: false } ], samples: [ @@ -1022,4 +1070,4 @@ } ] } -} +} \ No newline at end of file diff --git a/test/Sentry.Profiling.Tests/TraceLogProcessorTests.Profile_Serialization_Works.verified.txt b/test/Sentry.Profiling.Tests/TraceLogProcessorTests.Profile_Serialization_Works.verified.txt index 7720ab6755..6eb35f3c22 100644 --- a/test/Sentry.Profiling.Tests/TraceLogProcessorTests.Profile_Serialization_Works.verified.txt +++ b/test/Sentry.Profiling.Tests/TraceLogProcessorTests.Profile_Serialization_Works.verified.txt @@ -84,12 +84,11 @@ 36 ], [ - 17 + 37 ], [ 0, 1, - 37, 38, 39, 40, @@ -97,37 +96,35 @@ 42, 43, 44, - 21, + 45, + 46, 22 ], [ - 45, - 46, 47, 48, 49, 50, - 17 - ], - [ 51, 52, + 37 + ], + [ 53, 54, 55, - 17 - ], - [ 56, 57, - 58, - 59, - 17 + 37 ], [ + 58, + 59, 60, 61, - 62, + 62 + ], + [ 63, 64, 65, @@ -135,55 +132,48 @@ 67, 68, 69, - 56, - 57, - 58, - 59, - 17 - ], - [ 70, 71, 72, - 73, - 74, - 69, - 56, - 57, 58, 59, - 17 + 60, + 61, + 62 ], [ - 75, + 73, 74, - 69, - 56, - 57, + 75, + 76, + 77, + 78, 58, 59, - 17 + 60, + 61, + 62 ], [ - 56, - 57, + 79, + 80, + 78, 58, 59, - 17 + 60, + 61, + 62 ], [ - 76, - 77, - 78, - 79, - 80, 81, - 82, - 83, 59, - 17 + 60, + 61, + 62 ], [ + 82, + 83, 84, 85, 86, @@ -191,22 +181,31 @@ 88, 89, 90, + 62 + ], + [ 91, 92, - 77, - 78, - 79, - 80, - 81, - 82, - 83, - 59, - 17 + 93, + 94, + 95, + 96, + 97, + 98, + 99, + 100, + 84, + 85, + 86, + 87, + 88, + 89, + 90, + 62 ], [ 0, 1, - 37, 38, 39, 40, @@ -214,14 +213,15 @@ 42, 43, 44, - 21, + 45, + 46, 22 ], [ 18, 19, 20, - 21, + 101, 22 ] ], @@ -317,6 +317,7 @@ in_app: true }, { + in_app: false, instruction_addr: 0x7ff947fd8378 }, { @@ -409,6 +410,11 @@ module: System.Private.CoreLib.il, in_app: false }, + { + function: Aura.UI.Gallery.NetCore.Program.Main(class System.String[]), + module: Aura.UI.Gallery.NetCore, + in_app: true + }, { function: System.IO.Stream+<>c.b__40_0(class System.Object), module: System.Private.CoreLib.il, @@ -449,6 +455,11 @@ module: System.Private.CoreLib.il, in_app: false }, + { + function: System.Threading.PortableThreadPool+WorkerThread.WorkerThreadStart(), + module: System.Private.CoreLib.il, + in_app: false + }, { function: System.Collections.Concurrent.ConcurrentDictionary`2[System.__Canon,System.IntPtr].TryAddInternal(!0,value class System.Nullable`1,!1,bool,bool,!1&), module: System.Collections.Concurrent.il, @@ -475,6 +486,7 @@ in_app: true }, { + in_app: false, instruction_addr: 0x7ff94be7aed3 }, { @@ -522,6 +534,11 @@ module: Avalonia.Controls, in_app: true }, + { + function: Aura.UI.Gallery.NetCore.Program.Main(class System.String[]), + module: Aura.UI.Gallery.NetCore, + in_app: true + }, { function: Avalonia.Animation.Animation..cctor(), module: Avalonia.Animation, @@ -597,11 +614,26 @@ module: Avalonia.Controls, in_app: true }, + { + function: Avalonia.Controls.Primitives.TemplatedControl..cctor(), + module: Avalonia.Controls, + in_app: true + }, { function: System.Reactive.Linq.Observable.Merge(class System.IObservable`1[]), module: system.reactive, in_app: false }, + { + function: Avalonia.Controls.TextBlock..cctor(), + module: Avalonia.Controls, + in_app: true + }, + { + function: Avalonia.Controls.Window..cctor(), + module: Avalonia.Controls, + in_app: true + }, { function: Avalonia.Win32.Win32Platform.SetDpiAwareness(), module: avalonia.win32, @@ -642,6 +674,11 @@ module: Avalonia.Controls, in_app: true }, + { + function: Avalonia.ClassicDesktopStyleApplicationLifetimeExtensions.StartWithClassicDesktopLifetime(!!0,class System.String[],value class Avalonia.Controls.ShutdownMode), + module: Avalonia.Controls, + in_app: true + }, { function: System.Drawing.SafeNativeMethods+Gdip..cctor(), module: system.drawing.common, @@ -678,12 +715,23 @@ in_app: true }, { + in_app: false, instruction_addr: 0x7ffa0d468281 }, { function: Avalonia.Win32.Win32Platform.CreateMessageWindow(), module: avalonia.win32, in_app: true + }, + { + function: Avalonia.Win32.Win32Platform..ctor(), + module: avalonia.win32, + in_app: true + }, + { + function: System.Threading.PortableThreadPool+WorkerThread.WorkerThreadStart(), + module: System.Private.CoreLib.il, + in_app: false } ], samples: [ @@ -998,4 +1046,4 @@ stack_id: 18 } ] -} +} \ No newline at end of file diff --git a/test/Sentry.Profiling.Tests/TraceLogProcessorTests.verify.cs b/test/Sentry.Profiling.Tests/TraceLogProcessorTests.verify.cs index 7a01150aec..5b4307e951 100644 --- a/test/Sentry.Profiling.Tests/TraceLogProcessorTests.verify.cs +++ b/test/Sentry.Profiling.Tests/TraceLogProcessorTests.verify.cs @@ -20,24 +20,16 @@ public TraceLogProcessorTests(ITestOutputHelper output) // [Fact] // public void ManualDebugging() // { - // var etlFilePath = "T:/src/sentry-dotnet/samples/Sentry.Samples.Console.Profiling/Sentry.Samples.Console.Profiling.exe_20250124_144756.nettrace"; + // var etlFilePath = "C:/dev/Aura.UI/profile.nettrace"; // var etlxFilePath = Path.ChangeExtension(etlFilePath, ".etlx"); // if (!File.Exists(etlxFilePath)) // { // TraceLog.CreateFromEventTraceLogFile(etlFilePath, etlxFilePath); // } // using var eventLog = new TraceLog(etlxFilePath); - // var builder = new SampleProfileBuilder(new(), eventLog); - // foreach (var event_ in eventLog.Events) - // { - // if (event_.ProviderGuid == SampleProfilerTraceEventParser.ProviderGuid) - // { - // builder.AddSample(event_, event_.TimeStampRelativeMSec); - // } - // } - - // var json = builder.Profile.ToJsonString(_testOutputLogger); - // VerifyJson(json); + // var processor = new TraceLogProcessor(new(), eventLog); + // var profile = processor.Process(CancellationToken.None); + // var json = profile.ToJsonString(_testOutputLogger); // } private SampleProfile BuilProfile(TraceLogEventSource eventSource) diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt index 843b28f7e4..2a4a0e61a3 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt @@ -190,6 +190,11 @@ namespace Sentry Sentry.SentryUser User { get; set; } void AddBreadcrumb(Sentry.Breadcrumb breadcrumb); } + public interface IHasData + { + System.Collections.Generic.IReadOnlyDictionary Data { get; } + void SetData(string key, object? value); + } public interface IHasExtra { System.Collections.Generic.IReadOnlyDictionary Extra { get; } @@ -269,7 +274,7 @@ namespace Sentry { Sentry.SentryUser? Create(); } - public interface ISpan : Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpanData, Sentry.Protocol.ITraceContext + public interface ISpan : Sentry.IHasData, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpanData, Sentry.Protocol.ITraceContext { new string? Description { get; set; } new string Operation { get; set; } @@ -280,7 +285,7 @@ namespace Sentry void Finish(System.Exception exception, Sentry.SpanStatus status); Sentry.ISpan StartChild(string operation); } - public interface ISpanData : Sentry.IHasExtra, Sentry.IHasTags, Sentry.Protocol.ITraceContext + public interface ISpanData : Sentry.IHasData, Sentry.IHasExtra, Sentry.IHasTags, Sentry.Protocol.ITraceContext { System.DateTimeOffset? EndTimestamp { get; } bool IsFinished { get; } @@ -295,11 +300,11 @@ namespace Sentry string Name { get; } Sentry.TransactionNameSource NameSource { get; } } - public interface ITransactionData : Sentry.IEventLike, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.Protocol.ITraceContext + public interface ITransactionData : Sentry.IEventLike, Sentry.IHasData, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.Protocol.ITraceContext { string? Platform { get; set; } } - public interface ITransactionTracer : Sentry.IEventLike, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpan, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.Protocol.ITraceContext + public interface ITransactionTracer : Sentry.IEventLike, Sentry.IHasData, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpan, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.Protocol.ITraceContext { new bool? IsParentSampled { get; set; } new string Name { get; set; } @@ -849,12 +854,14 @@ namespace Sentry public string? UserAgent { get; } public void ReportError() { } } - public class SentrySpan : Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISentryJsonSerializable, Sentry.ISpanData, Sentry.Protocol.ITraceContext + public class SentrySpan : Sentry.IHasData, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISentryJsonSerializable, Sentry.ISpanData, Sentry.Protocol.ITraceContext { public SentrySpan(Sentry.ISpan tracer) { } public SentrySpan(Sentry.SpanId? parentSpanId, string operation) { } + public System.Collections.Generic.IReadOnlyDictionary Data { get; } public string? Description { get; set; } public System.DateTimeOffset? EndTimestamp { get; } + [System.Obsolete("Use SetData")] public System.Collections.Generic.IReadOnlyDictionary Extra { get; } public bool IsFinished { get; } public bool? IsSampled { get; } @@ -868,6 +875,8 @@ namespace Sentry public System.Collections.Generic.IReadOnlyDictionary Tags { get; } public Sentry.SentryId TraceId { get; } public Sentry.SentryTraceHeader GetTraceHeader() { } + public void SetData(string key, object? value) { } + [System.Obsolete("Use Data")] public void SetExtra(string key, object? value) { } public void SetMeasurement(string name, Sentry.Protocol.Measurement measurement) { } public void SetTag(string key, string value) { } @@ -930,18 +939,20 @@ namespace Sentry public override string ToString() { } public static Sentry.SentryTraceHeader? Parse(string value) { } } - public class SentryTransaction : Sentry.IEventLike, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISentryJsonSerializable, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.Protocol.ITraceContext + public class SentryTransaction : Sentry.IEventLike, Sentry.IHasData, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISentryJsonSerializable, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.Protocol.ITraceContext { public SentryTransaction(Sentry.ITransactionTracer tracer) { } public SentryTransaction(string name, string operation) { } public SentryTransaction(string name, string operation, Sentry.TransactionNameSource nameSource) { } public System.Collections.Generic.IReadOnlyCollection Breadcrumbs { get; } public Sentry.SentryContexts Contexts { get; set; } + public System.Collections.Generic.IReadOnlyDictionary Data { get; } public string? Description { get; set; } public string? Distribution { get; set; } public System.DateTimeOffset? EndTimestamp { get; } public string? Environment { get; set; } public Sentry.SentryId EventId { get; } + [System.Obsolete("Use Data")] public System.Collections.Generic.IReadOnlyDictionary Extra { get; } public System.Collections.Generic.IReadOnlyList Fingerprint { get; set; } public bool IsFinished { get; } @@ -968,6 +979,8 @@ namespace Sentry public Sentry.SentryUser User { get; set; } public void AddBreadcrumb(Sentry.Breadcrumb breadcrumb) { } public Sentry.SentryTraceHeader GetTraceHeader() { } + public void SetData(string key, object? value) { } + [System.Obsolete("Use SetData")] public void SetExtra(string key, object? value) { } public void SetMeasurement(string name, Sentry.Protocol.Measurement measurement) { } public void SetTag(string key, string value) { } @@ -1078,9 +1091,10 @@ namespace Sentry OutOfRange = 15, DataLoss = 16, } - public class SpanTracer : Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpan, Sentry.ISpanData, Sentry.Protocol.ITraceContext + public class SpanTracer : Sentry.IHasData, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpan, Sentry.ISpanData, Sentry.Protocol.ITraceContext { public SpanTracer(Sentry.IHub hub, Sentry.TransactionTracer transaction, Sentry.SpanId? parentSpanId, Sentry.SentryId traceId, string operation) { } + public System.Collections.Generic.IReadOnlyDictionary Data { get; } public string? Description { get; set; } public System.DateTimeOffset? EndTimestamp { get; } public System.Collections.Generic.IReadOnlyDictionary Extra { get; } @@ -1100,6 +1114,7 @@ namespace Sentry public void Finish(System.Exception exception) { } public void Finish(System.Exception exception, Sentry.SpanStatus status) { } public Sentry.SentryTraceHeader GetTraceHeader() { } + public void SetData(string key, object? value) { } public void SetExtra(string key, object? value) { } public void SetMeasurement(string name, Sentry.Protocol.Measurement measurement) { } public void SetTag(string key, string value) { } @@ -1155,15 +1170,17 @@ namespace Sentry public System.Collections.Generic.IReadOnlyDictionary CustomSamplingContext { get; } public Sentry.ITransactionContext TransactionContext { get; } } - public class TransactionTracer : Sentry.IEventLike, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpan, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.ITransactionTracer, Sentry.Protocol.ITraceContext + public class TransactionTracer : Sentry.IEventLike, Sentry.IHasData, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpan, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.ITransactionTracer, Sentry.Protocol.ITraceContext { public TransactionTracer(Sentry.IHub hub, Sentry.ITransactionContext context) { } public System.Collections.Generic.IReadOnlyCollection Breadcrumbs { get; } public Sentry.SentryContexts Contexts { get; set; } + public System.Collections.Generic.IReadOnlyDictionary Data { get; } public string? Description { get; set; } public string? Distribution { get; set; } public System.DateTimeOffset? EndTimestamp { get; } public string? Environment { get; set; } + [System.Obsolete("Use Data")] public System.Collections.Generic.IReadOnlyDictionary Extra { get; } public System.Collections.Generic.IReadOnlyList Fingerprint { get; set; } public bool IsFinished { get; } @@ -1195,6 +1212,8 @@ namespace Sentry public void Finish(System.Exception exception, Sentry.SpanStatus status) { } public Sentry.ISpan? GetLastActiveSpan() { } public Sentry.SentryTraceHeader GetTraceHeader() { } + public void SetData(string key, object? value) { } + [System.Obsolete("Use SetData")] public void SetExtra(string key, object? value) { } public void SetMeasurement(string name, Sentry.Protocol.Measurement measurement) { } public void SetTag(string key, string value) { } @@ -1749,6 +1768,7 @@ namespace Sentry.Protocol { public const string Type = "trace"; public Trace() { } + public System.Collections.Generic.IReadOnlyDictionary Data { get; } public string? Description { get; set; } public bool? IsSampled { get; } public string Operation { get; set; } @@ -1757,6 +1777,7 @@ namespace Sentry.Protocol public Sentry.SpanId SpanId { get; set; } public Sentry.SpanStatus? Status { get; set; } public Sentry.SentryId TraceId { get; set; } + public void SetData(string key, object? value) { } public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } public static Sentry.Protocol.Trace FromJson(System.Text.Json.JsonElement json) { } } diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt index 843b28f7e4..2a4a0e61a3 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt @@ -190,6 +190,11 @@ namespace Sentry Sentry.SentryUser User { get; set; } void AddBreadcrumb(Sentry.Breadcrumb breadcrumb); } + public interface IHasData + { + System.Collections.Generic.IReadOnlyDictionary Data { get; } + void SetData(string key, object? value); + } public interface IHasExtra { System.Collections.Generic.IReadOnlyDictionary Extra { get; } @@ -269,7 +274,7 @@ namespace Sentry { Sentry.SentryUser? Create(); } - public interface ISpan : Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpanData, Sentry.Protocol.ITraceContext + public interface ISpan : Sentry.IHasData, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpanData, Sentry.Protocol.ITraceContext { new string? Description { get; set; } new string Operation { get; set; } @@ -280,7 +285,7 @@ namespace Sentry void Finish(System.Exception exception, Sentry.SpanStatus status); Sentry.ISpan StartChild(string operation); } - public interface ISpanData : Sentry.IHasExtra, Sentry.IHasTags, Sentry.Protocol.ITraceContext + public interface ISpanData : Sentry.IHasData, Sentry.IHasExtra, Sentry.IHasTags, Sentry.Protocol.ITraceContext { System.DateTimeOffset? EndTimestamp { get; } bool IsFinished { get; } @@ -295,11 +300,11 @@ namespace Sentry string Name { get; } Sentry.TransactionNameSource NameSource { get; } } - public interface ITransactionData : Sentry.IEventLike, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.Protocol.ITraceContext + public interface ITransactionData : Sentry.IEventLike, Sentry.IHasData, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.Protocol.ITraceContext { string? Platform { get; set; } } - public interface ITransactionTracer : Sentry.IEventLike, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpan, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.Protocol.ITraceContext + public interface ITransactionTracer : Sentry.IEventLike, Sentry.IHasData, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpan, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.Protocol.ITraceContext { new bool? IsParentSampled { get; set; } new string Name { get; set; } @@ -849,12 +854,14 @@ namespace Sentry public string? UserAgent { get; } public void ReportError() { } } - public class SentrySpan : Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISentryJsonSerializable, Sentry.ISpanData, Sentry.Protocol.ITraceContext + public class SentrySpan : Sentry.IHasData, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISentryJsonSerializable, Sentry.ISpanData, Sentry.Protocol.ITraceContext { public SentrySpan(Sentry.ISpan tracer) { } public SentrySpan(Sentry.SpanId? parentSpanId, string operation) { } + public System.Collections.Generic.IReadOnlyDictionary Data { get; } public string? Description { get; set; } public System.DateTimeOffset? EndTimestamp { get; } + [System.Obsolete("Use SetData")] public System.Collections.Generic.IReadOnlyDictionary Extra { get; } public bool IsFinished { get; } public bool? IsSampled { get; } @@ -868,6 +875,8 @@ namespace Sentry public System.Collections.Generic.IReadOnlyDictionary Tags { get; } public Sentry.SentryId TraceId { get; } public Sentry.SentryTraceHeader GetTraceHeader() { } + public void SetData(string key, object? value) { } + [System.Obsolete("Use Data")] public void SetExtra(string key, object? value) { } public void SetMeasurement(string name, Sentry.Protocol.Measurement measurement) { } public void SetTag(string key, string value) { } @@ -930,18 +939,20 @@ namespace Sentry public override string ToString() { } public static Sentry.SentryTraceHeader? Parse(string value) { } } - public class SentryTransaction : Sentry.IEventLike, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISentryJsonSerializable, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.Protocol.ITraceContext + public class SentryTransaction : Sentry.IEventLike, Sentry.IHasData, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISentryJsonSerializable, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.Protocol.ITraceContext { public SentryTransaction(Sentry.ITransactionTracer tracer) { } public SentryTransaction(string name, string operation) { } public SentryTransaction(string name, string operation, Sentry.TransactionNameSource nameSource) { } public System.Collections.Generic.IReadOnlyCollection Breadcrumbs { get; } public Sentry.SentryContexts Contexts { get; set; } + public System.Collections.Generic.IReadOnlyDictionary Data { get; } public string? Description { get; set; } public string? Distribution { get; set; } public System.DateTimeOffset? EndTimestamp { get; } public string? Environment { get; set; } public Sentry.SentryId EventId { get; } + [System.Obsolete("Use Data")] public System.Collections.Generic.IReadOnlyDictionary Extra { get; } public System.Collections.Generic.IReadOnlyList Fingerprint { get; set; } public bool IsFinished { get; } @@ -968,6 +979,8 @@ namespace Sentry public Sentry.SentryUser User { get; set; } public void AddBreadcrumb(Sentry.Breadcrumb breadcrumb) { } public Sentry.SentryTraceHeader GetTraceHeader() { } + public void SetData(string key, object? value) { } + [System.Obsolete("Use SetData")] public void SetExtra(string key, object? value) { } public void SetMeasurement(string name, Sentry.Protocol.Measurement measurement) { } public void SetTag(string key, string value) { } @@ -1078,9 +1091,10 @@ namespace Sentry OutOfRange = 15, DataLoss = 16, } - public class SpanTracer : Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpan, Sentry.ISpanData, Sentry.Protocol.ITraceContext + public class SpanTracer : Sentry.IHasData, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpan, Sentry.ISpanData, Sentry.Protocol.ITraceContext { public SpanTracer(Sentry.IHub hub, Sentry.TransactionTracer transaction, Sentry.SpanId? parentSpanId, Sentry.SentryId traceId, string operation) { } + public System.Collections.Generic.IReadOnlyDictionary Data { get; } public string? Description { get; set; } public System.DateTimeOffset? EndTimestamp { get; } public System.Collections.Generic.IReadOnlyDictionary Extra { get; } @@ -1100,6 +1114,7 @@ namespace Sentry public void Finish(System.Exception exception) { } public void Finish(System.Exception exception, Sentry.SpanStatus status) { } public Sentry.SentryTraceHeader GetTraceHeader() { } + public void SetData(string key, object? value) { } public void SetExtra(string key, object? value) { } public void SetMeasurement(string name, Sentry.Protocol.Measurement measurement) { } public void SetTag(string key, string value) { } @@ -1155,15 +1170,17 @@ namespace Sentry public System.Collections.Generic.IReadOnlyDictionary CustomSamplingContext { get; } public Sentry.ITransactionContext TransactionContext { get; } } - public class TransactionTracer : Sentry.IEventLike, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpan, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.ITransactionTracer, Sentry.Protocol.ITraceContext + public class TransactionTracer : Sentry.IEventLike, Sentry.IHasData, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpan, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.ITransactionTracer, Sentry.Protocol.ITraceContext { public TransactionTracer(Sentry.IHub hub, Sentry.ITransactionContext context) { } public System.Collections.Generic.IReadOnlyCollection Breadcrumbs { get; } public Sentry.SentryContexts Contexts { get; set; } + public System.Collections.Generic.IReadOnlyDictionary Data { get; } public string? Description { get; set; } public string? Distribution { get; set; } public System.DateTimeOffset? EndTimestamp { get; } public string? Environment { get; set; } + [System.Obsolete("Use Data")] public System.Collections.Generic.IReadOnlyDictionary Extra { get; } public System.Collections.Generic.IReadOnlyList Fingerprint { get; set; } public bool IsFinished { get; } @@ -1195,6 +1212,8 @@ namespace Sentry public void Finish(System.Exception exception, Sentry.SpanStatus status) { } public Sentry.ISpan? GetLastActiveSpan() { } public Sentry.SentryTraceHeader GetTraceHeader() { } + public void SetData(string key, object? value) { } + [System.Obsolete("Use SetData")] public void SetExtra(string key, object? value) { } public void SetMeasurement(string name, Sentry.Protocol.Measurement measurement) { } public void SetTag(string key, string value) { } @@ -1749,6 +1768,7 @@ namespace Sentry.Protocol { public const string Type = "trace"; public Trace() { } + public System.Collections.Generic.IReadOnlyDictionary Data { get; } public string? Description { get; set; } public bool? IsSampled { get; } public string Operation { get; set; } @@ -1757,6 +1777,7 @@ namespace Sentry.Protocol public Sentry.SpanId SpanId { get; set; } public Sentry.SpanStatus? Status { get; set; } public Sentry.SentryId TraceId { get; set; } + public void SetData(string key, object? value) { } public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } public static Sentry.Protocol.Trace FromJson(System.Text.Json.JsonElement json) { } } diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt index a14cf232b7..2730a34f6d 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt @@ -178,6 +178,11 @@ namespace Sentry Sentry.SentryUser User { get; set; } void AddBreadcrumb(Sentry.Breadcrumb breadcrumb); } + public interface IHasData + { + System.Collections.Generic.IReadOnlyDictionary Data { get; } + void SetData(string key, object? value); + } public interface IHasExtra { System.Collections.Generic.IReadOnlyDictionary Extra { get; } @@ -257,7 +262,7 @@ namespace Sentry { Sentry.SentryUser? Create(); } - public interface ISpan : Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpanData, Sentry.Protocol.ITraceContext + public interface ISpan : Sentry.IHasData, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpanData, Sentry.Protocol.ITraceContext { new string? Description { get; set; } new string Operation { get; set; } @@ -268,7 +273,7 @@ namespace Sentry void Finish(System.Exception exception, Sentry.SpanStatus status); Sentry.ISpan StartChild(string operation); } - public interface ISpanData : Sentry.IHasExtra, Sentry.IHasTags, Sentry.Protocol.ITraceContext + public interface ISpanData : Sentry.IHasData, Sentry.IHasExtra, Sentry.IHasTags, Sentry.Protocol.ITraceContext { System.DateTimeOffset? EndTimestamp { get; } bool IsFinished { get; } @@ -283,11 +288,11 @@ namespace Sentry string Name { get; } Sentry.TransactionNameSource NameSource { get; } } - public interface ITransactionData : Sentry.IEventLike, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.Protocol.ITraceContext + public interface ITransactionData : Sentry.IEventLike, Sentry.IHasData, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.Protocol.ITraceContext { string? Platform { get; set; } } - public interface ITransactionTracer : Sentry.IEventLike, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpan, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.Protocol.ITraceContext + public interface ITransactionTracer : Sentry.IEventLike, Sentry.IHasData, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpan, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.Protocol.ITraceContext { new bool? IsParentSampled { get; set; } new string Name { get; set; } @@ -830,12 +835,14 @@ namespace Sentry public string? UserAgent { get; } public void ReportError() { } } - public class SentrySpan : Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISentryJsonSerializable, Sentry.ISpanData, Sentry.Protocol.ITraceContext + public class SentrySpan : Sentry.IHasData, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISentryJsonSerializable, Sentry.ISpanData, Sentry.Protocol.ITraceContext { public SentrySpan(Sentry.ISpan tracer) { } public SentrySpan(Sentry.SpanId? parentSpanId, string operation) { } + public System.Collections.Generic.IReadOnlyDictionary Data { get; } public string? Description { get; set; } public System.DateTimeOffset? EndTimestamp { get; } + [System.Obsolete("Use SetData")] public System.Collections.Generic.IReadOnlyDictionary Extra { get; } public bool IsFinished { get; } public bool? IsSampled { get; } @@ -849,6 +856,8 @@ namespace Sentry public System.Collections.Generic.IReadOnlyDictionary Tags { get; } public Sentry.SentryId TraceId { get; } public Sentry.SentryTraceHeader GetTraceHeader() { } + public void SetData(string key, object? value) { } + [System.Obsolete("Use Data")] public void SetExtra(string key, object? value) { } public void SetMeasurement(string name, Sentry.Protocol.Measurement measurement) { } public void SetTag(string key, string value) { } @@ -911,18 +920,20 @@ namespace Sentry public override string ToString() { } public static Sentry.SentryTraceHeader? Parse(string value) { } } - public class SentryTransaction : Sentry.IEventLike, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISentryJsonSerializable, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.Protocol.ITraceContext + public class SentryTransaction : Sentry.IEventLike, Sentry.IHasData, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISentryJsonSerializable, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.Protocol.ITraceContext { public SentryTransaction(Sentry.ITransactionTracer tracer) { } public SentryTransaction(string name, string operation) { } public SentryTransaction(string name, string operation, Sentry.TransactionNameSource nameSource) { } public System.Collections.Generic.IReadOnlyCollection Breadcrumbs { get; } public Sentry.SentryContexts Contexts { get; set; } + public System.Collections.Generic.IReadOnlyDictionary Data { get; } public string? Description { get; set; } public string? Distribution { get; set; } public System.DateTimeOffset? EndTimestamp { get; } public string? Environment { get; set; } public Sentry.SentryId EventId { get; } + [System.Obsolete("Use Data")] public System.Collections.Generic.IReadOnlyDictionary Extra { get; } public System.Collections.Generic.IReadOnlyList Fingerprint { get; set; } public bool IsFinished { get; } @@ -949,6 +960,8 @@ namespace Sentry public Sentry.SentryUser User { get; set; } public void AddBreadcrumb(Sentry.Breadcrumb breadcrumb) { } public Sentry.SentryTraceHeader GetTraceHeader() { } + public void SetData(string key, object? value) { } + [System.Obsolete("Use SetData")] public void SetExtra(string key, object? value) { } public void SetMeasurement(string name, Sentry.Protocol.Measurement measurement) { } public void SetTag(string key, string value) { } @@ -1059,9 +1072,10 @@ namespace Sentry OutOfRange = 15, DataLoss = 16, } - public class SpanTracer : Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpan, Sentry.ISpanData, Sentry.Protocol.ITraceContext + public class SpanTracer : Sentry.IHasData, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpan, Sentry.ISpanData, Sentry.Protocol.ITraceContext { public SpanTracer(Sentry.IHub hub, Sentry.TransactionTracer transaction, Sentry.SpanId? parentSpanId, Sentry.SentryId traceId, string operation) { } + public System.Collections.Generic.IReadOnlyDictionary Data { get; } public string? Description { get; set; } public System.DateTimeOffset? EndTimestamp { get; } public System.Collections.Generic.IReadOnlyDictionary Extra { get; } @@ -1081,6 +1095,7 @@ namespace Sentry public void Finish(System.Exception exception) { } public void Finish(System.Exception exception, Sentry.SpanStatus status) { } public Sentry.SentryTraceHeader GetTraceHeader() { } + public void SetData(string key, object? value) { } public void SetExtra(string key, object? value) { } public void SetMeasurement(string name, Sentry.Protocol.Measurement measurement) { } public void SetTag(string key, string value) { } @@ -1136,15 +1151,17 @@ namespace Sentry public System.Collections.Generic.IReadOnlyDictionary CustomSamplingContext { get; } public Sentry.ITransactionContext TransactionContext { get; } } - public class TransactionTracer : Sentry.IEventLike, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpan, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.ITransactionTracer, Sentry.Protocol.ITraceContext + public class TransactionTracer : Sentry.IEventLike, Sentry.IHasData, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpan, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.ITransactionTracer, Sentry.Protocol.ITraceContext { public TransactionTracer(Sentry.IHub hub, Sentry.ITransactionContext context) { } public System.Collections.Generic.IReadOnlyCollection Breadcrumbs { get; } public Sentry.SentryContexts Contexts { get; set; } + public System.Collections.Generic.IReadOnlyDictionary Data { get; } public string? Description { get; set; } public string? Distribution { get; set; } public System.DateTimeOffset? EndTimestamp { get; } public string? Environment { get; set; } + [System.Obsolete("Use Data")] public System.Collections.Generic.IReadOnlyDictionary Extra { get; } public System.Collections.Generic.IReadOnlyList Fingerprint { get; set; } public bool IsFinished { get; } @@ -1176,6 +1193,8 @@ namespace Sentry public void Finish(System.Exception exception, Sentry.SpanStatus status) { } public Sentry.ISpan? GetLastActiveSpan() { } public Sentry.SentryTraceHeader GetTraceHeader() { } + public void SetData(string key, object? value) { } + [System.Obsolete("Use SetData")] public void SetExtra(string key, object? value) { } public void SetMeasurement(string name, Sentry.Protocol.Measurement measurement) { } public void SetTag(string key, string value) { } @@ -1731,6 +1750,7 @@ namespace Sentry.Protocol { public const string Type = "trace"; public Trace() { } + public System.Collections.Generic.IReadOnlyDictionary Data { get; } public string? Description { get; set; } public bool? IsSampled { get; } public string Operation { get; set; } @@ -1739,6 +1759,7 @@ namespace Sentry.Protocol public Sentry.SpanId SpanId { get; set; } public Sentry.SpanStatus? Status { get; set; } public Sentry.SentryId TraceId { get; set; } + public void SetData(string key, object? value) { } public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } public static Sentry.Protocol.Trace FromJson(System.Text.Json.JsonElement json) { } } From 05ac853ae29c10bd76db0e382777a7122bd6b171 Mon Sep 17 00:00:00 2001 From: getsentry-bot Date: Thu, 13 Feb 2025 07:20:59 +0000 Subject: [PATCH 128/363] release: 5.1.1 --- CHANGELOG.md | 2 +- Directory.Build.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 49ac701258..477c567677 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## Unreleased +## 5.1.1 ### Fixes diff --git a/Directory.Build.props b/Directory.Build.props index 485fbcc7a4..d012b7c186 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,7 +1,7 @@ - 5.1.0 + 5.1.1 13 true true From 99259680727a60c553db7296fd08d0217258b134 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos <6349682+vaind@users.noreply.github.com> Date: Thu, 13 Feb 2025 23:05:59 +0100 Subject: [PATCH 129/363] Update CHANGELOG.md (#3968) --- CHANGELOG.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 477c567677..8f26c4c514 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,8 +8,6 @@ - Native SIGSEGV errors resulting from managed NullReferenceExceptions are now suppressed on Android ([#3903](https://github.com/getsentry/sentry-dotnet/pull/3903)) - OTel activities that are marked as not recorded are no longer sent to Sentry ([#3890](https://github.com/getsentry/sentry-dotnet/pull/3890)) - Fixed envelopes with oversized attachments getting stuck in __processing ([#3938](https://github.com/getsentry/sentry-dotnet/pull/3938)) -- Unknown stack frames in profiles on .NET 8+ ([#3942](https://github.com/getsentry/sentry-dotnet/pull/3942)) -- Deduplicate profiling stack frames ([#3941](https://github.com/getsentry/sentry-dotnet/pull/3941)) - OperatingSystem will now return macOS as OS name instead of 'Darwin' as well as the proper version. ([#2710](https://github.com/getsentry/sentry-dotnet/pull/3956)) - Ignore null value on CocoaScopeObserver.SetTag ([#3948](https://github.com/getsentry/sentry-dotnet/pull/3948)) From 8efb6e5bbdbdf2b27f99298834063e7f84135f76 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 18 Feb 2025 14:59:44 +1300 Subject: [PATCH 130/363] build(deps): bump actions/create-github-app-token from 1.11.3 to 1.11.5 (#3973) Bumps [actions/create-github-app-token](https://github.com/actions/create-github-app-token) from 1.11.3 to 1.11.5. - [Release notes](https://github.com/actions/create-github-app-token/releases) - [Commits](https://github.com/actions/create-github-app-token/compare/67e27a7eb7db372a1c61a7f9bdab8699e9ee57f7...0d564482f06ca65fa9e77e2510873638c82206f2) --- updated-dependencies: - dependency-name: actions/create-github-app-token dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8049860b19..bc20ec278c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -20,7 +20,7 @@ jobs: steps: - name: Get auth token id: token - uses: actions/create-github-app-token@67e27a7eb7db372a1c61a7f9bdab8699e9ee57f7 # v1.11.3 + uses: actions/create-github-app-token@0d564482f06ca65fa9e77e2510873638c82206f2 # v1.11.5 with: app-id: ${{ vars.SENTRY_RELEASE_BOT_CLIENT_ID }} private-key: ${{ secrets.SENTRY_RELEASE_BOT_PRIVATE_KEY }} From 3974e41a285574a3c9203bf2e423205465927ab3 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos <6349682+vaind@users.noreply.github.com> Date: Tue, 18 Feb 2025 09:50:13 +0100 Subject: [PATCH 131/363] fix: Deduplicate profiling stack frames (#3969) --- CHANGELOG.md | 1 + src/Sentry.Profiling/SampleProfileBuilder.cs | 118 +++++++------ ...ofileInfo_Serialization_Works.verified.txt | 162 ++++++------------ ...s.Profile_Serialization_Works.verified.txt | 162 ++++++------------ 4 files changed, 184 insertions(+), 259 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f26c4c514..38051d2126 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ - Fixed envelopes with oversized attachments getting stuck in __processing ([#3938](https://github.com/getsentry/sentry-dotnet/pull/3938)) - OperatingSystem will now return macOS as OS name instead of 'Darwin' as well as the proper version. ([#2710](https://github.com/getsentry/sentry-dotnet/pull/3956)) - Ignore null value on CocoaScopeObserver.SetTag ([#3948](https://github.com/getsentry/sentry-dotnet/pull/3948)) +- Deduplicate profiling stack frames ([#3969](https://github.com/getsentry/sentry-dotnet/pull/3969)) ## 5.1.0 diff --git a/src/Sentry.Profiling/SampleProfileBuilder.cs b/src/Sentry.Profiling/SampleProfileBuilder.cs index bdef768d85..56927be4a9 100644 --- a/src/Sentry.Profiling/SampleProfileBuilder.cs +++ b/src/Sentry.Profiling/SampleProfileBuilder.cs @@ -16,8 +16,12 @@ internal class SampleProfileBuilder // Output profile being built. public readonly SampleProfile Profile = new(); - // A sparse array that maps from StackSourceFrameIndex to an index in the output Profile.frames. - private readonly Dictionary _frameIndexes = new(); + // A sparse array that maps from CodeAddressIndex to an index in the output Profile.frames. + private readonly Dictionary _frameIndexesByCodeAddressIndex = new(); + + // A sparse array that maps from MethodIndex to an index in the output Profile.frames. + // This deduplicates frames that map to the same method but have a different CodeAddressIndex. + private readonly Dictionary _frameIndexesByMethodIndex = new(); // A dictionary from a CallStackIndex to an index in the output Profile.stacks. private readonly Dictionary _stackIndexes = new(); @@ -85,13 +89,14 @@ private int AddStackTrace(CallStackIndex callstackIndex) { var key = (int)callstackIndex; - if (!_stackIndexes.ContainsKey(key)) + if (!_stackIndexes.TryGetValue(key, out var value)) { Profile.Stacks.Add(CreateStackTrace(callstackIndex)); - _stackIndexes[key] = Profile.Stacks.Count - 1; + value = Profile.Stacks.Count - 1; + _stackIndexes[key] = value; } - return _stackIndexes[key]; + return value; } private Internal.GrowableArray CreateStackTrace(CallStackIndex callstackIndex) @@ -116,21 +121,71 @@ private Internal.GrowableArray CreateStackTrace(CallStackIndex callstackInd return stackTrace; } + private int PushNewFrame(SentryStackFrame frame) + { + Profile.Frames.Add(frame); + return Profile.Frames.Count - 1; + } + /// /// Check if the frame is already stored in the output Profile, or adds it. /// /// The index to the output Profile frames array. private int AddStackFrame(CodeAddressIndex codeAddressIndex) { - var key = (int)codeAddressIndex; + if (_frameIndexesByCodeAddressIndex.TryGetValue((int)codeAddressIndex, out var value)) + { + return value; + } - if (!_frameIndexes.ContainsKey(key)) + var methodIndex = _traceLog.CodeAddresses.MethodIndex(codeAddressIndex); + if (methodIndex != MethodIndex.Invalid) + { + value = AddStackFrame(methodIndex); + _frameIndexesByCodeAddressIndex[(int)codeAddressIndex] = value; + return value; + } + + // Fall back if the method info is unknown, see more info on Symbol resolution in + // https://github.com/getsentry/perfview/blob/031250ffb4f9fcadb9263525d6c9f274be19ca51/src/PerfView/SupportFiles/UsersGuide.htm#L7745-L7784 + if (_traceLog.CodeAddresses[codeAddressIndex] is { } codeAddressInfo) { - Profile.Frames.Add(CreateStackFrame(codeAddressIndex)); - _frameIndexes[key] = Profile.Frames.Count - 1; + var frame = new SentryStackFrame + { + InstructionAddress = (long?)codeAddressInfo.Address, + Module = codeAddressInfo.ModuleFile?.Name, + }; + frame.ConfigureAppFrame(_options); + + return _frameIndexesByCodeAddressIndex[(int)codeAddressIndex] = PushNewFrame(frame); } - return _frameIndexes[key]; + // If all else fails, it's a completely unknown frame. + // TODO check this - maybe we would be able to resolve it later in the future? + return PushNewFrame(new SentryStackFrame { InApp = false }); + } + + /// + /// Check if the frame is already stored in the output Profile, or adds it. + /// + /// The index to the output Profile frames array. + private int AddStackFrame(MethodIndex methodIndex) + { + if (_frameIndexesByMethodIndex.TryGetValue((int)methodIndex, out var value)) + { + return value; + } + + var method = _traceLog.CodeAddresses.Methods[methodIndex]; + + var frame = new SentryStackFrame + { + Function = method.FullMethodName, + Module = method.MethodModuleFile?.Name + }; + frame.ConfigureAppFrame(_options); + + return _frameIndexesByMethodIndex[(int)methodIndex] = PushNewFrame(frame); } /// @@ -141,52 +196,17 @@ private int AddThread(TraceThread thread) { var key = (int)thread.ThreadIndex; - if (!_threadIndexes.ContainsKey(key)) + if (!_threadIndexes.TryGetValue(key, out var value)) { Profile.Threads.Add(new() { Name = thread.ThreadInfo ?? $"Thread {thread.ThreadID}", }); - _threadIndexes[key] = Profile.Threads.Count - 1; + value = Profile.Threads.Count - 1; + _threadIndexes[key] = value; _downsampler.NewThreadAdded(_threadIndexes[key]); } - return _threadIndexes[key]; - } - - private SentryStackFrame CreateStackFrame(CodeAddressIndex codeAddressIndex) - { - var frame = new SentryStackFrame(); - - var methodIndex = _traceLog.CodeAddresses.MethodIndex(codeAddressIndex); - if (_traceLog.CodeAddresses.Methods[methodIndex] is { } method) - { - frame.Function = method.FullMethodName; - - if (method.MethodModuleFile is { } moduleFile) - { - frame.Module = moduleFile.Name; - } - - frame.ConfigureAppFrame(_options); - } - else - { - // Fall back if the method info is unknown, see more info on Symbol resolution in - // https://github.com/getsentry/perfview/blob/031250ffb4f9fcadb9263525d6c9f274be19ca51/src/PerfView/SupportFiles/UsersGuide.htm#L7745-L7784 - frame.InstructionAddress = (long?)_traceLog.CodeAddresses.Address(codeAddressIndex); - - if (_traceLog.CodeAddresses.ModuleFile(codeAddressIndex) is { } moduleFile) - { - frame.Module = moduleFile.Name; - frame.ConfigureAppFrame(_options); - } - else - { - frame.InApp = false; - } - } - - return frame; + return value; } } diff --git a/test/Sentry.Profiling.Tests/TraceLogProcessorTests.ProfileInfo_Serialization_Works.verified.txt b/test/Sentry.Profiling.Tests/TraceLogProcessorTests.ProfileInfo_Serialization_Works.verified.txt index 714859c86d..481fcf2e42 100644 --- a/test/Sentry.Profiling.Tests/TraceLogProcessorTests.ProfileInfo_Serialization_Works.verified.txt +++ b/test/Sentry.Profiling.Tests/TraceLogProcessorTests.ProfileInfo_Serialization_Works.verified.txt @@ -107,11 +107,12 @@ 36 ], [ - 37 + 17 ], [ 0, 1, + 37, 38, 39, 40, @@ -119,35 +120,37 @@ 42, 43, 44, - 45, - 46, + 21, 22 ], [ + 45, + 46, 47, 48, 49, 50, - 51, - 52, - 37 + 17 ], [ + 51, + 52, 53, 54, 55, - 56, - 57, - 37 + 17 ], [ + 56, + 57, 58, 59, - 60, - 61, - 62 + 17 ], [ + 60, + 61, + 62, 63, 64, 65, @@ -155,48 +158,55 @@ 67, 68, 69, + 56, + 57, + 58, + 59, + 17 + ], + [ 70, 71, 72, + 73, + 74, + 69, + 56, + 57, 58, 59, - 60, - 61, - 62 + 17 ], [ - 73, - 74, 75, - 76, - 77, - 78, + 74, + 69, + 56, + 57, 58, 59, - 60, - 61, - 62 + 17 ], [ - 79, - 80, - 78, + 56, + 57, 58, 59, - 60, - 61, - 62 + 17 ], [ + 76, + 77, + 78, + 79, + 80, 81, + 82, + 83, 59, - 60, - 61, - 62 + 17 ], [ - 82, - 83, 84, 85, 86, @@ -204,31 +214,22 @@ 88, 89, 90, - 62 - ], - [ 91, 92, - 93, - 94, - 95, - 96, - 97, - 98, - 99, - 100, - 84, - 85, - 86, - 87, - 88, - 89, - 90, - 62 + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 59, + 17 ], [ 0, 1, + 37, 38, 39, 40, @@ -236,15 +237,14 @@ 42, 43, 44, - 45, - 46, + 21, 22 ], [ 18, 19, 20, - 101, + 21, 22 ] ], @@ -340,7 +340,6 @@ in_app: true }, { - in_app: false, instruction_addr: 0x7ff947fd8378 }, { @@ -433,11 +432,6 @@ module: System.Private.CoreLib.il, in_app: false }, - { - function: Aura.UI.Gallery.NetCore.Program.Main(class System.String[]), - module: Aura.UI.Gallery.NetCore, - in_app: true - }, { function: System.IO.Stream+<>c.b__40_0(class System.Object), module: System.Private.CoreLib.il, @@ -478,11 +472,6 @@ module: System.Private.CoreLib.il, in_app: false }, - { - function: System.Threading.PortableThreadPool+WorkerThread.WorkerThreadStart(), - module: System.Private.CoreLib.il, - in_app: false - }, { function: System.Collections.Concurrent.ConcurrentDictionary`2[System.__Canon,System.IntPtr].TryAddInternal(!0,value class System.Nullable`1,!1,bool,bool,!1&), module: System.Collections.Concurrent.il, @@ -509,7 +498,6 @@ in_app: true }, { - in_app: false, instruction_addr: 0x7ff94be7aed3 }, { @@ -557,11 +545,6 @@ module: Avalonia.Controls, in_app: true }, - { - function: Aura.UI.Gallery.NetCore.Program.Main(class System.String[]), - module: Aura.UI.Gallery.NetCore, - in_app: true - }, { function: Avalonia.Animation.Animation..cctor(), module: Avalonia.Animation, @@ -637,26 +620,11 @@ module: Avalonia.Controls, in_app: true }, - { - function: Avalonia.Controls.Primitives.TemplatedControl..cctor(), - module: Avalonia.Controls, - in_app: true - }, { function: System.Reactive.Linq.Observable.Merge(class System.IObservable`1[]), module: system.reactive, in_app: false }, - { - function: Avalonia.Controls.TextBlock..cctor(), - module: Avalonia.Controls, - in_app: true - }, - { - function: Avalonia.Controls.Window..cctor(), - module: Avalonia.Controls, - in_app: true - }, { function: Avalonia.Win32.Win32Platform.SetDpiAwareness(), module: avalonia.win32, @@ -697,11 +665,6 @@ module: Avalonia.Controls, in_app: true }, - { - function: Avalonia.ClassicDesktopStyleApplicationLifetimeExtensions.StartWithClassicDesktopLifetime(!!0,class System.String[],value class Avalonia.Controls.ShutdownMode), - module: Avalonia.Controls, - in_app: true - }, { function: System.Drawing.SafeNativeMethods+Gdip..cctor(), module: system.drawing.common, @@ -738,23 +701,12 @@ in_app: true }, { - in_app: false, instruction_addr: 0x7ffa0d468281 }, { function: Avalonia.Win32.Win32Platform.CreateMessageWindow(), module: avalonia.win32, in_app: true - }, - { - function: Avalonia.Win32.Win32Platform..ctor(), - module: avalonia.win32, - in_app: true - }, - { - function: System.Threading.PortableThreadPool+WorkerThread.WorkerThreadStart(), - module: System.Private.CoreLib.il, - in_app: false } ], samples: [ @@ -1070,4 +1022,4 @@ } ] } -} \ No newline at end of file +} diff --git a/test/Sentry.Profiling.Tests/TraceLogProcessorTests.Profile_Serialization_Works.verified.txt b/test/Sentry.Profiling.Tests/TraceLogProcessorTests.Profile_Serialization_Works.verified.txt index 6eb35f3c22..7720ab6755 100644 --- a/test/Sentry.Profiling.Tests/TraceLogProcessorTests.Profile_Serialization_Works.verified.txt +++ b/test/Sentry.Profiling.Tests/TraceLogProcessorTests.Profile_Serialization_Works.verified.txt @@ -84,11 +84,12 @@ 36 ], [ - 37 + 17 ], [ 0, 1, + 37, 38, 39, 40, @@ -96,35 +97,37 @@ 42, 43, 44, - 45, - 46, + 21, 22 ], [ + 45, + 46, 47, 48, 49, 50, - 51, - 52, - 37 + 17 ], [ + 51, + 52, 53, 54, 55, - 56, - 57, - 37 + 17 ], [ + 56, + 57, 58, 59, - 60, - 61, - 62 + 17 ], [ + 60, + 61, + 62, 63, 64, 65, @@ -132,48 +135,55 @@ 67, 68, 69, + 56, + 57, + 58, + 59, + 17 + ], + [ 70, 71, 72, + 73, + 74, + 69, + 56, + 57, 58, 59, - 60, - 61, - 62 + 17 ], [ - 73, - 74, 75, - 76, - 77, - 78, + 74, + 69, + 56, + 57, 58, 59, - 60, - 61, - 62 + 17 ], [ - 79, - 80, - 78, + 56, + 57, 58, 59, - 60, - 61, - 62 + 17 ], [ + 76, + 77, + 78, + 79, + 80, 81, + 82, + 83, 59, - 60, - 61, - 62 + 17 ], [ - 82, - 83, 84, 85, 86, @@ -181,31 +191,22 @@ 88, 89, 90, - 62 - ], - [ 91, 92, - 93, - 94, - 95, - 96, - 97, - 98, - 99, - 100, - 84, - 85, - 86, - 87, - 88, - 89, - 90, - 62 + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 59, + 17 ], [ 0, 1, + 37, 38, 39, 40, @@ -213,15 +214,14 @@ 42, 43, 44, - 45, - 46, + 21, 22 ], [ 18, 19, 20, - 101, + 21, 22 ] ], @@ -317,7 +317,6 @@ in_app: true }, { - in_app: false, instruction_addr: 0x7ff947fd8378 }, { @@ -410,11 +409,6 @@ module: System.Private.CoreLib.il, in_app: false }, - { - function: Aura.UI.Gallery.NetCore.Program.Main(class System.String[]), - module: Aura.UI.Gallery.NetCore, - in_app: true - }, { function: System.IO.Stream+<>c.b__40_0(class System.Object), module: System.Private.CoreLib.il, @@ -455,11 +449,6 @@ module: System.Private.CoreLib.il, in_app: false }, - { - function: System.Threading.PortableThreadPool+WorkerThread.WorkerThreadStart(), - module: System.Private.CoreLib.il, - in_app: false - }, { function: System.Collections.Concurrent.ConcurrentDictionary`2[System.__Canon,System.IntPtr].TryAddInternal(!0,value class System.Nullable`1,!1,bool,bool,!1&), module: System.Collections.Concurrent.il, @@ -486,7 +475,6 @@ in_app: true }, { - in_app: false, instruction_addr: 0x7ff94be7aed3 }, { @@ -534,11 +522,6 @@ module: Avalonia.Controls, in_app: true }, - { - function: Aura.UI.Gallery.NetCore.Program.Main(class System.String[]), - module: Aura.UI.Gallery.NetCore, - in_app: true - }, { function: Avalonia.Animation.Animation..cctor(), module: Avalonia.Animation, @@ -614,26 +597,11 @@ module: Avalonia.Controls, in_app: true }, - { - function: Avalonia.Controls.Primitives.TemplatedControl..cctor(), - module: Avalonia.Controls, - in_app: true - }, { function: System.Reactive.Linq.Observable.Merge(class System.IObservable`1[]), module: system.reactive, in_app: false }, - { - function: Avalonia.Controls.TextBlock..cctor(), - module: Avalonia.Controls, - in_app: true - }, - { - function: Avalonia.Controls.Window..cctor(), - module: Avalonia.Controls, - in_app: true - }, { function: Avalonia.Win32.Win32Platform.SetDpiAwareness(), module: avalonia.win32, @@ -674,11 +642,6 @@ module: Avalonia.Controls, in_app: true }, - { - function: Avalonia.ClassicDesktopStyleApplicationLifetimeExtensions.StartWithClassicDesktopLifetime(!!0,class System.String[],value class Avalonia.Controls.ShutdownMode), - module: Avalonia.Controls, - in_app: true - }, { function: System.Drawing.SafeNativeMethods+Gdip..cctor(), module: system.drawing.common, @@ -715,23 +678,12 @@ in_app: true }, { - in_app: false, instruction_addr: 0x7ffa0d468281 }, { function: Avalonia.Win32.Win32Platform.CreateMessageWindow(), module: avalonia.win32, in_app: true - }, - { - function: Avalonia.Win32.Win32Platform..ctor(), - module: avalonia.win32, - in_app: true - }, - { - function: System.Threading.PortableThreadPool+WorkerThread.WorkerThreadStart(), - module: System.Private.CoreLib.il, - in_app: false } ], samples: [ @@ -1046,4 +998,4 @@ stack_id: 18 } ] -} \ No newline at end of file +} From c6a5d4af27c68c99dd67c50684e8e5ea7b4e7b18 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos <6349682+vaind@users.noreply.github.com> Date: Tue, 18 Feb 2025 09:50:33 +0100 Subject: [PATCH 132/363] fix: wait for profiling session shutdown (#3966) --- src/Sentry.Profiling/SampleProfilerSession.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Sentry.Profiling/SampleProfilerSession.cs b/src/Sentry.Profiling/SampleProfilerSession.cs index 3bb606a960..e54cf62020 100644 --- a/src/Sentry.Profiling/SampleProfilerSession.cs +++ b/src/Sentry.Profiling/SampleProfilerSession.cs @@ -17,14 +17,16 @@ internal class SampleProfilerSession : IDisposable private readonly IDiagnosticLogger? _logger; private readonly SentryStopwatch _stopwatch; private bool _stopped = false; + private Task _processing; - private SampleProfilerSession(SentryStopwatch stopwatch, EventPipeSession session, TraceLogEventSource eventSource, IDiagnosticLogger? logger) + private SampleProfilerSession(SentryStopwatch stopwatch, EventPipeSession session, TraceLogEventSource eventSource, Task processing, IDiagnosticLogger? logger) { _session = session; _logger = logger; _eventSource = eventSource; _sampleEventParser = new SampleProfilerTraceEventParser(_eventSource); _stopwatch = stopwatch; + _processing = processing; } // Exposed only for benchmarks. @@ -86,7 +88,7 @@ public static SampleProfilerSession StartNew(IDiagnosticLogger? logger = null) var eventSource = TraceLog.CreateFromEventPipeSession(session, TraceLog.EventPipeRundownConfiguration.Enable(client)); // Process() blocks until the session is stopped so we need to run it on a separate thread. - Task.Factory.StartNew(eventSource.Process, TaskCreationOptions.LongRunning) + var processing = Task.Factory.StartNew(eventSource.Process, TaskCreationOptions.LongRunning) .ContinueWith(_ => { if (_.Exception?.InnerException is { } e) @@ -95,7 +97,7 @@ public static SampleProfilerSession StartNew(IDiagnosticLogger? logger = null) } }, TaskContinuationOptions.OnlyOnFaulted); - return new SampleProfilerSession(stopWatch, session, eventSource, logger); + return new SampleProfilerSession(stopWatch, session, eventSource, processing, logger); } catch (Exception ex) { @@ -128,6 +130,7 @@ public void Stop() { _stopped = true; _session.Stop(); + _processing.Wait(); _session.Dispose(); _eventSource.Dispose(); } From 5dcb81279c27d5c3233d107f52f962750526e5f6 Mon Sep 17 00:00:00 2001 From: Allan Ritchie Date: Tue, 18 Feb 2025 11:49:53 -0500 Subject: [PATCH 133/363] Add Additional UseSentry extensions for Easy Azure Function setup (#3971) * Add overload for azure function setup * Update CHANGELOG.md * Update verify files * Update CHANGELOG.md --- CHANGELOG.md | 5 ++++ ...tionsWorkerApplicationBuilderExtensions.cs | 26 ++++++++++++++++++- ...piApprovalTests.Run.DotNet8_0.verified.txt | 2 ++ .../ApiApprovalTests.Run.Net4_8.verified.txt | 2 ++ 4 files changed, 34 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 38051d2126..def04db62a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## Unreleased + +### Fixes +- Add Azure Function UseSentry overloads for easier wire ups ([#3971](https://github.com/getsentry/sentry-dotnet/pull/3971)) + ## 5.1.1 ### Fixes diff --git a/src/Sentry.Azure.Functions.Worker/SentryFunctionsWorkerApplicationBuilderExtensions.cs b/src/Sentry.Azure.Functions.Worker/SentryFunctionsWorkerApplicationBuilderExtensions.cs index d20d1880f7..250db84e34 100644 --- a/src/Sentry.Azure.Functions.Worker/SentryFunctionsWorkerApplicationBuilderExtensions.cs +++ b/src/Sentry.Azure.Functions.Worker/SentryFunctionsWorkerApplicationBuilderExtensions.cs @@ -26,6 +26,20 @@ public static IFunctionsWorkerApplicationBuilder UseSentry(this IFunctionsWorker public static IFunctionsWorkerApplicationBuilder UseSentry(this IFunctionsWorkerApplicationBuilder builder, HostBuilderContext context, string dsn) => builder.UseSentry(context, o => o.Dsn = dsn); + /// + /// Uses Sentry integration. + /// + public static IFunctionsWorkerApplicationBuilder UseSentry( + this IFunctionsWorkerApplicationBuilder builder, + Action? optionsConfiguration) + { + if (builder is IHostApplicationBuilder appBuilder) + { + return builder.UseSentry(appBuilder.Configuration, optionsConfiguration); + } + throw new InvalidOperationException("Builder is not of type " + typeof(IHostApplicationBuilder)); + } + /// /// Uses Sentry integration. /// @@ -33,11 +47,21 @@ public static IFunctionsWorkerApplicationBuilder UseSentry( this IFunctionsWorkerApplicationBuilder builder, HostBuilderContext context, Action? optionsConfiguration) + => builder.UseSentry(context.Configuration, optionsConfiguration); + + + /// + /// Uses Sentry integration. + /// + public static IFunctionsWorkerApplicationBuilder UseSentry( + this IFunctionsWorkerApplicationBuilder builder, + IConfiguration configuration, + Action? optionsConfiguration) { builder.UseMiddleware(); var services = builder.Services; - var section = context.Configuration.GetSection("Sentry"); + var section = configuration.GetSection("Sentry"); #if NET8_0_OR_GREATER services.AddSingleton>(_ => new SentryAzureFunctionsOptionsSetup(section) diff --git a/test/Sentry.Azure.Functions.Worker.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt b/test/Sentry.Azure.Functions.Worker.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt index f6955863c0..f6ee752df8 100644 --- a/test/Sentry.Azure.Functions.Worker.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt +++ b/test/Sentry.Azure.Functions.Worker.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt @@ -7,6 +7,8 @@ public static class SentryFunctionsWorkerApplicationBuilderExtensions { public static Microsoft.Azure.Functions.Worker.IFunctionsWorkerApplicationBuilder UseSentry(this Microsoft.Azure.Functions.Worker.IFunctionsWorkerApplicationBuilder builder, Microsoft.Extensions.Hosting.HostBuilderContext context) { } + public static Microsoft.Azure.Functions.Worker.IFunctionsWorkerApplicationBuilder UseSentry(this Microsoft.Azure.Functions.Worker.IFunctionsWorkerApplicationBuilder builder, System.Action? optionsConfiguration) { } + public static Microsoft.Azure.Functions.Worker.IFunctionsWorkerApplicationBuilder UseSentry(this Microsoft.Azure.Functions.Worker.IFunctionsWorkerApplicationBuilder builder, Microsoft.Extensions.Configuration.IConfiguration configuration, System.Action? optionsConfiguration) { } public static Microsoft.Azure.Functions.Worker.IFunctionsWorkerApplicationBuilder UseSentry(this Microsoft.Azure.Functions.Worker.IFunctionsWorkerApplicationBuilder builder, Microsoft.Extensions.Hosting.HostBuilderContext context, System.Action? optionsConfiguration) { } public static Microsoft.Azure.Functions.Worker.IFunctionsWorkerApplicationBuilder UseSentry(this Microsoft.Azure.Functions.Worker.IFunctionsWorkerApplicationBuilder builder, Microsoft.Extensions.Hosting.HostBuilderContext context, string dsn) { } } diff --git a/test/Sentry.Azure.Functions.Worker.Tests/ApiApprovalTests.Run.Net4_8.verified.txt b/test/Sentry.Azure.Functions.Worker.Tests/ApiApprovalTests.Run.Net4_8.verified.txt index f6955863c0..f6ee752df8 100644 --- a/test/Sentry.Azure.Functions.Worker.Tests/ApiApprovalTests.Run.Net4_8.verified.txt +++ b/test/Sentry.Azure.Functions.Worker.Tests/ApiApprovalTests.Run.Net4_8.verified.txt @@ -7,6 +7,8 @@ public static class SentryFunctionsWorkerApplicationBuilderExtensions { public static Microsoft.Azure.Functions.Worker.IFunctionsWorkerApplicationBuilder UseSentry(this Microsoft.Azure.Functions.Worker.IFunctionsWorkerApplicationBuilder builder, Microsoft.Extensions.Hosting.HostBuilderContext context) { } + public static Microsoft.Azure.Functions.Worker.IFunctionsWorkerApplicationBuilder UseSentry(this Microsoft.Azure.Functions.Worker.IFunctionsWorkerApplicationBuilder builder, System.Action? optionsConfiguration) { } + public static Microsoft.Azure.Functions.Worker.IFunctionsWorkerApplicationBuilder UseSentry(this Microsoft.Azure.Functions.Worker.IFunctionsWorkerApplicationBuilder builder, Microsoft.Extensions.Configuration.IConfiguration configuration, System.Action? optionsConfiguration) { } public static Microsoft.Azure.Functions.Worker.IFunctionsWorkerApplicationBuilder UseSentry(this Microsoft.Azure.Functions.Worker.IFunctionsWorkerApplicationBuilder builder, Microsoft.Extensions.Hosting.HostBuilderContext context, System.Action? optionsConfiguration) { } public static Microsoft.Azure.Functions.Worker.IFunctionsWorkerApplicationBuilder UseSentry(this Microsoft.Azure.Functions.Worker.IFunctionsWorkerApplicationBuilder builder, Microsoft.Extensions.Hosting.HostBuilderContext context, string dsn) { } } From 1f9d4f288960450979ca079abace44b6f6524a35 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 20 Feb 2025 00:30:09 +1300 Subject: [PATCH 134/363] chore: update scripts/update-cli.ps1 to 2.42.1 (#3979) Co-authored-by: GitHub --- CHANGELOG.md | 6 ++++++ Directory.Build.props | 2 +- src/Sentry/Sentry.csproj | 14 +++++++------- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index def04db62a..64d63a8567 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ ### Fixes - Add Azure Function UseSentry overloads for easier wire ups ([#3971](https://github.com/getsentry/sentry-dotnet/pull/3971)) +### Dependencies + +- Bump CLI from v2.41.1 to v2.42.1 ([#3979](https://github.com/getsentry/sentry-dotnet/pull/3979)) + - [changelog](https://github.com/getsentry/sentry-cli/blob/master/CHANGELOG.md#2421) + - [diff](https://github.com/getsentry/sentry-cli/compare/2.41.1...2.42.1) + ## 5.1.1 ### Fixes diff --git a/Directory.Build.props b/Directory.Build.props index d012b7c186..ce40ac61b9 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -83,7 +83,7 @@ - 2.41.1 + 2.42.1 $(MSBuildThisFileDirectory)tools\sentry-cli\$(SentryCLIVersion)\ diff --git a/src/Sentry/Sentry.csproj b/src/Sentry/Sentry.csproj index 2a12b41a38..2799c161c5 100644 --- a/src/Sentry/Sentry.csproj +++ b/src/Sentry/Sentry.csproj @@ -120,13 +120,13 @@ <_OSArchitecture>$([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture) - - - - - - - + + + + + + + From e0ed84c752a1245af901cfb1165d016270a7648b Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Thu, 20 Feb 2025 11:03:30 +1300 Subject: [PATCH 135/363] Propagate Sampling Seed (#3951) --- CHANGELOG.md | 5 + src/Sentry/BaggageHeader.cs | 2 +- src/Sentry/DynamicSamplingContext.cs | 45 +++++- src/Sentry/Internal/FnvHash.cs | 43 ++++++ src/Sentry/Internal/Hub.cs | 11 +- src/Sentry/Internal/SampleRandHelper.cs | 15 ++ src/Sentry/TransactionTracer.cs | 2 + .../SentryTracingMiddlewareTests.cs | 6 +- test/Sentry.Testing/VerifyExtensions.cs | 1 + .../DynamicSamplingContextTests.cs | 109 ++++++++++++++- ...rocessorTests.WithTransaction.verified.txt | 1 + ...ctionEndedAsCrashed.DotNet8_0.verified.txt | 2 + ...ctionEndedAsCrashed.DotNet9_0.verified.txt | 2 + ...nsactionEndedAsCrashed.Net4_8.verified.txt | 2 + test/Sentry.Tests/HubTests.cs | 130 ++++++++++++++++++ test/Sentry.Tests/Internals/FnvHashTests.cs | 24 ++++ .../SentryPropagationContextTests.cs | 3 +- ...sactionProcessorTests.Discard.verified.txt | 1 + ...nsactionProcessorTests.Simple.verified.txt | 2 + 19 files changed, 391 insertions(+), 15 deletions(-) create mode 100644 src/Sentry/Internal/FnvHash.cs create mode 100644 src/Sentry/Internal/SampleRandHelper.cs create mode 100644 test/Sentry.Tests/Internals/FnvHashTests.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 64d63a8567..6688a7bc47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,12 @@ ## Unreleased +### Features + +- The sample seed used for sampling decisions is now propagated, for use in downstream custom trace samplers ([#3951](https://github.com/getsentry/sentry-dotnet/pull/3951)) + ### Fixes + - Add Azure Function UseSentry overloads for easier wire ups ([#3971](https://github.com/getsentry/sentry-dotnet/pull/3971)) ### Dependencies diff --git a/src/Sentry/BaggageHeader.cs b/src/Sentry/BaggageHeader.cs index 83fe9f0a84..66846a1fcd 100644 --- a/src/Sentry/BaggageHeader.cs +++ b/src/Sentry/BaggageHeader.cs @@ -26,7 +26,7 @@ private BaggageHeader(IEnumerable> members) => // We can safely return a dictionary of Sentry members, as we are in control over the keys added. // Just to be safe though, we'll group by key and only take the first of each one. - internal IReadOnlyDictionary GetSentryMembers() => + internal Dictionary GetSentryMembers() => Members .Where(kvp => kvp.Key.StartsWith(SentryKeyPrefix)) .GroupBy(kvp => kvp.Key, kvp => kvp.Value) diff --git a/src/Sentry/DynamicSamplingContext.cs b/src/Sentry/DynamicSamplingContext.cs index 26056d17cc..e85ec88b84 100644 --- a/src/Sentry/DynamicSamplingContext.cs +++ b/src/Sentry/DynamicSamplingContext.cs @@ -1,3 +1,4 @@ +using Sentry.Internal; using Sentry.Internal.Extensions; namespace Sentry; @@ -24,6 +25,7 @@ private DynamicSamplingContext( string publicKey, bool? sampled, double? sampleRate = null, + double? sampleRand = null, string? release = null, string? environment = null, string? transactionName = null) @@ -31,20 +33,25 @@ private DynamicSamplingContext( // Validate and set required values if (traceId == SentryId.Empty) { - throw new ArgumentOutOfRangeException(nameof(traceId)); + throw new ArgumentOutOfRangeException(nameof(traceId), "cannot be empty"); } if (string.IsNullOrWhiteSpace(publicKey)) { - throw new ArgumentException(default, nameof(publicKey)); + throw new ArgumentException("cannot be empty", nameof(publicKey)); } if (sampleRate is < 0.0 or > 1.0) { - throw new ArgumentOutOfRangeException(nameof(sampleRate)); + throw new ArgumentOutOfRangeException(nameof(sampleRate), "Arg invalid if < 0.0 or > 1.0"); } - var items = new Dictionary(capacity: 7) + if (sampleRand is < 0.0 or >= 1.0) + { + throw new ArgumentOutOfRangeException(nameof(sampleRand), "Arg invalid if < 0.0 or >= 1.0"); + } + + var items = new Dictionary(capacity: 8) { ["trace_id"] = traceId.ToString(), ["public_key"] = publicKey, @@ -61,6 +68,11 @@ private DynamicSamplingContext( items.Add("sample_rate", sampleRate.Value.ToString(CultureInfo.InvariantCulture)); } + if (sampleRand is not null) + { + items.Add("sample_rand", sampleRand.Value.ToString("N4", CultureInfo.InvariantCulture)); + } + if (!string.IsNullOrWhiteSpace(release)) { items.Add("release", release); @@ -99,7 +111,7 @@ private DynamicSamplingContext( return null; } - if (items.TryGetValue("sampled", out var sampledString) && !bool.TryParse(sampledString, out _)) + if (items.TryGetValue("sampled", out var sampledString) && !bool.TryParse(sampledString, out var sampled)) { return null; } @@ -111,6 +123,27 @@ private DynamicSamplingContext( return null; } + // See https://develop.sentry.dev/sdk/telemetry/traces/#propagated-random-value + if (items.TryGetValue("sample_rand", out var sampleRand)) + { + if (!double.TryParse(sampleRand, NumberStyles.Float, CultureInfo.InvariantCulture, out var rand) || + rand is < 0.0 or >= 1.0) + { + return null; + } + } + else + { + var rand = SampleRandHelper.GenerateSampleRand(traceId); + if (!string.IsNullOrEmpty(sampledString)) + { + // Ensure sample_rand is consistent with the sampling decision that has already been made + rand = bool.Parse(sampledString) + ? rand * rate // 0 <= sampleRand < rate + : rate + (1 - rate) * rand; // rate < sampleRand < 1 + } + items.Add("sample_rand", rand.ToString("N4", CultureInfo.InvariantCulture)); + } return new DynamicSamplingContext(items); } @@ -121,6 +154,7 @@ public static DynamicSamplingContext CreateFromTransaction(TransactionTracer tra var traceId = transaction.TraceId; var sampled = transaction.IsSampled; var sampleRate = transaction.SampleRate!.Value; + var sampleRand = transaction.SampleRand; var transactionName = transaction.NameSource.IsHighQuality() ? transaction.Name : null; // These two may not have been set yet on the transaction, but we can get them directly. @@ -132,6 +166,7 @@ public static DynamicSamplingContext CreateFromTransaction(TransactionTracer tra publicKey, sampled, sampleRate, + sampleRand, release, environment, transactionName); diff --git a/src/Sentry/Internal/FnvHash.cs b/src/Sentry/Internal/FnvHash.cs new file mode 100644 index 0000000000..10281e90bd --- /dev/null +++ b/src/Sentry/Internal/FnvHash.cs @@ -0,0 +1,43 @@ +namespace Sentry.Internal; + +/// +/// FNV is a non-cryptographic hash. +/// +/// See https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV_hash_parameters +/// +/// +/// We use a struct to avoid heap allocations. +/// +internal struct FnvHash +{ + public FnvHash() + { + } + + private const int Offset = unchecked((int)2166136261); + private const int Prime = 16777619; + + private int HashCode { get; set; } = Offset; + + private void Combine(byte data) + { + unchecked + { + HashCode ^= data; + HashCode *= Prime; + } + } + + private static int ComputeHash(byte[] data) + { + var result = new FnvHash(); + foreach (var b in data) + { + result.Combine(b); + } + + return result.HashCode; + } + + public static int ComputeHash(string data) => ComputeHash(Encoding.UTF8.GetBytes(data)); +} diff --git a/src/Sentry/Internal/Hub.cs b/src/Sentry/Internal/Hub.cs index b90f4ca603..a564ab1791 100644 --- a/src/Sentry/Internal/Hub.cs +++ b/src/Sentry/Internal/Hub.cs @@ -127,7 +127,12 @@ internal ITransactionTracer StartTransaction( IReadOnlyDictionary customSamplingContext, DynamicSamplingContext? dynamicSamplingContext) { - var transaction = new TransactionTracer(this, context); + var transaction = new TransactionTracer(this, context) + { + SampleRand = dynamicSamplingContext?.Items.TryGetValue("sample_rand", out var sampleRand) ?? false + ? double.Parse(sampleRand, NumberStyles.Float, CultureInfo.InvariantCulture) + : SampleRandHelper.GenerateSampleRand(context.TraceId.ToString()) + }; // If the hub is disabled, we will always sample out. In other words, starting a transaction // after disposing the hub will result in that transaction not being sent to Sentry. @@ -151,7 +156,7 @@ internal ITransactionTracer StartTransaction( if (tracesSampler(samplingContext) is { } sampleRate) { - transaction.IsSampled = _randomValuesFactory.NextBool(sampleRate); + transaction.IsSampled = SampleRandHelper.IsSampled(transaction.SampleRand.Value, sampleRate); transaction.SampleRate = sampleRate; } } @@ -160,7 +165,7 @@ internal ITransactionTracer StartTransaction( if (transaction.IsSampled == null) { var sampleRate = _options.TracesSampleRate ?? 0.0; - transaction.IsSampled = _randomValuesFactory.NextBool(sampleRate); + transaction.IsSampled = SampleRandHelper.IsSampled(transaction.SampleRand.Value, sampleRate); transaction.SampleRate = sampleRate; } diff --git a/src/Sentry/Internal/SampleRandHelper.cs b/src/Sentry/Internal/SampleRandHelper.cs new file mode 100644 index 0000000000..5e420c70f8 --- /dev/null +++ b/src/Sentry/Internal/SampleRandHelper.cs @@ -0,0 +1,15 @@ +namespace Sentry.Internal; + +internal static class SampleRandHelper +{ + internal static double GenerateSampleRand(string traceId) + => new Random(FnvHash.ComputeHash(traceId)).NextDouble(); + + internal static bool IsSampled(double sampleRand, double rate) => rate switch + { + >= 1 => true, + <= 0 => false, + _ => sampleRand < rate + }; + +} diff --git a/src/Sentry/TransactionTracer.cs b/src/Sentry/TransactionTracer.cs index e8059a2d00..c4da3d5933 100644 --- a/src/Sentry/TransactionTracer.cs +++ b/src/Sentry/TransactionTracer.cs @@ -100,6 +100,8 @@ internal set /// public double? SampleRate { get; internal set; } + internal double? SampleRand { get; set; } + /// public SentryLevel? Level { get; set; } diff --git a/test/Sentry.AspNetCore.Tests/SentryTracingMiddlewareTests.cs b/test/Sentry.AspNetCore.Tests/SentryTracingMiddlewareTests.cs index 3c2d440c7f..13b34e7385 100644 --- a/test/Sentry.AspNetCore.Tests/SentryTracingMiddlewareTests.cs +++ b/test/Sentry.AspNetCore.Tests/SentryTracingMiddlewareTests.cs @@ -1,4 +1,3 @@ -#if NETCOREAPP3_1_OR_GREATER using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; @@ -285,6 +284,7 @@ public async Task Baggage_header_propagates_to_outbound_requests(bool shouldProp const string incomingBaggage = "sentry-trace_id=75302ac48a024bde9a3b3734a82e36c8, " + "sentry-public_key=d4d82fc1c2c4032a83f3a29aa3a3aff, " + + "sentry-sample_rand=0.1234, " + "sentry-sample_rate=0.5, " + "foo-bar=abc123"; @@ -299,6 +299,7 @@ public async Task Baggage_header_propagates_to_outbound_requests(bool shouldProp "other-value=abc123, " + "sentry-trace_id=75302ac48a024bde9a3b3734a82e36c8, " + "sentry-public_key=d4d82fc1c2c4032a83f3a29aa3a3aff, " + + "sentry-sample_rand=0.1234, " + "sentry-sample_rate=0.5"; } else @@ -382,6 +383,7 @@ public async Task Baggage_header_sets_dynamic_sampling_context() const string baggage = "sentry-trace_id=75302ac48a024bde9a3b3734a82e36c8, " + "sentry-public_key=d4d82fc1c2c4032a83f3a29aa3a3aff, " + + "sentry-sample_rand=0.1234, " + "sentry-sample_rate=0.5"; // Arrange @@ -677,5 +679,3 @@ public async Task Transaction_TransactionNameProviderSetUnset_TransactionNameSet transaction.NameSource.Should().Be(TransactionNameSource.Url); } } - -#endif diff --git a/test/Sentry.Testing/VerifyExtensions.cs b/test/Sentry.Testing/VerifyExtensions.cs index 7027580f9c..c3b3a1585f 100644 --- a/test/Sentry.Testing/VerifyExtensions.cs +++ b/test/Sentry.Testing/VerifyExtensions.cs @@ -11,6 +11,7 @@ public static SettingsTask IgnoreStandardSentryMembers(this SettingsTask setting return settings .ScrubMachineName() .ScrubUserName() + .ScrubMember("sample_rand") .AddExtraSettings(_ => { _.Converters.Add(new SpansConverter()); diff --git a/test/Sentry.Tests/DynamicSamplingContextTests.cs b/test/Sentry.Tests/DynamicSamplingContextTests.cs index b23a1c4962..374be0c371 100644 --- a/test/Sentry.Tests/DynamicSamplingContextTests.cs +++ b/test/Sentry.Tests/DynamicSamplingContextTests.cs @@ -142,6 +142,105 @@ public void CreateFromBaggage_SampleRate_TooHigh() Assert.Null(dsc); } + [Fact] + public void CreateFromBaggage_SampleRand_Invalid() + { + var baggage = BaggageHeader.Create(new List> + { + {"sentry-trace_id", "43365712692146d08ee11a729dfbcaca"}, + {"sentry-public_key", "d4d82fc1c2c4032a83f3a29aa3a3aff"}, + {"sentry-sample_rate", "1.0"}, + {"sentry-sample_rand", "not-a-number"}, + }); + + var dsc = baggage.CreateDynamicSamplingContext(); + + Assert.Null(dsc); + } + + [Fact] + public void CreateFromBaggage_SampleRand_TooLow() + { + var baggage = BaggageHeader.Create(new List> + { + {"sentry-trace_id", "43365712692146d08ee11a729dfbcaca"}, + {"sentry-public_key", "d4d82fc1c2c4032a83f3a29aa3a3aff"}, + {"sentry-sample_rate", "1.0"}, + {"sentry-sample_rand", "-0.1"} + }); + + var dsc = baggage.CreateDynamicSamplingContext(); + + Assert.Null(dsc); + } + + [Fact] + public void CreateFromBaggage_SampleRand_TooHigh() + { + var baggage = BaggageHeader.Create(new List> + { + {"sentry-trace_id", "43365712692146d08ee11a729dfbcaca"}, + {"sentry-public_key", "d4d82fc1c2c4032a83f3a29aa3a3aff"}, + {"sentry-sample_rate", "1.0"}, + {"sentry-sample_rand", "1.0"} // Must be less than 1 + }); + + var dsc = baggage.CreateDynamicSamplingContext(); + + Assert.Null(dsc); + } + + [Fact] + public void CreateFromBaggage_NotSampledNoSampleRand_GeneratesSampleRand() + { + var baggage = BaggageHeader.Create(new List> + { + {"sentry-trace_id", "43365712692146d08ee11a729dfbcaca"}, + {"sentry-public_key", "d4d82fc1c2c4032a83f3a29aa3a3aff"}, + {"sentry-sample_rate", "0.5"} + }); + + var dsc = baggage.CreateDynamicSamplingContext(); + + using var scope = new AssertionScope(); + Assert.NotNull(dsc); + var sampleRandItem = Assert.Contains("sample_rand", dsc.Items); + var sampleRand = double.Parse(sampleRandItem, NumberStyles.Float, CultureInfo.InvariantCulture); + Assert.True(sampleRand >= 0.0); + Assert.True(sampleRand < 1.0); + } + + [Theory] + [InlineData("true")] + [InlineData("false")] + public void CreateFromBaggage_SampledNoSampleRand_GeneratesConsistentSampleRand(string sampled) + { + var baggage = BaggageHeader.Create(new List> + { + {"sentry-trace_id", "43365712692146d08ee11a729dfbcaca"}, + {"sentry-public_key", "d4d82fc1c2c4032a83f3a29aa3a3aff"}, + {"sentry-sample_rate", "0.5"}, + {"sentry-sampled", sampled}, + }); + + var dsc = baggage.CreateDynamicSamplingContext(); + + using var scope = new AssertionScope(); + Assert.NotNull(dsc); + var sampleRandItem = Assert.Contains("sample_rand", dsc.Items); + var sampleRand = double.Parse(sampleRandItem, NumberStyles.Float, CultureInfo.InvariantCulture); + if (sampled == "true") + { + Assert.True(sampleRand >= 0.0); + Assert.True(sampleRand < 0.5); + } + else + { + Assert.True(sampleRand >= 0.5); + Assert.True(sampleRand < 1.0); + } + } + [Fact] public void CreateFromBaggage_Sampled_MalFormed() { @@ -171,10 +270,11 @@ public void CreateFromBaggage_Valid_Minimum() var dsc = baggage.CreateDynamicSamplingContext(); Assert.NotNull(dsc); - Assert.Equal(3, dsc.Items.Count); + Assert.Equal(4, dsc.Items.Count); Assert.Equal("43365712692146d08ee11a729dfbcaca", Assert.Contains("trace_id", dsc.Items)); Assert.Equal("d4d82fc1c2c4032a83f3a29aa3a3aff", Assert.Contains("public_key", dsc.Items)); Assert.Equal("1.0", Assert.Contains("sample_rate", dsc.Items)); + Assert.Contains("sample_rand", dsc.Items); } [Fact] @@ -186,6 +286,7 @@ public void CreateFromBaggage_Valid_Complete() {"sentry-public_key", "d4d82fc1c2c4032a83f3a29aa3a3aff"}, {"sentry-sampled", "true"}, {"sentry-sample_rate", "1.0"}, + {"sentry-sample_rand", "0.1234"}, {"sentry-release", "test@1.0.0+abc"}, {"sentry-environment", "production"}, {"sentry-user_segment", "Group B"}, @@ -200,6 +301,7 @@ public void CreateFromBaggage_Valid_Complete() Assert.Equal("d4d82fc1c2c4032a83f3a29aa3a3aff", Assert.Contains("public_key", dsc.Items)); Assert.Equal("true", Assert.Contains("sampled", dsc.Items)); Assert.Equal("1.0", Assert.Contains("sample_rate", dsc.Items)); + Assert.Equal("0.1234", Assert.Contains("sample_rand", dsc.Items)); Assert.Equal("test@1.0.0+abc", Assert.Contains("release", dsc.Items)); Assert.Equal("production", Assert.Contains("environment", dsc.Items)); Assert.Equal("Group B", Assert.Contains("user_segment", dsc.Items)); @@ -214,6 +316,7 @@ public void ToBaggageHeader() {"sentry-trace_id", "43365712692146d08ee11a729dfbcaca"}, {"sentry-public_key", "d4d82fc1c2c4032a83f3a29aa3a3aff"}, {"sentry-sample_rate", "1.0"}, + {"sentry-sample_rand", "0.1234"}, {"sentry-release", "test@1.0.0+abc"}, {"sentry-environment", "production"}, {"sentry-user_segment", "Group B"}, @@ -253,6 +356,7 @@ public void CreateFromTransaction(bool? isSampled) NameSource = TransactionNameSource.Route, IsSampled = isSampled, SampleRate = 0.5, + SampleRand = (isSampled ?? true) ? 0.4000 : 0.6000, // Lower than the sample rate means sampled == true User = { }, @@ -261,7 +365,7 @@ public void CreateFromTransaction(bool? isSampled) var dsc = transaction.CreateDynamicSamplingContext(options); Assert.NotNull(dsc); - Assert.Equal(isSampled.HasValue ? 7 : 6, dsc.Items.Count); + Assert.Equal(isSampled.HasValue ? 8 : 7, dsc.Items.Count); Assert.Equal(traceId.ToString(), Assert.Contains("trace_id", dsc.Items)); Assert.Equal("d4d82fc1c2c4032a83f3a29aa3a3aff", Assert.Contains("public_key", dsc.Items)); if (transaction.IsSampled is { } sampled) @@ -273,6 +377,7 @@ public void CreateFromTransaction(bool? isSampled) Assert.DoesNotContain("sampled", dsc.Items); } Assert.Equal("0.5", Assert.Contains("sample_rate", dsc.Items)); + Assert.Equal((isSampled ?? true) ? "0.4000" : "0.6000", Assert.Contains("sample_rand", dsc.Items)); Assert.Equal("foo@2.4.5", Assert.Contains("release", dsc.Items)); Assert.Equal("staging", Assert.Contains("environment", dsc.Items)); Assert.Equal("GET /person/{id}", Assert.Contains("transaction", dsc.Items)); diff --git a/test/Sentry.Tests/EventProcessorTests.WithTransaction.verified.txt b/test/Sentry.Tests/EventProcessorTests.WithTransaction.verified.txt index af9d84138f..a39b4497ea 100644 --- a/test/Sentry.Tests/EventProcessorTests.WithTransaction.verified.txt +++ b/test/Sentry.Tests/EventProcessorTests.WithTransaction.verified.txt @@ -9,6 +9,7 @@ environment: production, public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, release: release, + sample_rand: {Scrubbed}, sample_rate: 1, sampled: true, trace_id: Guid_2, diff --git a/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.DotNet8_0.verified.txt b/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.DotNet8_0.verified.txt index 34439cfbcc..c510d50d43 100644 --- a/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.DotNet8_0.verified.txt +++ b/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.DotNet8_0.verified.txt @@ -31,6 +31,7 @@ environment: production, public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, release: release, + sample_rand: {Scrubbed}, sample_rate: 1, sampled: true, trace_id: Guid_3, @@ -146,6 +147,7 @@ environment: production, public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, release: release, + sample_rand: {Scrubbed}, sample_rate: 1, sampled: true, trace_id: Guid_3, diff --git a/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.DotNet9_0.verified.txt b/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.DotNet9_0.verified.txt index 34439cfbcc..c510d50d43 100644 --- a/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.DotNet9_0.verified.txt +++ b/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.DotNet9_0.verified.txt @@ -31,6 +31,7 @@ environment: production, public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, release: release, + sample_rand: {Scrubbed}, sample_rate: 1, sampled: true, trace_id: Guid_3, @@ -146,6 +147,7 @@ environment: production, public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, release: release, + sample_rand: {Scrubbed}, sample_rate: 1, sampled: true, trace_id: Guid_3, diff --git a/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.Net4_8.verified.txt b/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.Net4_8.verified.txt index b453712534..f41bcaa626 100644 --- a/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.Net4_8.verified.txt +++ b/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.Net4_8.verified.txt @@ -31,6 +31,7 @@ environment: production, public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, release: release, + sample_rand: {Scrubbed}, sample_rate: 1, sampled: true, trace_id: Guid_3, @@ -146,6 +147,7 @@ environment: production, public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, release: release, + sample_rand: {Scrubbed}, sample_rate: 1, sampled: true, trace_id: Guid_3, diff --git a/test/Sentry.Tests/HubTests.cs b/test/Sentry.Tests/HubTests.cs index 17206a5c4f..b00cc89e7f 100644 --- a/test/Sentry.Tests/HubTests.cs +++ b/test/Sentry.Tests/HubTests.cs @@ -680,6 +680,136 @@ public void StartTransaction_SameInstrumenter_SampledIn() transaction.IsSampled.Should().BeTrue(); } + [Fact] + public void StartTransaction_NoDynamicSamplingContext_GeneratesSampleRand() + { + // Arrange + var transactionContext = new TransactionContext("name", "operation"); + var customContext = new Dictionary(); + + var hub = _fixture.GetSut(); + + // Act + var transaction = hub.StartTransaction(transactionContext, customContext); + + // Assert + var transactionTracer = ((TransactionTracer)transaction); + transactionTracer.SampleRand.Should().NotBeNull(); + transactionTracer.DynamicSamplingContext.Should().NotBeNull(); + transactionTracer.DynamicSamplingContext!.Items.Should().ContainKey("sample_rand"); + transactionTracer.DynamicSamplingContext.Items["sample_rand"].Should().Be(transactionTracer.SampleRand!.Value.ToString("N4", CultureInfo.InvariantCulture)); + } + + [Fact] + public void StartTransaction_DynamicSamplingContextWithoutSampleRand_SampleRandNotPropagated() + { + // Arrange + var transactionContext = new TransactionContext("name", "operation"); + var customContext = new Dictionary(); + + var hub = _fixture.GetSut(); + + // Act + var transaction = hub.StartTransaction(transactionContext, customContext, DynamicSamplingContext.Empty); + + // Assert + var transactionTracer = ((TransactionTracer)transaction); + transactionTracer.SampleRand.Should().NotBeNull(); + transactionTracer.DynamicSamplingContext.Should().NotBeNull(); + // See https://develop.sentry.dev/sdk/telemetry/traces/dynamic-sampling-context/#freezing-dynamic-sampling-context + transactionTracer.DynamicSamplingContext!.Items.Should().NotContainKey("sample_rand"); + } + + [Fact] + public void StartTransaction_DynamicSamplingContextWithSampleRand_InheritsSampleRand() + { + // Arrange + var transactionContext = new TransactionContext("name", "operation"); + var customContext = new Dictionary(); + var dsc = BaggageHeader.Create(new List> + { + {"sentry-trace_id", "43365712692146d08ee11a729dfbcaca"}, + {"sentry-public_key", "d4d82fc1c2c4032a83f3a29aa3a3aff"}, + {"sentry-sampled", "true"}, + {"sentry-sample_rate", "0.5"}, // Required in the baggage header, but ignored by sampling logic + {"sentry-sample_rand", "0.1234"} + }).CreateDynamicSamplingContext(); + + _fixture.Options.TracesSampleRate = 0.4; + var hub = _fixture.GetSut(); + + // Act + var transaction = hub.StartTransaction(transactionContext, customContext, dsc); + + // Assert + var transactionTracer = ((TransactionTracer)transaction); + transactionTracer.IsSampled.Should().Be(true); + transactionTracer.SampleRate.Should().Be(0.4); + transactionTracer.SampleRand.Should().Be(0.1234); + transactionTracer.DynamicSamplingContext.Should().Be(dsc); + } + + [Theory] + [InlineData(0.1, false)] + [InlineData(0.2, true)] + public void StartTransaction_TraceSampler_UsesSampleRand(double sampleRate, bool expectedIsSampled) + { + // Arrange + var transactionContext = new TransactionContext("name", "operation"); + var customContext = new Dictionary(); + var dsc = BaggageHeader.Create(new List> + { + {"sentry-trace_id", "43365712692146d08ee11a729dfbcaca"}, + {"sentry-public_key", "d4d82fc1c2c4032a83f3a29aa3a3aff"}, + {"sentry-sampled", "true"}, + {"sentry-sample_rate", "0.5"}, + {"sentry-sample_rand", "0.1234"} + }).CreateDynamicSamplingContext(); + + _fixture.Options.TracesSampler = _ => sampleRate; + var hub = _fixture.GetSut(); + + // Act + var transaction = hub.StartTransaction(transactionContext, customContext, dsc); + + // Assert + var transactionTracer = ((TransactionTracer)transaction); + transactionTracer.IsSampled.Should().Be(expectedIsSampled); + transactionTracer.SampleRate.Should().Be(sampleRate); + transactionTracer.SampleRand.Should().Be(0.1234); + transactionTracer.DynamicSamplingContext.Should().Be(dsc); + } + + [Theory] + [InlineData(0.1, false)] + [InlineData(0.2, true)] + public void StartTransaction_StaticSampler_UsesSampleRand(double sampleRate, bool expectedIsSampled) + { + // Arrange + var transactionContext = new TransactionContext("name", "operation"); + var customContext = new Dictionary(); + var dsc = BaggageHeader.Create(new List> + { + {"sentry-trace_id", "43365712692146d08ee11a729dfbcaca"}, + {"sentry-public_key", "d4d82fc1c2c4032a83f3a29aa3a3aff"}, + {"sentry-sample_rate", "0.5"}, // Static sampling ignores this and uses options.TracesSampleRate instead + {"sentry-sample_rand", "0.1234"} + }).CreateDynamicSamplingContext(); + + _fixture.Options.TracesSampleRate = sampleRate; + var hub = _fixture.GetSut(); + + // Act + var transaction = hub.StartTransaction(transactionContext, customContext, dsc); + + // Assert + var transactionTracer = ((TransactionTracer)transaction); + transactionTracer.IsSampled.Should().Be(expectedIsSampled); + transactionTracer.SampleRate.Should().Be(sampleRate); + transactionTracer.SampleRand.Should().Be(0.1234); + transactionTracer.DynamicSamplingContext.Should().Be(dsc); + } + [Fact] public void StartTransaction_DifferentInstrumenter_SampledIn() { diff --git a/test/Sentry.Tests/Internals/FnvHashTests.cs b/test/Sentry.Tests/Internals/FnvHashTests.cs new file mode 100644 index 0000000000..757882c3de --- /dev/null +++ b/test/Sentry.Tests/Internals/FnvHashTests.cs @@ -0,0 +1,24 @@ +namespace Sentry.Tests.Internals; + +public class FnvHashTests +{ + [Theory] + [InlineData("", 2_166_136_261)] + [InlineData("h", 3_977_000_791)] + [InlineData("he", 1_547_363_254)] + [InlineData("hel", 179_613_742)] + [InlineData("hell", 477_198_310)] + [InlineData("hello", 1_335_831_723)] + [InlineData("hello ", 3_801_292_497)] + [InlineData("hello w", 1_402_552_146)] + [InlineData("hello wo", 3_611_200_775)] + [InlineData("hello wor", 1_282_977_583)] + [InlineData("hello worl", 2_767_971_961)] + [InlineData("hello world", 3_582_672_807)] + public void ComputeHash_WithString_ReturnsExpected(string input, uint expected) + { + var actual = FnvHash.ComputeHash(input); + + Assert.Equal(unchecked((int)expected), actual); + } +} diff --git a/test/Sentry.Tests/SentryPropagationContextTests.cs b/test/Sentry.Tests/SentryPropagationContextTests.cs index 89dfa87957..dc658d2ed9 100644 --- a/test/Sentry.Tests/SentryPropagationContextTests.cs +++ b/test/Sentry.Tests/SentryPropagationContextTests.cs @@ -83,6 +83,7 @@ public void CreateFromHeaders_BaggageHeaderNotNull_CreatesPropagationContextWith var baggageHeader = BaggageHeader.Create(new List> { { "sentry-sample_rate", "1.0" }, + { "sentry-sample_rand", "0.1234" }, { "sentry-trace_id", "75302ac48a024bde9a3b3734a82e36c8" }, { "sentry-public_key", "d4d82fc1c2c4032a83f3a29aa3a3aff" }, { "sentry-replay_id", "bfd31b89a59d41c99d96dc2baf840ecd" } @@ -90,6 +91,6 @@ public void CreateFromHeaders_BaggageHeaderNotNull_CreatesPropagationContextWith var propagationContext = SentryPropagationContext.CreateFromHeaders(null, traceHeader, baggageHeader); - Assert.Equal(4, propagationContext.GetOrCreateDynamicSamplingContext(new SentryOptions()).Items.Count); + Assert.Equal(5, propagationContext.GetOrCreateDynamicSamplingContext(new SentryOptions()).Items.Count); } } diff --git a/test/Sentry.Tests/TransactionProcessorTests.Discard.verified.txt b/test/Sentry.Tests/TransactionProcessorTests.Discard.verified.txt index 6ffc1f77f4..1c801c9b72 100644 --- a/test/Sentry.Tests/TransactionProcessorTests.Discard.verified.txt +++ b/test/Sentry.Tests/TransactionProcessorTests.Discard.verified.txt @@ -9,6 +9,7 @@ environment: production, public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, release: release, + sample_rand: {Scrubbed}, sample_rate: 1, sampled: true, trace_id: Guid_2, diff --git a/test/Sentry.Tests/TransactionProcessorTests.Simple.verified.txt b/test/Sentry.Tests/TransactionProcessorTests.Simple.verified.txt index b8a5f7a62b..0e9cc2a73b 100644 --- a/test/Sentry.Tests/TransactionProcessorTests.Simple.verified.txt +++ b/test/Sentry.Tests/TransactionProcessorTests.Simple.verified.txt @@ -9,6 +9,7 @@ environment: production, public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, release: release, + sample_rand: {Scrubbed}, sample_rate: 1, sampled: true, trace_id: Guid_2, @@ -53,6 +54,7 @@ environment: production, public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, release: release, + sample_rand: {Scrubbed}, sample_rate: 1, sampled: true, trace_id: Guid_2, From 2a798a803b85c11dc8feec123a0490961c70d3fe Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Thu, 20 Feb 2025 11:05:25 +1300 Subject: [PATCH 136/363] Sync Serilog scope properties to Sentry events (#3976) --- CHANGELOG.md | 1 + src/Sentry.Serilog/SentryOptionExtensions.cs | 21 +++++++ src/Sentry.Serilog/SentrySinkExtensions.cs | 8 +++ .../SerilogScopeEventProcessor.cs | 57 +++++++++++++++++++ ...piApprovalTests.Run.DotNet8_0.verified.txt | 5 ++ ...piApprovalTests.Run.DotNet9_0.verified.txt | 5 ++ .../ApiApprovalTests.Run.Net4_8.verified.txt | 5 ++ .../SerilogScopeEventProcessorTests.cs | 32 +++++++++++ 8 files changed, 134 insertions(+) create mode 100644 src/Sentry.Serilog/SentryOptionExtensions.cs create mode 100644 src/Sentry.Serilog/SerilogScopeEventProcessor.cs create mode 100644 test/Sentry.Serilog.Tests/SerilogScopeEventProcessorTests.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 6688a7bc47..4ef14d6c76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Features +- Serilog scope properties are now sent with Sentry events ([#3976](https://github.com/getsentry/sentry-dotnet/pull/3976)) - The sample seed used for sampling decisions is now propagated, for use in downstream custom trace samplers ([#3951](https://github.com/getsentry/sentry-dotnet/pull/3951)) ### Fixes diff --git a/src/Sentry.Serilog/SentryOptionExtensions.cs b/src/Sentry.Serilog/SentryOptionExtensions.cs new file mode 100644 index 0000000000..94dfbc5212 --- /dev/null +++ b/src/Sentry.Serilog/SentryOptionExtensions.cs @@ -0,0 +1,21 @@ +namespace Sentry.Serilog; + +/// +/// Extensions for to add Serilog specific configuration. +/// +public static class SentryOptionExtensions +{ + /// + /// Ensures Serilog scope properties get applied to Sentry events. If you are not initialising Sentry when + /// configuring the Sentry sink for Serilog then you should call this method in the options callback for whichever + /// Sentry integration you are using to initialise Sentry. + /// + /// + /// + /// + public static T ApplySerilogScopeToEvents(this T options) where T : SentryOptions + { + options.AddEventProcessor(new SerilogScopeEventProcessor(options)); + return options; + } +} diff --git a/src/Sentry.Serilog/SentrySinkExtensions.cs b/src/Sentry.Serilog/SentrySinkExtensions.cs index f023819a41..3eb5b56c3a 100644 --- a/src/Sentry.Serilog/SentrySinkExtensions.cs +++ b/src/Sentry.Serilog/SentrySinkExtensions.cs @@ -326,6 +326,14 @@ internal static void ConfigureSentrySerilogOptions( sentrySerilogOptions.DefaultTags.Add(tag.Key, tag.Value); } } + + // This only works when the SDK is initialized using the LoggerSinkConfiguration extensions. If the SDK is + // initialized using some other integration then the processor will need to be added manually to whichever + // options are used to initialize the SDK. + if (sentrySerilogOptions.InitializeSdk) + { + sentrySerilogOptions.ApplySerilogScopeToEvents(); + } } /// diff --git a/src/Sentry.Serilog/SerilogScopeEventProcessor.cs b/src/Sentry.Serilog/SerilogScopeEventProcessor.cs new file mode 100644 index 0000000000..4b3de83fe0 --- /dev/null +++ b/src/Sentry.Serilog/SerilogScopeEventProcessor.cs @@ -0,0 +1,57 @@ +using Serilog.Context; + +namespace Sentry.Serilog; + +/// +/// Sentry event processor that applies properties from the Serilog scope to Sentry events. +/// +internal class SerilogScopeEventProcessor : ISentryEventProcessor +{ + private readonly SentryOptions _options; + + /// + /// This processor extracts properties from the Serilog context and applies these to Sentry events. + /// + public SerilogScopeEventProcessor(SentryOptions options) + { + _options = options; + _options.LogDebug("Initializing Serilog scope event processor."); + } + + /// + public SentryEvent Process(SentryEvent @event) + { + _options.LogDebug("Running Serilog scope event processor on: Event {0}", @event.EventId); + + // This is a bit of a hack. Serilog doesn't have any hooks that let us inspect the context. We can, however, + // apply the context to a dummy log event and then copy across the properties from that log event to our Sentry + // event. + // See: https://github.com/getsentry/sentry-dotnet/issues/3544#issuecomment-2307884977 + var enricher = LogContext.Clone(); + var logEvent = new LogEvent(DateTimeOffset.Now, LogEventLevel.Error, null, MessageTemplate.Empty, []); + enricher.Enrich(logEvent, new LogEventPropertyFactory()); + foreach (var (key, value) in logEvent.Properties) + { + if (!@event.Tags.ContainsKey(key)) + { + // Potentially we could be doing SetData here instead of SetTag. See DefaultSentryScopeStateProcessor. + @event.SetTag( + key, + value is ScalarValue { Value: string stringValue } + ? stringValue + : value.ToString() + ); + } + } + return @event; + } + + private class LogEventPropertyFactory : ILogEventPropertyFactory + { + public LogEventProperty CreateProperty(string name, object? value, bool destructureObjects = false) + { + var scalarValue = new ScalarValue(value); + return new LogEventProperty(name, scalarValue); + } + } +} diff --git a/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt b/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt index 893bc8a67e..1455bbc51b 100644 --- a/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt +++ b/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt @@ -1,6 +1,11 @@ [assembly: System.CLSCompliant(true)] namespace Sentry.Serilog { + public static class SentryOptionExtensions + { + public static T ApplySerilogScopeToEvents(this T options) + where T : Sentry.SentryOptions { } + } public class SentrySerilogOptions : Sentry.SentryOptions { public SentrySerilogOptions() { } diff --git a/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt b/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt index 893bc8a67e..1455bbc51b 100644 --- a/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt +++ b/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt @@ -1,6 +1,11 @@ [assembly: System.CLSCompliant(true)] namespace Sentry.Serilog { + public static class SentryOptionExtensions + { + public static T ApplySerilogScopeToEvents(this T options) + where T : Sentry.SentryOptions { } + } public class SentrySerilogOptions : Sentry.SentryOptions { public SentrySerilogOptions() { } diff --git a/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.Net4_8.verified.txt b/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.Net4_8.verified.txt index 893bc8a67e..1455bbc51b 100644 --- a/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.Net4_8.verified.txt +++ b/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.Net4_8.verified.txt @@ -1,6 +1,11 @@ [assembly: System.CLSCompliant(true)] namespace Sentry.Serilog { + public static class SentryOptionExtensions + { + public static T ApplySerilogScopeToEvents(this T options) + where T : Sentry.SentryOptions { } + } public class SentrySerilogOptions : Sentry.SentryOptions { public SentrySerilogOptions() { } diff --git a/test/Sentry.Serilog.Tests/SerilogScopeEventProcessorTests.cs b/test/Sentry.Serilog.Tests/SerilogScopeEventProcessorTests.cs new file mode 100644 index 0000000000..378849dc9f --- /dev/null +++ b/test/Sentry.Serilog.Tests/SerilogScopeEventProcessorTests.cs @@ -0,0 +1,32 @@ +using Microsoft.Extensions.Logging; + +namespace Sentry.Serilog.Tests; + +public class SerilogScopeEventProcessorTests +{ + [Theory] + [InlineData("42", "42")] + [InlineData(42, "42")] + public void Emit_WithException_CreatesEventWithException(object value, string expected) + { + // Arrange + var options = new SentryOptions(); + var sut = new SerilogScopeEventProcessor(options); + + using var log = new LoggerConfiguration().CreateLogger(); + var factory = new LoggerFactory().AddSerilog(log); + var logger = factory.CreateLogger(); + + // Act + SentryEvent evt; + using (logger.BeginScope(new Dictionary { ["Answer"] = value })) + { + evt = new SentryEvent(); + sut.Process(evt); + } + + // Assert + evt.Tags.Should().ContainKey("Answer"); + evt.Tags["Answer"].Should().Be(expected); + } +} From 80593663bf69cea8d4299c36249353f407a10252 Mon Sep 17 00:00:00 2001 From: Allan Ritchie Date: Mon, 24 Feb 2025 16:58:22 -0500 Subject: [PATCH 137/363] Fix fromjson serialization of breadcrumbs from android (#3993) * Fix fromjson serialization of breadcrumbs from android * Update CHANGELOG.md --- CHANGELOG.md | 2 ++ src/Sentry/Breadcrumb.cs | 12 +++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ef14d6c76..e52220cf69 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +- Fix mismapped breadcrumb levels coming in from native to dotnet SDK ([#3993](https://github.com/getsentry/sentry-dotnet/pull/3993)) + ### Features - Serilog scope properties are now sent with Sentry events ([#3976](https://github.com/getsentry/sentry-dotnet/pull/3976)) diff --git a/src/Sentry/Breadcrumb.cs b/src/Sentry/Breadcrumb.cs index e7d447edf6..b3b58606b0 100644 --- a/src/Sentry/Breadcrumb.cs +++ b/src/Sentry/Breadcrumb.cs @@ -157,8 +157,18 @@ public static Breadcrumb FromJson(JsonElement json) var type = json.GetPropertyOrNull("type")?.GetString(); var data = json.GetPropertyOrNull("data")?.GetStringDictionaryOrNull(); var category = json.GetPropertyOrNull("category")?.GetString(); - var level = json.GetPropertyOrNull("level")?.GetString()?.ParseEnum() ?? default; + var levelString = json.GetPropertyOrNull("level")?.GetString(); + var level = levelString?.ToUpper() switch + { + "DEBUG" => BreadcrumbLevel.Debug, + "INFO" => BreadcrumbLevel.Info, + "WARNING" => BreadcrumbLevel.Warning, + "ERROR" => BreadcrumbLevel.Error, + "CRITICAL" => BreadcrumbLevel.Critical, + "FATAL" => BreadcrumbLevel.Critical, + _ => default + }; return new Breadcrumb(timestamp, message, type, data!, category, level); } } From 29b34ee2ab9bdec3ef56c47877dfc47e19d5606e Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Tue, 25 Feb 2025 12:02:15 +1300 Subject: [PATCH 138/363] fix CHANGELOG.md (#4001) --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e52220cf69..573f2ffda9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,16 +2,16 @@ ## Unreleased -- Fix mismapped breadcrumb levels coming in from native to dotnet SDK ([#3993](https://github.com/getsentry/sentry-dotnet/pull/3993)) ### Features - Serilog scope properties are now sent with Sentry events ([#3976](https://github.com/getsentry/sentry-dotnet/pull/3976)) - The sample seed used for sampling decisions is now propagated, for use in downstream custom trace samplers ([#3951](https://github.com/getsentry/sentry-dotnet/pull/3951)) +- Add Azure Function UseSentry overloads for easier wire ups ([#3971](https://github.com/getsentry/sentry-dotnet/pull/3971)) ### Fixes -- Add Azure Function UseSentry overloads for easier wire ups ([#3971](https://github.com/getsentry/sentry-dotnet/pull/3971)) +- Fix mismapped breadcrumb levels coming in from native to dotnet SDK ([#3993](https://github.com/getsentry/sentry-dotnet/pull/3993)) ### Dependencies From 4874a09f6c6f0a66f9541a5c0dd3c673bced4e76 Mon Sep 17 00:00:00 2001 From: getsentry-bot Date: Mon, 24 Feb 2025 23:43:08 +0000 Subject: [PATCH 139/363] release: 5.2.0 --- CHANGELOG.md | 3 +-- Directory.Build.props | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 573f2ffda9..6bd1a8a138 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,6 @@ # Changelog -## Unreleased - +## 5.2.0 ### Features diff --git a/Directory.Build.props b/Directory.Build.props index ce40ac61b9..fdf535fdec 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,7 +1,7 @@ - 5.1.1 + 5.2.0 13 true true From 738626be43ae7fdc81718d85a49529e4f1b38d4d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 25 Feb 2025 18:07:02 +1300 Subject: [PATCH 140/363] chore: update scripts/update-cli.ps1 to 2.42.2 (#4002) Co-authored-by: GitHub --- CHANGELOG.md | 6 +++--- Directory.Build.props | 2 +- src/Sentry/Sentry.csproj | 14 +++++++------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 573f2ffda9..fa12aa3f58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,9 +15,9 @@ ### Dependencies -- Bump CLI from v2.41.1 to v2.42.1 ([#3979](https://github.com/getsentry/sentry-dotnet/pull/3979)) - - [changelog](https://github.com/getsentry/sentry-cli/blob/master/CHANGELOG.md#2421) - - [diff](https://github.com/getsentry/sentry-cli/compare/2.41.1...2.42.1) +- Bump CLI from v2.41.1 to v2.42.2 ([#3979](https://github.com/getsentry/sentry-dotnet/pull/3979), [#4002](https://github.com/getsentry/sentry-dotnet/pull/4002)) + - [changelog](https://github.com/getsentry/sentry-cli/blob/master/CHANGELOG.md#2422) + - [diff](https://github.com/getsentry/sentry-cli/compare/2.41.1...2.42.2) ## 5.1.1 diff --git a/Directory.Build.props b/Directory.Build.props index ce40ac61b9..08e5dc6d83 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -83,7 +83,7 @@ - 2.42.1 + 2.42.2 $(MSBuildThisFileDirectory)tools\sentry-cli\$(SentryCLIVersion)\ diff --git a/src/Sentry/Sentry.csproj b/src/Sentry/Sentry.csproj index 2799c161c5..2562974f85 100644 --- a/src/Sentry/Sentry.csproj +++ b/src/Sentry/Sentry.csproj @@ -120,13 +120,13 @@ <_OSArchitecture>$([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture) - - - - - - - + + + + + + + From 0f24f0dc6896bdcacd4b5f38dfd001f4150b636c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 25 Feb 2025 18:08:40 +1300 Subject: [PATCH 141/363] chore: update modules/sentry-native to 0.8.0 (#4003) Co-authored-by: GitHub Co-authored-by: James Crosswell --- CHANGELOG.md | 3 +++ modules/sentry-native | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fa12aa3f58..ee70eb44fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,9 @@ - Bump CLI from v2.41.1 to v2.42.2 ([#3979](https://github.com/getsentry/sentry-dotnet/pull/3979), [#4002](https://github.com/getsentry/sentry-dotnet/pull/4002)) - [changelog](https://github.com/getsentry/sentry-cli/blob/master/CHANGELOG.md#2422) - [diff](https://github.com/getsentry/sentry-cli/compare/2.41.1...2.42.2) +- Bump Native SDK from v0.7.20 to v0.8.0 ([#4003](https://github.com/getsentry/sentry-dotnet/pull/4003)) + - [changelog](https://github.com/getsentry/sentry-native/blob/master/CHANGELOG.md#080) + - [diff](https://github.com/getsentry/sentry-native/compare/0.7.20...0.8.0) ## 5.1.1 diff --git a/modules/sentry-native b/modules/sentry-native index 69569b5eca..2a3bdfb9e9 160000 --- a/modules/sentry-native +++ b/modules/sentry-native @@ -1 +1 @@ -Subproject commit 69569b5eca87b5c2afd1ce5a0df7b01ded85023b +Subproject commit 2a3bdfb9e9899574ee2558df2eea39dfa097f103 From fbdcce90ab56f94bf9c052ba9335e9c6bc6c70b9 Mon Sep 17 00:00:00 2001 From: Allan Ritchie Date: Tue, 25 Feb 2025 18:18:11 -0500 Subject: [PATCH 142/363] Add element binding extensibility (#3997) * Add element binding extensibility * Update CHANGELOG.md * Add missed test updates * Fix tests * Add additional image button binder and tests * Update verified api approval tests --- CHANGELOG.md | 1 + src/Sentry.Maui/IMauiElementEventBinder.cs | 35 ++++++++++ .../Internal/MauiButtonEventsBinder.cs | 41 +++++++++++ src/Sentry.Maui/Internal/MauiEventsBinder.cs | 68 +++++++++---------- .../Internal/MauiImageButtonEventsBinder.cs | 41 +++++++++++ .../SentryMauiAppBuilderExtensions.cs | 3 + ...piApprovalTests.Run.DotNet8_0.verified.txt | 32 +++++++++ ...piApprovalTests.Run.DotNet9_0.verified.txt | 32 +++++++++ .../MauiEventsBinderTests.Button.cs | 9 ++- .../MauiEventsBinderTests.ImageButton.cs | 58 ++++++++++++++++ .../MauiEventsBinderTests.cs | 9 ++- 11 files changed, 289 insertions(+), 40 deletions(-) create mode 100644 src/Sentry.Maui/IMauiElementEventBinder.cs create mode 100644 src/Sentry.Maui/Internal/MauiButtonEventsBinder.cs create mode 100644 src/Sentry.Maui/Internal/MauiImageButtonEventsBinder.cs create mode 100644 test/Sentry.Maui.Tests/MauiEventsBinderTests.ImageButton.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index ee70eb44fd..775f3248c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Features +- Users can now register their own MAUI controls for breadcrumb creation ([#3997](https://github.com/getsentry/sentry-dotnet/pull/3997)) - Serilog scope properties are now sent with Sentry events ([#3976](https://github.com/getsentry/sentry-dotnet/pull/3976)) - The sample seed used for sampling decisions is now propagated, for use in downstream custom trace samplers ([#3951](https://github.com/getsentry/sentry-dotnet/pull/3951)) - Add Azure Function UseSentry overloads for easier wire ups ([#3971](https://github.com/getsentry/sentry-dotnet/pull/3971)) diff --git a/src/Sentry.Maui/IMauiElementEventBinder.cs b/src/Sentry.Maui/IMauiElementEventBinder.cs new file mode 100644 index 0000000000..2fe12e1a76 --- /dev/null +++ b/src/Sentry.Maui/IMauiElementEventBinder.cs @@ -0,0 +1,35 @@ +namespace Sentry.Maui; + +/// +/// Bind to MAUI controls to generate breadcrumbs and other metrics +/// +public interface IMauiElementEventBinder +{ + /// + /// Bind to an element + /// + /// + /// + /// This adds a breadcrumb to the sentry hub + /// NOTE: we will override the type, timestamp, and category of the breadcrumb + /// + void Bind(VisualElement element, Action addBreadcrumb); + + /// + /// Unbind the element because MAUI is removing the page + /// + /// + void UnBind(VisualElement element); +} + +/// +/// Breadcrumb arguments +/// +/// +/// +/// +public record BreadcrumbEvent( + object? Sender, + string EventName, + params IEnumerable<(string Key, string Value)>[] ExtraData +); diff --git a/src/Sentry.Maui/Internal/MauiButtonEventsBinder.cs b/src/Sentry.Maui/Internal/MauiButtonEventsBinder.cs new file mode 100644 index 0000000000..f9b1dcbcb6 --- /dev/null +++ b/src/Sentry.Maui/Internal/MauiButtonEventsBinder.cs @@ -0,0 +1,41 @@ +namespace Sentry.Maui.Internal; + +/// +public class MauiButtonEventsBinder : IMauiElementEventBinder +{ + private Action? addBreadcrumbCallback; + + /// + public void Bind(VisualElement element, Action addBreadcrumb) + { + addBreadcrumbCallback = addBreadcrumb; + + if (element is Button button) + { + button.Clicked += OnButtonOnClicked; + button.Pressed += OnButtonOnPressed; + button.Released += OnButtonOnReleased; + } + } + + /// + public void UnBind(VisualElement element) + { + if (element is Button button) + { + button.Clicked -= OnButtonOnClicked; + button.Pressed -= OnButtonOnPressed; + button.Released -= OnButtonOnReleased; + } + } + + + private void OnButtonOnClicked(object? sender, EventArgs _) + => addBreadcrumbCallback?.Invoke(new(sender, nameof(Button.Clicked))); + + private void OnButtonOnPressed(object? sender, EventArgs _) + => addBreadcrumbCallback?.Invoke(new(sender, nameof(Button.Pressed))); + + private void OnButtonOnReleased(object? sender, EventArgs _) + => addBreadcrumbCallback?.Invoke(new(sender, nameof(Button.Released))); +} diff --git a/src/Sentry.Maui/Internal/MauiEventsBinder.cs b/src/Sentry.Maui/Internal/MauiEventsBinder.cs index 093bfc095b..23605ccb2e 100644 --- a/src/Sentry.Maui/Internal/MauiEventsBinder.cs +++ b/src/Sentry.Maui/Internal/MauiEventsBinder.cs @@ -11,6 +11,7 @@ internal class MauiEventsBinder : IMauiEventsBinder { private readonly IHub _hub; private readonly SentryMauiOptions _options; + private readonly IEnumerable _elementEventBinders; // https://develop.sentry.dev/sdk/event-payloads/breadcrumbs/#breadcrumb-types // https://github.com/getsentry/sentry/blob/master/static/app/types/breadcrumbs.tsx @@ -22,10 +23,11 @@ internal class MauiEventsBinder : IMauiEventsBinder internal const string RenderingCategory = "ui.rendering"; internal const string UserActionCategory = "ui.useraction"; - public MauiEventsBinder(IHub hub, IOptions options) + public MauiEventsBinder(IHub hub, IOptions options, IEnumerable elementEventBinders) { _hub = hub; _options = options.Value; + _elementEventBinders = elementEventBinders; } public void HandleApplicationEvents(Application application, bool bind = true) @@ -70,7 +72,7 @@ public void HandleApplicationEvents(Application application, bool bind = true) } } - private void OnApplicationOnDescendantAdded(object? _, ElementEventArgs e) + internal void OnApplicationOnDescendantAdded(object? _, ElementEventArgs e) { if (_options.CreateElementEventsBreadcrumbs) { @@ -96,15 +98,30 @@ private void OnApplicationOnDescendantAdded(object? _, ElementEventArgs e) case Page page: HandlePageEvents(page); break; - case Button button: - HandleButtonEvents(button); + default: + if (e.Element is VisualElement ve) + { + foreach (var binder in _elementEventBinders) + { + binder.Bind(ve, OnBreadcrumbCreateCallback); + } + } break; - - // TODO: Attach to specific events on more control types } } - private void OnApplicationOnDescendantRemoved(object? _, ElementEventArgs e) + internal void OnBreadcrumbCreateCallback(BreadcrumbEvent breadcrumb) + { + _hub.AddBreadcrumbForEvent( + _options, + breadcrumb.Sender, + breadcrumb.EventName, + UserType, + UserActionCategory + ); + } + + internal void OnApplicationOnDescendantRemoved(object? _, ElementEventArgs e) { // All elements have a set of common events we can hook HandleElementEvents(e.Element, bind: false); @@ -127,8 +144,14 @@ private void OnApplicationOnDescendantRemoved(object? _, ElementEventArgs e) case Page page: HandlePageEvents(page, bind: false); break; - case Button button: - HandleButtonEvents(button, bind: false); + default: + if (e.Element is VisualElement ve) + { + foreach (var binder in _elementEventBinders) + { + binder.UnBind(ve); + } + } break; } } @@ -279,22 +302,6 @@ internal void HandlePageEvents(Page page, bool bind = true) } } - internal void HandleButtonEvents(Button button, bool bind = true) - { - if (bind) - { - button.Clicked += OnButtonOnClicked; - button.Pressed += OnButtonOnPressed; - button.Released += OnButtonOnReleased; - } - else - { - button.Clicked -= OnButtonOnClicked; - button.Pressed -= OnButtonOnPressed; - button.Released -= OnButtonOnReleased; - } - } - // Application Events private void OnApplicationOnPageAppearing(object? sender, Page page) => @@ -424,15 +431,4 @@ private void OnPageOnNavigatedTo(object? sender, NavigatedToEventArgs e) => private void OnPageOnLayoutChanged(object? sender, EventArgs _) => _hub.AddBreadcrumbForEvent(_options, sender, nameof(Page.LayoutChanged), SystemType, RenderingCategory); - - // Button Events - - private void OnButtonOnClicked(object? sender, EventArgs _) => - _hub.AddBreadcrumbForEvent(_options, sender, nameof(Button.Clicked), UserType, UserActionCategory); - - private void OnButtonOnPressed(object? sender, EventArgs _) => - _hub.AddBreadcrumbForEvent(_options, sender, nameof(Button.Pressed), UserType, UserActionCategory); - - private void OnButtonOnReleased(object? sender, EventArgs _) => - _hub.AddBreadcrumbForEvent(_options, sender, nameof(Button.Released), UserType, UserActionCategory); } diff --git a/src/Sentry.Maui/Internal/MauiImageButtonEventsBinder.cs b/src/Sentry.Maui/Internal/MauiImageButtonEventsBinder.cs new file mode 100644 index 0000000000..70f50005c3 --- /dev/null +++ b/src/Sentry.Maui/Internal/MauiImageButtonEventsBinder.cs @@ -0,0 +1,41 @@ +namespace Sentry.Maui.Internal; + +/// +public class MauiImageButtonEventsBinder : IMauiElementEventBinder +{ + private Action? addBreadcrumbCallback; + + /// + public void Bind(VisualElement element, Action addBreadcrumb) + { + addBreadcrumbCallback = addBreadcrumb; + + if (element is ImageButton image) + { + image.Clicked += OnButtonOnClicked; + image.Pressed += OnButtonOnPressed; + image.Released += OnButtonOnReleased; + } + } + + /// + public void UnBind(VisualElement element) + { + if (element is ImageButton image) + { + image.Clicked -= OnButtonOnClicked; + image.Pressed -= OnButtonOnPressed; + image.Released -= OnButtonOnReleased; + } + } + + + private void OnButtonOnClicked(object? sender, EventArgs _) + => addBreadcrumbCallback?.Invoke(new(sender, nameof(ImageButton.Clicked))); + + private void OnButtonOnPressed(object? sender, EventArgs _) + => addBreadcrumbCallback?.Invoke(new(sender, nameof(ImageButton.Pressed))); + + private void OnButtonOnReleased(object? sender, EventArgs _) + => addBreadcrumbCallback?.Invoke(new(sender, nameof(ImageButton.Released))); +} diff --git a/src/Sentry.Maui/SentryMauiAppBuilderExtensions.cs b/src/Sentry.Maui/SentryMauiAppBuilderExtensions.cs index 1cbefb19bf..b10eab38d7 100644 --- a/src/Sentry.Maui/SentryMauiAppBuilderExtensions.cs +++ b/src/Sentry.Maui/SentryMauiAppBuilderExtensions.cs @@ -54,6 +54,9 @@ public static MauiAppBuilder UseSentry(this MauiAppBuilder builder, services.AddSingleton(); services.AddSingleton, SentryMauiOptionsSetup>(); services.AddSingleton(); + + services.AddSingleton(); + services.AddSingleton(); services.TryAddSingleton(); services.AddSentry(); diff --git a/test/Sentry.Maui.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt b/test/Sentry.Maui.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt index 2113579db2..570aad846b 100644 --- a/test/Sentry.Maui.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt +++ b/test/Sentry.Maui.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt @@ -9,6 +9,23 @@ } namespace Sentry.Maui { + public class BreadcrumbEvent : System.IEquatable + { + public BreadcrumbEvent(object? Sender, string EventName, [System.Runtime.CompilerServices.TupleElementNames(new string[] { + "Key", + "Value"})] params System.Collections.Generic.IEnumerable>[] ExtraData) { } + public string EventName { get; init; } + [System.Runtime.CompilerServices.TupleElementNames(new string[] { + "Key", + "Value"})] + public System.Collections.Generic.IEnumerable>[] ExtraData { get; init; } + public object? Sender { get; init; } + } + public interface IMauiElementEventBinder + { + void Bind(Microsoft.Maui.Controls.VisualElement element, System.Action addBreadcrumb); + void UnBind(Microsoft.Maui.Controls.VisualElement element); + } public class SentryMauiOptions : Sentry.Extensions.Logging.SentryLoggingOptions { public SentryMauiOptions() { } @@ -19,4 +36,19 @@ namespace Sentry.Maui public bool IncludeTitleInBreadcrumbs { get; set; } public void SetBeforeScreenshotCapture(System.Func beforeCapture) { } } +} +namespace Sentry.Maui.Internal +{ + public class MauiButtonEventsBinder : Sentry.Maui.IMauiElementEventBinder + { + public MauiButtonEventsBinder() { } + public void Bind(Microsoft.Maui.Controls.VisualElement element, System.Action addBreadcrumb) { } + public void UnBind(Microsoft.Maui.Controls.VisualElement element) { } + } + public class MauiImageButtonEventsBinder : Sentry.Maui.IMauiElementEventBinder + { + public MauiImageButtonEventsBinder() { } + public void Bind(Microsoft.Maui.Controls.VisualElement element, System.Action addBreadcrumb) { } + public void UnBind(Microsoft.Maui.Controls.VisualElement element) { } + } } \ No newline at end of file diff --git a/test/Sentry.Maui.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt b/test/Sentry.Maui.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt index 2113579db2..570aad846b 100644 --- a/test/Sentry.Maui.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt +++ b/test/Sentry.Maui.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt @@ -9,6 +9,23 @@ } namespace Sentry.Maui { + public class BreadcrumbEvent : System.IEquatable + { + public BreadcrumbEvent(object? Sender, string EventName, [System.Runtime.CompilerServices.TupleElementNames(new string[] { + "Key", + "Value"})] params System.Collections.Generic.IEnumerable>[] ExtraData) { } + public string EventName { get; init; } + [System.Runtime.CompilerServices.TupleElementNames(new string[] { + "Key", + "Value"})] + public System.Collections.Generic.IEnumerable>[] ExtraData { get; init; } + public object? Sender { get; init; } + } + public interface IMauiElementEventBinder + { + void Bind(Microsoft.Maui.Controls.VisualElement element, System.Action addBreadcrumb); + void UnBind(Microsoft.Maui.Controls.VisualElement element); + } public class SentryMauiOptions : Sentry.Extensions.Logging.SentryLoggingOptions { public SentryMauiOptions() { } @@ -19,4 +36,19 @@ namespace Sentry.Maui public bool IncludeTitleInBreadcrumbs { get; set; } public void SetBeforeScreenshotCapture(System.Func beforeCapture) { } } +} +namespace Sentry.Maui.Internal +{ + public class MauiButtonEventsBinder : Sentry.Maui.IMauiElementEventBinder + { + public MauiButtonEventsBinder() { } + public void Bind(Microsoft.Maui.Controls.VisualElement element, System.Action addBreadcrumb) { } + public void UnBind(Microsoft.Maui.Controls.VisualElement element) { } + } + public class MauiImageButtonEventsBinder : Sentry.Maui.IMauiElementEventBinder + { + public MauiImageButtonEventsBinder() { } + public void Bind(Microsoft.Maui.Controls.VisualElement element, System.Action addBreadcrumb) { } + public void UnBind(Microsoft.Maui.Controls.VisualElement element) { } + } } \ No newline at end of file diff --git a/test/Sentry.Maui.Tests/MauiEventsBinderTests.Button.cs b/test/Sentry.Maui.Tests/MauiEventsBinderTests.Button.cs index a11c25b443..9fe7e2922e 100644 --- a/test/Sentry.Maui.Tests/MauiEventsBinderTests.Button.cs +++ b/test/Sentry.Maui.Tests/MauiEventsBinderTests.Button.cs @@ -15,7 +15,8 @@ public void Button_CommonEvents_AddsBreadcrumb(string eventName) { StyleId = "button" }; - _fixture.Binder.HandleButtonEvents(button); + var el = new ElementEventArgs(button); + _fixture.Binder.OnApplicationOnDescendantAdded(null, el); // Act button.RaiseEvent(eventName, EventArgs.Empty); @@ -40,11 +41,13 @@ public void Button_UnbindCommonEvents_DoesNotAddBreadcrumb(string eventName) { StyleId = "button" }; - _fixture.Binder.HandleButtonEvents(button); + var el = new ElementEventArgs(button); + _fixture.Binder.OnApplicationOnDescendantAdded(null, el); + button.RaiseEvent(eventName, EventArgs.Empty); Assert.Single(_fixture.Scope.Breadcrumbs); // Sanity check - _fixture.Binder.HandleButtonEvents(button, bind: false); + _fixture.Binder.OnApplicationOnDescendantRemoved(null, el); // Act button.RaiseEvent(eventName, EventArgs.Empty); diff --git a/test/Sentry.Maui.Tests/MauiEventsBinderTests.ImageButton.cs b/test/Sentry.Maui.Tests/MauiEventsBinderTests.ImageButton.cs new file mode 100644 index 0000000000..83f9d4aab5 --- /dev/null +++ b/test/Sentry.Maui.Tests/MauiEventsBinderTests.ImageButton.cs @@ -0,0 +1,58 @@ +using Sentry.Maui.Internal; + +namespace Sentry.Maui.Tests; + +public partial class MauiEventsBinderTests +{ + [Theory] + [InlineData(nameof(ImageButton.Clicked))] + [InlineData(nameof(ImageButton.Pressed))] + [InlineData(nameof(ImageButton.Released))] + public void ImageButton_CommonEvents_AddsBreadcrumb(string eventName) + { + // Arrange + var button = new ImageButton + { + StyleId = "button" + }; + var el = new ElementEventArgs(button); + _fixture.Binder.OnApplicationOnDescendantAdded(null, el); + + // Act + button.RaiseEvent(eventName, EventArgs.Empty); + + // Assert + var crumb = Assert.Single(_fixture.Scope.Breadcrumbs); + Assert.Equal($"{nameof(ImageButton)}.{eventName}", crumb.Message); + Assert.Equal(BreadcrumbLevel.Info, crumb.Level); + Assert.Equal(MauiEventsBinder.UserType, crumb.Type); + Assert.Equal(MauiEventsBinder.UserActionCategory, crumb.Category); + crumb.Data.Should().Contain($"{nameof(ImageButton)}.Name", "button"); + } + + [Theory] + [InlineData(nameof(ImageButton.Clicked))] + [InlineData(nameof(ImageButton.Pressed))] + [InlineData(nameof(ImageButton.Released))] + public void ImageButton_UnbindCommonEvents_DoesNotAddBreadcrumb(string eventName) + { + // Arrange + var button = new ImageButton + { + StyleId = "button" + }; + var el = new ElementEventArgs(button); + _fixture.Binder.OnApplicationOnDescendantAdded(null, el); + + button.RaiseEvent(eventName, EventArgs.Empty); + Assert.Single(_fixture.Scope.Breadcrumbs); // Sanity check + + _fixture.Binder.OnApplicationOnDescendantRemoved(null, el); + + // Act + button.RaiseEvent(eventName, EventArgs.Empty); + + // Assert + Assert.Single(_fixture.Scope.Breadcrumbs); + } +} diff --git a/test/Sentry.Maui.Tests/MauiEventsBinderTests.cs b/test/Sentry.Maui.Tests/MauiEventsBinderTests.cs index 66a57936e0..6d2c7ce1ef 100644 --- a/test/Sentry.Maui.Tests/MauiEventsBinderTests.cs +++ b/test/Sentry.Maui.Tests/MauiEventsBinderTests.cs @@ -19,7 +19,14 @@ public Fixture() .Do(c => c.Arg>()(Scope)); var options = Microsoft.Extensions.Options.Options.Create(Options); - Binder = new MauiEventsBinder(hub, options); + Binder = new MauiEventsBinder( + hub, + options, + [ + new MauiButtonEventsBinder(), + new MauiImageButtonEventsBinder() + ] + ); } } From 9c2ac27015f3369c891da6b2e4fc45444e362948 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Mon, 3 Mar 2025 10:47:53 +1300 Subject: [PATCH 143/363] Clarify the process for maintaining Ben.Demystifier (#4010) --- CONTRIBUTING.md | 18 ++++++++++++++++++ modules/Ben.Demystifier | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 560fd84716..ac3763b74a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -153,3 +153,21 @@ In CI, these packages are expected to be present, while locally, scripts will ru You can run individual tests either via Pester integration (e.g. in VS Code), or from command line: `./integration-test/cli.Tests.ps1`. Consult Pester docs for details on how to write tests. Because these tests rely on a Sentry server mock (`Invoke-SentryServer`) from , you need to check out [getsentry/github-workflows](https://github.com/getsentry/github-workflows) as a sibling directory next to your `getsentry/sentry-dotnet` checkout. + +## Maintaining the Ben.Demystifier Submodule + +This repo uses a variety of techniques to vendor in third party code without creating external dependencies. One of +those is submodules. + +One of those submodules is Ben.Demystifier, which was originally written by Ben Adams. Attempts to contact Ben in recent +years have been unsuccessful so we've started maintaining a permanent fork of the project at: +- https://github.com/getsentry/Ben.Demystifier + +Any significant changes to the submodule should be made in a branch and merged into the submodule's `main` branch. +However, many of the members of Ben.Demystifier are public. That makes sense if people are using Ben.Demystifier as a +library, but in this repo we want to keep those members internal. + +Once changes to Ben.Demystifier have been merged into the main branch then, the `internal` branch of Ben.Demystifier +should be updated from the main branch and the `modules/make-internal.sh` script run again (if necessary). This repo +should reference the most recent commit on the `internal` branch of Ben.Demystifier then (functionally identical to the +main branch - the only difference being the changes to member visibility). diff --git a/modules/Ben.Demystifier b/modules/Ben.Demystifier index fe04751006..0a6b30f6ac 160000 --- a/modules/Ben.Demystifier +++ b/modules/Ben.Demystifier @@ -1 +1 @@ -Subproject commit fe0475100614508fff1c76aa896e17807adde83f +Subproject commit 0a6b30f6ac6892ff45b9dcd5b07d710e5228256e From 7953f21b735872ee91fe21fb6371e48a4282371c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 3 Mar 2025 19:08:00 +1300 Subject: [PATCH 144/363] chore: update modules/sentry-native to 0.8.1 (#4014) Co-authored-by: GitHub --- CHANGELOG.md | 8 ++++++++ modules/sentry-native | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index abc724ccfa..10efd54d1e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## Unreleased + +### Dependencies + +- Bump Native SDK from v0.8.0 to v0.8.1 ([#4014](https://github.com/getsentry/sentry-dotnet/pull/4014)) + - [changelog](https://github.com/getsentry/sentry-native/blob/master/CHANGELOG.md#081) + - [diff](https://github.com/getsentry/sentry-native/compare/0.8.0...0.8.1) + ## 5.2.0 ### Features diff --git a/modules/sentry-native b/modules/sentry-native index 2a3bdfb9e9..ccef7125b3 160000 --- a/modules/sentry-native +++ b/modules/sentry-native @@ -1 +1 @@ -Subproject commit 2a3bdfb9e9899574ee2558df2eea39dfa097f103 +Subproject commit ccef7125b3783210f44ee6b100df7278e6ba3eff From bdbad695f6cfde85bda8e46a4747264e412c406b Mon Sep 17 00:00:00 2001 From: Allan Ritchie Date: Mon, 3 Mar 2025 17:24:17 -0500 Subject: [PATCH 145/363] Map iOS Native BeforeSend & OnCrashedLastEvent to SentryOptions.Native (#3958) * Initial commit for exposing iOS native before send & crashed last run * Initial feature set is working * Format code * Update CHANGELOG.md * Merge signal abort code before user based BeforeSend * Move native BeforeSend & OnCrashedLastRun events to SentryOptions - OnCrashedLastRun only available to IOS/MacCatalyst for now * Always run NativeOptions.BeforeSend so sig aborts can be filtered * Update SentrySdk.cs * Update CHANGELOG.md * Format code * WIP - removing serialization mechanics as they cause a native crash due to SentryEnvelopeItem being deserialized instead of SentryEvent * Transferring transformed sentryevent back to original native event instead of trying to remap the entire object * Remove unnecessary hacks * Format code * Add missing bindable option for native iOS sentryoptions * Update SentrySdk.cs * Update SentrySdk.cs * Update CHANGELOG.md * Update CHANGELOG.md * Unit tests mapping checks to/from native iOS * Format code * Update NativeSerializationTests.cs * Revert "Add missing bindable option for native iOS sentryoptions" This reverts commit bf4d33db66b4295c64c97c483c30924948f06765. * Remove iOS native event suppress configuration * Update CHANGELOG.md * Rename tests according to review * add timestamp for deserialize test * Account for fractions of seconds * Platforms should not change * Update CocoaExtensionsTests.cs * Update CocoaExtensionsTests.cs * Update CocoaExtensionsTests.cs * Update CocoaExtensionsTests.cs * Safety SentryLevel in both directions * Update EnumExtensions.cs * Format code --------- Co-authored-by: Sentry Github Bot --- CHANGELOG.md | 1 + samples/Sentry.Samples.Ios/AppDelegate.cs | 11 ++ .../Sentry.Samples.Ios.csproj | 2 +- src/Sentry.Bindings.Cocoa/ApiDefinitions.cs | 1 - .../Internal/Extensions/JsonExtensions.cs | 25 +++ .../Cocoa/Extensions/CocoaExtensions.cs | 140 +++++++++++++++++ .../Cocoa/Extensions/EnumExtensions.cs | 11 +- .../Cocoa/Extensions/SentryEventExtensions.cs | 45 ------ src/Sentry/Platforms/Cocoa/SentryOptions.cs | 1 - src/Sentry/Platforms/Cocoa/SentrySdk.cs | 144 ++++++++++-------- src/Sentry/SentryEvent.cs | 24 ++- src/Sentry/SentryOptions.cs | 8 + .../Platforms/iOS/CocoaExtensionsTests.cs | 118 ++++++++++++++ 13 files changed, 419 insertions(+), 112 deletions(-) delete mode 100644 src/Sentry/Platforms/Cocoa/Extensions/SentryEventExtensions.cs create mode 100644 test/Sentry.Tests/Platforms/iOS/CocoaExtensionsTests.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 10efd54d1e..27b9f8d2bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ ### Features +- Native iOS events are now exposed to the dotnet layer for users to hook through SentryOptions.BeforeSend and SentryOptions.OnCrashedLastRun ([#2102](https://github.com/getsentry/sentry-dotnet/pull/3958)) - Users can now register their own MAUI controls for breadcrumb creation ([#3997](https://github.com/getsentry/sentry-dotnet/pull/3997)) - Serilog scope properties are now sent with Sentry events ([#3976](https://github.com/getsentry/sentry-dotnet/pull/3976)) - The sample seed used for sampling decisions is now propagated, for use in downstream custom trace samplers ([#3951](https://github.com/getsentry/sentry-dotnet/pull/3951)) diff --git a/samples/Sentry.Samples.Ios/AppDelegate.cs b/samples/Sentry.Samples.Ios/AppDelegate.cs index 80e9ea09a5..2d50a320c3 100644 --- a/samples/Sentry.Samples.Ios/AppDelegate.cs +++ b/samples/Sentry.Samples.Ios/AppDelegate.cs @@ -28,6 +28,17 @@ public override bool FinishedLaunching(UIApplication application, NSDictionary l options.Native.EnableAppHangTracking = true; options.CacheDirectoryPath = Path.GetTempPath(); + + options.SetBeforeSend((evt, _) => + { + evt.SetTag("dotnet-iOS-Native-Before", "Hello World"); + return evt; + }); + + options.OnCrashedLastRun = e => + { + Console.WriteLine(e); + }; }); // create a new window instance based on the screen size diff --git a/samples/Sentry.Samples.Ios/Sentry.Samples.Ios.csproj b/samples/Sentry.Samples.Ios/Sentry.Samples.Ios.csproj index 76b122d042..b1820343e6 100644 --- a/samples/Sentry.Samples.Ios/Sentry.Samples.Ios.csproj +++ b/samples/Sentry.Samples.Ios/Sentry.Samples.Ios.csproj @@ -1,7 +1,7 @@ - net9.0-ios18.0 + net9.0-ios Exe enable true diff --git a/src/Sentry.Bindings.Cocoa/ApiDefinitions.cs b/src/Sentry.Bindings.Cocoa/ApiDefinitions.cs index 3bbfc298dc..77ad1d5797 100644 --- a/src/Sentry.Bindings.Cocoa/ApiDefinitions.cs +++ b/src/Sentry.Bindings.Cocoa/ApiDefinitions.cs @@ -27,7 +27,6 @@ namespace Sentry.CocoaSdk; // typedef SentryEvent * _Nullable (^SentryBeforeSendEventCallback)(SentryEvent * _Nonnull); [Internal] -[return: NullAllowed] delegate SentryEvent SentryBeforeSendEventCallback (SentryEvent @event); // typedef id _Nullable (^SentryBeforeSendSpanCallback)(id _Nonnull); diff --git a/src/Sentry/Internal/Extensions/JsonExtensions.cs b/src/Sentry/Internal/Extensions/JsonExtensions.cs index fa29909eac..8bc25eba83 100644 --- a/src/Sentry/Internal/Extensions/JsonExtensions.cs +++ b/src/Sentry/Internal/Extensions/JsonExtensions.cs @@ -213,6 +213,31 @@ public static void Deconstruct(this JsonProperty jsonProperty, out string name, return double.Parse(json.ToString()!, CultureInfo.InvariantCulture); } + /// + /// Safety value to deal with native serialization - allows datetimeoffset to come in as a long or string value + /// + /// + /// + /// + public static DateTimeOffset? GetSafeDateTimeOffset(this JsonElement json, string propertyName) + { + DateTimeOffset? result = null; + var dtRaw = json.GetPropertyOrNull(propertyName); + if (dtRaw != null) + { + if (dtRaw.Value.ValueKind == JsonValueKind.Number) + { + var epoch = Convert.ToInt64(dtRaw.Value.GetDouble()); + result = DateTimeOffset.FromUnixTimeSeconds(epoch); + } + else + { + result = dtRaw.Value.GetDateTimeOffset(); + } + } + return result; + } + public static long? GetHexAsLong(this JsonElement json) { // If the address is in json as a number, we can just use it. diff --git a/src/Sentry/Platforms/Cocoa/Extensions/CocoaExtensions.cs b/src/Sentry/Platforms/Cocoa/Extensions/CocoaExtensions.cs index e0fb5e7bc0..cf6776a450 100644 --- a/src/Sentry/Platforms/Cocoa/Extensions/CocoaExtensions.cs +++ b/src/Sentry/Platforms/Cocoa/Extensions/CocoaExtensions.cs @@ -169,6 +169,24 @@ public static NSDictionary ToNSDictionary( d.Keys.ToArray()); } + public static NSDictionary ToNSDictionaryStrings( + this IEnumerable> dict) + { + var d = new Dictionary(); + foreach (var item in dict) + { + if (item.Value != null) + { + d.Add((NSString)item.Key, new NSString(item.Value)); + } + } + + return NSDictionary + .FromObjectsAndKeys( + d.Values.ToArray(), + d.Keys.ToArray()); + } + public static NSDictionary? ToNullableNSDictionary( this ICollection> dict) => dict.Count == 0 ? null : dict.ToNSDictionary(); @@ -224,4 +242,126 @@ public static object ToObject(this NSNumber n) $"NSNumber \"{n.StringValue}\" has an unknown ObjCType \"{n.ObjCType}\" (Class: \"{n.Class.Name}\")") }; } + + public static void CopyToCocoaSentryEvent(this SentryEvent managed, CocoaSdk.SentryEvent native) + { + // we only support a subset of mutated data to be passed back to the native SDK at this time + native.ServerName = managed.ServerName; + native.Dist = managed.Distribution; + native.Logger = managed.Logger; + native.ReleaseName = managed.Release; + native.Environment = managed.Environment; + native.Transaction = managed.TransactionName!; + native.Message = managed.Message?.ToCocoaSentryMessage(); + native.Tags = managed.Tags?.ToNSDictionaryStrings(); + native.Extra = managed.Extra?.ToNSDictionary(); + native.Breadcrumbs = managed.Breadcrumbs?.Select(x => x.ToCocoaBreadcrumb()).ToArray(); + native.User = managed.User?.ToCocoaUser(); + + if (managed.Level != null) + { + native.Level = managed.Level.Value.ToCocoaSentryLevel(); + } + + if (managed.Exception != null) + { + native.Error = new NSError(new NSString(managed.Exception.ToString()), IntPtr.Zero); + } + } + + public static SentryEvent? ToSentryEvent(this CocoaSdk.SentryEvent sentryEvent) + { + using var stream = sentryEvent.ToJsonStream(); + if (stream == null) + return null; + + using var json = JsonDocument.Parse(stream); + var exception = sentryEvent.Error == null ? null : new NSErrorException(sentryEvent.Error); + var ev = SentryEvent.FromJson(json.RootElement, exception); + return ev; + } + + public static CocoaSdk.SentryMessage ToCocoaSentryMessage(this SentryMessage msg) + { + var native = new CocoaSdk.SentryMessage(msg.Formatted ?? string.Empty); + native.Params = msg.Params?.Select(x => x.ToString()!).ToArray() ?? new string[0]; + + return native; + } + + // not tested or needed yet - leaving for future just in case + // public static CocoaSdk.SentryThread ToCocoaSentryThread(this SentryThread thread) + // { + // var id = NSNumber.FromInt32(thread.Id ?? 0); + // var native = new CocoaSdk.SentryThread(id); + // native.Crashed = thread.Crashed; + // native.Current = thread.Current; + // native.Name = thread.Name; + // native.Stacktrace = thread.Stacktrace?.ToCocoaSentryStackTrace(); + // // native.IsMain = not in dotnet + // return native; + // } + // + // public static CocoaSdk.SentryRequest ToCocoaSentryRequest(this SentryRequest request) + // { + // var native = new CocoaSdk.SentryRequest(); + // native.Cookies = request.Cookies; + // native.Headers = request.Headers?.ToNSDictionaryStrings(); + // native.Method = request.Method; + // native.QueryString = request.QueryString; + // native.Url = request.Url; + // + // // native.BodySize does not exist in dotnet + // return native; + // } + // + + // public static CocoaSdk.SentryException ToCocoaSentryException(this SentryException ex) + // { + // var native = new CocoaSdk.SentryException(ex.Value ?? string.Empty, ex.Type ?? string.Empty); + // native.Module = ex.Module; + // native.Mechanism = ex.Mechanism?.ToCocoaSentryMechanism(); + // native.Stacktrace = ex.Stacktrace?.ToCocoaSentryStackTrace(); + // // not part of native - ex.ThreadId; + // return native; + // } + // + // public static CocoaSdk.SentryStacktrace ToCocoaSentryStackTrace(this SentryStackTrace stackTrace) + // { + // var frames = stackTrace.Frames?.Select(x => x.ToCocoaSentryFrame()).ToArray() ?? new CocoaSdk.SentryFrame[0]; + // var native = new CocoaSdk.SentryStacktrace(frames, new NSDictionary()); + // // native.Register & native.Snapshot missing in dotnet + // return native; + // } + // + // public static CocoaSdk.SentryFrame ToCocoaSentryFrame(this SentryStackFrame frame) + // { + // var native = new CocoaSdk.SentryFrame(); + // native.Module = frame.Module; + // native.Package = frame.Package; + // native.InstructionAddress = frame.InstructionAddress?.ToString(); + // native.Function = frame.Function; + // native.Platform = frame.Platform; + // native.ColumnNumber = frame.ColumnNumber; + // native.FileName = frame.FileName; + // native.InApp = frame.InApp; + // native.ImageAddress = frame.ImageAddress?.ToString(); + // native.LineNumber = frame.LineNumber; + // native.SymbolAddress = frame.SymbolAddress?.ToString(); + // + // // native.StackStart = doesn't exist in dotnet + // return native; + // } + // + // public static CocoaSdk.SentryMechanism ToCocoaSentryMechanism(this Mechanism mechanism) + // { + // var native = new CocoaSdk.SentryMechanism(mechanism.Type); + // native.Synthetic = mechanism.Synthetic; + // native.Handled = mechanism.Handled; + // native.Desc = mechanism.Description; + // native.HelpLink = mechanism.HelpLink; + // native.Data = mechanism.Data?.ToNSDictionary(); + // // TODO: Meta does not currently translate in dotnet - native.Meta = null; + // return native; + // } } diff --git a/src/Sentry/Platforms/Cocoa/Extensions/EnumExtensions.cs b/src/Sentry/Platforms/Cocoa/Extensions/EnumExtensions.cs index 3cb7df8c99..feb2a37e91 100644 --- a/src/Sentry/Platforms/Cocoa/Extensions/EnumExtensions.cs +++ b/src/Sentry/Platforms/Cocoa/Extensions/EnumExtensions.cs @@ -4,7 +4,16 @@ internal static class EnumExtensions { // These align, so we can just cast public static SentryLevel ToSentryLevel(this CocoaSdk.SentryLevel level) => (SentryLevel)level; - public static CocoaSdk.SentryLevel ToCocoaSentryLevel(this SentryLevel level) => (CocoaSdk.SentryLevel)level; + + public static CocoaSdk.SentryLevel ToCocoaSentryLevel(this SentryLevel level) => level switch + { + SentryLevel.Debug => CocoaSdk.SentryLevel.Debug, + SentryLevel.Info => CocoaSdk.SentryLevel.Info, + SentryLevel.Warning => CocoaSdk.SentryLevel.Warning, + SentryLevel.Error => CocoaSdk.SentryLevel.Error, + SentryLevel.Fatal => CocoaSdk.SentryLevel.Fatal, + _ => throw new ArgumentOutOfRangeException(nameof(level), level, null) + }; public static BreadcrumbLevel ToBreadcrumbLevel(this CocoaSdk.SentryLevel level) => level switch diff --git a/src/Sentry/Platforms/Cocoa/Extensions/SentryEventExtensions.cs b/src/Sentry/Platforms/Cocoa/Extensions/SentryEventExtensions.cs deleted file mode 100644 index ebb7c735ba..0000000000 --- a/src/Sentry/Platforms/Cocoa/Extensions/SentryEventExtensions.cs +++ /dev/null @@ -1,45 +0,0 @@ -// using Sentry.Extensibility; -// using Sentry.Protocol.Envelopes; -// -// namespace Sentry.Cocoa.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 CocoaSdk.SentryEvent sentryEvent, SentryCocoaSdkOptions nativeOptions) -// { -// 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 CocoaSdk.SentryEvent ToCocoaSentryEvent(this SentryEvent sentryEvent, SentryOptions options, SentryCocoaSdkOptions nativeOptions) -// { -// 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 = CocoaSdk.PrivateSentrySDKOnly.EnvelopeWithData(data); -// -// var cocoaEvent = (CocoaSdk.SentryEvent) cocoaEnvelope.Items[0]; -// return cocoaEvent; -// } -// } diff --git a/src/Sentry/Platforms/Cocoa/SentryOptions.cs b/src/Sentry/Platforms/Cocoa/SentryOptions.cs index 6b0e9587e6..91172d9938 100644 --- a/src/Sentry/Platforms/Cocoa/SentryOptions.cs +++ b/src/Sentry/Platforms/Cocoa/SentryOptions.cs @@ -197,7 +197,6 @@ internal NativeOptions(SentryOptions options) /// public NSUrlSessionDelegate? UrlSessionDelegate { get; set; } = null; - // ---------- Other ---------- /// diff --git a/src/Sentry/Platforms/Cocoa/SentrySdk.cs b/src/Sentry/Platforms/Cocoa/SentrySdk.cs index dcc8e48006..8908cd78c0 100644 --- a/src/Sentry/Platforms/Cocoa/SentrySdk.cs +++ b/src/Sentry/Platforms/Cocoa/SentrySdk.cs @@ -86,29 +86,90 @@ private static void InitSentryCocoaSdk(SentryOptions options) } } - // TODO: Finish SentryEventExtensions to enable these - - // if (options.Native.EnableBeforeSend && options.BeforeSend is { } beforeSend) - // { - // nativeOptions.BeforeSend = evt => - // { - // var sentryEvent = evt.ToSentryEvent(nativeOptions); - // var result = beforeSend(sentryEvent)?.ToCocoaSentryEvent(options, nativeOptions); - // - // // 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.Native.OnCrashedLastRun is { } onCrashedLastRun) - // { - // nativeOptions.OnCrashedLastRun = evt => - // { - // var sentryEvent = evt.ToSentryEvent(nativeOptions); - // onCrashedLastRun(sentryEvent); - // }; - // } + nativeOptions.BeforeSend = evt => + { + // 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. + + // 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]; + + // Thankfully, sometimes 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 + 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 send it + options.LogDebug("Discarded {0} error ({1}). Captured as managed exception instead.", ex.Type, + ex.Value); + return null!; + } + + // Similar workaround for NullReferenceExceptions. We don't have any easy way to know whether the + // exception is managed code (compiled to native) or original native code though. + // See: https://github.com/getsentry/sentry-dotnet/issues/3776 + if (ex.Type == "EXC_BAD_ACCESS") + { + // Don't send it + options.LogDebug("Discarded {0} error ({1}). Captured as managed exception instead.", ex.Type, + ex.Value); + return null!; + } + } + + // we run our SIGABRT checks first before handing over to user events + // because we delegate to user code, we need to protect anything that could happen in this event + if (options.BeforeSendInternal == null) + return evt; + + try + { + var sentryEvent = evt.ToSentryEvent(); + if (sentryEvent == null) + return evt; + + var result = options.BeforeSendInternal(sentryEvent, null!); + if (result == null) + return null!; + + // we only support a subset of mutated data to be passed back to the native SDK at this time + result.CopyToCocoaSentryEvent(evt); + + // Note: Nullable result is allowed but delegate is generated incorrectly + // See https://github.com/xamarin/xamarin-macios/issues/15299#issuecomment-1201863294 + return evt!; + } + catch (Exception ex) + { + options.LogError(ex, "Before Send Error"); + return evt; + } + }; + + if (options.OnCrashedLastRun is { } onCrashedLastRun) + { + nativeOptions.OnCrashedLastRun = evt => + { + // because we delegate to user code, we need to protect anything that could happen in this event + try + { + var sentryEvent = evt.ToSentryEvent(); + if (sentryEvent != null) + { + onCrashedLastRun(sentryEvent); + } + } + catch (Exception ex) + { + options.LogError(ex, "Crashed Last Run Error"); + } + }; + } // These options are from Cocoa's SentryOptions nativeOptions.AttachScreenshot = options.Native.AttachScreenshot; @@ -145,43 +206,6 @@ private static void InitSentryCocoaSdk(SentryOptions options) // nativeOptions.DefaultIntegrations // nativeOptions.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. - nativeOptions.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]; - - // Thankfully, sometimes 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 - 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 send it - options.LogDebug("Discarded {0} error ({1}). Captured as managed exception instead.", ex.Type, ex.Value); - return null!; - } - - // Similar workaround for NullReferenceExceptions. We don't have any easy way to know whether the - // exception is managed code (compiled to native) or original native code though. - // See: https://github.com/getsentry/sentry-dotnet/issues/3776 - if (ex.Type == "EXC_BAD_ACCESS") - { - // Don't send it - options.LogDebug("Discarded {0} error ({1}). Captured as managed exception instead.", ex.Type, ex.Value); - return null!; - } - } - - // Other event, send as normal - return evt; - }; - // Set hybrid SDK name SentryCocoaHybridSdk.SetSdkName("sentry.cocoa.dotnet"); diff --git a/src/Sentry/SentryEvent.cs b/src/Sentry/SentryEvent.cs index b580a89881..81abbe4ddd 100644 --- a/src/Sentry/SentryEvent.cs +++ b/src/Sentry/SentryEvent.cs @@ -286,11 +286,29 @@ public void WriteTo(Utf8JsonWriter writer, IDiagnosticLogger? logger) /// public static SentryEvent FromJson(JsonElement json) => FromJson(json, null); + private static SentryLevel? SafeLevelFromJson(JsonElement json) + { + var levelString = json.GetPropertyOrNull("level")?.GetString(); + if (levelString == null) + return null; + + // Native SentryLevel.None does not exist in dotnet + return levelString.ToLowerInvariant() switch + { + "debug" => SentryLevel.Debug, + "info" => SentryLevel.Info, + "warning" => SentryLevel.Warning, + "fatal" => SentryLevel.Fatal, + "error" => SentryLevel.Error, + _ => null + }; + } + internal static SentryEvent FromJson(JsonElement json, Exception? exception) { var modules = json.GetPropertyOrNull("modules")?.GetStringDictionaryOrNull(); var eventId = json.GetPropertyOrNull("event_id")?.Pipe(SentryId.FromJson) ?? SentryId.Empty; - var timestamp = json.GetPropertyOrNull("timestamp")?.GetDateTimeOffset(); + var timestamp = json.GetSafeDateTimeOffset("timestamp"); // Native sentryevents are serialized to epoch timestamps var message = json.GetPropertyOrNull("logentry")?.Pipe(SentryMessage.FromJson); var logger = json.GetPropertyOrNull("logger")?.GetString(); var platform = json.GetPropertyOrNull("platform")?.GetString(); @@ -299,7 +317,7 @@ internal static SentryEvent FromJson(JsonElement json, Exception? exception) var distribution = json.GetPropertyOrNull("dist")?.GetString(); var exceptionValues = json.GetPropertyOrNull("exception")?.GetPropertyOrNull("values")?.EnumerateArray().Select(SentryException.FromJson).ToList().Pipe(v => new SentryValues(v)); var threadValues = json.GetPropertyOrNull("threads")?.GetPropertyOrNull("values")?.EnumerateArray().Select(SentryThread.FromJson).ToList().Pipe(v => new SentryValues(v)); - var level = json.GetPropertyOrNull("level")?.GetString()?.ParseEnum(); + var transaction = json.GetPropertyOrNull("transaction")?.GetString(); var request = json.GetPropertyOrNull("request")?.Pipe(SentryRequest.FromJson); var contexts = json.GetPropertyOrNull("contexts")?.Pipe(SentryContexts.FromJson); @@ -310,7 +328,7 @@ internal static SentryEvent FromJson(JsonElement json, Exception? exception) var breadcrumbs = json.GetPropertyOrNull("breadcrumbs")?.EnumerateArray().Select(Breadcrumb.FromJson).ToList(); var extra = json.GetPropertyOrNull("extra")?.GetDictionaryOrNull(); var tags = json.GetPropertyOrNull("tags")?.GetStringDictionaryOrNull(); - + var level = SafeLevelFromJson(json); var debugMeta = json.GetPropertyOrNull("debug_meta")?.Pipe(DebugMeta.FromJson); return new SentryEvent(exception, timestamp, eventId) diff --git a/src/Sentry/SentryOptions.cs b/src/Sentry/SentryOptions.cs index 7c99988acf..69a87685a7 100644 --- a/src/Sentry/SentryOptions.cs +++ b/src/Sentry/SentryOptions.cs @@ -1075,6 +1075,14 @@ public StackTraceMode StackTraceMode /// public Func? CrashedLastRun { get; set; } +#if IOS || MACCATALYST + // this event currently isn't being pushed from Android + /// + /// Delegate which is run with event information if the application crashed during last run. + /// + public Action? OnCrashedLastRun { get; set; } +#endif + /// /// /// Gets the used to create spans. diff --git a/test/Sentry.Tests/Platforms/iOS/CocoaExtensionsTests.cs b/test/Sentry.Tests/Platforms/iOS/CocoaExtensionsTests.cs new file mode 100644 index 0000000000..04e782e639 --- /dev/null +++ b/test/Sentry.Tests/Platforms/iOS/CocoaExtensionsTests.cs @@ -0,0 +1,118 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using FluentAssertions; +using Foundation; +using Sentry.Cocoa.Extensions; +using Xunit; + +namespace Sentry.Tests.Platforms.iOS; + +public class CocoaExtensionsTests +{ + [Fact] + public void CopyToCocoaSentryEvent_CopiesProperties() + { + var evt = new SentryEvent(new Exception("Test Exception")); + + evt.Level = SentryLevel.Debug; + evt.ServerName = "test server name"; + evt.Distribution = "test distribution"; + evt.Logger = "test logger"; + evt.Release = "test release"; + evt.Environment = "test environment"; + evt.TransactionName = "test transaction name"; + evt.Message = new SentryMessage { Params = ["Test"] }; + evt.SetTag("TestTagKey", "TestTagValue"); + evt.AddBreadcrumb(new Breadcrumb("test breadcrumb", "test type")); + evt.SetExtra("TestExtraKey", "TestExtraValue"); + evt.User = new SentryUser + { + Id = "user id", + Username = "test", + Email = "test@sentry.io", + IpAddress = "127.0.0.1" + }; + + var native = new CocoaSdk.SentryEvent(); + evt.CopyToCocoaSentryEvent(native); + + AssertEqual(evt, native); + + // message - native does not copy this over to dotnet + native.Message.Should().NotBeNull("Message should not be null"); + native.Message.Params.Should().NotBeNull("Message params should not be null"); + native.Message.Params.First().Should().Be(evt.Message!.Params!.First().ToString()); + } + + + [Fact] + public void ToSentryEvent_ConvertToManaged() + { + var native = new CocoaSdk.SentryEvent(); + + native.Timestamp = DateTimeOffset.UtcNow.ToNSDate(); + native.Level = Sentry.CocoaSdk.SentryLevel.Debug; + native.ServerName = "native server name"; + native.Dist = "native dist"; + native.Logger = "native logger"; + native.ReleaseName = "native release"; + native.Environment = "native env"; + native.Transaction = "native transaction"; + native.Message = new SentryMessage { Params = ["Test"] }.ToCocoaSentryMessage(); + native.Tags = new Dictionary { { "TestTagKey", "TestTagValue" } }.ToNSDictionaryStrings(); + native.Extra = new Dictionary { { "TestExtraKey", "TestExtraValue" } }.ToNSDictionary(); + native.Error = new NSError(new NSString("Test Error"), IntPtr.Zero); + native.Breadcrumbs = + [ + new CocoaSdk.SentryBreadcrumb(CocoaSdk.SentryLevel.Debug, "category") + ]; + native.User = new CocoaSdk.SentryUser + { + UserId = "user id", + Username = "test", + Email = "test@sentry.io", + IpAddress = "127.0.0.1" + }; + var managed = native.ToSentryEvent(); + AssertEqual(managed, native); + } + + private static void AssertEqual(SentryEvent managed, CocoaSdk.SentryEvent native) + { + native.ServerName.Should().Be(managed.ServerName, "Server Name"); + native.Dist.Should().Be(managed.Distribution, "Distribution"); + native.Logger.Should().Be(managed.Logger, "Logger"); + native.ReleaseName.Should().Be(managed.Release, "Release"); + native.Environment.Should().Be(managed.Environment, "Environment"); + native.Transaction.Should().Be(managed.TransactionName!, "Transaction"); + native.Level!.ToString().Should().Be(managed.Level.ToString(), "Level"); + + native.Extra.Should().NotBeNull("No extras found"); + native.Extra.Count.Should().Be(1, "Extras should have 1 item"); + native.Extra!.Keys![0]!.Should().Be(new NSString(managed.Extra.Keys.First()), "Extras key should match"); + native.Extra!.Values![0]!.Should().Be(NSObject.FromObject(managed.Extra.Values.First()), "Extra value should match"); + + // tags + native.Tags.Should().NotBeNull("No tags found"); + native.Tags.Count.Should().Be(1, "Tags should have 1 item"); + native.Tags!.Keys![0]!.Should().Be(new NSString(managed.Tags.Keys.First())); + native.Tags!.Values![0]!.Should().Be(new NSString(managed.Tags.Values.First())); + + // breadcrumbs + native.Breadcrumbs.Should().NotBeNull("No breadcrumbs found"); + var nb = native.Breadcrumbs!.First(); + var mb = managed.Breadcrumbs!.First(); + nb.Message.Should().Be(mb.Message, "Breadcrumb message"); + nb.Type.Should().Be(mb.Type, "Breadcrumb type"); + + // user + native.User!.UserId.Should().Be(managed.User.Id, "UserId should match"); + native.User.Email.Should().Be(managed.User.Email, "Email should match"); + native.User.Username.Should().Be(managed.User.Username, "Username should match"); + native.User.IpAddress.Should().Be(managed.User.IpAddress, "IpAddress should match"); + + // check contains because ios/android dotnet tend to move how this works from time to time + managed.Exception!.ToString().Contains(native.Error!.Domain!).Should().BeTrue("Domain message should be included in dotnet exception"); + } +} From cdb36a2430415829781205b4a2bd250dc0523e15 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Mar 2025 11:38:32 +1300 Subject: [PATCH 146/363] build(deps): bump codecov/codecov-action from 5.3.1 to 5.4.0 (#4019) Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 5.3.1 to 5.4.0. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/13ce06bfc6bbe3ecf90edbbf1bc32fe5978ca1d3...0565863a31f2c772f9f0395002a31e3f06189574) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 04bbd7a4a5..72b2e92f51 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -123,7 +123,7 @@ jobs: run: dotnet test Sentry-CI-Build-${{ runner.os }}.slnf -c Release --no-build --nologo -l GitHubActions -l "trx;LogFilePrefix=testresults_${{ runner.os }}" --collect "XPlat Code Coverage" - name: Upload code coverage - uses: codecov/codecov-action@13ce06bfc6bbe3ecf90edbbf1bc32fe5978ca1d3 + uses: codecov/codecov-action@0565863a31f2c772f9f0395002a31e3f06189574 - name: Upload build and test outputs if: failure() From 1f56e7ef8dfb37c9b9ffbd04d155992933f780b6 Mon Sep 17 00:00:00 2001 From: Allan Ritchie Date: Tue, 4 Mar 2025 16:12:33 -0500 Subject: [PATCH 147/363] #4018 - Safety calls to/from Android Native Serialization (#4022) --- CHANGELOG.md | 4 + .../Sentry.Samples.Android/MainActivity.cs | 11 ++ .../Sentry.Samples.Android.csproj | 2 +- samples/Sentry.Samples.Ios/AppDelegate.cs | 7 +- .../Internal/Extensions/JsonExtensions.cs | 9 +- .../Android/Callbacks/BeforeSendCallback.cs | 20 ++- .../Extensions/SentryEventExtensions.cs | 13 ++ src/Sentry/Platforms/Android/SentrySdk.cs | 5 +- .../Platforms/Android/JsonExtensionsTests.cs | 126 ++++++++++++++++++ 9 files changed, 188 insertions(+), 9 deletions(-) create mode 100644 test/Sentry.Tests/Platforms/Android/JsonExtensionsTests.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 27b9f8d2bd..f74bf20a51 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Fixes + +- Prevent crashes from occurring on Android during OnBeforeSend ([#4022](https://github.com/getsentry/sentry-dotnet/pull/4022)) + ### Dependencies - Bump Native SDK from v0.8.0 to v0.8.1 ([#4014](https://github.com/getsentry/sentry-dotnet/pull/4014)) diff --git a/samples/Sentry.Samples.Android/MainActivity.cs b/samples/Sentry.Samples.Android/MainActivity.cs index a362b886aa..7ec70a4f9b 100644 --- a/samples/Sentry.Samples.Android/MainActivity.cs +++ b/samples/Sentry.Samples.Android/MainActivity.cs @@ -20,6 +20,17 @@ protected override void OnCreate(Bundle? savedInstanceState) // https://docs.sentry.io/platforms/android/configuration/ // Enable Native Android SDK ANR detection options.Native.AnrEnabled = true; + + options.SetBeforeSend(evt => + { + if (evt.Exception?.Message.Contains("Something you don't care want logged?") ?? false) + { + return null; // return null to filter out event + } + // or add additional data + evt.SetTag("dotnet-Android-Native-Before", "Hello World"); + return evt; + }); }); // Here's an example of adding custom scope information. diff --git a/samples/Sentry.Samples.Android/Sentry.Samples.Android.csproj b/samples/Sentry.Samples.Android/Sentry.Samples.Android.csproj index ede53769c4..ace5748bae 100644 --- a/samples/Sentry.Samples.Android/Sentry.Samples.Android.csproj +++ b/samples/Sentry.Samples.Android/Sentry.Samples.Android.csproj @@ -1,6 +1,6 @@ - net8.0-android34.0 + net8.0-android 21 Exe enable diff --git a/samples/Sentry.Samples.Ios/AppDelegate.cs b/samples/Sentry.Samples.Ios/AppDelegate.cs index 2d50a320c3..70340fdd98 100644 --- a/samples/Sentry.Samples.Ios/AppDelegate.cs +++ b/samples/Sentry.Samples.Ios/AppDelegate.cs @@ -29,8 +29,13 @@ public override bool FinishedLaunching(UIApplication application, NSDictionary l options.CacheDirectoryPath = Path.GetTempPath(); - options.SetBeforeSend((evt, _) => + options.SetBeforeSend(evt => { + if (evt.Exception?.Message.Contains("Something you don't care want logged?") ?? false) + { + return null; // return null to filter out event + } + // or add additional data evt.SetTag("dotnet-iOS-Native-Before", "Hello World"); return evt; }); diff --git a/src/Sentry/Internal/Extensions/JsonExtensions.cs b/src/Sentry/Internal/Extensions/JsonExtensions.cs index 8bc25eba83..96b28bf81b 100644 --- a/src/Sentry/Internal/Extensions/JsonExtensions.cs +++ b/src/Sentry/Internal/Extensions/JsonExtensions.cs @@ -156,7 +156,14 @@ public static void Deconstruct(this JsonProperty jsonProperty, out string name, foreach (var (name, value) in json.EnumerateObject()) { - result[name] = value.GetString(); + if (value.ValueKind == JsonValueKind.String) + { + result[name] = value.GetString(); + } + else + { + result[name] = value.ToString(); + } } return result; diff --git a/src/Sentry/Platforms/Android/Callbacks/BeforeSendCallback.cs b/src/Sentry/Platforms/Android/Callbacks/BeforeSendCallback.cs index 1fd15c6e1d..b529ec6bee 100644 --- a/src/Sentry/Platforms/Android/Callbacks/BeforeSendCallback.cs +++ b/src/Sentry/Platforms/Android/Callbacks/BeforeSendCallback.cs @@ -1,4 +1,5 @@ using Sentry.Android.Extensions; +using Sentry.Extensibility; namespace Sentry.Android.Callbacks; @@ -22,10 +23,19 @@ public BeforeSendCallback( { // Note: Hint is unused due to: // https://github.com/getsentry/sentry-dotnet/issues/1469 - - var evnt = e.ToSentryEvent(_javaOptions); - var hint = h.ToHint(); - var result = _beforeSend?.Invoke(evnt, hint); - return result?.ToJavaSentryEvent(_options, _javaOptions); + try + { + // because this can go out to user code, we want to prevent external crashing + // also, native types tend to move before dotnet does, so serialization can fail + var evnt = e.ToSentryEvent(_javaOptions); + var hint = h.ToHint(); + var result = _beforeSend?.Invoke(evnt, hint); + return result?.ToJavaSentryEvent(_options, _javaOptions); + } + catch (Exception exception) + { + _options.LogError(exception, "Before Send Error"); + return e; + } } } diff --git a/src/Sentry/Platforms/Android/Extensions/SentryEventExtensions.cs b/src/Sentry/Platforms/Android/Extensions/SentryEventExtensions.cs index 99ca913312..eb28e3de96 100644 --- a/src/Sentry/Platforms/Android/Extensions/SentryEventExtensions.cs +++ b/src/Sentry/Platforms/Android/Extensions/SentryEventExtensions.cs @@ -1,3 +1,5 @@ +using Sentry.Internal; + namespace Sentry.Android.Extensions; internal static class SentryEventExtensions @@ -17,6 +19,12 @@ internal static class SentryEventExtensions public static SentryEvent ToSentryEvent(this JavaSdk.SentryEvent sentryEvent, JavaSdk.SentryOptions javaOptions) { + if (sentryEvent.Sdk != null) + { + // when we cast this serialize this over, this value must be set + sentryEvent.Sdk.Name ??= Constants.SdkName; + sentryEvent.Sdk.Version ??= SdkVersion.Instance.Version ?? "0.0.0"; + } using var stream = new MemoryStream(); using var streamWriter = new JavaOutputStreamWriter(stream); using var jsonWriter = new JavaSdk.JsonObjectWriter(streamWriter, javaOptions.MaxDepth); @@ -31,6 +39,11 @@ public static SentryEvent ToSentryEvent(this JavaSdk.SentryEvent sentryEvent, Ja public static JavaSdk.SentryEvent ToJavaSentryEvent(this SentryEvent sentryEvent, SentryOptions options, JavaSdk.SentryOptions javaOptions) { + if (sentryEvent.Sdk != null) + { + sentryEvent.Sdk.Name ??= Constants.SdkName; + sentryEvent.Sdk.Version ??= SdkVersion.Instance.Version ?? "0.0.0"; + } using var stream = new MemoryStream(); using var jsonWriter = new Utf8JsonWriter(stream); sentryEvent.WriteTo(jsonWriter, options.DiagnosticLogger); diff --git a/src/Sentry/Platforms/Android/SentrySdk.cs b/src/Sentry/Platforms/Android/SentrySdk.cs index 3cba5dac2c..f0db0a1fef 100644 --- a/src/Sentry/Platforms/Android/SentrySdk.cs +++ b/src/Sentry/Platforms/Android/SentrySdk.cs @@ -100,7 +100,10 @@ private static void InitSentryAndroidSdk(SentryOptions options) } } - o.BeforeSend = new BeforeSendCallback(BeforeSendWrapper(options), options, o); + if (options.Android.SuppressSegfaults || (options.Native.EnableBeforeSend && options.BeforeSendInternal != null)) + { + o.BeforeSend = new BeforeSendCallback(BeforeSendWrapper(options), options, o); + } // These options are from SentryAndroidOptions o.AttachScreenshot = options.Native.AttachScreenshot; diff --git a/test/Sentry.Tests/Platforms/Android/JsonExtensionsTests.cs b/test/Sentry.Tests/Platforms/Android/JsonExtensionsTests.cs new file mode 100644 index 0000000000..8e2bd35b26 --- /dev/null +++ b/test/Sentry.Tests/Platforms/Android/JsonExtensionsTests.cs @@ -0,0 +1,126 @@ +using System; +using System.Linq; +using FluentAssertions; +using Sentry.Android.Extensions; +using Xunit; + +namespace Sentry.Tests.Platforms.Android; + +public class JsonExtensionsTests + +{ + [Fact] + public void ToJavaSentryEvent_Success() + + { + var evt = new SentryEvent(new Exception("Test Exception")); + + evt.Level = SentryLevel.Debug; + evt.ServerName = "test server name"; + evt.Distribution = "test distribution"; + evt.Logger = "test logger"; + evt.Release = "test release"; + evt.Environment = "test environment"; + evt.TransactionName = "test transaction name"; + evt.Message = new SentryMessage + { + Params = ["Test"] + }; + + evt.SetTag("TestTagKey", "TestTagValue"); + evt.AddBreadcrumb(new Breadcrumb("test breadcrumb", "test type")); + evt.SetExtra("TestExtraKey", "TestExtraValue"); + evt.User = new SentryUser + { + Id = "user id", + Username = "test", + Email = "test@sentry.io", + IpAddress = "127.0.0.1" + }; + + var native = evt.ToJavaSentryEvent(new SentryOptions(), new JavaSdk.SentryOptions()); + + AssertEqual(evt, native); + } + + + [Fact] + public void ToSentryEvent_ConvertToManaged() + { + var native = new JavaSdk.SentryEvent(); + + native.Throwable = new Exception("Test Exception").ToThrowable(); + native.Timestamp = DateTimeOffset.UtcNow.ToJavaDate(); + native.Level = JavaSdk.SentryLevel.Debug; + native.ServerName = "native server name"; + native.Dist = "native dist"; + native.Logger = "native logger"; + native.Release = "native release"; + native.Environment = "native env"; + native.Transaction = "native transaction"; + native.Message = new JavaSdk.Protocol.Message + { + Params = ["Test"] + }; + native.SetTag("TestTagKey", "TestTagValue"); + native.SetExtra("TestExtraKey", "TestExtraValue"); + native.Breadcrumbs = + [ + new JavaSdk.Breadcrumb + { + Category = "category", + Level = JavaSdk.SentryLevel.Debug + } + ]; + + native.User = new JavaSdk.Protocol.User + { + Id = "user id", + Username = "test", + Email = "test@sentry.io", + IpAddress = "127.0.0.1" + }; + + var managed = native.ToSentryEvent(new JavaSdk.SentryOptions()); + + AssertEqual(managed, native); + } + + + private static void AssertEqual(SentryEvent managed, JavaSdk.SentryEvent native) + { + native.ServerName.Should().Be(managed.ServerName, "Server Name"); + native.Dist.Should().Be(managed.Distribution, "Distribution"); + native.Logger.Should().Be(managed.Logger, "Logger"); + native.Release.Should().Be(managed.Release, "Release"); + native.Environment.Should().Be(managed.Environment, "Environment"); + native.Transaction.Should().Be(managed.TransactionName!, "Transaction"); + native.Level!.ToString().ToUpper().Should().Be(managed.Level!.ToString()!.ToUpper(), "Level"); + // native.Throwable.Message.Should().Be(managed.Exception!.Message, "Message should match"); + + // extras + native.Extras.Should().NotBeNull("No extras found"); + native.Extras!.Count.Should().Be(1, "Extras should have 1 item"); + native.Extras!.Keys!.First().Should().Be(managed.Extra.Keys.First(), "Extras key should match"); + native.Extras!.Values!.First().ToString().Should().Be(managed.Extra.Values.First().ToString(), "Extra value should match"); + + // tags + native.Tags.Should().NotBeNull("No tags found"); + native.Tags!.Count.Should().Be(1, "Tags should have 1 item"); + native.Tags!.Keys!.First().Should().Be(managed.Tags.Keys.First()); + native.Tags!.Values!.First().Should().Be(managed.Tags.Values.First()); + + // breadcrumbs + native.Breadcrumbs.Should().NotBeNull("No breadcrumbs found"); + var nb = native.Breadcrumbs!.First(); + var mb = managed.Breadcrumbs!.First(); + nb.Message.Should().Be(mb.Message, "Breadcrumb message"); + nb.Type.Should().Be(mb.Type, "Breadcrumb type"); + + // user + native.User!.Id.Should().Be(managed.User.Id, "UserId should match"); + native.User.Email.Should().Be(managed.User.Email, "Email should match"); + native.User.Username.Should().Be(managed.User.Username, "Username should match"); + native.User.IpAddress.Should().Be(managed.User.IpAddress, "IpAddress should match"); + } +} From b330e6759e8df91c9451fdffc198435256e0fa10 Mon Sep 17 00:00:00 2001 From: Allan Ritchie Date: Tue, 4 Mar 2025 18:57:07 -0500 Subject: [PATCH 148/363] Add option to enable/disable EXC_BAD_ACCESS suppression (#3998) --- CHANGELOG.md | 3 +- .../Platforms/Cocoa/BindableSentryOptions.cs | 4 + src/Sentry/Platforms/Cocoa/SentryOptions.cs | 32 +++++ src/Sentry/Platforms/Cocoa/SentrySdk.cs | 130 +++++++++--------- test/Sentry.Tests/SentrySdkTests.cs | 86 ++++++++++++ 5 files changed, 190 insertions(+), 65 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f74bf20a51..cf29d0999a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ ### Fixes +- Using SentryOptions.Native.SuppressExcBadAccess and SentryOptions.Native.SuppressSignalAborts, users can now block duplicate errors from native due to dotnet NullReferenceExceptions - Defaults to false ([#3998](https://github.com/getsentry/sentry-dotnet/pull/3998)) +- Native iOS events are now exposed to the dotnet layer for users to hook through SentryOptions.BeforeSend and SentryOptions.OnCrashedLastRun ([#2102](https://github.com/getsentry/sentry-dotnet/pull/3958)) - Prevent crashes from occurring on Android during OnBeforeSend ([#4022](https://github.com/getsentry/sentry-dotnet/pull/4022)) ### Dependencies @@ -16,7 +18,6 @@ ### Features -- Native iOS events are now exposed to the dotnet layer for users to hook through SentryOptions.BeforeSend and SentryOptions.OnCrashedLastRun ([#2102](https://github.com/getsentry/sentry-dotnet/pull/3958)) - Users can now register their own MAUI controls for breadcrumb creation ([#3997](https://github.com/getsentry/sentry-dotnet/pull/3997)) - Serilog scope properties are now sent with Sentry events ([#3976](https://github.com/getsentry/sentry-dotnet/pull/3976)) - The sample seed used for sampling decisions is now propagated, for use in downstream custom trace samplers ([#3951](https://github.com/getsentry/sentry-dotnet/pull/3951)) diff --git a/src/Sentry/Platforms/Cocoa/BindableSentryOptions.cs b/src/Sentry/Platforms/Cocoa/BindableSentryOptions.cs index f8dae26213..edc529dc86 100644 --- a/src/Sentry/Platforms/Cocoa/BindableSentryOptions.cs +++ b/src/Sentry/Platforms/Cocoa/BindableSentryOptions.cs @@ -26,6 +26,8 @@ public class NativeOptions public bool? EnableUIViewControllerTracing { get; set; } public bool? EnableUserInteractionTracing { get; set; } public bool? EnableTracing { get; set; } + public bool? SuppressSignalAborts { get; set; } + public bool? SuppressExcBadAccess { get; set; } public void ApplyTo(SentryOptions.NativeOptions options) { @@ -47,6 +49,8 @@ public void ApplyTo(SentryOptions.NativeOptions options) options.EnableUIViewControllerTracing = EnableUIViewControllerTracing ?? options.EnableUIViewControllerTracing; options.EnableUserInteractionTracing = EnableUserInteractionTracing ?? options.EnableUserInteractionTracing; options.EnableTracing = EnableTracing ?? options.EnableTracing; + options.SuppressSignalAborts = SuppressSignalAborts ?? options.SuppressSignalAborts; + options.SuppressExcBadAccess = SuppressExcBadAccess ?? options.SuppressExcBadAccess; } } } diff --git a/src/Sentry/Platforms/Cocoa/SentryOptions.cs b/src/Sentry/Platforms/Cocoa/SentryOptions.cs index 91172d9938..21ead2c413 100644 --- a/src/Sentry/Platforms/Cocoa/SentryOptions.cs +++ b/src/Sentry/Platforms/Cocoa/SentryOptions.cs @@ -197,6 +197,38 @@ internal NativeOptions(SentryOptions options) /// public NSUrlSessionDelegate? UrlSessionDelegate { get; set; } = null; + /// + /// + /// Whether to suppress capturing SIGABRT errors in the Native SDK. + /// + /// + /// When managed code results in a NullReferenceException, this also causes a SIGABRT. Duplicate + /// events (one managed and one native) can be prevented by suppressing native SIGABRT, which may be + /// convenient. + /// + /// + /// Enabling this may prevent the capture of SIGABRT originating from native (not managed) code... so it may + /// prevent the capture of genuine native SIGABRT errors. + /// + /// + public bool SuppressSignalAborts { get; set; } = false; + + /// + /// + /// Whether to suppress capturing EXC_BAD_ACCESS errors in the Native SDK. + /// + /// + /// When managed code results in a NullReferenceException, this also causes a EXC_BAD_ACCESS. Duplicate + /// events (one managed and one native) can be prevented by suppressing native EXC_BAD_ACCESS, which may be + /// convenient. + /// + /// + /// Enabling this may prevent the capture of EXC_BAD_ACCESS originating from native (not managed) code... so it may + /// prevent the capture of genuine native EXC_BAD_ACCESS errors. + /// + /// + public bool SuppressExcBadAccess { get; set; } = false; + // ---------- Other ---------- /// diff --git a/src/Sentry/Platforms/Cocoa/SentrySdk.cs b/src/Sentry/Platforms/Cocoa/SentrySdk.cs index 8908cd78c0..5e60eb100d 100644 --- a/src/Sentry/Platforms/Cocoa/SentrySdk.cs +++ b/src/Sentry/Platforms/Cocoa/SentrySdk.cs @@ -86,70 +86,7 @@ private static void InitSentryCocoaSdk(SentryOptions options) } } - nativeOptions.BeforeSend = evt => - { - // 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. - - // 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]; - - // Thankfully, sometimes 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 - 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 send it - options.LogDebug("Discarded {0} error ({1}). Captured as managed exception instead.", ex.Type, - ex.Value); - return null!; - } - - // Similar workaround for NullReferenceExceptions. We don't have any easy way to know whether the - // exception is managed code (compiled to native) or original native code though. - // See: https://github.com/getsentry/sentry-dotnet/issues/3776 - if (ex.Type == "EXC_BAD_ACCESS") - { - // Don't send it - options.LogDebug("Discarded {0} error ({1}). Captured as managed exception instead.", ex.Type, - ex.Value); - return null!; - } - } - - // we run our SIGABRT checks first before handing over to user events - // because we delegate to user code, we need to protect anything that could happen in this event - if (options.BeforeSendInternal == null) - return evt; - - try - { - var sentryEvent = evt.ToSentryEvent(); - if (sentryEvent == null) - return evt; - - var result = options.BeforeSendInternal(sentryEvent, null!); - if (result == null) - return null!; - - // we only support a subset of mutated data to be passed back to the native SDK at this time - result.CopyToCocoaSentryEvent(evt); - - // Note: Nullable result is allowed but delegate is generated incorrectly - // See https://github.com/xamarin/xamarin-macios/issues/15299#issuecomment-1201863294 - return evt!; - } - catch (Exception ex) - { - options.LogError(ex, "Before Send Error"); - return evt; - } - }; + nativeOptions.BeforeSend = evt => ProcessOnBeforeSend(options, evt)!; if (options.OnCrashedLastRun is { } onCrashedLastRun) { @@ -250,4 +187,69 @@ private static CocoaSdk.SentryHttpStatusCodeRange[] GetFailedRequestStatusCodes( return nativeRanges; } + + internal static CocoaSdk.SentryEvent? ProcessOnBeforeSend(SentryOptions options, CocoaSdk.SentryEvent evt) + { + // 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. + + // There should only be one exception on the event in this case + if ((options.Native.SuppressSignalAborts || options.Native.SuppressExcBadAccess) && evt.Exceptions?.Length == 1) + { + // It will match the following characteristics + var ex = evt.Exceptions[0]; + + // Thankfully, sometimes 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 + if (options.Native.SuppressSignalAborts && ex.Type == "SIGABRT" && ex.Value == "Signal 6, Code 0" && + ex.Stacktrace?.Frames.Any(f => f.Function == "xamarin_unhandled_exception_handler") is true) + { + // Don't send it + options.LogDebug("Discarded {0} error ({1}). Captured as managed exception instead.", ex.Type, + ex.Value); + return null!; + } + + // Similar workaround for NullReferenceExceptions. We don't have any easy way to know whether the + // exception is managed code (compiled to native) or original native code though. + // See: https://github.com/getsentry/sentry-dotnet/issues/3776 + if (options.Native.SuppressExcBadAccess && ex.Type == "EXC_BAD_ACCESS") + { + // Don't send it + options.LogDebug("Discarded {0} error ({1}). Captured as managed exception instead.", ex.Type, + ex.Value); + return null!; + } + } + + // we run our SIGABRT checks first before handing over to user events + // because we delegate to user code, we need to protect anything that could happen in this event + if (options.BeforeSendInternal == null) + return evt; + + try + { + var sentryEvent = evt.ToSentryEvent(); + if (sentryEvent == null) + return evt; + + var result = options.BeforeSendInternal(sentryEvent, null!); + if (result == null) + return null!; + + // we only support a subset of mutated data to be passed back to the native SDK at this time + result.CopyToCocoaSentryEvent(evt); + + // Note: Nullable result is allowed but delegate is generated incorrectly + // See https://github.com/xamarin/xamarin-macios/issues/15299#issuecomment-1201863294 + return evt!; + } + catch (Exception ex) + { + options.LogError(ex, "Before Send Error"); + return evt; + } + } } diff --git a/test/Sentry.Tests/SentrySdkTests.cs b/test/Sentry.Tests/SentrySdkTests.cs index 4f8a45fec6..f88876e914 100644 --- a/test/Sentry.Tests/SentrySdkTests.cs +++ b/test/Sentry.Tests/SentrySdkTests.cs @@ -1,5 +1,17 @@ +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using FluentAssertions; +using NSubstitute; +using Sentry.Extensibility; +using Sentry.Internal; using Sentry.Internal.Http; using Sentry.Internal.ScopeStack; +using Sentry.Protocol.Envelopes; +using Sentry.Testing; +using Xunit; +using Xunit.Abstractions; using static Sentry.Internal.Constants; namespace Sentry.Tests; @@ -831,6 +843,80 @@ public void InitHub_DebugEnabled_DebugLogsLogged() Arg.Any()); } +#if __IOS__ + [Theory] + [InlineData(true)] + [InlineData(false)] + public void ProcessOnBeforeSend_NativeErrorSuppression(bool suppressNativeErrors) + { + var options = new SentryOptions + { + Dsn = ValidDsn, + DiagnosticLogger = _logger, + IsGlobalModeEnabled = true, + Debug = true, + AutoSessionTracking = false, + BackgroundWorker = Substitute.For(), + InitNativeSdks = false, + }; + options.Native.SuppressExcBadAccess = suppressNativeErrors; + + var called = false; + options.SetBeforeSend(e => + { + called = true; + return e; + }); + var evt = new Sentry.CocoaSdk.SentryEvent(); + var ex = new Sentry.CocoaSdk.SentryException("Not checked", "EXC_BAD_ACCESS"); + evt.Exceptions = [ex]; + var result = SentrySdk.ProcessOnBeforeSend(options, evt); + + if (suppressNativeErrors) + { + called.Should().BeFalse(); + result.Should().BeNull(); + } + else + { + called.Should().BeTrue(); + result.Exceptions.First().Type.Should().Be("EXC_BAD_ACCESS"); + } + } + + [Fact] + public void ProcessOnBeforeSend_OptionsBeforeOnSendRuns() + { + var options = new SentryOptions + { + Dsn = ValidDsn, + DiagnosticLogger = _logger, + IsGlobalModeEnabled = true, + Debug = true, + AutoSessionTracking = false, + BackgroundWorker = Substitute.For(), + InitNativeSdks = false + }; + + var native = new Sentry.CocoaSdk.SentryEvent(); + native.ServerName = "server name"; + native.Dist = "dist"; + native.Logger = "logger"; + native.ReleaseName = "release name"; + native.Environment = "environment"; + native.Transaction = "transaction name"; + + options.SetBeforeSend(e => + { + e.TransactionName = "dotnet"; + return e; + }); + var result = SentrySdk.ProcessOnBeforeSend(options, native); + result.Should().NotBeNull(); + result.Transaction.Should().Be("dotnet"); + } +#endif + public void Dispose() { SentrySdk.Close(); From db5606833a4b0662c6bea0663cca10cb05fb5157 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Wed, 5 Mar 2025 18:22:15 +1300 Subject: [PATCH 149/363] Support sending User Feedback without errors/exceptions (#3981) --- CHANGELOG.md | 1 + .../Controllers/HomeController.cs | 30 ++++++ .../Models/FeedbackModel.cs | 16 +++ .../Sentry.Samples.AspNetCore.Mvc.csproj | 4 + .../Views/Home/Feedback.cshtml | 36 +++++++ .../Views/Shared/_Layout.cshtml | 3 + .../Program.cs | 12 ++- samples/Sentry.Samples.Maui/MainPage.xaml | 7 ++ samples/Sentry.Samples.Maui/MainPage.xaml.cs | 5 + .../Sentry.Samples.Maui/SubmitFeedback.xaml | 25 +++++ .../SubmitFeedback.xaml.cs | 83 ++++++++++++++ src/Sentry/Extensibility/DisabledHub.cs | 8 ++ src/Sentry/Extensibility/HubAdapter.cs | 9 ++ src/Sentry/ISentryClient.cs | 9 ++ .../Extensions/CollectionsExtensions.cs | 18 ++++ src/Sentry/Internal/Hub.cs | 12 +++ src/Sentry/Protocol/Envelopes/Envelope.cs | 96 ++++++++++++----- src/Sentry/Protocol/Envelopes/EnvelopeItem.cs | 17 +++ src/Sentry/SentryClient.cs | 36 +++++++ src/Sentry/SentryClientExtensions.cs | 10 ++ src/Sentry/SentryContexts.cs | 21 ++++ src/Sentry/SentryFeedback.cs | 101 ++++++++++++++++++ src/Sentry/SentrySdk.cs | 19 ++++ src/Sentry/UserFeedback.cs | 1 + ...piApprovalTests.Run.DotNet8_0.verified.txt | 32 ++++++ ...piApprovalTests.Run.DotNet9_0.verified.txt | 32 ++++++ .../ApiApprovalTests.Run.Net4_8.verified.txt | 32 ++++++ test/Sentry.Tests/HubTests.cs | 23 ++++ .../CollectionExtensionsTests.verify.cs | 46 ++++++++ .../Protocol/Envelopes/EnvelopeTests.cs | 90 ++++++++++++++++ .../Protocol/UserFeedbackTests.cs | 2 + .../SentryClientExtensionsTests.cs | 4 + test/Sentry.Tests/SentryClientTests.cs | 96 +++++++++++++++++ 33 files changed, 909 insertions(+), 27 deletions(-) create mode 100644 samples/Sentry.Samples.AspNetCore.Mvc/Models/FeedbackModel.cs create mode 100644 samples/Sentry.Samples.AspNetCore.Mvc/Views/Home/Feedback.cshtml create mode 100644 samples/Sentry.Samples.Maui/SubmitFeedback.xaml create mode 100644 samples/Sentry.Samples.Maui/SubmitFeedback.xaml.cs create mode 100644 src/Sentry/SentryFeedback.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index cf29d0999a..34e7779d51 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ ### Features +- User Feedback can now be captured without errors/exceptions. Note that these APIs replace the older UserFeedback APIs, which have now been marked as obsolete (and will be removed in a future major version bump) ([#3981](https://github.com/getsentry/sentry-dotnet/pull/3981)) - Users can now register their own MAUI controls for breadcrumb creation ([#3997](https://github.com/getsentry/sentry-dotnet/pull/3997)) - Serilog scope properties are now sent with Sentry events ([#3976](https://github.com/getsentry/sentry-dotnet/pull/3976)) - The sample seed used for sampling decisions is now propagated, for use in downstream custom trace samplers ([#3951](https://github.com/getsentry/sentry-dotnet/pull/3951)) diff --git a/samples/Sentry.Samples.AspNetCore.Mvc/Controllers/HomeController.cs b/samples/Sentry.Samples.AspNetCore.Mvc/Controllers/HomeController.cs index de18940dd7..ef3a27275b 100644 --- a/samples/Sentry.Samples.AspNetCore.Mvc/Controllers/HomeController.cs +++ b/samples/Sentry.Samples.AspNetCore.Mvc/Controllers/HomeController.cs @@ -1,6 +1,7 @@ using System.Data; using System.Diagnostics; using Microsoft.AspNetCore.Mvc; +using Samples.AspNetCore.Mvc.Models; using Sentry.Ben.BlockingDetector; using Sentry.Samples.AspNetCore.Mvc.Models; @@ -176,4 +177,33 @@ public IActionResult Error() { return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); } + + [HttpGet("[controller]/feedback")] + public IActionResult Feedback() + { + return View(); + } + + [HttpPost] + public async Task SubmitFeedback(FeedbackModel feedback) + { + if (!ModelState.IsValid) + { + return View("Feedback", feedback); + } + + var sentryFeedback = new SentryFeedback(feedback.Message!, feedback.ContactEmail, feedback.Name); + var hint = new SentryHint(); + + if (feedback.Screenshot is { Length: > 0 }) + { + await using var memoryStream = new MemoryStream(); + await feedback.Screenshot.CopyToAsync(memoryStream); + hint.AddAttachment(memoryStream.ToArray(), feedback.Screenshot.FileName, AttachmentType.Default, "image/png"); + } + + SentrySdk.CaptureFeedback(sentryFeedback, null, hint); + ViewBag.Message = "Feedback submitted successfully!"; + return View("Index"); + } } diff --git a/samples/Sentry.Samples.AspNetCore.Mvc/Models/FeedbackModel.cs b/samples/Sentry.Samples.AspNetCore.Mvc/Models/FeedbackModel.cs new file mode 100644 index 0000000000..00dfba0ca3 --- /dev/null +++ b/samples/Sentry.Samples.AspNetCore.Mvc/Models/FeedbackModel.cs @@ -0,0 +1,16 @@ +using System.ComponentModel.DataAnnotations; + +namespace Samples.AspNetCore.Mvc.Models; + +public class FeedbackModel +{ + [Required] + public string? Message { get; set; } + + [EmailAddress] + public string? ContactEmail { get; set; } + + public string? Name { get; set; } + + public IFormFile? Screenshot { get; set; } +} diff --git a/samples/Sentry.Samples.AspNetCore.Mvc/Sentry.Samples.AspNetCore.Mvc.csproj b/samples/Sentry.Samples.AspNetCore.Mvc/Sentry.Samples.AspNetCore.Mvc.csproj index b2b958bf3c..d3bbf0aa34 100644 --- a/samples/Sentry.Samples.AspNetCore.Mvc/Sentry.Samples.AspNetCore.Mvc.csproj +++ b/samples/Sentry.Samples.AspNetCore.Mvc/Sentry.Samples.AspNetCore.Mvc.csproj @@ -18,4 +18,8 @@ + + + + diff --git a/samples/Sentry.Samples.AspNetCore.Mvc/Views/Home/Feedback.cshtml b/samples/Sentry.Samples.AspNetCore.Mvc/Views/Home/Feedback.cshtml new file mode 100644 index 0000000000..1534eda1c8 --- /dev/null +++ b/samples/Sentry.Samples.AspNetCore.Mvc/Views/Home/Feedback.cshtml @@ -0,0 +1,36 @@ +@model Samples.AspNetCore.Mvc.Models.FeedbackModel + +@{ + ViewData["Title"] = "Submit Feedback"; +} + +

Submit Feedback

+ +@if (ViewBag.Message != null) +{ +
@ViewBag.Message
+} + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+ +
diff --git a/samples/Sentry.Samples.AspNetCore.Mvc/Views/Shared/_Layout.cshtml b/samples/Sentry.Samples.AspNetCore.Mvc/Views/Shared/_Layout.cshtml index a10ed1f464..6de93829f5 100644 --- a/samples/Sentry.Samples.AspNetCore.Mvc/Views/Shared/_Layout.cshtml +++ b/samples/Sentry.Samples.AspNetCore.Mvc/Views/Shared/_Layout.cshtml @@ -31,6 +31,9 @@ + diff --git a/samples/Sentry.Samples.Console.Customized/Program.cs b/samples/Sentry.Samples.Console.Customized/Program.cs index f85ae49da6..dac1d9062a 100644 --- a/samples/Sentry.Samples.Console.Customized/Program.cs +++ b/samples/Sentry.Samples.Console.Customized/Program.cs @@ -142,12 +142,18 @@ await SentrySdk.ConfigureScopeAsync(async scope => var eventId = SentrySdk.CaptureMessage("Some warning!", SentryLevel.Warning); - // Send an user feedback linked to the warning. + // Send feedback linked to the warning. var timestamp = DateTime.Now.Ticks; var user = $"user{timestamp}"; var email = $"user{timestamp}@user{timestamp}.com"; - - SentrySdk.CaptureUserFeedback(new UserFeedback(eventId, user, email, "this is a sample user feedback")); + SentrySdk.CaptureFeedback(new SentryFeedback( + message: "this is a sample user feedback", + contactEmail: email, + name: user, + replayId: null, + url: null, + associatedEventId: eventId + )); var error = new Exception("Attempting to send this multiple times"); diff --git a/samples/Sentry.Samples.Maui/MainPage.xaml b/samples/Sentry.Samples.Maui/MainPage.xaml index d1519f610c..672b9d2347 100644 --- a/samples/Sentry.Samples.Maui/MainPage.xaml +++ b/samples/Sentry.Samples.Maui/MainPage.xaml @@ -70,6 +70,13 @@ Clicked="OnNativeCrashClicked" HorizontalOptions="Center" /> +
public SentryId CaptureEvent(SentryEvent evt, Scope? scope = null, SentryHint? hint = null) => SentryId.Empty; + /// + /// No-Op. + /// + public void CaptureFeedback(SentryFeedback feedback, Scope? scope = null, SentryHint? hint = null) + { + } + /// /// No-Op. /// @@ -205,6 +212,7 @@ public void Dispose() /// /// No-Op. /// + [Obsolete("Use CaptureFeedback instead.")] public void CaptureUserFeedback(UserFeedback userFeedback) { } diff --git a/src/Sentry/Extensibility/HubAdapter.cs b/src/Sentry/Extensibility/HubAdapter.cs index 5a4f01b62e..61d715d341 100644 --- a/src/Sentry/Extensibility/HubAdapter.cs +++ b/src/Sentry/Extensibility/HubAdapter.cs @@ -222,6 +222,14 @@ public SentryId CaptureEvent(SentryEvent evt, Scope? scope) public SentryId CaptureEvent(SentryEvent evt, Scope? scope, SentryHint? hint = null) => SentrySdk.CaptureEvent(evt, scope, hint); + /// + /// Forwards the call to . + /// + [DebuggerStepThrough] + [EditorBrowsable(EditorBrowsableState.Never)] + public void CaptureFeedback(SentryFeedback feedback, Scope? scope = null, SentryHint? hint = null) + => SentrySdk.CaptureFeedback(feedback, scope, hint); + /// /// Forwards the call to . /// @@ -292,6 +300,7 @@ public Task FlushAsync(TimeSpan timeout) ///
[DebuggerStepThrough] [EditorBrowsable(EditorBrowsableState.Never)] + [Obsolete("Use CaptureFeedback instead.")] public void CaptureUserFeedback(UserFeedback sentryUserFeedback) => SentrySdk.CaptureUserFeedback(sentryUserFeedback); } diff --git a/src/Sentry/ISentryClient.cs b/src/Sentry/ISentryClient.cs index e3e12fe191..3841539cc2 100644 --- a/src/Sentry/ISentryClient.cs +++ b/src/Sentry/ISentryClient.cs @@ -28,10 +28,19 @@ public interface ISentryClient /// The Id of the event. SentryId CaptureEvent(SentryEvent evt, Scope? scope = null, SentryHint? hint = null); + /// + /// Captures feedback from the user. + /// + /// The feedback to send to Sentry. + /// An optional scope to be applied to the event. + /// An optional hint providing high level context for the source of the event + void CaptureFeedback(SentryFeedback feedback, Scope? scope = null, SentryHint? hint = null); + /// /// Captures a user feedback. /// /// The user feedback to send to Sentry. + [Obsolete("Use CaptureFeedback instead.")] void CaptureUserFeedback(UserFeedback userFeedback); /// diff --git a/src/Sentry/Internal/Extensions/CollectionsExtensions.cs b/src/Sentry/Internal/Extensions/CollectionsExtensions.cs index 19277a632a..8d7b6f7bdf 100644 --- a/src/Sentry/Internal/Extensions/CollectionsExtensions.cs +++ b/src/Sentry/Internal/Extensions/CollectionsExtensions.cs @@ -17,6 +17,24 @@ public static TValue GetOrCreate( throw new($"Expected a type of {typeof(TValue)} to exist for the key '{key}'. Instead found a {value.GetType()}. The likely cause of this is that the value for '{key}' has been incorrectly set to an instance of a different type."); } + public static TValue? TryGetValue( + this ConcurrentDictionary dictionary, + string key) + where TValue : class + { + if (!dictionary.TryGetValue(key, out var value)) + { + return null; + } + + if (value is TValue casted) + { + return casted; + } + + throw new($"Expected a type of {typeof(TValue)} to exist for the key '{key}'. Instead found a {value.GetType()}. The likely cause of this is that the value for '{key}' has been incorrectly set to an instance of a different type."); + } + public static void TryCopyTo(this IDictionary from, IDictionary to) where TKey : notnull { diff --git a/src/Sentry/Internal/Hub.cs b/src/Sentry/Internal/Hub.cs index a564ab1791..cd00686250 100644 --- a/src/Sentry/Internal/Hub.cs +++ b/src/Sentry/Internal/Hub.cs @@ -506,6 +506,17 @@ private SentryId CaptureEvent(SentryEvent evt, SentryHint? hint, Scope scope) } } + public void CaptureFeedback(SentryFeedback feedback, Scope? scope = null, SentryHint? hint = null) + { + if (!IsEnabled) + { + return; + } + + scope ??= CurrentScope; + CurrentClient.CaptureFeedback(feedback, scope, hint); + } + #if MEMORY_DUMP_SUPPORTED internal void CaptureHeapDump(string dumpFile) { @@ -534,6 +545,7 @@ internal void CaptureHeapDump(string dumpFile) } #endif + [Obsolete("Use CaptureFeedback instead.")] public void CaptureUserFeedback(UserFeedback userFeedback) { if (!IsEnabled) diff --git a/src/Sentry/Protocol/Envelopes/Envelope.cs b/src/Sentry/Protocol/Envelopes/Envelope.cs index 29ad6a8e07..b62dc82c98 100644 --- a/src/Sentry/Protocol/Envelopes/Envelope.cs +++ b/src/Sentry/Protocol/Envelopes/Envelope.cs @@ -248,31 +248,78 @@ public static Envelope FromEvent( continue; } - try - { - // We pull the stream out here so we can length check - // to avoid adding an invalid attachment - var stream = attachment.Content.GetStream(); - if (stream.TryGetLength() != 0) - { - items.Add(EnvelopeItem.FromAttachment(attachment, stream)); - } - else - { - // We would normally dispose the stream when we dispose the envelope item - // But in this case, we need to explicitly dispose here or we will be leaving - // the stream open indefinitely. - stream.Dispose(); - - logger?.LogWarning("Did not add '{0}' to envelope because the stream was empty.", - attachment.FileName); - } - } - catch (Exception exception) - { - logger?.LogError(exception, "Failed to add attachment: {0}.", attachment.FileName); - } + AddEnvelopeItemFromAttachment(items, attachment, logger); + } + } + + if (sessionUpdate is not null) + { + items.Add(EnvelopeItem.FromSession(sessionUpdate)); + } + + return new Envelope(eventId, header, items); + } + + private static void AddEnvelopeItemFromAttachment(List items, SentryAttachment attachment, + IDiagnosticLogger? logger) + { + try + { + // We pull the stream out here so we can length check + // to avoid adding an invalid attachment + var stream = attachment.Content.GetStream(); + if (stream.TryGetLength() != 0) + { + items.Add(EnvelopeItem.FromAttachment(attachment, stream)); } + else + { + // We would normally dispose the stream when we dispose the envelope item + // But in this case, we need to explicitly dispose here or we will be leaving + // the stream open indefinitely. + stream.Dispose(); + + logger?.LogWarning("Did not add '{0}' to envelope because the stream was empty.", + attachment.FileName); + } + } + catch (Exception exception) + { + logger?.LogError(exception, "Failed to add attachment: {0}.", attachment.FileName); + } + } + + /// + /// Creates an envelope that contains a single feedback event. + /// + public static Envelope FromFeedback( + SentryEvent @event, + IDiagnosticLogger? logger = null, + IReadOnlyCollection? attachments = null, + SessionUpdate? sessionUpdate = null) + { + if (@event.Contexts.Feedback == null) + { + throw new ArgumentException("Unable to create envelope - the event does not contain any feedback."); + } + + var eventId = @event.EventId; + var header = CreateHeader(eventId, @event.DynamicSamplingContext); + + var items = new List + { + EnvelopeItem.FromFeedback(@event) + }; + + if (attachments is { Count: > 0 }) + { + if (attachments.Count > 1) + { + logger?.LogWarning("Feedback can only contain one attachment. Discarding {0} additional attachments.", + attachments.Count - 1); + } + + AddEnvelopeItemFromAttachment(items, attachments.First(), logger); } if (sessionUpdate is not null) @@ -286,6 +333,7 @@ public static Envelope FromEvent( /// /// Creates an envelope that contains a single user feedback. /// + [Obsolete("Use FromFeedback instead.")] public static Envelope FromUserFeedback(UserFeedback sentryUserFeedback) { var eventId = sentryUserFeedback.EventId; diff --git a/src/Sentry/Protocol/Envelopes/EnvelopeItem.cs b/src/Sentry/Protocol/Envelopes/EnvelopeItem.cs index 9954de43d6..7c721db581 100644 --- a/src/Sentry/Protocol/Envelopes/EnvelopeItem.cs +++ b/src/Sentry/Protocol/Envelopes/EnvelopeItem.cs @@ -13,6 +13,7 @@ public sealed class EnvelopeItem : ISerializable, IDisposable private const string TypeKey = "type"; internal const string TypeValueEvent = "event"; + internal const string TypeValueFeedback = "feedback"; internal const string TypeValueUserReport = "user_report"; internal const string TypeValueTransaction = "transaction"; internal const string TypeValueSpan = "span"; @@ -216,9 +217,23 @@ public static EnvelopeItem FromEvent(SentryEvent @event) return new EnvelopeItem(header, new JsonSerializable(@event)); } + /// + /// Creates an from a feedback . + /// + public static EnvelopeItem FromFeedback(SentryEvent @event) + { + var header = new Dictionary(1, StringComparer.Ordinal) + { + [TypeKey] = TypeValueFeedback + }; + + return new EnvelopeItem(header, new JsonSerializable(@event)); + } + /// /// Creates an from . /// + [Obsolete("Use FromFeedback instead.")] public static EnvelopeItem FromUserFeedback(UserFeedback sentryUserFeedback) { var header = new Dictionary(1, StringComparer.Ordinal) @@ -392,9 +407,11 @@ private static async Task DeserializePayloadAsync( // User report if (string.Equals(payloadType, TypeValueUserReport, StringComparison.OrdinalIgnoreCase)) { +#pragma warning disable CS0618 // Type or member is obsolete var bufferLength = (int)(payloadLength ?? stream.Length); var buffer = await stream.ReadByteChunkAsync(bufferLength, cancellationToken).ConfigureAwait(false); var userFeedback = Json.Parse(buffer, UserFeedback.FromJson); +#pragma warning restore CS0618 // Type or member is obsolete return new JsonSerializable(userFeedback); } diff --git a/src/Sentry/SentryClient.cs b/src/Sentry/SentryClient.cs index 7d5b071dc5..7aef9aa8b5 100644 --- a/src/Sentry/SentryClient.cs +++ b/src/Sentry/SentryClient.cs @@ -82,6 +82,42 @@ public SentryId CaptureEvent(SentryEvent? @event, Scope? scope = null, SentryHin } /// + public void CaptureFeedback(SentryFeedback feedback, Scope? scope = null, SentryHint? hint = null) + { + if (string.IsNullOrEmpty(feedback.Message)) + { + _options.LogWarning("Feedback dropped due to empty message."); + return; + } + + scope ??= new Scope(_options); + hint ??= new SentryHint(); + hint.AddAttachmentsFromScope(scope); + + _options.LogInfo("Capturing feedback: '{0}'.", feedback.Message); + + var evt = new SentryEvent { Level = SentryLevel.Info }; + evt.Contexts.Feedback = feedback; + // type: 'feedback', + + // Evaluate and copy before invoking the callback + scope.Evaluate(); + scope.Apply(evt); + + if (scope.Level != null && scope.Level != SentryLevel.Info) + { + // Level on scope takes precedence over the one on event + _options.LogInfo("Overriding level set on feedback event '{0}' with level set on scope '{1}'.", evt.Level, scope.Level); + evt.Level = scope.Level; + } + + var attachments = hint.Attachments.ToList(); + var envelope = Envelope.FromFeedback(evt, _options.DiagnosticLogger, attachments, scope.SessionUpdate); + CaptureEnvelope(envelope); + } + + /// + [Obsolete("Use CaptureFeedback instead.")] public void CaptureUserFeedback(UserFeedback userFeedback) { if (userFeedback.EventId.Equals(SentryId.Empty)) diff --git a/src/Sentry/SentryClientExtensions.cs b/src/Sentry/SentryClientExtensions.cs index 686a732229..2af8b85f8c 100644 --- a/src/Sentry/SentryClientExtensions.cs +++ b/src/Sentry/SentryClientExtensions.cs @@ -40,6 +40,15 @@ public static SentryId CaptureMessage(this ISentryClient client, string message, return SentryId.Empty; } + /// + /// Captures feedback from the user. + /// + public static void CaptureFeedback(this ISentryClient client, string message, string? contactEmail = null, + string? name = null, string? replayId = null, string? url = null, SentryId? associatedEventId = null, + Scope? scope = null, SentryHint? hint = null) + => client.CaptureFeedback(new SentryFeedback(message, contactEmail, name, replayId, url, associatedEventId), + scope, hint); + /// /// Captures a user feedback. /// @@ -48,6 +57,7 @@ public static SentryId CaptureMessage(this ISentryClient client, string message, /// The user email. /// The user comments. /// The optional username. + [Obsolete("Use CaptureFeedback instead.")] public static void CaptureUserFeedback(this ISentryClient client, SentryId eventId, string email, string comments, string? name = null) { diff --git a/src/Sentry/SentryContexts.cs b/src/Sentry/SentryContexts.cs index a9a2ed3efb..b0abb14227 100644 --- a/src/Sentry/SentryContexts.cs +++ b/src/Sentry/SentryContexts.cs @@ -30,6 +30,23 @@ public sealed class SentryContexts : IDictionary, ISentryJsonSer /// public Device Device => _innerDictionary.GetOrCreate(Device.Type); + /// + /// Holds user feedback. + /// + public SentryFeedback? Feedback + { + get => _innerDictionary.TryGetValue(SentryFeedback.Type); + set + { + if (value is null) + { + _innerDictionary.TryRemove(SentryFeedback.Type, out _); + return; + } + _innerDictionary[SentryFeedback.Type] = value; + } + } + /// /// Defines the operating system. /// @@ -147,6 +164,10 @@ public static SentryContexts FromJson(JsonElement json) { result[name] = Device.FromJson(value); } + else if (string.Equals(type, SentryFeedback.Type, StringComparison.OrdinalIgnoreCase)) + { + result[name] = SentryFeedback.FromJson(value); + } else if (string.Equals(type, OperatingSystem.Type, StringComparison.OrdinalIgnoreCase)) { result[name] = OperatingSystem.FromJson(value); diff --git a/src/Sentry/SentryFeedback.cs b/src/Sentry/SentryFeedback.cs new file mode 100644 index 0000000000..19dee45099 --- /dev/null +++ b/src/Sentry/SentryFeedback.cs @@ -0,0 +1,101 @@ +using Sentry.Extensibility; +using Sentry.Internal; +using Sentry.Internal.Extensions; + +namespace Sentry; + +/// +/// Sentry User Feedback. +/// +public sealed class SentryFeedback : ISentryJsonSerializable, ICloneable +{ + /// + /// Tells Sentry which type of context this is. + /// + internal const string Type = "feedback"; + + /// + /// Message containing the user's feedback. + /// + public string Message { get; set; } = string.Empty; + + /// + /// The user's contact email address. + /// + public string? ContactEmail { get; set; } + + /// + /// The name of the user. + /// + public string? Name { get; set; } + + /// + /// Optional ID of the Replay session associated with the feedback. + /// + public string? ReplayId { get; set; } + + /// + /// Url that the feedback relates to + /// + public string? Url { get; set; } + + /// + /// Optional ID of the event that the user feedback is associated with. + /// + public SentryId? AssociatedEventId { get; set; } + + /// + /// Creates an instance of . + /// + public SentryFeedback(string message, string? contactEmail = null, string? name = null, string? replayId = null, string? url = null, SentryId? associatedEventId = null) + { + Message = message; + ContactEmail = contactEmail; + Name = name; + ReplayId = replayId; + Url = url; + AssociatedEventId = associatedEventId; + } + + /// + public void WriteTo(Utf8JsonWriter writer, IDiagnosticLogger? logger) + { + if (string.IsNullOrEmpty(Message)) + { + logger?.LogWarning("Feedback message is empty - serializing as null"); + writer.WriteNullValue(); + return; + } + + writer.WriteStartObject(); + + writer.WriteString("message", Message); + writer.WriteStringIfNotWhiteSpace("contact_email", ContactEmail); + writer.WriteStringIfNotWhiteSpace("name", Name); + writer.WriteStringIfNotWhiteSpace("replay_id", ReplayId); + writer.WriteStringIfNotWhiteSpace("url", Url); + writer.WriteSerializableIfNotNull("associated_event_id", AssociatedEventId, logger); + + writer.WriteEndObject(); + } + + /// + /// Parses from JSON. + /// + public static SentryFeedback FromJson(JsonElement json) + { + var message = json.GetPropertyOrNull("message")?.GetString() ?? ""; + var contactEmail = json.GetPropertyOrNull("contact_email")?.GetString(); + var name = json.GetPropertyOrNull("name")?.GetString(); + var replayId = json.GetPropertyOrNull("replay_id")?.GetString(); + var url = json.GetPropertyOrNull("url")?.GetString(); + var eventId = json.GetPropertyOrNull("associated_event_id")?.Pipe(SentryId.FromJson); + + return new SentryFeedback(message, contactEmail, name, replayId, url, eventId); + } + + internal SentryFeedback Clone() => ((ICloneable)this).Clone(); + + SentryFeedback ICloneable.Clone() + => new(Message, ContactEmail, Name, ReplayId, Url, AssociatedEventId); +} diff --git a/src/Sentry/SentrySdk.cs b/src/Sentry/SentrySdk.cs index 505bf7bb1b..401a0fa6f0 100644 --- a/src/Sentry/SentrySdk.cs +++ b/src/Sentry/SentrySdk.cs @@ -481,11 +481,29 @@ public static SentryId CaptureMessage(string message, SentryLevel level = Sentry public static SentryId CaptureMessage(string message, Action configureScope, SentryLevel level = SentryLevel.Info) => CurrentHub.CaptureMessage(message, configureScope, level); + /// + /// Captures feedback from the user. + /// + [DebuggerStepThrough] + public static void CaptureFeedback(SentryFeedback feedback, Scope? scope = null, SentryHint? hint = null) + => CurrentHub.CaptureFeedback(feedback, scope, hint); + + /// + /// Captures feedback from the user. + /// + [DebuggerStepThrough] + public static void CaptureFeedback(string message, string? contactEmail = null, string? name = null, + string? replayId = null, string? url = null, SentryId? associatedEventId = null, Scope? scope = null, + SentryHint? hint = null) + => CurrentHub.CaptureFeedback(new SentryFeedback(message, contactEmail, name, replayId, url, associatedEventId), + scope, hint); + /// /// Captures a user feedback. /// /// The user feedback to send to Sentry. [DebuggerStepThrough] + [Obsolete("Use CaptureFeedback instead.")] public static void CaptureUserFeedback(UserFeedback userFeedback) => CurrentHub.CaptureUserFeedback(userFeedback); @@ -497,6 +515,7 @@ public static void CaptureUserFeedback(UserFeedback userFeedback) /// The user comments. /// The optional username. [DebuggerStepThrough] + [Obsolete("Use CaptureFeedback instead.")] public static void CaptureUserFeedback(SentryId eventId, string email, string comments, string? name = null) => CurrentHub.CaptureUserFeedback(new UserFeedback(eventId, name, email, comments)); diff --git a/src/Sentry/UserFeedback.cs b/src/Sentry/UserFeedback.cs index 3de5773d69..0094a6ade4 100644 --- a/src/Sentry/UserFeedback.cs +++ b/src/Sentry/UserFeedback.cs @@ -6,6 +6,7 @@ namespace Sentry; /// /// Sentry User Feedback. /// +[Obsolete("Use SentryFeedback instead.")] public sealed class UserFeedback : ISentryJsonSerializable { /// diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt index 2a4a0e61a3..67795954b7 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt @@ -237,9 +237,11 @@ namespace Sentry Sentry.SentryId CaptureCheckIn(string monitorSlug, Sentry.CheckInStatus status, Sentry.SentryId? sentryId = default, System.TimeSpan? duration = default, Sentry.Scope? scope = null, System.Action? configureMonitorOptions = null); bool CaptureEnvelope(Sentry.Protocol.Envelopes.Envelope envelope); Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, Sentry.Scope? scope = null, Sentry.SentryHint? hint = null); + void CaptureFeedback(Sentry.SentryFeedback feedback, Sentry.Scope? scope = null, Sentry.SentryHint? hint = null); void CaptureSession(Sentry.SessionUpdate sessionUpdate); void CaptureTransaction(Sentry.SentryTransaction transaction); void CaptureTransaction(Sentry.SentryTransaction transaction, Sentry.Scope? scope, Sentry.SentryHint? hint); + [System.Obsolete("Use CaptureFeedback instead.")] void CaptureUserFeedback(Sentry.UserFeedback userFeedback); System.Threading.Tasks.Task FlushAsync(System.TimeSpan timeout); } @@ -461,9 +463,11 @@ namespace Sentry public Sentry.SentryId CaptureCheckIn(string monitorSlug, Sentry.CheckInStatus status, Sentry.SentryId? sentryId = default, System.TimeSpan? duration = default, Sentry.Scope? scope = null, System.Action? configureMonitorOptions = null) { } public bool CaptureEnvelope(Sentry.Protocol.Envelopes.Envelope envelope) { } public Sentry.SentryId CaptureEvent(Sentry.SentryEvent? @event, Sentry.Scope? scope = null, Sentry.SentryHint? hint = null) { } + public void CaptureFeedback(Sentry.SentryFeedback feedback, Sentry.Scope? scope = null, Sentry.SentryHint? hint = null) { } public void CaptureSession(Sentry.SessionUpdate sessionUpdate) { } public void CaptureTransaction(Sentry.SentryTransaction transaction) { } public void CaptureTransaction(Sentry.SentryTransaction transaction, Sentry.Scope? scope, Sentry.SentryHint? hint) { } + [System.Obsolete("Use CaptureFeedback instead.")] public void CaptureUserFeedback(Sentry.UserFeedback userFeedback) { } public void Dispose() { } public System.Threading.Tasks.Task FlushAsync(System.TimeSpan timeout) { } @@ -471,7 +475,9 @@ namespace Sentry public static class SentryClientExtensions { public static Sentry.SentryId CaptureException(this Sentry.ISentryClient client, System.Exception ex) { } + public static void CaptureFeedback(this Sentry.ISentryClient client, string message, string? contactEmail = null, string? name = null, string? replayId = null, string? url = null, Sentry.SentryId? associatedEventId = default, Sentry.Scope? scope = null, Sentry.SentryHint? hint = null) { } public static Sentry.SentryId CaptureMessage(this Sentry.ISentryClient client, string message, Sentry.SentryLevel level = 1) { } + [System.Obsolete("Use CaptureFeedback instead.")] public static void CaptureUserFeedback(this Sentry.ISentryClient client, Sentry.SentryId eventId, string email, string comments, string? name = null) { } public static void Flush(this Sentry.ISentryClient client) { } public static void Flush(this Sentry.ISentryClient client, System.TimeSpan timeout) { } @@ -491,6 +497,7 @@ namespace Sentry public Sentry.Protocol.Browser Browser { get; } public int Count { get; } public Sentry.Protocol.Device Device { get; } + public Sentry.SentryFeedback? Feedback { get; set; } public Sentry.Protocol.Gpu Gpu { get; } public bool IsReadOnly { get; } public object this[string key] { get; set; } @@ -549,6 +556,18 @@ namespace Sentry public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } public static Sentry.SentryEvent FromJson(System.Text.Json.JsonElement json) { } } + public sealed class SentryFeedback : Sentry.ISentryJsonSerializable + { + public SentryFeedback(string message, string? contactEmail = null, string? name = null, string? replayId = null, string? url = null, Sentry.SentryId? associatedEventId = default) { } + public Sentry.SentryId? AssociatedEventId { get; set; } + public string? ContactEmail { get; set; } + public string Message { get; set; } + public string? Name { get; set; } + public string? ReplayId { get; set; } + public string? Url { get; set; } + public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } + public static Sentry.SentryFeedback FromJson(System.Text.Json.JsonElement json) { } + } public class SentryGraphQLHttpMessageHandler : Sentry.SentryMessageHandler { public SentryGraphQLHttpMessageHandler(System.Net.Http.HttpMessageHandler? innerHandler = null, Sentry.IHub? hub = null) { } @@ -803,12 +822,16 @@ namespace Sentry public static Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, Sentry.SentryHint? hint, System.Action configureScope) { } public static Sentry.SentryId CaptureException(System.Exception exception) { } public static Sentry.SentryId CaptureException(System.Exception exception, System.Action configureScope) { } + public static void CaptureFeedback(Sentry.SentryFeedback feedback, Sentry.Scope? scope = null, Sentry.SentryHint? hint = null) { } + public static void CaptureFeedback(string message, string? contactEmail = null, string? name = null, string? replayId = null, string? url = null, Sentry.SentryId? associatedEventId = default, Sentry.Scope? scope = null, Sentry.SentryHint? hint = null) { } public static Sentry.SentryId CaptureMessage(string message, Sentry.SentryLevel level = 1) { } public static Sentry.SentryId CaptureMessage(string message, System.Action configureScope, Sentry.SentryLevel level = 1) { } public static void CaptureSession(Sentry.SessionUpdate sessionUpdate) { } public static void CaptureTransaction(Sentry.SentryTransaction transaction) { } public static void CaptureTransaction(Sentry.SentryTransaction transaction, Sentry.Scope? scope, Sentry.SentryHint? hint) { } + [System.Obsolete("Use CaptureFeedback instead.")] public static void CaptureUserFeedback(Sentry.UserFeedback userFeedback) { } + [System.Obsolete("Use CaptureFeedback instead.")] public static void CaptureUserFeedback(Sentry.SentryId eventId, string email, string comments, string? name = null) { } [System.Obsolete("WARNING: This method deliberately causes a crash, and should not be used in a rea" + "l application.")] @@ -1220,6 +1243,7 @@ namespace Sentry public Sentry.ISpan StartChild(string operation) { } public void UnsetTag(string key) { } } + [System.Obsolete("Use SentryFeedback instead.")] public sealed class UserFeedback : Sentry.ISentryJsonSerializable { public UserFeedback(Sentry.SentryId eventId, string? name, string? email, string? comments) { } @@ -1308,9 +1332,11 @@ namespace Sentry.Extensibility public Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, System.Action configureScope) { } public Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, Sentry.Scope? scope = null, Sentry.SentryHint? hint = null) { } public Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, Sentry.SentryHint? hint, System.Action configureScope) { } + public void CaptureFeedback(Sentry.SentryFeedback feedback, Sentry.Scope? scope = null, Sentry.SentryHint? hint = null) { } public void CaptureSession(Sentry.SessionUpdate sessionUpdate) { } public void CaptureTransaction(Sentry.SentryTransaction transaction) { } public void CaptureTransaction(Sentry.SentryTransaction transaction, Sentry.Scope? scope, Sentry.SentryHint? hint) { } + [System.Obsolete("Use CaptureFeedback instead.")] public void CaptureUserFeedback(Sentry.UserFeedback userFeedback) { } public void ConfigureScope(System.Action configureScope) { } public System.Threading.Tasks.Task ConfigureScopeAsync(System.Func configureScope) { } @@ -1352,9 +1378,11 @@ namespace Sentry.Extensibility public Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, Sentry.Scope? scope, Sentry.SentryHint? hint = null) { } public Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, Sentry.SentryHint? hint, System.Action configureScope) { } public Sentry.SentryId CaptureException(System.Exception exception) { } + public void CaptureFeedback(Sentry.SentryFeedback feedback, Sentry.Scope? scope = null, Sentry.SentryHint? hint = null) { } public void CaptureSession(Sentry.SessionUpdate sessionUpdate) { } public void CaptureTransaction(Sentry.SentryTransaction transaction) { } public void CaptureTransaction(Sentry.SentryTransaction transaction, Sentry.Scope? scope, Sentry.SentryHint? hint) { } + [System.Obsolete("Use CaptureFeedback instead.")] public void CaptureUserFeedback(Sentry.UserFeedback sentryUserFeedback) { } public void ConfigureScope(System.Action configureScope) { } public System.Threading.Tasks.Task ConfigureScopeAsync(System.Func configureScope) { } @@ -1796,8 +1824,10 @@ namespace Sentry.Protocol.Envelopes public static System.Threading.Tasks.Task DeserializeAsync(System.IO.Stream stream, System.Threading.CancellationToken cancellationToken = default) { } public static Sentry.Protocol.Envelopes.Envelope FromCheckIn(Sentry.SentryCheckIn checkIn) { } public static Sentry.Protocol.Envelopes.Envelope FromEvent(Sentry.SentryEvent @event, Sentry.Extensibility.IDiagnosticLogger? logger = null, System.Collections.Generic.IReadOnlyCollection? attachments = null, Sentry.SessionUpdate? sessionUpdate = null) { } + public static Sentry.Protocol.Envelopes.Envelope FromFeedback(Sentry.SentryEvent @event, Sentry.Extensibility.IDiagnosticLogger? logger = null, System.Collections.Generic.IReadOnlyCollection? attachments = null, Sentry.SessionUpdate? sessionUpdate = null) { } public static Sentry.Protocol.Envelopes.Envelope FromSession(Sentry.SessionUpdate sessionUpdate) { } public static Sentry.Protocol.Envelopes.Envelope FromTransaction(Sentry.SentryTransaction transaction) { } + [System.Obsolete("Use FromFeedback instead.")] public static Sentry.Protocol.Envelopes.Envelope FromUserFeedback(Sentry.UserFeedback sentryUserFeedback) { } } public sealed class EnvelopeItem : Sentry.Protocol.Envelopes.ISerializable, System.IDisposable @@ -1815,8 +1845,10 @@ namespace Sentry.Protocol.Envelopes public static Sentry.Protocol.Envelopes.EnvelopeItem FromAttachment(Sentry.SentryAttachment attachment) { } public static Sentry.Protocol.Envelopes.EnvelopeItem FromCheckIn(Sentry.SentryCheckIn checkIn) { } public static Sentry.Protocol.Envelopes.EnvelopeItem FromEvent(Sentry.SentryEvent @event) { } + public static Sentry.Protocol.Envelopes.EnvelopeItem FromFeedback(Sentry.SentryEvent @event) { } public static Sentry.Protocol.Envelopes.EnvelopeItem FromSession(Sentry.SessionUpdate sessionUpdate) { } public static Sentry.Protocol.Envelopes.EnvelopeItem FromTransaction(Sentry.SentryTransaction transaction) { } + [System.Obsolete("Use FromFeedback instead.")] public static Sentry.Protocol.Envelopes.EnvelopeItem FromUserFeedback(Sentry.UserFeedback sentryUserFeedback) { } } public interface ISerializable diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt index 2a4a0e61a3..67795954b7 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt @@ -237,9 +237,11 @@ namespace Sentry Sentry.SentryId CaptureCheckIn(string monitorSlug, Sentry.CheckInStatus status, Sentry.SentryId? sentryId = default, System.TimeSpan? duration = default, Sentry.Scope? scope = null, System.Action? configureMonitorOptions = null); bool CaptureEnvelope(Sentry.Protocol.Envelopes.Envelope envelope); Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, Sentry.Scope? scope = null, Sentry.SentryHint? hint = null); + void CaptureFeedback(Sentry.SentryFeedback feedback, Sentry.Scope? scope = null, Sentry.SentryHint? hint = null); void CaptureSession(Sentry.SessionUpdate sessionUpdate); void CaptureTransaction(Sentry.SentryTransaction transaction); void CaptureTransaction(Sentry.SentryTransaction transaction, Sentry.Scope? scope, Sentry.SentryHint? hint); + [System.Obsolete("Use CaptureFeedback instead.")] void CaptureUserFeedback(Sentry.UserFeedback userFeedback); System.Threading.Tasks.Task FlushAsync(System.TimeSpan timeout); } @@ -461,9 +463,11 @@ namespace Sentry public Sentry.SentryId CaptureCheckIn(string monitorSlug, Sentry.CheckInStatus status, Sentry.SentryId? sentryId = default, System.TimeSpan? duration = default, Sentry.Scope? scope = null, System.Action? configureMonitorOptions = null) { } public bool CaptureEnvelope(Sentry.Protocol.Envelopes.Envelope envelope) { } public Sentry.SentryId CaptureEvent(Sentry.SentryEvent? @event, Sentry.Scope? scope = null, Sentry.SentryHint? hint = null) { } + public void CaptureFeedback(Sentry.SentryFeedback feedback, Sentry.Scope? scope = null, Sentry.SentryHint? hint = null) { } public void CaptureSession(Sentry.SessionUpdate sessionUpdate) { } public void CaptureTransaction(Sentry.SentryTransaction transaction) { } public void CaptureTransaction(Sentry.SentryTransaction transaction, Sentry.Scope? scope, Sentry.SentryHint? hint) { } + [System.Obsolete("Use CaptureFeedback instead.")] public void CaptureUserFeedback(Sentry.UserFeedback userFeedback) { } public void Dispose() { } public System.Threading.Tasks.Task FlushAsync(System.TimeSpan timeout) { } @@ -471,7 +475,9 @@ namespace Sentry public static class SentryClientExtensions { public static Sentry.SentryId CaptureException(this Sentry.ISentryClient client, System.Exception ex) { } + public static void CaptureFeedback(this Sentry.ISentryClient client, string message, string? contactEmail = null, string? name = null, string? replayId = null, string? url = null, Sentry.SentryId? associatedEventId = default, Sentry.Scope? scope = null, Sentry.SentryHint? hint = null) { } public static Sentry.SentryId CaptureMessage(this Sentry.ISentryClient client, string message, Sentry.SentryLevel level = 1) { } + [System.Obsolete("Use CaptureFeedback instead.")] public static void CaptureUserFeedback(this Sentry.ISentryClient client, Sentry.SentryId eventId, string email, string comments, string? name = null) { } public static void Flush(this Sentry.ISentryClient client) { } public static void Flush(this Sentry.ISentryClient client, System.TimeSpan timeout) { } @@ -491,6 +497,7 @@ namespace Sentry public Sentry.Protocol.Browser Browser { get; } public int Count { get; } public Sentry.Protocol.Device Device { get; } + public Sentry.SentryFeedback? Feedback { get; set; } public Sentry.Protocol.Gpu Gpu { get; } public bool IsReadOnly { get; } public object this[string key] { get; set; } @@ -549,6 +556,18 @@ namespace Sentry public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } public static Sentry.SentryEvent FromJson(System.Text.Json.JsonElement json) { } } + public sealed class SentryFeedback : Sentry.ISentryJsonSerializable + { + public SentryFeedback(string message, string? contactEmail = null, string? name = null, string? replayId = null, string? url = null, Sentry.SentryId? associatedEventId = default) { } + public Sentry.SentryId? AssociatedEventId { get; set; } + public string? ContactEmail { get; set; } + public string Message { get; set; } + public string? Name { get; set; } + public string? ReplayId { get; set; } + public string? Url { get; set; } + public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } + public static Sentry.SentryFeedback FromJson(System.Text.Json.JsonElement json) { } + } public class SentryGraphQLHttpMessageHandler : Sentry.SentryMessageHandler { public SentryGraphQLHttpMessageHandler(System.Net.Http.HttpMessageHandler? innerHandler = null, Sentry.IHub? hub = null) { } @@ -803,12 +822,16 @@ namespace Sentry public static Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, Sentry.SentryHint? hint, System.Action configureScope) { } public static Sentry.SentryId CaptureException(System.Exception exception) { } public static Sentry.SentryId CaptureException(System.Exception exception, System.Action configureScope) { } + public static void CaptureFeedback(Sentry.SentryFeedback feedback, Sentry.Scope? scope = null, Sentry.SentryHint? hint = null) { } + public static void CaptureFeedback(string message, string? contactEmail = null, string? name = null, string? replayId = null, string? url = null, Sentry.SentryId? associatedEventId = default, Sentry.Scope? scope = null, Sentry.SentryHint? hint = null) { } public static Sentry.SentryId CaptureMessage(string message, Sentry.SentryLevel level = 1) { } public static Sentry.SentryId CaptureMessage(string message, System.Action configureScope, Sentry.SentryLevel level = 1) { } public static void CaptureSession(Sentry.SessionUpdate sessionUpdate) { } public static void CaptureTransaction(Sentry.SentryTransaction transaction) { } public static void CaptureTransaction(Sentry.SentryTransaction transaction, Sentry.Scope? scope, Sentry.SentryHint? hint) { } + [System.Obsolete("Use CaptureFeedback instead.")] public static void CaptureUserFeedback(Sentry.UserFeedback userFeedback) { } + [System.Obsolete("Use CaptureFeedback instead.")] public static void CaptureUserFeedback(Sentry.SentryId eventId, string email, string comments, string? name = null) { } [System.Obsolete("WARNING: This method deliberately causes a crash, and should not be used in a rea" + "l application.")] @@ -1220,6 +1243,7 @@ namespace Sentry public Sentry.ISpan StartChild(string operation) { } public void UnsetTag(string key) { } } + [System.Obsolete("Use SentryFeedback instead.")] public sealed class UserFeedback : Sentry.ISentryJsonSerializable { public UserFeedback(Sentry.SentryId eventId, string? name, string? email, string? comments) { } @@ -1308,9 +1332,11 @@ namespace Sentry.Extensibility public Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, System.Action configureScope) { } public Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, Sentry.Scope? scope = null, Sentry.SentryHint? hint = null) { } public Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, Sentry.SentryHint? hint, System.Action configureScope) { } + public void CaptureFeedback(Sentry.SentryFeedback feedback, Sentry.Scope? scope = null, Sentry.SentryHint? hint = null) { } public void CaptureSession(Sentry.SessionUpdate sessionUpdate) { } public void CaptureTransaction(Sentry.SentryTransaction transaction) { } public void CaptureTransaction(Sentry.SentryTransaction transaction, Sentry.Scope? scope, Sentry.SentryHint? hint) { } + [System.Obsolete("Use CaptureFeedback instead.")] public void CaptureUserFeedback(Sentry.UserFeedback userFeedback) { } public void ConfigureScope(System.Action configureScope) { } public System.Threading.Tasks.Task ConfigureScopeAsync(System.Func configureScope) { } @@ -1352,9 +1378,11 @@ namespace Sentry.Extensibility public Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, Sentry.Scope? scope, Sentry.SentryHint? hint = null) { } public Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, Sentry.SentryHint? hint, System.Action configureScope) { } public Sentry.SentryId CaptureException(System.Exception exception) { } + public void CaptureFeedback(Sentry.SentryFeedback feedback, Sentry.Scope? scope = null, Sentry.SentryHint? hint = null) { } public void CaptureSession(Sentry.SessionUpdate sessionUpdate) { } public void CaptureTransaction(Sentry.SentryTransaction transaction) { } public void CaptureTransaction(Sentry.SentryTransaction transaction, Sentry.Scope? scope, Sentry.SentryHint? hint) { } + [System.Obsolete("Use CaptureFeedback instead.")] public void CaptureUserFeedback(Sentry.UserFeedback sentryUserFeedback) { } public void ConfigureScope(System.Action configureScope) { } public System.Threading.Tasks.Task ConfigureScopeAsync(System.Func configureScope) { } @@ -1796,8 +1824,10 @@ namespace Sentry.Protocol.Envelopes public static System.Threading.Tasks.Task DeserializeAsync(System.IO.Stream stream, System.Threading.CancellationToken cancellationToken = default) { } public static Sentry.Protocol.Envelopes.Envelope FromCheckIn(Sentry.SentryCheckIn checkIn) { } public static Sentry.Protocol.Envelopes.Envelope FromEvent(Sentry.SentryEvent @event, Sentry.Extensibility.IDiagnosticLogger? logger = null, System.Collections.Generic.IReadOnlyCollection? attachments = null, Sentry.SessionUpdate? sessionUpdate = null) { } + public static Sentry.Protocol.Envelopes.Envelope FromFeedback(Sentry.SentryEvent @event, Sentry.Extensibility.IDiagnosticLogger? logger = null, System.Collections.Generic.IReadOnlyCollection? attachments = null, Sentry.SessionUpdate? sessionUpdate = null) { } public static Sentry.Protocol.Envelopes.Envelope FromSession(Sentry.SessionUpdate sessionUpdate) { } public static Sentry.Protocol.Envelopes.Envelope FromTransaction(Sentry.SentryTransaction transaction) { } + [System.Obsolete("Use FromFeedback instead.")] public static Sentry.Protocol.Envelopes.Envelope FromUserFeedback(Sentry.UserFeedback sentryUserFeedback) { } } public sealed class EnvelopeItem : Sentry.Protocol.Envelopes.ISerializable, System.IDisposable @@ -1815,8 +1845,10 @@ namespace Sentry.Protocol.Envelopes public static Sentry.Protocol.Envelopes.EnvelopeItem FromAttachment(Sentry.SentryAttachment attachment) { } public static Sentry.Protocol.Envelopes.EnvelopeItem FromCheckIn(Sentry.SentryCheckIn checkIn) { } public static Sentry.Protocol.Envelopes.EnvelopeItem FromEvent(Sentry.SentryEvent @event) { } + public static Sentry.Protocol.Envelopes.EnvelopeItem FromFeedback(Sentry.SentryEvent @event) { } public static Sentry.Protocol.Envelopes.EnvelopeItem FromSession(Sentry.SessionUpdate sessionUpdate) { } public static Sentry.Protocol.Envelopes.EnvelopeItem FromTransaction(Sentry.SentryTransaction transaction) { } + [System.Obsolete("Use FromFeedback instead.")] public static Sentry.Protocol.Envelopes.EnvelopeItem FromUserFeedback(Sentry.UserFeedback sentryUserFeedback) { } } public interface ISerializable diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt index 2730a34f6d..0c06282d34 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt @@ -225,9 +225,11 @@ namespace Sentry Sentry.SentryId CaptureCheckIn(string monitorSlug, Sentry.CheckInStatus status, Sentry.SentryId? sentryId = default, System.TimeSpan? duration = default, Sentry.Scope? scope = null, System.Action? configureMonitorOptions = null); bool CaptureEnvelope(Sentry.Protocol.Envelopes.Envelope envelope); Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, Sentry.Scope? scope = null, Sentry.SentryHint? hint = null); + void CaptureFeedback(Sentry.SentryFeedback feedback, Sentry.Scope? scope = null, Sentry.SentryHint? hint = null); void CaptureSession(Sentry.SessionUpdate sessionUpdate); void CaptureTransaction(Sentry.SentryTransaction transaction); void CaptureTransaction(Sentry.SentryTransaction transaction, Sentry.Scope? scope, Sentry.SentryHint? hint); + [System.Obsolete("Use CaptureFeedback instead.")] void CaptureUserFeedback(Sentry.UserFeedback userFeedback); System.Threading.Tasks.Task FlushAsync(System.TimeSpan timeout); } @@ -449,9 +451,11 @@ namespace Sentry public Sentry.SentryId CaptureCheckIn(string monitorSlug, Sentry.CheckInStatus status, Sentry.SentryId? sentryId = default, System.TimeSpan? duration = default, Sentry.Scope? scope = null, System.Action? configureMonitorOptions = null) { } public bool CaptureEnvelope(Sentry.Protocol.Envelopes.Envelope envelope) { } public Sentry.SentryId CaptureEvent(Sentry.SentryEvent? @event, Sentry.Scope? scope = null, Sentry.SentryHint? hint = null) { } + public void CaptureFeedback(Sentry.SentryFeedback feedback, Sentry.Scope? scope = null, Sentry.SentryHint? hint = null) { } public void CaptureSession(Sentry.SessionUpdate sessionUpdate) { } public void CaptureTransaction(Sentry.SentryTransaction transaction) { } public void CaptureTransaction(Sentry.SentryTransaction transaction, Sentry.Scope? scope, Sentry.SentryHint? hint) { } + [System.Obsolete("Use CaptureFeedback instead.")] public void CaptureUserFeedback(Sentry.UserFeedback userFeedback) { } public void Dispose() { } public System.Threading.Tasks.Task FlushAsync(System.TimeSpan timeout) { } @@ -459,7 +463,9 @@ namespace Sentry public static class SentryClientExtensions { public static Sentry.SentryId CaptureException(this Sentry.ISentryClient client, System.Exception ex) { } + public static void CaptureFeedback(this Sentry.ISentryClient client, string message, string? contactEmail = null, string? name = null, string? replayId = null, string? url = null, Sentry.SentryId? associatedEventId = default, Sentry.Scope? scope = null, Sentry.SentryHint? hint = null) { } public static Sentry.SentryId CaptureMessage(this Sentry.ISentryClient client, string message, Sentry.SentryLevel level = 1) { } + [System.Obsolete("Use CaptureFeedback instead.")] public static void CaptureUserFeedback(this Sentry.ISentryClient client, Sentry.SentryId eventId, string email, string comments, string? name = null) { } public static void Flush(this Sentry.ISentryClient client) { } public static void Flush(this Sentry.ISentryClient client, System.TimeSpan timeout) { } @@ -479,6 +485,7 @@ namespace Sentry public Sentry.Protocol.Browser Browser { get; } public int Count { get; } public Sentry.Protocol.Device Device { get; } + public Sentry.SentryFeedback? Feedback { get; set; } public Sentry.Protocol.Gpu Gpu { get; } public bool IsReadOnly { get; } public object this[string key] { get; set; } @@ -537,6 +544,18 @@ namespace Sentry public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } public static Sentry.SentryEvent FromJson(System.Text.Json.JsonElement json) { } } + public sealed class SentryFeedback : Sentry.ISentryJsonSerializable + { + public SentryFeedback(string message, string? contactEmail = null, string? name = null, string? replayId = null, string? url = null, Sentry.SentryId? associatedEventId = default) { } + public Sentry.SentryId? AssociatedEventId { get; set; } + public string? ContactEmail { get; set; } + public string Message { get; set; } + public string? Name { get; set; } + public string? ReplayId { get; set; } + public string? Url { get; set; } + public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } + public static Sentry.SentryFeedback FromJson(System.Text.Json.JsonElement json) { } + } public class SentryGraphQLHttpMessageHandler : Sentry.SentryMessageHandler { public SentryGraphQLHttpMessageHandler(System.Net.Http.HttpMessageHandler? innerHandler = null, Sentry.IHub? hub = null) { } @@ -784,12 +803,16 @@ namespace Sentry public static Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, Sentry.SentryHint? hint, System.Action configureScope) { } public static Sentry.SentryId CaptureException(System.Exception exception) { } public static Sentry.SentryId CaptureException(System.Exception exception, System.Action configureScope) { } + public static void CaptureFeedback(Sentry.SentryFeedback feedback, Sentry.Scope? scope = null, Sentry.SentryHint? hint = null) { } + public static void CaptureFeedback(string message, string? contactEmail = null, string? name = null, string? replayId = null, string? url = null, Sentry.SentryId? associatedEventId = default, Sentry.Scope? scope = null, Sentry.SentryHint? hint = null) { } public static Sentry.SentryId CaptureMessage(string message, Sentry.SentryLevel level = 1) { } public static Sentry.SentryId CaptureMessage(string message, System.Action configureScope, Sentry.SentryLevel level = 1) { } public static void CaptureSession(Sentry.SessionUpdate sessionUpdate) { } public static void CaptureTransaction(Sentry.SentryTransaction transaction) { } public static void CaptureTransaction(Sentry.SentryTransaction transaction, Sentry.Scope? scope, Sentry.SentryHint? hint) { } + [System.Obsolete("Use CaptureFeedback instead.")] public static void CaptureUserFeedback(Sentry.UserFeedback userFeedback) { } + [System.Obsolete("Use CaptureFeedback instead.")] public static void CaptureUserFeedback(Sentry.SentryId eventId, string email, string comments, string? name = null) { } [System.Obsolete("WARNING: This method deliberately causes a crash, and should not be used in a rea" + "l application.")] @@ -1201,6 +1224,7 @@ namespace Sentry public Sentry.ISpan StartChild(string operation) { } public void UnsetTag(string key) { } } + [System.Obsolete("Use SentryFeedback instead.")] public sealed class UserFeedback : Sentry.ISentryJsonSerializable { public UserFeedback(Sentry.SentryId eventId, string? name, string? email, string? comments) { } @@ -1289,9 +1313,11 @@ namespace Sentry.Extensibility public Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, System.Action configureScope) { } public Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, Sentry.Scope? scope = null, Sentry.SentryHint? hint = null) { } public Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, Sentry.SentryHint? hint, System.Action configureScope) { } + public void CaptureFeedback(Sentry.SentryFeedback feedback, Sentry.Scope? scope = null, Sentry.SentryHint? hint = null) { } public void CaptureSession(Sentry.SessionUpdate sessionUpdate) { } public void CaptureTransaction(Sentry.SentryTransaction transaction) { } public void CaptureTransaction(Sentry.SentryTransaction transaction, Sentry.Scope? scope, Sentry.SentryHint? hint) { } + [System.Obsolete("Use CaptureFeedback instead.")] public void CaptureUserFeedback(Sentry.UserFeedback userFeedback) { } public void ConfigureScope(System.Action configureScope) { } public System.Threading.Tasks.Task ConfigureScopeAsync(System.Func configureScope) { } @@ -1333,9 +1359,11 @@ namespace Sentry.Extensibility public Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, Sentry.Scope? scope, Sentry.SentryHint? hint = null) { } public Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, Sentry.SentryHint? hint, System.Action configureScope) { } public Sentry.SentryId CaptureException(System.Exception exception) { } + public void CaptureFeedback(Sentry.SentryFeedback feedback, Sentry.Scope? scope = null, Sentry.SentryHint? hint = null) { } public void CaptureSession(Sentry.SessionUpdate sessionUpdate) { } public void CaptureTransaction(Sentry.SentryTransaction transaction) { } public void CaptureTransaction(Sentry.SentryTransaction transaction, Sentry.Scope? scope, Sentry.SentryHint? hint) { } + [System.Obsolete("Use CaptureFeedback instead.")] public void CaptureUserFeedback(Sentry.UserFeedback sentryUserFeedback) { } public void ConfigureScope(System.Action configureScope) { } public System.Threading.Tasks.Task ConfigureScopeAsync(System.Func configureScope) { } @@ -1778,8 +1806,10 @@ namespace Sentry.Protocol.Envelopes public static System.Threading.Tasks.Task DeserializeAsync(System.IO.Stream stream, System.Threading.CancellationToken cancellationToken = default) { } public static Sentry.Protocol.Envelopes.Envelope FromCheckIn(Sentry.SentryCheckIn checkIn) { } public static Sentry.Protocol.Envelopes.Envelope FromEvent(Sentry.SentryEvent @event, Sentry.Extensibility.IDiagnosticLogger? logger = null, System.Collections.Generic.IReadOnlyCollection? attachments = null, Sentry.SessionUpdate? sessionUpdate = null) { } + public static Sentry.Protocol.Envelopes.Envelope FromFeedback(Sentry.SentryEvent @event, Sentry.Extensibility.IDiagnosticLogger? logger = null, System.Collections.Generic.IReadOnlyCollection? attachments = null, Sentry.SessionUpdate? sessionUpdate = null) { } public static Sentry.Protocol.Envelopes.Envelope FromSession(Sentry.SessionUpdate sessionUpdate) { } public static Sentry.Protocol.Envelopes.Envelope FromTransaction(Sentry.SentryTransaction transaction) { } + [System.Obsolete("Use FromFeedback instead.")] public static Sentry.Protocol.Envelopes.Envelope FromUserFeedback(Sentry.UserFeedback sentryUserFeedback) { } } public sealed class EnvelopeItem : Sentry.Protocol.Envelopes.ISerializable, System.IDisposable @@ -1797,8 +1827,10 @@ namespace Sentry.Protocol.Envelopes public static Sentry.Protocol.Envelopes.EnvelopeItem FromAttachment(Sentry.SentryAttachment attachment) { } public static Sentry.Protocol.Envelopes.EnvelopeItem FromCheckIn(Sentry.SentryCheckIn checkIn) { } public static Sentry.Protocol.Envelopes.EnvelopeItem FromEvent(Sentry.SentryEvent @event) { } + public static Sentry.Protocol.Envelopes.EnvelopeItem FromFeedback(Sentry.SentryEvent @event) { } public static Sentry.Protocol.Envelopes.EnvelopeItem FromSession(Sentry.SessionUpdate sessionUpdate) { } public static Sentry.Protocol.Envelopes.EnvelopeItem FromTransaction(Sentry.SentryTransaction transaction) { } + [System.Obsolete("Use FromFeedback instead.")] public static Sentry.Protocol.Envelopes.EnvelopeItem FromUserFeedback(Sentry.UserFeedback sentryUserFeedback) { } } public interface ISerializable diff --git a/test/Sentry.Tests/HubTests.cs b/test/Sentry.Tests/HubTests.cs index b00cc89e7f..d22326b119 100644 --- a/test/Sentry.Tests/HubTests.cs +++ b/test/Sentry.Tests/HubTests.cs @@ -1575,11 +1575,33 @@ public void CaptureEvent_HubEnabled(bool enabled) _fixture.Client.Received(enabled ? 1 : 0).CaptureEvent(Arg.Any(), Arg.Any(), Arg.Any()); } + [Theory] + [InlineData(true)] + [InlineData(false)] + public void CaptureFeedback_HubEnabled(bool enabled) + { + // Arrange + var hub = _fixture.GetSut(); + if (!enabled) + { + hub.Dispose(); + } + + var feedback = new SentryFeedback("Test feedback"); + + // Act + hub.CaptureFeedback(feedback); + + // Assert + _fixture.Client.Received(enabled ? 1 : 0).CaptureFeedback(Arg.Any(), Arg.Any(), Arg.Any()); + } + [Theory] [InlineData(true)] [InlineData(false)] public void CaptureUserFeedback_HubEnabled(bool enabled) { +#pragma warning disable CS0618 // Type or member is obsolete // Arrange var hub = _fixture.GetSut(); if (!enabled) @@ -1594,6 +1616,7 @@ public void CaptureUserFeedback_HubEnabled(bool enabled) // Assert _fixture.Client.Received(enabled ? 1 : 0).CaptureUserFeedback(Arg.Any()); +#pragma warning restore CS0618 // Type or member is obsolete } [Theory] diff --git a/test/Sentry.Tests/Internals/CollectionExtensionsTests.verify.cs b/test/Sentry.Tests/Internals/CollectionExtensionsTests.verify.cs index 1a8f5528e6..35d2dcc202 100644 --- a/test/Sentry.Tests/Internals/CollectionExtensionsTests.verify.cs +++ b/test/Sentry.Tests/Internals/CollectionExtensionsTests.verify.cs @@ -13,4 +13,50 @@ public Task GetOrCreate_invalid_type() private class Value { } + + [Fact] + public void TryGetValue_KeyDoesNotExist_ReturnsNull() + { + // Arrange + var dictionary = new ConcurrentDictionary(); + + // Act + var result = dictionary.TryGetValue("nonexistentKey"); + + // Assert + result.Should().BeNull(); + } + + [Fact] + public void TryGetValue_KeyExistsAndTypeMatches_ReturnsValue() + { + // Arrange + var dictionary = new ConcurrentDictionary + { + ["existingKey"] = "testValue" + }; + + // Act + var result = dictionary.TryGetValue("existingKey"); + + // Assert + result.Should().Be("testValue"); + } + + [Fact] + public void TryGetValue_KeyExistsButTypeDoesNotMatch_Throws() + { + // Arrange + var dictionary = new ConcurrentDictionary + { + ["existingKey"] = 123 + }; + + // Act + Action act = () => dictionary.TryGetValue("existingKey"); + + // Assert + act.Should().Throw() + .WithMessage("Expected a type of System.String to exist for the key 'existingKey'. Instead found a System.Int32. The likely cause of this is that the value for 'existingKey' has been incorrectly set to an instance of a different type."); + } } diff --git a/test/Sentry.Tests/Protocol/Envelopes/EnvelopeTests.cs b/test/Sentry.Tests/Protocol/Envelopes/EnvelopeTests.cs index 93f28b7379..d1715c54ff 100644 --- a/test/Sentry.Tests/Protocol/Envelopes/EnvelopeTests.cs +++ b/test/Sentry.Tests/Protocol/Envelopes/EnvelopeTests.cs @@ -798,6 +798,7 @@ public async Task Roundtrip_WithEvent_WithSession_Success() [Fact] public async Task Roundtrip_WithUserFeedback_Success() { +#pragma warning disable CS0618 // Type or member is obsolete // Arrange var feedback = new UserFeedback( SentryId.Create(), @@ -817,6 +818,95 @@ public async Task Roundtrip_WithUserFeedback_Success() // Assert envelopeRoundtrip.Should().BeEquivalentTo(envelope); +#pragma warning restore CS0618 // Type or member is obsolete + } + + [Fact] + public async Task Roundtrip_WithFeedback_Success() + { + // Arrange + var feedback = new SentryFeedback( + "Everything is great!", + "foo@bar.com", + "Someone Nice", + "fake-replay-id", + "https://www.example.com", + SentryId.Create() + ); + var evt = new SentryEvent + { + Level = SentryLevel.Info, + Contexts = + { + Feedback = feedback + } + }; + + using var envelope = Envelope.FromFeedback(evt); + + using var stream = new MemoryStream(); + + // Act + await envelope.SerializeAsync(stream, _testOutputLogger); + stream.Seek(0, SeekOrigin.Begin); + + using var envelopeRoundtrip = await Envelope.DeserializeAsync(stream); + + // Assert + envelopeRoundtrip.Should().BeEquivalentTo(envelope); + } + + [Fact] + public void FromFeedback_NoFeedbackContext_Throws() + { + // Arrange + var evt = new SentryEvent { Level = SentryLevel.Info }; + + // Act + Action act = () => Envelope.FromFeedback(evt); + + // Assert + act.Should().Throw() + .WithMessage("Unable to create envelope - the event does not contain any feedback."); + } + + [Fact] + public void FromFeedback_MultipleAttachments_LogsWarning() + { + // Arrange + var feedback = new SentryFeedback( + "Everything is great!", + "foo@bar.com", + "Someone Nice", + "fake-replay-id", + "https://www.example.com", + SentryId.Create() + ); + var evt = new SentryEvent + { + Level = SentryLevel.Info, + Contexts = + { + Feedback = feedback + } + }; + var logger = Substitute.For(); + logger.IsEnabled(Arg.Any()).Returns(true); + + List attachments = [ + AttachmentHelper.FakeAttachment("file1.txt"), AttachmentHelper.FakeAttachment("file2.txt") + ]; + + // Act + using var envelope = Envelope.FromFeedback(evt, logger, attachments); + + // Assert + logger.Received(1).Log( + SentryLevel.Warning, + Arg.Is(m => m.Contains("Feedback can only contain one attachment")), + null, + Arg.Any()); + envelope.Items.Should().ContainSingle(item => item.TryGetType() == EnvelopeItem.TypeValueAttachment); } [Fact] diff --git a/test/Sentry.Tests/Protocol/UserFeedbackTests.cs b/test/Sentry.Tests/Protocol/UserFeedbackTests.cs index 2e12f56b0a..5358773067 100644 --- a/test/Sentry.Tests/Protocol/UserFeedbackTests.cs +++ b/test/Sentry.Tests/Protocol/UserFeedbackTests.cs @@ -12,6 +12,7 @@ public UserFeedbackTests(ITestOutputHelper output) [Fact] public void Serialization_SentryUserFeedbacks_Success() { +#pragma warning disable CS0618 // Type or member is obsolete // Arrange var eventId = new SentryId(Guid.Parse("acbe351c61494e7b807fd7e82a435ffc")); var userFeedback = new UserFeedback(eventId, "myName", "myEmail@service.com", "my comment"); @@ -30,5 +31,6 @@ public void Serialization_SentryUserFeedbacks_Success() } """, actual); +#pragma warning restore CS0618 // Type or member is obsolete } } diff --git a/test/Sentry.Tests/SentryClientExtensionsTests.cs b/test/Sentry.Tests/SentryClientExtensionsTests.cs index 9210c7c62f..cb73d048ba 100644 --- a/test/Sentry.Tests/SentryClientExtensionsTests.cs +++ b/test/Sentry.Tests/SentryClientExtensionsTests.cs @@ -81,18 +81,22 @@ public void CaptureMessage_NullMessage_DoesNotCapturesEventWithMessage() [Fact] public void CaptureUserFeedback_EnabledClient_CapturesUserFeedback() { +#pragma warning disable CS0618 // Type or member is obsolete _ = _sut.IsEnabled.Returns(true); _sut.CaptureUserFeedback(Guid.Parse("1ec19311a7c048818de80b18dcc43eaa"), "email@email.com", "comments"); _sut.Received(1).CaptureUserFeedback(Arg.Any()); +#pragma warning restore CS0618 // Type or member is obsolete } [Fact] public void CaptureUserFeedback_DisabledClient_DoesNotCaptureUserFeedback() { +#pragma warning disable CS0618 // Type or member is obsolete _ = _sut.IsEnabled.Returns(false); _sut.CaptureUserFeedback(Guid.Parse("1ec19311a7c048818de80b18dcc43eea"), "email@email.com", "comments"); _sut.DidNotReceive().CaptureUserFeedback(Arg.Any()); +#pragma warning restore CS0618 // Type or member is obsolete } [Fact] diff --git a/test/Sentry.Tests/SentryClientTests.cs b/test/Sentry.Tests/SentryClientTests.cs index dccaf26882..6b85c74de9 100644 --- a/test/Sentry.Tests/SentryClientTests.cs +++ b/test/Sentry.Tests/SentryClientTests.cs @@ -799,6 +799,7 @@ public void CaptureEvent_DisposedClient_DoesNotThrow() [Fact] public void CaptureUserFeedback_EventIdEmpty_IgnoreUserFeedback() { +#pragma warning disable CS0618 // Type or member is obsolete //Arrange var sut = _fixture.GetSut(); @@ -808,11 +809,13 @@ public void CaptureUserFeedback_EventIdEmpty_IgnoreUserFeedback() //Assert _ = sut.Worker.DidNotReceive().EnqueueEnvelope(Arg.Any()); +#pragma warning restore CS0618 // Type or member is obsolete } [Fact] public void CaptureUserFeedback_ValidUserFeedback_FeedbackRegistered() { +#pragma warning disable CS0618 // Type or member is obsolete //Arrange var sut = _fixture.GetSut(); @@ -822,11 +825,13 @@ public void CaptureUserFeedback_ValidUserFeedback_FeedbackRegistered() //Assert _ = sut.Worker.Received(1).EnqueueEnvelope(Arg.Any()); +#pragma warning restore CS0618 // Type or member is obsolete } [Fact] public void CaptureUserFeedback_EventIdEmpty_FeedbackIgnored() { +#pragma warning disable CS0618 // Type or member is obsolete //Arrange var sut = _fixture.GetSut(); @@ -835,7 +840,9 @@ public void CaptureUserFeedback_EventIdEmpty_FeedbackIgnored() //Assert _ = sut.Worker.DidNotReceive().EnqueueEnvelope(Arg.Any()); +#pragma warning restore CS0618 // Type or member is obsolete } + [Fact] public void Dispose_should_only_flush() { @@ -849,12 +856,101 @@ public void Dispose_should_only_flush() client.CaptureEvent(new SentryEvent { Message = "Test" }); } + [Fact] + public void CaptureFeedback_DisposedClient_DoesNotThrow() + { + // Arrange + var feedback = new SentryFeedback("Everything is great!"); + + var sut = _fixture.GetSut(); + sut.Dispose(); + + // Act / Assert + sut.CaptureFeedback(feedback); + } + + [Fact] + public void CaptureFeedback_NoMessage_FeedbackIgnored() + { + //Arrange + var sut = _fixture.GetSut(); + var feedback = new SentryFeedback(string.Empty); + + //Act + sut.CaptureFeedback(feedback); + + //Assert + _ = sut.Worker.DidNotReceive().EnqueueEnvelope(Arg.Any()); + } + + [Fact] + public void CaptureFeedback_ValidUserFeedback_FeedbackRegistered() + { + //Arrange + var sut = _fixture.GetSut(); + var feedback = new SentryFeedback("Everything is great!"); + + //Act + sut.CaptureFeedback(feedback); + + //Assert + _ = sut.Worker.Received(1).EnqueueEnvelope(Arg.Any()); + } + + [Fact] + public void CaptureFeedback_WithScope_ScopeCopiedToEvent() + { + //Arrange + const string expectedBreadcrumb = "test"; + var scope = new Scope(_fixture.SentryOptions); + scope.AddBreadcrumb(expectedBreadcrumb); + scope.Level = SentryLevel.Warning; + var feedback = new SentryFeedback("Everything is great!"); + var sut = _fixture.GetSut(); + + Envelope envelope = null; + sut.Worker.When(w => w.EnqueueEnvelope(Arg.Any())) + .Do(callback => envelope = callback.Arg()); + + //Act + sut.CaptureFeedback(feedback, scope); + + //Assert + _ = sut.Worker.Received(1).EnqueueEnvelope(Arg.Any()); + envelope.Should().NotBeNull(); + envelope.Items.Should().Contain(item => item.TryGetType() == EnvelopeItem.TypeValueFeedback); + var item = envelope.Items.First(x => x.TryGetType() == EnvelopeItem.TypeValueFeedback); + var @event = (SentryEvent)((JsonSerializable)item.Payload).Source; + @event.Level.Should().Be(scope.Level); + Assert.Equal(scope.Breadcrumbs, @event.Breadcrumbs); + } + + [Fact] + public void CaptureFeedback_WithHint_HasHintAttachment() + { + //Arrange + var sut = _fixture.GetSut(); + var feedback = new SentryFeedback("Everything is great!"); + var hint = new SentryHint(); + hint.Attachments.Add(AttachmentHelper.FakeAttachment("foo.txt")); + + //Act + sut.CaptureFeedback(feedback, null, hint); + + //Assert + _ = sut.Worker.Received(1).EnqueueEnvelope(Arg.Any()); + sut.Worker.Received(1).EnqueueEnvelope(Arg.Is(envelope => + envelope.Items.Count(item => item.TryGetType() == "attachment") == 1)); + } + [Fact] public void CaptureUserFeedback_DisposedClient_DoesNotThrow() { +#pragma warning disable CS0618 // Type or member is obsolete var sut = _fixture.GetSut(); sut.Dispose(); sut.CaptureUserFeedback(new UserFeedback(SentryId.Empty, "name", "email", "comment")); +#pragma warning restore CS0618 // Type or member is obsolete } [Fact] From d4d14619f390a850a9844cbc7c810ce1fba4edfb Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Thu, 6 Mar 2025 06:31:52 +1300 Subject: [PATCH 150/363] Fix changelog (#4023) --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 34e7779d51..6d4af42a83 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Features + +- User Feedback can now be captured without errors/exceptions. Note that these APIs replace the older UserFeedback APIs, which have now been marked as obsolete (and will be removed in a future major version bump) ([#3981](https://github.com/getsentry/sentry-dotnet/pull/3981)) + ### Fixes - Using SentryOptions.Native.SuppressExcBadAccess and SentryOptions.Native.SuppressSignalAborts, users can now block duplicate errors from native due to dotnet NullReferenceExceptions - Defaults to false ([#3998](https://github.com/getsentry/sentry-dotnet/pull/3998)) @@ -18,7 +22,6 @@ ### Features -- User Feedback can now be captured without errors/exceptions. Note that these APIs replace the older UserFeedback APIs, which have now been marked as obsolete (and will be removed in a future major version bump) ([#3981](https://github.com/getsentry/sentry-dotnet/pull/3981)) - Users can now register their own MAUI controls for breadcrumb creation ([#3997](https://github.com/getsentry/sentry-dotnet/pull/3997)) - Serilog scope properties are now sent with Sentry events ([#3976](https://github.com/getsentry/sentry-dotnet/pull/3976)) - The sample seed used for sampling decisions is now propagated, for use in downstream custom trace samplers ([#3951](https://github.com/getsentry/sentry-dotnet/pull/3951)) From 7b198c96b767bec403c96bf9b5e2c90b2b6151fd Mon Sep 17 00:00:00 2001 From: getsentry-bot Date: Wed, 5 Mar 2025 20:17:00 +0000 Subject: [PATCH 151/363] release: 5.3.0 --- CHANGELOG.md | 2 +- Directory.Build.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d4af42a83..6897f38c15 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## Unreleased +## 5.3.0 ### Features diff --git a/Directory.Build.props b/Directory.Build.props index 70c288fb59..80232624dc 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,7 +1,7 @@ - 5.2.0 + 5.3.0 13 true true From 1d2df6d567c8f13ae254fcf89b1edc2863f088c0 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos <6349682+vaind@users.noreply.github.com> Date: Sun, 9 Mar 2025 05:51:15 +0100 Subject: [PATCH 152/363] fix: profiling - unknown stacktraces on net8 (#3967) * fix: unknown stacktraces on net8 * chore: update changelog * slacken test criteria * chore: fixup changelog * add pr to changelog released in 5.2.0 --------- Co-authored-by: Bruno Garcia --- CHANGELOG.md | 8 +++- src/Sentry.Profiling/SampleProfilerSession.cs | 18 ++++---- .../SamplingTransactionProfilerTests.cs | 43 ++++++++++++++++++- 3 files changed, 59 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6897f38c15..17b3dd6135 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +### Fixes + +- Unknown stack frames in profiles on .NET 8+ ([#3967](https://github.com/getsentry/sentry-dotnet/pull/3967)) + ## 5.3.0 ### Features @@ -30,6 +36,7 @@ ### Fixes - Fix mismapped breadcrumb levels coming in from native to dotnet SDK ([#3993](https://github.com/getsentry/sentry-dotnet/pull/3993)) +- Deduplicate profiling stack frames ([#3969](https://github.com/getsentry/sentry-dotnet/pull/3969)) ### Dependencies @@ -50,7 +57,6 @@ - Fixed envelopes with oversized attachments getting stuck in __processing ([#3938](https://github.com/getsentry/sentry-dotnet/pull/3938)) - OperatingSystem will now return macOS as OS name instead of 'Darwin' as well as the proper version. ([#2710](https://github.com/getsentry/sentry-dotnet/pull/3956)) - Ignore null value on CocoaScopeObserver.SetTag ([#3948](https://github.com/getsentry/sentry-dotnet/pull/3948)) -- Deduplicate profiling stack frames ([#3969](https://github.com/getsentry/sentry-dotnet/pull/3969)) ## 5.1.0 diff --git a/src/Sentry.Profiling/SampleProfilerSession.cs b/src/Sentry.Profiling/SampleProfilerSession.cs index e54cf62020..9b5e2756b1 100644 --- a/src/Sentry.Profiling/SampleProfilerSession.cs +++ b/src/Sentry.Profiling/SampleProfilerSession.cs @@ -12,7 +12,6 @@ namespace Sentry.Profiling; internal class SampleProfilerSession : IDisposable { private readonly EventPipeSession _session; - private readonly TraceLogEventSource _eventSource; private readonly SampleProfilerTraceEventParser _sampleEventParser; private readonly IDiagnosticLogger? _logger; private readonly SentryStopwatch _stopwatch; @@ -23,8 +22,8 @@ private SampleProfilerSession(SentryStopwatch stopwatch, EventPipeSession sessio { _session = session; _logger = logger; - _eventSource = eventSource; - _sampleEventParser = new SampleProfilerTraceEventParser(_eventSource); + EventSource = eventSource; + _sampleEventParser = new SampleProfilerTraceEventParser(EventSource); _stopwatch = stopwatch; _processing = processing; } @@ -38,7 +37,7 @@ private SampleProfilerSession(SentryStopwatch stopwatch, EventPipeSession sessio // Default = GC | Type | GCHeapSurvivalAndMovement | Binder | Loader | Jit | NGen | SupressNGen // | StopEnumeration | Security | AppDomainResourceManagement | Exception | Threading | Contention | Stack | JittedMethodILToNativeMap // | ThreadTransfer | GCHeapAndTypeNames | Codesymbols | Compilation, - new EventPipeProvider(ClrTraceEventParser.ProviderName, EventLevel.Informational, (long) ClrTraceEventParser.Keywords.Default), + new EventPipeProvider(ClrTraceEventParser.ProviderName, EventLevel.Verbose, (long) ClrTraceEventParser.Keywords.Default), new EventPipeProvider(SampleProfilerTraceEventParser.ProviderName, EventLevel.Informational), // new EventPipeProvider(TplEtwProviderTraceEventParser.ProviderName, EventLevel.Informational, (long) TplEtwProviderTraceEventParser.Keywords.Default) }; @@ -48,11 +47,14 @@ private SampleProfilerSession(SentryStopwatch stopwatch, EventPipeSession sessio // need a large buffer if we're connecting righ away. Leaving it too large increases app memory usage. internal static int CircularBufferMB = 16; + // Exposed for tests + internal TraceLogEventSource EventSource { get; } + public SampleProfilerTraceEventParser SampleEventParser => _sampleEventParser; public TimeSpan Elapsed => _stopwatch.Elapsed; - public TraceLog TraceLog => _eventSource.TraceLog; + public TraceLog TraceLog => EventSource.TraceLog; // default is false, set 1 for true. private static int _throwOnNextStartupForTests = 0; @@ -110,7 +112,7 @@ public async Task WaitForFirstEventAsync(CancellationToken cancellationToken = d { var tcs = new TaskCompletionSource(); var cb = (TraceEvent _) => { tcs.TrySetResult(); }; - _eventSource.AllEvents += cb; + EventSource.AllEvents += cb; try { // Wait for the first event to be processed. @@ -118,7 +120,7 @@ public async Task WaitForFirstEventAsync(CancellationToken cancellationToken = d } finally { - _eventSource.AllEvents -= cb; + EventSource.AllEvents -= cb; } } @@ -132,7 +134,7 @@ public void Stop() _session.Stop(); _processing.Wait(); _session.Dispose(); - _eventSource.Dispose(); + EventSource.Dispose(); } catch (Exception ex) { diff --git a/test/Sentry.Profiling.Tests/SamplingTransactionProfilerTests.cs b/test/Sentry.Profiling.Tests/SamplingTransactionProfilerTests.cs index 12bdcc5995..af5914011d 100644 --- a/test/Sentry.Profiling.Tests/SamplingTransactionProfilerTests.cs +++ b/test/Sentry.Profiling.Tests/SamplingTransactionProfilerTests.cs @@ -1,4 +1,5 @@ -using System.IO.Abstractions.TestingHelpers; +using Microsoft.Diagnostics.Tracing; +using Microsoft.Diagnostics.Tracing.Parsers.Clr; using Sentry.Internal.Http; namespace Sentry.Profiling.Tests; @@ -180,6 +181,46 @@ public async Task Profiler_AfterTimeout_Stops() } } + + /// + /// Guards regression of https://github.com/microsoft/perfview/issues/2155 + /// + [SkippableFact] + public async Task EventPipeSession_ReceivesExpectedCLREvents() + { + SampleProfilerSession? session = null; + SkipIfFailsInCI(() => session = SampleProfilerSession.StartNew(_testOutputLogger)); + using (session) + { + var eventsReceived = new HashSet(); + session!.EventSource.Clr.MethodLoadVerbose += (_) => eventsReceived.Add("Method/LoadVerbose"); + session!.EventSource.Clr.MethodILToNativeMap += (_) => eventsReceived.Add("Method/ILToNativeMap"); + + await session.WaitForFirstEventAsync(CancellationToken.None); + var tries = 0; + while (eventsReceived.Count < 2 && tries++ < 10) + { + if (tries > 1) + { + await Task.Delay(100); + } + var limitMs = 50; + var sut = new SamplingTransactionProfiler(_testSentryOptions, session, limitMs, CancellationToken.None); + MethodToBeLoaded(100); + RunForMs(limitMs); + sut.Finish(); + } + + Assert.Contains("Method/LoadVerbose", eventsReceived); + Assert.Contains("Method/ILToNativeMap", eventsReceived); + } + } + + private static long MethodToBeLoaded(int n) + { + return -n; + } + [SkippableTheory] [InlineData(true)] [InlineData(false)] From f0a778bbf3f6662cde29f3f1e215837cfe48b9bf Mon Sep 17 00:00:00 2001 From: Ivan Dlugos <6349682+vaind@users.noreply.github.com> Date: Mon, 10 Mar 2025 08:41:07 +0100 Subject: [PATCH 153/363] perf: subscribe only to necessary CLR events (#3970) * perf: subscribe only to necessary CLR events * chore: changelog * Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ src/Sentry.Profiling/SampleProfilerSession.cs | 14 +++++++------- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 17b3dd6135..0103cf05e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Enhancements + +- Profiling: improve performance by subscribing only to necessary CLR events ([#3970](https://github.com/getsentry/sentry-dotnet/pull/3970)) + ### Fixes - Unknown stack frames in profiles on .NET 8+ ([#3967](https://github.com/getsentry/sentry-dotnet/pull/3967)) diff --git a/src/Sentry.Profiling/SampleProfilerSession.cs b/src/Sentry.Profiling/SampleProfilerSession.cs index 9b5e2756b1..6cd9a9242a 100644 --- a/src/Sentry.Profiling/SampleProfilerSession.cs +++ b/src/Sentry.Profiling/SampleProfilerSession.cs @@ -31,13 +31,13 @@ private SampleProfilerSession(SentryStopwatch stopwatch, EventPipeSession sessio // Exposed only for benchmarks. internal static EventPipeProvider[] Providers = new[] { - // Note: all events we need issued by "DotNETRuntime" provider are at "EventLevel.Informational" - // see https://learn.microsoft.com/en-us/dotnet/fundamentals/diagnostics/runtime-events - // TODO replace Keywords.Default with a subset. Currently it is: - // Default = GC | Type | GCHeapSurvivalAndMovement | Binder | Loader | Jit | NGen | SupressNGen - // | StopEnumeration | Security | AppDomainResourceManagement | Exception | Threading | Contention | Stack | JittedMethodILToNativeMap - // | ThreadTransfer | GCHeapAndTypeNames | Codesymbols | Compilation, - new EventPipeProvider(ClrTraceEventParser.ProviderName, EventLevel.Verbose, (long) ClrTraceEventParser.Keywords.Default), + new EventPipeProvider(ClrTraceEventParser.ProviderName, EventLevel.Verbose, (long) ( + ClrTraceEventParser.Keywords.Jit + | ClrTraceEventParser.Keywords.NGen + | ClrTraceEventParser.Keywords.Loader + | ClrTraceEventParser.Keywords.Binder + | ClrTraceEventParser.Keywords.JittedMethodILToNativeMap + )), new EventPipeProvider(SampleProfilerTraceEventParser.ProviderName, EventLevel.Informational), // new EventPipeProvider(TplEtwProviderTraceEventParser.ProviderName, EventLevel.Informational, (long) TplEtwProviderTraceEventParser.Keywords.Default) }; From ab208ce33a4888fdce954cf3f2b1c4e2633b55bb Mon Sep 17 00:00:00 2001 From: getsentry-bot Date: Wed, 12 Mar 2025 16:49:03 +0000 Subject: [PATCH 154/363] release: 5.4.0 --- CHANGELOG.md | 2 +- Directory.Build.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0103cf05e9..e596729413 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## Unreleased +## 5.4.0 ### Enhancements diff --git a/Directory.Build.props b/Directory.Build.props index 80232624dc..1660302243 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,7 +1,7 @@ - 5.3.0 + 5.4.0 13 true true From a542a8184f17290a1c28e24d9faac14e8b3b6b44 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 14 Mar 2025 11:42:14 +1300 Subject: [PATCH 155/363] build(deps): bump github/codeql-action from 3.28.9 to 3.28.11 (#4031) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.28.9 to 3.28.11. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/9e8d0789d4a0fa9ceb6b1738f7e269594bdd67f0...6bb031afdd8eb862ea3fc1848194185e076637e5) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index b8110bb8a9..ec82ce1bbf 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -35,7 +35,7 @@ jobs: uses: ./.github/actions/environment - name: Initialize CodeQL - uses: github/codeql-action/init@9e8d0789d4a0fa9ceb6b1738f7e269594bdd67f0 # pin@v2 + uses: github/codeql-action/init@6bb031afdd8eb862ea3fc1848194185e076637e5 # pin@v2 with: languages: csharp @@ -49,6 +49,6 @@ jobs: run: dotnet build Sentry-CI-CodeQL.slnf --no-restore --nologo - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@9e8d0789d4a0fa9ceb6b1738f7e269594bdd67f0 # pin@v2 + uses: github/codeql-action/analyze@6bb031afdd8eb862ea3fc1848194185e076637e5 # pin@v2 with: category: '/language:csharp' From 993d434f1ccfe372fa3a6fb7168f26c419b0a795 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 17 Mar 2025 10:31:00 +1300 Subject: [PATCH 156/363] chore: update scripts/update-cli.ps1 to 2.42.3 (#4036) Co-authored-by: GitHub --- CHANGELOG.md | 8 ++++++++ Directory.Build.props | 2 +- src/Sentry/Sentry.csproj | 14 +++++++------- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e596729413..fb7ff6ca80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## Unreleased + +### Dependencies + +- Bump CLI from v2.42.2 to v2.42.3 ([#4036](https://github.com/getsentry/sentry-dotnet/pull/4036)) + - [changelog](https://github.com/getsentry/sentry-cli/blob/master/CHANGELOG.md#2423) + - [diff](https://github.com/getsentry/sentry-cli/compare/2.42.2...2.42.3) + ## 5.4.0 ### Enhancements diff --git a/Directory.Build.props b/Directory.Build.props index 1660302243..80110e78f7 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -83,7 +83,7 @@ - 2.42.2 + 2.42.3 $(MSBuildThisFileDirectory)tools\sentry-cli\$(SentryCLIVersion)\ diff --git a/src/Sentry/Sentry.csproj b/src/Sentry/Sentry.csproj index 2562974f85..ad6388defe 100644 --- a/src/Sentry/Sentry.csproj +++ b/src/Sentry/Sentry.csproj @@ -120,13 +120,13 @@ <_OSArchitecture>$([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture) - - - - - - - + + + + + + + From 35a26f872956e38e5ba238bd2213388c49c9ceeb Mon Sep 17 00:00:00 2001 From: Bruno Garcia Date: Sun, 16 Mar 2025 17:32:33 -0400 Subject: [PATCH 157/363] adjust required versions on contributing (#4038) --- CONTRIBUTING.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ac3763b74a..e08736a579 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,7 +6,7 @@ For big feature it's advised to raise an issue to discuss it first. # Guidelines * Please avoid mixing changes needed for a feature with other changes such as refactors, automated IDE changes like adding BOM characters, empty lines, etc. -* Feel free to start with a draft PR, while you work on it. You can ask pointed questoins by reviewing your own code this way, while signaling to the reviewer that the PR isn't ready for review just yet. +* Feel free to start with a draft PR, while you work on it. You can ask pointed questions by reviewing your own code this way, while signaling to the reviewer that the PR isn't ready for review just yet. * Mark the PR ready for review once you've completed the change, including: * The description should link to relevant context such as tickets, discussions or previous PRs. Consider screenshots of the events in Sentry or other relevant visual things. * Add tests that excercise your change. The repo has lots of examples of Unit and integration tests. Including device tests that run on Android and iOS. @@ -25,10 +25,10 @@ For big feature it's advised to raise an issue to discuss it first. ## Minimal Dependencies * The latest versions of the following .NET SDKs: + - [.NET 9.0](https://dotnet.microsoft.com/download/dotnet/9.0) - [.NET 8.0](https://dotnet.microsoft.com/download/dotnet/8.0) - - [.NET 6.0](https://dotnet.microsoft.com/download/dotnet/6.0) - *Technically, you only need the full SDK install for the latest version (8.0). If you like, you can install the smaller ASP.NET Core Runtime packages for .NET 6.0. However, installing the full SDKs will also give you the runtimes.* + *Technically, you only need the full SDK install for the latest version (9.0). If you like, you can install the smaller ASP.NET Core Runtime packages for .NET 8.0. However, installing the full SDKs will also give you the runtimes.* * [`pwsh`](https://github.com/PowerShell/PowerShell#get-powershell) Core version 6 or later on PATH. From 90ee8b1f293050aa4b7bb42734df618170897d84 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Mar 2025 11:16:57 +1300 Subject: [PATCH 158/363] build(deps): bump actions/create-github-app-token from 1.11.5 to 1.11.6 (#4020) Bumps [actions/create-github-app-token](https://github.com/actions/create-github-app-token) from 1.11.5 to 1.11.6. - [Release notes](https://github.com/actions/create-github-app-token/releases) - [Commits](https://github.com/actions/create-github-app-token/compare/0d564482f06ca65fa9e77e2510873638c82206f2...21cfef2b496dd8ef5b904c159339626a10ad380e) --- updated-dependencies: - dependency-name: actions/create-github-app-token dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bc20ec278c..d278b1511f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -20,7 +20,7 @@ jobs: steps: - name: Get auth token id: token - uses: actions/create-github-app-token@0d564482f06ca65fa9e77e2510873638c82206f2 # v1.11.5 + uses: actions/create-github-app-token@21cfef2b496dd8ef5b904c159339626a10ad380e # v1.11.6 with: app-id: ${{ vars.SENTRY_RELEASE_BOT_CLIENT_ID }} private-key: ${{ secrets.SENTRY_RELEASE_BOT_PRIVATE_KEY }} From a59729fa13fe0977b3137bc2c80ba0d34a3665bf Mon Sep 17 00:00:00 2001 From: DismissedLight <1686188646@qq.com> Date: Mon, 17 Mar 2025 11:44:50 +0800 Subject: [PATCH 159/363] Add Exception HResult to mechanism data (#4029) --------- Co-authored-by: James Crosswell --- CHANGELOG.md | 4 ++++ src/Sentry/Internal/MainExceptionProcessor.cs | 3 +++ ...rTests.RecordsEfAsync.DotNet8_0.verified.txt | 10 +++++++++- ...rTests.RecordsEfAsync.DotNet9_0.verified.txt | 10 +++++++++- ...enerTests.RecordsEfAsync.Net4_8.verified.txt | 10 +++++++++- ...qlListenerTests.RecordsSqlAsync.verified.txt | 10 +++++++++- .../IntegrationTests.Simple.verified.txt | 10 +++++++++- ...tegrationTests.Simple.DotNet8_0.verified.txt | 5 ++++- ...tegrationTests.Simple.DotNet9_0.verified.txt | 5 ++++- ...IntegrationTests.Simple.Mono4_0.verified.txt | 5 ++++- .../IntegrationTests.Simple.Net4_8.verified.txt | 5 ++++- ...tegrationTests.Simple.DotNet8_0.verified.txt | 3 ++- ...tegrationTests.Simple.DotNet9_0.verified.txt | 3 ++- ...CreateSentryException_Aggregate.verified.txt | 13 ++++++++++--- .../Internals/MainExceptionProcessorTests.cs | 17 ++++++++++++++--- 15 files changed, 96 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb7ff6ca80..e5925af8ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Features + +- Exception.HResult is now included in the mechanism data for all exceptions ([#4029](https://github.com/getsentry/sentry-dotnet/pull/4029)) + ### Dependencies - Bump CLI from v2.42.2 to v2.42.3 ([#4036](https://github.com/getsentry/sentry-dotnet/pull/4036)) diff --git a/src/Sentry/Internal/MainExceptionProcessor.cs b/src/Sentry/Internal/MainExceptionProcessor.cs index 57fca6597a..b80ab1ea94 100644 --- a/src/Sentry/Internal/MainExceptionProcessor.cs +++ b/src/Sentry/Internal/MainExceptionProcessor.cs @@ -199,6 +199,9 @@ private static Mechanism GetMechanism(Exception exception, int id, int? parentId exception.Data.Remove(Mechanism.DescriptionKey); } + // Add HResult to mechanism data before adding exception data, so that it can be overridden. + mechanism.Data["HResult"] = $"0x{exception.HResult:X8}"; + // Copy remaining exception data to mechanism data. foreach (var key in exception.Data.Keys.OfType()) { diff --git a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.DotNet8_0.verified.txt b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.DotNet8_0.verified.txt index 58b52da963..05803effbe 100644 --- a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.DotNet8_0.verified.txt +++ b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.DotNet8_0.verified.txt @@ -10,7 +10,15 @@ SentryExceptions: [ { Type: System.Exception, - Value: my exception + Value: my exception, + Mechanism: { + Type: generic, + Synthetic: false, + IsExceptionGroup: false, + Data: { + HResult: 0x80131500 + } + } } ], Level: error, diff --git a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.DotNet9_0.verified.txt b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.DotNet9_0.verified.txt index 58b52da963..05803effbe 100644 --- a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.DotNet9_0.verified.txt +++ b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.DotNet9_0.verified.txt @@ -10,7 +10,15 @@ SentryExceptions: [ { Type: System.Exception, - Value: my exception + Value: my exception, + Mechanism: { + Type: generic, + Synthetic: false, + IsExceptionGroup: false, + Data: { + HResult: 0x80131500 + } + } } ], Level: error, diff --git a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.Net4_8.verified.txt b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.Net4_8.verified.txt index 57227d296e..75d6b3e321 100644 --- a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.Net4_8.verified.txt +++ b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.Net4_8.verified.txt @@ -10,7 +10,15 @@ SentryExceptions: [ { Type: System.Exception, - Value: my exception + Value: my exception, + Mechanism: { + Type: generic, + Synthetic: false, + IsExceptionGroup: false, + Data: { + HResult: 0x80131500 + } + } } ], Level: error, diff --git a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsSqlAsync.verified.txt b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsSqlAsync.verified.txt index 0172e27d4e..e317514b27 100644 --- a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsSqlAsync.verified.txt +++ b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsSqlAsync.verified.txt @@ -10,7 +10,15 @@ SentryExceptions: [ { Type: System.Exception, - Value: my exception + Value: my exception, + Mechanism: { + Type: generic, + Synthetic: false, + IsExceptionGroup: false, + Data: { + HResult: 0x80131500 + } + } } ], Level: error, diff --git a/test/Sentry.EntityFramework.Tests/IntegrationTests.Simple.verified.txt b/test/Sentry.EntityFramework.Tests/IntegrationTests.Simple.verified.txt index e24261c17c..826fb58e95 100644 --- a/test/Sentry.EntityFramework.Tests/IntegrationTests.Simple.verified.txt +++ b/test/Sentry.EntityFramework.Tests/IntegrationTests.Simple.verified.txt @@ -10,7 +10,15 @@ SentryExceptions: [ { Type: System.Exception, - Value: my exception + Value: my exception, + Mechanism: { + Type: generic, + Synthetic: false, + IsExceptionGroup: false, + Data: { + HResult: 0x80131500 + } + } } ], Level: error, diff --git a/test/Sentry.NLog.Tests/IntegrationTests.Simple.DotNet8_0.verified.txt b/test/Sentry.NLog.Tests/IntegrationTests.Simple.DotNet8_0.verified.txt index 0546a4fe28..c1b263a0fb 100644 --- a/test/Sentry.NLog.Tests/IntegrationTests.Simple.DotNet8_0.verified.txt +++ b/test/Sentry.NLog.Tests/IntegrationTests.Simple.DotNet8_0.verified.txt @@ -59,7 +59,10 @@ Type: generic, Handled: true, Synthetic: false, - IsExceptionGroup: false + IsExceptionGroup: false, + Data: { + HResult: 0x80131500 + } } } ], diff --git a/test/Sentry.NLog.Tests/IntegrationTests.Simple.DotNet9_0.verified.txt b/test/Sentry.NLog.Tests/IntegrationTests.Simple.DotNet9_0.verified.txt index 0546a4fe28..c1b263a0fb 100644 --- a/test/Sentry.NLog.Tests/IntegrationTests.Simple.DotNet9_0.verified.txt +++ b/test/Sentry.NLog.Tests/IntegrationTests.Simple.DotNet9_0.verified.txt @@ -59,7 +59,10 @@ Type: generic, Handled: true, Synthetic: false, - IsExceptionGroup: false + IsExceptionGroup: false, + Data: { + HResult: 0x80131500 + } } } ], diff --git a/test/Sentry.NLog.Tests/IntegrationTests.Simple.Mono4_0.verified.txt b/test/Sentry.NLog.Tests/IntegrationTests.Simple.Mono4_0.verified.txt index 983ec3e5c1..099ffe6958 100644 --- a/test/Sentry.NLog.Tests/IntegrationTests.Simple.Mono4_0.verified.txt +++ b/test/Sentry.NLog.Tests/IntegrationTests.Simple.Mono4_0.verified.txt @@ -59,7 +59,10 @@ Type: generic, Handled: true, Synthetic: false, - IsExceptionGroup: false + IsExceptionGroup: false, + Data: { + HResult: 0x80131500 + } } } ], diff --git a/test/Sentry.NLog.Tests/IntegrationTests.Simple.Net4_8.verified.txt b/test/Sentry.NLog.Tests/IntegrationTests.Simple.Net4_8.verified.txt index 0546a4fe28..c1b263a0fb 100644 --- a/test/Sentry.NLog.Tests/IntegrationTests.Simple.Net4_8.verified.txt +++ b/test/Sentry.NLog.Tests/IntegrationTests.Simple.Net4_8.verified.txt @@ -59,7 +59,10 @@ Type: generic, Handled: true, Synthetic: false, - IsExceptionGroup: false + IsExceptionGroup: false, + Data: { + HResult: 0x80131500 + } } } ], diff --git a/test/Sentry.Serilog.Tests/IntegrationTests.Simple.DotNet8_0.verified.txt b/test/Sentry.Serilog.Tests/IntegrationTests.Simple.DotNet8_0.verified.txt index 1f894bd0ef..e426868eab 100644 --- a/test/Sentry.Serilog.Tests/IntegrationTests.Simple.DotNet8_0.verified.txt +++ b/test/Sentry.Serilog.Tests/IntegrationTests.Simple.DotNet8_0.verified.txt @@ -224,7 +224,8 @@ Synthetic: false, IsExceptionGroup: false, Data: { - details: Do work always throws. + details: Do work always throws., + HResult: 0x80131500 } } } diff --git a/test/Sentry.Serilog.Tests/IntegrationTests.Simple.DotNet9_0.verified.txt b/test/Sentry.Serilog.Tests/IntegrationTests.Simple.DotNet9_0.verified.txt index 1f894bd0ef..e426868eab 100644 --- a/test/Sentry.Serilog.Tests/IntegrationTests.Simple.DotNet9_0.verified.txt +++ b/test/Sentry.Serilog.Tests/IntegrationTests.Simple.DotNet9_0.verified.txt @@ -224,7 +224,8 @@ Synthetic: false, IsExceptionGroup: false, Data: { - details: Do work always throws. + details: Do work always throws., + HResult: 0x80131500 } } } diff --git a/test/Sentry.Tests/Internals/MainExceptionProcessorTests.CreateSentryException_Aggregate.verified.txt b/test/Sentry.Tests/Internals/MainExceptionProcessorTests.CreateSentryException_Aggregate.verified.txt index d48b44f301..520f33f3fa 100644 --- a/test/Sentry.Tests/Internals/MainExceptionProcessorTests.CreateSentryException_Aggregate.verified.txt +++ b/test/Sentry.Tests/Internals/MainExceptionProcessorTests.CreateSentryException_Aggregate.verified.txt @@ -8,7 +8,10 @@ Synthetic: false, IsExceptionGroup: false, ExceptionId: 2, - ParentId: 0 + ParentId: 0, + Data: { + HResult: 0x80131500 + } } }, { @@ -20,7 +23,10 @@ Synthetic: false, IsExceptionGroup: false, ExceptionId: 1, - ParentId: 0 + ParentId: 0, + Data: { + HResult: 0x80131500 + } } }, { @@ -33,7 +39,8 @@ IsExceptionGroup: true, ExceptionId: 0, Data: { - foo: bar + foo: bar, + HResult: 0x80131500 } } } diff --git a/test/Sentry.Tests/Internals/MainExceptionProcessorTests.cs b/test/Sentry.Tests/Internals/MainExceptionProcessorTests.cs index 8d9c5ab1ca..f5dedc0b4e 100644 --- a/test/Sentry.Tests/Internals/MainExceptionProcessorTests.cs +++ b/test/Sentry.Tests/Internals/MainExceptionProcessorTests.cs @@ -12,7 +12,7 @@ private class Fixture private readonly Fixture _fixture = new(); [Fact] - public void Process_ExceptionsWithoutData_MechanismDataIsEmpty() + public void Process_ExceptionsWithoutData_MechanismDataIsMinimal() { var sut = _fixture.GetSut(); var evt = new SentryEvent(); @@ -21,7 +21,12 @@ public void Process_ExceptionsWithoutData_MechanismDataIsEmpty() sut.Process(ex, evt); var sentryException = evt.SentryExceptions!.Single(); - Assert.Empty(sentryException.Mechanism!.Data); + + // All managed exceptions has an HResult, at a bare minimum (which we store in the Mechanism.Data) + sentryException.Mechanism!.Data.Should().BeEquivalentTo(new Dictionary() + { + ["HResult"] = "0x80131500" // The default value of HRESULT for managed exceptions (COR_E_EXCEPTION) + }); } [Fact] @@ -109,7 +114,13 @@ public void CreateSentryException_DataHasObjectAsKey_ItemIgnored() var actual = sut.CreateSentryExceptions(ex).Single(); - Assert.Null(actual.Mechanism); + // The custom data won't be added, but the mechanism data will still contain an HResult. + // We add the HResult for all exceptions + actual.Mechanism!.Data.Should().BeEquivalentTo(new Dictionary() + { + ["HResult"] = "0x80131500" // The default value of HRESULT for managed exceptions (COR_E_EXCEPTION) + }); + } [Fact] From ac4f837508d996608df6b77102b1afd7ab1e7221 Mon Sep 17 00:00:00 2001 From: Alberto Aldegheri Date: Mon, 17 Mar 2025 05:42:10 +0100 Subject: [PATCH 160/363] Fix missing MAUI `Shell` navigation breadcrumbs on iOS (#4006) --------- Co-authored-by: James Crosswell --- CHANGELOG.md | 1 + src/Sentry.Maui/Internal/MauiEventsBinder.cs | 11 ++++++- .../IntegrationTests.verify.cs | 2 +- .../MauiEventsBinderTests.Application.cs | 30 +++++++++++++++++++ .../Mocks/MockApplication.cs | 18 +++++++++++ .../Platforms/MacCatalyst/AppDelegate.cs | 4 +-- .../Platforms/MacCatalyst/Program.cs | 14 ++++----- .../Platforms/Tizen/Main.cs | 14 ++++----- .../Platforms/Windows/App.xaml.cs | 20 ++++++------- 9 files changed, 86 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e5925af8ca..9c8af8560f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ ### Fixes - Unknown stack frames in profiles on .NET 8+ ([#3967](https://github.com/getsentry/sentry-dotnet/pull/3967)) +- Missing MAUI `Shell` navigation breadcrumbs on iOS ([#4006](https://github.com/getsentry/sentry-dotnet/pull/4006)) ## 5.3.0 diff --git a/src/Sentry.Maui/Internal/MauiEventsBinder.cs b/src/Sentry.Maui/Internal/MauiEventsBinder.cs index 23605ccb2e..40356438b5 100644 --- a/src/Sentry.Maui/Internal/MauiEventsBinder.cs +++ b/src/Sentry.Maui/Internal/MauiEventsBinder.cs @@ -34,7 +34,16 @@ public void HandleApplicationEvents(Application application, bool bind = true) { if (bind) { - // Attach element events to all descendents as they are added to the application. + // Attach element events to all existing descendants (skip the application itself) + foreach (var descendant in application.GetVisualTreeDescendants().Skip(1)) + { + if (descendant is VisualElement element) + { + OnApplicationOnDescendantAdded(application, new ElementEventArgs(element)); + } + } + + // Attach element events to all descendants as they are added to the application. application.DescendantAdded += OnApplicationOnDescendantAdded; application.DescendantRemoved += OnApplicationOnDescendantRemoved; diff --git a/test/Sentry.EntityFramework.Tests/IntegrationTests.verify.cs b/test/Sentry.EntityFramework.Tests/IntegrationTests.verify.cs index 8a3c944f85..e691784afa 100644 --- a/test/Sentry.EntityFramework.Tests/IntegrationTests.verify.cs +++ b/test/Sentry.EntityFramework.Tests/IntegrationTests.verify.cs @@ -5,7 +5,7 @@ namespace Sentry.EntityFramework.Tests; public class IntegrationTests { // needs to be a variable to stop EF from inlining it as a constant - static string shouldNotAppearInPayload = "SHOULD NOT APPEAR IN PAYLOAD"; + private static string shouldNotAppearInPayload = "SHOULD NOT APPEAR IN PAYLOAD"; [SkippableFact] public async Task Simple() diff --git a/test/Sentry.Maui.Tests/MauiEventsBinderTests.Application.cs b/test/Sentry.Maui.Tests/MauiEventsBinderTests.Application.cs index 81e2cb4f0d..3d659faaaf 100644 --- a/test/Sentry.Maui.Tests/MauiEventsBinderTests.Application.cs +++ b/test/Sentry.Maui.Tests/MauiEventsBinderTests.Application.cs @@ -145,6 +145,36 @@ public void Application_UnbindModalEvents_DoesNotAddBreadcrumb(string eventName, Assert.Single(_fixture.Scope.Breadcrumbs); } + [Fact] + public void Application_HandleApplicationEvents_TracksExistingDescendants() + { + // Arrange + var application = MockApplication.Create(); + var element = new MockVisualElement("element"); + var mainPage = new ContentPage + { + Content = new VerticalStackLayout + { + element + } + }; + + application.AddWindow(mainPage); + + _fixture.Binder.HandleApplicationEvents(application); + + // Act + element.RaiseEvent(nameof(VisualElement.Focused), new FocusEventArgs(element, true)); + + // Assert + var crumb = Assert.Single(_fixture.Scope.Breadcrumbs); + Assert.Equal($"{nameof(MockVisualElement)}.{nameof(VisualElement.Focused)}", crumb.Message); + Assert.Equal(BreadcrumbLevel.Info, crumb.Level); + Assert.Equal(MauiEventsBinder.SystemType, crumb.Type); + Assert.Equal(MauiEventsBinder.RenderingCategory, crumb.Category); + crumb.Data.Should().Contain($"{nameof(MockVisualElement)}.Name", "element"); + } + public static IEnumerable ApplicationModalEventsData { get diff --git a/test/Sentry.Maui.Tests/Mocks/MockApplication.cs b/test/Sentry.Maui.Tests/Mocks/MockApplication.cs index f1bf30ae20..e67d2405d7 100644 --- a/test/Sentry.Maui.Tests/Mocks/MockApplication.cs +++ b/test/Sentry.Maui.Tests/Mocks/MockApplication.cs @@ -5,6 +5,7 @@ namespace Sentry.Maui.Tests.Mocks; public class MockApplication : Application { private static readonly object LockObj = new(); + private Page _mainPage; static MockApplication() { @@ -18,6 +19,23 @@ private MockApplication() { } + public void AddWindow(Page mainPage) + { + _mainPage = mainPage; + ((IApplication)this).CreateWindow(null); + _mainPage = null; + } + + protected override Window CreateWindow(IActivationState activationState) + { + if (_mainPage != null) + { + return new Window(_mainPage); + } + + return base.CreateWindow(activationState); + } + public static MockApplication Create() { // The base constructor will try to set the mock as the current application, which we don't want in tests. diff --git a/test/Sentry.MauiTrimTest/Platforms/MacCatalyst/AppDelegate.cs b/test/Sentry.MauiTrimTest/Platforms/MacCatalyst/AppDelegate.cs index e24cb88f7b..b1699db753 100644 --- a/test/Sentry.MauiTrimTest/Platforms/MacCatalyst/AppDelegate.cs +++ b/test/Sentry.MauiTrimTest/Platforms/MacCatalyst/AppDelegate.cs @@ -1,9 +1,9 @@ -using Foundation; +using Foundation; namespace Sentry.MauiTrimTest; [Register("AppDelegate")] public class AppDelegate : MauiUIApplicationDelegate { - protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); + protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); } diff --git a/test/Sentry.MauiTrimTest/Platforms/MacCatalyst/Program.cs b/test/Sentry.MauiTrimTest/Platforms/MacCatalyst/Program.cs index 05a59280ab..41d708b660 100644 --- a/test/Sentry.MauiTrimTest/Platforms/MacCatalyst/Program.cs +++ b/test/Sentry.MauiTrimTest/Platforms/MacCatalyst/Program.cs @@ -5,11 +5,11 @@ namespace Sentry.MauiTrimTest; public class Program { - // This is the main entry point of the application. - static void Main(string[] args) - { - // if you want to use a different Application Delegate class from "AppDelegate" - // you can specify it here. - UIApplication.Main(args, null, typeof(AppDelegate)); - } + // This is the main entry point of the application. + private static void Main(string[] args) + { + // if you want to use a different Application Delegate class from "AppDelegate" + // you can specify it here. + UIApplication.Main(args, null, typeof(AppDelegate)); + } } diff --git a/test/Sentry.MauiTrimTest/Platforms/Tizen/Main.cs b/test/Sentry.MauiTrimTest/Platforms/Tizen/Main.cs index d49b5550af..7c820fbe05 100644 --- a/test/Sentry.MauiTrimTest/Platforms/Tizen/Main.cs +++ b/test/Sentry.MauiTrimTest/Platforms/Tizen/Main.cs @@ -4,13 +4,13 @@ namespace Sentry.MauiTrimTest; -class Program : MauiApplication +public class Program : MauiApplication { - protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); + protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); - static void Main(string[] args) - { - var app = new Program(); - app.Run(args); - } + private static void Main(string[] args) + { + var app = new Program(); + app.Run(args); + } } diff --git a/test/Sentry.MauiTrimTest/Platforms/Windows/App.xaml.cs b/test/Sentry.MauiTrimTest/Platforms/Windows/App.xaml.cs index dbacb63fe1..5f852b91c0 100644 --- a/test/Sentry.MauiTrimTest/Platforms/Windows/App.xaml.cs +++ b/test/Sentry.MauiTrimTest/Platforms/Windows/App.xaml.cs @@ -1,4 +1,4 @@ -using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml; // To learn more about WinUI, the WinUI project structure, // and more about our project templates, see: http://aka.ms/winui-project-info. @@ -10,15 +10,15 @@ namespace Sentry.MauiTrimTest.WinUI; /// public partial class App : MauiWinUIApplication { - /// - /// Initializes the singleton application object. This is the first line of authored code - /// executed, and as such is the logical equivalent of main() or WinMain(). - /// - public App() - { - this.InitializeComponent(); - } + /// + /// Initializes the singleton application object. This is the first line of authored code + /// executed, and as such is the logical equivalent of main() or WinMain(). + /// + public App() + { + this.InitializeComponent(); + } - protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); + protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); } From cdbb4bc267bc6c7f382c7db8516a2525843fb83d Mon Sep 17 00:00:00 2001 From: Bruno Garcia Date: Mon, 17 Mar 2025 03:28:46 -0400 Subject: [PATCH 161/363] target net9 on gcp (#4039) --- CHANGELOG.md | 4 ++++ .../Sentry.Google.Cloud.Functions.csproj | 8 +++++++- .../ApiApprovalTests.Run.DotNet9_0.verified.txt | 10 ++++++++++ .../Sentry.Google.Cloud.Functions.Tests.csproj | 2 +- .../Platforms/MacCatalyst/Program.cs | 2 +- 5 files changed, 23 insertions(+), 3 deletions(-) create mode 100644 test/Sentry.Google.Cloud.Functions.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c8af8560f..1d7b3b2d7d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Fixes + +- Target `net9.0` on Sentry.Google.Cloud.Functions to avoid conflict with Sentry.AspNetCore ([#4039](https://github.com/getsentry/sentry-dotnet/pull/4039)) + ### Features - Exception.HResult is now included in the mechanism data for all exceptions ([#4029](https://github.com/getsentry/sentry-dotnet/pull/4029)) diff --git a/src/Sentry.Google.Cloud.Functions/Sentry.Google.Cloud.Functions.csproj b/src/Sentry.Google.Cloud.Functions/Sentry.Google.Cloud.Functions.csproj index e704ddaf3a..6c455dc928 100644 --- a/src/Sentry.Google.Cloud.Functions/Sentry.Google.Cloud.Functions.csproj +++ b/src/Sentry.Google.Cloud.Functions/Sentry.Google.Cloud.Functions.csproj @@ -1,7 +1,7 @@ - net8.0 + net9.0;net8.0 $(PackageTags);GCP;Google Cloud Functions Official Google Cloud Functions integration for Sentry - Open-source error tracking that helps developers monitor and fix crashes in real time. @@ -14,7 +14,13 @@ + + + + + + diff --git a/test/Sentry.Google.Cloud.Functions.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt b/test/Sentry.Google.Cloud.Functions.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt new file mode 100644 index 0000000000..acc2519014 --- /dev/null +++ b/test/Sentry.Google.Cloud.Functions.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt @@ -0,0 +1,10 @@ +namespace Google.Cloud.Functions.Framework +{ + public class SentryStartup : Google.Cloud.Functions.Hosting.FunctionsStartup + { + public SentryStartup() { } + public override void Configure(Microsoft.AspNetCore.Hosting.WebHostBuilderContext context, Microsoft.AspNetCore.Builder.IApplicationBuilder app) { } + public override void ConfigureLogging(Microsoft.AspNetCore.Hosting.WebHostBuilderContext context, Microsoft.Extensions.Logging.ILoggingBuilder logging) { } + public override void ConfigureServices(Microsoft.AspNetCore.Hosting.WebHostBuilderContext context, Microsoft.Extensions.DependencyInjection.IServiceCollection services) { } + } +} \ No newline at end of file diff --git a/test/Sentry.Google.Cloud.Functions.Tests/Sentry.Google.Cloud.Functions.Tests.csproj b/test/Sentry.Google.Cloud.Functions.Tests/Sentry.Google.Cloud.Functions.Tests.csproj index 0e877187df..6eb35fea95 100644 --- a/test/Sentry.Google.Cloud.Functions.Tests/Sentry.Google.Cloud.Functions.Tests.csproj +++ b/test/Sentry.Google.Cloud.Functions.Tests/Sentry.Google.Cloud.Functions.Tests.csproj @@ -1,7 +1,7 @@ - net8.0 + net9.0;net8.0 diff --git a/test/Sentry.MauiTrimTest/Platforms/MacCatalyst/Program.cs b/test/Sentry.MauiTrimTest/Platforms/MacCatalyst/Program.cs index 41d708b660..cc72b21c23 100644 --- a/test/Sentry.MauiTrimTest/Platforms/MacCatalyst/Program.cs +++ b/test/Sentry.MauiTrimTest/Platforms/MacCatalyst/Program.cs @@ -1,4 +1,4 @@ -using ObjCRuntime; +using ObjCRuntime; using UIKit; namespace Sentry.MauiTrimTest; From 522e197634f0058f13972f410d329fc265ccb4ad Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 21 Mar 2025 09:55:42 +1300 Subject: [PATCH 162/363] chore: update scripts/update-cli.ps1 to 2.42.4 (#4049) Co-authored-by: GitHub --- CHANGELOG.md | 6 +++--- Directory.Build.props | 2 +- src/Sentry/Sentry.csproj | 14 +++++++------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d7b3b2d7d..53989c0b87 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,9 +12,9 @@ ### Dependencies -- Bump CLI from v2.42.2 to v2.42.3 ([#4036](https://github.com/getsentry/sentry-dotnet/pull/4036)) - - [changelog](https://github.com/getsentry/sentry-cli/blob/master/CHANGELOG.md#2423) - - [diff](https://github.com/getsentry/sentry-cli/compare/2.42.2...2.42.3) +- Bump CLI from v2.42.2 to v2.42.4 ([#4036](https://github.com/getsentry/sentry-dotnet/pull/4036), [#4049](https://github.com/getsentry/sentry-dotnet/pull/4049)) + - [changelog](https://github.com/getsentry/sentry-cli/blob/master/CHANGELOG.md#2424) + - [diff](https://github.com/getsentry/sentry-cli/compare/2.42.2...2.42.4) ## 5.4.0 diff --git a/Directory.Build.props b/Directory.Build.props index 80110e78f7..e8891669cd 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -83,7 +83,7 @@ - 2.42.3 + 2.42.4 $(MSBuildThisFileDirectory)tools\sentry-cli\$(SentryCLIVersion)\ diff --git a/src/Sentry/Sentry.csproj b/src/Sentry/Sentry.csproj index ad6388defe..b930fe619e 100644 --- a/src/Sentry/Sentry.csproj +++ b/src/Sentry/Sentry.csproj @@ -120,13 +120,13 @@ <_OSArchitecture>$([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture)
- - - - - - - + + + + + + + From 72ad46933c0ce9ea4e845d62482b0d2dad294a66 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Fri, 21 Mar 2025 14:00:45 +1300 Subject: [PATCH 163/363] Bump to .NET SDK 9.0.202 (#4054) --- .github/actions/environment/action.yml | 2 +- global.json | 4 +-- .../IAndroidAssemblyReader.cs | 2 +- .../IProtobufRequest.cs | 4 +-- .../IProtobufRequestPayloadExtractor.cs | 2 +- src/Sentry.AspNetCore/ISentryBuilder.cs | 2 +- src/Sentry.EntityFramework/IQueryLogger.cs | 2 +- .../ILogEntryFilter.cs | 2 +- src/Sentry.Maui/IMauiElementEventBinder.cs | 4 +-- src/Sentry.Maui/Internal/MauiEventsBinder.cs | 2 +- .../IOpenTelemetryEnricher.cs | 2 +- .../Ben.BlockingDetector/IBlockingMonitor.cs | 4 +-- .../Ben.BlockingDetector/IRecursionTracker.cs | 6 ++-- .../ITaskBlockingListenerState.cs | 6 ++-- src/Sentry/Extensibility/IBackgroundWorker.cs | 6 ++-- src/Sentry/Extensibility/IDiagnosticLogger.cs | 4 +-- src/Sentry/Extensibility/IExceptionFilter.cs | 2 +- .../Extensibility/INetworkStatusListener.cs | 4 +-- .../Extensibility/IRequestPayloadExtractor.cs | 2 +- .../ISentryEventExceptionProcessor.cs | 2 +- .../Extensibility/ISentryEventProcessor.cs | 2 +- .../ISentryEventProcessorWithHint.cs | 2 +- .../Extensibility/ISentryHttpRequest.cs | 8 +++--- .../Extensibility/ISentryStackTraceFactory.cs | 2 +- .../ISentryTransactionProcessor.cs | 2 +- .../ISentryTransactionProcessorWithHint.cs | 2 +- src/Sentry/Extensibility/ITransport.cs | 2 +- src/Sentry/Http/ISentryHttpClientFactory.cs | 2 +- src/Sentry/IAttachmentContent.cs | 2 +- src/Sentry/IEventLike.cs | 22 +++++++-------- src/Sentry/IHasData.cs | 4 +-- src/Sentry/IHasExtra.cs | 4 +-- src/Sentry/IHasTags.cs | 6 ++-- src/Sentry/IHub.cs | 24 ++++++++-------- src/Sentry/IScopeObserver.cs | 10 +++---- src/Sentry/ISentryClient.cs | 20 ++++++------- src/Sentry/ISentryFailedRequestHandler.cs | 2 +- src/Sentry/ISentryJsonSerializable.cs | 2 +- src/Sentry/ISentryScopeManager.cs | 10 +++---- src/Sentry/ISentryScopeStateProcessor.cs | 2 +- src/Sentry/ISentrySession.cs | 16 +++++------ src/Sentry/ISessionManager.cs | 16 +++++------ src/Sentry/ISpan.cs | 16 +++++------ src/Sentry/ISpanData.cs | 12 ++++---- src/Sentry/ITransactionContext.cs | 6 ++-- src/Sentry/ITransactionTracer.cs | 8 +++--- src/Sentry/Infrastructure/ISystemClock.cs | 2 +- src/Sentry/Integrations/ISdkIntegration.cs | 2 +- src/Sentry/Internal/AppDomainAdapter.cs | 6 ++-- src/Sentry/Internal/IClientReportRecorder.cs | 6 ++-- src/Sentry/Internal/ICloneable.cs | 2 +- src/Sentry/Internal/IFileSystem.cs | 28 +++++++++---------- src/Sentry/Internal/IGCImplementation.cs | 8 +++--- src/Sentry/Internal/IInternalScopeManager.cs | 6 ++-- src/Sentry/Internal/IStringOrRegexMatcher.cs | 2 +- src/Sentry/Internal/ITransactionProfiler.cs | 6 ++-- src/Sentry/Internal/IUpdatable.cs | 4 +-- .../ScopeStack/IScopeStackContainer.cs | 2 +- src/Sentry/Internal/StackFrame.cs | 2 +- src/Sentry/Internal/TcpPing.cs | 2 +- .../Protocol/Envelopes/ISerializable.cs | 4 +-- src/Sentry/Protocol/ITraceContext.cs | 16 +++++------ 62 files changed, 183 insertions(+), 183 deletions(-) diff --git a/.github/actions/environment/action.yml b/.github/actions/environment/action.yml index f67824f33f..80582afd47 100644 --- a/.github/actions/environment/action.yml +++ b/.github/actions/environment/action.yml @@ -60,7 +60,7 @@ runs: with: dotnet-version: | 8.0.x - 9.0.100 + 9.0.202 - name: Install .NET Workloads shell: bash diff --git a/global.json b/global.json index 40584df3e9..e721e283bc 100644 --- a/global.json +++ b/global.json @@ -1,7 +1,7 @@ { "sdk": { - "version": "9.0.100", - "rollForward": "latestMinor", + "version": "9.0.202", + "rollForward": "disable", "allowPrerelease": false } } diff --git a/src/Sentry.Android.AssemblyReader/IAndroidAssemblyReader.cs b/src/Sentry.Android.AssemblyReader/IAndroidAssemblyReader.cs index ef8b3173fe..f0435d4303 100644 --- a/src/Sentry.Android.AssemblyReader/IAndroidAssemblyReader.cs +++ b/src/Sentry.Android.AssemblyReader/IAndroidAssemblyReader.cs @@ -10,5 +10,5 @@ public interface IAndroidAssemblyReader : IDisposable ///
/// The name of the assembly. /// The reader, or null if the assembly could not be found. - PEReader? TryReadAssembly(string name); + public PEReader? TryReadAssembly(string name); } diff --git a/src/Sentry.AspNetCore.Grpc/IProtobufRequest.cs b/src/Sentry.AspNetCore.Grpc/IProtobufRequest.cs index fecefc8bc6..a4dc5d099a 100644 --- a/src/Sentry.AspNetCore.Grpc/IProtobufRequest.cs +++ b/src/Sentry.AspNetCore.Grpc/IProtobufRequest.cs @@ -8,10 +8,10 @@ public interface IProtobufRequest /// /// The content length. /// - long? ContentLength { get; } + public long? ContentLength { get; } /// /// The request message. /// - TRequest Request { get; } + public TRequest Request { get; } } diff --git a/src/Sentry.AspNetCore.Grpc/IProtobufRequestPayloadExtractor.cs b/src/Sentry.AspNetCore.Grpc/IProtobufRequestPayloadExtractor.cs index 6572a6246a..2ffb2cb103 100644 --- a/src/Sentry.AspNetCore.Grpc/IProtobufRequestPayloadExtractor.cs +++ b/src/Sentry.AspNetCore.Grpc/IProtobufRequestPayloadExtractor.cs @@ -12,6 +12,6 @@ public interface IProtobufRequestPayloadExtractor ///
/// The gRPC Request object. /// The extracted payload. - IMessage? ExtractPayload(IProtobufRequest request) + public IMessage? ExtractPayload(IProtobufRequest request) where TRequest : class, IMessage; } diff --git a/src/Sentry.AspNetCore/ISentryBuilder.cs b/src/Sentry.AspNetCore/ISentryBuilder.cs index a8cb95576d..4d94d09513 100644 --- a/src/Sentry.AspNetCore/ISentryBuilder.cs +++ b/src/Sentry.AspNetCore/ISentryBuilder.cs @@ -10,5 +10,5 @@ public interface ISentryBuilder /// /// Gets the where Sentry services are configured. /// - IServiceCollection Services { get; } + public IServiceCollection Services { get; } } diff --git a/src/Sentry.EntityFramework/IQueryLogger.cs b/src/Sentry.EntityFramework/IQueryLogger.cs index eaa7cc7a97..3b1a77c4bd 100644 --- a/src/Sentry.EntityFramework/IQueryLogger.cs +++ b/src/Sentry.EntityFramework/IQueryLogger.cs @@ -10,5 +10,5 @@ public interface IQueryLogger ///
/// The query text. /// The level. - void Log(string text, BreadcrumbLevel level = BreadcrumbLevel.Debug); + public void Log(string text, BreadcrumbLevel level = BreadcrumbLevel.Debug); } diff --git a/src/Sentry.Extensions.Logging/ILogEntryFilter.cs b/src/Sentry.Extensions.Logging/ILogEntryFilter.cs index 0592443725..9ab597b3a6 100644 --- a/src/Sentry.Extensions.Logging/ILogEntryFilter.cs +++ b/src/Sentry.Extensions.Logging/ILogEntryFilter.cs @@ -18,7 +18,7 @@ public interface ILogEntryFilter /// The event level. /// The EventId. /// The Exception, if any. - bool Filter( + public bool Filter( string categoryName, LogLevel logLevel, EventId eventId, diff --git a/src/Sentry.Maui/IMauiElementEventBinder.cs b/src/Sentry.Maui/IMauiElementEventBinder.cs index 2fe12e1a76..fcb9d1a1cc 100644 --- a/src/Sentry.Maui/IMauiElementEventBinder.cs +++ b/src/Sentry.Maui/IMauiElementEventBinder.cs @@ -13,13 +13,13 @@ public interface IMauiElementEventBinder /// This adds a breadcrumb to the sentry hub /// NOTE: we will override the type, timestamp, and category of the breadcrumb /// - void Bind(VisualElement element, Action addBreadcrumb); + public void Bind(VisualElement element, Action addBreadcrumb); /// /// Unbind the element because MAUI is removing the page /// /// - void UnBind(VisualElement element); + public void UnBind(VisualElement element); } /// diff --git a/src/Sentry.Maui/Internal/MauiEventsBinder.cs b/src/Sentry.Maui/Internal/MauiEventsBinder.cs index 40356438b5..08f6d91dd7 100644 --- a/src/Sentry.Maui/Internal/MauiEventsBinder.cs +++ b/src/Sentry.Maui/Internal/MauiEventsBinder.cs @@ -4,7 +4,7 @@ namespace Sentry.Maui.Internal; internal interface IMauiEventsBinder { - void HandleApplicationEvents(Application application, bool bind = true); + public void HandleApplicationEvents(Application application, bool bind = true); } internal class MauiEventsBinder : IMauiEventsBinder diff --git a/src/Sentry.OpenTelemetry/IOpenTelemetryEnricher.cs b/src/Sentry.OpenTelemetry/IOpenTelemetryEnricher.cs index 2fa72949f0..7d6062ca3e 100644 --- a/src/Sentry.OpenTelemetry/IOpenTelemetryEnricher.cs +++ b/src/Sentry.OpenTelemetry/IOpenTelemetryEnricher.cs @@ -2,5 +2,5 @@ namespace Sentry.OpenTelemetry; internal interface IOpenTelemetryEnricher { - void Enrich(ISpan span, Activity activity, IHub hub, SentryOptions? options); + public void Enrich(ISpan span, Activity activity, IHub hub, SentryOptions? options); } diff --git a/src/Sentry/Ben.BlockingDetector/IBlockingMonitor.cs b/src/Sentry/Ben.BlockingDetector/IBlockingMonitor.cs index 4169f95f13..9c3bc41cdf 100644 --- a/src/Sentry/Ben.BlockingDetector/IBlockingMonitor.cs +++ b/src/Sentry/Ben.BlockingDetector/IBlockingMonitor.cs @@ -2,6 +2,6 @@ namespace Sentry.Ben.BlockingDetector; internal interface IBlockingMonitor { - void BlockingStart(DetectionSource detectionSource); - void BlockingEnd(); + public void BlockingStart(DetectionSource detectionSource); + public void BlockingEnd(); } diff --git a/src/Sentry/Ben.BlockingDetector/IRecursionTracker.cs b/src/Sentry/Ben.BlockingDetector/IRecursionTracker.cs index fe881d41ad..930d9b666f 100644 --- a/src/Sentry/Ben.BlockingDetector/IRecursionTracker.cs +++ b/src/Sentry/Ben.BlockingDetector/IRecursionTracker.cs @@ -2,7 +2,7 @@ namespace Sentry.Ben.BlockingDetector; internal interface IRecursionTracker { - void Recurse(); - void Backtrack(); - bool IsFirstRecursion(); + public void Recurse(); + public void Backtrack(); + public bool IsFirstRecursion(); } diff --git a/src/Sentry/Ben.BlockingDetector/ITaskBlockingListenerState.cs b/src/Sentry/Ben.BlockingDetector/ITaskBlockingListenerState.cs index 263a3cdbe1..e81891b810 100644 --- a/src/Sentry/Ben.BlockingDetector/ITaskBlockingListenerState.cs +++ b/src/Sentry/Ben.BlockingDetector/ITaskBlockingListenerState.cs @@ -2,7 +2,7 @@ namespace Sentry.Ben.BlockingDetector; internal interface ITaskBlockingListenerState { - void Suppress(); - bool IsSuppressed(); - void Restore(); + public void Suppress(); + public bool IsSuppressed(); + public void Restore(); } diff --git a/src/Sentry/Extensibility/IBackgroundWorker.cs b/src/Sentry/Extensibility/IBackgroundWorker.cs index e42a0e27e2..dc25b0d604 100644 --- a/src/Sentry/Extensibility/IBackgroundWorker.cs +++ b/src/Sentry/Extensibility/IBackgroundWorker.cs @@ -12,17 +12,17 @@ public interface IBackgroundWorker /// /// The envelope to enqueue. /// True of queueing was successful. Otherwise, false. - bool EnqueueEnvelope(Envelope envelope); + public bool EnqueueEnvelope(Envelope envelope); /// /// Flushes envelopes asynchronously. /// /// How long to wait for flush to finish. /// A task to await for the flush operation. - Task FlushAsync(TimeSpan timeout); + public Task FlushAsync(TimeSpan timeout); /// /// Current count of items queued up. /// - int QueuedItems { get; } + public int QueuedItems { get; } } diff --git a/src/Sentry/Extensibility/IDiagnosticLogger.cs b/src/Sentry/Extensibility/IDiagnosticLogger.cs index 3fd73e2795..0104fc1000 100644 --- a/src/Sentry/Extensibility/IDiagnosticLogger.cs +++ b/src/Sentry/Extensibility/IDiagnosticLogger.cs @@ -8,7 +8,7 @@ public interface IDiagnosticLogger /// /// Whether the logger is enabled or not to the specified . /// - bool IsEnabled(SentryLevel level); + public bool IsEnabled(SentryLevel level); /// /// Log an internal SDK message. @@ -17,5 +17,5 @@ public interface IDiagnosticLogger /// The message. /// An optional Exception. /// Optional arguments for string template. - void Log(SentryLevel logLevel, string message, Exception? exception = null, params object?[] args); + public void Log(SentryLevel logLevel, string message, Exception? exception = null, params object?[] args); } diff --git a/src/Sentry/Extensibility/IExceptionFilter.cs b/src/Sentry/Extensibility/IExceptionFilter.cs index fe7cfcaf97..b24ff44b30 100644 --- a/src/Sentry/Extensibility/IExceptionFilter.cs +++ b/src/Sentry/Extensibility/IExceptionFilter.cs @@ -10,5 +10,5 @@ public interface IExceptionFilter /// /// The exception about to be captured. /// true if [the event should be filtered out]; otherwise, false. - bool Filter(Exception ex); + public bool Filter(Exception ex); } diff --git a/src/Sentry/Extensibility/INetworkStatusListener.cs b/src/Sentry/Extensibility/INetworkStatusListener.cs index 1c419cbe86..ed890810c7 100644 --- a/src/Sentry/Extensibility/INetworkStatusListener.cs +++ b/src/Sentry/Extensibility/INetworkStatusListener.cs @@ -14,11 +14,11 @@ public interface INetworkStatusListener /// /// Gets a value that indicates whether the network is online. /// - bool Online { get; } + public bool Online { get; } /// /// Asynchronously waits for the network to come online. /// /// A token which cancels waiting. - Task WaitForNetworkOnlineAsync(CancellationToken cancellationToken); + public Task WaitForNetworkOnlineAsync(CancellationToken cancellationToken); } diff --git a/src/Sentry/Extensibility/IRequestPayloadExtractor.cs b/src/Sentry/Extensibility/IRequestPayloadExtractor.cs index 730a14e7b7..3a761e1d28 100644 --- a/src/Sentry/Extensibility/IRequestPayloadExtractor.cs +++ b/src/Sentry/Extensibility/IRequestPayloadExtractor.cs @@ -10,5 +10,5 @@ public interface IRequestPayloadExtractor ///
/// The HTTP Request object. /// The extracted payload. - object? ExtractPayload(IHttpRequest request); + public object? ExtractPayload(IHttpRequest request); } diff --git a/src/Sentry/Extensibility/ISentryEventExceptionProcessor.cs b/src/Sentry/Extensibility/ISentryEventExceptionProcessor.cs index 129062ddd6..9a736248a0 100644 --- a/src/Sentry/Extensibility/ISentryEventExceptionProcessor.cs +++ b/src/Sentry/Extensibility/ISentryEventExceptionProcessor.cs @@ -10,5 +10,5 @@ public interface ISentryEventExceptionProcessor ///
/// The exception to process. /// The event to add data to. - void Process(Exception exception, SentryEvent sentryEvent); + public void Process(Exception exception, SentryEvent sentryEvent); } diff --git a/src/Sentry/Extensibility/ISentryEventProcessor.cs b/src/Sentry/Extensibility/ISentryEventProcessor.cs index 30e43c2382..67a089f945 100644 --- a/src/Sentry/Extensibility/ISentryEventProcessor.cs +++ b/src/Sentry/Extensibility/ISentryEventProcessor.cs @@ -15,7 +15,7 @@ public interface ISentryEventProcessor /// Returning null will stop the processing pipeline. /// Meaning the event should no longer be processed nor send. /// - SentryEvent? Process(SentryEvent @event); + public SentryEvent? Process(SentryEvent @event); } internal static class ISentryEventProcessorExtensions diff --git a/src/Sentry/Extensibility/ISentryEventProcessorWithHint.cs b/src/Sentry/Extensibility/ISentryEventProcessorWithHint.cs index 287e710b38..0d9a8bc1c5 100644 --- a/src/Sentry/Extensibility/ISentryEventProcessorWithHint.cs +++ b/src/Sentry/Extensibility/ISentryEventProcessorWithHint.cs @@ -16,6 +16,6 @@ public interface ISentryEventProcessorWithHint : ISentryEventProcessor /// Returning null will stop the processing pipeline so that the event will neither be processed by /// additional event processors or sent to Sentry. /// - SentryEvent? Process(SentryEvent @event, SentryHint hint); + public SentryEvent? Process(SentryEvent @event, SentryHint hint); } diff --git a/src/Sentry/Extensibility/ISentryHttpRequest.cs b/src/Sentry/Extensibility/ISentryHttpRequest.cs index f592c5fe77..2c7f5288e6 100644 --- a/src/Sentry/Extensibility/ISentryHttpRequest.cs +++ b/src/Sentry/Extensibility/ISentryHttpRequest.cs @@ -8,20 +8,20 @@ public interface IHttpRequest /// /// The content length. /// - long? ContentLength { get; } + public long? ContentLength { get; } /// /// The content type. /// - string? ContentType { get; } + public string? ContentType { get; } /// /// The request body. /// - Stream? Body { get; } + public Stream? Body { get; } /// /// Represents the parsed form values sent with the HttpRequest. /// - IEnumerable>>? Form { get; } + public IEnumerable>>? Form { get; } } diff --git a/src/Sentry/Extensibility/ISentryStackTraceFactory.cs b/src/Sentry/Extensibility/ISentryStackTraceFactory.cs index 1a3b41e965..db572f60a2 100644 --- a/src/Sentry/Extensibility/ISentryStackTraceFactory.cs +++ b/src/Sentry/Extensibility/ISentryStackTraceFactory.cs @@ -10,5 +10,5 @@ public interface ISentryStackTraceFactory ///
/// The exception to create the stacktrace from. /// A Sentry stack trace. - SentryStackTrace? Create(Exception? exception = null); + public SentryStackTrace? Create(Exception? exception = null); } diff --git a/src/Sentry/Extensibility/ISentryTransactionProcessor.cs b/src/Sentry/Extensibility/ISentryTransactionProcessor.cs index f8fedc524f..da0ba8f5f2 100644 --- a/src/Sentry/Extensibility/ISentryTransactionProcessor.cs +++ b/src/Sentry/Extensibility/ISentryTransactionProcessor.cs @@ -14,7 +14,7 @@ public interface ISentryTransactionProcessor /// Returning null will stop the processing pipeline. /// Meaning the transaction should no longer be processed nor send. /// - SentryTransaction? Process(SentryTransaction transaction); + public SentryTransaction? Process(SentryTransaction transaction); } internal static class ISentryTransactionProcessorExtensions diff --git a/src/Sentry/Extensibility/ISentryTransactionProcessorWithHint.cs b/src/Sentry/Extensibility/ISentryTransactionProcessorWithHint.cs index c6e62586de..a84ceb78f3 100644 --- a/src/Sentry/Extensibility/ISentryTransactionProcessorWithHint.cs +++ b/src/Sentry/Extensibility/ISentryTransactionProcessorWithHint.cs @@ -15,5 +15,5 @@ public interface ISentryTransactionProcessorWithHint : ISentryTransactionProcess /// Returning null will stop the processing pipeline. /// Meaning the transaction should no longer be processed nor send. /// - SentryTransaction? Process(SentryTransaction transaction, SentryHint hint); + public SentryTransaction? Process(SentryTransaction transaction, SentryHint hint); } diff --git a/src/Sentry/Extensibility/ITransport.cs b/src/Sentry/Extensibility/ITransport.cs index d38db9a925..e95fa45a65 100644 --- a/src/Sentry/Extensibility/ITransport.cs +++ b/src/Sentry/Extensibility/ITransport.cs @@ -12,5 +12,5 @@ public interface ITransport /// /// The envelope to send to Sentry. /// The cancellation token. - Task SendEnvelopeAsync(Envelope envelope, CancellationToken cancellationToken = default); + public Task SendEnvelopeAsync(Envelope envelope, CancellationToken cancellationToken = default); } diff --git a/src/Sentry/Http/ISentryHttpClientFactory.cs b/src/Sentry/Http/ISentryHttpClientFactory.cs index c45067ef75..9a8b763ec1 100644 --- a/src/Sentry/Http/ISentryHttpClientFactory.cs +++ b/src/Sentry/Http/ISentryHttpClientFactory.cs @@ -10,5 +10,5 @@ public interface ISentryHttpClientFactory /// /// The options. /// . - HttpClient Create(SentryOptions options); + public HttpClient Create(SentryOptions options); } diff --git a/src/Sentry/IAttachmentContent.cs b/src/Sentry/IAttachmentContent.cs index fcb0d234b0..757bc4942f 100644 --- a/src/Sentry/IAttachmentContent.cs +++ b/src/Sentry/IAttachmentContent.cs @@ -8,5 +8,5 @@ public interface IAttachmentContent /// /// Gets the stream that represents attachment content. /// - Stream GetStream(); + public Stream GetStream(); } diff --git a/src/Sentry/IEventLike.cs b/src/Sentry/IEventLike.cs index 26ccbed0ed..092b73e6f4 100644 --- a/src/Sentry/IEventLike.cs +++ b/src/Sentry/IEventLike.cs @@ -9,12 +9,12 @@ public interface IEventLike : IHasTags, IHasExtra /// A trail of events which happened prior to an issue. /// /// - IReadOnlyCollection Breadcrumbs { get; } + public IReadOnlyCollection Breadcrumbs { get; } /// /// Adds a breadcrumb. /// - void AddBreadcrumb(Breadcrumb breadcrumb); + public void AddBreadcrumb(Breadcrumb breadcrumb); /// /// The release distribution of the application. @@ -24,7 +24,7 @@ public interface IEventLike : IHasTags, IHasExtra /// /// Sentry level. /// - SentryLevel? Level { get; set; } + public SentryLevel? Level { get; set; } /// /// Gets or sets the HTTP. @@ -32,7 +32,7 @@ public interface IEventLike : IHasTags, IHasExtra /// /// The HTTP. /// - SentryRequest Request { get; set; } + public SentryRequest Request { get; set; } /// /// Gets the structured Sentry context. @@ -40,7 +40,7 @@ public interface IEventLike : IHasTags, IHasExtra /// /// The contexts. /// - SentryContexts Contexts { get; set; } + public SentryContexts Contexts { get; set; } /// /// Gets the user information. @@ -48,18 +48,18 @@ public interface IEventLike : IHasTags, IHasExtra /// /// The user. /// - SentryUser User { get; set; } + public SentryUser User { get; set; } /// /// The release version of the application. /// - string? Release { get; set; } + public string? Release { get; set; } /// /// The environment name, such as 'production' or 'staging'. /// /// Requires Sentry 8.0 or higher. - string? Environment { get; set; } + public string? Environment { get; set; } /// /// The name of the transaction in which there was an event. @@ -70,13 +70,13 @@ public interface IEventLike : IHasTags, IHasExtra /// rather than the actual request path. That is so GET /user/10 and /user/20 /// (which have route template /user/{id}) are identified as the same transaction. /// - string? TransactionName { get; set; } + public string? TransactionName { get; set; } /// /// SDK information. /// /// New in Sentry version: 8.4 - SdkVersion Sdk { get; } + public SdkVersion Sdk { get; } /// /// A list of strings used to dictate the deduplication of this event. @@ -88,7 +88,7 @@ public interface IEventLike : IHasTags, IHasExtra /// /// { "fingerprint": ["myrpc", "POST", "/foo.bar"] } /// { "fingerprint": ["{{ default }}", "http://example.com/my.url"] } - IReadOnlyList Fingerprint { get; set; } + public IReadOnlyList Fingerprint { get; set; } } /// diff --git a/src/Sentry/IHasData.cs b/src/Sentry/IHasData.cs index 980f403731..1f7257b11d 100644 --- a/src/Sentry/IHasData.cs +++ b/src/Sentry/IHasData.cs @@ -8,10 +8,10 @@ public interface IHasData /// /// An arbitrary mapping of additional metadata to store with the event. /// - IReadOnlyDictionary Data { get; } + public IReadOnlyDictionary Data { get; } /// /// Sets an extra. /// - void SetData(string key, object? value); + public void SetData(string key, object? value); } diff --git a/src/Sentry/IHasExtra.cs b/src/Sentry/IHasExtra.cs index 174b73f3c9..54bc034d53 100644 --- a/src/Sentry/IHasExtra.cs +++ b/src/Sentry/IHasExtra.cs @@ -8,12 +8,12 @@ public interface IHasExtra /// /// An arbitrary mapping of additional metadata to store with the event. /// - IReadOnlyDictionary Extra { get; } + public IReadOnlyDictionary Extra { get; } /// /// Sets an extra. /// - void SetExtra(string key, object? value); + public void SetExtra(string key, object? value); } /// diff --git a/src/Sentry/IHasTags.cs b/src/Sentry/IHasTags.cs index 3a78dc5792..e5900563ab 100644 --- a/src/Sentry/IHasTags.cs +++ b/src/Sentry/IHasTags.cs @@ -8,17 +8,17 @@ public interface IHasTags /// /// Arbitrary key-value for this event. /// - IReadOnlyDictionary Tags { get; } + public IReadOnlyDictionary Tags { get; } /// /// Sets a tag. /// - void SetTag(string key, string value); + public void SetTag(string key, string value); /// /// Removes a tag. /// - void UnsetTag(string key); + public void UnsetTag(string key); } /// diff --git a/src/Sentry/IHub.cs b/src/Sentry/IHub.cs index 5b8d923f7b..f7a609805b 100644 --- a/src/Sentry/IHub.cs +++ b/src/Sentry/IHub.cs @@ -15,12 +15,12 @@ public interface IHub : ISentryClient, ISentryScopeManager /// /// Last event id recorded in the current scope. /// - SentryId LastEventId { get; } + public SentryId LastEventId { get; } /// /// Starts a transaction. /// - ITransactionTracer StartTransaction( + public ITransactionTracer StartTransaction( ITransactionContext context, IReadOnlyDictionary customSamplingContext); @@ -30,22 +30,22 @@ ITransactionTracer StartTransaction( /// /// This method is used internally and is not meant for public use. /// - void BindException(Exception exception, ISpan span); + public void BindException(Exception exception, ISpan span); /// /// Gets the currently ongoing (not finished) span or null if none available. /// - ISpan? GetSpan(); + public ISpan? GetSpan(); /// /// Gets the Sentry trace header that allows tracing across services /// - SentryTraceHeader? GetTraceHeader(); + public SentryTraceHeader? GetTraceHeader(); /// /// Gets the Sentry baggage header that allows tracing across services /// - BaggageHeader? GetBaggage(); + public BaggageHeader? GetBaggage(); /// /// Continues a trace based on HTTP header values provided as strings. @@ -53,7 +53,7 @@ ITransactionTracer StartTransaction( /// /// If no "sentry-trace" header is provided a random trace ID and span ID is created. /// - TransactionContext ContinueTrace( + public TransactionContext ContinueTrace( string? traceHeader, string? baggageHeader, string? name = null, @@ -65,7 +65,7 @@ TransactionContext ContinueTrace( /// /// If no "sentry-trace" header is provided a random trace ID and span ID is created. /// - TransactionContext ContinueTrace( + public TransactionContext ContinueTrace( SentryTraceHeader? traceHeader, BaggageHeader? baggageHeader, string? name = null, @@ -74,12 +74,12 @@ TransactionContext ContinueTrace( /// /// Starts a new session. /// - void StartSession(); + public void StartSession(); /// /// Pauses an active session. /// - void PauseSession(); + public void PauseSession(); /// /// Resumes an active session. @@ -87,12 +87,12 @@ TransactionContext ContinueTrace( /// then the paused session is /// ended and a new one is started instead. /// - void ResumeSession(); + public void ResumeSession(); /// /// Ends the currently active session. /// - void EndSession(SessionEndStatus status = SessionEndStatus.Exited); + public void EndSession(SessionEndStatus status = SessionEndStatus.Exited); /// /// Captures an event with a configurable scope. diff --git a/src/Sentry/IScopeObserver.cs b/src/Sentry/IScopeObserver.cs index 3757122d83..591188b3c8 100644 --- a/src/Sentry/IScopeObserver.cs +++ b/src/Sentry/IScopeObserver.cs @@ -8,25 +8,25 @@ public interface IScopeObserver /// /// Adds a breadcrumb. /// - void AddBreadcrumb(Breadcrumb breadcrumb); + public void AddBreadcrumb(Breadcrumb breadcrumb); /// /// Sets an extra. /// - void SetExtra(string key, object? value); + public void SetExtra(string key, object? value); /// /// Sets a tag. /// - void SetTag(string key, string value); + public void SetTag(string key, string value); /// /// Removes a tag. /// - void UnsetTag(string key); + public void UnsetTag(string key); /// /// Sets the user information. /// - void SetUser(SentryUser? user); + public void SetUser(SentryUser? user); } diff --git a/src/Sentry/ISentryClient.cs b/src/Sentry/ISentryClient.cs index 3841539cc2..8229d57780 100644 --- a/src/Sentry/ISentryClient.cs +++ b/src/Sentry/ISentryClient.cs @@ -10,14 +10,14 @@ public interface ISentryClient /// /// Whether the client is enabled or not. /// - bool IsEnabled { get; } + public bool IsEnabled { get; } /// /// Capture an envelope and queue it. /// /// The envelope. /// true if the enveloped was queued, false otherwise. - bool CaptureEnvelope(Envelope envelope); + public bool CaptureEnvelope(Envelope envelope); /// /// Capture the event @@ -26,7 +26,7 @@ public interface ISentryClient /// An optional scope to be applied to the event. /// An optional hint providing high level context for the source of the event /// The Id of the event. - SentryId CaptureEvent(SentryEvent evt, Scope? scope = null, SentryHint? hint = null); + public SentryId CaptureEvent(SentryEvent evt, Scope? scope = null, SentryHint? hint = null); /// /// Captures feedback from the user. @@ -34,14 +34,14 @@ public interface ISentryClient /// The feedback to send to Sentry. /// An optional scope to be applied to the event. /// An optional hint providing high level context for the source of the event - void CaptureFeedback(SentryFeedback feedback, Scope? scope = null, SentryHint? hint = null); + public void CaptureFeedback(SentryFeedback feedback, Scope? scope = null, SentryHint? hint = null); /// /// Captures a user feedback. /// /// The user feedback to send to Sentry. [Obsolete("Use CaptureFeedback instead.")] - void CaptureUserFeedback(UserFeedback userFeedback); + public void CaptureUserFeedback(UserFeedback userFeedback); /// /// Captures a transaction. @@ -52,7 +52,7 @@ public interface ISentryClient /// /// The transaction. [EditorBrowsable(EditorBrowsableState.Never)] - void CaptureTransaction(SentryTransaction transaction); + public void CaptureTransaction(SentryTransaction transaction); /// /// Captures a transaction. @@ -68,7 +68,7 @@ public interface ISentryClient /// This will be available in callbacks prior to processing the transaction. /// [EditorBrowsable(EditorBrowsableState.Never)] - void CaptureTransaction(SentryTransaction transaction, Scope? scope, SentryHint? hint); + public void CaptureTransaction(SentryTransaction transaction, Scope? scope, SentryHint? hint); /// /// Captures a session update. @@ -78,7 +78,7 @@ public interface ISentryClient /// It will be called automatically by the SDK. /// /// The update to send to Sentry. - void CaptureSession(SessionUpdate sessionUpdate); + public void CaptureSession(SessionUpdate sessionUpdate); /// /// Captures a Checkin. @@ -89,7 +89,7 @@ public interface ISentryClient /// /// /// The optional monitor config used to create a Check-In programmatically. - SentryId CaptureCheckIn(string monitorSlug, + public SentryId CaptureCheckIn(string monitorSlug, CheckInStatus status, SentryId? sentryId = null, TimeSpan? duration = null, @@ -101,5 +101,5 @@ SentryId CaptureCheckIn(string monitorSlug, /// /// The amount of time allowed for flushing. /// A task to await for the flush operation. - Task FlushAsync(TimeSpan timeout); + public Task FlushAsync(TimeSpan timeout); } diff --git a/src/Sentry/ISentryFailedRequestHandler.cs b/src/Sentry/ISentryFailedRequestHandler.cs index dcde535338..bb2f947d4b 100644 --- a/src/Sentry/ISentryFailedRequestHandler.cs +++ b/src/Sentry/ISentryFailedRequestHandler.cs @@ -2,5 +2,5 @@ namespace Sentry; internal interface ISentryFailedRequestHandler { - void HandleResponse(HttpResponseMessage response); + public void HandleResponse(HttpResponseMessage response); } diff --git a/src/Sentry/ISentryJsonSerializable.cs b/src/Sentry/ISentryJsonSerializable.cs index 4775d59fed..387a5f501d 100644 --- a/src/Sentry/ISentryJsonSerializable.cs +++ b/src/Sentry/ISentryJsonSerializable.cs @@ -15,5 +15,5 @@ public interface ISentryJsonSerializable /// Note: this method is meant only for internal use and is exposed due to a language limitation. /// Avoid relying on this method in user code. /// - void WriteTo(Utf8JsonWriter writer, IDiagnosticLogger? logger); + public void WriteTo(Utf8JsonWriter writer, IDiagnosticLogger? logger); } diff --git a/src/Sentry/ISentryScopeManager.cs b/src/Sentry/ISentryScopeManager.cs index 72456843b1..3fd221c229 100644 --- a/src/Sentry/ISentryScopeManager.cs +++ b/src/Sentry/ISentryScopeManager.cs @@ -13,27 +13,27 @@ public interface ISentryScopeManager /// Configures the current scope. /// /// The configure scope. - void ConfigureScope(Action configureScope); + public void ConfigureScope(Action configureScope); /// /// Asynchronously configure the current scope. /// /// The configure scope. /// A task that completes when the callback is done or a completed task if the SDK is disabled. - Task ConfigureScopeAsync(Func configureScope); + public Task ConfigureScopeAsync(Func configureScope); /// /// Binds the client to the current scope. /// /// The client. - void BindClient(ISentryClient client); + public void BindClient(ISentryClient client); /// /// Pushes a new scope into the stack which is removed upon Dispose. /// /// A disposable which removes the scope /// from the environment when invoked. - IDisposable PushScope(); + public IDisposable PushScope(); /// /// Pushes a new scope into the stack which is removed upon Dispose. @@ -41,5 +41,5 @@ public interface ISentryScopeManager /// A state to associate with the scope. /// A disposable which removes the scope /// from the environment when invoked. - IDisposable PushScope(TState state); + public IDisposable PushScope(TState state); } diff --git a/src/Sentry/ISentryScopeStateProcessor.cs b/src/Sentry/ISentryScopeStateProcessor.cs index c6aeb56fe4..d339fe0b83 100644 --- a/src/Sentry/ISentryScopeStateProcessor.cs +++ b/src/Sentry/ISentryScopeStateProcessor.cs @@ -8,5 +8,5 @@ public interface ISentryScopeStateProcessor /// /// Applies state onto a scope. /// - void Apply(Scope scope, object state); + public void Apply(Scope scope, object state); } diff --git a/src/Sentry/ISentrySession.cs b/src/Sentry/ISentrySession.cs index ecf721d0cc..d2f8686584 100644 --- a/src/Sentry/ISentrySession.cs +++ b/src/Sentry/ISentrySession.cs @@ -8,40 +8,40 @@ public interface ISentrySession /// /// Session auto-generated ID. /// - SentryId Id { get; } + public SentryId Id { get; } /// /// Session distinct ID. /// - string? DistinctId { get; } + public string? DistinctId { get; } /// /// Session start timestamp. /// - DateTimeOffset StartTimestamp { get; } + public DateTimeOffset StartTimestamp { get; } /// /// Release. /// - string Release { get; } + public string Release { get; } /// /// Environment. /// - string? Environment { get; } + public string? Environment { get; } /// /// IP address of the user. /// - string? IpAddress { get; } + public string? IpAddress { get; } /// /// User agent. /// - string? UserAgent { get; } + public string? UserAgent { get; } /// /// Reported error count. /// - int ErrorCount { get; } + public int ErrorCount { get; } } diff --git a/src/Sentry/ISessionManager.cs b/src/Sentry/ISessionManager.cs index 7f52f43e46..b529a72b56 100644 --- a/src/Sentry/ISessionManager.cs +++ b/src/Sentry/ISessionManager.cs @@ -2,19 +2,19 @@ namespace Sentry; internal interface ISessionManager { - bool IsSessionActive { get; } + public bool IsSessionActive { get; } - SessionUpdate? TryRecoverPersistedSession(); + public SessionUpdate? TryRecoverPersistedSession(); - SessionUpdate? StartSession(); + public SessionUpdate? StartSession(); - SessionUpdate? EndSession(DateTimeOffset timestamp, SessionEndStatus status); + public SessionUpdate? EndSession(DateTimeOffset timestamp, SessionEndStatus status); - SessionUpdate? EndSession(SessionEndStatus status); + public SessionUpdate? EndSession(SessionEndStatus status); - void PauseSession(); + public void PauseSession(); - IReadOnlyList ResumeSession(); + public IReadOnlyList ResumeSession(); - SessionUpdate? ReportError(); + public SessionUpdate? ReportError(); } diff --git a/src/Sentry/ISpan.cs b/src/Sentry/ISpan.cs index e1f4ad436c..0259ac02c0 100644 --- a/src/Sentry/ISpan.cs +++ b/src/Sentry/ISpan.cs @@ -11,44 +11,44 @@ public interface ISpan : ISpanData /// Span description. /// // 'new' because it adds a setter. - new string? Description { get; set; } + public new string? Description { get; set; } /// /// Span operation. /// // 'new' because it adds a setter. - new string Operation { get; set; } + public new string Operation { get; set; } /// /// Span status. /// // 'new' because it adds a setter. - new SpanStatus? Status { get; set; } + public new SpanStatus? Status { get; set; } /// /// Starts a child span. /// - ISpan StartChild(string operation); + public ISpan StartChild(string operation); /// /// Finishes the span. /// - void Finish(); + public void Finish(); /// /// Finishes the span with the specified status. /// - void Finish(SpanStatus status); + public void Finish(SpanStatus status); /// /// Finishes the span with the specified exception and status. /// - void Finish(Exception exception, SpanStatus status); + public void Finish(Exception exception, SpanStatus status); /// /// Finishes the span with the specified exception and automatically inferred status. /// - void Finish(Exception exception); + public void Finish(Exception exception); } /// diff --git a/src/Sentry/ISpanData.cs b/src/Sentry/ISpanData.cs index 3a475102b8..55c1dd80c1 100644 --- a/src/Sentry/ISpanData.cs +++ b/src/Sentry/ISpanData.cs @@ -10,34 +10,34 @@ public interface ISpanData : ITraceContext, IHasData, IHasTags, IHasExtra /// /// Start timestamp. /// - DateTimeOffset StartTimestamp { get; } + public DateTimeOffset StartTimestamp { get; } /// /// End timestamp. /// - DateTimeOffset? EndTimestamp { get; } + public DateTimeOffset? EndTimestamp { get; } /// /// Whether the span is finished. /// - bool IsFinished { get; } + public bool IsFinished { get; } /// /// Get Sentry trace header. /// - SentryTraceHeader GetTraceHeader(); + public SentryTraceHeader GetTraceHeader(); /// /// The measurements that have been set on the transaction. /// - IReadOnlyDictionary Measurements { get; } + public IReadOnlyDictionary Measurements { get; } /// /// Sets a measurement on the transaction. /// /// The name of the measurement. /// The measurement. - void SetMeasurement(string name, Measurement measurement); + public void SetMeasurement(string name, Measurement measurement); } /// diff --git a/src/Sentry/ITransactionContext.cs b/src/Sentry/ITransactionContext.cs index d1cb0dc849..e1ffc74bb6 100644 --- a/src/Sentry/ITransactionContext.cs +++ b/src/Sentry/ITransactionContext.cs @@ -10,15 +10,15 @@ public interface ITransactionContext : ITraceContext /// /// Transaction name. /// - string Name { get; } + public string Name { get; } /// /// Whether the parent transaction of this transaction has been sampled. /// - bool? IsParentSampled { get; } + public bool? IsParentSampled { get; } /// /// The source of the transaction name. /// - TransactionNameSource NameSource { get; } + public TransactionNameSource NameSource { get; } } diff --git a/src/Sentry/ITransactionTracer.cs b/src/Sentry/ITransactionTracer.cs index 015840061c..9971321f2c 100644 --- a/src/Sentry/ITransactionTracer.cs +++ b/src/Sentry/ITransactionTracer.cs @@ -9,21 +9,21 @@ public interface ITransactionTracer : ITransactionData, ISpan /// Transaction name. /// // 'new' because it adds a setter - new string Name { get; set; } + public new string Name { get; set; } /// /// Whether the parent transaction of this transaction has been sampled. /// // 'new' because it adds a setter - new bool? IsParentSampled { get; set; } + public new bool? IsParentSampled { get; set; } /// /// Flat list of spans within this transaction. /// - IReadOnlyCollection Spans { get; } + public IReadOnlyCollection Spans { get; } /// /// Gets the last active (not finished) span in this transaction. /// - ISpan? GetLastActiveSpan(); + public ISpan? GetLastActiveSpan(); } diff --git a/src/Sentry/Infrastructure/ISystemClock.cs b/src/Sentry/Infrastructure/ISystemClock.cs index ca9b8ee7f2..d13f407ced 100644 --- a/src/Sentry/Infrastructure/ISystemClock.cs +++ b/src/Sentry/Infrastructure/ISystemClock.cs @@ -11,5 +11,5 @@ public interface ISystemClock /// /// Gets the current time in UTC. /// - DateTimeOffset GetUtcNow(); + public DateTimeOffset GetUtcNow(); } diff --git a/src/Sentry/Integrations/ISdkIntegration.cs b/src/Sentry/Integrations/ISdkIntegration.cs index 561195ffc7..898e5b39db 100644 --- a/src/Sentry/Integrations/ISdkIntegration.cs +++ b/src/Sentry/Integrations/ISdkIntegration.cs @@ -13,5 +13,5 @@ public interface ISdkIntegration /// /// The hub. /// The options. - void Register(IHub hub, SentryOptions options); + public void Register(IHub hub, SentryOptions options); } diff --git a/src/Sentry/Internal/AppDomainAdapter.cs b/src/Sentry/Internal/AppDomainAdapter.cs index e6201182c0..1849a88cac 100644 --- a/src/Sentry/Internal/AppDomainAdapter.cs +++ b/src/Sentry/Internal/AppDomainAdapter.cs @@ -2,11 +2,11 @@ namespace Sentry.Internal; internal interface IAppDomain { - event UnhandledExceptionEventHandler UnhandledException; + public event UnhandledExceptionEventHandler UnhandledException; - event EventHandler ProcessExit; + public event EventHandler ProcessExit; - event EventHandler UnobservedTaskException; + public event EventHandler UnobservedTaskException; } internal sealed class AppDomainAdapter : IAppDomain diff --git a/src/Sentry/Internal/IClientReportRecorder.cs b/src/Sentry/Internal/IClientReportRecorder.cs index 980d827b9d..de80abb795 100644 --- a/src/Sentry/Internal/IClientReportRecorder.cs +++ b/src/Sentry/Internal/IClientReportRecorder.cs @@ -8,7 +8,7 @@ internal interface IClientReportRecorder /// The reason for the event being discarded. /// The data category of the event being discardedd. /// The number of items discarded (defaults to 1) - void RecordDiscardedEvent(DiscardReason reason, DataCategory category, int quantity = 1); + public void RecordDiscardedEvent(DiscardReason reason, DataCategory category, int quantity = 1); /// /// Generates a containing counts of discarded events that have been recorded. @@ -18,7 +18,7 @@ internal interface IClientReportRecorder /// The , as long as there is something to report. /// Returns null if there were no discarded events recorded since the previous call to this method. /// - ClientReport? GenerateClientReport(); + public ClientReport? GenerateClientReport(); /// /// Loads the current instance with the events from the provided . @@ -27,5 +27,5 @@ internal interface IClientReportRecorder /// Useful when recovering from failures while sending client reports. /// /// The client report to load. - void Load(ClientReport report); + public void Load(ClientReport report); } diff --git a/src/Sentry/Internal/ICloneable.cs b/src/Sentry/Internal/ICloneable.cs index 7a6e8ed0e6..a64a7c48a8 100644 --- a/src/Sentry/Internal/ICloneable.cs +++ b/src/Sentry/Internal/ICloneable.cs @@ -2,5 +2,5 @@ namespace Sentry.Internal; internal interface ICloneable { - T Clone(); + public T Clone(); } diff --git a/src/Sentry/Internal/IFileSystem.cs b/src/Sentry/Internal/IFileSystem.cs index bcb041336f..366bf70583 100644 --- a/src/Sentry/Internal/IFileSystem.cs +++ b/src/Sentry/Internal/IFileSystem.cs @@ -10,19 +10,19 @@ internal interface IFileSystem // Note: This is not comprehensive. If you need other filesystem methods, add to this interface, // then implement in both Sentry.Internal.FileSystem and Sentry.Testing.FakeFileSystem. - IEnumerable EnumerateFiles(string path); - IEnumerable EnumerateFiles(string path, string searchPattern); - IEnumerable EnumerateFiles(string path, string searchPattern, SearchOption searchOption); - bool DirectoryExists(string path); - bool FileExists(string path); - DateTimeOffset GetFileCreationTime(string path); - string? ReadAllTextFromFile(string file); - Stream OpenFileForReading(string path); + public IEnumerable EnumerateFiles(string path); + public IEnumerable EnumerateFiles(string path, string searchPattern); + public IEnumerable EnumerateFiles(string path, string searchPattern, SearchOption searchOption); + public bool DirectoryExists(string path); + public bool FileExists(string path); + public DateTimeOffset GetFileCreationTime(string path); + public string? ReadAllTextFromFile(string file); + public Stream OpenFileForReading(string path); - bool CreateDirectory(string path); - bool DeleteDirectory(string path, bool recursive = false); - bool CreateFileForWriting(string path, out Stream fileStream); - bool WriteAllTextToFile(string path, string contents); - bool MoveFile(string sourceFileName, string destFileName, bool overwrite = false); - bool DeleteFile(string path); + public bool CreateDirectory(string path); + public bool DeleteDirectory(string path, bool recursive = false); + public bool CreateFileForWriting(string path, out Stream fileStream); + public bool WriteAllTextToFile(string path, string contents); + public bool MoveFile(string sourceFileName, string destFileName, bool overwrite = false); + public bool DeleteFile(string path); } diff --git a/src/Sentry/Internal/IGCImplementation.cs b/src/Sentry/Internal/IGCImplementation.cs index a148aa2936..a62a08df24 100644 --- a/src/Sentry/Internal/IGCImplementation.cs +++ b/src/Sentry/Internal/IGCImplementation.cs @@ -5,8 +5,8 @@ namespace Sentry.Internal; /// internal interface IGCImplementation { - void RegisterForFullGCNotification(int maxGenerationThreshold, int largeObjectHeapThreshold); - GCNotificationStatus WaitForFullGCComplete(TimeSpan timeout); - void CancelFullGCNotification(); - long TotalAvailableMemoryBytes { get; } + public void RegisterForFullGCNotification(int maxGenerationThreshold, int largeObjectHeapThreshold); + public GCNotificationStatus WaitForFullGCComplete(TimeSpan timeout); + public void CancelFullGCNotification(); + public long TotalAvailableMemoryBytes { get; } } diff --git a/src/Sentry/Internal/IInternalScopeManager.cs b/src/Sentry/Internal/IInternalScopeManager.cs index 81398d1ec4..474c2681f4 100644 --- a/src/Sentry/Internal/IInternalScopeManager.cs +++ b/src/Sentry/Internal/IInternalScopeManager.cs @@ -4,8 +4,8 @@ namespace Sentry.Internal; internal interface IInternalScopeManager : ISentryScopeManager, IDisposable { - KeyValuePair GetCurrent(); - void RestoreScope(Scope savedScope); + public KeyValuePair GetCurrent(); + public void RestoreScope(Scope savedScope); - IScopeStackContainer ScopeStackContainer { get; } + public IScopeStackContainer ScopeStackContainer { get; } } diff --git a/src/Sentry/Internal/IStringOrRegexMatcher.cs b/src/Sentry/Internal/IStringOrRegexMatcher.cs index 916eb5fc55..18826f7f46 100644 --- a/src/Sentry/Internal/IStringOrRegexMatcher.cs +++ b/src/Sentry/Internal/IStringOrRegexMatcher.cs @@ -5,5 +5,5 @@ internal interface IStringOrRegexMatcher /// /// Evaluates if the given value matches the string or regex. /// - bool IsMatch(StringOrRegex stringOrRegex, string value); + public bool IsMatch(StringOrRegex stringOrRegex, string value); } diff --git a/src/Sentry/Internal/ITransactionProfiler.cs b/src/Sentry/Internal/ITransactionProfiler.cs index e17ace104a..480ef7480c 100644 --- a/src/Sentry/Internal/ITransactionProfiler.cs +++ b/src/Sentry/Internal/ITransactionProfiler.cs @@ -8,7 +8,7 @@ internal interface ITransactionProfilerFactory /// /// Called during transaction start to start a new profiler, if applicable. /// - ITransactionProfiler? Start(ITransactionTracer transaction, CancellationToken cancellationToken); + public ITransactionProfiler? Start(ITransactionTracer transaction, CancellationToken cancellationToken); } /// @@ -19,9 +19,9 @@ internal interface ITransactionProfiler /// /// Called when the transaction ends - this should stop profile samples collection. /// - void Finish(); + public void Finish(); /// Process and collect the profile. /// The collected profile. - ISerializable? Collect(SentryTransaction transaction); + public ISerializable? Collect(SentryTransaction transaction); } diff --git a/src/Sentry/Internal/IUpdatable.cs b/src/Sentry/Internal/IUpdatable.cs index 7606a4ae13..dced987210 100644 --- a/src/Sentry/Internal/IUpdatable.cs +++ b/src/Sentry/Internal/IUpdatable.cs @@ -3,10 +3,10 @@ namespace Sentry.Internal; internal interface IUpdatable : IUpdatable where T : IUpdatable { - void UpdateFrom(T source); + public void UpdateFrom(T source); } internal interface IUpdatable { - void UpdateFrom(object source); + public void UpdateFrom(object source); } diff --git a/src/Sentry/Internal/ScopeStack/IScopeStackContainer.cs b/src/Sentry/Internal/ScopeStack/IScopeStackContainer.cs index 8d50c3d528..56060d03fe 100644 --- a/src/Sentry/Internal/ScopeStack/IScopeStackContainer.cs +++ b/src/Sentry/Internal/ScopeStack/IScopeStackContainer.cs @@ -2,5 +2,5 @@ namespace Sentry.Internal.ScopeStack; internal interface IScopeStackContainer { - KeyValuePair[]? Stack { get; set; } + public KeyValuePair[]? Stack { get; set; } } diff --git a/src/Sentry/Internal/StackFrame.cs b/src/Sentry/Internal/StackFrame.cs index 5d11ec2145..ad71b40a04 100644 --- a/src/Sentry/Internal/StackFrame.cs +++ b/src/Sentry/Internal/StackFrame.cs @@ -6,7 +6,7 @@ namespace Sentry.Internal; /// internal interface IStackFrame { - StackFrame? Frame { get; } + public StackFrame? Frame { get; } /// /// Returns a pointer to the base address of the native image that this stack frame is executing. diff --git a/src/Sentry/Internal/TcpPing.cs b/src/Sentry/Internal/TcpPing.cs index 7a922c96e6..9df889c5df 100644 --- a/src/Sentry/Internal/TcpPing.cs +++ b/src/Sentry/Internal/TcpPing.cs @@ -2,7 +2,7 @@ namespace Sentry.Internal; internal interface IPing { - Task IsAvailableAsync(CancellationToken cancellationToken); + public Task IsAvailableAsync(CancellationToken cancellationToken); } internal class TcpPing(string hostToCheck, int portToCheck = 443) : IPing diff --git a/src/Sentry/Protocol/Envelopes/ISerializable.cs b/src/Sentry/Protocol/Envelopes/ISerializable.cs index 3b861c93ae..4fefc262b4 100644 --- a/src/Sentry/Protocol/Envelopes/ISerializable.cs +++ b/src/Sentry/Protocol/Envelopes/ISerializable.cs @@ -10,10 +10,10 @@ public interface ISerializable /// /// Serializes the object to a stream asynchronously. /// - Task SerializeAsync(Stream stream, IDiagnosticLogger? logger, CancellationToken cancellationToken = default); + public Task SerializeAsync(Stream stream, IDiagnosticLogger? logger, CancellationToken cancellationToken = default); /// /// Serializes the object to a stream synchronously. /// - void Serialize(Stream stream, IDiagnosticLogger? logger); + public void Serialize(Stream stream, IDiagnosticLogger? logger); } diff --git a/src/Sentry/Protocol/ITraceContext.cs b/src/Sentry/Protocol/ITraceContext.cs index 3a0ec29aa5..6bad1e1a4e 100644 --- a/src/Sentry/Protocol/ITraceContext.cs +++ b/src/Sentry/Protocol/ITraceContext.cs @@ -8,22 +8,22 @@ public interface ITraceContext /// /// Span ID. /// - SpanId SpanId { get; } + public SpanId SpanId { get; } /// /// Parent ID. /// - SpanId? ParentSpanId { get; } + public SpanId? ParentSpanId { get; } /// /// Trace ID. /// - SentryId TraceId { get; } + public SentryId TraceId { get; } /// /// Operation. /// - string Operation { get; } + public string Operation { get; } /// /// Specifies the origin of the trace. If no origin is set then the trace origin is assumed to be "manual". @@ -31,17 +31,17 @@ public interface ITraceContext /// /// See https://develop.sentry.dev/sdk/performance/trace-origin/ for more information. /// - string? Origin { get; } + public string? Origin { get; } /// /// Description. /// - string? Description { get; } + public string? Description { get; } /// /// Status. /// - SpanStatus? Status { get; } + public SpanStatus? Status { get; } // Note: this may need to be mutated internally, // but the user should never be able to change it @@ -50,5 +50,5 @@ public interface ITraceContext /// /// Whether the span or transaction is sampled in (i.e. eligible for sending to Sentry). /// - bool? IsSampled { get; } + public bool? IsSampled { get; } } From 068f004501731498d843e0e8c7c384d8ec5c7298 Mon Sep 17 00:00:00 2001 From: thisisthekap Date: Fri, 21 Mar 2025 03:58:47 +0100 Subject: [PATCH 164/363] Changed default value for SentryOptions.EnableAppHangTrackingV2 to false (#4042) Co-authored-by: James Crosswell --- CHANGELOG.md | 1 + src/Sentry/Platforms/Cocoa/SentryOptions.cs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 53989c0b87..bd7277914e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Fixes - Target `net9.0` on Sentry.Google.Cloud.Functions to avoid conflict with Sentry.AspNetCore ([#4039](https://github.com/getsentry/sentry-dotnet/pull/4039)) +- Changed default value for `SentryOptions.EnableAppHangTrackingV2` to `false` ([#4042](https://github.com/getsentry/sentry-dotnet/pull/4042)) ### Features diff --git a/src/Sentry/Platforms/Cocoa/SentryOptions.cs b/src/Sentry/Platforms/Cocoa/SentryOptions.cs index 21ead2c413..2197098d8a 100644 --- a/src/Sentry/Platforms/Cocoa/SentryOptions.cs +++ b/src/Sentry/Platforms/Cocoa/SentryOptions.cs @@ -83,7 +83,7 @@ internal NativeOptions(SentryOptions options) /// /// See https://docs.sentry.io/platforms/apple/configuration/app-hangs/#app-hangs-v2 /// - public bool EnableAppHangTrackingV2 { get; set; } = true; + public bool EnableAppHangTrackingV2 { get; set; } = false; /// /// When enabled, the SDK adds breadcrumbs for various system events. From 7f829169fa82efc545dba28bcee116b84518a804 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 25 Mar 2025 09:44:39 +1300 Subject: [PATCH 165/363] build(deps): bump actions/create-github-app-token from 1.11.6 to 1.11.7 (#4058) Bumps [actions/create-github-app-token](https://github.com/actions/create-github-app-token) from 1.11.6 to 1.11.7. - [Release notes](https://github.com/actions/create-github-app-token/releases) - [Commits](https://github.com/actions/create-github-app-token/compare/21cfef2b496dd8ef5b904c159339626a10ad380e...af35edadc00be37caa72ed9f3e6d5f7801bfdf09) --- updated-dependencies: - dependency-name: actions/create-github-app-token dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d278b1511f..dfe0dc41f0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -20,7 +20,7 @@ jobs: steps: - name: Get auth token id: token - uses: actions/create-github-app-token@21cfef2b496dd8ef5b904c159339626a10ad380e # v1.11.6 + uses: actions/create-github-app-token@af35edadc00be37caa72ed9f3e6d5f7801bfdf09 # v1.11.7 with: app-id: ${{ vars.SENTRY_RELEASE_BOT_CLIENT_ID }} private-key: ${{ secrets.SENTRY_RELEASE_BOT_PRIVATE_KEY }} From 641e44feeefd9b6792328eaf3e7bba5155c329b3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 25 Mar 2025 09:45:03 +1300 Subject: [PATCH 166/363] build(deps): bump github/codeql-action from 3.28.11 to 3.28.12 (#4057) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.28.11 to 3.28.12. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/6bb031afdd8eb862ea3fc1848194185e076637e5...5f8171a638ada777af81d42b55959a643bb29017) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index ec82ce1bbf..133ca3c067 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -35,7 +35,7 @@ jobs: uses: ./.github/actions/environment - name: Initialize CodeQL - uses: github/codeql-action/init@6bb031afdd8eb862ea3fc1848194185e076637e5 # pin@v2 + uses: github/codeql-action/init@5f8171a638ada777af81d42b55959a643bb29017 # pin@v2 with: languages: csharp @@ -49,6 +49,6 @@ jobs: run: dotnet build Sentry-CI-CodeQL.slnf --no-restore --nologo - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@6bb031afdd8eb862ea3fc1848194185e076637e5 # pin@v2 + uses: github/codeql-action/analyze@5f8171a638ada777af81d42b55959a643bb29017 # pin@v2 with: category: '/language:csharp' From 992547e938e06661e264d9eebf45103335c6bbea Mon Sep 17 00:00:00 2001 From: Tung Huynh <31434093+huynhsontung@users.noreply.github.com> Date: Wed, 26 Mar 2025 00:28:14 -0700 Subject: [PATCH 167/363] fix: MSBuild target produces incorrect release name (#4015) * fix: msbuild target produces incorrect release name * Update CHANGELOG.md --------- Co-authored-by: James Crosswell Co-authored-by: Bruno Garcia --- CHANGELOG.md | 1 + src/Sentry/buildTransitive/Sentry.targets | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bd7277914e..c7cf98ad9f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Fixes +- Single quotes added to the release name when using MS Build to create Sentry releases on Windows ([#4015](https://github.com/getsentry/sentry-dotnet/pull/4015)) - Target `net9.0` on Sentry.Google.Cloud.Functions to avoid conflict with Sentry.AspNetCore ([#4039](https://github.com/getsentry/sentry-dotnet/pull/4039)) - Changed default value for `SentryOptions.EnableAppHangTrackingV2` to `false` ([#4042](https://github.com/getsentry/sentry-dotnet/pull/4042)) diff --git a/src/Sentry/buildTransitive/Sentry.targets b/src/Sentry/buildTransitive/Sentry.targets index d7aeada335..57ab5a9285 100644 --- a/src/Sentry/buildTransitive/Sentry.targets +++ b/src/Sentry/buildTransitive/Sentry.targets @@ -333,7 +333,7 @@ Condition="'$(SentryCLI)' != '' and '$(SentryCreateRelease)' == 'true'"> @@ -344,7 +344,7 @@ Condition="'$(SentryCLI)' != '' and '$(SentrySetCommits)' == 'true'"> From c55a2f076f80888a9a3b00fed3b9c3ba2e045674 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 27 Mar 2025 21:13:15 +1300 Subject: [PATCH 168/363] chore: update scripts/update-cli.ps1 to 2.42.5 (#4060) Co-authored-by: GitHub --- CHANGELOG.md | 6 +++--- Directory.Build.props | 2 +- src/Sentry/Sentry.csproj | 14 +++++++------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c7cf98ad9f..b4e5347bb7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,9 +14,9 @@ ### Dependencies -- Bump CLI from v2.42.2 to v2.42.4 ([#4036](https://github.com/getsentry/sentry-dotnet/pull/4036), [#4049](https://github.com/getsentry/sentry-dotnet/pull/4049)) - - [changelog](https://github.com/getsentry/sentry-cli/blob/master/CHANGELOG.md#2424) - - [diff](https://github.com/getsentry/sentry-cli/compare/2.42.2...2.42.4) +- Bump CLI from v2.42.2 to v2.42.5 ([#4036](https://github.com/getsentry/sentry-dotnet/pull/4036), [#4049](https://github.com/getsentry/sentry-dotnet/pull/4049), [#4060](https://github.com/getsentry/sentry-dotnet/pull/4060)) + - [changelog](https://github.com/getsentry/sentry-cli/blob/master/CHANGELOG.md#2425) + - [diff](https://github.com/getsentry/sentry-cli/compare/2.42.2...2.42.5) ## 5.4.0 diff --git a/Directory.Build.props b/Directory.Build.props index e8891669cd..96d4f5a360 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -83,7 +83,7 @@ - 2.42.4 + 2.42.5 $(MSBuildThisFileDirectory)tools\sentry-cli\$(SentryCLIVersion)\ diff --git a/src/Sentry/Sentry.csproj b/src/Sentry/Sentry.csproj index b930fe619e..0267f0e68f 100644 --- a/src/Sentry/Sentry.csproj +++ b/src/Sentry/Sentry.csproj @@ -120,13 +120,13 @@ <_OSArchitecture>$([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture) - - - - - - - + + + + + + + From de296f9e24a2b5ef056f207324f1884bd3f884c3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 28 Mar 2025 21:58:14 +1300 Subject: [PATCH 169/363] chore: update scripts/update-cli.ps1 to 2.43.0 (#4062) Co-authored-by: GitHub --- CHANGELOG.md | 6 +++--- Directory.Build.props | 2 +- src/Sentry/Sentry.csproj | 14 +++++++------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b4e5347bb7..5ed7eb87c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,9 +14,9 @@ ### Dependencies -- Bump CLI from v2.42.2 to v2.42.5 ([#4036](https://github.com/getsentry/sentry-dotnet/pull/4036), [#4049](https://github.com/getsentry/sentry-dotnet/pull/4049), [#4060](https://github.com/getsentry/sentry-dotnet/pull/4060)) - - [changelog](https://github.com/getsentry/sentry-cli/blob/master/CHANGELOG.md#2425) - - [diff](https://github.com/getsentry/sentry-cli/compare/2.42.2...2.42.5) +- Bump CLI from v2.42.2 to v2.43.0 ([#4036](https://github.com/getsentry/sentry-dotnet/pull/4036), [#4049](https://github.com/getsentry/sentry-dotnet/pull/4049), [#4060](https://github.com/getsentry/sentry-dotnet/pull/4060), [#4062](https://github.com/getsentry/sentry-dotnet/pull/4062)) + - [changelog](https://github.com/getsentry/sentry-cli/blob/master/CHANGELOG.md#2430) + - [diff](https://github.com/getsentry/sentry-cli/compare/2.42.2...2.43.0) ## 5.4.0 diff --git a/Directory.Build.props b/Directory.Build.props index 96d4f5a360..ece069c283 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -83,7 +83,7 @@ - 2.42.5 + 2.43.0 $(MSBuildThisFileDirectory)tools\sentry-cli\$(SentryCLIVersion)\ diff --git a/src/Sentry/Sentry.csproj b/src/Sentry/Sentry.csproj index 0267f0e68f..f7eb60f38a 100644 --- a/src/Sentry/Sentry.csproj +++ b/src/Sentry/Sentry.csproj @@ -120,13 +120,13 @@ <_OSArchitecture>$([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture) - - - - - - - + + + + + + + From 790f34105883a4f92f4bf161f8ba5054f46d7b36 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Tue, 1 Apr 2025 11:10:07 +1300 Subject: [PATCH 170/363] Update CHANGELOG.md (#4063) --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ed7eb87c3..efc1251bc8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - Single quotes added to the release name when using MS Build to create Sentry releases on Windows ([#4015](https://github.com/getsentry/sentry-dotnet/pull/4015)) - Target `net9.0` on Sentry.Google.Cloud.Functions to avoid conflict with Sentry.AspNetCore ([#4039](https://github.com/getsentry/sentry-dotnet/pull/4039)) - Changed default value for `SentryOptions.EnableAppHangTrackingV2` to `false` ([#4042](https://github.com/getsentry/sentry-dotnet/pull/4042)) +- Missing MAUI `Shell` navigation breadcrumbs on iOS ([#4006](https://github.com/getsentry/sentry-dotnet/pull/4006)) ### Features @@ -27,7 +28,6 @@ ### Fixes - Unknown stack frames in profiles on .NET 8+ ([#3967](https://github.com/getsentry/sentry-dotnet/pull/3967)) -- Missing MAUI `Shell` navigation breadcrumbs on iOS ([#4006](https://github.com/getsentry/sentry-dotnet/pull/4006)) ## 5.3.0 From c8f258c6c4268d201275fc2c978028cf1327d183 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Apr 2025 12:57:37 +1300 Subject: [PATCH 171/363] build(deps): bump reactivecircus/android-emulator-runner (#4064) Bumps [reactivecircus/android-emulator-runner](https://github.com/reactivecircus/android-emulator-runner) from 2.33.0 to 2.34.0. - [Release notes](https://github.com/reactivecircus/android-emulator-runner/releases) - [Changelog](https://github.com/ReactiveCircus/android-emulator-runner/blob/main/CHANGELOG.md) - [Commits](https://github.com/reactivecircus/android-emulator-runner/compare/62dbb605bba737720e10b196cb4220d374026a6d...1dcd0090116d15e7c562f8db72807de5e036a4ed) --- updated-dependencies: - dependency-name: reactivecircus/android-emulator-runner dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/device-tests-android.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/device-tests-android.yml b/.github/workflows/device-tests-android.yml index 64749e1b8f..83880326ae 100644 --- a/.github/workflows/device-tests-android.yml +++ b/.github/workflows/device-tests-android.yml @@ -81,7 +81,7 @@ jobs: - name: Run Tests timeout-minutes: 40 - uses: reactivecircus/android-emulator-runner@62dbb605bba737720e10b196cb4220d374026a6d # Tag: v2.33.0 + uses: reactivecircus/android-emulator-runner@1dcd0090116d15e7c562f8db72807de5e036a4ed # Tag: v2.34.0 with: api-level: ${{ matrix.api-level }} # We don't need the Google APIs, but the default images are not available for 32+ From 6247e96dd5300f025e4ebbaea93ebd1c729b2eda Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Apr 2025 13:51:22 +1300 Subject: [PATCH 172/363] build(deps): bump gradle/actions from 4.3.0 to 4.3.1 (#4066) Bumps [gradle/actions](https://github.com/gradle/actions) from 4.3.0 to 4.3.1. - [Release notes](https://github.com/gradle/actions/releases) - [Commits](https://github.com/gradle/actions/compare/94baf225fe0a508e581a564467443d0e2379123b...06832c7b30a0129d7fb559bcc6e43d26f6374244) --- updated-dependencies: - dependency-name: gradle/actions dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/device-tests-android.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/device-tests-android.yml b/.github/workflows/device-tests-android.yml index 83880326ae..e4a7dcd448 100644 --- a/.github/workflows/device-tests-android.yml +++ b/.github/workflows/device-tests-android.yml @@ -74,7 +74,7 @@ jobs: path: bin - name: Setup Gradle - uses: gradle/actions/setup-gradle@94baf225fe0a508e581a564467443d0e2379123b # pin@v3 + uses: gradle/actions/setup-gradle@06832c7b30a0129d7fb559bcc6e43d26f6374244 # pin@v3 # Cached AVD setup per https://github.com/ReactiveCircus/android-emulator-runner/blob/main/README.md From 807c80d7e858af1587eafa7749d6100225ac5083 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Apr 2025 14:53:33 +1300 Subject: [PATCH 173/363] build(deps): bump github/codeql-action from 3.28.12 to 3.28.13 (#4065) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.28.12 to 3.28.13. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/5f8171a638ada777af81d42b55959a643bb29017...1b549b9259bda1cb5ddde3b41741a82a2d15a841) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 133ca3c067..d088039ebd 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -35,7 +35,7 @@ jobs: uses: ./.github/actions/environment - name: Initialize CodeQL - uses: github/codeql-action/init@5f8171a638ada777af81d42b55959a643bb29017 # pin@v2 + uses: github/codeql-action/init@1b549b9259bda1cb5ddde3b41741a82a2d15a841 # pin@v2 with: languages: csharp @@ -49,6 +49,6 @@ jobs: run: dotnet build Sentry-CI-CodeQL.slnf --no-restore --nologo - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@5f8171a638ada777af81d42b55959a643bb29017 # pin@v2 + uses: github/codeql-action/analyze@1b549b9259bda1cb5ddde3b41741a82a2d15a841 # pin@v2 with: category: '/language:csharp' From 1771557a1937f357bd72cc738ca4ce279677fb5b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 1 Apr 2025 17:45:37 +1300 Subject: [PATCH 174/363] chore: update modules/sentry-native to 0.8.2 (#4050) Co-authored-by: GitHub Co-authored-by: James Crosswell --- CHANGELOG.md | 3 +++ modules/sentry-native | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index efc1251bc8..f5296b8b64 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,9 @@ ### Dependencies +- Bump Native SDK from v0.8.1 to v0.8.2 ([#4050](https://github.com/getsentry/sentry-dotnet/pull/4050)) + - [changelog](https://github.com/getsentry/sentry-native/blob/master/CHANGELOG.md#082) + - [diff](https://github.com/getsentry/sentry-native/compare/0.8.1...0.8.2) - Bump CLI from v2.42.2 to v2.43.0 ([#4036](https://github.com/getsentry/sentry-dotnet/pull/4036), [#4049](https://github.com/getsentry/sentry-dotnet/pull/4049), [#4060](https://github.com/getsentry/sentry-dotnet/pull/4060), [#4062](https://github.com/getsentry/sentry-dotnet/pull/4062)) - [changelog](https://github.com/getsentry/sentry-cli/blob/master/CHANGELOG.md#2430) - [diff](https://github.com/getsentry/sentry-cli/compare/2.42.2...2.43.0) diff --git a/modules/sentry-native b/modules/sentry-native index ccef7125b3..77dbf6a600 160000 --- a/modules/sentry-native +++ b/modules/sentry-native @@ -1 +1 @@ -Subproject commit ccef7125b3783210f44ee6b100df7278e6ba3eff +Subproject commit 77dbf6a6006f0be24af30d056bdcac98a7bf5877 From b6e01124afca98ec75bb3e6aad2960b7d0bd5e5b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Apr 2025 20:39:52 +1300 Subject: [PATCH 175/363] build(deps): bump actions/create-github-app-token from 1.11.7 to 1.12.0 (#4067) Bumps [actions/create-github-app-token](https://github.com/actions/create-github-app-token) from 1.11.7 to 1.12.0. - [Release notes](https://github.com/actions/create-github-app-token/releases) - [Commits](https://github.com/actions/create-github-app-token/compare/af35edadc00be37caa72ed9f3e6d5f7801bfdf09...d72941d797fd3113feb6b93fd0dec494b13a2547) --- updated-dependencies: - dependency-name: actions/create-github-app-token dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index dfe0dc41f0..d479620bf1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -20,7 +20,7 @@ jobs: steps: - name: Get auth token id: token - uses: actions/create-github-app-token@af35edadc00be37caa72ed9f3e6d5f7801bfdf09 # v1.11.7 + uses: actions/create-github-app-token@d72941d797fd3113feb6b93fd0dec494b13a2547 # v1.12.0 with: app-id: ${{ vars.SENTRY_RELEASE_BOT_CLIENT_ID }} private-key: ${{ secrets.SENTRY_RELEASE_BOT_PRIVATE_KEY }} From 8f448d2604479e53ca563b047a1ac204e08b3e2f Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Wed, 2 Apr 2025 10:39:36 +1300 Subject: [PATCH 176/363] Prevent application crashes from MAUI ScreenshotImplementation (#4069) --- CHANGELOG.md | 1 + src/Sentry.Maui/Internal/ScreenshotAttachment.cs | 16 ++++++++++++++++ .../SentryMauiScreenshotTests.cs | 16 ++++++++++++++++ 3 files changed, 33 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f5296b8b64..9f1818da93 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - Target `net9.0` on Sentry.Google.Cloud.Functions to avoid conflict with Sentry.AspNetCore ([#4039](https://github.com/getsentry/sentry-dotnet/pull/4039)) - Changed default value for `SentryOptions.EnableAppHangTrackingV2` to `false` ([#4042](https://github.com/getsentry/sentry-dotnet/pull/4042)) - Missing MAUI `Shell` navigation breadcrumbs on iOS ([#4006](https://github.com/getsentry/sentry-dotnet/pull/4006)) +- Prevent application crashes when capturing screenshots on iOS ([#4069](https://github.com/getsentry/sentry-dotnet/pull/4069)) ### Features diff --git a/src/Sentry.Maui/Internal/ScreenshotAttachment.cs b/src/Sentry.Maui/Internal/ScreenshotAttachment.cs index 60dab1a1fa..6aa8d3181e 100644 --- a/src/Sentry.Maui/Internal/ScreenshotAttachment.cs +++ b/src/Sentry.Maui/Internal/ScreenshotAttachment.cs @@ -39,6 +39,22 @@ public ScreenshotAttachmentContent(SentryMauiOptions options) } public Stream GetStream() + { + try + { + return GetStreamInternal(); + } + catch (Exception ex) + { + // See https://github.com/getsentry/sentry-dotnet/issues/3880#issuecomment-2640159466 + _options.LogError("Failed to capture screenshot", ex); + // Return empty stream since calling code may assume a non-null return value + // E.g. https://github.com/getsentry/sentry-dotnet/blob/db5606833a4b0662c6bea0663cca10cb05fb5157/src/Sentry/Protocol/Envelopes/EnvelopeItem.cs#L332-L333 + return new MemoryStream(); + } + } + + private Stream GetStreamInternal() { var stream = Stream.Null; // Not including this on Windows specific build because on WinUI this can deadlock. diff --git a/test/Sentry.Maui.Tests/SentryMauiScreenshotTests.cs b/test/Sentry.Maui.Tests/SentryMauiScreenshotTests.cs index 601b042a1a..970558eab3 100644 --- a/test/Sentry.Maui.Tests/SentryMauiScreenshotTests.cs +++ b/test/Sentry.Maui.Tests/SentryMauiScreenshotTests.cs @@ -34,12 +34,20 @@ public Fixture() private readonly Fixture _fixture = new(); #if __MOBILE__ + // TODO: This test doesn't work on Android or iOS, so isn't much use. We should consider replacing it with some + // more granular tests that test SentryMauiOptionsSetup, SentryMauiScreenshotProcessor and ScreenshotAttachment + // in isolation. It's generally hard to test any of this functionality since ScreenshotImplementation relies of + // various static members like ActivityStateManager.Default: + // https://github.com/dotnet/maui/blob/3c7b65264d2f341a48db32263a271fd8718cfd23/src/Essentials/src/Screenshot/Screenshot.android.cs#L28 [SkippableFact] public async Task CaptureException_WhenAttachScreenshots_ContainsScreenshotAttachmentAsync() { #if __IOS__ Skip.If(true, "Flaky on iOS"); #endif +#if ANDROID + Skip.If(true, "Doesn't work on Android"); +#endif // Arrange var builder = _fixture.Builder.UseSentry(); @@ -133,12 +141,20 @@ public async Task CaptureException_BeforeCaptureScreenshot_DisableCaptureAsync() envelopeItem.Should().BeNull(); } + // TODO: This test doesn't work on Android or iOS, so isn't much use. We should consider replacing it with some + // more granular tests that test SentryMauiOptionsSetup, SentryMauiScreenshotProcessor and ScreenshotAttachment + // in isolation. It's generally hard to test any of this functionality since ScreenshotImplementation relies of + // various static members like ActivityStateManager.Default: + // https://github.com/dotnet/maui/blob/3c7b65264d2f341a48db32263a271fd8718cfd23/src/Essentials/src/Screenshot/Screenshot.android.cs#L28 [SkippableFact] public async Task CaptureException_BeforeCaptureScreenshot_DefaultAsync() { #if __IOS__ Skip.If(true, "Flaky on iOS"); #endif +#if ANDROID + Skip.If(true, "Doesn't work on Android"); +#endif // Arrange var builder = _fixture.Builder.UseSentry(options => options.SetBeforeScreenshotCapture((e, hint) => From 6924e88acd59e9e9bd4b9c0d62e4d27a76a8543d Mon Sep 17 00:00:00 2001 From: Allan Ritchie Date: Tue, 1 Apr 2025 19:00:29 -0400 Subject: [PATCH 177/363] Update bug_report.yml (#4068) --- .github/ISSUE_TEMPLATE/bug_report.yml | 42 +++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 069b48fbbe..d78bb3c52e 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -36,7 +36,6 @@ body: - .NET Framework - IL2CPP - Mono - - Xamarin - Other validations: required: true @@ -45,8 +44,8 @@ body: id: dotnet_version attributes: label: .NET Version - description: .NET Version - placeholder: 6.0.0 ← should look like this + description: If using .NET Core, please copy the output of 'dotnet --version' here + placeholder: 9.0.200 ← should look like this validations: required: true @@ -67,6 +66,27 @@ body: validations: required: true + - type: input + id: os_version + attributes: + label: OS Version + description: Operating System Version + + - type: dropdown + id: ide + attributes: + label: Development Environment + description: What development environment are you using + options: + - Visual Studio v17.x + - Visual Studio Code (Windows) + - Visual Studio Code (MacOS) + - Rider 2024 (Windows) + - Rider 2024 (MacOS) + - Other + validations: + required: true + - type: input id: sentry_sdk_version attributes: @@ -84,6 +104,22 @@ body: validations: required: false + - type: textarea + id: workloadset + attributes: + label: Workload Versions + description: If using .NET Core, please copy/paste the output of 'dotnet workload list' here + validations: + required: true + + - type: textarea + id: sentryuse + attributes: + label: UseSentry or SentrySdk.Init call + description: Please include the block of code used in UseSentry or SentrySdk.Init - please blank out your DSN + validations: + required: true + - type: textarea id: repro attributes: From 13d4a9f2038e142f9dc8c7799df06edfe17a2dd4 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Wed, 2 Apr 2025 19:31:22 +1300 Subject: [PATCH 178/363] Enable symbolication and source context for net9.0-android (#4033) --- .github/workflows/build.yml | 8 +- .github/workflows/device-tests-android.yml | 32 +- CHANGELOG.md | 9 +- .../Sentry.Benchmarks.csproj | 2 +- scripts/device-test.ps1 | 18 +- .../AndroidAssemblyReader.cs | 65 --- .../AndroidAssemblyReaderFactory.cs | 25 +- .../ArchiveUtils.cs | 72 +++ .../ELFSharp/ATTRIBUTION.txt | 76 ++++ .../ELFSharp/ELF/Class.cs | 9 + .../ELFSharp/ELF/Consts.cs | 11 + .../ELFSharp/ELF/ELF.cs | 411 ++++++++++++++++++ .../ELFSharp/ELF/ELFReader.cs | 116 +++++ .../ELFSharp/ELF/FileType.cs | 11 + .../ELFSharp/ELF/IELF.cs | 26 ++ .../ELFSharp/ELF/Machine.cs | 169 +++++++ .../ELFSharp/ELF/Sections/DynamicEntry.cs | 27 ++ .../ELFSharp/ELF/Sections/DynamicSection.cs | 53 +++ .../ELFSharp/ELF/Sections/DynamicTag.cs | 64 +++ .../ELFSharp/ELF/Sections/IDynamicEntry.cs | 12 + .../ELFSharp/ELF/Sections/IDynamicSection.cs | 9 + .../ELFSharp/ELF/Sections/INoteSection.cs | 8 + .../ELFSharp/ELF/Sections/IProgBitsSection.cs | 7 + .../ELFSharp/ELF/Sections/ISection.cs | 11 + .../ELFSharp/ELF/Sections/IStringTable.cs | 10 + .../ELFSharp/ELF/Sections/ISymbolEntry.cs | 13 + .../ELFSharp/ELF/Sections/ISymbolTable.cs | 9 + .../ELFSharp/ELF/Sections/NoteData.cs | 98 +++++ .../ELFSharp/ELF/Sections/NoteSection.cs | 25 ++ .../ELFSharp/ELF/Sections/ProgBitsSection.cs | 29 ++ .../ELFSharp/ELF/Sections/Section.cs | 58 +++ .../ELFSharp/ELF/Sections/SectionFlags.cs | 12 + .../ELFSharp/ELF/Sections/SectionHeader.cs | 70 +++ .../ELFSharp/ELF/Sections/SectionType.cs | 18 + .../ELF/Sections/SpecialSectionIndex.cs | 9 + .../ELF/Sections/SpecialSectionType.cs | 12 + .../ELFSharp/ELF/Sections/StringTable.cs | 82 ++++ .../ELFSharp/ELF/Sections/SymbolBinding.cs | 10 + .../ELFSharp/ELF/Sections/SymbolEntry.cs | 60 +++ .../ELFSharp/ELF/Sections/SymbolTable.cs | 64 +++ .../ELFSharp/ELF/Sections/SymbolType.cs | 12 + .../ELFSharp/ELF/Sections/SymbolVisibility.cs | 10 + .../ELFSharp/ELF/Segments/INoteData.cs | 31 ++ .../ELFSharp/ELF/Segments/INoteSegment.cs | 16 + .../ELFSharp/ELF/Segments/ISegment.cs | 11 + .../ELFSharp/ELF/Segments/NoteSegment.cs | 54 +++ .../ELFSharp/ELF/Segments/Segment.cs | 114 +++++ .../ELFSharp/ELF/Segments/SegmentFlags.cs | 12 + .../ELFSharp/ELF/Segments/SegmentType.cs | 13 + .../ELFSharp/ELF/Utilities.cs | 12 + .../ELFSharp/Endianess.cs | 8 + .../ELFSharp/MachO/Command.cs | 17 + .../ELFSharp/MachO/CommandType.cs | 15 + .../ELFSharp/MachO/Dylib.cs | 38 ++ .../ELFSharp/MachO/EntryPoint.cs | 18 + .../ELFSharp/MachO/FatArchiveReader.cs | 32 ++ .../ELFSharp/MachO/FileType.cs | 17 + .../ELFSharp/MachO/HeaderFlags.cs | 76 ++++ .../ELFSharp/MachO/IdDylib.cs | 13 + .../ELFSharp/MachO/LoadDylib.cs | 13 + .../ELFSharp/MachO/LoadWeakDylib.cs | 13 + .../ELFSharp/MachO/MachO.cs | 85 ++++ .../ELFSharp/MachO/MachOReader.cs | 92 ++++ .../ELFSharp/MachO/MachOResult.cs | 9 + .../ELFSharp/MachO/Machine.cs | 20 + .../ELFSharp/MachO/Protection.cs | 12 + .../ELFSharp/MachO/ReexportDylib.cs | 13 + .../ELFSharp/MachO/Section.cs | 45 ++ .../ELFSharp/MachO/Segment.cs | 106 +++++ .../ELFSharp/MachO/Symbol.cs | 19 + .../ELFSharp/MachO/SymbolTable.cs | 84 ++++ .../ELFSharp/MachO/UUID.cs | 31 ++ .../ELFSharp/UImage/Architecture.cs | 27 ++ .../ELFSharp/UImage/CompressionType.cs | 11 + .../ELFSharp/UImage/ImageDataResult.cs | 10 + .../ELFSharp/UImage/ImageType.cs | 10 + .../ELFSharp/UImage/OS.cs | 30 ++ .../ELFSharp/UImage/UImage.cs | 161 +++++++ .../ELFSharp/UImage/UImageReader.cs | 102 +++++ .../ELFSharp/UImage/UImageResult.cs | 10 + .../ELFSharp/Utilities/Extensions.cs | 21 + .../Utilities/SimpleEndianessAwareReader.cs | 81 ++++ .../ELFSharp/Utilities/SubStream.cs | 93 ++++ .../ELFSharp/make-internal.sh | 10 + .../Sentry.Android.AssemblyReader.csproj | 12 +- .../{ => V1}/ATTRIBUTION.txt | 0 .../AndroidAssemblyDirectoryReaderV1.cs} | 15 +- .../AndroidAssemblyStoreReaderV1.cs} | 10 +- .../V2/ATTRIBUTION.txt | 28 ++ .../V2/AndroidAssemblyDirectoryReaderV2.cs | 267 ++++++++++++ .../V2/AndroidAssemblyStoreReaderV2.cs | 117 +++++ .../V2/AndroidTargetArch.cs | 18 + .../V2/AssemblyStoreExplorer.cs | 132 ++++++ .../V2/AssemblyStoreItem.cs | 27 ++ .../V2/AssemblyStoreReader.cs | 93 ++++ .../V2/DeviceArchitectureExtensions.cs | 15 + .../V2/ELFPayloadError.cs | 16 + .../V2/FileFormat.cs | 17 + .../V2/MonoAndroidHelper.Basic.cs | 92 ++++ .../V2/StoreReaderV1.cs | 38 ++ .../V2/StoreReaderV2.Classes.cs | 96 ++++ .../V2/StoreReaderV2.cs | 241 ++++++++++ src/Sentry.Android.AssemblyReader/V2/Utils.cs | 169 +++++++ test/AndroidTestApp/AndroidTestApp.csproj | 3 +- test/Directory.Build.props | 2 +- .../AndroidAssemblyReaderTests.cs | 85 +++- ...Sentry.Android.AssemblyReader.Tests.csproj | 39 +- .../Sentry.Extensions.Logging.Tests.csproj | 6 +- .../Sentry.Maui.Device.TestApp.csproj | 4 +- .../Sentry.Maui.Tests.csproj | 6 +- 110 files changed, 4851 insertions(+), 154 deletions(-) create mode 100644 src/Sentry.Android.AssemblyReader/ArchiveUtils.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/ATTRIBUTION.txt create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Class.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Consts.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/ELF/ELF.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/ELF/ELFReader.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/ELF/FileType.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/ELF/IELF.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Machine.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/DynamicEntry.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/DynamicSection.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/DynamicTag.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/IDynamicEntry.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/IDynamicSection.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/INoteSection.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/IProgBitsSection.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/ISection.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/IStringTable.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/ISymbolEntry.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/ISymbolTable.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/NoteData.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/NoteSection.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/ProgBitsSection.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/Section.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/SectionFlags.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/SectionHeader.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/SectionType.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/SpecialSectionIndex.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/SpecialSectionType.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/StringTable.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/SymbolBinding.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/SymbolEntry.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/SymbolTable.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/SymbolType.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/SymbolVisibility.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Segments/INoteData.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Segments/INoteSegment.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Segments/ISegment.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Segments/NoteSegment.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Segments/Segment.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Segments/SegmentFlags.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Segments/SegmentType.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Utilities.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/Endianess.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/MachO/Command.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/MachO/CommandType.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/MachO/Dylib.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/MachO/EntryPoint.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/MachO/FatArchiveReader.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/MachO/FileType.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/MachO/HeaderFlags.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/MachO/IdDylib.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/MachO/LoadDylib.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/MachO/LoadWeakDylib.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/MachO/MachO.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/MachO/MachOReader.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/MachO/MachOResult.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/MachO/Machine.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/MachO/Protection.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/MachO/ReexportDylib.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/MachO/Section.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/MachO/Segment.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/MachO/Symbol.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/MachO/SymbolTable.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/MachO/UUID.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/UImage/Architecture.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/UImage/CompressionType.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/UImage/ImageDataResult.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/UImage/ImageType.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/UImage/OS.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/UImage/UImage.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/UImage/UImageReader.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/UImage/UImageResult.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/Utilities/Extensions.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/Utilities/SimpleEndianessAwareReader.cs create mode 100644 src/Sentry.Android.AssemblyReader/ELFSharp/Utilities/SubStream.cs create mode 100755 src/Sentry.Android.AssemblyReader/ELFSharp/make-internal.sh rename src/Sentry.Android.AssemblyReader/{ => V1}/ATTRIBUTION.txt (100%) rename src/Sentry.Android.AssemblyReader/{AndroidAssemblyDirectoryReader.cs => V1/AndroidAssemblyDirectoryReaderV1.cs} (73%) rename src/Sentry.Android.AssemblyReader/{AndroidAssemblyStoreReader.cs => V1/AndroidAssemblyStoreReaderV1.cs} (97%) create mode 100644 src/Sentry.Android.AssemblyReader/V2/ATTRIBUTION.txt create mode 100644 src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyDirectoryReaderV2.cs create mode 100644 src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyStoreReaderV2.cs create mode 100644 src/Sentry.Android.AssemblyReader/V2/AndroidTargetArch.cs create mode 100644 src/Sentry.Android.AssemblyReader/V2/AssemblyStoreExplorer.cs create mode 100644 src/Sentry.Android.AssemblyReader/V2/AssemblyStoreItem.cs create mode 100644 src/Sentry.Android.AssemblyReader/V2/AssemblyStoreReader.cs create mode 100644 src/Sentry.Android.AssemblyReader/V2/DeviceArchitectureExtensions.cs create mode 100644 src/Sentry.Android.AssemblyReader/V2/ELFPayloadError.cs create mode 100644 src/Sentry.Android.AssemblyReader/V2/FileFormat.cs create mode 100644 src/Sentry.Android.AssemblyReader/V2/MonoAndroidHelper.Basic.cs create mode 100644 src/Sentry.Android.AssemblyReader/V2/StoreReaderV1.cs create mode 100644 src/Sentry.Android.AssemblyReader/V2/StoreReaderV2.Classes.cs create mode 100644 src/Sentry.Android.AssemblyReader/V2/StoreReaderV2.cs create mode 100644 src/Sentry.Android.AssemblyReader/V2/Utils.cs diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 72b2e92f51..277ff99842 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -117,7 +117,13 @@ jobs: dotnet build src/Sentry/Sentry.csproj -t:InstallAndroidDependencies -f:net8.0-android34.0 -p:AcceptAndroidSDKLicenses=True -p:AndroidSdkPath="/usr/local/lib/android/sdk/" - name: Build - run: dotnet build Sentry-CI-Build-${{ runner.os }}.slnf -c Release --no-restore --nologo -v:minimal -flp:logfile=build.log -p:CopyLocalLockFileAssemblies=true + run: dotnet build Sentry-CI-Build-${{ runner.os }}.slnf -c Release --no-restore --nologo -v:minimal -flp:logfile=build.log -p:CopyLocalLockFileAssemblies=true -bl:build.binlog + + - name: Upload build logs + uses: actions/upload-artifact@v4 + with: + name: ${{ runner.os }}-build-logs + path: build.binlog - name: Test run: dotnet test Sentry-CI-Build-${{ runner.os }}.slnf -c Release --no-build --nologo -l GitHubActions -l "trx;LogFilePrefix=testresults_${{ runner.os }}" --collect "XPlat Code Coverage" diff --git a/.github/workflows/device-tests-android.yml b/.github/workflows/device-tests-android.yml index e4a7dcd448..ea9776f15d 100644 --- a/.github/workflows/device-tests-android.yml +++ b/.github/workflows/device-tests-android.yml @@ -11,7 +11,12 @@ on: jobs: build: + name: Build (${{ matrix.tfm }}) runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + tfm: [net8.0, net9.0] env: DOTNET_CLI_TELEMETRY_OPTOUT: 1 DOTNET_NOLOGO: 1 @@ -32,18 +37,27 @@ jobs: uses: ./.github/actions/buildnative - name: Build Android Test App - run: pwsh ./scripts/device-test.ps1 android -Build + run: pwsh ./scripts/device-test.ps1 android -Build -Tfm ${{ matrix.tfm }} + + - name: Upload Android Test App (net8.0) + if: matrix.tfm == 'net8.0' + uses: actions/upload-artifact@v4 + with: + name: device-test-android-net8.0 + if-no-files-found: error + path: test/Sentry.Maui.Device.TestApp/bin/Release/net8.0-android/android-x64/io.sentry.dotnet.maui.device.testapp-Signed.apk - - name: Upload Android Test App + - name: Upload Android Test App (net9.0) + if: matrix.tfm == 'net9.0' uses: actions/upload-artifact@v4 with: - name: device-test-android + name: device-test-android-net9.0 if-no-files-found: error - path: test/Sentry.Maui.Device.TestApp/bin/Release/net8.0-android34.0/android-x64/io.sentry.dotnet.maui.device.testapp-Signed.apk + path: test/Sentry.Maui.Device.TestApp/bin/Release/net9.0-android/android-x64/io.sentry.dotnet.maui.device.testapp-Signed.apk android: needs: [build] - name: Run Android API-${{ matrix.api-level }} Test + name: Run Android API-${{ matrix.api-level }} Test (${{ matrix.tfm }}) # Requires a "larger runner", for nested virtualization support runs-on: ubuntu-latest-4-cores @@ -51,6 +65,7 @@ jobs: strategy: fail-fast: false matrix: + tfm: [net8.0, net9.0] # We run against both an older and a newer API api-level: [27, 33] env: @@ -70,7 +85,7 @@ jobs: - name: Download test app artifact uses: actions/download-artifact@v4 with: - name: device-test-android + name: device-test-android-${{ matrix.tfm }} path: bin - name: Setup Gradle @@ -78,7 +93,6 @@ jobs: # Cached AVD setup per https://github.com/ReactiveCircus/android-emulator-runner/blob/main/README.md - - name: Run Tests timeout-minutes: 40 uses: reactivecircus/android-emulator-runner@1dcd0090116d15e7c562f8db72807de5e036a4ed # Tag: v2.34.0 @@ -92,11 +106,11 @@ jobs: disk-size: 4096M emulator-options: -no-snapshot-save -no-window -accel on -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none disable-animations: false - script: pwsh scripts/device-test.ps1 android -Run + script: pwsh scripts/device-test.ps1 android -Run -Tfm ${{ matrix.tfm }} - name: Upload results if: success() || failure() uses: actions/upload-artifact@v4 with: - name: device-test-android-${{ matrix.api-level }}-results + name: device-test-android-${{ matrix.api-level }}-${{ matrix.tfm }}-results path: test_output diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f1818da93..9d8943e55d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,18 +2,19 @@ ## Unreleased +### Features + +- Exception.HResult is now included in the mechanism data for all exceptions ([#4029](https://github.com/getsentry/sentry-dotnet/pull/4029)) + ### Fixes +- Fixed symbolication and source context for net9.0-android ([#4033](https://github.com/getsentry/sentry-dotnet/pull/4033)) - Single quotes added to the release name when using MS Build to create Sentry releases on Windows ([#4015](https://github.com/getsentry/sentry-dotnet/pull/4015)) - Target `net9.0` on Sentry.Google.Cloud.Functions to avoid conflict with Sentry.AspNetCore ([#4039](https://github.com/getsentry/sentry-dotnet/pull/4039)) - Changed default value for `SentryOptions.EnableAppHangTrackingV2` to `false` ([#4042](https://github.com/getsentry/sentry-dotnet/pull/4042)) - Missing MAUI `Shell` navigation breadcrumbs on iOS ([#4006](https://github.com/getsentry/sentry-dotnet/pull/4006)) - Prevent application crashes when capturing screenshots on iOS ([#4069](https://github.com/getsentry/sentry-dotnet/pull/4069)) -### Features - -- Exception.HResult is now included in the mechanism data for all exceptions ([#4029](https://github.com/getsentry/sentry-dotnet/pull/4029)) - ### Dependencies - Bump Native SDK from v0.8.1 to v0.8.2 ([#4050](https://github.com/getsentry/sentry-dotnet/pull/4050)) diff --git a/benchmarks/Sentry.Benchmarks/Sentry.Benchmarks.csproj b/benchmarks/Sentry.Benchmarks/Sentry.Benchmarks.csproj index 6cf009ec38..bdb8ed918a 100644 --- a/benchmarks/Sentry.Benchmarks/Sentry.Benchmarks.csproj +++ b/benchmarks/Sentry.Benchmarks/Sentry.Benchmarks.csproj @@ -9,7 +9,7 @@ - + diff --git a/scripts/device-test.ps1 b/scripts/device-test.ps1 index 6812c42dff..c9e03ffdd8 100644 --- a/scripts/device-test.ps1 +++ b/scripts/device-test.ps1 @@ -5,7 +5,8 @@ param( [String] $Platform, [Switch] $Build, - [Switch] $Run + [Switch] $Run, + [String] $Tfm ) Set-StrictMode -Version latest @@ -21,13 +22,16 @@ $CI = Test-Path env:CI Push-Location $PSScriptRoot/.. try { - $tfm = 'net8.0-' + if (!$Tfm) + { + $Tfm = 'net8.0' + } $arch = (!$IsWindows -and $(uname -m) -eq 'arm64') ? 'arm64' : 'x64' if ($Platform -eq 'android') { - $tfm += 'android34.0' + $Tfm += '-android' $group = 'android' - $buildDir = $CI ? 'bin' : "test/Sentry.Maui.Device.TestApp/bin/Release/$tfm/android-$arch" + $buildDir = $CI ? 'bin' : "test/Sentry.Maui.Device.TestApp/bin/Release/$Tfm/android-$arch" $arguments = @( '--app', "$buildDir/io.sentry.dotnet.maui.device.testapp-Signed.apk", '--package-name', 'io.sentry.dotnet.maui.device.testapp', @@ -43,11 +47,11 @@ try } elseif ($Platform -eq 'ios') { - $tfm += 'ios17.0' + $Tfm += '-ios' $group = 'apple' # Always use x64 on iOS, since arm64 doesn't support JIT, which is required for tests using NSubstitute $arch = 'x64' - $buildDir = "test/Sentry.Maui.Device.TestApp/bin/Release/$tfm/iossimulator-$arch" + $buildDir = "test/Sentry.Maui.Device.TestApp/bin/Release/$Tfm/iossimulator-$arch" $envValue = $CI ? 'true' : 'false' $arguments = @( '--app', "$buildDir/Sentry.Maui.Device.TestApp.app", @@ -60,7 +64,7 @@ try if ($Build) { # We disable AOT for device tests: https://github.com/nsubstitute/NSubstitute/issues/834 - dotnet build -f $tfm -c Release -p:EnableAot=false -p:NoSymbolStrip=true test/Sentry.Maui.Device.TestApp + dotnet build -f $Tfm -c Release -p:EnableAot=false -p:NoSymbolStrip=true test/Sentry.Maui.Device.TestApp if ($LASTEXITCODE -ne 0) { throw 'Failed to build Sentry.Maui.Device.TestApp' diff --git a/src/Sentry.Android.AssemblyReader/AndroidAssemblyReader.cs b/src/Sentry.Android.AssemblyReader/AndroidAssemblyReader.cs index 746b11cbe2..addd6633ff 100644 --- a/src/Sentry.Android.AssemblyReader/AndroidAssemblyReader.cs +++ b/src/Sentry.Android.AssemblyReader/AndroidAssemblyReader.cs @@ -17,69 +17,4 @@ public void Dispose() { ZipArchive.Dispose(); } - - protected PEReader CreatePEReader(string assemblyName, MemoryStream inputStream) - { - var decompressedStream = TryDecompressLZ4(assemblyName, inputStream); - - // Use the decompressed stream, or if null, i.e. it wasn't compressed, use the original. - return new PEReader(decompressedStream ?? inputStream); - } - - /// - /// The DLL may be LZ4 compressed, see https://github.com/xamarin/xamarin-android/pull/4686 - /// The format is: - /// [ 4 byte magic header ] (XALZ) - /// [ 4 byte header index ] - /// [ 4 byte uncompressed payload length ] - /// [rest: lz4 compressed payload] - /// - /// - private Stream? TryDecompressLZ4(string assemblyName, MemoryStream inputStream) - { - const uint compressedDataMagic = 0x5A4C4158; // 'XALZ', little-endian - const int payloadOffset = 12; - var reader = new BinaryReader(inputStream); - if (reader.ReadUInt32() != compressedDataMagic) - { - // Restore the input stream to the beginning if we're not decompressing. - inputStream.Position = 0; - return null; - } - reader.ReadUInt32(); // ignore descriptor index, we don't need it - var decompressedLength = reader.ReadInt32(); - Debug.Assert(inputStream.Position == payloadOffset); - var inputLength = (int)(inputStream.Length - payloadOffset); - - Logger?.Invoke("Decompressing assembly ({0} bytes uncompressed) using LZ4", decompressedLength); - - var outputStream = new MemoryStream(decompressedLength); - - // We're writing to the underlying array manually, so we need to set the length. - outputStream.SetLength(decompressedLength); - var outputBuffer = outputStream.GetBuffer(); - - var inputBuffer = inputStream is MemorySlice slice ? slice.FullBuffer : inputStream.GetBuffer(); - var offset = inputStream is MemorySlice memorySlice ? memorySlice.Offset + payloadOffset : payloadOffset; - var decoded = LZ4Codec.Decode(inputBuffer, offset, inputLength, outputBuffer, 0, decompressedLength); - if (decoded != decompressedLength) - { - throw new Exception($"Failed to decompress LZ4 data of assembly {assemblyName} - decoded {decoded} instead of expected {decompressedLength} bytes"); - } - return outputStream; - } - - // Allows consumer to access the underlying buffer even if the MemoryStream is created as a slice over another. - // Plain MemoryStream would throw "MemoryStream's internal buffer cannot be accessed." - protected class MemorySlice : MemoryStream - { - public readonly int Offset; - public readonly byte[] FullBuffer; - - public MemorySlice(MemoryStream other, int offset, int size) : base(other.GetBuffer(), offset, size, writable: false) - { - Offset = offset; - FullBuffer = other.GetBuffer(); - } - } } diff --git a/src/Sentry.Android.AssemblyReader/AndroidAssemblyReaderFactory.cs b/src/Sentry.Android.AssemblyReader/AndroidAssemblyReaderFactory.cs index 399f8622e3..62670899a9 100644 --- a/src/Sentry.Android.AssemblyReader/AndroidAssemblyReaderFactory.cs +++ b/src/Sentry.Android.AssemblyReader/AndroidAssemblyReaderFactory.cs @@ -1,3 +1,6 @@ +using Sentry.Android.AssemblyReader.V1; +using Sentry.Android.AssemblyReader.V2; + namespace Sentry.Android.AssemblyReader; /// @@ -15,15 +18,29 @@ public static class AndroidAssemblyReaderFactory public static IAndroidAssemblyReader Open(string apkPath, IList supportedAbis, DebugLogger? logger = null) { logger?.Invoke("Opening APK: {0}", apkPath); - var zipArchive = ZipFile.Open(apkPath, ZipArchiveMode.Read); +#if NET9_0 + logger?.Invoke("Reading files using V2 APK layout."); + if (AndroidAssemblyStoreReaderV2.TryReadStore(apkPath, supportedAbis, logger, out var readerV2)) + { + logger?.Invoke("APK uses AssemblyStore V2"); + return readerV2; + } + + logger?.Invoke("APK doesn't use AssemblyStore"); + return new AndroidAssemblyDirectoryReaderV2(apkPath, supportedAbis, logger); +#else + logger?.Invoke("Reading files using V1 APK layout."); + + var zipArchive = ZipFile.OpenRead(apkPath); if (zipArchive.GetEntry("assemblies/assemblies.manifest") is not null) { - logger?.Invoke("APK uses AssemblyStore"); - return new AndroidAssemblyStoreReader(zipArchive, supportedAbis, logger); + logger?.Invoke("APK uses AssemblyStore V1"); + return new AndroidAssemblyStoreReaderV1(zipArchive, supportedAbis, logger); } logger?.Invoke("APK doesn't use AssemblyStore"); - return new AndroidAssemblyDirectoryReader(zipArchive, supportedAbis, logger); + return new AndroidAssemblyDirectoryReaderV1(zipArchive, supportedAbis, logger); +#endif } } diff --git a/src/Sentry.Android.AssemblyReader/ArchiveUtils.cs b/src/Sentry.Android.AssemblyReader/ArchiveUtils.cs new file mode 100644 index 0000000000..0613eed1f9 --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ArchiveUtils.cs @@ -0,0 +1,72 @@ +namespace Sentry.Android.AssemblyReader; + +internal static class ArchiveUtils +{ + internal static PEReader CreatePEReader(string assemblyName, MemoryStream inputStream, DebugLogger? logger) + { + var decompressedStream = TryDecompressLZ4(assemblyName, inputStream, logger); // Returns null if not compressed + return new PEReader(decompressedStream ?? inputStream); + } + + internal static MemoryStream Extract(this ZipArchiveEntry zipEntry) + { + var memStream = new MemoryStream((int)zipEntry.Length); + using var zipStream = zipEntry.Open(); + zipStream.CopyTo(memStream); + memStream.Position = 0; + return memStream; + } + + /// + /// The DLL may be LZ4 compressed, see https://github.com/xamarin/xamarin-android/pull/4686 + /// In particular: https://github.com/dotnet/android/blob/44c5c30d3da692c54ca27d4a41571ef20b73670f/src/Xamarin.Android.Build.Tasks/Utilities/AssemblyCompression.cs#L96-L104 + /// The format is: + /// [ 4 byte magic header ] (XALZ) + /// [ 4 byte descriptor header index ] + /// [ 4 byte uncompressed payload length ] + /// [rest: lz4 compressed payload] + /// + /// + private static Stream? TryDecompressLZ4(string assemblyName, MemoryStream inputStream, DebugLogger? logger) + { + const uint compressedDataMagic = 0x5A4C4158; // 'XALZ', little-endian + const int payloadOffset = 12; + var reader = new BinaryReader(inputStream); + if (reader.ReadUInt32() != compressedDataMagic) + { + // Restore the input stream to the beginning if we're not decompressing. + inputStream.Position = 0; + return null; + } + reader.ReadUInt32(); // ignore descriptor index, we don't need it + var decompressedLength = reader.ReadInt32(); + Debug.Assert(inputStream.Position == payloadOffset); + var inputLength = (int)(inputStream.Length - payloadOffset); + + logger?.Invoke("Decompressing assembly ({0} bytes uncompressed) using LZ4", decompressedLength); + + var outputStream = new MemoryStream(decompressedLength); + + // We're writing to the underlying array manually, so we need to set the length. + outputStream.SetLength(decompressedLength); + var outputBuffer = outputStream.GetBuffer(); + + var inputBuffer = inputStream is MemorySlice slice ? slice.FullBuffer : inputStream.GetBuffer(); + var offset = inputStream is MemorySlice memorySlice ? memorySlice.Offset + payloadOffset : payloadOffset; + var decoded = LZ4Codec.Decode(inputBuffer, offset, inputLength, outputBuffer, 0, decompressedLength); + if (decoded != decompressedLength) + { + throw new Exception($"Failed to decompress LZ4 data of assembly {assemblyName} - decoded {decoded} instead of expected {decompressedLength} bytes"); + } + return outputStream; + } + + // Allows consumer to access the underlying buffer even if the MemoryStream is created as a slice over another. + // Plain MemoryStream would throw "MemoryStream's internal buffer cannot be accessed." + internal class MemorySlice(MemoryStream other, int offset, int size) + : MemoryStream(other.GetBuffer(), offset, size, writable: false) + { + public readonly int Offset = offset; + public readonly byte[] FullBuffer = other.GetBuffer(); + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/ATTRIBUTION.txt b/src/Sentry.Android.AssemblyReader/ELFSharp/ATTRIBUTION.txt new file mode 100644 index 0000000000..1c47bdece1 --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/ATTRIBUTION.txt @@ -0,0 +1,76 @@ +The code in this subdirectory was adapted from: +https://github.com/konrad-kruczynski/elfsharp/tree/0c859b7b4b8c73bd1194021672681586c1b7139e + +The only changes to the code are: +- Public members have been made internal +- Nullability warnings have been disabled + +The original license is as follows: + +Copyright (c) 2011 Konrad Kruczyński and other contributors + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +This software uses ELF machine constants from the LLVM projects, whose license +is provided below: + +============================================================================== +LLVM Release License +============================================================================== +University of Illinois/NCSA +Open Source License + +Copyright (c) 2003-2010 University of Illinois at Urbana-Champaign. +All rights reserved. + +Developed by: + + LLVM Team + + University of Illinois at Urbana-Champaign + + http://llvm.org + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal with +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimers. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimers in the + documentation and/or other materials provided with the distribution. + + * Neither the names of the LLVM Team, University of Illinois at + Urbana-Champaign, nor the names of its contributors may be used to + endorse or promote products derived from this Software without specific + prior written permission. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE +SOFTWARE. diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Class.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Class.cs new file mode 100644 index 0000000000..ca60eb04f5 --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Class.cs @@ -0,0 +1,9 @@ +namespace ELFSharp.ELF +{ + internal enum Class + { + Bit32, + Bit64, + NotELF + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Consts.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Consts.cs new file mode 100644 index 0000000000..83c44d49c1 --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Consts.cs @@ -0,0 +1,11 @@ +namespace ELFSharp.ELF +{ + internal static class Consts + { + public const string ObjectsStringTableName = ".strtab"; + public const string DynamicStringTableName = ".dynstr"; + public const int SymbolEntrySize32 = 16; + public const int SymbolEntrySize64 = 24; + public const int MinimalELFSize = 16; + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/ELF.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/ELF.cs new file mode 100644 index 0000000000..19eb3dfbda --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/ELF.cs @@ -0,0 +1,411 @@ +using ELFSharp.ELF.Sections; +using ELFSharp.ELF.Segments; +using ELFSharp.Utilities; +using SectionHeader = ELFSharp.ELF.Sections.SectionHeader; + +#nullable disable + +namespace ELFSharp.ELF +{ + internal sealed class ELF : IELF where T : struct + { + private const int SectionNameNotUniqueMarker = -1; + private readonly bool ownsStream; + + private readonly SimpleEndianessAwareReader reader; + private Stage currentStage; + private StringTable dynamicStringTable; + private StringTable objectsStringTable; + private uint sectionHeaderEntryCount; + private ushort sectionHeaderEntrySize; + private long sectionHeaderOffset; + private List sectionHeaders; + private Dictionary sectionIndicesByName; + private List> sections; + private ushort segmentHeaderEntryCount; + private ushort segmentHeaderEntrySize; + private long segmentHeaderOffset; + private List> segments; + private uint stringTableIndex; + + internal ELF(Stream stream, bool ownsStream) + { + this.ownsStream = ownsStream; + reader = ObtainEndianessAwareReader(stream); + ReadFields(); + ReadStringTable(); + ReadSections(); + ReadSegmentHeaders(); + } + + public T EntryPoint { get; private set; } + + public T MachineFlags { get; private set; } + + public IReadOnlyList> Segments => segments.AsReadOnly(); + + public IReadOnlyList> Sections => sections.AsReadOnly(); + + public Endianess Endianess { get; private set; } + + public Class Class { get; private set; } + + public FileType Type { get; private set; } + + public Machine Machine { get; private set; } + + public bool HasSegmentHeader => segmentHeaderOffset != 0; + + public bool HasSectionHeader => sectionHeaderOffset != 0; + + public bool HasSectionsStringTable => stringTableIndex != 0; + + IReadOnlyList IELF.Segments => Segments; + + public IStringTable SectionsStringTable { get; private set; } + + IEnumerable IELF.GetSections() + { + return Sections.Where(x => x is TSectionType).Cast(); + } + + IReadOnlyList IELF.Sections => Sections; + + bool IELF.TryGetSection(string name, out ISection section) + { + var result = TryGetSection(name, out var concreteSection); + section = concreteSection; + return result; + } + + ISection IELF.GetSection(string name) + { + return GetSection(name); + } + + bool IELF.TryGetSection(int index, out ISection section) + { + var result = TryGetSection(index, out var sectionConcrete); + section = sectionConcrete; + return result; + } + + ISection IELF.GetSection(int index) + { + return GetSection(index); + } + + public void Dispose() + { + if (ownsStream) + reader.BaseStream.Dispose(); + } + + public IEnumerable GetSections() where TSection : Section + { + return Sections.Where(x => x is TSection).Cast(); + } + + public bool TryGetSection(string name, out Section section) + { + return TryGetSectionInner(name, out section) == GetSectionResult.Success; + } + + public Section GetSection(string name) + { + var result = TryGetSectionInner(name, out var section); + + switch (result) + { + case GetSectionResult.Success: + return section; + case GetSectionResult.SectionNameNotUnique: + throw new InvalidOperationException("Given section name is not unique, order is ambigous."); + case GetSectionResult.NoSectionsStringTable: + throw new InvalidOperationException( + "Given ELF does not contain section header string table, therefore names of sections cannot be obtained."); + case GetSectionResult.NoSuchSection: + throw new KeyNotFoundException(string.Format("Given section {0} could not be found in the file.", + name)); + default: + throw new InvalidOperationException("Unhandled error."); + } + } + + public Section GetSection(int index) + { + var result = TryGetSectionInner(index, out var section); + switch (result) + { + case GetSectionResult.Success: + return section; + case GetSectionResult.NoSuchSection: + throw new IndexOutOfRangeException(string.Format("Given section index {0} is out of range.", + index)); + default: + throw new ArgumentOutOfRangeException(); + } + } + + public override string ToString() + { + return string.Format("[ELF: Endianess={0}, Class={1}, Type={2}, Machine={3}, EntryPoint=0x{4:X}, " + + "NumberOfSections={5}, NumberOfSegments={6}]", Endianess, Class, Type, Machine, + EntryPoint, sections.Count, segments.Count); + } + + private bool TryGetSection(int index, out Section section) + { + return TryGetSectionInner(index, out section) == GetSectionResult.Success; + } + + private Section GetSectionFromSectionHeader(SectionHeader header) + { + Section returned; + switch (header.Type) + { + case SectionType.Null: + goto default; + case SectionType.ProgBits: + returned = new ProgBitsSection(header, reader); + break; + case SectionType.SymbolTable: + returned = new SymbolTable(header, reader, objectsStringTable, this); + break; + case SectionType.StringTable: + returned = new StringTable(header, reader); + break; + case SectionType.RelocationAddends: + goto default; + case SectionType.HashTable: + goto default; + case SectionType.Dynamic: + returned = new DynamicSection(header, reader, this); + break; + case SectionType.Note: + returned = new NoteSection(header, reader); + break; + case SectionType.NoBits: + goto default; + case SectionType.Relocation: + goto default; + case SectionType.Shlib: + goto default; + case SectionType.DynamicSymbolTable: + returned = new SymbolTable(header, reader, dynamicStringTable, this); + break; + default: + returned = new Section(header, reader); + break; + } + + return returned; + } + + private void ReadSegmentHeaders() + { + segments = new List>(segmentHeaderEntryCount); + + for (var i = 0u; i < segmentHeaderEntryCount; i++) + { + var seekTo = segmentHeaderOffset + i * segmentHeaderEntrySize; + reader.BaseStream.Seek(seekTo, SeekOrigin.Begin); + var segmentType = Segment.ProbeType(reader); + + Segment segment; + if (segmentType == SegmentType.Note) + segment = new NoteSegment(segmentHeaderOffset + i * segmentHeaderEntrySize, Class, reader); + else + segment = new Segment(segmentHeaderOffset + i * segmentHeaderEntrySize, Class, reader); + + segments.Add(segment); + } + } + + private void ReadSections() + { + sectionHeaders = new List(); + if (HasSectionsStringTable) + sectionIndicesByName = new Dictionary(); + + for (var i = 0; i < sectionHeaderEntryCount; i++) + { + var header = ReadSectionHeader(i); + sectionHeaders.Add(header); + if (HasSectionsStringTable) + { + var name = header.Name; + if (!sectionIndicesByName.ContainsKey(name)) + sectionIndicesByName.Add(name, i); + else + sectionIndicesByName[name] = SectionNameNotUniqueMarker; + } + } + + sections = new List>(Enumerable.Repeat>( + null, + sectionHeaders.Count + )); + FindStringTables(); + for (var i = 0; i < sectionHeaders.Count; i++) + TouchSection(i); + sectionHeaders = null; + currentStage = Stage.AfterSectionsAreRead; + } + + private void TouchSection(int index) + { + if (currentStage != Stage.Initalizing) + throw new InvalidOperationException("TouchSection invoked in improper state."); + if (sections[index] != null) + return; + var section = GetSectionFromSectionHeader(sectionHeaders[index]); + sections[index] = section; + } + + private void FindStringTables() + { + TryGetSection(Consts.ObjectsStringTableName, out var section); + objectsStringTable = (StringTable)section; + TryGetSection(Consts.DynamicStringTableName, out section); + + // It might happen that the section is not really available, represented as a NoBits one. + dynamicStringTable = section as StringTable; + } + + private void ReadStringTable() + { + if (!HasSectionHeader || !HasSectionsStringTable) + return; + + var header = ReadSectionHeader(checked((int)stringTableIndex)); + if (header.Type != SectionType.StringTable) + throw new InvalidOperationException( + "Given index of section header does not point at string table which was expected."); + + SectionsStringTable = new StringTable(header, reader); + } + + private SectionHeader ReadSectionHeader(int index, bool ignoreUpperLimit = false) + { + if (index < 0 || (!ignoreUpperLimit && index >= sectionHeaderEntryCount)) + throw new ArgumentOutOfRangeException(nameof(index)); + + reader.BaseStream.Seek( + sectionHeaderOffset + index * sectionHeaderEntrySize, + SeekOrigin.Begin + ); + + return new SectionHeader(reader, Class, SectionsStringTable); + } + + private SimpleEndianessAwareReader ObtainEndianessAwareReader(Stream stream) + { + var reader = new BinaryReader(stream); + reader.ReadBytes(4); // ELF magic + var classByte = reader.ReadByte(); + + Class = classByte switch + { + 1 => Class.Bit32, + 2 => Class.Bit64, + _ => throw new ArgumentException($"Given ELF file is of unknown class {classByte}.") + }; + + var endianessByte = reader.ReadByte(); + + Endianess = endianessByte switch + { + 1 => Endianess.LittleEndian, + 2 => Endianess.BigEndian, + _ => throw new ArgumentException($"Given ELF file uses unknown endianess {endianessByte}.") + }; + + reader.ReadBytes(10); // padding bytes of section e_ident + return new SimpleEndianessAwareReader(stream, Endianess); + } + + private void ReadFields() + { + Type = (FileType)reader.ReadUInt16(); + Machine = (Machine)reader.ReadUInt16(); + var version = reader.ReadUInt32(); + if (version != 1) + throw new ArgumentException(string.Format( + "Given ELF file is of unknown version {0}.", + version + )); + EntryPoint = (Class == Class.Bit32 ? reader.ReadUInt32() : reader.ReadUInt64()).To(); + // TODO: assertions for (u)longs + segmentHeaderOffset = Class == Class.Bit32 ? reader.ReadUInt32() : reader.ReadInt64(); + sectionHeaderOffset = Class == Class.Bit32 ? reader.ReadUInt32() : reader.ReadInt64(); + MachineFlags = reader.ReadUInt32().To(); // TODO: always 32bit? + reader.ReadUInt16(); // elf header size + segmentHeaderEntrySize = reader.ReadUInt16(); + segmentHeaderEntryCount = reader.ReadUInt16(); + sectionHeaderEntrySize = reader.ReadUInt16(); + sectionHeaderEntryCount = reader.ReadUInt16(); + stringTableIndex = reader.ReadUInt16(); + + // If the number of sections is greater than or equal to SHN_LORESERVE (0xff00), this member has the + // value zero and the actual number of section header table entries is contained in the sh_size field + // of the section header at index 0. (Otherwise, the sh_size member of the initial entry contains 0.) + if (sectionHeaderEntryCount == 0) + { + var firstSectionHeader = ReadSectionHeader(0, true); + sectionHeaderEntryCount = checked((uint)firstSectionHeader.Size); + + // If the index of the string table is larger than or equal to SHN_LORESERVE (0xff00), this member holds SHN_XINDEX (0xffff) + // and the real index of the section name string table section is held in the sh_link member of the initial entry in section + // header table. Otherwise, the sh_link member of the initial entry in section header table contains the value zero. + if (stringTableIndex == 0xffff) + stringTableIndex = checked(firstSectionHeader.Link); + } + } + + private GetSectionResult TryGetSectionInner(string name, out Section section) + { + section = default; + if (!HasSectionsStringTable) + return GetSectionResult.NoSectionsStringTable; + if (!sectionIndicesByName.TryGetValue(name, out var index)) + return GetSectionResult.NoSuchSection; + if (index == SectionNameNotUniqueMarker) + return GetSectionResult.SectionNameNotUnique; + return TryGetSectionInner(index, out section); + } + + private GetSectionResult TryGetSectionInner(int index, out Section section) + { + section = default; + if (index >= sections.Count) + return GetSectionResult.NoSuchSection; + if (sections[index] != null) + { + section = sections[index]; + return GetSectionResult.Success; + } + + if (currentStage != Stage.Initalizing) + throw new InvalidOperationException( + "Assert not met: null section by proper index in not initializing stage."); + TouchSection(index); + section = sections[index]; + return GetSectionResult.Success; + } + + private enum Stage + { + Initalizing, + AfterSectionsAreRead + } + + private enum GetSectionResult + { + Success, + SectionNameNotUnique, + NoSectionsStringTable, + NoSuchSection + } + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/ELFReader.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/ELFReader.cs new file mode 100644 index 0000000000..13521bb3ae --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/ELFReader.cs @@ -0,0 +1,116 @@ +using System; +using System.IO; +using System.Text; + +#nullable disable + +namespace ELFSharp.ELF +{ + internal static class ELFReader + { + private const string NotELFMessage = "Given stream is not a proper ELF file."; + + private static readonly byte[] Magic = + { + 0x7F, + 0x45, + 0x4C, + 0x46 + }; // 0x7F 'E' 'L' 'F' + + public static IELF Load(Stream stream, bool shouldOwnStream) + { + if (!TryLoad(stream, shouldOwnStream, out var elf)) + throw new ArgumentException(NotELFMessage); + + return elf; + } + + public static IELF Load(string fileName) + { + return Load(File.OpenRead(fileName), true); + } + + public static bool TryLoad(Stream stream, bool shouldOwnStream, out IELF elf) + { + switch (CheckELFType(stream)) + { + case Class.Bit32: + elf = new ELF(stream, shouldOwnStream); + return true; + case Class.Bit64: + elf = new ELF(stream, shouldOwnStream); + return true; + default: + elf = null; + stream.Close(); + return false; + } + } + + public static bool TryLoad(string fileName, out IELF elf) + { + return TryLoad(File.OpenRead(fileName), true, out elf); + } + + public static Class CheckELFType(Stream stream) + { + var currentStreamPosition = stream.Position; + + if (stream.Length < Consts.MinimalELFSize) + return Class.NotELF; + + using (var reader = new BinaryReader(stream, Encoding.UTF8, true)) + { + var magic = reader.ReadBytes(4); + for (var i = 0; i < 4; i++) + if (magic[i] != Magic[i]) + return Class.NotELF; + + var value = reader.ReadByte(); + stream.Position = currentStreamPosition; + return value == 1 ? Class.Bit32 : Class.Bit64; + } + } + + public static Class CheckELFType(string fileName) + { + using (var stream = File.OpenRead(fileName)) + { + return CheckELFType(stream); + } + } + + public static ELF Load(Stream stream, bool shouldOwnStream) where T : struct + { + if (CheckELFType(stream) == Class.NotELF) + throw new ArgumentException(NotELFMessage); + + return new ELF(stream, shouldOwnStream); + } + + public static ELF Load(string fileName) where T : struct + { + return Load(File.OpenRead(fileName), true); + } + + public static bool TryLoad(Stream stream, bool shouldOwnStream, out ELF elf) where T : struct + { + switch (CheckELFType(stream)) + { + case Class.Bit32: + case Class.Bit64: + elf = new ELF(stream, shouldOwnStream); + return true; + default: + elf = null; + return false; + } + } + + public static bool TryLoad(string fileName, out ELF elf) where T : struct + { + return TryLoad(File.OpenRead(fileName), true, out elf); + } + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/FileType.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/FileType.cs new file mode 100644 index 0000000000..9e04bf8ab9 --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/FileType.cs @@ -0,0 +1,11 @@ +namespace ELFSharp.ELF +{ + internal enum FileType : ushort + { + None = 0, + Relocatable, + Executable, + SharedObject, + Core + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/IELF.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/IELF.cs new file mode 100644 index 0000000000..9310425cdd --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/IELF.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using ELFSharp.ELF.Sections; +using ELFSharp.ELF.Segments; + +namespace ELFSharp.ELF +{ + internal interface IELF : IDisposable + { + public Endianess Endianess { get; } + public Class Class { get; } + public FileType Type { get; } + public Machine Machine { get; } + public bool HasSegmentHeader { get; } + public bool HasSectionHeader { get; } + public bool HasSectionsStringTable { get; } + public IReadOnlyList Segments { get; } + public IStringTable SectionsStringTable { get; } + public IReadOnlyList Sections { get; } + public IEnumerable GetSections() where T : ISection; + public bool TryGetSection(string name, out ISection section); + public ISection GetSection(string name); + public bool TryGetSection(int index, out ISection section); + public ISection GetSection(int index); + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Machine.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Machine.cs new file mode 100644 index 0000000000..42e823288a --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Machine.cs @@ -0,0 +1,169 @@ +/* + * This file is based on LLVM's elf.h file. You can find its license + * in the LICENSE file. + * + */ + +namespace ELFSharp.ELF +{ + internal enum Machine : ushort + { + None = 0, // No machine + M32 = 1, // AT&T WE 32100 + SPARC = 2, // SPARC + Intel386 = 3, // Intel 386 + M68K = 4, // Motorola 68000 + M88K = 5, // Motorola 88000 + Intel486 = 6, // Intel 486 (deprecated) + Intel860 = 7, // Intel 80860 + MIPS = 8, // MIPS R3000 + S370 = 9, // IBM System/370 + MIPSRS3LE = 10, // MIPS RS3000 Little-endian + PARISC = 15, // Hewlett-Packard PA-RISC + VPP500 = 17, // Fujitsu VPP500 + SPARC32Plus = 18, // Enhanced instruction set SPARC + Intel960 = 19, // Intel 80960 + PPC = 20, // PowerPC + PPC64 = 21, // PowerPC64 + S390 = 22, // IBM System/390 + SPU = 23, // IBM SPU/SPC + V800 = 36, // NEC V800 + FR20 = 37, // Fujitsu FR20 + RH32 = 38, // TRW RH-32 + RCE = 39, // Motorola RCE + ARM = 40, // ARM + Alpha = 41, // DEC Alpha + SuperH = 42, // Hitachi SH + SPARCv9 = 43, // SPARC V9 + TriCore = 44, // Siemens TriCore + ARC = 45, // Argonaut RISC Core + H8300 = 46, // Hitachi H8/300 + H8300H = 47, // Hitachi H8/300H + H8S = 48, // Hitachi H8S + H8500 = 49, // Hitachi H8/500 + IA64 = 50, // Intel IA-64 processor architecture + MIPSX = 51, // Stanford MIPS-X + ColdFire = 52, // Motorola ColdFire + M68HC12 = 53, // Motorola M68HC12 + MMA = 54, // Fujitsu MMA Multimedia Accelerator + PCP = 55, // Siemens PCP + NCPU = 56, // Sony nCPU embedded RISC processor + NDR1 = 57, // Denso NDR1 microprocessor + StarCore = 58, // Motorola Star*Core processor + ME16 = 59, // Toyota ME16 processor + ST100 = 60, // STMicroelectronics ST100 processor + TinyJ = 61, // Advanced Logic Corp. TinyJ embedded processor family + AMD64 = 62, // AMD x86-64 architecture + PDSP = 63, // Sony DSP Processor + PDP10 = 64, // Digital Equipment Corp. PDP-10 + PDP11 = 65, // Digital Equipment Corp. PDP-11 + FX66 = 66, // Siemens FX66 microcontroller + ST9PLUS = 67, // STMicroelectronics ST9+ 8/16 bit microcontroller + ST7 = 68, // STMicroelectronics ST7 8-bit microcontroller + M68HC16 = 69, // Motorola MC68HC16 Microcontroller + M68HC11 = 70, // Motorola MC68HC11 Microcontroller + M68HC08 = 71, // Motorola MC68HC08 Microcontroller + M68HC05 = 72, // Motorola MC68HC05 Microcontroller + SVX = 73, // Silicon Graphics SVx + ST19 = 74, // STMicroelectronics ST19 8-bit microcontroller + VAX = 75, // Digital VAX + CRIS = 76, // Axis Communications 32-bit embedded processor + Javelin = 77, // Infineon Technologies 32-bit embedded processor + FirePath = 78, // Element 14 64-bit DSP Processor + ZSP = 79, // LSI Logic 16-bit DSP Processor + MMIX = 80, // Donald Knuth's educational 64-bit processor + HUANY = 81, // Harvard University machine-independent object files + PRISM = 82, // SiTera Prism + AVR = 83, // Atmel AVR 8-bit microcontroller + FR30 = 84, // Fujitsu FR30 + D10V = 85, // Mitsubishi D10V + D30V = 86, // Mitsubishi D30V + V850 = 87, // NEC v850 + M32R = 88, // Mitsubishi M32R + MN10300 = 89, // Matsushita MN10300 + MN10200 = 90, // Matsushita MN10200 + PicoJava = 91, // picoJava + OpenRISC = 92, // OpenRISC 32-bit embedded processor + ARCompact = 93, // ARC International ARCompact processo + Xtensa = 94, // Tensilica Xtensa Architecture + VideoCore = 95, // Alphamosaic VideoCore processor + TMMGPP = 96, // Thompson Multimedia General Purpose Processor + NS32K = 97, // National Semiconductor 32000 series + TPC = 98, // Tenor Network TPC processor + SNP1k = 99, // Trebia SNP 1000 processor + ST200 = 100, // STMicroelectronics (www.st.com) ST200 + IP2K = 101, // Ubicom IP2xxx microcontroller family + MAX = 102, // MAX Processor + CompactRISC = 103, // National Semiconductor CompactRISC microprocessor + F2MC16 = 104, // Fujitsu F2MC16 + MSP430 = 105, // Texas Instruments embedded microcontroller msp430 + Blackfin = 106, // Analog Devices Blackfin (DSP) processor + S1C33 = 107, // S1C33 Family of Seiko Epson processors + SEP = 108, // Sharp embedded microprocessor + ArcaRISC = 109, // Arca RISC Microprocessor + UNICORE = 110, // Microprocessor series from PKU-Unity Ltd. and MPRC of Peking University + Excess = 111, // eXcess: 16/32/64-bit configurable embedded CPU + DXP = 112, // Icera Semiconductor Inc. Deep Execution Processor + AlteraNios2 = 113, // Altera Nios II soft-core processor + CRX = 114, // National Semiconductor CompactRISC CRX + XGATE = 115, // Motorola XGATE embedded processor + C166 = 116, // Infineon C16x/XC16x processor + M16C = 117, // Renesas M16C series microprocessors + DSPIC30F = 118, // Microchip Technology dsPIC30F Digital Signal + + // Controller + EngineRISC = 119, // Freescale Communication Engine RISC core + M32C = 120, // Renesas M32C series microprocessors + TSK3000 = 131, // Altium TSK3000 core + RS08 = 132, // Freescale RS08 embedded processor + SHARC = 133, // Analog Devices SHARC family of 32-bit DSP processors + ECOG2 = 134, // Cyan Technology eCOG2 microprocessor + Score7 = 135, // Sunplus S+core7 RISC processor + DSP24 = 136, // New Japan Radio (NJR) 24-bit DSP Processor + VideoCore3 = 137, // Broadcom VideoCore III processor + LatticeMico32 = 138, // RISC processor for Lattice FPGA architecture + SeikoEpsonC17 = 139, // Seiko Epson C17 family + TIC6000 = 140, // The Texas Instruments TMS320C6000 DSP family + TIC2000 = 141, // The Texas Instruments TMS320C2000 DSP family + TIC5500 = 142, // The Texas Instruments TMS320C55x DSP family + MMDSPPlus = 160, // STMicroelectronics 64bit VLIW Data Signal Processor + CypressM8C = 161, // Cypress M8C microprocessor + R32C = 162, // Renesas R32C series microprocessors + TriMedia = 163, // NXP Semiconductors TriMedia architecture family + Hexagon = 164, // Qualcomm Hexagon processor + Intel8051 = 165, // Intel 8051 and variants + STxP7x = 166, // STMicroelectronics STxP7x family of configurable and extensible RISC processors + NDS32 = 167, // Andes Technology compact code size embedded RISC processor family + ECOG1 = 168, // Cyan Technology eCOG1X family + ECOG1X = 168, // Cyan Technology eCOG1X family + MAXQ30 = 169, // Dallas Semiconductor MAXQ30 Core Micro-controllers + XIMO16 = 170, // New Japan Radio (NJR) 16-bit DSP Processor + MANIK = 171, // M2000 Reconfigurable RISC Microprocessor + CrayNV2 = 172, // Cray Inc. NV2 vector architecture + RX = 173, // Renesas RX family + METAG = 174, // Imagination Technologies META processor architecture + MCSTElbrus = 175, // MCST Elbrus general purpose hardware architecture + ECOG16 = 176, // Cyan Technology eCOG16 family + CR16 = 177, // National Semiconductor CompactRISC CR16 16-bit microprocessor + ETPU = 178, // Freescale Extended Time Processing Unit + SLE9X = 179, // Infineon Technologies SLE9X core + L10M = 180, // Intel L10M + K10M = 181, // Intel K10M + AArch64 = 183, // ARM AArch64 + AVR32 = 185, // Atmel Corporation 32-bit microprocessor family + STM8 = 186, // STMicroeletronics STM8 8-bit microcontroller + TILE64 = 187, // Tilera TILE64 multicore architecture family + TILEPro = 188, // Tilera TILEPro multicore architecture family + CUDA = 190, // NVIDIA CUDA architecture + TILEGx = 191, // Tilera TILE-Gx multicore architecture family + CloudShield = 192, // CloudShield architecture family + CoreA1st = 193, // KIPO-KAIST Core-A 1st generation processor family + CoreA2nd = 194, // KIPO-KAIST Core-A 2nd generation processor family + ARCompact2 = 195, // Synopsys ARCompact V2 + Open8 = 196, // Open8 8-bit RISC soft processor core + RL78 = 197, // Renesas RL78 family + VideoCore5 = 198, // Broadcom VideoCore V processor + R78KOR = 199, // Renesas 78KOR family + F56800EX = 200 // Freescale 56800EX Digital Signal Controller (DSC) + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/DynamicEntry.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/DynamicEntry.cs new file mode 100644 index 0000000000..64d2c50b6a --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/DynamicEntry.cs @@ -0,0 +1,27 @@ +#nullable disable + +namespace ELFSharp.ELF.Sections +{ + /// + /// Dynamic table entries are made up of a 32 bit or 64 bit "tag" + /// and a 32 bit or 64 bit union (val/pointer in 64 bit, val/pointer/offset in 32 bit). + /// See LLVM elf.h file for the C/C++ version. + /// + internal class DynamicEntry : IDynamicEntry + { + public DynamicEntry(T tagValue, T value) + { + Tag = (DynamicTag)tagValue.To(); + Value = value; + } + + public T Value { get; } + + public DynamicTag Tag { get; } + + public override string ToString() + { + return string.Format("{0} \t 0x{1:X}", Tag, Value); + } + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/DynamicSection.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/DynamicSection.cs new file mode 100644 index 0000000000..00c6ad3424 --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/DynamicSection.cs @@ -0,0 +1,53 @@ +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using ELFSharp.Utilities; + +#nullable disable + +namespace ELFSharp.ELF.Sections +{ + internal sealed class DynamicSection : Section, IDynamicSection where T : struct + { + private readonly ELF elf; + + private List> entries; + + internal DynamicSection(SectionHeader header, SimpleEndianessAwareReader reader, ELF elf) : base(header, + reader) + { + this.elf = elf; + ReadEntries(); + } + + public IEnumerable> Entries => new ReadOnlyCollection>(entries); + + IEnumerable IDynamicSection.Entries => entries; + + public override string ToString() + { + return string.Format("{0}: {2}, load @0x{4:X}, {5} entries", Name, NameIndex, Type, RawFlags, LoadAddress, + Entries.Count()); + } + + private void ReadEntries() + { + // "Kind-of" Bug: + // So, this winds up with "extra" DT_NULL entries for some executables. The issue + // is basically that sometimes the .dynamic section's size (and # of entries) per the + // header is higher than the actual # of entries. The extra space gets filled with null + // entries in all of the ELF files I tested, so we shouldn't end up with any 'incorrect' entries + // here unless someone is messing with the ELF structure. + + SeekToSectionBeginning(); + var entryCount = elf.Class == Class.Bit32 ? Header.Size / 8 : Header.Size / 16; + entries = new List>(); + + for (ulong i = 0; i < entryCount; i++) + if (elf.Class == Class.Bit32) + entries.Add(new DynamicEntry(Reader.ReadUInt32().To(), Reader.ReadUInt32().To())); + else if (elf.Class == Class.Bit64) + entries.Add(new DynamicEntry(Reader.ReadUInt64().To(), Reader.ReadUInt64().To())); + } + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/DynamicTag.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/DynamicTag.cs new file mode 100644 index 0000000000..716874df4c --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/DynamicTag.cs @@ -0,0 +1,64 @@ +namespace ELFSharp.ELF.Sections +{ + /// + /// This enum holds some of the possible values for the DynamicTag value (dropping platform + /// specific contents, such as MIPS flags.) + /// Values are coming from LLVM's elf.h headers. + /// File can be found in LLVM 3.8.1 source at: + /// ../include/llvm/support/elf.h + /// License of the original C code is LLVM license. + /// + internal enum DynamicTag : ulong + { + Null = 0, // Marks end of dynamic array. + Needed = 1, // String table offset of needed library. + PLTRelSz = 2, // Size of relocation entries in PLT. + PLTGOT = 3, // Address associated with linkage table. + Hash = 4, // Address of symbolic hash table. + StrTab = 5, // Address of dynamic string table. + SymTab = 6, // Address of dynamic symbol table. + RelA = 7, // Address of relocation table (Rela entries). + RelASz = 8, // Size of Rela relocation table. + RelAEnt = 9, // Size of a Rela relocation entry. + StrSz = 10, // Total size of the string table. + SymEnt = 11, // Size of a symbol table entry. + Init = 12, // Address of initialization function. + Fini = 13, // Address of termination function. + SoName = 14, // String table offset of a shared objects name. + RPath = 15, // String table offset of library search path. + Symbolic = 16, // Changes symbol resolution algorithm. + Rel = 17, // Address of relocation table (Rel entries). + RelSz = 18, // Size of Rel relocation table. + RelEnt = 19, // Size of a Rel relocation entry. + PLTRel = 20, // Type of relocation entry used for linking. + Debug = 21, // Reserved for debugger. + TextRel = 22, // Relocations exist for non-writable segments. + JmpRel = 23, // Address of relocations associated with PLT. + BindNow = 24, // Process all relocations before execution. + InitArray = 25, // Pointer to array of initialization functions. + FiniArray = 26, // Pointer to array of termination functions. + InitArraySz = 27, // Size of DT_INIT_ARRAY. + FiniArraySz = 28, // Size of DT_FINI_ARRAY. + RunPath = 29, // String table offset of lib search path. + Flags = 30, // Flags. + Encoding = 32, // Values from here to DT_LOOS follow the rules for the interpretation of the d_un union. + + PreInitArray = 32, // Pointer to array of preinit functions. + PreInitArraySz = 33, // Size of the DT_PREINIT_ARRAY array. + LoOS = 0x60000000, // Start of environment specific tags. + HiOS = 0x6FFFFFFF, // End of environment specific tags. + LoProc = 0x70000000, // Start of processor specific tags. + HiProc = 0x7FFFFFFF, // End of processor specific tags. + GNUHash = 0x6FFFFEF5, // Reference to the GNU hash table. + TLSDescPLT = 0x6FFFFEF6, // Location of PLT entry for TLS descriptor resolver calls. + TLSDescGOT = 0x6FFFFEF7, // Location of GOT entry used by TLS descriptor resolver PLT entry. + RelACount = 0x6FFFFFF9, // ELF32_Rela count. + RelCount = 0x6FFFFFFA, // ELF32_Rel count. + Flags1 = 0X6FFFFFFB, // Flags_1. + VerSym = 0x6FFFFFF0, // The address of .gnu.version section. + VerDef = 0X6FFFFFFC, // The address of the version definition table. + VerDefNum = 0X6FFFFFFD, // The number of entries in DT_VERDEF. + VerNeed = 0X6FFFFFFE, // The address of the version Dependency table. + VerNeedNum = 0X6FFFFFFF // The number of entries in DT_VERNEED. + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/IDynamicEntry.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/IDynamicEntry.cs new file mode 100644 index 0000000000..40fd1d65c3 --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/IDynamicEntry.cs @@ -0,0 +1,12 @@ +namespace ELFSharp.ELF.Sections +{ + /// + /// Represents an entry in the dynamic table. + /// Interface--because this is a union type in C, if we want more detail at some point on the values in the Union type, + /// we can have separate classes. + /// + internal interface IDynamicEntry + { + public DynamicTag Tag { get; } + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/IDynamicSection.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/IDynamicSection.cs new file mode 100644 index 0000000000..1b88de7dc9 --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/IDynamicSection.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace ELFSharp.ELF.Sections +{ + internal interface IDynamicSection : ISection + { + public IEnumerable Entries { get; } + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/INoteSection.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/INoteSection.cs new file mode 100644 index 0000000000..82516c4653 --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/INoteSection.cs @@ -0,0 +1,8 @@ +namespace ELFSharp.ELF.Sections +{ + internal interface INoteSection : ISection + { + public string NoteName { get; } + public byte[] Description { get; } + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/IProgBitsSection.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/IProgBitsSection.cs new file mode 100644 index 0000000000..e98415ceaf --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/IProgBitsSection.cs @@ -0,0 +1,7 @@ +namespace ELFSharp.ELF.Sections +{ + internal interface IProgBitsSection : ISection + { + public void WriteContents(byte[] destination, int offset, int length = 0); + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/ISection.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/ISection.cs new file mode 100644 index 0000000000..23f812c7e7 --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/ISection.cs @@ -0,0 +1,11 @@ +namespace ELFSharp.ELF.Sections +{ + internal interface ISection + { + public string Name { get; } + public uint NameIndex { get; } + public SectionType Type { get; } + public SectionFlags Flags { get; } + public byte[] GetContents(); + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/IStringTable.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/IStringTable.cs new file mode 100644 index 0000000000..f529c273c5 --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/IStringTable.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; + +namespace ELFSharp.ELF.Sections +{ + internal interface IStringTable : ISection + { + public string this[long index] { get; } + public IEnumerable Strings { get; } + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/ISymbolEntry.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/ISymbolEntry.cs new file mode 100644 index 0000000000..ac1ced6a9b --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/ISymbolEntry.cs @@ -0,0 +1,13 @@ +namespace ELFSharp.ELF.Sections +{ + internal interface ISymbolEntry + { + public string Name { get; } + public SymbolBinding Binding { get; } + public SymbolType Type { get; } + public SymbolVisibility Visibility { get; } + public bool IsPointedIndexSpecial { get; } + public ISection PointedSection { get; } + public ushort PointedSectionIndex { get; } + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/ISymbolTable.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/ISymbolTable.cs new file mode 100644 index 0000000000..da9763727d --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/ISymbolTable.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace ELFSharp.ELF.Sections +{ + internal interface ISymbolTable : ISection + { + public IEnumerable Entries { get; } + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/NoteData.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/NoteData.cs new file mode 100644 index 0000000000..e26b4f4ee3 --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/NoteData.cs @@ -0,0 +1,98 @@ +using System; +using System.Collections.ObjectModel; +using System.IO; +using System.Text; +using ELFSharp.ELF.Segments; +using ELFSharp.Utilities; + +#nullable disable + +namespace ELFSharp.ELF.Sections +{ + internal class NoteData : INoteData + { + public const ulong NoteDataHeaderSize = 12; // name size + description size + field + + private readonly SimpleEndianessAwareReader reader; + + internal NoteData(ulong sectionOffset, ulong sectionSize, SimpleEndianessAwareReader reader) + { + this.reader = reader; + var sectionEnd = (long)(sectionOffset + sectionSize); + reader.BaseStream.Seek((long)sectionOffset, SeekOrigin.Begin); + var nameSize = ReadSize(); + var descriptionSize = ReadSize(); + Type = ReadField(); + int remainder; + var fields = Math.DivRem(nameSize, FieldSize, out remainder); + var alignedNameSize = FieldSize * (remainder > 0 ? fields + 1 : fields); + + fields = Math.DivRem(descriptionSize, FieldSize, out remainder); + var alignedDescriptionSize = FieldSize * (remainder > 0 ? fields + 1 : fields); + + // We encountered binaries where nameSize and descriptionSize are + // invalid (i.e. significantly larger than the size of the binary itself). + // To avoid throwing on such binaries, we only read in name and description + // if the sizes are within range of the containing section. + if (reader.BaseStream.Position + alignedNameSize <= sectionEnd) + { + var name = reader.ReadBytes(alignedNameSize); + if (nameSize > 0) + Name = Encoding.UTF8.GetString(name, 0, nameSize - 1); // minus one to omit terminating NUL + if (reader.BaseStream.Position + descriptionSize <= sectionEnd) + DescriptionBytes = descriptionSize > 0 ? reader.ReadBytes(descriptionSize) : new byte[0]; + } + + // If there are multiple notes inside one segment, keep track of the end position so we can read them + // all when parsing the segment + NoteOffset = sectionOffset; + NoteFileSize = (ulong)alignedNameSize + (ulong)alignedDescriptionSize + NoteDataHeaderSize; + } + + internal byte[] DescriptionBytes { get; } + + internal ulong NoteOffset { get; } + internal ulong NoteFileSize { get; } + internal ulong NoteFileEnd => NoteOffset + NoteFileSize; + + private int FieldSize => 4; + + public string Name { get; } + + public ReadOnlyCollection Description => new ReadOnlyCollection(DescriptionBytes); + + public ulong Type { get; } + + public Stream ToStream() + { + return new MemoryStream(DescriptionBytes); + } + + public override string ToString() + { + return $"Name={Name} DataSize=0x{DescriptionBytes.Length.ToString("x8")}"; + } + + private int ReadSize() + { + /* + * According to some versions of ELF64 specfication, in 64-bit ELF files words, of which + * such section consists, should have 8 byte length. However, this is not the case in + * some other specifications (some of theme contradicts with themselves like the 64bit MIPS + * one). In real life scenarios I also observed that note sections are identical in both + * ELF classes. There is also only one structure (i.e. Elf_External_Note) in existing and + * well tested GNU tools. + * + * Nevertheless I leave here the whole machinery as it is already written and may be useful + * some day. + */ + return reader.ReadInt32(); + } + + private ulong ReadField() + { + // see comment above + return reader.ReadUInt32(); + } + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/NoteSection.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/NoteSection.cs new file mode 100644 index 0000000000..145c21daaa --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/NoteSection.cs @@ -0,0 +1,25 @@ +using ELFSharp.Utilities; + +namespace ELFSharp.ELF.Sections +{ + internal sealed class NoteSection : Section, INoteSection where T : struct + { + private readonly NoteData data; + + internal NoteSection(SectionHeader header, SimpleEndianessAwareReader reader) : base(header, reader) + { + data = new NoteData(header.Offset, header.Size, reader); + } + + public T NoteType => data.Type.To(); + + public string NoteName => data.Name; + + public byte[] Description => data.DescriptionBytes; + + public override string ToString() + { + return string.Format("{0}: {2}, Type={1}", Name, NoteType, Type); + } + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/ProgBitsSection.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/ProgBitsSection.cs new file mode 100644 index 0000000000..4d1f39ea2c --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/ProgBitsSection.cs @@ -0,0 +1,29 @@ +using System; +using ELFSharp.Utilities; + +namespace ELFSharp.ELF.Sections +{ + internal sealed class ProgBitsSection : Section, IProgBitsSection where T : struct + { + private const int BufferSize = 10 * 1024; + + internal ProgBitsSection(SectionHeader header, SimpleEndianessAwareReader reader) : base(header, reader) + { + } + + + public void WriteContents(byte[] destination, int offset, int length = 0) + { + SeekToSectionBeginning(); + if (length == 0 || (ulong)length > Header.Size) + length = Convert.ToInt32(Header.Size); + var remaining = length; + while (remaining > 0) + { + var buffer = Reader.ReadBytes(Math.Min(BufferSize, remaining)); + buffer.CopyTo(destination, offset + (length - remaining)); + remaining -= buffer.Length; + } + } + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/Section.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/Section.cs new file mode 100644 index 0000000000..5c88fa1a1d --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/Section.cs @@ -0,0 +1,58 @@ +using System; +using System.IO; +using ELFSharp.Utilities; + +namespace ELFSharp.ELF.Sections +{ + internal class Section : ISection where T : struct + { + protected readonly SimpleEndianessAwareReader Reader; + + internal Section(SectionHeader header, SimpleEndianessAwareReader reader) + { + Header = header; + Reader = reader; + } + + public T RawFlags => Header.RawFlags.To(); + + public T LoadAddress => Header.LoadAddress.To(); + + public T Alignment => Header.Alignment.To(); + + public T EntrySize => Header.EntrySize.To(); + + public T Size => Header.Size.To(); + + public T Offset => Header.Offset.To(); + + internal SectionHeader Header { get; } + + public virtual byte[] GetContents() + { + if (Type == SectionType.NoBits) + return Array.Empty(); + + Reader.BaseStream.Seek((long)Header.Offset, SeekOrigin.Begin); + return Reader.ReadBytes(Convert.ToInt32(Header.Size)); + } + + public string Name => Header.Name; + + public uint NameIndex => Header.NameIndex; + + public SectionType Type => Header.Type; + + public SectionFlags Flags => Header.Flags; + + public override string ToString() + { + return Header.ToString(); + } + + protected void SeekToSectionBeginning() + { + Reader.BaseStream.Seek((long)Header.Offset, SeekOrigin.Begin); + } + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/SectionFlags.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/SectionFlags.cs new file mode 100644 index 0000000000..7abd030186 --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/SectionFlags.cs @@ -0,0 +1,12 @@ +using System; + +namespace ELFSharp.ELF.Sections +{ + [Flags] + internal enum SectionFlags + { + Writable = 1, + Allocatable = 2, + Executable = 4 + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/SectionHeader.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/SectionHeader.cs new file mode 100644 index 0000000000..54ea890727 --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/SectionHeader.cs @@ -0,0 +1,70 @@ +using ELFSharp.Utilities; + +#nullable disable + +namespace ELFSharp.ELF.Sections +{ + internal sealed class SectionHeader + { + private readonly Class elfClass; + + private readonly SimpleEndianessAwareReader reader; + + private readonly IStringTable table; + + // TODO: make elf consts file with things like SHT_LOUSER + internal SectionHeader(SimpleEndianessAwareReader reader, Class elfClass, IStringTable table = null) + { + this.reader = reader; + this.table = table; + this.elfClass = elfClass; + ReadSectionHeader(); + } + + internal string Name { get; private set; } + internal uint NameIndex { get; private set; } + internal SectionType Type { get; private set; } + internal SectionFlags Flags { get; private set; } + internal ulong RawFlags { get; private set; } + internal ulong LoadAddress { get; private set; } + internal ulong Alignment { get; private set; } + internal ulong EntrySize { get; private set; } + internal ulong Size { get; private set; } + internal ulong Offset { get; private set; } + internal uint Link { get; private set; } + internal uint Info { get; private set; } + + public override string ToString() + { + return string.Format("{0}: {2}, load @0x{4:X}, {5} bytes long", Name, NameIndex, Type, RawFlags, + LoadAddress, Size); + } + + private void ReadSectionHeader() + { + NameIndex = reader.ReadUInt32(); + if (table != null) + Name = table[NameIndex]; + Type = (SectionType)reader.ReadUInt32(); + RawFlags = ReadAddress(); + Flags = unchecked((SectionFlags)RawFlags); + LoadAddress = ReadAddress(); + Offset = ReadOffset(); + Size = ReadOffset(); + Link = reader.ReadUInt32(); + Info = reader.ReadUInt32(); + Alignment = ReadAddress(); + EntrySize = ReadAddress(); + } + + private ulong ReadAddress() + { + return elfClass == Class.Bit32 ? reader.ReadUInt32() : reader.ReadUInt64(); + } + + private ulong ReadOffset() + { + return elfClass == Class.Bit32 ? reader.ReadUInt32() : reader.ReadUInt64(); + } + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/SectionType.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/SectionType.cs new file mode 100644 index 0000000000..c1b3b006a1 --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/SectionType.cs @@ -0,0 +1,18 @@ +namespace ELFSharp.ELF.Sections +{ + internal enum SectionType : uint + { + Null = 0, + ProgBits, + SymbolTable, + StringTable, + RelocationAddends, + HashTable, + Dynamic, + Note, + NoBits, + Relocation, + Shlib, + DynamicSymbolTable + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/SpecialSectionIndex.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/SpecialSectionIndex.cs new file mode 100644 index 0000000000..ca51e9e648 --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/SpecialSectionIndex.cs @@ -0,0 +1,9 @@ +namespace ELFSharp.ELF.Sections +{ + internal enum SpecialSectionIndex : ushort + { + Absolute = 0, + Common = 0xFFF1, + Undefined = 0xFFF2 + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/SpecialSectionType.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/SpecialSectionType.cs new file mode 100644 index 0000000000..710773e6cc --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/SpecialSectionType.cs @@ -0,0 +1,12 @@ +namespace ELFSharp.ELF.Sections +{ + internal enum SpecialSectionType + { + Null, + ProgBits, + NoBits, + Shlib, + ProcessorSpecific, + UserSpecific + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/StringTable.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/StringTable.cs new file mode 100644 index 0000000000..c1d710e708 --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/StringTable.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; +using ELFSharp.Utilities; + +namespace ELFSharp.ELF.Sections +{ + internal sealed class StringTable : Section, IStringTable where T : struct + { + private readonly byte[] stringBlob; + + private readonly Dictionary stringCache; + private bool cachePopulated; + + internal StringTable(SectionHeader header, SimpleEndianessAwareReader reader) : base(header, reader) + { + stringCache = new Dictionary + { + { 0, string.Empty } + }; + stringBlob = ReadStringData(); + } + + public IEnumerable Strings + { + get + { + if (!cachePopulated) + PrepopulateCache(); + return stringCache.Values; + } + } + + public string this[long index] + { + get + { + if (stringCache.TryGetValue(index, out var result)) + return result; + return HandleUnexpectedIndex(index); + } + } + + private string HandleUnexpectedIndex(long index) + { + var stringStart = (int)index; + for (var i = stringStart; i < stringBlob.Length; ++i) + if (stringBlob[i] == 0) + { + var str = Encoding.UTF8.GetString(stringBlob, stringStart, i - stringStart); + stringCache.Add(stringStart, str); + return str; + } + + throw new IndexOutOfRangeException(); + } + + private void PrepopulateCache() + { + cachePopulated = true; + + var stringStart = 1; + for (var i = 1; i < stringBlob.Length; ++i) + if (stringBlob[i] == 0) + { + if (!stringCache.ContainsKey(stringStart)) + stringCache.Add(stringStart, Encoding.UTF8.GetString(stringBlob, stringStart, i - stringStart)); + stringStart = i + 1; + } + } + + private byte[] ReadStringData() + { + SeekToSectionBeginning(); + var blob = Reader.ReadBytes((int)Header.Size); + Debug.Assert(blob.Length == 0 || (blob[0] == 0 && blob[blob.Length - 1] == 0), + "First and last bytes must be the null character (except for empty string tables)"); + return blob; + } + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/SymbolBinding.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/SymbolBinding.cs new file mode 100644 index 0000000000..3f447e97b1 --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/SymbolBinding.cs @@ -0,0 +1,10 @@ +namespace ELFSharp.ELF.Sections +{ + internal enum SymbolBinding + { + Local, + Global, + Weak, + ProcessorSpecific + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/SymbolEntry.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/SymbolEntry.cs new file mode 100644 index 0000000000..445b9a445a --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/SymbolEntry.cs @@ -0,0 +1,60 @@ +using System; + +#nullable disable + +namespace ELFSharp.ELF.Sections +{ + internal class SymbolEntry : ISymbolEntry where T : struct + { + private readonly ELF elf; + + public SymbolEntry(string name, T value, T size, SymbolVisibility visibility, + SymbolBinding binding, SymbolType type, ELF elf, ushort sectionIdx) + { + Name = name; + Value = value; + Size = size; + Binding = binding; + Type = type; + Visibility = visibility; + this.elf = elf; + PointedSectionIndex = sectionIdx; + } + + public T Value { get; } + + public T Size { get; } + + public Section PointedSection => IsPointedIndexSpecial ? null : elf.GetSection(PointedSectionIndex); + + public SpecialSectionIndex SpecialPointedSectionIndex + { + get + { + if (IsPointedIndexSpecial) + return (SpecialSectionIndex)PointedSectionIndex; + throw new InvalidOperationException("Given pointed section index does not have special meaning."); + } + } + + public string Name { get; } + + public SymbolBinding Binding { get; } + + public SymbolType Type { get; } + + public SymbolVisibility Visibility { get; } + + public bool IsPointedIndexSpecial => Enum.IsDefined(typeof(SpecialSectionIndex), PointedSectionIndex); + + ISection ISymbolEntry.PointedSection => PointedSection; + + public ushort PointedSectionIndex { get; } + + public override string ToString() + { + return string.Format("[{3} {4} {0}: 0x{1:X}, size: {2}, section idx: {5}]", + Name, Value, Size, Binding, Type, (SpecialSectionIndex)PointedSectionIndex); + } + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/SymbolTable.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/SymbolTable.cs new file mode 100644 index 0000000000..86b2cab0b2 --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/SymbolTable.cs @@ -0,0 +1,64 @@ +using System.Collections.Generic; +using System.Collections.ObjectModel; +using ELFSharp.Utilities; + +#nullable disable + +namespace ELFSharp.ELF.Sections +{ + internal sealed class SymbolTable : Section, ISymbolTable where T : struct + { + private readonly ELF elf; + private readonly IStringTable table; + + private List> entries; + + internal SymbolTable(SectionHeader header, SimpleEndianessAwareReader Reader, IStringTable table, ELF elf) : + base(header, Reader) + { + this.table = table; + this.elf = elf; + ReadSymbols(); + } + + public IEnumerable> Entries => new ReadOnlyCollection>(entries); + + IEnumerable ISymbolTable.Entries => Entries; + + private void ReadSymbols() + { + SeekToSectionBeginning(); + entries = new List>(); + var adder = (ulong)(elf.Class == Class.Bit32 ? Consts.SymbolEntrySize32 : Consts.SymbolEntrySize64); + for (var i = 0UL; i < Header.Size; i += adder) + { + var value = 0UL; + var size = 0UL; + var nameIdx = Reader.ReadUInt32(); + + if (elf.Class == Class.Bit32) + { + value = Reader.ReadUInt32(); + size = Reader.ReadUInt32(); + } + + var info = Reader.ReadByte(); + var other = Reader.ReadByte(); + var visibility = (SymbolVisibility)(other & 3); // Only three lowest bits are meaningful. + var sectionIdx = Reader.ReadUInt16(); + + if (elf.Class == Class.Bit64) + { + value = Reader.ReadUInt64(); + size = Reader.ReadUInt64(); + } + + var name = table == null ? "" : table[nameIdx]; + var binding = (SymbolBinding)(info >> 4); + var type = (SymbolType)(info & 0x0F); + entries.Add(new SymbolEntry(name, value.To(), size.To(), visibility, binding, type, elf, + sectionIdx)); + } + } + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/SymbolType.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/SymbolType.cs new file mode 100644 index 0000000000..ce2fa60b6f --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/SymbolType.cs @@ -0,0 +1,12 @@ +namespace ELFSharp.ELF.Sections +{ + internal enum SymbolType + { + NotSpecified, + Object, + Function, + Section, + File, + ProcessorSpecific + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/SymbolVisibility.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/SymbolVisibility.cs new file mode 100644 index 0000000000..535762ece8 --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Sections/SymbolVisibility.cs @@ -0,0 +1,10 @@ +namespace ELFSharp.ELF.Sections +{ + internal enum SymbolVisibility : byte + { + Default = 0, + Internal = 1, + Hidden = 2, + Protected = 3 + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Segments/INoteData.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Segments/INoteData.cs new file mode 100644 index 0000000000..c8e560581a --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Segments/INoteData.cs @@ -0,0 +1,31 @@ +using System.Collections.ObjectModel; +using System.IO; + +namespace ELFSharp.ELF.Segments +{ + internal interface INoteData + { + /// + /// Owner of the note. + /// + public string Name { get; } + + /// + /// Data contents of the note. The format of this depends on the combination of the Name and Type properties and often + /// corresponds to a struct. + /// For example, see elf.h in the Linux kernel source tree. + /// + public ReadOnlyCollection Description { get; } + + /// + /// Data type + /// + public ulong Type { get; } + + /// + /// Returns the Description byte[] as a Stream + /// + /// + public Stream ToStream(); + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Segments/INoteSegment.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Segments/INoteSegment.cs new file mode 100644 index 0000000000..fba41c72cc --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Segments/INoteSegment.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; + +namespace ELFSharp.ELF.Segments +{ + internal interface INoteSegment : ISegment + { + public string NoteName { get; } + public ulong NoteType { get; } + public byte[] NoteDescription { get; } + + /// + /// Returns all notes within the segment + /// + public IReadOnlyList Notes { get; } + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Segments/ISegment.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Segments/ISegment.cs new file mode 100644 index 0000000000..2d9b00b608 --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Segments/ISegment.cs @@ -0,0 +1,11 @@ +namespace ELFSharp.ELF.Segments +{ + internal interface ISegment + { + public SegmentType Type { get; } + public SegmentFlags Flags { get; } + public byte[] GetRawHeader(); + public byte[] GetFileContents(); + public byte[] GetMemoryContents(); + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Segments/NoteSegment.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Segments/NoteSegment.cs new file mode 100644 index 0000000000..2085294e52 --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Segments/NoteSegment.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using ELFSharp.ELF.Sections; +using ELFSharp.Utilities; + +namespace ELFSharp.ELF.Segments +{ + internal sealed class NoteSegment : Segment, INoteSegment + { + private readonly NoteData data; + + private readonly List notes = new List(); + + internal NoteSegment(long headerOffset, Class elfClass, SimpleEndianessAwareReader reader) + : base(headerOffset, elfClass, reader) + { + var offset = (ulong)Offset; + var fileSize = (ulong)FileSize; + var remainingSize = fileSize; + + // Keep the first NoteData as a property for backwards compatibility + data = new NoteData(offset, remainingSize, reader); + notes.Add(data); + + offset += data.NoteFileSize; + + // Read all additional notes within the segment + // Multiple notes are common in ELF core files + if (data.NoteFileSize < remainingSize) + { + remainingSize -= data.NoteFileSize; + + while (remainingSize > NoteData.NoteDataHeaderSize) + { + var note = new NoteData(offset, remainingSize, reader); + notes.Add(note); + offset += note.NoteFileSize; + if (note.NoteFileSize <= remainingSize) + remainingSize -= note.NoteFileSize; + else + // File is damaged + throw new IndexOutOfRangeException("NoteSegment internal note-data is out of bounds"); + } + } + } + + public string NoteName => data.Name; + + public ulong NoteType => data.Type; + + public byte[] NoteDescription => data.DescriptionBytes; + public IReadOnlyList Notes => notes.AsReadOnly(); + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Segments/Segment.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Segments/Segment.cs new file mode 100644 index 0000000000..91d28c38d4 --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Segments/Segment.cs @@ -0,0 +1,114 @@ +using System; +using System.IO; +using ELFSharp.Utilities; + +#nullable disable + +namespace ELFSharp.ELF.Segments +{ + internal class Segment : ISegment + { + private readonly Class elfClass; + + private readonly long headerOffset; + private readonly SimpleEndianessAwareReader reader; + + internal Segment(long headerOffset, Class elfClass, SimpleEndianessAwareReader reader) + { + this.reader = reader; + this.headerOffset = headerOffset; + this.elfClass = elfClass; + ReadHeader(); + } + + public T Address { get; private set; } + + public T PhysicalAddress { get; private set; } + + public T Size { get; private set; } + + public T Alignment { get; private set; } + + public long FileSize { get; private set; } + + public long Offset { get; private set; } + + public SegmentType Type { get; private set; } + + public SegmentFlags Flags { get; private set; } + + /// + /// Returns content of the section as it is given in the file. + /// Note that it may be an array of length 0. + /// + /// Segment contents as byte array. + public byte[] GetFileContents() + { + if (FileSize == 0) + return new byte[0]; + + SeekTo(Offset); + var result = new byte[checked((int)FileSize)]; + var fileImage = reader.ReadBytes(result.Length); + fileImage.CopyTo(result, 0); + return result; + } + + /// + /// Returns content of the section, possibly padded or truncated to the memory size. + /// Note that it may be an array of length 0. + /// + /// Segment image as a byte array. + public byte[] GetMemoryContents() + { + var sizeAsInt = Size.To(); + if (sizeAsInt == 0) + return new byte[0]; + + SeekTo(Offset); + var result = new byte[sizeAsInt]; + var fileImage = reader.ReadBytes(Math.Min(result.Length, checked((int)FileSize))); + fileImage.CopyTo(result, 0); + return result; + } + + public byte[] GetRawHeader() + { + SeekTo(headerOffset); + return reader.ReadBytes(elfClass == Class.Bit32 ? 32 : 56); + } + + public static SegmentType ProbeType(SimpleEndianessAwareReader reader) + { + return (SegmentType)reader.ReadUInt32(); + } + + public override string ToString() + { + return string.Format("{2}: size {3}, @ 0x{0:X}", Address, PhysicalAddress, Type, Size); + } + + private void ReadHeader() + { + SeekTo(headerOffset); + Type = (SegmentType)reader.ReadUInt32(); + if (elfClass == Class.Bit64) + Flags = (SegmentFlags)reader.ReadUInt32(); + // TODO: some functions?s + Offset = elfClass == Class.Bit32 ? reader.ReadUInt32() : reader.ReadInt64(); + Address = (elfClass == Class.Bit32 ? reader.ReadUInt32() : reader.ReadUInt64()).To(); + PhysicalAddress = (elfClass == Class.Bit32 ? reader.ReadUInt32() : reader.ReadUInt64()).To(); + FileSize = elfClass == Class.Bit32 ? reader.ReadInt32() : reader.ReadInt64(); + Size = (elfClass == Class.Bit32 ? reader.ReadUInt32() : reader.ReadUInt64()).To(); + if (elfClass == Class.Bit32) + Flags = (SegmentFlags)reader.ReadUInt32(); + + Alignment = (elfClass == Class.Bit32 ? reader.ReadUInt32() : reader.ReadUInt64()).To(); + } + + private void SeekTo(long givenOffset) + { + reader.BaseStream.Seek(givenOffset, SeekOrigin.Begin); + } + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Segments/SegmentFlags.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Segments/SegmentFlags.cs new file mode 100644 index 0000000000..4d10a6589d --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Segments/SegmentFlags.cs @@ -0,0 +1,12 @@ +using System; + +namespace ELFSharp.ELF.Segments +{ + [Flags] + internal enum SegmentFlags : uint + { + Execute = 1, + Write = 2, + Read = 4 + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Segments/SegmentType.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Segments/SegmentType.cs new file mode 100644 index 0000000000..f9c681aa54 --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Segments/SegmentType.cs @@ -0,0 +1,13 @@ +namespace ELFSharp.ELF.Segments +{ + internal enum SegmentType : uint + { + Null = 0, + Load, + Dynamic, + Interpreter, + Note, + SharedLibrary, + ProgramHeader + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Utilities.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Utilities.cs new file mode 100644 index 0000000000..8d84290714 --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/ELF/Utilities.cs @@ -0,0 +1,12 @@ +using System; + +namespace ELFSharp.ELF +{ + internal static class Utilities + { + internal static T To(this object source) + { + return (T)Convert.ChangeType(source, typeof(T)); + } + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/Endianess.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/Endianess.cs new file mode 100644 index 0000000000..a2c0aadb0d --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/Endianess.cs @@ -0,0 +1,8 @@ +namespace ELFSharp +{ + internal enum Endianess + { + LittleEndian, + BigEndian + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/Command.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/Command.cs new file mode 100644 index 0000000000..01ae9069bf --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/Command.cs @@ -0,0 +1,17 @@ +using System.IO; +using ELFSharp.Utilities; + +namespace ELFSharp.MachO +{ + internal class Command + { + protected readonly SimpleEndianessAwareReader Reader; + protected readonly Stream Stream; + + internal Command(SimpleEndianessAwareReader reader, Stream stream) + { + Stream = stream; + Reader = reader; + } + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/CommandType.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/CommandType.cs new file mode 100644 index 0000000000..1e5acb136d --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/CommandType.cs @@ -0,0 +1,15 @@ +namespace ELFSharp.MachO +{ + internal enum CommandType : uint + { + Segment = 0x1, + SymbolTable = 0x2, + LoadDylib = 0xc, + IdDylib = 0xd, + LoadWeakDylib = 0x80000018u, + Segment64 = 0x19, + ReexportDylib = 0x8000001fu, + Main = 0x80000028u, + UUID = 0x1b + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/Dylib.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/Dylib.cs new file mode 100644 index 0000000000..de9e5f12b8 --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/Dylib.cs @@ -0,0 +1,38 @@ +using System; +using System.IO; +using System.Text; +using ELFSharp.Utilities; + +namespace ELFSharp.MachO +{ + internal abstract class Dylib : Command + { + internal Dylib(SimpleEndianessAwareReader reader, Stream stream, uint commandSize) : base(reader, stream) + { + var offset = reader.ReadUInt32(); + var timestamp = reader.ReadInt32(); + var currentVersion = reader.ReadUInt32(); + var compatibilityVersion = reader.ReadUInt32(); + Timestamp = DateTimeOffset.FromUnixTimeSeconds(timestamp).UtcDateTime; + CurrentVersion = GetVersion(currentVersion); + CompatibilityVersion = GetVersion(compatibilityVersion); + Name = GetString(reader.ReadBytes((int)(commandSize - offset))); + } + + public string Name { get; } + public DateTime Timestamp { get; } + public Version CurrentVersion { get; } + public Version CompatibilityVersion { get; } + + private static Version GetVersion(uint version) + { + return new Version((int)(version >> 16), (int)((version >> 8) & 0xff), (int)(version & 0xff)); + } + + private static string GetString(byte[] bytes) + { + var nullTerminatorIndex = Array.FindIndex(bytes, e => e == '\0'); + return Encoding.ASCII.GetString(bytes, 0, nullTerminatorIndex >= 0 ? nullTerminatorIndex : bytes.Length); + } + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/EntryPoint.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/EntryPoint.cs new file mode 100644 index 0000000000..0d41fc1a34 --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/EntryPoint.cs @@ -0,0 +1,18 @@ +using System.IO; +using ELFSharp.Utilities; + +namespace ELFSharp.MachO +{ + internal class EntryPoint : Command + { + public EntryPoint(SimpleEndianessAwareReader reader, Stream stream) : base(reader, stream) + { + Value = Reader.ReadInt64(); + StackSize = Reader.ReadInt64(); + } + + public long Value { get; private set; } + + public long StackSize { get; private set; } + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/FatArchiveReader.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/FatArchiveReader.cs new file mode 100644 index 0000000000..401382c02a --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/FatArchiveReader.cs @@ -0,0 +1,32 @@ +using System.Collections.Generic; +using System.IO; +using ELFSharp.Utilities; + +namespace ELFSharp.MachO +{ + internal static class FatArchiveReader + { + public static IEnumerable Enumerate(Stream stream, bool shouldOwnStream) + { + // Fat header is always big endian. + var reader = new SimpleEndianessAwareReader(stream, Endianess.BigEndian, !shouldOwnStream); + + // We assume that fat magic has been already read. + var machOCount = reader.ReadInt32(); + var alreadyRead = 0; + var fatEntriesBegin = stream.Position; + + while (alreadyRead < machOCount) + { + // We're only interested in offset and size. + stream.Seek(fatEntriesBegin + 20 * alreadyRead + 8, SeekOrigin.Begin); + var offset = reader.ReadInt32(); + var size = reader.ReadInt32(); + var substream = new SubStream(stream, offset, size); + yield return MachOReader.Load(substream, false); + + alreadyRead++; + } + } + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/FileType.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/FileType.cs new file mode 100644 index 0000000000..515717389a --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/FileType.cs @@ -0,0 +1,17 @@ +namespace ELFSharp.MachO +{ + internal enum FileType : uint + { + Object = 0x1, + Executable = 0x2, + FixedVM = 0x3, + Core = 0x4, + Preload = 0x5, + DynamicLibrary = 0x6, + DynamicLinker = 0x7, + Bundle = 0x8, + DynamicLibraryStub = 0x9, + Debug = 0xA, + Kext = 0xB + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/HeaderFlags.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/HeaderFlags.cs new file mode 100644 index 0000000000..1866e4569c --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/HeaderFlags.cs @@ -0,0 +1,76 @@ +using System; + +namespace ELFSharp.MachO +{ + [Flags] + internal enum HeaderFlags : uint + { + NoUndefs = 0x1, /* the object file has no undefined references */ + + IncrLink = 0x2, /* the object file is the output of an incremental link against a base file + * and can't be link edited again */ + + DyldLink = 0x4, /* the object file is input for the dynamic linker and can't be staticly link edited again */ + + BindAtLoad = 0x8, /* the object file's undefined references are bound by the dynamic + * linker when loaded. */ + + Prebound = 0x10, /* the file has its dynamic undefined references prebound. */ + + SplitSeg = 0x20, /* the file has its read-only and read-write segments split */ + + LazyInit = 0x40, /* the shared library init routine is to be run lazily via catching memory + * faults to its writeable segments (obsolete) */ + + TwoLevel = 0x80, /* the image is using two-level name space bindings */ + + ForceFlat = 0x100, /* the executable is forcing all images to use flat name space bindings */ + + NoMultiDefs = 0x200, /* this umbrella guarantees no multiple defintions of symbols in its + * sub-images so the two-level namespace hints can always be used. */ + + NoFixPrebinding = 0x400, /* do not have dyld notify the prebinding agent about this executable*/ + + Prebindable = 0x800, /* the binary is not prebound but can have its prebinding redone. only used + * when MH_PREBOUND is not set. */ + + AllModsBound = 0x1000, /* indicates that this binary binds to all two-level namespace modules of + * its dependent libraries.only used when MH_PREBINDABLE and MH_TWOLEVEL are both set. */ + + SubsectionsViaSymbols = 0x2000, /* safe to divide up the sections into sub-sections via symbols for dead + * code stripping*/ + + Canonical = 0x4000, /* the binary has been canonicalized via the unprebind operation */ + + WeakDefines = 0x8000, /* the final linked image contains external weak symbols*/ + + BindsToWeak = 0x10000, /* the final linked image uses weak symbols */ + + AllowStackExecution = 0x20000, /* When this bit is set, all stacks in the task will be given stack + * execution privilege. Only used in MH_EXECUTE filetypes. */ + + RootSafe = + 0x40000, /* When this bit is set, the binary declares it is safe for use in processes with uid zero */ + + SetuidSafe = + 0x80000, /* When this bit is set, the binary declares it is safe for use in processes when issetugid() is true */ + + NoReexportedDylibs = 0x100000, /* When this bit is set on a dylib, the static linker does not need to + * examine dependent dylibs to see if any are re-exported */ + + PIE = 0x200000, /* When this bit is set, the OS will load the main executable at a random + * address.Only used in MH_EXECUTE filetypes. */ + + DeadStrippableDylib = 0x400000, /* Only for use on dylibs. When linking against a dylib that has this bit + * set, the static linker will automatically not create a LC_LOAD_DYLIB + * load command to the dylib if no symbols are being referenced from the dylib. */ + + HasTLVDescriptors = 0x800000, /* Contains a section of type S_THREAD_LOCAL_VARIABLES*/ + + NoHeapExecution = 0x1000000, /* When this bit is set, the OS will run the main executable with a + * non-executable heap even on platforms (e.g.i386) that don't require + * it. Only used in MH_EXECUTE filetypes. */ + + AppExtensionSafe = 0x02000000 /* The code was linked for use in an application extension. */ + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/IdDylib.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/IdDylib.cs new file mode 100644 index 0000000000..c57ae7b66b --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/IdDylib.cs @@ -0,0 +1,13 @@ +using System.IO; +using ELFSharp.Utilities; + +namespace ELFSharp.MachO +{ + internal class IdDylib : Dylib + { + public IdDylib(SimpleEndianessAwareReader reader, Stream stream, uint commandSize) : base(reader, stream, + commandSize) + { + } + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/LoadDylib.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/LoadDylib.cs new file mode 100644 index 0000000000..339bc62a0e --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/LoadDylib.cs @@ -0,0 +1,13 @@ +using System.IO; +using ELFSharp.Utilities; + +namespace ELFSharp.MachO +{ + internal class LoadDylib : Dylib + { + public LoadDylib(SimpleEndianessAwareReader reader, Stream stream, uint commandSize) : base(reader, stream, + commandSize) + { + } + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/LoadWeakDylib.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/LoadWeakDylib.cs new file mode 100644 index 0000000000..aa92ebf5d2 --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/LoadWeakDylib.cs @@ -0,0 +1,13 @@ +using System.IO; +using ELFSharp.Utilities; + +namespace ELFSharp.MachO +{ + internal class LoadWeakDylib : Dylib + { + public LoadWeakDylib(SimpleEndianessAwareReader reader, Stream stream, uint commandSize) : base(reader, stream, + commandSize) + { + } + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/MachO.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/MachO.cs new file mode 100644 index 0000000000..a900c167e0 --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/MachO.cs @@ -0,0 +1,85 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using ELFSharp.Utilities; + +namespace ELFSharp.MachO +{ + internal sealed class MachO + { + internal const int Architecture64 = 0x1000000; + private readonly Command[] commands; + + internal MachO(Stream stream, bool is64, Endianess endianess, bool ownsStream) + { + Is64 = is64; + + using var reader = new SimpleEndianessAwareReader(stream, endianess, !ownsStream); + + Machine = (Machine)reader.ReadInt32(); + reader.ReadBytes(4); // we don't support the cpu subtype now + FileType = (FileType)reader.ReadUInt32(); + var noOfCommands = reader.ReadInt32(); + reader.ReadInt32(); // size of commands + Flags = (HeaderFlags)reader.ReadUInt32(); + if (is64) + reader.ReadBytes(4); // reserved + commands = new Command[noOfCommands]; + ReadCommands(noOfCommands, stream, reader); + } + + public Machine Machine { get; private set; } + + public FileType FileType { get; private set; } + + public HeaderFlags Flags { get; private set; } + + public bool Is64 { get; } + + public IEnumerable GetCommandsOfType() where T : Command + { + return commands.Where(x => x != null).OfType(); + } + + private void ReadCommands(int noOfCommands, Stream stream, SimpleEndianessAwareReader reader) + { + for (var i = 0; i < noOfCommands; i++) + { + var loadCommandType = reader.ReadUInt32(); + var commandSize = reader.ReadUInt32(); + switch ((CommandType)loadCommandType) + { + case CommandType.SymbolTable: + commands[i] = new SymbolTable(reader, stream, Is64, + commands.OfType().SelectMany(e => e.Sections).ToList()); + break; + case CommandType.IdDylib: + commands[i] = new IdDylib(reader, stream, commandSize); + break; + case CommandType.LoadDylib: + commands[i] = new LoadDylib(reader, stream, commandSize); + break; + case CommandType.LoadWeakDylib: + commands[i] = new LoadWeakDylib(reader, stream, commandSize); + break; + case CommandType.ReexportDylib: + commands[i] = new ReexportDylib(reader, stream, commandSize); + break; + case CommandType.Main: + commands[i] = new EntryPoint(reader, stream); + break; + case CommandType.Segment: + case CommandType.Segment64: + commands[i] = new Segment(reader, stream, this); + break; + case CommandType.UUID: + commands[i] = new UUID(reader, stream); + break; + default: + reader.ReadBytes((int)commandSize - 8); // 8 bytes is the size of the common command header + break; + } + } + } + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/MachOReader.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/MachOReader.cs new file mode 100644 index 0000000000..3ce1e516ec --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/MachOReader.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +#nullable disable + +namespace ELFSharp.MachO +{ + internal static class MachOReader + { + private const uint FatMagic = 0xBEBAFECA; + + private const string FatArchiveErrorMessage = + "Given file is a fat archive, contains more than one MachO binary. Use (Try)LoadFat to handle it."; + + private const string NotMachOErrorMessage = "Given file is not a Mach-O file."; + + private static readonly IReadOnlyDictionary MagicToMachOType = + new Dictionary + { + { 0xFEEDFACE, (false, Endianess.LittleEndian) }, + { 0xFEEDFACF, (true, Endianess.LittleEndian) }, + { 0xCEFAEDFE, (false, Endianess.BigEndian) }, + { 0xCFFAEDFE, (true, Endianess.BigEndian) } + }; + + public static MachO Load(string fileName) + { + return Load(File.OpenRead(fileName), true); + } + + public static MachO Load(Stream stream, bool shouldOwnStream) + { + return TryLoad(stream, shouldOwnStream, out var result) switch + { + MachOResult.OK => result, + MachOResult.NotMachO => throw new InvalidOperationException(NotMachOErrorMessage), + MachOResult.FatMachO => throw new InvalidOperationException(FatArchiveErrorMessage), + _ => throw new ArgumentOutOfRangeException() + }; + } + + public static IReadOnlyList LoadFat(Stream stream, bool shouldOwnStream) + { + var result = TryLoadFat(stream, shouldOwnStream, out var machOs); + if (result == MachOResult.OK || result == MachOResult.FatMachO) + return machOs; + + throw new InvalidOperationException(NotMachOErrorMessage); + } + + public static MachOResult TryLoad(string fileName, out MachO machO) + { + return TryLoad(File.OpenRead(fileName), true, out machO); + } + + public static MachOResult TryLoad(Stream stream, bool shouldOwnStream, out MachO machO) + { + var result = TryLoadFat(stream, shouldOwnStream, out var machOs); + + if (result == MachOResult.OK) + machO = machOs.SingleOrDefault(); + else + machO = null; + + return result; + } + + public static MachOResult TryLoadFat(Stream stream, bool shouldOwnStream, out IReadOnlyList machOs) + { + machOs = null; + + using var reader = new BinaryReader(stream, Encoding.UTF8, true); + var magic = reader.ReadUInt32(); + + if (magic == FatMagic) + { + machOs = FatArchiveReader.Enumerate(stream, shouldOwnStream).ToArray(); + return MachOResult.FatMachO; + } + + if (!MagicToMachOType.TryGetValue(magic, out var machOType)) + return MachOResult.NotMachO; + + var machO = new MachO(stream, machOType.Is64Bit, machOType.Endianess, shouldOwnStream); + machOs = new[] { machO }; + return MachOResult.OK; + } + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/MachOResult.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/MachOResult.cs new file mode 100644 index 0000000000..1ea75cc7a1 --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/MachOResult.cs @@ -0,0 +1,9 @@ +namespace ELFSharp.MachO +{ + internal enum MachOResult + { + OK, + NotMachO, + FatMachO + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/Machine.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/Machine.cs new file mode 100644 index 0000000000..55545e5a28 --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/Machine.cs @@ -0,0 +1,20 @@ +namespace ELFSharp.MachO +{ + internal enum Machine + { + Any = -1, + Vax = 1, + M68k = 6, + X86 = 7, + X86_64 = X86 | MachO.Architecture64, + M98k = 10, + PaRisc = 11, + Arm = 12, + Arm64 = Arm | MachO.Architecture64, + M88k = 13, + Sparc = 14, + I860 = 15, + PowerPc = 18, + PowerPc64 = PowerPc | MachO.Architecture64 + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/Protection.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/Protection.cs new file mode 100644 index 0000000000..f50d18fcdf --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/Protection.cs @@ -0,0 +1,12 @@ +using System; + +namespace ELFSharp.MachO +{ + [Flags] + internal enum Protection + { + Read = 1, + Write = 2, + Execute = 4 + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/ReexportDylib.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/ReexportDylib.cs new file mode 100644 index 0000000000..b187cf6b19 --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/ReexportDylib.cs @@ -0,0 +1,13 @@ +using System.IO; +using ELFSharp.Utilities; + +namespace ELFSharp.MachO +{ + internal class ReexportDylib : Dylib + { + public ReexportDylib(SimpleEndianessAwareReader reader, Stream stream, uint commandSize) : base(reader, stream, + commandSize) + { + } + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/Section.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/Section.cs new file mode 100644 index 0000000000..2cbd673dff --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/Section.cs @@ -0,0 +1,45 @@ +using System; +using System.Diagnostics; + +namespace ELFSharp.MachO +{ + [DebuggerDisplay("Section({segment.Name,nq},{Name,nq})")] + internal sealed class Section + { + private readonly Segment segment; + + public Section(string name, string segmentName, ulong address, ulong size, uint offset, uint alignExponent, + uint relocOffset, uint numberOfReloc, uint flags, Segment segment) + { + Name = name; + SegmentName = segmentName; + Address = address; + Size = size; + Offset = offset; + AlignExponent = alignExponent; + RelocOffset = relocOffset; + RelocCount = numberOfReloc; + Flags = flags; + this.segment = segment; + } + + public string Name { get; private set; } + public string SegmentName { get; private set; } + public ulong Address { get; private set; } + public ulong Size { get; } + public uint Offset { get; } + public uint AlignExponent { get; private set; } + public uint RelocOffset { get; private set; } + public uint RelocCount { get; private set; } + public uint Flags { get; private set; } + + public byte[] GetData() + { + if (Offset < segment.FileOffset || Offset + Size > segment.FileOffset + segment.Size) + return new byte[0]; + var result = new byte[Size]; + Array.Copy(segment.GetData(), (int)(Offset - segment.FileOffset), result, 0, (int)Size); + return result; + } + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/Segment.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/Segment.cs new file mode 100644 index 0000000000..fd755fc74a --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/Segment.cs @@ -0,0 +1,106 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using ELFSharp.Utilities; + +#nullable disable + +namespace ELFSharp.MachO +{ + [DebuggerDisplay("{Type}({Name,nq})")] + internal sealed class Segment : Command + { + private readonly byte[] data; + + private readonly bool is64; + + public Segment(SimpleEndianessAwareReader reader, Stream stream, MachO machO) : base(reader, stream) + { + is64 = machO.Is64; + Name = ReadSectionOrSegmentName(); + Address = ReadUInt32OrUInt64(); + Size = ReadUInt32OrUInt64(); + FileOffset = ReadUInt32OrUInt64(); + var fileSize = ReadUInt32OrUInt64(); + MaximalProtection = ReadProtection(); + InitialProtection = ReadProtection(); + var numberOfSections = Reader.ReadInt32(); + Reader.ReadInt32(); // we ignore flags for now + + if (fileSize > 0) + { + var streamPosition = Stream.Position; + Stream.Seek((long)FileOffset, SeekOrigin.Begin); + data = new byte[Size]; + var buffer = stream.ReadBytesOrThrow(checked((int)fileSize)); + Array.Copy(buffer, data, buffer.Length); + Stream.Position = streamPosition; + } + + var sections = new List
(); + for (var i = 0; i < numberOfSections; i++) + { + var sectionName = ReadSectionOrSegmentName(); + var segmentName = ReadSectionOrSegmentName(); + + // An intermediate object file contains only one segment. + // This segment name is empty, its sections segment names are not empty. + if (machO.FileType != FileType.Object && segmentName != Name) + throw new InvalidOperationException("Unexpected name of the section's segment."); + + var sectionAddress = ReadUInt32OrUInt64(); + var sectionSize = ReadUInt32OrUInt64(); + var offset = Reader.ReadUInt32(); + var alignExponent = Reader.ReadUInt32(); + var relocOffset = Reader.ReadUInt32(); + var numberOfReloc = Reader.ReadUInt32(); + var flags = Reader.ReadUInt32(); + _ = Reader.ReadUInt32(); // reserved1 + _ = Reader.ReadUInt32(); // reserved2 + _ = is64 ? Reader.ReadUInt32() : 0; // reserved3 + + var section = new Section(sectionName, segmentName, sectionAddress, sectionSize, offset, alignExponent, + relocOffset, numberOfReloc, flags, this); + sections.Add(section); + } + + Sections = new ReadOnlyCollection
(sections); + } + + public string Name { get; private set; } + public ulong Address { get; private set; } + public ulong Size { get; } + public ulong FileOffset { get; } + public Protection InitialProtection { get; private set; } + public Protection MaximalProtection { get; private set; } + public ReadOnlyCollection
Sections { get; private set; } + private CommandType Type => is64 ? CommandType.Segment64 : CommandType.Segment; + + public byte[] GetData() + { + if (data == null) + return new byte[Size]; + return data.ToArray(); + } + + private ulong ReadUInt32OrUInt64() + { + return is64 ? Reader.ReadUInt64() : Reader.ReadUInt32(); + } + + private Protection ReadProtection() + { + return (Protection)Reader.ReadInt32(); + } + + private string ReadSectionOrSegmentName() + { + var nameAsBytes = Reader.ReadBytes(16).TakeWhile(x => x != 0).ToArray(); + return Encoding.UTF8.GetString(nameAsBytes); + } + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/Symbol.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/Symbol.cs new file mode 100644 index 0000000000..94715b7c02 --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/Symbol.cs @@ -0,0 +1,19 @@ +using System.Diagnostics; + +namespace ELFSharp.MachO +{ + [DebuggerDisplay("Symbol({Name,nq},{Value}) in {Section}")] + internal struct Symbol + { + public Symbol(string name, long value, Section section) : this() + { + Name = name; + Value = value; + Section = section; + } + + public string Name { get; private set; } + public long Value { get; private set; } + public Section Section { get; private set; } + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/SymbolTable.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/SymbolTable.cs new file mode 100644 index 0000000000..c8293f0263 --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/SymbolTable.cs @@ -0,0 +1,84 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using ELFSharp.Utilities; + +#nullable disable + +namespace ELFSharp.MachO +{ + internal class SymbolTable : Command + { + private readonly bool is64; + + private Symbol[] symbols; + + public SymbolTable(SimpleEndianessAwareReader reader, Stream stream, bool is64, IReadOnlyList
sections) + : base(reader, stream) + { + this.is64 = is64; + ReadSymbols(sections); + } + + public IEnumerable Symbols + { + get { return symbols.Select(x => x); } + } + + private void ReadSymbols(IReadOnlyList
sections) + { + var symbolTableOffset = Reader.ReadInt32(); + var numberOfSymbols = Reader.ReadInt32(); + symbols = new Symbol[numberOfSymbols]; + var stringTableOffset = Reader.ReadInt32(); + Reader.ReadInt32(); // string table size + + var streamPosition = Stream.Position; + Stream.Seek(symbolTableOffset, SeekOrigin.Begin); + + try + { + for (var i = 0; i < numberOfSymbols; i++) + { + var nameOffset = Reader.ReadInt32(); + var name = ReadStringFromOffset(stringTableOffset + nameOffset); + var type = Reader.ReadByte(); + var sect = Reader.ReadByte(); + var desc = Reader.ReadInt16(); + var value = is64 ? Reader.ReadInt64() : Reader.ReadInt32(); + var symbol = new Symbol(name, value, + sect > 0 && sect <= sections.Count ? sections[sect - 1] : null); + symbols[i] = symbol; + } + } + finally + { + Stream.Position = streamPosition; + } + } + + private string ReadStringFromOffset(int offset) + { + var streamPosition = Stream.Position; + Stream.Seek(offset, SeekOrigin.Begin); + try + { + var asBytes = new List(); + int readByte; + while ((readByte = Stream.ReadByte()) != 0) + { + if (readByte == -1) + throw new EndOfStreamException("Premature end of the stream while reading string."); + asBytes.Add((byte)readByte); + } + + return Encoding.UTF8.GetString(asBytes.ToArray()); + } + finally + { + Stream.Position = streamPosition; + } + } + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/UUID.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/UUID.cs new file mode 100644 index 0000000000..893cd4c325 --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/MachO/UUID.cs @@ -0,0 +1,31 @@ +using System; +using System.IO; +using System.Linq; +using ELFSharp.Utilities; + +namespace ELFSharp.MachO +{ + internal class UUID : Command + { + internal UUID(SimpleEndianessAwareReader reader, Stream stream) : base(reader, stream) + { + ID = ReadUUID(); + } + + public Guid ID { get; } + + private Guid ReadUUID() + { + var rawBytes = Reader.ReadBytes(16).ToArray(); + + // Deal here with UUID endianess. Switch scheme is 4(r)-2(r)-2(r)-8(o) + // where r is reverse, o is original order. + Array.Reverse(rawBytes, 0, 4); + Array.Reverse(rawBytes, 4, 2); + Array.Reverse(rawBytes, 6, 2); + + var guid = new Guid(rawBytes); + return guid; + } + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/UImage/Architecture.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/UImage/Architecture.cs new file mode 100644 index 0000000000..8d320d7b15 --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/UImage/Architecture.cs @@ -0,0 +1,27 @@ +namespace ELFSharp.UImage +{ + internal enum Architecture : byte + { + Invalid = 0, + Alpha = 1, + ARM = 2, + Ix86 = 3, + Itanium = 4, + MIPS = 5, + MIPS64 = 6, + PowerPC = 7, + S390 = 8, + SuperH = 9, + SPARC = 10, + SPARC64 = 11, + M68k = 12, + MicroBlaze = 14, + Nios2 = 15, + Blackfin = 16, + AVR32 = 17, + ST200 = 18, + Sandbox = 19, + NDS32 = 20, + OpenRISC = 21 + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/UImage/CompressionType.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/UImage/CompressionType.cs new file mode 100644 index 0000000000..555c3aa9a3 --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/UImage/CompressionType.cs @@ -0,0 +1,11 @@ +namespace ELFSharp.UImage +{ + internal enum CompressionType : byte + { + None = 0, + Gzip = 1, + Bzip2 = 2, + Lzma = 3, + Lzo = 4 + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/UImage/ImageDataResult.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/UImage/ImageDataResult.cs new file mode 100644 index 0000000000..acea570b1d --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/UImage/ImageDataResult.cs @@ -0,0 +1,10 @@ +namespace ELFSharp.UImage +{ + internal enum ImageDataResult + { + OK, + BadChecksum, + UnsupportedCompressionFormat, + InvalidIndex + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/UImage/ImageType.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/UImage/ImageType.cs new file mode 100644 index 0000000000..f54c189990 --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/UImage/ImageType.cs @@ -0,0 +1,10 @@ +namespace ELFSharp.UImage +{ + // here only supported image types are listed + internal enum ImageType : byte + { + Standalone = 1, + Kernel = 2, + MultiFileImage = 4 + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/UImage/OS.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/UImage/OS.cs new file mode 100644 index 0000000000..6fa0e6118a --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/UImage/OS.cs @@ -0,0 +1,30 @@ +namespace ELFSharp.UImage +{ + internal enum OS : byte + { + Invalid = 0, + OpenBSD = 1, + NetBSD = 2, + FreeBSD = 3, + BSD44 = 4, + Linux = 5, + SVR4 = 6, + Esix = 7, + Solaris = 8, + Irix = 9, + SCO = 10, + Dell = 11, + NCR = 12, + LynxOS = 13, + VxWorks = 14, + PSOS = 15, + QNX = 16, + Firmware = 17, + RTEMS = 18, + ARTOS = 19, + UnityOS = 20, + INTEGRITY = 21, + OSE = 22, + Plan9 = 23 + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/UImage/UImage.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/UImage/UImage.cs new file mode 100644 index 0000000000..680ca7e6fb --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/UImage/UImage.cs @@ -0,0 +1,161 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using System.Linq; +using System.Text; + +#nullable disable + +namespace ELFSharp.UImage +{ + internal sealed class UImage + { + private const int MaximumNameLength = 32; + private readonly List imageSizes; + private readonly byte[] rawImage; + private readonly bool shouldOwnStream; + + internal UImage(Stream stream, bool multiFileImage, bool ownsStream) + { + shouldOwnStream = ownsStream; + imageSizes = new List(); + + using var reader = new BinaryReader(stream, Encoding.UTF8, !ownsStream); + + reader.ReadBytes(8); // magic and CRC, already checked + Timestamp = (new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc) + + TimeSpan.FromSeconds(reader.ReadInt32BigEndian())).ToLocalTime(); + Size = reader.ReadUInt32BigEndian(); + LoadAddress = reader.ReadUInt32BigEndian(); + EntryPoint = reader.ReadUInt32BigEndian(); + CRC = reader.ReadUInt32BigEndian(); + OperatingSystem = (OS)reader.ReadByte(); + Architecture = (Architecture)reader.ReadByte(); + Type = (ImageType)reader.ReadByte(); + Compression = (CompressionType)reader.ReadByte(); + var nameAsBytes = reader.ReadBytes(32); + Name = Encoding.UTF8.GetString(nameAsBytes.Reverse().SkipWhile(x => x == 0).Reverse().ToArray()); + + if (multiFileImage) + { + var startingPosition = stream.Position; + + int nextImageSize; + do + { + nextImageSize = reader.ReadInt32BigEndian(); + imageSizes.Add(nextImageSize); + } while (nextImageSize != 0); + + // Last image size is actually a terminator. + imageSizes.RemoveAt(imageSizes.Count - 1); + ImageCount = imageSizes.Count; + stream.Position = startingPosition; + } + + rawImage = reader.ReadBytes((int)Size); + } + + public uint CRC { get; } + public bool IsChecksumOK { get; private set; } + public uint Size { get; } + public uint LoadAddress { get; private set; } + public uint EntryPoint { get; private set; } + public string Name { get; private set; } + public DateTime Timestamp { get; private set; } + public CompressionType Compression { get; } + public ImageType Type { get; private set; } + public OS OperatingSystem { get; private set; } + public Architecture Architecture { get; private set; } + public int ImageCount { get; } + + public ImageDataResult TryGetImageData(int imageIndex, out byte[] result) + { + result = null; + + if (imageIndex > ImageCount - 1 || imageIndex < 0) + return ImageDataResult.InvalidIndex; + + if (ImageCount == 1) + return TryGetImageData(out result); + + if (Compression != CompressionType.None) + // We only support multi file images without compression + return ImageDataResult.UnsupportedCompressionFormat; + + if (CRC != UImageReader.GzipCrc32(rawImage)) + return ImageDataResult.BadChecksum; + + // Images sizes * 4 + terminator (which also takes 4 bytes). + var startingOffset = 4 * (ImageCount + 1) + imageSizes.Take(imageIndex).Sum(); + result = new byte[imageSizes[imageIndex]]; + Array.Copy(rawImage, startingOffset, result, 0, result.Length); + + return ImageDataResult.OK; + } + + public ImageDataResult TryGetImageData(out byte[] result) + { + result = null; + + if (ImageCount > 1) + return TryGetImageData(0, out result); + + if (Compression != CompressionType.None && Compression != CompressionType.Gzip) + return ImageDataResult.UnsupportedCompressionFormat; + + if (CRC != UImageReader.GzipCrc32(rawImage)) + return ImageDataResult.BadChecksum; + + result = new byte[rawImage.Length]; + Array.Copy(rawImage, result, result.Length); + if (Compression == CompressionType.Gzip) + using (var stream = new GZipStream(new MemoryStream(result), CompressionMode.Decompress)) + { + using (var decompressed = new MemoryStream()) + { + stream.CopyTo(decompressed); + result = decompressed.ToArray(); + } + } + + return ImageDataResult.OK; + } + + public byte[] GetImageData(int imageIndex) + { + byte[] result; + var imageDataResult = TryGetImageData(imageIndex, out result); + return InterpretImageResult(result, imageDataResult); + } + + public byte[] GetImageData() + { + byte[] result; + var imageDataResult = TryGetImageData(out result); + return InterpretImageResult(result, imageDataResult); + } + + private byte[] InterpretImageResult(byte[] result, ImageDataResult imageDataResult) + { + return imageDataResult switch + { + ImageDataResult.OK => result, + ImageDataResult.BadChecksum => throw new InvalidOperationException( + "Bad checksum of the image, probably corrupted image."), + ImageDataResult.UnsupportedCompressionFormat => throw new InvalidOperationException( + string.Format("Unsupported compression format '{0}'.", Compression)), + ImageDataResult.InvalidIndex => throw new ArgumentException("Invalid image index."), + _ => throw new ArgumentOutOfRangeException() + }; + } + + public byte[] GetRawImageData() + { + var result = new byte[rawImage.Length]; + Array.Copy(rawImage, result, result.Length); + return result; + } + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/UImage/UImageReader.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/UImage/UImageReader.cs new file mode 100644 index 0000000000..b81c887415 --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/UImage/UImageReader.cs @@ -0,0 +1,102 @@ +using System; +using System.IO; +using System.Net; +using System.Text; + +#nullable disable + +namespace ELFSharp.UImage +{ + internal static class UImageReader + { + private const uint Magic = 0x27051956; + private const uint Polynomial = 0xEDB88320; + private const uint Seed = 0xFFFFFFFF; + + public static UImage Load(string fileName) + { + return Load(File.OpenRead(fileName), true); + } + + public static UImage Load(Stream stream, bool shouldOwnStream) + { + return TryLoad(stream, shouldOwnStream, out var result) switch + { + UImageResult.OK => result, + UImageResult.NotUImage => throw new InvalidOperationException("Given file is not an UBoot image."), + UImageResult.BadChecksum => throw new InvalidOperationException( + "Wrong header checksum of the given UImage file."), + UImageResult.NotSupportedImageType => throw new InvalidOperationException( + "Given image type is not supported."), + _ => throw new ArgumentOutOfRangeException() + }; + } + + public static UImageResult TryLoad(string fileName, out UImage uImage) + { + return TryLoad(File.OpenRead(fileName), true, out uImage); + } + + public static UImageResult TryLoad(Stream stream, bool shouldOwnStream, out UImage uImage) + { + var startingStreamPosition = stream.Position; + + uImage = null; + if (stream.Length < 64) + return UImageResult.NotUImage; + + using var reader = new BinaryReader(stream, Encoding.UTF8, true); + + var headerForCrc = reader.ReadBytes(64); + // we need to zero crc part + for (var i = 4; i < 8; i++) + headerForCrc[i] = 0; + + stream.Position = startingStreamPosition; + + var magic = reader.ReadUInt32BigEndian(); + if (magic != Magic) + return UImageResult.NotUImage; + + var crc = reader.ReadUInt32BigEndian(); + if (crc != GzipCrc32(headerForCrc)) + return UImageResult.BadChecksum; + + reader.ReadBytes(22); + var imageType = (ImageType)reader.ReadByte(); + if (!Enum.IsDefined(typeof(ImageType), imageType)) + return UImageResult.NotSupportedImageType; + + var multiFileImage = imageType == ImageType.MultiFileImage; + stream.Position = startingStreamPosition; + uImage = new UImage(stream, multiFileImage, shouldOwnStream); + return UImageResult.OK; + } + + internal static uint GzipCrc32(byte[] data) + { + var remainder = Seed; + for (var i = 0; i < data.Length; i++) + { + remainder ^= data[i]; + for (var j = 0; j < 8; j++) + if ((remainder & 1) != 0) + remainder = (remainder >> 1) ^ Polynomial; + else + remainder >>= 1; + } + + return remainder ^ Seed; + } + + internal static uint ReadUInt32BigEndian(this BinaryReader reader) + { + return (uint)IPAddress.HostToNetworkOrder(reader.ReadInt32()); + } + + internal static int ReadInt32BigEndian(this BinaryReader reader) + { + return IPAddress.HostToNetworkOrder(reader.ReadInt32()); + } + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/UImage/UImageResult.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/UImage/UImageResult.cs new file mode 100644 index 0000000000..c84858d27f --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/UImage/UImageResult.cs @@ -0,0 +1,10 @@ +namespace ELFSharp.UImage +{ + internal enum UImageResult + { + OK, + NotUImage, + BadChecksum, + NotSupportedImageType + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/Utilities/Extensions.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/Utilities/Extensions.cs new file mode 100644 index 0000000000..3ccdd3a478 --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/Utilities/Extensions.cs @@ -0,0 +1,21 @@ +using System.IO; + +namespace ELFSharp.Utilities +{ + internal static class Extensions + { + public static byte[] ReadBytesOrThrow(this Stream stream, int count) + { + var result = new byte[count]; + while (count > 0) + { + var readThisTurn = stream.Read(result, result.Length - count, count); + if (readThisTurn == 0) + throw new EndOfStreamException($"End of stream reached while {count} bytes more expected."); + count -= readThisTurn; + } + + return result; + } + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/Utilities/SimpleEndianessAwareReader.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/Utilities/SimpleEndianessAwareReader.cs new file mode 100644 index 0000000000..74df4c4521 --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/Utilities/SimpleEndianessAwareReader.cs @@ -0,0 +1,81 @@ +using System; +using System.IO; +using System.Net; + +namespace ELFSharp.Utilities +{ + internal sealed class SimpleEndianessAwareReader : IDisposable + { + private readonly bool beNonClosing; + + private readonly bool needsAdjusting; + + public SimpleEndianessAwareReader(Stream stream, Endianess endianess, bool beNonClosing = false) + { + this.beNonClosing = beNonClosing; + BaseStream = stream; + needsAdjusting = (endianess == Endianess.LittleEndian) ^ BitConverter.IsLittleEndian; + } + + public Stream BaseStream { get; } + + public void Dispose() + { + if (beNonClosing) + return; + BaseStream.Dispose(); + } + + public byte[] ReadBytes(int count) + { + return BaseStream.ReadBytesOrThrow(count); + } + + public byte ReadByte() + { + var result = BaseStream.ReadByte(); + if (result == -1) + throw new EndOfStreamException("End of stream reached while trying to read one byte."); + return (byte)result; + } + + public short ReadInt16() + { + var value = BitConverter.ToInt16(ReadBytes(2), 0); + if (needsAdjusting) + value = IPAddress.NetworkToHostOrder(value); + return value; + } + + public ushort ReadUInt16() + { + return (ushort)ReadInt16(); + } + + public int ReadInt32() + { + var value = BitConverter.ToInt32(ReadBytes(4), 0); + if (needsAdjusting) + value = IPAddress.NetworkToHostOrder(value); + return value; + } + + public uint ReadUInt32() + { + return (uint)ReadInt32(); + } + + public long ReadInt64() + { + var value = BitConverter.ToInt64(ReadBytes(8), 0); + if (needsAdjusting) + value = IPAddress.NetworkToHostOrder(value); + return value; + } + + public ulong ReadUInt64() + { + return (ulong)ReadInt64(); + } + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/Utilities/SubStream.cs b/src/Sentry.Android.AssemblyReader/ELFSharp/Utilities/SubStream.cs new file mode 100644 index 0000000000..3bedf6d06b --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/Utilities/SubStream.cs @@ -0,0 +1,93 @@ +using System; +using System.IO; + +namespace ELFSharp.Utilities +{ + internal sealed class SubStream : Stream + { + private const string NegativeArgumentMessage = "The argument cannot be negative."; + private const string OutsideStreamMessage = "The argument must be within the wrapped stream."; + private readonly long startingPosition; + + private readonly Stream wrappedStream; + + public SubStream(Stream wrappedStream, long startingPosition, long length) + { + if (startingPosition < 0) + throw new ArgumentException(nameof(startingPosition), NegativeArgumentMessage); + + if (startingPosition > wrappedStream.Length) + throw new ArgumentException(nameof(startingPosition), OutsideStreamMessage); + + if (length < 0) + throw new ArgumentException(nameof(length), NegativeArgumentMessage); + + if (startingPosition + length > wrappedStream.Length) + throw new ArgumentException(nameof(startingPosition), OutsideStreamMessage); + + if (!wrappedStream.CanSeek) + throw new ArgumentException(nameof(wrappedStream), "Wrapped streem has to be seekable."); + ; + this.wrappedStream = wrappedStream; + this.startingPosition = startingPosition; + Length = length; + + wrappedStream.Seek(startingPosition, SeekOrigin.Begin); + } + + public override bool CanRead => wrappedStream.CanRead; + + public override bool CanSeek => wrappedStream.CanSeek; + + public override bool CanWrite => false; + + public override long Length { get; } + + public override long Position + { + get => wrappedStream.Position - startingPosition; + + set => wrappedStream.Position = value + startingPosition; + } + + public override void Flush() + { + wrappedStream.Flush(); + } + + public override int Read(byte[] buffer, int offset, int count) + { + count = (int)Math.Min(count, Length - Position); + return wrappedStream.Read(buffer, offset, count); + } + + public override long Seek(long offset, SeekOrigin origin) + { + // All offsets are adjusted to represent a begin-based offset in + // the original stream, so that we can simplify sanity checks. + + var adjustedOffset = origin switch + { + SeekOrigin.Begin => offset + startingPosition, + SeekOrigin.End => wrappedStream.Length - offset, + SeekOrigin.Current => wrappedStream.Position + offset, + _ => throw new InvalidOperationException("Should never reach here.") + }; + + if (adjustedOffset < startingPosition || adjustedOffset > startingPosition + Length) + throw new ArgumentException(nameof(offset), "Effective offset cannot move outside of the substream."); + + return wrappedStream.Seek(adjustedOffset, SeekOrigin.Begin) - startingPosition; + } + + public override void SetLength(long value) + { + throw new NotSupportedException($"Setting length is not available for {nameof(SubStream)}."); + } + + public override void Write(byte[] buffer, int offset, int count) + { + throw new NotSupportedException($"Writing is not available for {nameof(SubStream)}."); + } + } +} diff --git a/src/Sentry.Android.AssemblyReader/ELFSharp/make-internal.sh b/src/Sentry.Android.AssemblyReader/ELFSharp/make-internal.sh new file mode 100755 index 0000000000..4d04f6dd78 --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/ELFSharp/make-internal.sh @@ -0,0 +1,10 @@ +#!/bin/bash +set -e + +find . -name \*.cs -print0 | xargs -0 sed -E -i '' 's/public sealed class/internal sealed class/g' +find . -name \*.cs -print0 | xargs -0 sed -E -i '' 's/public static class/internal static class/g' +find . -name \*.cs -print0 | xargs -0 sed -E -i '' 's/public partial class/internal partial class/g' +find . -name \*.cs -print0 | xargs -0 sed -E -i '' 's/public class/internal class/g' +find . -name \*.cs -print0 | xargs -0 sed -E -i '' 's/public struct/internal struct/g' +find . -name \*.cs -print0 | xargs -0 sed -E -i '' 's/public enum/internal enum/g' +find . -name \*.cs -print0 | xargs -0 sed -E -i '' 's/public interface/internal interface/g' diff --git a/src/Sentry.Android.AssemblyReader/Sentry.Android.AssemblyReader.csproj b/src/Sentry.Android.AssemblyReader/Sentry.Android.AssemblyReader.csproj index f64bf0f045..2c6dcde33e 100644 --- a/src/Sentry.Android.AssemblyReader/Sentry.Android.AssemblyReader.csproj +++ b/src/Sentry.Android.AssemblyReader/Sentry.Android.AssemblyReader.csproj @@ -1,16 +1,12 @@ - netstandard2.0;net8.0;net9.0 + net8.0;net9.0 .NET assembly reader for Android - - - - - + @@ -30,4 +26,8 @@ + + + + diff --git a/src/Sentry.Android.AssemblyReader/ATTRIBUTION.txt b/src/Sentry.Android.AssemblyReader/V1/ATTRIBUTION.txt similarity index 100% rename from src/Sentry.Android.AssemblyReader/ATTRIBUTION.txt rename to src/Sentry.Android.AssemblyReader/V1/ATTRIBUTION.txt diff --git a/src/Sentry.Android.AssemblyReader/AndroidAssemblyDirectoryReader.cs b/src/Sentry.Android.AssemblyReader/V1/AndroidAssemblyDirectoryReaderV1.cs similarity index 73% rename from src/Sentry.Android.AssemblyReader/AndroidAssemblyDirectoryReader.cs rename to src/Sentry.Android.AssemblyReader/V1/AndroidAssemblyDirectoryReaderV1.cs index b32a11112f..30314a5546 100644 --- a/src/Sentry.Android.AssemblyReader/AndroidAssemblyDirectoryReader.cs +++ b/src/Sentry.Android.AssemblyReader/V1/AndroidAssemblyDirectoryReaderV1.cs @@ -1,9 +1,9 @@ -namespace Sentry.Android.AssemblyReader; +namespace Sentry.Android.AssemblyReader.V1; // The "Old" app type - where each DLL is placed in the 'assemblies' directory as an individual file. -internal sealed class AndroidAssemblyDirectoryReader : AndroidAssemblyReader, IAndroidAssemblyReader +internal sealed class AndroidAssemblyDirectoryReaderV1 : AndroidAssemblyReader, IAndroidAssemblyReader { - public AndroidAssemblyDirectoryReader(ZipArchive zip, IList supportedAbis, DebugLogger? logger) + public AndroidAssemblyDirectoryReaderV1(ZipArchive zip, IList supportedAbis, DebugLogger? logger) : base(zip, supportedAbis, logger) { } public PEReader? TryReadAssembly(string name) @@ -25,13 +25,8 @@ public AndroidAssemblyDirectoryReader(ZipArchive zip, IList supportedAbi Logger?.Invoke("Resolved assembly {0} in the APK at {1}", name, zipEntry.FullName); // We need a seekable stream for the PEReader (or even to check whether the DLL is compressed), so make a copy. - var memStream = new MemoryStream((int)zipEntry.Length); - using (var zipStream = zipEntry.Open()) - { - zipStream.CopyTo(memStream); - memStream.Position = 0; - } - return CreatePEReader(name, memStream); + var memStream = zipEntry.Extract(); + return ArchiveUtils.CreatePEReader(name, memStream, Logger); } private ZipArchiveEntry? FindAssembly(string name) diff --git a/src/Sentry.Android.AssemblyReader/AndroidAssemblyStoreReader.cs b/src/Sentry.Android.AssemblyReader/V1/AndroidAssemblyStoreReaderV1.cs similarity index 97% rename from src/Sentry.Android.AssemblyReader/AndroidAssemblyStoreReader.cs rename to src/Sentry.Android.AssemblyReader/V1/AndroidAssemblyStoreReaderV1.cs index dbdb5451ba..3073cc2095 100644 --- a/src/Sentry.Android.AssemblyReader/AndroidAssemblyStoreReader.cs +++ b/src/Sentry.Android.AssemblyReader/V1/AndroidAssemblyStoreReaderV1.cs @@ -1,11 +1,11 @@ -namespace Sentry.Android.AssemblyReader; +namespace Sentry.Android.AssemblyReader.V1; // See https://devblogs.microsoft.com/dotnet/performance-improvements-in-dotnet-maui/#single-file-assembly-stores -internal sealed class AndroidAssemblyStoreReader : AndroidAssemblyReader, IAndroidAssemblyReader +internal sealed class AndroidAssemblyStoreReaderV1 : AndroidAssemblyReader, IAndroidAssemblyReader { private readonly AssemblyStoreExplorer _explorer; - public AndroidAssemblyStoreReader(ZipArchive zip, IList supportedAbis, DebugLogger? logger) + public AndroidAssemblyStoreReaderV1(ZipArchive zip, IList supportedAbis, DebugLogger? logger) : base(zip, supportedAbis, logger) { _explorer = new(zip, supportedAbis, logger); @@ -29,7 +29,7 @@ public AndroidAssemblyStoreReader(ZipArchive zip, IList supportedAbis, D return null; } - return CreatePEReader(name, stream); + return ArchiveUtils.CreatePEReader(name, stream, Logger); } private AssemblyStoreAssembly? TryFindAssembly(string name) @@ -409,7 +409,7 @@ public AssemblyStoreReader(MemoryStream store, string? arch = null) assembly.ConfigDataOffset == 0 ? null : GetDataSlice(assembly.ConfigDataOffset, assembly.ConfigDataSize); private MemoryStream? GetDataSlice(uint offset, uint size) => - size == 0 ? null : new MemorySlice(_storeData, (int)offset, (int)size); + size == 0 ? null : new ArchiveUtils.MemorySlice(_storeData, (int)offset, (int)size); public bool HasIdenticalContent(AssemblyStoreReader other) { diff --git a/src/Sentry.Android.AssemblyReader/V2/ATTRIBUTION.txt b/src/Sentry.Android.AssemblyReader/V2/ATTRIBUTION.txt new file mode 100644 index 0000000000..e782059c7d --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/V2/ATTRIBUTION.txt @@ -0,0 +1,28 @@ +Parts of the code in this subdirectory have been adapted from +https://github.com/dotnet/android/blob/5ebcb1dd1503648391e3c0548200495f634d90c6/tools/assembly-store-reader-mk2/assembly-store-reader.csproj + +The original license is as follows: + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyDirectoryReaderV2.cs b/src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyDirectoryReaderV2.cs new file mode 100644 index 0000000000..d89f81da79 --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyDirectoryReaderV2.cs @@ -0,0 +1,267 @@ +namespace Sentry.Android.AssemblyReader.V2; + +// The "Old" app type - where each DLL is placed in the 'assemblies' directory as an individual file. +internal sealed class AndroidAssemblyDirectoryReaderV2 : IAndroidAssemblyReader +{ + private DebugLogger? Logger { get; } + private HashSet SupportedArchitectures { get; } = new(); + private readonly ArchiveAssemblyHelper _archiveAssemblyHelper; + + public AndroidAssemblyDirectoryReaderV2(string apkPath, IList supportedAbis, DebugLogger? logger) + { + Logger = logger; + foreach (var abi in supportedAbis) + { + SupportedArchitectures.Add(abi.AbiToDeviceArchitecture()); + } + _archiveAssemblyHelper = new ArchiveAssemblyHelper(apkPath, logger); + } + + public PEReader? TryReadAssembly(string name) + { + if (File.Exists(name)) + { + // The assembly is already extracted to the file system. Just read it. + var stream = File.OpenRead(name); + return new PEReader(stream); + } + + foreach (var arch in SupportedArchitectures) + { + if (_archiveAssemblyHelper.ReadEntry($"assemblies/{name}", arch) is not { } memStream) + { + continue; + } + + Logger?.Invoke("Resolved assembly {0} in the APK", name); + return ArchiveUtils.CreatePEReader(name, memStream, Logger); + } + + Logger?.Invoke("Couldn't find assembly {0} in the APK", name); + return null; + } + + public void Dispose() + { + // No-op + } + + /* + * Adapted from https://github.com/dotnet/android/blob/6394773fad5108b0d7b4e6f087dc3e6ea997401a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/ArchiveAssemblyHelper.cs + * Original code licensed under the MIT License (https://github.com/dotnet/android-tools/blob/ab2165daf27d4fcb29e88bc022e0ab0be33aff69/LICENSE) + */ + internal class ArchiveAssemblyHelper + { + private static readonly ArrayPool Buffers = ArrayPool.Shared; + + private readonly string _archivePath; + private readonly DebugLogger? _logger; + + public ArchiveAssemblyHelper(string archivePath, DebugLogger? logger) + { + if (string.IsNullOrEmpty(archivePath)) + { + throw new ArgumentException("must not be null or empty", nameof(archivePath)); + } + + _archivePath = archivePath; + _logger = logger; + } + + public MemoryStream? ReadEntry(string path, AndroidTargetArch arch = AndroidTargetArch.None, bool uncompressIfNecessary = false) + { + var ret = ReadZipEntry(path, arch); + if (ret == null) + { + return null; + } + + ret.Flush(); + ret.Seek(0, SeekOrigin.Begin); + var (elfPayloadOffset, elfPayloadSize, error) = Utils.FindELFPayloadSectionOffsetAndSize(ret); + + if (error != ELFPayloadError.None) + { + var message = error switch + { + ELFPayloadError.NotELF => $"Entry '{path}' is not a valid ELF binary", + ELFPayloadError.LoadFailed => $"Entry '{path}' could not be loaded", + ELFPayloadError.NotSharedLibrary => $"Entry '{path}' is not a shared ELF library", + ELFPayloadError.NotLittleEndian => $"Entry '{path}' is not a little-endian ELF image", + ELFPayloadError.NoPayloadSection => $"Entry '{path}' does not contain the 'payload' section", + _ => $"Unknown ELF payload section error for entry '{path}': {error}" + }; + _logger?.Invoke(message); + } + else + { + _logger?.Invoke($"Extracted content from ELF image '{path}'"); + } + + if (elfPayloadOffset == 0) + { + ret.Seek(0, SeekOrigin.Begin); + return ret; + } + + // Make a copy of JUST the payload section, so that it contains only the data the tests expect and support + var payload = new MemoryStream(); + var data = Buffers.Rent(16384); + var toRead = data.Length; + var nRead = 0; + var remaining = elfPayloadSize; + + ret.Seek((long)elfPayloadOffset, SeekOrigin.Begin); + while (remaining > 0 && (nRead = ret.Read(data, 0, toRead)) > 0) + { + payload.Write(data, 0, nRead); + remaining -= (ulong)nRead; + + if (remaining < (ulong)data.Length) + { + // Make sure the last chunk doesn't gobble in more than we need + toRead = (int)remaining; + } + } + Buffers.Return(data); + + payload.Flush(); + ret.Dispose(); + + payload.Seek(0, SeekOrigin.Begin); + return payload; + } + + private MemoryStream? ReadZipEntry(string path, AndroidTargetArch arch) + { + var potentialEntries = TransformArchiveAssemblyPath(path, arch); + if (potentialEntries == null || potentialEntries.Count == 0) + { + return null; + } + + using var zip = ZipFile.OpenRead(_archivePath); + foreach (var assemblyPath in potentialEntries) + { + if (zip.GetEntry(assemblyPath) is not { } entry) + { + continue; + } + + var ret = entry.Extract(); + ret.Flush(); + return ret; + } + + return null; + } + + /// + /// Takes "old style" `assemblies/assembly.dll` path and returns (if possible) a set of paths that reflect the new + /// location of `lib/{ARCH}/assembly.dll.so`. A list is returned because, if `arch` is `None`, we'll return all + /// the possible architectural paths. + /// An exception is thrown if we cannot transform the path for some reason. It should **not** be handled. + /// + private static List? TransformArchiveAssemblyPath(string path, AndroidTargetArch arch) + { + if (string.IsNullOrEmpty(path)) + { + throw new ArgumentException(nameof(path), "must not be null or empty"); + } + + if (!path.StartsWith("assemblies/", StringComparison.Ordinal)) + { + return [path]; + } + + var parts = path.Split('/'); + if (parts.Length < 2) + { + throw new InvalidOperationException($"Path '{path}' must consist of at least two segments separated by `/`"); + } + + // We accept: + // assemblies/assembly.dll + // assemblies/{CULTURE}/assembly.dll + // assemblies/{ABI}/assembly.dll + // assemblies/{ABI}/{CULTURE}/assembly.dll + if (parts.Length > 4) + { + throw new InvalidOperationException($"Path '{path}' must not consist of more than 4 segments separated by `/`"); + } + + string? fileName = null; + string? culture = null; + string? abi = null; + + switch (parts.Length) + { + // Full satellite assembly path, with abi + case 4: + abi = parts[1]; + culture = parts[2]; + fileName = parts[3]; + break; + + // Assembly path with abi or culture + case 3: + // If the middle part isn't a valid abi, we treat it as a culture name + if (MonoAndroidHelper.IsValidAbi(parts[1])) + { + abi = parts[1]; + } + else + { + culture = parts[1]; + } + fileName = parts[2]; + break; + + // Assembly path without abi or culture + case 2: + fileName = parts[1]; + break; + } + + var fileTypeMarker = MonoAndroidHelper.MANGLED_ASSEMBLY_REGULAR_ASSEMBLY_MARKER; + var abis = new List(); + if (!string.IsNullOrEmpty(abi)) + { + abis.Add(abi); + } + else if (arch == AndroidTargetArch.None) + { + foreach (AndroidTargetArch targetArch in MonoAndroidHelper.SupportedTargetArchitectures) + { + abis.Add(MonoAndroidHelper.ArchToAbi(targetArch)); + } + } + else + { + abis.Add(MonoAndroidHelper.ArchToAbi(arch)); + } + + if (!string.IsNullOrEmpty(culture)) + { + // Android doesn't allow us to put satellite assemblies in lib/{CULTURE}/assembly.dll.so, we must instead + // mangle the name. + fileTypeMarker = MonoAndroidHelper.MANGLED_ASSEMBLY_SATELLITE_ASSEMBLY_MARKER; + fileName = $"{culture}{MonoAndroidHelper.SATELLITE_CULTURE_END_MARKER_CHAR}{fileName}"; + } + + var ret = new List(); + var newParts = new List { + string.Empty, // ABI placeholder + $"{fileTypeMarker}{fileName}.so", + }; + + foreach (var a in abis) + { + newParts[0] = a; + ret.Add(MonoAndroidHelper.MakeZipArchivePath("lib", newParts)); + } + + return ret; + } + } +} diff --git a/src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyStoreReaderV2.cs b/src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyStoreReaderV2.cs new file mode 100644 index 0000000000..73b170a0c5 --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyStoreReaderV2.cs @@ -0,0 +1,117 @@ +namespace Sentry.Android.AssemblyReader.V2; + +internal class AndroidAssemblyStoreReaderV2 : IAndroidAssemblyReader +{ + private readonly IList _explorers; + private readonly DebugLogger? _logger; + + private AndroidAssemblyStoreReaderV2(IList explorers, DebugLogger? logger) + { + _explorers = explorers; + _logger = logger; + } + + public static bool TryReadStore(string inputFile, IList supportedAbis, DebugLogger? logger, [NotNullWhen(true)] out AndroidAssemblyStoreReaderV2? reader) + { + var (explorers, errorMessage) = AssemblyStoreExplorer.Open(inputFile, logger); + if (errorMessage != null) + { + logger?.Invoke(errorMessage); + reader = null; + return false; + } + + List supportedExplorers = []; + if (explorers is not null) + { + foreach (var explorer in explorers) + { + if (explorer.TargetArch is null) + { + continue; + } + + foreach (var supportedAbi in supportedAbis) + { + if (supportedAbi.AbiToDeviceArchitecture() == explorer.TargetArch) + { + supportedExplorers.Add(explorer); + } + } + } + } + + if (supportedExplorers.Count == 0) + { + logger?.Invoke("Could not find V2 AssemblyStoreExplorer for the supported ABIs: {0}", string.Join(", ", supportedAbis)); + reader = null; + return false; + } + + reader = new AndroidAssemblyStoreReaderV2(supportedExplorers, logger); + return true; + } + + public PEReader? TryReadAssembly(string name) + { + var explorerAssembly = TryFindAssembly(name); + if (explorerAssembly is null) + { + _logger?.Invoke("Couldn't find assembly {0} in the APK AssemblyStore", name); + return null; + } + + var (explorer, storeItem) = explorerAssembly; + _logger?.Invoke("Resolved assembly {0} in the APK {1} AssemblyStore", name, storeItem.TargetArch); + + var stream = explorer.ReadImageData(storeItem, false); + if (stream is null) + { + _logger?.Invoke("Couldn't access assembly {0} image stream", name); + return null; + } + + return ArchiveUtils.CreatePEReader(name, stream, _logger); + } + + private ExplorerStoreItem? TryFindAssembly(string name) + { + if (FindBestAssembly(name, out var assembly)) + { + return assembly; + } + + if ((IsFileType(".dll") || IsFileType(".exe")) && FindBestAssembly(name[..^4], out assembly)) + { + return assembly; + } + + return null; + + bool IsFileType(string extension) + { + return name.EndsWith(extension, ignoreCase: true, CultureInfo.InvariantCulture); + } + } + + private bool FindBestAssembly(string name, out ExplorerStoreItem? explorerAssembly) + { + foreach (var explorer in _explorers) + { + if (explorer.AssembliesByName?.TryGetValue(name, out var assembly) is true) + { + explorerAssembly = new(explorer, assembly); + return true; + } + } + explorerAssembly = null; + return false; + } + + private record ExplorerStoreItem(AssemblyStoreExplorer Explorer, AssemblyStoreItem StoreItem); + + public void Dispose() + { + // No-op + } +} diff --git a/src/Sentry.Android.AssemblyReader/V2/AndroidTargetArch.cs b/src/Sentry.Android.AssemblyReader/V2/AndroidTargetArch.cs new file mode 100644 index 0000000000..8564e04e6f --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/V2/AndroidTargetArch.cs @@ -0,0 +1,18 @@ +/* + * Adapted from https://github.com/dotnet/android-tools/blob/ab2165daf27d4fcb29e88bc022e0ab0be33aff69/src/Xamarin.Android.Tools.AndroidSdk/AndroidTargetArch.cs + * Original code licensed under the MIT License (https://github.com/dotnet/android-tools/blob/ab2165daf27d4fcb29e88bc022e0ab0be33aff69/LICENSE) + */ + +namespace Sentry.Android.AssemblyReader.V2; + +[Flags] +internal enum AndroidTargetArch +{ + None = 0, + Arm = 1, + X86 = 2, + Mips = 4, + Arm64 = 8, + X86_64 = 16, + Other = 0x10000 // hope it's not too optimistic +} diff --git a/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreExplorer.cs b/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreExplorer.cs new file mode 100644 index 0000000000..de2148ddeb --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreExplorer.cs @@ -0,0 +1,132 @@ +/* + * Adapted from https://github.com/dotnet/android/blob/5ebcb1dd1503648391e3c0548200495f634d90c6/tools/assembly-store-reader-mk2/AssemblyStore/AssemblyStoreExplorer.cs + * Original code licensed under the MIT License (https://github.com/dotnet/android/blob/5ebcb1dd1503648391e3c0548200495f634d90c6/LICENSE.TXT) + */ + +namespace Sentry.Android.AssemblyReader.V2; + +internal class AssemblyStoreExplorer +{ + private readonly AssemblyStoreReader _reader; + + public AndroidTargetArch? TargetArch { get; } + public IList? Assemblies { get; } + public IDictionary? AssembliesByName { get; } + public bool Is64Bit { get; } + + private AssemblyStoreExplorer(Stream storeStream, string path, DebugLogger? logger) + { + var storeReader = AssemblyStoreReader.Create(storeStream, path, logger); + if (storeReader == null) + { + storeStream.Dispose(); + throw new NotSupportedException($"Format of assembly store '{path}' is unsupported"); + } + + _reader = storeReader; + TargetArch = _reader.TargetArch; + Assemblies = _reader.Assemblies; + Is64Bit = _reader.Is64Bit; + + var dict = new Dictionary(StringComparer.Ordinal); + if (Assemblies is not null) + { + foreach (var item in Assemblies) + { + dict.Add(item.Name, item); + } + } + AssembliesByName = dict.AsReadOnly(); + } + + private AssemblyStoreExplorer(FileInfo storeInfo, DebugLogger? logger) + : this(storeInfo.OpenRead(), storeInfo.FullName, logger) + { } + + public static (IList? explorers, string? errorMessage) Open(string inputFile, DebugLogger? logger) + { + var (format, info) = Utils.DetectFileFormat(inputFile); + if (info == null) + { + return (null, $"File '{inputFile}' does not exist."); + } + + switch (format) + { + case FileFormat.Unknown: + return (null, $"File '{inputFile}' has an unknown format."); + + case FileFormat.Zip: + return (null, $"File '{inputFile}' is a ZIP archive, but not an Android one."); + + case FileFormat.AssemblyStore: + case FileFormat.ELF: + return (new List { new AssemblyStoreExplorer(info, logger) }, null); + + case FileFormat.Aab: + return OpenAab(info, logger); + + case FileFormat.AabBase: + return OpenAabBase(info, logger); + + case FileFormat.Apk: + return OpenApk(info, logger); + + default: + return (null, $"File '{inputFile}' has an unsupported format '{format}'"); + } + } + + private static (IList? explorers, string? errorMessage) OpenAab(FileInfo fi, DebugLogger? logger) + => OpenCommon(fi, [StoreReaderV2.AabPaths, StoreReader_V1.AabPaths], logger); + + private static (IList? explorers, string? errorMessage) OpenAabBase(FileInfo fi, DebugLogger? logger) + => OpenCommon(fi, [StoreReaderV2.AabBasePaths, StoreReader_V1.AabBasePaths], logger); + + private static (IList? explorers, string? errorMessage) OpenApk(FileInfo fi, DebugLogger? logger) + => OpenCommon(fi, [StoreReaderV2.ApkPaths, StoreReader_V1.ApkPaths], logger); + + private static (IList? explorers, string? errorMessage) OpenCommon(FileInfo fi, List> pathLists, DebugLogger? logger) + { + using var zip = ZipFile.OpenRead(fi.FullName); + + foreach (var paths in pathLists) + { + var (explorers, errorMessage, pathsFound) = TryLoad(fi, zip, paths, logger); + if (pathsFound) + { + return (explorers, errorMessage); + } + } + + return (null, "Unable to find any blob entries"); + } + + private static (IList? explorers, string? errorMessage, bool pathsFound) TryLoad(FileInfo fi, ZipArchive zip, IList paths, DebugLogger? logger) + { + var ret = new List(); + + foreach (var path in paths) + { + if (zip.GetEntry(path) is not { } entry) + { + continue; + } + + var stream = entry.Extract(); + ret.Add(new AssemblyStoreExplorer(stream, $"{fi.FullName}!{path}", logger)); + } + + if (ret.Count == 0) + { + return (null, null, false); + } + + return (ret, null, true); + } + + public MemoryStream? ReadImageData(AssemblyStoreItem item, bool uncompressIfNeeded = false) + { + return _reader.ReadEntryImageData(item, uncompressIfNeeded); + } +} diff --git a/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreItem.cs b/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreItem.cs new file mode 100644 index 0000000000..a132f51ead --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreItem.cs @@ -0,0 +1,27 @@ +/* + * Adapted from https://github.com/dotnet/android/blob/86260ed36dfe1a90c8ed6a2bb1cd0607d637f403/tools/assembly-store-reader-mk2/AssemblyStore/AssemblyStoreItem.cs + * Original code licensed under the MIT License (https://github.com/dotnet/android/blob/5ebcb1dd1503648391e3c0548200495f634d90c6/LICENSE.TXT) + */ + +namespace Sentry.Android.AssemblyReader.V2; + +internal abstract class AssemblyStoreItem +{ + public string Name { get; } + public IList Hashes { get; } + public bool Is64Bit { get; } + public uint DataOffset { get; protected set; } + public uint DataSize { get; protected set; } + public uint DebugOffset { get; protected set; } + public uint DebugSize { get; protected set; } + public uint ConfigOffset { get; protected set; } + public uint ConfigSize { get; protected set; } + public AndroidTargetArch TargetArch { get; protected set; } + + protected AssemblyStoreItem(string name, bool is64Bit, List hashes) + { + Name = name; + Hashes = hashes.AsReadOnly(); + Is64Bit = is64Bit; + } +} diff --git a/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreReader.cs b/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreReader.cs new file mode 100644 index 0000000000..834cb2c36d --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreReader.cs @@ -0,0 +1,93 @@ +/* + * Adapted from https://github.com/dotnet/android/blob/5ebcb1dd1503648391e3c0548200495f634d90c6/tools/assembly-store-reader-mk2/AssemblyStore/AssemblyStoreReader.cs + * Original code licensed under the MIT License (https://github.com/dotnet/android/blob/5ebcb1dd1503648391e3c0548200495f634d90c6/LICENSE.TXT) + */ + +namespace Sentry.Android.AssemblyReader.V2; + +internal abstract class AssemblyStoreReader +{ + protected DebugLogger? Logger { get; } + + private static readonly UTF8Encoding ReaderEncoding = new UTF8Encoding(false); + + protected Stream StoreStream { get; } + + public abstract string Description { get; } + public abstract bool NeedsExtensionInName { get; } + public string StorePath { get; } + + public AndroidTargetArch TargetArch { get; protected set; } = AndroidTargetArch.Arm; + public uint AssemblyCount { get; protected set; } + public uint IndexEntryCount { get; protected set; } + public IList? Assemblies { get; protected set; } + public bool Is64Bit { get; protected set; } + + protected AssemblyStoreReader(Stream store, string path, DebugLogger? logger) + { + StoreStream = store; + StorePath = path; + Logger = logger; + } + + public static AssemblyStoreReader? Create(Stream store, string path, DebugLogger? logger) + { + var reader = MakeReaderReady(new StoreReader_V1(store, path, logger)); + if (reader != null) + { + return reader; + } + + reader = MakeReaderReady(new StoreReaderV2(store, path, logger)); + if (reader != null) + { + return reader; + } + + return null; + } + + private static AssemblyStoreReader? MakeReaderReady(AssemblyStoreReader reader) + { + if (!reader.IsSupported()) + { + return null; + } + + reader.Prepare(); + return reader; + } + + protected BinaryReader CreateReader() => new BinaryReader(StoreStream, ReaderEncoding, leaveOpen: true); + + protected abstract bool IsSupported(); + protected abstract void Prepare(); + protected abstract ulong GetStoreStartDataOffset(); + + public MemoryStream ReadEntryImageData(AssemblyStoreItem entry, bool uncompressIfNeeded = false) + { + ulong startOffset = GetStoreStartDataOffset(); + StoreStream.Seek((uint)startOffset + entry.DataOffset, SeekOrigin.Begin); + var stream = new MemoryStream(); + + if (uncompressIfNeeded) + { + throw new NotImplementedException(); + } + + const long BufferSize = 65535; + byte[] buffer = Utils.BytePool.Rent((int)BufferSize); + long remainingToRead = entry.DataSize; + + while (remainingToRead > 0) + { + int nread = StoreStream.Read(buffer, 0, (int)Math.Min(BufferSize, remainingToRead)); + stream.Write(buffer, 0, nread); + remainingToRead -= (long)nread; + } + stream.Flush(); + stream.Seek(0, SeekOrigin.Begin); + + return stream; + } +} diff --git a/src/Sentry.Android.AssemblyReader/V2/DeviceArchitectureExtensions.cs b/src/Sentry.Android.AssemblyReader/V2/DeviceArchitectureExtensions.cs new file mode 100644 index 0000000000..692789686f --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/V2/DeviceArchitectureExtensions.cs @@ -0,0 +1,15 @@ +namespace Sentry.Android.AssemblyReader.V2; + +internal static class DeviceArchitectureExtensions +{ + public static AndroidTargetArch AbiToDeviceArchitecture(this string abi) => + abi switch + { + "armeabi-v7a" => AndroidTargetArch.Arm, + "arm64-v8a" => AndroidTargetArch.Arm64, + "x86" => AndroidTargetArch.X86, + "x86_64" => AndroidTargetArch.X86_64, + "mips" => AndroidTargetArch.Mips, + _ => AndroidTargetArch.Other, + }; +} diff --git a/src/Sentry.Android.AssemblyReader/V2/ELFPayloadError.cs b/src/Sentry.Android.AssemblyReader/V2/ELFPayloadError.cs new file mode 100644 index 0000000000..b7644d68af --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/V2/ELFPayloadError.cs @@ -0,0 +1,16 @@ +/* + * Adapted from https://github.com/dotnet/android/blob/5ebcb1dd1503648391e3c0548200495f634d90c6/tools/assembly-store-reader-mk2/AssemblyStore/ELFPayloadError.cs + * Original code licensed under the MIT License (https://github.com/dotnet/android/blob/5ebcb1dd1503648391e3c0548200495f634d90c6/LICENSE.TXT) + */ + +namespace Sentry.Android.AssemblyReader.V2; + +internal enum ELFPayloadError +{ + None, + NotELF, + LoadFailed, + NotSharedLibrary, + NotLittleEndian, + NoPayloadSection, +} diff --git a/src/Sentry.Android.AssemblyReader/V2/FileFormat.cs b/src/Sentry.Android.AssemblyReader/V2/FileFormat.cs new file mode 100644 index 0000000000..d1cef2ad05 --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/V2/FileFormat.cs @@ -0,0 +1,17 @@ +/* + * Adapted from https://github.com/dotnet/android/blob/5ebcb1dd1503648391e3c0548200495f634d90c6/tools/assembly-store-reader-mk2/AssemblyStore/FileFormat.cs + * Original code licensed under the MIT License (https://github.com/dotnet/android/blob/5ebcb1dd1503648391e3c0548200495f634d90c6/LICENSE.TXT) + */ + +namespace Sentry.Android.AssemblyReader.V2; + +internal enum FileFormat +{ + Aab, + AabBase, + Apk, + AssemblyStore, + ELF, + Zip, + Unknown, +} diff --git a/src/Sentry.Android.AssemblyReader/V2/MonoAndroidHelper.Basic.cs b/src/Sentry.Android.AssemblyReader/V2/MonoAndroidHelper.Basic.cs new file mode 100644 index 0000000000..fda2ff8ad1 --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/V2/MonoAndroidHelper.Basic.cs @@ -0,0 +1,92 @@ +/* + * Adapted from https://github.com/dotnet/android/blob/3822f2b1ee7061813b1d456af22e043e66e2f698/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.Basic.cs + * Original code licensed under the MIT License (https://github.com/dotnet/android/blob/5ebcb1dd1503648391e3c0548200495f634d90c6/LICENSE.TXT) + */ + +namespace Sentry.Android.AssemblyReader.V2; + +internal static class MonoAndroidHelper +{ + private static class AndroidAbi + { + public const string Arm32 = "armeabi-v7a"; + public const string Arm64 = "arm64-v8a"; + public const string X86 = "x86"; + public const string X64 = "x86_64"; + } + + private static class RuntimeIdentifier + { + public const string Arm32 = "android-arm"; + public const string Arm64 = "android-arm64"; + public const string X86 = "android-x86"; + public const string X64 = "android-x64"; + } + + public static readonly HashSet SupportedTargetArchitectures = + [ + AndroidTargetArch.Arm, + AndroidTargetArch.Arm64, + AndroidTargetArch.X86, + AndroidTargetArch.X86_64 + ]; + private static readonly char[] ZipPathTrimmedChars = { '/', '\\' }; + private static readonly Dictionary AbiToRidMap = new(StringComparer.OrdinalIgnoreCase) { + { AndroidAbi.Arm32, RuntimeIdentifier.Arm32 }, + { AndroidAbi.Arm64, RuntimeIdentifier.Arm64 }, + { AndroidAbi.X86, RuntimeIdentifier.X86 }, + { AndroidAbi.X64, RuntimeIdentifier.X64 }, + }; + private static readonly Dictionary ArchToAbiMap = new Dictionary { + { AndroidTargetArch.Arm, AndroidAbi.Arm32 }, + { AndroidTargetArch.Arm64, AndroidAbi.Arm64 }, + { AndroidTargetArch.X86, AndroidAbi.X86 }, + { AndroidTargetArch.X86_64, AndroidAbi.X64 }, + }; + + public static string ArchToAbi(AndroidTargetArch arch) + { + if (!ArchToAbiMap.TryGetValue(arch, out var abi)) + { + throw new InvalidOperationException($"Internal error: unsupported architecture '{arch}'"); + } + + return abi; + } + + public static bool IsValidAbi(string abi) => AbiToRidMap.ContainsKey(abi); + + public static string MakeZipArchivePath(string part1, ICollection? pathParts) + { + var parts = new List(); + if (!string.IsNullOrEmpty(part1)) + { + parts.Add(part1.TrimEnd(ZipPathTrimmedChars)); + } + + if (pathParts != null && pathParts.Count > 0) + { + foreach (string p in pathParts) + { + if (string.IsNullOrEmpty(p)) + { + continue; + } + parts.Add(p.TrimEnd(ZipPathTrimmedChars)); + } + } + + if (parts.Count == 0) + { + return string.Empty; + } + + return string.Join("/", parts); + } + + // These 3 MUST be the same as the like-named constants in src/monodroid/jni/shared-constants.hh + public const string MANGLED_ASSEMBLY_NAME_EXT = ".so"; + public const string MANGLED_ASSEMBLY_REGULAR_ASSEMBLY_MARKER = "lib_"; + public const string MANGLED_ASSEMBLY_SATELLITE_ASSEMBLY_MARKER = "lib-"; + public const string SATELLITE_CULTURE_END_MARKER_CHAR = "_"; +} diff --git a/src/Sentry.Android.AssemblyReader/V2/StoreReaderV1.cs b/src/Sentry.Android.AssemblyReader/V2/StoreReaderV1.cs new file mode 100644 index 0000000000..c1a3fe3c2d --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/V2/StoreReaderV1.cs @@ -0,0 +1,38 @@ +/* + * Adapted from https://github.com/dotnet/android/blob/5ebcb1dd1503648391e3c0548200495f634d90c6/tools/assembly-store-reader-mk2/AssemblyStore/StoreReader_V1.cs + * Original code licensed under the MIT License (https://github.com/dotnet/android/blob/5ebcb1dd1503648391e3c0548200495f634d90c6/LICENSE.TXT) + */ + +namespace Sentry.Android.AssemblyReader.V2; + +internal class StoreReader_V1 : AssemblyStoreReader +{ + public override string Description => "Assembly store v1"; + public override bool NeedsExtensionInName => false; + + public static IList ApkPaths { get; } + public static IList AabPaths { get; } + public static IList AabBasePaths { get; } + + static StoreReader_V1() + { + ApkPaths = new List().AsReadOnly(); + AabPaths = new List().AsReadOnly(); + AabBasePaths = new List().AsReadOnly(); + } + + public StoreReader_V1(Stream store, string path, DebugLogger? logger) + : base(store, path, logger) + { } + + protected override bool IsSupported() + { + return false; + } + + protected override void Prepare() + { + } + + protected override ulong GetStoreStartDataOffset() => 0; +} diff --git a/src/Sentry.Android.AssemblyReader/V2/StoreReaderV2.Classes.cs b/src/Sentry.Android.AssemblyReader/V2/StoreReaderV2.Classes.cs new file mode 100644 index 0000000000..85aa91ba89 --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/V2/StoreReaderV2.Classes.cs @@ -0,0 +1,96 @@ +/* + * Adapted from https://github.com/dotnet/android/blob/5ebcb1dd1503648391e3c0548200495f634d90c6/tools/assembly-store-reader-mk2/AssemblyStore/StoreReader_V2.Classes.cs + * Original code licensed under the MIT License (https://github.com/dotnet/android/blob/5ebcb1dd1503648391e3c0548200495f634d90c6/LICENSE.TXT) + */ + +namespace Sentry.Android.AssemblyReader.V2; + +internal partial class StoreReaderV2 +{ + private sealed class Header + { + public const uint NativeSize = 5 * sizeof(uint); + + public readonly uint magic; + public readonly uint version; + public readonly uint entry_count; + public readonly uint index_entry_count; + + // Index size in bytes + public readonly uint index_size; + + public Header(uint magic, uint version, uint entry_count, uint index_entry_count, uint index_size) + { + this.magic = magic; + this.version = version; + this.entry_count = entry_count; + this.index_entry_count = index_entry_count; + this.index_size = index_size; + } + } + + private sealed class IndexEntry + { + public readonly ulong name_hash; + public readonly uint descriptor_index; + + public IndexEntry(ulong name_hash, uint descriptor_index) + { + this.name_hash = name_hash; + this.descriptor_index = descriptor_index; + } + } + + private sealed class EntryDescriptor + { + public uint mapping_index; + + public uint data_offset; + public uint data_size; + + public uint debug_data_offset; + public uint debug_data_size; + + public uint config_data_offset; + public uint config_data_size; + } + + private sealed class StoreItemV2 : AssemblyStoreItem + { + public StoreItemV2(AndroidTargetArch targetArch, string name, bool is64Bit, List indexEntries, EntryDescriptor descriptor) + : base(name, is64Bit, IndexToHashes(indexEntries)) + { + DataOffset = descriptor.data_offset; + DataSize = descriptor.data_size; + DebugOffset = descriptor.debug_data_offset; + DebugSize = descriptor.debug_data_size; + ConfigOffset = descriptor.config_data_offset; + ConfigSize = descriptor.config_data_size; + TargetArch = targetArch; + } + + private static List IndexToHashes(List indexEntries) + { + var ret = new List(); + foreach (var ie in indexEntries) + { + ret.Add(ie.name_hash); + } + + return ret; + } + } + + private sealed class TemporaryItem + { + public readonly string Name; + public readonly List IndexEntries = new List(); + public readonly EntryDescriptor Descriptor; + + public TemporaryItem(string name, EntryDescriptor descriptor) + { + Name = name; + Descriptor = descriptor; + } + } +} diff --git a/src/Sentry.Android.AssemblyReader/V2/StoreReaderV2.cs b/src/Sentry.Android.AssemblyReader/V2/StoreReaderV2.cs new file mode 100644 index 0000000000..6a40e05bc0 --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/V2/StoreReaderV2.cs @@ -0,0 +1,241 @@ +/* + * Adapted from https://github.com/dotnet/android/blob/5ebcb1dd1503648391e3c0548200495f634d90c6/tools/assembly-store-reader-mk2/AssemblyStore/StoreReader_V2.cs + * Original code licensed under the MIT License (https://github.com/dotnet/android/blob/5ebcb1dd1503648391e3c0548200495f634d90c6/LICENSE.TXT) + */ + +namespace Sentry.Android.AssemblyReader.V2; + +internal partial class StoreReaderV2 : AssemblyStoreReader +{ + // Bit 31 is set for 64-bit platforms, cleared for the 32-bit ones + private const uint ASSEMBLY_STORE_FORMAT_VERSION_64BIT = 0x80000002; // Must match the ASSEMBLY_STORE_FORMAT_VERSION native constant + private const uint ASSEMBLY_STORE_FORMAT_VERSION_32BIT = 0x00000002; + private const uint ASSEMBLY_STORE_FORMAT_VERSION_MASK = 0xF0000000; + private const uint ASSEMBLY_STORE_ABI_AARCH64 = 0x00010000; + private const uint ASSEMBLY_STORE_ABI_ARM = 0x00020000; + private const uint ASSEMBLY_STORE_ABI_X64 = 0x00030000; + private const uint ASSEMBLY_STORE_ABI_X86 = 0x00040000; + private const uint ASSEMBLY_STORE_ABI_MASK = 0x00FF0000; + + public override string Description => "Assembly store v2"; + public override bool NeedsExtensionInName => true; + + public static IList ApkPaths { get; } + public static IList AabPaths { get; } + public static IList AabBasePaths { get; } + + private readonly HashSet supportedVersions; + private Header? header; + private ulong elfOffset = 0; + + static StoreReaderV2() + { + var paths = new List { + GetArchPath (AndroidTargetArch.Arm64), + GetArchPath (AndroidTargetArch.Arm), + GetArchPath (AndroidTargetArch.X86_64), + GetArchPath (AndroidTargetArch.X86), + }; + ApkPaths = paths.AsReadOnly(); + AabBasePaths = ApkPaths; + + const string AabBaseDir = "base"; + paths = new List { + GetArchPath (AndroidTargetArch.Arm64, AabBaseDir), + GetArchPath (AndroidTargetArch.Arm, AabBaseDir), + GetArchPath (AndroidTargetArch.X86_64, AabBaseDir), + GetArchPath (AndroidTargetArch.X86, AabBaseDir), + }; + AabPaths = paths.AsReadOnly(); + + string GetArchPath(AndroidTargetArch arch, string? root = null) + { + const string LibDirName = "lib"; + + string abi = MonoAndroidHelper.ArchToAbi(arch); + var parts = new List(); + if (!string.IsNullOrEmpty(root)) + { + parts.Add(LibDirName); + } + else + { + root = LibDirName; + } + parts.Add(abi); + parts.Add(GetBlobName(abi)); + + return MonoAndroidHelper.MakeZipArchivePath(root, parts); + } + } + + public StoreReaderV2(Stream store, string path, DebugLogger? logger) + : base(store, path, logger) + { + supportedVersions = new HashSet { + ASSEMBLY_STORE_FORMAT_VERSION_64BIT | ASSEMBLY_STORE_ABI_AARCH64, + ASSEMBLY_STORE_FORMAT_VERSION_64BIT | ASSEMBLY_STORE_ABI_X64, + ASSEMBLY_STORE_FORMAT_VERSION_32BIT | ASSEMBLY_STORE_ABI_ARM, + ASSEMBLY_STORE_FORMAT_VERSION_32BIT | ASSEMBLY_STORE_ABI_X86, + }; + } + + private static string GetBlobName(string abi) => $"libassemblies.{abi}.blob.so"; + + protected override ulong GetStoreStartDataOffset() => elfOffset; + + protected override bool IsSupported() + { + StoreStream.Seek(0, SeekOrigin.Begin); + using var reader = CreateReader(); + + uint magic = reader.ReadUInt32(); + if (magic == Utils.ELFMagic) + { + ELFPayloadError error; + (elfOffset, _, error) = Utils.FindELFPayloadSectionOffsetAndSize(StoreStream); + + if (error != ELFPayloadError.None) + { + string message = error switch + { + ELFPayloadError.NotELF => $"Store '{StorePath}' is not a valid ELF binary", + ELFPayloadError.LoadFailed => $"Store '{StorePath}' could not be loaded", + ELFPayloadError.NotSharedLibrary => $"Store '{StorePath}' is not a shared ELF library", + ELFPayloadError.NotLittleEndian => $"Store '{StorePath}' is not a little-endian ELF image", + ELFPayloadError.NoPayloadSection => $"Store '{StorePath}' does not contain the 'payload' section", + _ => $"Unknown ELF payload section error for store '{StorePath}': {error}" + }; + Logger?.Invoke(message); + // Was originally: + // ``` + // } else if (elfOffset >= 0) { + // ``` + // However since elfOffset is an ulong, it will never be less than 0 + } + else + { + StoreStream.Seek((long)elfOffset, SeekOrigin.Begin); + magic = reader.ReadUInt32(); + } + } + + if (magic != Utils.AssemblyStoreMagic) + { + Logger?.Invoke("Store '{0}' has invalid header magic number.", StorePath); + return false; + } + + uint version = reader.ReadUInt32(); + if (!supportedVersions.Contains(version)) + { + Logger?.Invoke("Store '{0}' has unsupported version 0x{1:x}", StorePath, version); + return false; + } + + uint entry_count = reader.ReadUInt32(); + uint index_entry_count = reader.ReadUInt32(); + uint index_size = reader.ReadUInt32(); + + header = new Header(magic, version, entry_count, index_entry_count, index_size); + return true; + } + + protected override void Prepare() + { + if (header == null) + { + throw new InvalidOperationException("Internal error: header not set, was IsSupported() called?"); + } + + TargetArch = (header.version & ASSEMBLY_STORE_ABI_MASK) switch + { + ASSEMBLY_STORE_ABI_AARCH64 => AndroidTargetArch.Arm64, + ASSEMBLY_STORE_ABI_ARM => AndroidTargetArch.Arm, + ASSEMBLY_STORE_ABI_X64 => AndroidTargetArch.X86_64, + ASSEMBLY_STORE_ABI_X86 => AndroidTargetArch.X86, + _ => throw new NotSupportedException($"Unsupported ABI in store version: 0x{header.version:x}") + }; + + Is64Bit = (header.version & ASSEMBLY_STORE_FORMAT_VERSION_MASK) != 0; + AssemblyCount = header.entry_count; + IndexEntryCount = header.index_entry_count; + + StoreStream.Seek((long)elfOffset + Header.NativeSize, SeekOrigin.Begin); + using var reader = CreateReader(); + + var index = new List(); + for (uint i = 0; i < header.index_entry_count; i++) + { + ulong name_hash; + if (Is64Bit) + { + name_hash = reader.ReadUInt64(); + } + else + { + name_hash = (ulong)reader.ReadUInt32(); + } + + uint descriptor_index = reader.ReadUInt32(); + index.Add(new IndexEntry(name_hash, descriptor_index)); + } + + var descriptors = new List(); + for (uint i = 0; i < header.entry_count; i++) + { + uint mapping_index = reader.ReadUInt32(); + uint data_offset = reader.ReadUInt32(); + uint data_size = reader.ReadUInt32(); + uint debug_data_offset = reader.ReadUInt32(); + uint debug_data_size = reader.ReadUInt32(); + uint config_data_offset = reader.ReadUInt32(); + uint config_data_size = reader.ReadUInt32(); + + var desc = new EntryDescriptor + { + mapping_index = mapping_index, + data_offset = data_offset, + data_size = data_size, + debug_data_offset = debug_data_offset, + debug_data_size = debug_data_size, + config_data_offset = config_data_offset, + config_data_size = config_data_size, + }; + descriptors.Add(desc); + } + + var names = new List(); + for (uint i = 0; i < header.entry_count; i++) + { + uint name_length = reader.ReadUInt32(); + byte[] name_bytes = reader.ReadBytes((int)name_length); + names.Add(Encoding.UTF8.GetString(name_bytes)); + } + + var tempItems = new Dictionary(); + foreach (IndexEntry ie in index) + { + if (!tempItems.TryGetValue(ie.descriptor_index, out TemporaryItem? item)) + { + item = new TemporaryItem(names[(int)ie.descriptor_index], descriptors[(int)ie.descriptor_index]); + tempItems.Add(ie.descriptor_index, item); + } + item.IndexEntries.Add(ie); + } + + if (tempItems.Count != descriptors.Count) + { + throw new InvalidOperationException($"Assembly store '{StorePath}' index is corrupted."); + } + + var storeItems = new List(); + foreach (var kvp in tempItems) + { + TemporaryItem ti = kvp.Value; + var item = new StoreItemV2(TargetArch, ti.Name, Is64Bit, ti.IndexEntries, ti.Descriptor); + storeItems.Add(item); + } + Assemblies = storeItems.AsReadOnly(); + } +} diff --git a/src/Sentry.Android.AssemblyReader/V2/Utils.cs b/src/Sentry.Android.AssemblyReader/V2/Utils.cs new file mode 100644 index 0000000000..750f0eea62 --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/V2/Utils.cs @@ -0,0 +1,169 @@ +/* + * Adapted from https://github.com/dotnet/android/blob/5ebcb1dd1503648391e3c0548200495f634d90c6/tools/assembly-store-reader-mk2/AssemblyStore/Utils.cs + * Original code licensed under the MIT License (https://github.com/dotnet/android/blob/5ebcb1dd1503648391e3c0548200495f634d90c6/LICENSE.TXT) + */ +using ELFSharp.ELF; +using ELFSharp.ELF.Sections; +using Machine = ELFSharp.ELF.Machine; + +namespace Sentry.Android.AssemblyReader.V2; + +internal static class Utils +{ + private static readonly string[] AabZipEntries = { + "base/manifest/AndroidManifest.xml", + "BundleConfig.pb", + }; + private static readonly string[] AabBaseZipEntries = { + "manifest/AndroidManifest.xml", + }; + private static readonly string[] ApkZipEntries = { + "AndroidManifest.xml", + }; + + public const uint ZipMagic = 0x4034b50; + public const uint AssemblyStoreMagic = 0x41424158; + public const uint ELFMagic = 0x464c457f; + + public static readonly ArrayPool BytePool = ArrayPool.Shared; + + public static (ulong offset, ulong size, ELFPayloadError error) FindELFPayloadSectionOffsetAndSize(Stream stream) + { + stream.Seek(0, SeekOrigin.Begin); + var elfClass = ELFReader.CheckELFType(stream); + if (elfClass == Class.NotELF) + { + return ReturnError(null, ELFPayloadError.NotELF); + } + + if (!ELFReader.TryLoad(stream, shouldOwnStream: false, out IELF? elf)) + { + return ReturnError(elf, ELFPayloadError.LoadFailed); + } + + if (elf.Type != FileType.SharedObject) + { + return ReturnError(elf, ELFPayloadError.NotSharedLibrary); + } + + if (elf.Endianess != ELFSharp.Endianess.LittleEndian) + { + return ReturnError(elf, ELFPayloadError.NotLittleEndian); + } + + if (!elf.TryGetSection("payload", out ISection? payloadSection)) + { + return ReturnError(elf, ELFPayloadError.NoPayloadSection); + } + + var is64 = elf.Machine switch + { + Machine.ARM => false, + Machine.Intel386 => false, + + Machine.AArch64 => true, + Machine.AMD64 => true, + + _ => throw new NotSupportedException($"Unsupported ELF architecture '{elf.Machine}'") + }; + + ulong offset; + ulong size; + + if (is64) + { + (offset, size) = GetOffsetAndSize64((Section)payloadSection); + } + else + { + (offset, size) = GetOffsetAndSize32((Section)payloadSection); + } + + elf.Dispose(); + return (offset, size, ELFPayloadError.None); + + (ulong offset, ulong size) GetOffsetAndSize64(Section payload) + { + return (payload.Offset, payload.Size); + } + + (ulong offset, ulong size) GetOffsetAndSize32(Section payload) + { + return ((ulong)payload.Offset, (ulong)payload.Size); + } + + (ulong offset, ulong size, ELFPayloadError error) ReturnError(IELF? elf, ELFPayloadError error) + { + elf?.Dispose(); + + return (0, 0, error); + } + } + + public static (FileFormat format, FileInfo? info) DetectFileFormat(string path) + { + if (string.IsNullOrEmpty(path)) + { + return (FileFormat.Unknown, null); + } + + var info = new FileInfo(path); + if (!info.Exists) + { + return (FileFormat.Unknown, null); + } + + using var reader = new BinaryReader(info.OpenRead()); + + // ATM, all formats we recognize have 4-byte magic at the start + var format = reader.ReadUInt32() switch + { + ZipMagic => FileFormat.Zip, + ELFMagic => FileFormat.ELF, + AssemblyStoreMagic => FileFormat.AssemblyStore, + _ => FileFormat.Unknown + }; + + if (format == FileFormat.Unknown || format != FileFormat.Zip) + { + return (format, info); + } + + return (DetectAndroidArchive(info, format), info); + } + + private static FileFormat DetectAndroidArchive(FileInfo info, FileFormat defaultFormat) + { + using var zip = ZipFile.OpenRead(info.FullName); + + if (HasAllEntries(zip, AabZipEntries)) + { + return FileFormat.Aab; + } + + if (HasAllEntries(zip, ApkZipEntries)) + { + return FileFormat.Apk; + } + + if (HasAllEntries(zip, AabBaseZipEntries)) + { + return FileFormat.AabBase; + } + + return defaultFormat; + + static bool HasAllEntries(ZipArchive zip, string[] entries) + { + foreach (var entry in entries) + { + if (zip.GetEntry(entry) is null) + { + return false; + } + } + + return true; + } + } +} diff --git a/test/AndroidTestApp/AndroidTestApp.csproj b/test/AndroidTestApp/AndroidTestApp.csproj index 15dedbe4c9..3d8a1981e1 100644 --- a/test/AndroidTestApp/AndroidTestApp.csproj +++ b/test/AndroidTestApp/AndroidTestApp.csproj @@ -1,6 +1,7 @@ - net8.0-android34.0 + net8.0-android;net9.0-android + false 21 Exe enable diff --git a/test/Directory.Build.props b/test/Directory.Build.props index d6cc562cef..4cc80884d9 100644 --- a/test/Directory.Build.props +++ b/test/Directory.Build.props @@ -46,7 +46,7 @@ - + diff --git a/test/Sentry.Android.AssemblyReader.Tests/AndroidAssemblyReaderTests.cs b/test/Sentry.Android.AssemblyReader.Tests/AndroidAssemblyReaderTests.cs index 4b98a43257..4a9985f827 100644 --- a/test/Sentry.Android.AssemblyReader.Tests/AndroidAssemblyReaderTests.cs +++ b/test/Sentry.Android.AssemblyReader.Tests/AndroidAssemblyReaderTests.cs @@ -1,15 +1,27 @@ +using Sentry.Android.AssemblyReader.V1; +using Sentry.Android.AssemblyReader.V2; + namespace Sentry.Android.AssemblyReader.Tests; public class AndroidAssemblyReaderTests { private readonly ITestOutputHelper _output; +#if NET9_0 + private static string TargetFramework => "net9.0"; +#elif NET8_0 + private static string TargetFramework => "net8.0"; +#else + // Adding a new TFM to the project? Include it above +#error "Target Framework not yet supported for AndroidAssemblyReader" +#endif + public AndroidAssemblyReaderTests(ITestOutputHelper output) { _output = output; } - private IAndroidAssemblyReader GetSut(bool isAssemblyStore, bool isCompressed) + private IAndroidAssemblyReader GetSut(bool isAot, bool isAssemblyStore, bool isCompressed) { #if ANDROID var logger = new TestOutputDiagnosticLogger(_output); @@ -19,33 +31,55 @@ private IAndroidAssemblyReader GetSut(bool isAssemblyStore, bool isCompressed) Path.GetFullPath(Path.Combine( Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!, "..", "..", "..", "TestAPKs", - $"android-Store={isAssemblyStore}-Compressed={isCompressed}.apk")); + $"{TargetFramework}-android-A={isAot}-S={isAssemblyStore}-C={isCompressed}.apk")); _output.WriteLine($"Checking if APK exists: {apkPath}"); File.Exists(apkPath).Should().BeTrue(); + // Note: This needs to match the RID used when publishing the test APK string[] supportedAbis = { "x86_64" }; return AndroidAssemblyReaderFactory.Open(apkPath, supportedAbis, logger: (message, args) => _output.WriteLine(message, args)); #endif } - [SkippableTheory] - [InlineData(false)] - [InlineData(true)] - public void CreatesCorrectReader(bool isAssemblyStore) + [SkippableFact] + public void CreatesCorrectStoreReader() { #if ANDROID Skip.If(true, "It's unknown whether the current Android app APK is an assembly store or not."); #endif - using var sut = GetSut(isAssemblyStore, isCompressed: true); - if (isAssemblyStore) + using var sut = GetSut(isAot: false, isAssemblyStore: true, isCompressed: true); + switch (TargetFramework) { - Assert.IsType(sut); + case "net9.0": + Assert.IsType(sut); + break; + case "net8.0": + Assert.IsType(sut); + break; + default: + throw new NotSupportedException($"Unsupported target framework: {TargetFramework}"); } - else + } + + [SkippableFact] + public void CreatesCorrectArchiveReader() + { +#if ANDROID + Skip.If(true, "It's unknown whether the current Android app APK is an assembly store or not."); +#endif + using var sut = GetSut(isAot: false, isAssemblyStore: false, isCompressed: true); + switch (TargetFramework) { - Assert.IsType(sut); + case "net9.0": + Assert.IsType(sut); + break; + case "net8.0": + Assert.IsType(sut); + break; + default: + throw new NotSupportedException($"Unsupported target framework: {TargetFramework}"); } } @@ -54,27 +88,32 @@ public void CreatesCorrectReader(bool isAssemblyStore) [InlineData(true)] public void ReturnsNullIfAssemblyDoesntExist(bool isAssemblyStore) { - using var sut = GetSut(isAssemblyStore, isCompressed: true); + using var sut = GetSut(isAot: false, isAssemblyStore, isCompressed: true); Assert.Null(sut.TryReadAssembly("NonExistent.dll")); } + public static IEnumerable ReadsAssemblyPermutations => +#if NET8_0 + from isAot in new[] { false } +#else + from isAot in new[] { true, false } +#endif + from isStore in new[] { true, false } + from isCompressed in new[] { true, false } + from assemblyName in new[] { "Mono.Android.dll", "System.Private.CoreLib.dll" } + select new object[] { isAot, isStore, isCompressed, assemblyName }; + [SkippableTheory] - [InlineData(false, true, "Mono.Android.dll")] - [InlineData(false, false, "Mono.Android.dll")] - [InlineData(false, true, "System.Runtime.dll")] - [InlineData(false, false, "System.Runtime.dll")] - [InlineData(true, true, "Mono.Android.dll")] - [InlineData(true, false, "Mono.Android.dll")] - [InlineData(true, true, "System.Runtime.dll")] - [InlineData(true, false, "System.Runtime.dll")] - public void ReadsAssembly(bool isAssemblyStore, bool isCompressed, string assemblyName) + [MemberData(nameof(ReadsAssemblyPermutations))] + public void ReadsAssembly(bool isAot, bool isAssemblyStore, bool isCompressed, string assemblyName) { #if ANDROID - // No need to run all combinations - we only test the current APK which is (likely) compressed assembly store. + // No need to run all combinations - we only test the current APK which is likely JIT compressed assembly store. + Skip.If(isAot); Skip.If(!isAssemblyStore); Skip.If(!isCompressed); #endif - using var sut = GetSut(isAssemblyStore, isCompressed); + using var sut = GetSut(isAot, isAssemblyStore, isCompressed); var peReader = sut.TryReadAssembly(assemblyName); Assert.NotNull(peReader); diff --git a/test/Sentry.Android.AssemblyReader.Tests/Sentry.Android.AssemblyReader.Tests.csproj b/test/Sentry.Android.AssemblyReader.Tests/Sentry.Android.AssemblyReader.Tests.csproj index d6dd9fc202..ee88950c58 100644 --- a/test/Sentry.Android.AssemblyReader.Tests/Sentry.Android.AssemblyReader.Tests.csproj +++ b/test/Sentry.Android.AssemblyReader.Tests/Sentry.Android.AssemblyReader.Tests.csproj @@ -2,7 +2,8 @@ net9.0;net8.0 - $(TargetFrameworks);net8.0-android34.0 + + $(TargetFrameworks);net8.0-android34.0;net9.0-android35.0 enable @@ -11,23 +12,43 @@ + + + + + + - - - - - + + + + + + + + + - <_ConfigString>Store=$(_Store)-Compressed=$(_Compressed) + <_ConfigString>A=$(_Aot)-S=$(_Store)-C=$(_Compressed) + ..\AndroidTestApp\bin\$(TargetFramework)\$(_ConfigString)\com.companyname.AndroidTestApp-Signed.apk + TestAPKs\$(TargetFramework)-$(_ConfigString).apk + + + + + + + + True + False - - + diff --git a/test/Sentry.Extensions.Logging.Tests/Sentry.Extensions.Logging.Tests.csproj b/test/Sentry.Extensions.Logging.Tests/Sentry.Extensions.Logging.Tests.csproj index 947d10b537..7b0d03edad 100644 --- a/test/Sentry.Extensions.Logging.Tests/Sentry.Extensions.Logging.Tests.csproj +++ b/test/Sentry.Extensions.Logging.Tests/Sentry.Extensions.Logging.Tests.csproj @@ -2,9 +2,9 @@ net9.0;net8.0;net48 - $(TargetFrameworks);net8.0-android34.0 - $(TargetFrameworks);net8.0-ios17.0 - $(TargetFrameworks);net8.0-maccatalyst17.0 + $(TargetFrameworks);net8.0-android34.0;net9.0-android35.0 + $(TargetFrameworks);net8.0-ios17.0;net9.0-ios18.0 + $(TargetFrameworks);net8.0-maccatalyst17.0;net9.0-maccatalyst18.0 diff --git a/test/Sentry.Maui.Device.TestApp/Sentry.Maui.Device.TestApp.csproj b/test/Sentry.Maui.Device.TestApp/Sentry.Maui.Device.TestApp.csproj index 5953b65d70..f08b24c8c4 100644 --- a/test/Sentry.Maui.Device.TestApp/Sentry.Maui.Device.TestApp.csproj +++ b/test/Sentry.Maui.Device.TestApp/Sentry.Maui.Device.TestApp.csproj @@ -2,8 +2,8 @@ - $(TargetFrameworks);net8.0-android34.0 - $(TargetFrameworks);net8.0-ios17.0 + $(TargetFrameworks);net8.0-android;net9.0-android + $(TargetFrameworks);net8.0-ios;net9.0-ios true + false + + + + diff --git a/src/Sentry/SentrySdk.cs b/src/Sentry/SentrySdk.cs index 401a0fa6f0..8c5efbd967 100644 --- a/src/Sentry/SentrySdk.cs +++ b/src/Sentry/SentrySdk.cs @@ -209,6 +209,13 @@ internal static IDisposable UseHub(IHub hub) return new DisposeHandle(hub); } + /// + /// Allows to set the trace + /// + internal static void SetTrace(SentryId traceId, SpanId parentSpanId) => + CurrentHub.ConfigureScope(scope => + scope.SetPropagationContext(new SentryPropagationContext(traceId, parentSpanId))); + /// /// Flushes the queue of captured events until the timeout set in /// is reached. diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt index 67795954b7..93d72d716c 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt @@ -228,6 +228,7 @@ namespace Sentry void AddBreadcrumb(Sentry.Breadcrumb breadcrumb); void SetExtra(string key, object? value); void SetTag(string key, string value); + void SetTrace(Sentry.SentryId traceId, Sentry.SpanId parentSpanId); void SetUser(Sentry.SentryUser? user); void UnsetTag(string key); } diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt index 67795954b7..93d72d716c 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt @@ -228,6 +228,7 @@ namespace Sentry void AddBreadcrumb(Sentry.Breadcrumb breadcrumb); void SetExtra(string key, object? value); void SetTag(string key, string value); + void SetTrace(Sentry.SentryId traceId, Sentry.SpanId parentSpanId); void SetUser(Sentry.SentryUser? user); void UnsetTag(string key); } diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt index 0c06282d34..75fa5ad89d 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt @@ -216,6 +216,7 @@ namespace Sentry void AddBreadcrumb(Sentry.Breadcrumb breadcrumb); void SetExtra(string key, object? value); void SetTag(string key, string value); + void SetTrace(Sentry.SentryId traceId, Sentry.SpanId parentSpanId); void SetUser(Sentry.SentryUser? user); void UnsetTag(string key); } diff --git a/test/Sentry.Tests/HubTests.cs b/test/Sentry.Tests/HubTests.cs index d22326b119..f0c141617e 100644 --- a/test/Sentry.Tests/HubTests.cs +++ b/test/Sentry.Tests/HubTests.cs @@ -1013,7 +1013,7 @@ public void GetTraceHeader_NoSpanActive_ReturnsHeaderFromPropagationContext() var propagationContext = new SentryPropagationContext( SentryId.Parse("75302ac48a024bde9a3b3734a82e36c8"), SpanId.Parse("2000000000000000")); - hub.ConfigureScope(scope => scope.PropagationContext = propagationContext); + hub.ConfigureScope(scope => scope.SetPropagationContext(propagationContext)); // Act var header = hub.GetTraceHeader(); @@ -1052,7 +1052,7 @@ public void GetBaggage_NoSpanActive_ReturnsBaggageFromPropagationContext() var hub = _fixture.GetSut(); var propagationContext = new SentryPropagationContext( SentryId.Parse("43365712692146d08ee11a729dfbcaca"), SpanId.Parse("1000000000000000")); - hub.ConfigureScope(scope => scope.PropagationContext = propagationContext); + hub.ConfigureScope(scope => scope.SetPropagationContext(propagationContext)); // Act var baggage = hub.GetBaggage(); @@ -1069,7 +1069,7 @@ public void ContinueTrace_SetsPropagationContextAndReturnsTransactionContext() var hub = _fixture.GetSut(); var propagationContext = new SentryPropagationContext( SentryId.Parse("43365712692146d08ee11a729dfbcaca"), SpanId.Parse("1000000000000000")); - hub.ConfigureScope(scope => scope.PropagationContext = propagationContext); + hub.ConfigureScope(scope => scope.SetPropagationContext(propagationContext)); var traceHeader = new SentryTraceHeader(SentryId.Parse("5bd5f6d346b442dd9177dce9302fd737"), SpanId.Parse("2000000000000000"), null); @@ -1104,7 +1104,7 @@ public void ContinueTrace_ReceivesHeadersAsStrings_SetsPropagationContextAndRetu var hub = _fixture.GetSut(); var propagationContext = new SentryPropagationContext( SentryId.Parse("43365712692146d08ee11a729dfbcaca"), SpanId.Parse("1000000000000000")); - hub.ConfigureScope(scope => scope.PropagationContext = propagationContext); + hub.ConfigureScope(scope => scope.SetPropagationContext(propagationContext)); var traceHeader = "5bd5f6d346b442dd9177dce9302fd737-2000000000000000"; var baggageHeader = "sentry-trace_id=5bd5f6d346b442dd9177dce9302fd737, sentry-public_key=49d0f7386ad645858ae85020e393bef3, sentry-sample_rate=1.0"; diff --git a/test/Sentry.Tests/ScopeTests.cs b/test/Sentry.Tests/ScopeTests.cs index ece48d5170..8040666b10 100644 --- a/test/Sentry.Tests/ScopeTests.cs +++ b/test/Sentry.Tests/ScopeTests.cs @@ -633,6 +633,31 @@ public void SetTag_NullValue_DoesNotThrowArgumentNullException() Assert.Null(exception); } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void SetPropagationContext_ObserverExist_ObserverSetsTraceIfEnabled(bool enableScopeSync) + { + // Arrange + var observer = Substitute.For(); + var scope = new Scope(new SentryOptions + { + ScopeObserver = observer, + EnableScopeSync = enableScopeSync + }); + var propagationContext = new SentryPropagationContext(); + var expectedTraceId = propagationContext.TraceId; + var expectedSpanId = propagationContext.SpanId; + var expectedCount = enableScopeSync ? 1 : 0; + + // Act + scope.SetPropagationContext(propagationContext); + + // Assert + scope.PropagationContext.Should().Be(propagationContext); + observer.Received(expectedCount).SetTrace(Arg.Is(expectedTraceId), Arg.Is(expectedSpanId)); + } } public static class ScopeTestExtensions From 92262a16455992af112cc03a4fc3c79462acaef5 Mon Sep 17 00:00:00 2001 From: getsentry-bot Date: Wed, 2 Apr 2025 06:43:25 +0000 Subject: [PATCH 180/363] release: 5.5.0 --- CHANGELOG.md | 2 +- Directory.Build.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 341932d861..e3dd61e5aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## Unreleased +## 5.5.0 ### Features diff --git a/Directory.Build.props b/Directory.Build.props index ece069c283..94c7e3dc2d 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,7 +1,7 @@ - 5.4.0 + 5.5.0 13 true true From 16716c0cf8309b04867189097036e35bf727f2c6 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Fri, 4 Apr 2025 07:18:42 +1300 Subject: [PATCH 181/363] Chore: add Stefan Poelz to CODEOWNERS (#4078) --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 392cfefbbb..799fa81286 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1 @@ -* @aritchie @jamescrosswell +* @aritchie @jamescrosswell @Flash0ver From 101a40bb2d93b6b3f4f134514bc23f95fb484d7d Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Fri, 4 Apr 2025 09:10:05 +1300 Subject: [PATCH 182/363] Fix Android readme (#4081) --- src/Sentry.Bindings.Android/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Sentry.Bindings.Android/README.md b/src/Sentry.Bindings.Android/README.md index 96d69e6173..3981ca163f 100644 --- a/src/Sentry.Bindings.Android/README.md +++ b/src/Sentry.Bindings.Android/README.md @@ -6,8 +6,8 @@ Instead, reference one of the following: ## SDK Developers and Contributors -For .NET SDK contributors, most of the classes in this package are generated automatically by proguard. - -- Proguard configuration is defined in `sentry-proguard.cfg` (see [Proguard usage](https://www.guardsquare.com/manual/configuration/usage)) +For .NET SDK contributors, most of the classes in this package are generated automatically. - Post generation transformations are controlled via various XML files stored in the `/Transforms` directory (see [Java Bindings Metadata documentation](https://learn.microsoft.com/en-gb/previous-versions/xamarin/android/platform/binding-java-library/customizing-bindings/java-bindings-metadata) for details). + +- Proguard configuration is defined in `sentry-proguard.cfg` (see [Proguard usage](https://www.guardsquare.com/manual/configuration/usage)) From b3b705cf6d55618a566b557f9c1bc4beb28380ce Mon Sep 17 00:00:00 2001 From: Stefan Jandl Date: Mon, 7 Apr 2025 01:14:06 +0200 Subject: [PATCH 183/363] chore(deps): update Java SDK to v8.6.0 (#4075) --- CHANGELOG.md | 3 ++ integration-test/cli.Tests.ps1 | 2 +- .../Sentry.Bindings.Android.csproj | 31 +++++++++++++++- .../Transforms/Metadata.xml | 35 ++++++++++++++++--- 4 files changed, 65 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e3dd61e5aa..70dc0214c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,9 @@ - Bump CLI from v2.42.2 to v2.43.0 ([#4036](https://github.com/getsentry/sentry-dotnet/pull/4036), [#4049](https://github.com/getsentry/sentry-dotnet/pull/4049), [#4060](https://github.com/getsentry/sentry-dotnet/pull/4060), [#4062](https://github.com/getsentry/sentry-dotnet/pull/4062)) - [changelog](https://github.com/getsentry/sentry-cli/blob/master/CHANGELOG.md#2430) - [diff](https://github.com/getsentry/sentry-cli/compare/2.42.2...2.43.0) +- Bump Java SDK from v7.20.1 to v8.6.0 ([#4075](https://github.com/getsentry/sentry-dotnet/pull/4075)) + - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#860) + - [diff](https://github.com/getsentry/sentry-java/compare/7.20.1...8.6.0) ## 5.4.0 diff --git a/integration-test/cli.Tests.ps1 b/integration-test/cli.Tests.ps1 index 3910024cee..eeffa7a031 100644 --- a/integration-test/cli.Tests.ps1 +++ b/integration-test/cli.Tests.ps1 @@ -165,7 +165,7 @@ Describe 'MAUI' -ForEach @( 'maui-app.pdb' ) $result.ScriptOutput | Should -AnyElementMatch 'Uploaded a total of 1 new mapping files' - $result.ScriptOutput | Should -AnyElementMatch 'Found 17 debug information files \(1 with embedded sources\)' + $result.ScriptOutput | Should -AnyElementMatch 'Found 25 debug information files \(1 with embedded sources\)' } It "uploads symbols and sources for an iOS build" -Skip:(!$IsMacOS) { diff --git a/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj b/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj index b9aec99266..b438dfde48 100644 --- a/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj +++ b/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj @@ -3,8 +3,10 @@ net8.0-android34.0 $(NoWarn);BG8605;BG8606 - 7.20.1 + 8.6.0 $(BaseIntermediateOutputPath)sdks\Sentry\Android\$(SentryAndroidSdkVersion)\ + + .NET Bindings for the Sentry Android SDK @@ -59,6 +61,33 @@ Condition="!Exists('$(SentryAndroidSdkDirectory)sentry-$(SentryAndroidSdkVersion).jar')" Retries="3" /> + + + + + + + + + + + + + + diff --git a/src/Sentry.Bindings.Android/Transforms/Metadata.xml b/src/Sentry.Bindings.Android/Transforms/Metadata.xml index 916cfdf2b9..9dd30189d6 100644 --- a/src/Sentry.Bindings.Android/Transforms/Metadata.xml +++ b/src/Sentry.Bindings.Android/Transforms/Metadata.xml @@ -64,13 +64,27 @@ internal - + + + + + + + + + + + + + + + + + + + - + + + + + + From 35254c0be34911c33cd9ca4e32f009563ea1d75b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Apr 2025 23:19:30 +1200 Subject: [PATCH 184/363] build(deps): bump actions/create-github-app-token from 1.12.0 to 2.0.2 (#4086) Bumps [actions/create-github-app-token](https://github.com/actions/create-github-app-token) from 1.12.0 to 2.0.2. - [Release notes](https://github.com/actions/create-github-app-token/releases) - [Commits](https://github.com/actions/create-github-app-token/compare/d72941d797fd3113feb6b93fd0dec494b13a2547...3ff1caaa28b64c9cc276ce0a02e2ff584f3900c5) --- updated-dependencies: - dependency-name: actions/create-github-app-token dependency-version: 2.0.2 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d479620bf1..8b90138ffb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -20,7 +20,7 @@ jobs: steps: - name: Get auth token id: token - uses: actions/create-github-app-token@d72941d797fd3113feb6b93fd0dec494b13a2547 # v1.12.0 + uses: actions/create-github-app-token@3ff1caaa28b64c9cc276ce0a02e2ff584f3900c5 # v2.0.2 with: app-id: ${{ vars.SENTRY_RELEASE_BOT_CLIENT_ID }} private-key: ${{ secrets.SENTRY_RELEASE_BOT_PRIVATE_KEY }} From a888ddef6620a95baa93d16e9ce79d35ffc5f712 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Tue, 8 Apr 2025 10:20:53 +1200 Subject: [PATCH 185/363] Detect Sentry Java SDK dependencies during build (#4079) --- CHANGELOG.md | 6 ++ .../Sentry.Bindings.Android.csproj | 69 +++++++++++++++---- 2 files changed, 62 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 70dc0214c6..db48381f21 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +### Fixes + +- Sentry Java SDK dependencies are now detected and included in the Android bindings ([#4079](https://github.com/getsentry/sentry-dotnet/pull/4079)) + ## 5.5.0 ### Features diff --git a/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj b/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj index b438dfde48..5893bf65c8 100644 --- a/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj +++ b/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj @@ -1,10 +1,8 @@ - net8.0-android34.0 - - $(NoWarn);BG8605;BG8606 + net8.0-android34.0;net9.0-android35.0 8.6.0 - $(BaseIntermediateOutputPath)sdks\Sentry\Android\$(SentryAndroidSdkVersion)\ + $(BaseIntermediateOutputPath)sdks\$(TargetFramework)\Sentry\Android\$(SentryAndroidSdkVersion)\ .NET Bindings for the Sentry Android SDK @@ -31,10 +29,46 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -46,13 +80,13 @@ + + - + - + - - - + + + + + From ad6ce30ed70ca1e7d026ab535463db6030c3a7a6 Mon Sep 17 00:00:00 2001 From: Jan Drozd <125502420+jdswcz@users.noreply.github.com> Date: Tue, 8 Apr 2025 02:37:00 +0200 Subject: [PATCH 186/363] Fix UWP Net Native compilation (#4085) --------- Co-authored-by: Jan Drozd Co-authored-by: James Crosswell --- CHANGELOG.md | 1 + src/Sentry/Internal/FnvHash.cs | 36 ++++++++++------------------------ 2 files changed, 11 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index db48381f21..3f6c54f42e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Fixes +- Fix UWP Net Native compilation ([#4085](https://github.com/getsentry/sentry-dotnet/pull/4085)) - Sentry Java SDK dependencies are now detected and included in the Android bindings ([#4079](https://github.com/getsentry/sentry-dotnet/pull/4079)) ## 5.5.0 diff --git a/src/Sentry/Internal/FnvHash.cs b/src/Sentry/Internal/FnvHash.cs index 10281e90bd..dd40e00380 100644 --- a/src/Sentry/Internal/FnvHash.cs +++ b/src/Sentry/Internal/FnvHash.cs @@ -5,39 +5,23 @@ namespace Sentry.Internal; /// /// See https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV_hash_parameters /// -/// -/// We use a struct to avoid heap allocations. -/// -internal struct FnvHash +internal static class FnvHash { - public FnvHash() - { - } - private const int Offset = unchecked((int)2166136261); private const int Prime = 16777619; - private int HashCode { get; set; } = Offset; - - private void Combine(byte data) - { - unchecked - { - HashCode ^= data; - HashCode *= Prime; - } - } - - private static int ComputeHash(byte[] data) + internal static int ComputeHash(string input) { - var result = new FnvHash(); - foreach (var b in data) + var hashCode = Offset; + foreach (var b in Encoding.UTF8.GetBytes(input)) { - result.Combine(b); + unchecked + { + hashCode ^= b; + hashCode *= Prime; + } } - return result.HashCode; + return hashCode; } - - public static int ComputeHash(string data) => ComputeHash(Encoding.UTF8.GetBytes(data)); } From 3ea2966eb7cf403c715b3ba28b41a8eff5899ee3 Mon Sep 17 00:00:00 2001 From: hangy Date: Tue, 8 Apr 2025 02:39:37 +0200 Subject: [PATCH 187/363] Add Linux dependencies for building sentry-native to CONTRIBUTING.md (#4090) --- CONTRIBUTING.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e08736a579..03e4d241a2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -39,6 +39,8 @@ For big feature it's advised to raise an issue to discuss it first. - `Sentry.DiagnosticSource.IntegrationTests.csproj` uses [SQL LocalDb](https://docs.microsoft.com/sql/database-engine/configure-windows/sql-server-express-localdb) - [download SQL LocalDB 2019](https://download.microsoft.com/download/7/c/1/7c14e92e-bdcb-4f89-b7cf-93543e7112d1/SqlLocalDB.msi). To avoid running these tests, unload `Sentry.DiagnosticSource.IntegrationTests.csproj` from the solution. * On macOS/Linux - [Mono 6 or higher](https://www.mono-project.com/download/stable) to run the unit tests on the `net4x` targets. +* On Linux + - **curl** and **zlib** libraries (e.g. on Ubuntu: libcurl4-openssl-dev, libz-dev) to build the [sentry-native](https://github.com/getsentry/sentry-native/) module. ## .NET MAUI Requirements From 47719fbe4fce9f86725c1e27c4644a59b9594446 Mon Sep 17 00:00:00 2001 From: getsentry-bot Date: Tue, 8 Apr 2025 02:23:04 +0000 Subject: [PATCH 188/363] release: 5.5.1 --- CHANGELOG.md | 2 +- Directory.Build.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f6c54f42e..dc9459e6cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## Unreleased +## 5.5.1 ### Fixes diff --git a/Directory.Build.props b/Directory.Build.props index 94c7e3dc2d..6ca7917196 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,7 +1,7 @@ - 5.5.0 + 5.5.1 13 true true From 5b0b14452b4b44e0f62ef744b3bad0a2f9a2966b Mon Sep 17 00:00:00 2001 From: Allan Ritchie Date: Thu, 10 Apr 2025 10:59:58 -0400 Subject: [PATCH 189/363] Prevent users building with AndroidEnableAssemblyCompression set to false (#4089) * Prevent users building with AndroidEnableAssemblyCompression set to false * Update CHANGELOG.md * Update src/Sentry/buildTransitive/Sentry.targets Co-authored-by: James Crosswell * Update Sentry.Samples.Android.csproj --------- Co-authored-by: James Crosswell --- CHANGELOG.md | 1 + src/Sentry/buildTransitive/Sentry.targets | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc9459e6cd..6986a39fa7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ - Changed default value for `SentryOptions.EnableAppHangTrackingV2` to `false` ([#4042](https://github.com/getsentry/sentry-dotnet/pull/4042)) - Missing MAUI `Shell` navigation breadcrumbs on iOS ([#4006](https://github.com/getsentry/sentry-dotnet/pull/4006)) - Prevent application crashes when capturing screenshots on iOS ([#4069](https://github.com/getsentry/sentry-dotnet/pull/4069)) +- Prevent users from disabling AndroidEnableAssemblyCompression which leads to untrappable crash ([#4089](https://github.com/getsentry/sentry-dotnet/pull/4089)) ### Dependencies diff --git a/src/Sentry/buildTransitive/Sentry.targets b/src/Sentry/buildTransitive/Sentry.targets index 57ab5a9285..790ed2aa1e 100644 --- a/src/Sentry/buildTransitive/Sentry.targets +++ b/src/Sentry/buildTransitive/Sentry.targets @@ -9,6 +9,12 @@ Sentry.Attributes$(MSBuildProjectExtension.Replace('proj', '')) + + + + Date: Fri, 11 Apr 2025 09:06:50 +1200 Subject: [PATCH 190/363] Pin iOS 18.0 in Samples and Test apps (#4096) --- samples/Sentry.Samples.Ios/Sentry.Samples.Ios.csproj | 1 + .../Sentry.Maui.Device.TestApp.csproj | 3 +++ 2 files changed, 4 insertions(+) diff --git a/samples/Sentry.Samples.Ios/Sentry.Samples.Ios.csproj b/samples/Sentry.Samples.Ios/Sentry.Samples.Ios.csproj index b1820343e6..0b0adbbe72 100644 --- a/samples/Sentry.Samples.Ios/Sentry.Samples.Ios.csproj +++ b/samples/Sentry.Samples.Ios/Sentry.Samples.Ios.csproj @@ -2,6 +2,7 @@ net9.0-ios + 18 Exe enable true diff --git a/test/Sentry.Maui.Device.TestApp/Sentry.Maui.Device.TestApp.csproj b/test/Sentry.Maui.Device.TestApp/Sentry.Maui.Device.TestApp.csproj index f08b24c8c4..7d49fa1699 100644 --- a/test/Sentry.Maui.Device.TestApp/Sentry.Maui.Device.TestApp.csproj +++ b/test/Sentry.Maui.Device.TestApp/Sentry.Maui.Device.TestApp.csproj @@ -4,6 +4,9 @@ $(TargetFrameworks);net8.0-android;net9.0-android $(TargetFrameworks);net8.0-ios;net9.0-ios + + 18 true + + + + + @@ -66,6 +71,7 @@ /> + @@ -89,6 +95,12 @@ Condition="!Exists('$(SentryAndroidSdkDirectory)sentry-android-ndk-$(SentryAndroidSdkVersion).aar') And $(TargetFramework.StartsWith('net8'))" Retries="3" /> + public bool EnableBeforeSend { get; set; } = false; + + public class NativeExperimentalOptions + { + public NativeSentryReplayOptions SessionReplay { get; set; } = new(); + } + + public class NativeSentryReplayOptions + { + public double? OnErrorSampleRate { get; set; } + public double? SessionSampleRate { get; set; } + public bool MaskAllImages { get; set; } = true; + public bool MaskAllText { get; set; } = true; + } + + /// + /// ExperimentalOptions + /// + public NativeExperimentalOptions ExperimentalOptions { get; set; } = new(); } } diff --git a/src/Sentry/Platforms/Android/SentrySdk.cs b/src/Sentry/Platforms/Android/SentrySdk.cs index f0db0a1fef..ed54760d68 100644 --- a/src/Sentry/Platforms/Android/SentrySdk.cs +++ b/src/Sentry/Platforms/Android/SentrySdk.cs @@ -135,6 +135,13 @@ private static void InitSentryAndroidSdk(SentryOptions options) options.Native.InAppExcludes?.ForEach(o.AddInAppExclude); options.Native.InAppIncludes?.ForEach(o.AddInAppInclude); + o.SessionReplay.OnErrorSampleRate = + (JavaDouble?)options.Native.ExperimentalOptions.SessionReplay.OnErrorSampleRate; + o.SessionReplay.SessionSampleRate = + (JavaDouble?)options.Native.ExperimentalOptions.SessionReplay.SessionSampleRate; + o.SessionReplay.SetMaskAllImages(options.Native.ExperimentalOptions.SessionReplay.MaskAllImages); + o.SessionReplay.SetMaskAllText(options.Native.ExperimentalOptions.SessionReplay.MaskAllText); + // These options are intentionally set and not exposed for modification o.EnableExternalConfiguration = false; o.EnableDeduplication = false; diff --git a/test/Sentry.Tests/Platforms/Android/BindableNativeOptionsTests.cs b/test/Sentry.Tests/Platforms/Android/BindableNativeOptionsTests.cs index c70a9cb565..136ac43629 100644 --- a/test/Sentry.Tests/Platforms/Android/BindableNativeOptionsTests.cs +++ b/test/Sentry.Tests/Platforms/Android/BindableNativeOptionsTests.cs @@ -5,6 +5,11 @@ namespace Sentry.Tests.Platforms.Android; public class BindableNativeOptionsTests : BindableTests { + public BindableNativeOptionsTests() + : base(nameof(BindableSentryOptions.NativeOptions.ExperimentalOptions)) + { + } + [Fact] public void BindableProperties_MatchOptionsProperties() { From 1ebdb8f3e8698991416f4179e0f9d4582652b3da Mon Sep 17 00:00:00 2001 From: hangy Date: Mon, 14 Apr 2025 08:01:00 +0200 Subject: [PATCH 193/363] feat: Add W3C traceparent support to tracing client and utils (#4084) --- CHANGELOG.md | 4 + src/Sentry.AspNet/HttpContextExtensions.cs | 24 ++++ .../Extensions/HttpContextExtensions.cs | 21 +++ src/Sentry.AspNetCore/SentryMiddleware.cs | 3 + .../HttpRequestDataExtensions.cs | 25 ++++ .../SentryFunctionsWorkerMiddleware.cs | 3 + src/Sentry/SentryMessageHandler.cs | 10 ++ src/Sentry/SentryOptions.cs | 3 +- src/Sentry/W3CTraceHeader.cs | 131 ++++++++++++++++++ .../SentryMiddlewareTests.cs | 28 ++-- .../SentryHttpMessageHandlerTests.cs | 107 +++++++++----- test/Sentry.Tests/W3CTraceHeaderTests.cs | 76 ++++++++++ 12 files changed, 385 insertions(+), 50 deletions(-) create mode 100644 src/Sentry/W3CTraceHeader.cs create mode 100644 test/Sentry.Tests/W3CTraceHeaderTests.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index cc49ac0676..12aed32927 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,10 @@ - Prevent users from disabling AndroidEnableAssemblyCompression which leads to untrappable crash ([#4089](https://github.com/getsentry/sentry-dotnet/pull/4089)) +### Features + +- If an incoming HTTP request has the `traceparent` header, it is now parsed and interpreted like the `sentry-trace` header. Outgoing requests now contain the `traceparent` header to facilitate integration with servesr that only support the [W3C Trace Context](https://www.w3.org/TR/trace-context/). ([#4084](https://github.com/getsentry/sentry-dotnet/pull/4084)) + ## 5.5.1 ### Fixes diff --git a/src/Sentry.AspNet/HttpContextExtensions.cs b/src/Sentry.AspNet/HttpContextExtensions.cs index e567433cdb..220413140f 100644 --- a/src/Sentry.AspNet/HttpContextExtensions.cs +++ b/src/Sentry.AspNet/HttpContextExtensions.cs @@ -33,6 +33,27 @@ public static class HttpContextExtensions } } + private static W3CTraceHeader? TryGetW3CTraceHeader(HttpContext context, SentryOptions? options) + { + var value = context.Request.Headers.Get(W3CTraceHeader.HttpHeaderName); + if (string.IsNullOrWhiteSpace(value)) + { + return null; + } + + options?.LogDebug("Received Sentry trace header '{0}'.", value); + + try + { + return W3CTraceHeader.Parse(value); + } + catch (Exception ex) + { + options?.LogError(ex, "Invalid Sentry trace header '{0}'.", value); + return null; + } + } + private static BaggageHeader? TryGetBaggageHeader(HttpContext context, SentryOptions? options) { var value = context.Request.Headers.Get(BaggageHeader.HttpHeaderName); @@ -64,7 +85,10 @@ public static void StartOrContinueTrace(this HttpContext httpContext) { var options = SentrySdk.CurrentOptions; + // If both sentry-trace and traceparent headers are present, sentry-trace takes precedence. + // See: https://github.com/getsentry/team-sdks/issues/41 var traceHeader = TryGetSentryTraceHeader(httpContext, options); + traceHeader ??= TryGetW3CTraceHeader(httpContext, options)?.SentryTraceHeader; var baggageHeader = TryGetBaggageHeader(httpContext, options); var method = httpContext.Request.HttpMethod; diff --git a/src/Sentry.AspNetCore/Extensions/HttpContextExtensions.cs b/src/Sentry.AspNetCore/Extensions/HttpContextExtensions.cs index 19e4dde3c4..1ba81d1444 100644 --- a/src/Sentry.AspNetCore/Extensions/HttpContextExtensions.cs +++ b/src/Sentry.AspNetCore/Extensions/HttpContextExtensions.cs @@ -65,6 +65,27 @@ internal static class HttpContextExtensions } } + public static W3CTraceHeader? TryGetW3CTraceHeader(this HttpContext context, SentryOptions? options) + { + var value = context.Request.Headers.GetValueOrDefault(W3CTraceHeader.HttpHeaderName); + if (string.IsNullOrWhiteSpace(value)) + { + return null; + } + + options?.LogDebug("Received Sentry trace header '{0}'.", value); + + try + { + return W3CTraceHeader.Parse(value!); + } + catch (Exception ex) + { + options?.LogError(ex, "Invalid Sentry trace header '{0}'.", value); + return null; + } + } + public static BaggageHeader? TryGetBaggageHeader(this HttpContext context, SentryOptions? options) { var value = context.Request.Headers.GetValueOrDefault(BaggageHeader.HttpHeaderName); diff --git a/src/Sentry.AspNetCore/SentryMiddleware.cs b/src/Sentry.AspNetCore/SentryMiddleware.cs index 33fca3e622..77bd2fc8bf 100644 --- a/src/Sentry.AspNetCore/SentryMiddleware.cs +++ b/src/Sentry.AspNetCore/SentryMiddleware.cs @@ -105,7 +105,10 @@ public async Task InvokeAsync(HttpContext context, RequestDelegate next) context.Response.OnCompleted(() => hub.FlushAsync(_options.FlushTimeout)); } + // If both sentry-trace and traceparent headers are present, sentry-trace takes precedence. + // See: https://github.com/getsentry/team-sdks/issues/41 var traceHeader = context.TryGetSentryTraceHeader(_options); + traceHeader ??= context.TryGetW3CTraceHeader(_options)?.SentryTraceHeader; var baggageHeader = context.TryGetBaggageHeader(_options); var transactionContext = hub.ContinueTrace(traceHeader, baggageHeader); diff --git a/src/Sentry.Azure.Functions.Worker/HttpRequestDataExtensions.cs b/src/Sentry.Azure.Functions.Worker/HttpRequestDataExtensions.cs index 194e8dcd6f..b4a311812b 100644 --- a/src/Sentry.Azure.Functions.Worker/HttpRequestDataExtensions.cs +++ b/src/Sentry.Azure.Functions.Worker/HttpRequestDataExtensions.cs @@ -30,6 +30,31 @@ internal static class HttpRequestDataExtensions } } + public static W3CTraceHeader? TryGetW3CTraceHeader(this HttpRequestData context, IDiagnosticLogger? logger) + { + var traceHeaderValue = context.Headers.TryGetValues(W3CTraceHeader.HttpHeaderName, out var values) + ? values.FirstOrDefault() + : null; + + if (traceHeaderValue is null) + { + logger?.LogDebug("Did not receive a Sentry trace header."); + return null; + } + + logger?.LogDebug("Received Sentry trace header '{0}'.", traceHeaderValue); + + try + { + return W3CTraceHeader.Parse(traceHeaderValue); + } + catch (Exception ex) + { + logger?.LogError(ex, "Invalid Sentry trace header '{0}'.", traceHeaderValue); + return null; + } + } + public static BaggageHeader? TryGetBaggageHeader(this HttpRequestData context, IDiagnosticLogger? logger) { var baggageValue = context.Headers.TryGetValues(BaggageHeader.HttpHeaderName, out var value) diff --git a/src/Sentry.Azure.Functions.Worker/SentryFunctionsWorkerMiddleware.cs b/src/Sentry.Azure.Functions.Worker/SentryFunctionsWorkerMiddleware.cs index c3c57fe71e..2ae17d004a 100644 --- a/src/Sentry.Azure.Functions.Worker/SentryFunctionsWorkerMiddleware.cs +++ b/src/Sentry.Azure.Functions.Worker/SentryFunctionsWorkerMiddleware.cs @@ -123,7 +123,10 @@ private async Task StartOrContinueTraceAsync(FunctionContext TransactionNameCache.TryAdd(transactionNameKey, transactionName); } + // If both sentry-trace and traceparent headers are present, sentry-trace takes precedence. + // See: https://github.com/getsentry/team-sdks/issues/41 var traceHeader = requestData.TryGetSentryTraceHeader(_logger); + traceHeader ??= requestData.TryGetW3CTraceHeader(_logger)?.SentryTraceHeader; var baggageHeader = requestData.TryGetBaggageHeader(_logger); return SentrySdk.ContinueTrace(traceHeader, baggageHeader, transactionName, Operation); diff --git a/src/Sentry/SentryMessageHandler.cs b/src/Sentry/SentryMessageHandler.cs index 8784ef1822..69b1f6560e 100644 --- a/src/Sentry/SentryMessageHandler.cs +++ b/src/Sentry/SentryMessageHandler.cs @@ -136,6 +136,7 @@ private void PropagateTraceHeaders(HttpRequestMessage request, string url) if (_options?.TracePropagationTargets.MatchesSubstringOrRegex(url) is true or null) { AddSentryTraceHeader(request); + AddW3CTraceHeader(request); AddBaggageHeader(request); } } @@ -149,6 +150,15 @@ private void AddSentryTraceHeader(HttpRequestMessage request) } } + private void AddW3CTraceHeader(HttpRequestMessage request) + { + // Set trace header if it hasn't already been set + if (!request.Headers.Contains(W3CTraceHeader.HttpHeaderName) && _hub.GetTraceHeader() is { } traceHeader) + { + request.Headers.Add(W3CTraceHeader.HttpHeaderName, new W3CTraceHeader(traceHeader).ToString()); + } + } + private void AddBaggageHeader(HttpRequestMessage request) { var baggage = _hub.GetBaggage(); diff --git a/src/Sentry/SentryOptions.cs b/src/Sentry/SentryOptions.cs index 69a87685a7..e70f16e70b 100644 --- a/src/Sentry/SentryOptions.cs +++ b/src/Sentry/SentryOptions.cs @@ -952,7 +952,8 @@ public double? ProfilesSampleRate /// /// A customizable list of objects, each containing either a /// substring or regular expression pattern that can be used to control which outgoing HTTP requests - /// will have the sentry-trace and baggage headers propagated, for purposes of distributed tracing. + /// will have the sentry-trace, traceparent, and baggage headers propagated, + /// for purposes of distributed tracing. /// The default value contains a single value of .*, which matches everything. /// To disable propagation completely, clear this collection or set it to an empty collection. /// diff --git a/src/Sentry/W3CTraceHeader.cs b/src/Sentry/W3CTraceHeader.cs new file mode 100644 index 0000000000..65043594a6 --- /dev/null +++ b/src/Sentry/W3CTraceHeader.cs @@ -0,0 +1,131 @@ +namespace Sentry; + +/// +/// Extension methods for working with Sentry trace headers. +/// +internal class W3CTraceHeader +{ + private const string SupportedVersion = "00"; + + /// + /// The name of the W3C trace context header used for distributed tracing. + /// This field contains the value "traceparent" which is part of the W3C Trace Context specification. + /// + public const string HttpHeaderName = "traceparent"; + + /// + /// Represents the sampled trace flags value ("01") in W3C Trace Context specification. + /// This flag indicates that the trace is part of the sampling set and should be recorded. + /// + public const string TraceFlagsSampled = "01"; + + /// + /// Represents the unsampled trace flags value ("00") in W3C Trace Context specification. + /// This flag indicates that the trace is not part of the sampling set and should not be recorded. + /// + public const string TraceFlagsNotSampled = "00"; + + /// + /// Initializes a new instance of the class from a Sentry trace header. + /// + /// The source Sentry trace header to create the W3C trace header from. + /// Thrown when is null. + public W3CTraceHeader(SentryTraceHeader source) + { + if (source is null) + { + throw new ArgumentNullException(nameof(source), "Source Sentry trace header cannot be null."); + } + + SentryTraceHeader = source; + } + + /// + /// Gets the Sentry trace header containing trace identification and sampling information. + /// + /// + /// The Sentry trace header that contains the trace ID, span ID, and sampling decision. + /// + public SentryTraceHeader SentryTraceHeader { get; } + + /// + /// Parses a from a string representation of the Sentry trace header. + /// + /// + /// A string containing the Sentry trace header, expected to follow the format "traceId-spanId-sampled", + /// where "sampled" is optional. + /// + /// + /// A object if parsing succeeds, or null if the input string is null, empty, or whitespace. + /// + /// + /// Thrown if the input string does not contain a valid trace header format, specifically if it lacks required trace ID and span ID components. + /// + public static W3CTraceHeader? Parse(string value) + { + if (string.IsNullOrWhiteSpace(value)) + { + return null; + } + + var components = value.Split('-', StringSplitOptions.RemoveEmptyEntries); + if (components.Length < 4) + { + throw new FormatException($"Invalid W3C trace header: {value}."); + } + + var version = components[0]; + if (version != SupportedVersion) + { + throw new FormatException($"Invalid W3C trace header version: {version}."); + } + + var traceId = SentryId.Parse(components[1]); + var spanId = SpanId.Parse(components[2]); + var isSampled = ConvertTraceFlagsToSampled(components[3]); + + return new W3CTraceHeader(new SentryTraceHeader(traceId, spanId, isSampled)); + } + + /// + public override string ToString() + { + var traceFlags = ConvertSampledToTraceFlags(SentryTraceHeader.IsSampled); + return $"{SupportedVersion}-{SentryTraceHeader.TraceId}-{SentryTraceHeader.SpanId}-{traceFlags}"; + } + + private static string? ConvertSampledToTraceFlags(bool? isSampled) => (isSampled ?? false) ? TraceFlagsSampled : TraceFlagsNotSampled; + + private static bool? ConvertTraceFlagsToSampled(string? traceFlags) + { + if (string.IsNullOrWhiteSpace(traceFlags) || traceFlags.Length != 2) + { + return null; + } + + // In version 00 of the W3C Trace Context specification, the trace flags field is 2 hex digits. + // Only the first bit is used. We use string comparison first to avoid parsing the hex value in + // the bulk of all cases. + // See https://github.com/getsentry/sentry-dotnet/pull/4084#discussion_r2035771628 + if (string.Equals(traceFlags, TraceFlagsSampled, StringComparison.Ordinal)) + { + return true; + } + else if (string.Equals(traceFlags, TraceFlagsNotSampled, StringComparison.Ordinal)) + { + return false; + } + + // If the trace flags field is not "01" or "00", we try to parse it as a hex number. + // This is a fallback for cases where the trace flags field is not in the expected format. + if (!byte.TryParse(traceFlags, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out byte traceFlagsBytes)) + { + // If it's not a valid hex number, we can't parse it. + return null; + } + + // The first bit of the trace flags field indicates whether the trace is sampled. + // We use bitwise AND to check if the first bit is set. + return (traceFlagsBytes & 0x01) == 1; + } +} diff --git a/test/Sentry.AspNetCore.Tests/SentryMiddlewareTests.cs b/test/Sentry.AspNetCore.Tests/SentryMiddlewareTests.cs index d6cedd9b73..0ad0628f77 100644 --- a/test/Sentry.AspNetCore.Tests/SentryMiddlewareTests.cs +++ b/test/Sentry.AspNetCore.Tests/SentryMiddlewareTests.cs @@ -682,8 +682,10 @@ public async Task InvokeAsync_InstrumenterOpenTelemetry_SavesScope() } } - [Fact] - public async Task InvokeAsync_RequestContainsSentryHeaders_ContinuesTrace() + [Theory] + [InlineData("Sentry-Trace", "4b4d2878507b43d3af7dd8c4ab7a96d9-3cc6fd1337d243de", "4b4d2878507b43d3af7dd8c4ab7a96d9")] + [InlineData("traceparent", "00-4b4d2878507b43d3af7dd8c4ab7a96d8-3cc6fd1337d243de-00", "4b4d2878507b43d3af7dd8c4ab7a96d8")] + public async Task InvokeAsync_RequestContainsSentryHeaders_ContinuesTrace(string headerName, string headerValue, string expectedTraceId) { SentryTraceHeader capturedTraceHeader = null; BaggageHeader capturedBaggageHeader = null; @@ -698,7 +700,7 @@ public async Task InvokeAsync_RequestContainsSentryHeaders_ContinuesTrace() var request = Substitute.For(); var fakeHeaders = new HeaderDictionary { - { "Sentry-Trace", "4b4d2878507b43d3af7dd8c4ab7a96d9-3cc6fd1337d243de"}, + { headerName, headerValue}, { "Baggage", "sentry-trace_id=4b4d2878507b43d3af7dd8c4ab7a96d9, sentry-public_key=eb18e953812b41c3aeb042e666fd3b5c"}, }; _ = request.Headers.Returns(fakeHeaders); @@ -710,19 +712,21 @@ public async Task InvokeAsync_RequestContainsSentryHeaders_ContinuesTrace() _fixture.Hub.Received().ContinueTrace(Arg.Any(), Arg.Any()); Assert.NotNull(capturedTraceHeader); - Assert.Equal("4b4d2878507b43d3af7dd8c4ab7a96d9", capturedTraceHeader.TraceId.ToString()); + Assert.Equal(expectedTraceId, capturedTraceHeader.TraceId.ToString()); Assert.NotNull(capturedBaggageHeader); Assert.Equal("sentry-trace_id=4b4d2878507b43d3af7dd8c4ab7a96d9, sentry-public_key=eb18e953812b41c3aeb042e666fd3b5c", capturedBaggageHeader.ToString()); } - [Fact] - public async Task InvokeAsync_RequestContainsSentryHeaders_AddsHeadersAndTransactionContextToItems() + [Theory] + [InlineData("Sentry-Trace", "4b4d2878507b43d3af7dd8c4ab7a96d9-3cc6fd1337d243de", "4b4d2878507b43d3af7dd8c4ab7a96d9")] + [InlineData("traceparent", "00-4b4d2878507b43d3af7dd8c4ab7a96d8-3cc6fd1337d243de-00", "4b4d2878507b43d3af7dd8c4ab7a96d8")] + public async Task InvokeAsync_RequestContainsSentryHeaders_AddsHeadersAndTransactionContextToItems(string headerName, string headerValue, string expectedTraceId) { var sut = _fixture.GetSut(); var request = Substitute.For(); var fakeHeaders = new HeaderDictionary { - { "Sentry-Trace", "4b4d2878507b43d3af7dd8c4ab7a96d9-3cc6fd1337d243de"}, + { headerName, headerValue}, { "Baggage", "sentry-trace_id=4b4d2878507b43d3af7dd8c4ab7a96d9, sentry-public_key=eb18e953812b41c3aeb042e666fd3b5c"}, }; var contextItems = new Dictionary(); @@ -741,7 +745,7 @@ public async Task InvokeAsync_RequestContainsSentryHeaders_AddsHeadersAndTransac var traceHeader = contextItems[SentryMiddleware.TraceHeaderItemKey] as SentryTraceHeader; Assert.NotNull(traceHeader); - Assert.Equal("4b4d2878507b43d3af7dd8c4ab7a96d9", traceHeader.TraceId.ToString()); + Assert.Equal(expectedTraceId, traceHeader.TraceId.ToString()); var baggageHeader = contextItems[SentryMiddleware.BaggageHeaderItemKey] as BaggageHeader; Assert.NotNull(baggageHeader); Assert.Equal("sentry-trace_id=4b4d2878507b43d3af7dd8c4ab7a96d9, sentry-public_key=eb18e953812b41c3aeb042e666fd3b5c", baggageHeader.ToString()); @@ -749,14 +753,16 @@ public async Task InvokeAsync_RequestContainsSentryHeaders_AddsHeadersAndTransac Assert.NotNull(transactionContext); } - [Fact] - public async Task InvokeAsync_InvokingWithTheSameContextTwice_DoesNotThrow() + [Theory] + [InlineData("Sentry-Trace", "4b4d2878507b43d3af7dd8c4ab7a96d9-3cc6fd1337d243de")] + [InlineData("traceparent", "00-4b4d2878507b43d3af7dd8c4ab7a96d8-3cc6fd1337d243de-01")] + public async Task InvokeAsync_InvokingWithTheSameContextTwice_DoesNotThrow(string headerName, string headerValue) { var sut = _fixture.GetSut(); var request = Substitute.For(); var fakeHeaders = new HeaderDictionary { - { "Sentry-Trace", "4b4d2878507b43d3af7dd8c4ab7a96d9-3cc6fd1337d243de"}, + { headerName, headerValue}, { "Baggage", "sentry-trace_id=4b4d2878507b43d3af7dd8c4ab7a96d9, sentry-public_key=eb18e953812b41c3aeb042e666fd3b5c"}, }; var contextItems = new Dictionary(); diff --git a/test/Sentry.Tests/SentryHttpMessageHandlerTests.cs b/test/Sentry.Tests/SentryHttpMessageHandlerTests.cs index 7cc372f5b9..19619fe1b1 100644 --- a/test/Sentry.Tests/SentryHttpMessageHandlerTests.cs +++ b/test/Sentry.Tests/SentryHttpMessageHandlerTests.cs @@ -5,21 +5,33 @@ namespace Sentry.Tests; /* * NOTE: All tests should be done for both asynchronous `SendAsync` and synchronous `Send` methods. * TODO: Find a way to consolidate these tests cleanly. - */ + */ public class SentryHttpMessageHandlerTests { - [Fact] - public async Task SendAsync_SentryTraceHeaderNotSet_SetsHeader_ByDefault() + [Theory] + [InlineData("75302ac48a024bde9a3b3734a82e36c8-1000000000000000-0", "sentry-trace", "75302ac48a024bde9a3b3734a82e36c8-1000000000000000-0")] + [InlineData("75302ac48a024bde9a3b3734a82e36c8-1000000000000000-1", "sentry-trace", "75302ac48a024bde9a3b3734a82e36c8-1000000000000000-1")] + [InlineData("75302ac48a024bde9a3b3734a82e36c8-1000000000000000-0", "traceparent", "00-75302ac48a024bde9a3b3734a82e36c8-1000000000000000-00")] + [InlineData("75302ac48a024bde9a3b3734a82e36c8-1000000000000000-1", "traceparent", "00-75302ac48a024bde9a3b3734a82e36c8-1000000000000000-01")] + public async Task SendAsync_SentryTraceHeaderNotSet_SetsHeader_WhenUrlMatchesPropagationOptions(string traceHeader, string headerName, string expectedValue) { // Arrange var hub = Substitute.For(); + var failedRequestHandler = Substitute.For(); + var options = new SentryOptions + { + TracePropagationTargets = new List + { + new("localhost") + } + }; hub.GetTraceHeader().ReturnsForAnyArgs( - SentryTraceHeader.Parse("75302ac48a024bde9a3b3734a82e36c8-1000000000000000-0")); + SentryTraceHeader.Parse(traceHeader)); using var innerHandler = new RecordingHttpMessageHandler(new FakeHttpMessageHandler()); - using var sentryHandler = new SentryHttpMessageHandler(innerHandler, hub); + using var sentryHandler = new SentryHttpMessageHandler(hub, options, innerHandler, failedRequestHandler); using var client = new HttpClient(sentryHandler); // Act @@ -29,12 +41,14 @@ public async Task SendAsync_SentryTraceHeaderNotSet_SetsHeader_ByDefault() // Assert request.Headers.Should().Contain(h => - h.Key == "sentry-trace" && - string.Concat(h.Value) == "75302ac48a024bde9a3b3734a82e36c8-1000000000000000-0"); + h.Key == headerName && + string.Concat(h.Value) == expectedValue); } - [Fact] - public async Task SendAsync_SentryTraceHeaderNotSet_SetsHeader_WhenUrlMatchesPropagationOptions() + [Theory] + [InlineData("sentry-trace")] + [InlineData("traceparent")] + public async Task SendAsync_SentryTraceHeaderNotSet_DoesntSetHeader_WhenUrlDoesntMatchesPropagationOptions(string headerName) { // Arrange var hub = Substitute.For(); @@ -43,7 +57,7 @@ public async Task SendAsync_SentryTraceHeaderNotSet_SetsHeader_WhenUrlMatchesPro { TracePropagationTargets = new List { - new("localhost") + new("foo") } }; @@ -60,13 +74,11 @@ public async Task SendAsync_SentryTraceHeaderNotSet_SetsHeader_WhenUrlMatchesPro using var request = innerHandler.GetRequests().Single(); // Assert - request.Headers.Should().Contain(h => - h.Key == "sentry-trace" && - string.Concat(h.Value) == "75302ac48a024bde9a3b3734a82e36c8-1000000000000000-0"); + request.Headers.Should().NotContain(h => h.Key == headerName); } [Fact] - public async Task SendAsync_SentryTraceHeaderNotSet_DoesntSetHeader_WhenUrlDoesntMatchesPropagationOptions() + public async Task SendAsync_SentryTraceHeaderNotSet_SetsBothHeadersHeader_WhenUrlMatchesPropagationOptions() { // Arrange var hub = Substitute.For(); @@ -75,12 +87,12 @@ public async Task SendAsync_SentryTraceHeaderNotSet_DoesntSetHeader_WhenUrlDoesn { TracePropagationTargets = new List { - new("foo") + new("localhost") } }; hub.GetTraceHeader().ReturnsForAnyArgs( - SentryTraceHeader.Parse("75302ac48a024bde9a3b3734a82e36c8-1000000000000000-0")); + SentryTraceHeader.Parse("6877cc6ac231622a3d1d518a472a65b8-5e3bc28befdb2e3c")); using var innerHandler = new RecordingHttpMessageHandler(new FakeHttpMessageHandler()); using var sentryHandler = new SentryHttpMessageHandler(hub, options, innerHandler, failedRequestHandler); @@ -92,11 +104,19 @@ public async Task SendAsync_SentryTraceHeaderNotSet_DoesntSetHeader_WhenUrlDoesn using var request = innerHandler.GetRequests().Single(); // Assert - request.Headers.Should().NotContain(h => h.Key == "sentry-trace"); + // Both headers should be set, see https://github.com/getsentry/team-sdks/issues/41 + request.Headers.Should().Contain(h => + h.Key == SentryTraceHeader.HttpHeaderName && + string.Concat(h.Value) == "6877cc6ac231622a3d1d518a472a65b8-5e3bc28befdb2e3c"); + request.Headers.Should().Contain(h => + h.Key == W3CTraceHeader.HttpHeaderName && + string.Concat(h.Value) == "00-6877cc6ac231622a3d1d518a472a65b8-5e3bc28befdb2e3c-00"); } - [Fact] - public async Task SendAsync_SentryTraceHeaderAlreadySet_NotOverwritten() + [Theory] + [InlineData("sentry-trace")] + [InlineData("traceparent")] + public async Task SendAsync_SentryTraceHeaderAlreadySet_NotOverwritten(string headerName) { // Arrange var hub = Substitute.For(); @@ -108,7 +128,7 @@ public async Task SendAsync_SentryTraceHeaderAlreadySet_NotOverwritten() using var sentryHandler = new SentryHttpMessageHandler(innerHandler, hub); using var client = new HttpClient(sentryHandler); - client.DefaultRequestHeaders.Add("sentry-trace", "foobar"); + client.DefaultRequestHeaders.Add(headerName, "foobar"); // Act await client.GetAsync("https://localhost/"); @@ -117,7 +137,7 @@ public async Task SendAsync_SentryTraceHeaderAlreadySet_NotOverwritten() // Assert request.Headers.Should().Contain(h => - h.Key == "sentry-trace" && + h.Key == headerName && string.Concat(h.Value) == "foobar"); } @@ -291,14 +311,17 @@ public void HandleResponse_SetsSpanData() } #if NET5_0_OR_GREATER - [Fact] - public void Send_SentryTraceHeaderNotSet_SetsHeader_ByDefault() + [Theory] + [InlineData("75302ac48a024bde9a3b3734a82e36c8-1000000000000000-0", "sentry-trace", "75302ac48a024bde9a3b3734a82e36c8-1000000000000000-0")] + [InlineData("75302ac48a024bde9a3b3734a82e36c8-1000000000000000-0", "traceparent", "00-75302ac48a024bde9a3b3734a82e36c8-1000000000000000-00")] + [InlineData("75302ac48a024bde9a3b3734a82e36c8-1000000000000000-1", "traceparent", "00-75302ac48a024bde9a3b3734a82e36c8-1000000000000000-01")] + public void Send_SentryTraceHeaderNotSet_SetsHeader_ByDefault(string traceHeader, string headerName, string expectedValue) { // Arrange var hub = Substitute.For(); hub.GetTraceHeader().ReturnsForAnyArgs( - SentryTraceHeader.Parse("75302ac48a024bde9a3b3734a82e36c8-1000000000000000-0")); + SentryTraceHeader.Parse(traceHeader)); using var innerHandler = new RecordingHttpMessageHandler(new FakeHttpMessageHandler()); using var sentryHandler = new SentryHttpMessageHandler(innerHandler, hub); @@ -311,12 +334,16 @@ public void Send_SentryTraceHeaderNotSet_SetsHeader_ByDefault() // Assert request.Headers.Should().Contain(h => - h.Key == "sentry-trace" && - string.Concat(h.Value) == "75302ac48a024bde9a3b3734a82e36c8-1000000000000000-0"); + h.Key == headerName && + string.Concat(h.Value) == expectedValue); } - [Fact] - public void Send_SentryTraceHeaderNotSet_SetsHeader_WhenUrlMatchesPropagationOptions() + [Theory] + [InlineData("75302ac48a024bde9a3b3734a82e36c8-1000000000000000-0", "sentry-trace", "75302ac48a024bde9a3b3734a82e36c8-1000000000000000-0")] + [InlineData("75302ac48a024bde9a3b3734a82e36c8-1000000000000000-1", "sentry-trace", "75302ac48a024bde9a3b3734a82e36c8-1000000000000000-1")] + [InlineData("75302ac48a024bde9a3b3734a82e36c8-1000000000000000-0", "traceparent", "00-75302ac48a024bde9a3b3734a82e36c8-1000000000000000-00")] + [InlineData("75302ac48a024bde9a3b3734a82e36c8-1000000000000000-1", "traceparent", "00-75302ac48a024bde9a3b3734a82e36c8-1000000000000000-01")] + public void Send_SentryTraceHeaderNotSet_SetsHeader_WhenUrlMatchesPropagationOptions(string traceHeader, string headerName, string expectedValue) { // Arrange var hub = Substitute.For(); @@ -330,7 +357,7 @@ public void Send_SentryTraceHeaderNotSet_SetsHeader_WhenUrlMatchesPropagationOpt }; hub.GetTraceHeader().ReturnsForAnyArgs( - SentryTraceHeader.Parse("75302ac48a024bde9a3b3734a82e36c8-1000000000000000-0")); + SentryTraceHeader.Parse(traceHeader)); using var innerHandler = new RecordingHttpMessageHandler(new FakeHttpMessageHandler()); using var sentryHandler = new SentryHttpMessageHandler(hub, options, innerHandler, failedRequestHandler); @@ -343,12 +370,14 @@ public void Send_SentryTraceHeaderNotSet_SetsHeader_WhenUrlMatchesPropagationOpt // Assert request.Headers.Should().Contain(h => - h.Key == "sentry-trace" && - string.Concat(h.Value) == "75302ac48a024bde9a3b3734a82e36c8-1000000000000000-0"); + h.Key == headerName && + string.Concat(h.Value) == expectedValue); } - [Fact] - public void Send_SentryTraceHeaderNotSet_DoesntSetHeader_WhenUrlDoesntMatchesPropagationOptions() + [Theory] + [InlineData("sentry-trace")] + [InlineData("traceparent")] + public void Send_SentryTraceHeaderNotSet_DoesntSetHeader_WhenUrlDoesntMatchesPropagationOptions(string headerName) { // Arrange var hub = Substitute.For(); @@ -374,11 +403,13 @@ public void Send_SentryTraceHeaderNotSet_DoesntSetHeader_WhenUrlDoesntMatchesPro using var request = innerHandler.GetRequests().Single(); // Assert - request.Headers.Should().NotContain(h => h.Key == "sentry-trace"); + request.Headers.Should().NotContain(h => h.Key == headerName); } - [Fact] - public void Send_SentryTraceHeaderAlreadySet_NotOverwritten() + [Theory] + [InlineData("sentry-trace")] + [InlineData("traceparent")] + public void Send_SentryTraceHeaderAlreadySet_NotOverwritten(string headerName) { // Arrange var hub = Substitute.For(); @@ -390,7 +421,7 @@ public void Send_SentryTraceHeaderAlreadySet_NotOverwritten() using var sentryHandler = new SentryHttpMessageHandler(innerHandler, hub); using var client = new HttpClient(sentryHandler); - client.DefaultRequestHeaders.Add("sentry-trace", "foobar"); + client.DefaultRequestHeaders.Add(headerName, "foobar"); // Act client.Get("https://localhost/"); @@ -399,7 +430,7 @@ public void Send_SentryTraceHeaderAlreadySet_NotOverwritten() // Assert request.Headers.Should().Contain(h => - h.Key == "sentry-trace" && + h.Key == headerName && string.Concat(h.Value) == "foobar"); } diff --git a/test/Sentry.Tests/W3CTraceHeaderTests.cs b/test/Sentry.Tests/W3CTraceHeaderTests.cs new file mode 100644 index 0000000000..f20bdccbfa --- /dev/null +++ b/test/Sentry.Tests/W3CTraceHeaderTests.cs @@ -0,0 +1,76 @@ +namespace Sentry.Tests; + +public class W3CTraceHeaderTests +{ + [Theory] + [InlineData(true, W3CTraceHeader.TraceFlagsSampled)] + [InlineData(false, W3CTraceHeader.TraceFlagsNotSampled)] + [InlineData(null, W3CTraceHeader.TraceFlagsNotSampled)] + public void ToString_ConvertsToW3CFormat(bool? isSampled, string traceFlags) + { + // Arrange + var source = new SentryTraceHeader(SentryId.Parse("75302ac48a024bde9a3b3734a82e36c8"), SpanId.Parse("1000000000000000"), isSampled); + var traceHeader = new W3CTraceHeader(source); + + // Act + var result = traceHeader.ToString(); + + // Assert + result.Should().Be($"00-75302ac48a024bde9a3b3734a82e36c8-1000000000000000-{traceFlags}"); + } + + [Theory] + [InlineData("00-4bc7d217a6721c0e60e85e46d25fb3e5-f51f11f284da5299-01", "4bc7d217a6721c0e60e85e46d25fb3e5", "f51f11f284da5299", true)] + [InlineData("00-3d19f80b6f7da306d7b5652745ec6173-703b42311109c14e-09", "3d19f80b6f7da306d7b5652745ec6173", "703b42311109c14e", true)] + [InlineData("00-992d690c7a3691eb0f409a3ba6ecc0cc-b4f1f8cbcc61a0e5-00", "992d690c7a3691eb0f409a3ba6ecc0cc", "b4f1f8cbcc61a0e5", false)] + [InlineData("00-19938c125f92c552c2e2711393725319-2ce52eea8ffe1335-xz", "19938c125f92c552c2e2711393725319", "2ce52eea8ffe1335", null)] // Invalid trace flags should not cause an exception + [InlineData("00-fba65b9f95900925670373fc2943339e-c5113ff625da6c9a-420", "fba65b9f95900925670373fc2943339e", "c5113ff625da6c9a", null)] // Invalid trace flags should not cause an exception + public void Parse_ValidW3CHeader_ReturnsW3CTraceHeader(string header, string expectedTraceId, string expectedSpanId, bool? expectedIsSampled) + { + // Act + var result = W3CTraceHeader.Parse(header); + + // Assert + result.Should().NotBeNull(); + result.SentryTraceHeader.TraceId.ToString().Should().Be(expectedTraceId); + result.SentryTraceHeader.SpanId.ToString().Should().Be(expectedSpanId); + result.SentryTraceHeader.IsSampled.Should().Be(expectedIsSampled); + } + + [Theory] + [InlineData("")] + [InlineData(" ")] + [InlineData("\n")] + [InlineData(null)] + public void Parse_Returns_Null_WhenHeaderIsNullOrEmpty(string header) + { + // Act + var result = W3CTraceHeader.Parse(header); + + // Assert + result.Should().BeNull(); + } + + [Theory] + [InlineData("00-4bc7d217a6721c0e60e85e46d25fb3e5-1000000000000000")] + [InlineData("01-f5cb855e16344ddd1538d10f82f6a018-a7018579d434fee4")] + [InlineData("01-7f97dee64921546b7c238cb8a0c1209d-a82702bf47683069-00")] + public void Parse_InvalidW3CHeader_ThrowsFormatException(string header) + { + // Act + Action act = () => W3CTraceHeader.Parse(header); + + // Assert + act.Should().Throw(); + } + + [Fact] + public void Null_Source_Throws_ArgumentNullException() + { + // Arrange &Act + Action act = static () => new W3CTraceHeader(null!); + + // Assert + act.Should().Throw().WithParameterName("source").WithMessage("*source*"); + } +} From 87919d1b6c15e1c84ec2016f9c34ad1713abc074 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 14 Apr 2025 20:10:40 +1200 Subject: [PATCH 194/363] chore(deps): update Native SDK to v0.8.3 (#4072) --------- Co-authored-by: GitHub Co-authored-by: James Crosswell Co-authored-by: Sentry Github Bot --- CHANGELOG.md | 13 ++++++++++++- modules/sentry-native | 2 +- .../Native/buildTransitive/Sentry.Native.targets | 1 + 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 12aed32927..f0d2b00e28 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,12 @@ - The `IScopeObserver` now has an `SetTrace` that allows observing changes to the scope's trace context. The SDK uses this to propagate the `trace ID` to `sentry-native`. This allows Sentry to connect errors coming from all layers of your application ([#4026](https://github.com/getsentry/sentry-dotnet/pull/4026)) - Exception.HResult is now included in the mechanism data for all exceptions ([#4029](https://github.com/getsentry/sentry-dotnet/pull/4029)) +### Dependencies + +- Bump Native SDK from v0.8.2 to v0.8.3 [#4072](https://github.com/getsentry/sentry-dotnet/pull/4072)) + - [changelog](https://github.com/getsentry/sentry-native/blob/master/CHANGELOG.md#083) + - [diff](https://github.com/getsentry/sentry-native/compare/0.8.2...0.8.3) + ### Fixes - Fixed symbolication and source context for net9.0-android ([#4033](https://github.com/getsentry/sentry-dotnet/pull/4033)) @@ -39,7 +45,7 @@ ### Dependencies -- Bump Native SDK from v0.8.1 to v0.8.2 ([#4050](https://github.com/getsentry/sentry-dotnet/pull/4050)) +- Bump Native SDK from v0.8.1 to v0.8.2 ([#4050](https://github.com/getsentry/sentry-dotnet/pull/4050) - [changelog](https://github.com/getsentry/sentry-native/blob/master/CHANGELOG.md#082) - [diff](https://github.com/getsentry/sentry-native/compare/0.8.1...0.8.2) - Bump CLI from v2.42.2 to v2.43.0 ([#4036](https://github.com/getsentry/sentry-dotnet/pull/4036), [#4049](https://github.com/getsentry/sentry-dotnet/pull/4049), [#4060](https://github.com/getsentry/sentry-dotnet/pull/4060), [#4062](https://github.com/getsentry/sentry-dotnet/pull/4062)) @@ -121,6 +127,11 @@ - .NET on iOS: Add experimental EnableAppHangTrackingV2 configuration flag to the options binding SDK ([#3877](https://github.com/getsentry/sentry-dotnet/pull/3877)) - Added `SentryOptions.DisableSentryHttpMessageHandler`. Useful if you're using `OpenTelemetry.Instrumentation.Http` and ending up with duplicate spans. ([#3879](https://github.com/getsentry/sentry-dotnet/pull/3879)) +### Dependencies + +- Bump Native SDK from v0.7.17 to v0.7.18 ([#3891](https://github.com/getsentry/sentry-dotnet/pull/3891)) + - [changelog](https://github.com/getsentry/sentry-native/blob/master/CHANGELOG.md#0718) + - [diff](https://github.com/getsentry/sentry-native/compare/0.7.17...0.7.18) ### Fixes - Prevent Native EXC_BAD_ACCESS signal errors from being captured when managed NullRefrenceExceptions occur ([#3909](https://github.com/getsentry/sentry-dotnet/pull/3909)) diff --git a/modules/sentry-native b/modules/sentry-native index 77dbf6a600..69f006a722 160000 --- a/modules/sentry-native +++ b/modules/sentry-native @@ -1 +1 @@ -Subproject commit 77dbf6a6006f0be24af30d056bdcac98a7bf5877 +Subproject commit 69f006a722ac1688ff3373acf0f7dcf0b067b56f diff --git a/src/Sentry/Platforms/Native/buildTransitive/Sentry.Native.targets b/src/Sentry/Platforms/Native/buildTransitive/Sentry.Native.targets index 48212c4d90..b9fc77351e 100644 --- a/src/Sentry/Platforms/Native/buildTransitive/Sentry.Native.targets +++ b/src/Sentry/Platforms/Native/buildTransitive/Sentry.Native.targets @@ -18,6 +18,7 @@ + From 4e7a114ce5d326a0645a3e5b2858cf8ff9e0e819 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Tue, 15 Apr 2025 09:39:10 +1200 Subject: [PATCH 195/363] fix: MSVCRT build warning on Windows (#4111) * fix: MSVCRT build warning on Windows Fixes `LNK4098` linker warning when compiling application AOT on windows. Resolves #3928 : - https://github.com/getsentry/sentry-dotnet/issues/3928 --- CHANGELOG.md | 1 + src/Sentry/buildTransitive/Sentry.targets | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f0d2b00e28..4a5f019bad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ ### Fixes - Prevent users from disabling AndroidEnableAssemblyCompression which leads to untrappable crash ([#4089](https://github.com/getsentry/sentry-dotnet/pull/4089)) +- Fixed MSVCRT build warning on Windows ([#4111](https://github.com/getsentry/sentry-dotnet/pull/4111)) ### Features diff --git a/src/Sentry/buildTransitive/Sentry.targets b/src/Sentry/buildTransitive/Sentry.targets index 790ed2aa1e..9e00c06a42 100644 --- a/src/Sentry/buildTransitive/Sentry.targets +++ b/src/Sentry/buildTransitive/Sentry.targets @@ -15,6 +15,11 @@ + + + + + Date: Tue, 15 Apr 2025 09:42:50 +1200 Subject: [PATCH 196/363] chore: Bump to dotnet SDK 9.0.203 (#4110) Bump the .NET SDK version to 9.0.203. Possibly impacts: - https://github.com/getsentry/sentry-dotnet/issues/3915 --- .github/actions/environment/action.yml | 2 +- global.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/actions/environment/action.yml b/.github/actions/environment/action.yml index 80582afd47..8df4fe9bf9 100644 --- a/.github/actions/environment/action.yml +++ b/.github/actions/environment/action.yml @@ -60,7 +60,7 @@ runs: with: dotnet-version: | 8.0.x - 9.0.202 + 9.0.203 - name: Install .NET Workloads shell: bash diff --git a/global.json b/global.json index e721e283bc..bd0f96e2b8 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "9.0.202", + "version": "9.0.203", "rollForward": "disable", "allowPrerelease": false } From 2d02cfdb57504147988eed56be84fd927bd7c6c8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 16 Apr 2025 09:56:33 +1200 Subject: [PATCH 197/363] build(deps): bump github/codeql-action from 3.28.13 to 3.28.15 (#4112) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.28.13 to 3.28.15. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/1b549b9259bda1cb5ddde3b41741a82a2d15a841...45775bd8235c68ba998cffa5171334d58593da47) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 3.28.15 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index d088039ebd..65119d8c48 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -35,7 +35,7 @@ jobs: uses: ./.github/actions/environment - name: Initialize CodeQL - uses: github/codeql-action/init@1b549b9259bda1cb5ddde3b41741a82a2d15a841 # pin@v2 + uses: github/codeql-action/init@45775bd8235c68ba998cffa5171334d58593da47 # pin@v2 with: languages: csharp @@ -49,6 +49,6 @@ jobs: run: dotnet build Sentry-CI-CodeQL.slnf --no-restore --nologo - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@1b549b9259bda1cb5ddde3b41741a82a2d15a841 # pin@v2 + uses: github/codeql-action/analyze@45775bd8235c68ba998cffa5171334d58593da47 # pin@v2 with: category: '/language:csharp' From 690a16972c803335c45727ed9fdfadd7a34c1016 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Wed, 16 Apr 2025 10:07:56 +1200 Subject: [PATCH 198/363] Bump to cocoa sdk v8.46.0 (#4103) --- CHANGELOG.md | 6 + Directory.Build.props | 2 +- Directory.Build.targets | 2 +- modules/sentry-cocoa.properties | 2 +- .../Sentry.Samples.Maui.csproj | 2 +- scripts/generate-cocoa-bindings.ps1 | 80 +--- src/Sentry.Bindings.Cocoa/ApiDefinitions.cs | 188 +++++--- .../Sentry.Bindings.Cocoa.csproj | 2 + src/Sentry.Bindings.Cocoa/StructsAndEnums.cs | 22 - .../SwiftApiDefinitions.cs | 405 ++++++++++++++++++ .../SwiftStructsAndEnums.cs | 65 +++ .../Sentry.Maui.Device.TestApp.csproj | 3 + 12 files changed, 623 insertions(+), 156 deletions(-) create mode 100644 src/Sentry.Bindings.Cocoa/SwiftApiDefinitions.cs create mode 100644 src/Sentry.Bindings.Cocoa/SwiftStructsAndEnums.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a5f019bad..cfe074f126 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,12 @@ - If an incoming HTTP request has the `traceparent` header, it is now parsed and interpreted like the `sentry-trace` header. Outgoing requests now contain the `traceparent` header to facilitate integration with servesr that only support the [W3C Trace Context](https://www.w3.org/TR/trace-context/). ([#4084](https://github.com/getsentry/sentry-dotnet/pull/4084)) +### Dependencies + +- Bump Cocoa SDK from v8.39.0 to v8.46.0 ([#4103](https://github.com/getsentry/sentry-dotnet/pull/4103)) + - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#8460) + - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.39.0...8.46.0) + ## 5.5.1 ### Fixes diff --git a/Directory.Build.props b/Directory.Build.props index 6ca7917196..be7695e161 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -50,7 +50,7 @@ --> $([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) - 12.2 + 13.0 15.0 21.0 10.0.17763.0 diff --git a/Directory.Build.targets b/Directory.Build.targets index 7593967e09..b01536755f 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -10,7 +10,7 @@ --> $([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) - 12.2 + 13.0 15.0 21.0 10.0.17763.0 diff --git a/modules/sentry-cocoa.properties b/modules/sentry-cocoa.properties index 799f17ac06..9d8498fef2 100644 --- a/modules/sentry-cocoa.properties +++ b/modules/sentry-cocoa.properties @@ -1,2 +1,2 @@ -version = 8.39.0 +version = 8.46.0 repo = https://github.com/getsentry/sentry-cocoa diff --git a/samples/Sentry.Samples.Maui/Sentry.Samples.Maui.csproj b/samples/Sentry.Samples.Maui/Sentry.Samples.Maui.csproj index 28c0f0d502..d570b75527 100644 --- a/samples/Sentry.Samples.Maui/Sentry.Samples.Maui.csproj +++ b/samples/Sentry.Samples.Maui/Sentry.Samples.Maui.csproj @@ -32,7 +32,7 @@ 1.0 1 - 12.2 + 13.0 15.0 21.0 10.0.17763.0 diff --git a/scripts/generate-cocoa-bindings.ps1 b/scripts/generate-cocoa-bindings.ps1 index 1a9f7a6a9e..0684bd7b56 100644 --- a/scripts/generate-cocoa-bindings.ps1 +++ b/scripts/generate-cocoa-bindings.ps1 @@ -1,3 +1,5 @@ +# Reference: https://github.com/xamarin/xamarin-macios/blob/main/docs/website/binding_types_reference_guide.md + Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' @@ -115,41 +117,6 @@ $Text = $Text -replace '\bpublic\b', 'internal' # Remove static CFunctions class $Text = $Text -replace '(?ms)\nstatic class CFunctions.*?}\n', '' -# This enum resides in the Sentry-Swift.h -# Appending it here so we don't need to import and create bindings for the entire header -$SentryLevel = @' - -[Native] -internal enum SentryLevel : ulong -{ - None = 0, - Debug = 1, - Info = 2, - Warning = 3, - Error = 4, - Fatal = 5 -} -'@ - -# This enum resides in the Sentry-Swift.h -# Appending it here so we don't need to import and create bindings for the entire header -$SentryTransactionNameSource = @' - -[Native] -internal enum SentryTransactionNameSource : long -{ - Custom = 0, - Url = 1, - Route = 2, - View = 3, - Component = 4, - Task = 5 -} -'@ - -$Text += "`n$SentryLevel" -$Text += "`n$SentryTransactionNameSource" - # Add header and output file $Text = "$Header`n`n$Text" $Text | Out-File "$BindingsPath/$File" @@ -180,6 +147,9 @@ $Text = $Text -replace '\bISentrySerializable\b', 'SentrySerializable' # Remove INSCopying due to https://github.com/xamarin/xamarin-macios/issues/17130 $Text = $Text -replace ': INSCopying,', ':' -replace '\s?[:,] INSCopying', '' +# Remove iOS attributes like [iOS (13, 0)] +$Text = $Text -replace '\[iOS \(13, 0\)\]\n?', '' + # Fix delegate argument names $Text = $Text -replace '(NSError) arg\d', '$1 error' $Text = $Text -replace '(NSHttpUrlResponse) arg\d', '$1 response' @@ -221,7 +191,7 @@ $Text = $Text -replace '\[Static\]\s*\[Internal\]\s*partial\s+interface\s+Consta # Update MethodToProperty translations $Text = $Text -replace '(Export \("get\w+"\)\]\n)\s*\[Verify \(MethodToProperty\)\]\n(.+ \{ get; \})', '$1$2' -$Text = $Text -replace '\[Verify \(MethodToProperty\)\]\n\s*(.+ (?:Hash|Value|DefaultIntegrations) \{ get; \})', '$1' +$Text = $Text -replace '\[Verify \(MethodToProperty\)\]\n\s*(.+ (?:Hash|Value|DefaultIntegrations|AppStartMeasurementWithSpans|BaggageHttpHeader) \{ get; \})', '$1' $Text = $Text -replace '\[Verify \(MethodToProperty\)\]\n\s*(.+) \{ get; \}', '$1();' # Allow weakly typed NSArray @@ -234,7 +204,7 @@ $Text = $Text -replace '(DEPRECATED_MSG_ATTRIBUTE\()\n\s*', '$1' # Remove default IsEqual implementation (already implemented by NSObject) $Text = $Text -replace '(?ms)\n?^ *// [^\n]*isEqual:.*?$.*?;\n', '' -# Replace obsolete platform avaialbility attributes +# Replace obsolete platform availability attributes $Text = $Text -replace '([\[,] )MacCatalyst \(', '$1Introduced (PlatformName.MacCatalyst, ' $Text = $Text -replace '([\[,] )Mac \(', '$1Introduced (PlatformName.MacOSX, ' $Text = $Text -replace '([\[,] )iOS \(', '$1Introduced (PlatformName.iOS, ' @@ -247,7 +217,6 @@ $Text = $Text -replace '(?m)(^\s*\/\/[^\r\n]*$\s*\[Export \("serialize"\)\]$\s*) $Text = $Text -replace '.*SentryEnvelope .*?[\s\S]*?\n\n', '' $Text = $Text -replace '.*typedef.*SentryOnAppStartMeasurementAvailable.*?[\s\S]*?\n\n', '' -$Text = $Text -replace '\n.*SentryReplayBreadcrumbConverter.*?[\s\S]*?\);\n', '' $propertiesToRemove = @( 'SentryAppStartMeasurement', @@ -262,41 +231,6 @@ foreach ($property in $propertiesToRemove) { $Text = $Text -replace "\n.*property.*$property.*?[\s\S]*?\}\n", '' } -# This interface resides in the Sentry-Swift.h -# Appending it here so we don't need to import and create bindings for the entire header -$SentryId = @' - -// @interface SentryId : NSObject -[BaseType (typeof(NSObject), Name = "_TtC6Sentry8SentryId")] -[Internal] -interface SentryId -{ - // @property (nonatomic, strong, class) SentryId * _Nonnull empty; - [Static] - [Export ("empty", ArgumentSemantic.Strong)] - SentryId Empty { get; set; } - - // @property (readonly, copy, nonatomic) NSString * _Nonnull sentryIdString; - [Export ("sentryIdString")] - string SentryIdString { get; } - - // -(instancetype _Nonnull)initWithUuid:(NSUUID * _Nonnull)uuid __attribute__((objc_designated_initializer)); - [Export ("initWithUuid:")] - [DesignatedInitializer] - NativeHandle Constructor (NSUuid uuid); - - // -(instancetype _Nonnull)initWithUUIDString:(NSString * _Nonnull)uuidString __attribute__((objc_designated_initializer)); - [Export ("initWithUUIDString:")] - [DesignatedInitializer] - NativeHandle Constructor (string uuidString); - - // @property (readonly, nonatomic) NSUInteger hash; - [Export ("hash")] - nuint Hash { get; } -} -'@ - -$Text += "`n$SentryId" # Add header and output file $Text = "$Header`n`n$Text" diff --git a/src/Sentry.Bindings.Cocoa/ApiDefinitions.cs b/src/Sentry.Bindings.Cocoa/ApiDefinitions.cs index 77ad1d5797..cf38a5e4b4 100644 --- a/src/Sentry.Bindings.Cocoa/ApiDefinitions.cs +++ b/src/Sentry.Bindings.Cocoa/ApiDefinitions.cs @@ -27,6 +27,7 @@ namespace Sentry.CocoaSdk; // typedef SentryEvent * _Nullable (^SentryBeforeSendEventCallback)(SentryEvent * _Nonnull); [Internal] +[return: NullAllowed] delegate SentryEvent SentryBeforeSendEventCallback (SentryEvent @event); // typedef id _Nullable (^SentryBeforeSendSpanCallback)(id _Nonnull); @@ -54,13 +55,13 @@ namespace Sentry.CocoaSdk; [return: NullAllowed] delegate NSNumber SentryTracesSamplerCallback (SentrySamplingContext samplingContext); -// typedef void (^SentrySpanCallback)(id _Nullable); +// typedef void (^SentrySpanCallback)(DEPRECATED_MSG_ATTRIBUTE("See `SentryScope.useSpan` for reasoning of deprecation.") id); [Internal] -delegate void SentrySpanCallback ([NullAllowed] SentrySpan span); +delegate void SentrySpanCallback (SentrySpan span); -// typedef BOOL (^SentryBeforeEmitMetricCallback)(NSString * _Nonnull, NSDictionary * _Nonnull); +// typedef void (^SentryUserFeedbackConfigurationBlock)(SentryUserFeedbackConfiguration * _Nonnull); [Internal] -delegate bool SentryBeforeEmitMetricCallback (string arg0, NSDictionary arg1); +delegate void SentryUserFeedbackConfigurationBlock (SentryUserFeedbackConfiguration arg0); // @interface SentryAttachment : NSObject [BaseType (typeof(NSObject))] @@ -138,6 +139,10 @@ interface SentryBaggage [NullAllowed, Export ("userSegment")] string UserSegment { get; } + // @property (readonly, nonatomic) NSString * _Nullable sampleRand; + [NullAllowed, Export ("sampleRand")] + string SampleRand { get; } + // @property (readonly, nonatomic) NSString * _Nullable sampleRate; [NullAllowed, Export ("sampleRate")] string SampleRate { get; } @@ -154,6 +159,10 @@ interface SentryBaggage [Export ("initWithTraceId:publicKey:releaseName:environment:transaction:userSegment:sampleRate:sampled:replayId:")] NativeHandle Constructor (SentryId traceId, string publicKey, [NullAllowed] string releaseName, [NullAllowed] string environment, [NullAllowed] string transaction, [NullAllowed] string userSegment, [NullAllowed] string sampleRate, [NullAllowed] string sampled, [NullAllowed] string replayId); + // -(instancetype _Nonnull)initWithTraceId:(SentryId * _Nonnull)traceId publicKey:(NSString * _Nonnull)publicKey releaseName:(NSString * _Nullable)releaseName environment:(NSString * _Nullable)environment transaction:(NSString * _Nullable)transaction userSegment:(NSString * _Nullable)userSegment sampleRate:(NSString * _Nullable)sampleRate sampleRand:(NSString * _Nullable)sampleRand sampled:(NSString * _Nullable)sampled replayId:(NSString * _Nullable)replayId; + [Export ("initWithTraceId:publicKey:releaseName:environment:transaction:userSegment:sampleRate:sampleRand:sampled:replayId:")] + NativeHandle Constructor (SentryId traceId, string publicKey, [NullAllowed] string releaseName, [NullAllowed] string environment, [NullAllowed] string transaction, [NullAllowed] string userSegment, [NullAllowed] string sampleRate, [NullAllowed] string sampleRand, [NullAllowed] string sampled, [NullAllowed] string replayId); + // -(NSString * _Nonnull)toHTTPHeaderWithOriginalBaggage:(NSDictionary * _Nullable)originalBaggage; [Export ("toHTTPHeaderWithOriginalBaggage:")] string ToHTTPHeaderWithOriginalBaggage ([NullAllowed] NSDictionary originalBaggage); @@ -198,6 +207,10 @@ interface SentryBreadcrumb : SentrySerializable [NullAllowed, Export ("message")] string Message { get; set; } + // @property (copy, nonatomic) NSString * _Nullable origin; + [NullAllowed, Export ("origin")] + string Origin { get; set; } + // @property (nonatomic, strong) NSDictionary * _Nullable data; [NullAllowed, Export ("data", ArgumentSemantic.Strong)] NSDictionary Data { get; set; } @@ -269,10 +282,14 @@ interface SentryClient [Export ("captureMessage:withScope:")] SentryId CaptureMessage (string message, SentryScope scope); - // -(void)captureUserFeedback:(SentryUserFeedback * _Nonnull)userFeedback __attribute__((swift_name("capture(userFeedback:)"))); + // -(void)captureUserFeedback:(SentryUserFeedback * _Nonnull)userFeedback __attribute__((swift_name("capture(userFeedback:)"))) __attribute__((deprecated("Use -[SentryClient captureFeedback:withScope:]."))); [Export ("captureUserFeedback:")] void CaptureUserFeedback (SentryUserFeedback userFeedback); + // -(void)captureFeedback:(SentryFeedback * _Nonnull)feedback withScope:(SentryScope * _Nonnull)scope __attribute__((swift_name("capture(feedback:scope:)"))); + [Export ("captureFeedback:withScope:")] + void CaptureFeedback (SentryFeedback feedback, SentryScope scope); + // -(void)flush:(NSTimeInterval)timeout __attribute__((swift_name("flush(timeout:)"))); [Export ("flush:")] void Flush (double timeout); @@ -282,13 +299,6 @@ interface SentryClient void Close (); } -// @interface SentryCrashExceptionApplication : NSObject -[BaseType (typeof(NSObject))] -[Internal] -interface SentryCrashExceptionApplication -{ -} - // @interface SentryDebugImageProvider : NSObject [BaseType (typeof(NSObject))] [Internal] @@ -298,7 +308,7 @@ interface SentryDebugImageProvider [Export ("getDebugImagesForThreads:")] SentryDebugMeta[] GetDebugImagesForThreads (SentryThread[] threads); - // -(NSArray * _Nonnull)getDebugImagesForThreads:(NSArray * _Nonnull)threads isCrash:(BOOL)isCrash; + // -(NSArray * _Nonnull)getDebugImagesForThreads:(NSArray * _Nonnull)threads isCrash:(BOOL)isCrash __attribute__((deprecated("This method is slow and will be removed in a future version. Use -[getDebugImagesFromCacheForThreads:] instead."))); [Export ("getDebugImagesForThreads:isCrash:")] SentryDebugMeta[] GetDebugImagesForThreads (SentryThread[] threads, bool isCrash); @@ -306,7 +316,7 @@ interface SentryDebugImageProvider [Export ("getDebugImagesForFrames:")] SentryDebugMeta[] GetDebugImagesForFrames (SentryFrame[] frames); - // -(NSArray * _Nonnull)getDebugImagesForFrames:(NSArray * _Nonnull)frames isCrash:(BOOL)isCrash; + // -(NSArray * _Nonnull)getDebugImagesForFrames:(NSArray * _Nonnull)frames isCrash:(BOOL)isCrash __attribute__((deprecated("This method is slow and will be removed in a future version. Use -[getDebugImagesFromCacheForFrames:] instead."))); [Export ("getDebugImagesForFrames:isCrash:")] SentryDebugMeta[] GetDebugImagesForFrames (SentryFrame[] frames, bool isCrash); @@ -314,7 +324,7 @@ interface SentryDebugImageProvider [Export ("getDebugImages")] SentryDebugMeta[] DebugImages { get; } - // -(NSArray * _Nonnull)getDebugImagesCrashed:(BOOL)isCrash; + // -(NSArray * _Nonnull)getDebugImagesCrashed:(BOOL)isCrash __attribute__((deprecated("This method is slow and will be removed in a future version. Use -[getDebugImagesFromCache:] instead."))); [Export ("getDebugImagesCrashed:")] SentryDebugMeta[] GetDebugImagesCrashed (bool isCrash); } @@ -549,6 +559,13 @@ interface SentryEvent : SentrySerializable NativeHandle Constructor (NSError error); } +// @interface SentryEventDecodable : SentryEvent +[BaseType (typeof(SentryEvent))] +[Internal] +interface SentryEventDecodable +{ +} + // @interface SentryException : NSObject [BaseType (typeof(NSObject))] [DisableDefaultCtor] @@ -873,7 +890,7 @@ interface SentrySpan : SentrySerializable // @required -(NSString * _Nullable)baggageHttpHeader; [Abstract] [NullAllowed, Export ("baggageHttpHeader")] - string BaggageHttpHeader(); + string BaggageHttpHeader { get; } } // @interface SentryHub : NSObject @@ -954,10 +971,14 @@ interface SentryHub [Export ("captureMessage:withScope:")] SentryId CaptureMessage (string message, SentryScope scope); - // -(void)captureUserFeedback:(SentryUserFeedback * _Nonnull)userFeedback __attribute__((swift_name("capture(userFeedback:)"))); + // -(void)captureUserFeedback:(SentryUserFeedback * _Nonnull)userFeedback __attribute__((swift_name("capture(userFeedback:)"))) __attribute__((deprecated("Use -[SentryHub captureFeedback:]."))); [Export ("captureUserFeedback:")] void CaptureUserFeedback (SentryUserFeedback userFeedback); + // -(void)captureFeedback:(SentryFeedback * _Nonnull)feedback; + [Export ("captureFeedback:")] + void CaptureFeedback (SentryFeedback feedback); + // -(void)configureScope:(void (^ _Nonnull)(SentryScope * _Nonnull))callback; [Export ("configureScope:")] void ConfigureScope (Action callback); @@ -1397,6 +1418,10 @@ interface SentryOptions [Export ("enablePerformanceV2")] bool EnablePerformanceV2 { get; set; } + // @property (assign, nonatomic) BOOL enablePersistingTracesWhenCrashing; + [Export ("enablePersistingTracesWhenCrashing")] + bool EnablePersistingTracesWhenCrashing { get; set; } + // @property (nonatomic) SentryScope * _Nonnull (^ _Nonnull)(SentryScope * _Nonnull) initialScope; [Export ("initialScope", ArgumentSemantic.Assign)] Func InitialScope { get; set; } @@ -1429,6 +1454,10 @@ interface SentryOptions [Export ("enablePreWarmedAppStartTracing")] bool EnablePreWarmedAppStartTracing { get; set; } + // @property (nonatomic, strong) SentryReplayOptions * _Nonnull sessionReplay; + [Export ("sessionReplay", ArgumentSemantic.Strong)] + SentryReplayOptions SessionReplay { get; set; } + // @property (assign, nonatomic) BOOL enableNetworkTracking; [Export ("enableNetworkTracking")] bool EnableNetworkTracking { get; set; } @@ -1575,6 +1604,10 @@ interface SentryOptions // @property (copy, nonatomic) NSString * _Nonnull spotlightUrl; [Export ("spotlightUrl")] string SpotlightUrl { get; set; } + + // @property (copy, nonatomic) API_AVAILABLE(ios(13.0)) SentryUserFeedbackConfigurationBlock configureUserFeedback __attribute__((availability(ios, introduced=13.0))); + [Export ("configureUserFeedback", ArgumentSemantic.Copy)] + SentryUserFeedbackConfigurationBlock ConfigureUserFeedback { get; set; } } // @interface SentryReplayApi : NSObject @@ -1605,6 +1638,18 @@ interface SentryReplayApi // -(void)stop; [Export ("stop")] void Stop (); + + // -(void)showMaskPreview; + [Export ("showMaskPreview")] + void ShowMaskPreview (); + + // -(void)showMaskPreview:(CGFloat)opacity; + [Export ("showMaskPreview:")] + void ShowMaskPreview (nfloat opacity); + + // -(void)hideMaskPreview; + [Export ("hideMaskPreview")] + void HideMaskPreview (); } // @interface SentryRequest : NSObject @@ -1762,15 +1807,15 @@ interface SentrySDK [Export ("captureMessage:withScopeBlock:")] SentryId CaptureMessage (string message, Action block); - // +(void)captureUserFeedback:(SentryUserFeedback * _Nonnull)userFeedback __attribute__((swift_name("capture(userFeedback:)"))); + // +(void)captureUserFeedback:(SentryUserFeedback * _Nonnull)userFeedback __attribute__((swift_name("capture(userFeedback:)"))) __attribute__((deprecated("Use SentrySDK.captureFeedback or use or configure our new managed UX with SentryOptions.configureUserFeedback."))); [Static] [Export ("captureUserFeedback:")] void CaptureUserFeedback (SentryUserFeedback userFeedback); - // +(void)showUserFeedbackForm; + // +(void)captureFeedback:(SentryFeedback * _Nonnull)feedback __attribute__((swift_name("capture(feedback:)"))); [Static] - [Export ("showUserFeedbackForm")] - void ShowUserFeedbackForm (); + [Export ("captureFeedback:")] + void CaptureFeedback (SentryFeedback feedback); // +(void)addBreadcrumb:(SentryBreadcrumb * _Nonnull)crumb __attribute__((swift_name("addBreadcrumb(_:)"))); [Static] @@ -1980,7 +2025,7 @@ partial interface SentryScope : SentrySerializable [Export ("clear")] void Clear (); - // -(void)useSpan:(SentrySpanCallback _Nonnull)callback; + // -(void)useSpan:(SentrySpanCallback _Nonnull)callback __attribute__((deprecated("This method was used to create an atomic block that could be used to mutate the current span. It is not atomic anymore and due to issues with memory safety in `NSBlock` it is now considered unsafe and deprecated. Use `span` instead."))); [Export ("useSpan:")] void UseSpan (SentrySpanCallback callback); } @@ -2103,6 +2148,10 @@ interface SentryTraceContext : SentrySerializable [NullAllowed, Export ("sampleRate")] string SampleRate { get; } + // @property (readonly, nonatomic) NSString * _Nullable sampleRand; + [NullAllowed, Export ("sampleRand")] + string SampleRand { get; } + // @property (readonly, nonatomic) NSString * _Nullable sampled; [NullAllowed, Export ("sampled")] string Sampled { get; } @@ -2115,6 +2164,10 @@ interface SentryTraceContext : SentrySerializable [Export ("initWithTraceId:publicKey:releaseName:environment:transaction:userSegment:sampleRate:sampled:replayId:")] NativeHandle Constructor (SentryId traceId, string publicKey, [NullAllowed] string releaseName, [NullAllowed] string environment, [NullAllowed] string transaction, [NullAllowed] string userSegment, [NullAllowed] string sampleRate, [NullAllowed] string sampled, [NullAllowed] string replayId); + // -(instancetype _Nonnull)initWithTraceId:(SentryId * _Nonnull)traceId publicKey:(NSString * _Nonnull)publicKey releaseName:(NSString * _Nullable)releaseName environment:(NSString * _Nullable)environment transaction:(NSString * _Nullable)transaction userSegment:(NSString * _Nullable)userSegment sampleRate:(NSString * _Nullable)sampleRate sampleRand:(NSString * _Nullable)sampleRand sampled:(NSString * _Nullable)sampled replayId:(NSString * _Nullable)replayId; + [Export ("initWithTraceId:publicKey:releaseName:environment:transaction:userSegment:sampleRate:sampleRand:sampled:replayId:")] + NativeHandle Constructor (SentryId traceId, string publicKey, [NullAllowed] string releaseName, [NullAllowed] string environment, [NullAllowed] string transaction, [NullAllowed] string userSegment, [NullAllowed] string sampleRate, [NullAllowed] string sampleRand, [NullAllowed] string sampled, [NullAllowed] string replayId); + // -(instancetype _Nullable)initWithScope:(SentryScope * _Nonnull)scope options:(SentryOptions * _Nonnull)options; [Export ("initWithScope:options:")] NativeHandle Constructor (SentryScope scope, SentryOptions options); @@ -2177,13 +2230,25 @@ interface SentryTransactionContext [Export ("nameSource")] SentryTransactionNameSource NameSource { get; } + // @property (nonatomic, strong) NSNumber * _Nullable sampleRate; + [NullAllowed, Export ("sampleRate", ArgumentSemantic.Strong)] + NSNumber SampleRate { get; set; } + + // @property (nonatomic, strong) NSNumber * _Nullable sampleRand; + [NullAllowed, Export ("sampleRand", ArgumentSemantic.Strong)] + NSNumber SampleRand { get; set; } + // @property (nonatomic) SentrySampleDecision parentSampled; [Export ("parentSampled", ArgumentSemantic.Assign)] SentrySampleDecision ParentSampled { get; set; } - // @property (nonatomic, strong) NSNumber * _Nullable sampleRate; - [NullAllowed, Export ("sampleRate", ArgumentSemantic.Strong)] - NSNumber SampleRate { get; set; } + // @property (nonatomic, strong) NSNumber * _Nullable parentSampleRate; + [NullAllowed, Export ("parentSampleRate", ArgumentSemantic.Strong)] + NSNumber ParentSampleRate { get; set; } + + // @property (nonatomic, strong) NSNumber * _Nullable parentSampleRand; + [NullAllowed, Export ("parentSampleRand", ArgumentSemantic.Strong)] + NSNumber ParentSampleRand { get; set; } // @property (assign, nonatomic) BOOL forNextAppLaunch; [Export ("forNextAppLaunch")] @@ -2193,13 +2258,21 @@ interface SentryTransactionContext [Export ("initWithName:operation:")] NativeHandle Constructor (string name, string operation); - // -(instancetype _Nonnull)initWithName:(NSString * _Nonnull)name operation:(NSString * _Nonnull)operation sampled:(SentrySampleDecision)sampled; + // -(instancetype _Nonnull)initWithName:(NSString * _Nonnull)name operation:(NSString * _Nonnull)operation sampled:(SentrySampleDecision)sampled __attribute__((deprecated("Use initWithName:operation:sampled:sampleRate:sampleRand instead"))); [Export ("initWithName:operation:sampled:")] NativeHandle Constructor (string name, string operation, SentrySampleDecision sampled); - // -(instancetype _Nonnull)initWithName:(NSString * _Nonnull)name operation:(NSString * _Nonnull)operation traceId:(SentryId * _Nonnull)traceId spanId:(SentrySpanId * _Nonnull)spanId parentSpanId:(SentrySpanId * _Nullable)parentSpanId parentSampled:(SentrySampleDecision)parentSampled; + // -(instancetype _Nonnull)initWithName:(NSString * _Nonnull)name operation:(NSString * _Nonnull)operation sampled:(SentrySampleDecision)sampled sampleRate:(NSNumber * _Nullable)sampleRate sampleRand:(NSNumber * _Nullable)sampleRand; + [Export ("initWithName:operation:sampled:sampleRate:sampleRand:")] + NativeHandle Constructor (string name, string operation, SentrySampleDecision sampled, [NullAllowed] NSNumber sampleRate, [NullAllowed] NSNumber sampleRand); + + // -(instancetype _Nonnull)initWithName:(NSString * _Nonnull)name operation:(NSString * _Nonnull)operation traceId:(SentryId * _Nonnull)traceId spanId:(SentrySpanId * _Nonnull)spanId parentSpanId:(SentrySpanId * _Nullable)parentSpanId parentSampled:(SentrySampleDecision)parentSampled __attribute__((deprecated("Use initWithName:operation:traceId:spanId:parentSpanId:parentSampled:parentSampleRate:parentSampleRand instead"))); [Export ("initWithName:operation:traceId:spanId:parentSpanId:parentSampled:")] NativeHandle Constructor (string name, string operation, SentryId traceId, SentrySpanId spanId, [NullAllowed] SentrySpanId parentSpanId, SentrySampleDecision parentSampled); + + // -(instancetype _Nonnull)initWithName:(NSString * _Nonnull)name operation:(NSString * _Nonnull)operation traceId:(SentryId * _Nonnull)traceId spanId:(SentrySpanId * _Nonnull)spanId parentSpanId:(SentrySpanId * _Nullable)parentSpanId parentSampled:(SentrySampleDecision)parentSampled parentSampleRate:(NSNumber * _Nullable)parentSampleRate parentSampleRand:(NSNumber * _Nullable)parentSampleRand; + [Export ("initWithName:operation:traceId:spanId:parentSpanId:parentSampled:parentSampleRate:parentSampleRand:")] + NativeHandle Constructor (string name, string operation, SentryId traceId, SentrySpanId spanId, [NullAllowed] SentrySpanId parentSpanId, SentrySampleDecision parentSampled, [NullAllowed] NSNumber parentSampleRate, [NullAllowed] NSNumber parentSampleRand); } // @interface SentryUser : NSObject @@ -2353,6 +2426,11 @@ interface PrivateSentrySDKOnly [Export ("getSdkVersionString")] string SdkVersionString { get; } + // +(void)addSdkPackage:(NSString * _Nonnull)name version:(NSString * _Nonnull)version; + [Static] + [Export ("addSdkPackage:version:")] + void AddSdkPackage (string name, string version); + // +(NSDictionary * _Nonnull)getExtraContext; [Static] [Export ("getExtraContext")] @@ -2419,6 +2497,16 @@ interface PrivateSentrySDKOnly [Export ("setCurrentScreen:")] void SetCurrentScreen (string screenName); + // +(UIView * _Nonnull)sessionReplayMaskingOverlay:(id _Nonnull)options; + [Static] + [Export ("sessionReplayMaskingOverlay:")] + UIView SessionReplayMaskingOverlay (SentryRedactOptions options); + + // +(void)configureSessionReplayWith:(id _Nullable)breadcrumbConverter screenshotProvider:(id _Nullable)screenshotProvider; + [Static] + [Export ("configureSessionReplayWith:screenshotProvider:")] + void ConfigureSessionReplayWith ([NullAllowed] SentryReplayBreadcrumbConverter breadcrumbConverter, [NullAllowed] SentryViewScreenshotProvider screenshotProvider); + // +(void)captureReplay; [Static] [Export ("captureReplay")] @@ -2439,10 +2527,25 @@ interface PrivateSentrySDKOnly [Export ("addReplayRedactClasses:")] void AddReplayRedactClasses (Class[] classes); + // +(void)setIgnoreContainerClass:(Class _Nonnull)containerClass; + [Static] + [Export ("setIgnoreContainerClass:")] + void SetIgnoreContainerClass (Class containerClass); + + // +(void)setRedactContainerClass:(Class _Nonnull)containerClass; + [Static] + [Export ("setRedactContainerClass:")] + void SetRedactContainerClass (Class containerClass); + + // +(void)setReplayTags:(NSDictionary * _Nonnull)tags; + [Static] + [Export ("setReplayTags:")] + void SetReplayTags (NSDictionary tags); + // +(NSDictionary * _Nullable)appStartMeasurementWithSpans; [Static] [NullAllowed, Export ("appStartMeasurementWithSpans")] - NSDictionary AppStartMeasurementWithSpans(); + NSDictionary AppStartMeasurementWithSpans { get; } // +(SentryUser * _Nonnull)userWithDictionary:(NSDictionary * _Nonnull)dictionary; [Static] @@ -2454,32 +2557,3 @@ interface PrivateSentrySDKOnly [Export ("breadcrumbWithDictionary:")] SentryBreadcrumb BreadcrumbWithDictionary (NSDictionary dictionary); } - -// @interface SentryId : NSObject -[BaseType (typeof(NSObject), Name = "_TtC6Sentry8SentryId")] -[Internal] -interface SentryId -{ - // @property (nonatomic, strong, class) SentryId * _Nonnull empty; - [Static] - [Export ("empty", ArgumentSemantic.Strong)] - SentryId Empty { get; set; } - - // @property (readonly, copy, nonatomic) NSString * _Nonnull sentryIdString; - [Export ("sentryIdString")] - string SentryIdString { get; } - - // -(instancetype _Nonnull)initWithUuid:(NSUUID * _Nonnull)uuid __attribute__((objc_designated_initializer)); - [Export ("initWithUuid:")] - [DesignatedInitializer] - NativeHandle Constructor (NSUuid uuid); - - // -(instancetype _Nonnull)initWithUUIDString:(NSString * _Nonnull)uuidString __attribute__((objc_designated_initializer)); - [Export ("initWithUUIDString:")] - [DesignatedInitializer] - NativeHandle Constructor (string uuidString); - - // @property (readonly, nonatomic) NSUInteger hash; - [Export ("hash")] - nuint Hash { get; } -} diff --git a/src/Sentry.Bindings.Cocoa/Sentry.Bindings.Cocoa.csproj b/src/Sentry.Bindings.Cocoa/Sentry.Bindings.Cocoa.csproj index 60364aae34..92164fc5ac 100644 --- a/src/Sentry.Bindings.Cocoa/Sentry.Bindings.Cocoa.csproj +++ b/src/Sentry.Bindings.Cocoa/Sentry.Bindings.Cocoa.csproj @@ -31,7 +31,9 @@ + + diff --git a/src/Sentry.Bindings.Cocoa/StructsAndEnums.cs b/src/Sentry.Bindings.Cocoa/StructsAndEnums.cs index 7fb471de9c..9735003cd1 100644 --- a/src/Sentry.Bindings.Cocoa/StructsAndEnums.cs +++ b/src/Sentry.Bindings.Cocoa/StructsAndEnums.cs @@ -66,25 +66,3 @@ internal enum SentrySpanStatus : ulong OutOfRange, DataLoss } - -[Native] -internal enum SentryLevel : ulong -{ - None = 0, - Debug = 1, - Info = 2, - Warning = 3, - Error = 4, - Fatal = 5 -} - -[Native] -internal enum SentryTransactionNameSource : long -{ - Custom = 0, - Url = 1, - Route = 2, - View = 3, - Component = 4, - Task = 5 -} diff --git a/src/Sentry.Bindings.Cocoa/SwiftApiDefinitions.cs b/src/Sentry.Bindings.Cocoa/SwiftApiDefinitions.cs new file mode 100644 index 0000000000..61fa7556a0 --- /dev/null +++ b/src/Sentry.Bindings.Cocoa/SwiftApiDefinitions.cs @@ -0,0 +1,405 @@ +/* + * This file defines iOS API contracts for the members we need from Sentry-Swift.h + * Note that we are **not** using Objective Sharpie to generate contracts (instead they're maintained manually). + */ +using System; +using Foundation; +using ObjCRuntime; +using UIKit; + +namespace Sentry.CocoaSdk; + +[BaseType(typeof(NSObject), Name = "_TtC6Sentry14SentryFeedback")] +[DisableDefaultCtor] // Marks the default constructor as unavailable +[Internal] +interface SentryFeedback +{ + [Export("name", ArgumentSemantic.Copy)] + string Name { get; set; } + + [Export("email", ArgumentSemantic.Copy)] + string Email { get; set; } + + [Export("message", ArgumentSemantic.Copy)] + string Message { get; set; } + + [Export("source")] + SentryFeedbackSource Source { get; set; } + + [Export("eventId", ArgumentSemantic.Strong)] + SentryId EventId { get; } + + [Export("associatedEventId", ArgumentSemantic.Strong)] + SentryId AssociatedEventId { get; set; } + + [Export("initWithMessage:name:email:source:associatedEventId:attachments:")] + [DesignatedInitializer] + IntPtr Constructor(string message, [NullAllowed] string name, [NullAllowed] string email, SentryFeedbackSource source, [NullAllowed] SentryId associatedEventId, [NullAllowed] NSData[] attachments); +} + +// @interface SentryId : NSObject +[BaseType (typeof(NSObject), Name = "_TtC6Sentry8SentryId")] +[Internal] +interface SentryId +{ + // @property (nonatomic, strong, class) SentryId * _Nonnull empty; + [Static] + [Export ("empty", ArgumentSemantic.Strong)] + SentryId Empty { get; set; } + + // @property (readonly, copy, nonatomic) NSString * _Nonnull sentryIdString; + [Export ("sentryIdString")] + string SentryIdString { get; } + + // -(instancetype _Nonnull)initWithUuid:(NSUUID * _Nonnull)uuid __attribute__((objc_designated_initializer)); + [Export ("initWithUuid:")] + [DesignatedInitializer] + NativeHandle Constructor (NSUuid uuid); + + // -(instancetype _Nonnull)initWithUUIDString:(NSString * _Nonnull)uuidString __attribute__((objc_designated_initializer)); + [Export ("initWithUUIDString:")] + [DesignatedInitializer] + NativeHandle Constructor (string uuidString); + + // @property (readonly, nonatomic) NSUInteger hash; + [Export ("hash")] + nuint Hash { get; } +} + +// @interface SentrySessionReplayIntegration : SentryBaseIntegration +[BaseType (typeof(NSObject))] +[Internal] +interface SentrySessionReplayIntegration +{ + // -(instancetype _Nonnull)initForManualUse:(SentryOptions * _Nonnull)options; + [Export ("initForManualUse:")] + NativeHandle Constructor (SentryOptions options); + // -(BOOL)captureReplay; + [Export ("captureReplay")] + bool CaptureReplay(); + // -(void)configureReplayWith:(id _Nullable)breadcrumbConverter screenshotProvider:(id _Nullable)screenshotProvider; + [Export ("configureReplayWith:screenshotProvider:")] + void ConfigureReplayWith ([NullAllowed] SentryReplayBreadcrumbConverter breadcrumbConverter, [NullAllowed] SentryViewScreenshotProvider screenshotProvider); + // -(void)pause; + [Export ("pause")] + void Pause (); + // -(void)resume; + [Export ("resume")] + void Resume (); + // -(void)stop; + [Export ("stop")] + void Stop (); + // -(void)start; + [Export ("start")] + void Start (); + // +(id _Nonnull)createBreadcrumbwithTimestamp:(NSDate * _Nonnull)timestamp category:(NSString * _Nonnull)category message:(NSString * _Nullable)message level:(enum SentryLevel)level data:(NSDictionary * _Nullable)data; + [Static] + [Export ("createBreadcrumbwithTimestamp:category:message:level:data:")] + SentryRRWebEvent CreateBreadcrumbwithTimestamp (NSDate timestamp, string category, [NullAllowed] string message, SentryLevel level, [NullAllowed] NSDictionary data); + // +(id _Nonnull)createNetworkBreadcrumbWithTimestamp:(NSDate * _Nonnull)timestamp endTimestamp:(NSDate * _Nonnull)endTimestamp operation:(NSString * _Nonnull)operation description:(NSString * _Nonnull)description data:(NSDictionary * _Nonnull)data; + [Static] + [Export ("createNetworkBreadcrumbWithTimestamp:endTimestamp:operation:description:data:")] + SentryRRWebEvent CreateNetworkBreadcrumbWithTimestamp (NSDate timestamp, NSDate endTimestamp, string operation, string description, NSDictionary data); + // +(id _Nonnull)createDefaultBreadcrumbConverter; + [Static] + [Export ("createDefaultBreadcrumbConverter")] + SentryReplayBreadcrumbConverter CreateDefaultBreadcrumbConverter(); +} + +[Protocol(Name = "_TtP6Sentry19SentryRedactOptions_")] +[Model] +[BaseType(typeof(NSObject))] +[Internal] +internal interface SentryRedactOptions +{ + [Abstract] + [Export("maskAllText")] + bool MaskAllText { get; } + + [Abstract] + [Export("maskAllImages")] + bool MaskAllImages { get; } + + [Abstract] + [Export("maskedViewClasses", ArgumentSemantic.Copy)] + Class[] MaskedViewClasses { get; } + + [Abstract] + [Export("unmaskedViewClasses", ArgumentSemantic.Copy)] + Class[] UnmaskedViewClasses { get; } +} + +// @protocol SentryReplayBreadcrumbConverter +[Protocol (Name = "_TtP6Sentry31SentryReplayBreadcrumbConverter_")] +[BaseType (typeof(NSObject), Name = "_TtP6Sentry31SentryReplayBreadcrumbConverter_")] +[Model] +[Internal] +interface SentryReplayBreadcrumbConverter +{ + // @required -(id _Nullable)convertFrom:(SentryBreadcrumb * _Nonnull)breadcrumb __attribute__((warn_unused_result(""))); + [Abstract] + [Export ("convertFrom:")] + [return: NullAllowed] + SentryRRWebEvent ConvertFrom (SentryBreadcrumb breadcrumb); +} + +// @interface SentryReplayOptions : NSObject +[BaseType (typeof(NSObject), Name = "_TtC6Sentry19SentryReplayOptions")] +[Internal] +interface SentryReplayOptions //: ISentryRedactOptions +{ + // @property (nonatomic) float sessionSampleRate; + [Export ("sessionSampleRate")] + float SessionSampleRate { get; set; } + // @property (nonatomic) float onErrorSampleRate; + [Export ("onErrorSampleRate")] + float OnErrorSampleRate { get; set; } + // @property (nonatomic) BOOL maskAllText; + [Export ("maskAllText")] + bool MaskAllText { get; set; } + // @property (nonatomic) BOOL maskAllImages; + [Export ("maskAllImages")] + bool MaskAllImages { get; set; } + // @property (nonatomic) enum SentryReplayQuality quality; + [Export ("quality", ArgumentSemantic.Assign)] + SentryReplayQuality Quality { get; set; } + /* + // @property (copy, nonatomic) NSArray * _Nonnull maskedViewClasses; + //[Export ("maskedViewClasses", ArgumentSemantic.Copy)] + //Class[] MaskedViewClasses { get; set; } + // @property (copy, nonatomic) NSArray * _Nonnull unmaskedViewClasses; + //[Export ("unmaskedViewClasses", ArgumentSemantic.Copy)] + //Class[] UnmaskedViewClasses { get; set; } + // @property (readonly, nonatomic) NSInteger replayBitRate; + [Export ("replayBitRate")] + nint ReplayBitRate { get; } + // @property (readonly, nonatomic) float sizeScale; + [Export ("sizeScale")] + float SizeScale { get; } + // @property (nonatomic) NSUInteger frameRate; + [Export ("frameRate")] + nuint FrameRate { get; set; } + // @property (readonly, nonatomic) NSTimeInterval errorReplayDuration; + [Export ("errorReplayDuration")] + double ErrorReplayDuration { get; } + // @property (readonly, nonatomic) NSTimeInterval sessionSegmentDuration; + [Export ("sessionSegmentDuration")] + double SessionSegmentDuration { get; } + // @property (readonly, nonatomic) NSTimeInterval maximumDuration; + [Export ("maximumDuration")] + double MaximumDuration { get; } + // -(instancetype _Nonnull)initWithSessionSampleRate:(float)sessionSampleRate onErrorSampleRate:(float)onErrorSampleRate maskAllText:(BOOL)maskAllText maskAllImages:(BOOL)maskAllImages __attribute__((objc_designated_initializer)); + [Export ("initWithSessionSampleRate:onErrorSampleRate:maskAllText:maskAllImages:")] + [DesignatedInitializer] + NativeHandle Constructor (float sessionSampleRate, float onErrorSampleRate, bool maskAllText, bool maskAllImages); + // -(instancetype _Nonnull)initWithDictionary:(NSDictionary * _Nonnull)dictionary; + [Export ("initWithDictionary:")] + NativeHandle Constructor (NSDictionary dictionary); + */ +} + +// @interface SentryRRWebEvent : NSObject +[BaseType (typeof(NSObject), Name = "_TtC6Sentry16SentryRRWebEvent")] +[Protocol] +[Model] +[DisableDefaultCtor] +[Internal] +interface SentryRRWebEvent : SentrySerializable +{ + // @property (readonly, nonatomic) enum SentryRRWebEventType type; + [Export ("type")] + SentryRRWebEventType Type { get; } + // @property (readonly, copy, nonatomic) NSDate * _Nonnull timestamp; + [Export ("timestamp", ArgumentSemantic.Copy)] + NSDate Timestamp { get; } + // @property (readonly, copy, nonatomic) NSDictionary * _Nullable data; + [NullAllowed, Export ("data", ArgumentSemantic.Copy)] + NSDictionary Data { get; } + // -(instancetype _Nonnull)initWithType:(enum SentryRRWebEventType)type timestamp:(NSDate * _Nonnull)timestamp data:(NSDictionary * _Nullable)data __attribute__((objc_designated_initializer)); + [Export ("initWithType:timestamp:data:")] + [DesignatedInitializer] + NativeHandle Constructor (SentryRRWebEventType type, NSDate timestamp, [NullAllowed] NSDictionary data); + // -(NSDictionary * _Nonnull)serialize __attribute__((warn_unused_result(""))); + [Export ("serialize")] + new NSDictionary Serialize(); +} + +[BaseType(typeof(NSObject), Name = "_TtC6Sentry31SentryUserFeedbackConfiguration")] +[DisableDefaultCtor] +[Internal] +interface SentryUserFeedbackConfiguration +{ + [Export("animations")] + bool Animations { get; set; } + + [NullAllowed, Export("configureWidget", ArgumentSemantic.Copy)] + Action ConfigureWidget { get; set; } + + [Export("widgetConfig", ArgumentSemantic.Strong)] + SentryUserFeedbackWidgetConfiguration WidgetConfig { get; set; } + + [Export("useShakeGesture")] + bool UseShakeGesture { get; set; } + + [Export("showFormForScreenshots")] + bool ShowFormForScreenshots { get; set; } + + // [NullAllowed, Export("configureForm", ArgumentSemantic.Copy)] + // Action ConfigureForm { get; set; } + + // [Export("formConfig", ArgumentSemantic.Strong)] + // SentryUserFeedbackFormConfiguration FormConfig { get; set; } + + [NullAllowed, Export("tags", ArgumentSemantic.Copy)] + NSDictionary Tags { get; set; } + + [NullAllowed, Export("onFormOpen", ArgumentSemantic.Copy)] + Action OnFormOpen { get; set; } + + [NullAllowed, Export("onFormClose", ArgumentSemantic.Copy)] + Action OnFormClose { get; set; } + + [NullAllowed, Export("onSubmitSuccess", ArgumentSemantic.Copy)] + Action> OnSubmitSuccess { get; set; } + + [NullAllowed, Export("onSubmitError", ArgumentSemantic.Copy)] + Action OnSubmitError { get; set; } + + // [NullAllowed, Export("configureTheme", ArgumentSemantic.Copy)] + // Action ConfigureTheme { get; set; } + // + // [Export("theme", ArgumentSemantic.Strong)] + // SentryUserFeedbackThemeConfiguration Theme { get; set; } + // + // [NullAllowed, Export("configureDarkTheme", ArgumentSemantic.Copy)] + // Action ConfigureDarkTheme { get; set; } + // + // [Export("darkTheme", ArgumentSemantic.Strong)] + // SentryUserFeedbackThemeConfiguration DarkTheme { get; set; } + + [Export("textEffectiveHeightCenter")] + nfloat TextEffectiveHeightCenter { get; set; } + + [Export("scaleFactor")] + nfloat ScaleFactor { get; set; } + + [Export("calculateScaleFactor")] + nfloat CalculateScaleFactor(); + + [Export("paddingScaleFactor")] + nfloat PaddingScaleFactor { get; set; } + + [Export("calculatePaddingScaleFactor")] + nfloat CalculatePaddingScaleFactor(); + + [Export("recalculateScaleFactors")] + void RecalculateScaleFactors(); + + [Export("padding")] + nfloat Padding { get; } + + [Export("spacing")] + nfloat Spacing { get; } + + [Export("margin")] + nfloat Margin { get; } + + [Export("init")] + [DesignatedInitializer] + IntPtr Constructor(); +} + +// [BaseType(typeof(NSObject), Name = "_TtC6Sentry37SentryUserFeedbackThemeConfiguration")] +// [DisableDefaultCtor] +// [Internal] +// interface SentryUserFeedbackThemeConfiguration +// { +// [Export("backgroundColor", ArgumentSemantic.Strong)] +// UIColor BackgroundColor { get; set; } +// +// [Export("textColor", ArgumentSemantic.Strong)] +// UIColor TextColor { get; set; } +// +// [Export("buttonColor", ArgumentSemantic.Strong)] +// UIColor ButtonColor { get; set; } +// +// [Export("buttonTextColor", ArgumentSemantic.Strong)] +// UIColor ButtonTextColor { get; set; } +// +// [Export("init")] +// [DesignatedInitializer] +// IntPtr Constructor(); +// } + +[BaseType(typeof(NSObject), Name = "_TtC6Sentry37SentryUserFeedbackWidgetConfiguration")] +[DisableDefaultCtor] +[Internal] +interface SentryUserFeedbackWidgetConfiguration +{ + [Export("autoInject")] + bool AutoInject { get; set; } + + [Export("defaultLabelText", ArgumentSemantic.Copy)] + string DefaultLabelText { get; } + + [NullAllowed, Export("labelText", ArgumentSemantic.Copy)] + string LabelText { get; set; } + + [Export("showIcon")] + bool ShowIcon { get; set; } + + [NullAllowed, Export("widgetAccessibilityLabel", ArgumentSemantic.Copy)] + string WidgetAccessibilityLabel { get; set; } + + [Export("windowLevel")] + nfloat WindowLevel { get; set; } + + [Export("location")] + NSDirectionalRectEdge Location { get; set; } + + [Export("layoutUIOffset")] + UIOffset LayoutUIOffset { get; set; } + + [Export("init")] + [DesignatedInitializer] + IntPtr Constructor(); +} + + +// [BaseType(typeof(NSObject), Name = "_TtC6Sentry37SentryUserFeedbackFormConfiguration")] +// [DisableDefaultCtor] +// [Internal] +// interface SentryUserFeedbackFormConfiguration +// { +// [Export("title", ArgumentSemantic.Copy)] +// string Title { get; set; } +// +// [Export("subtitle", ArgumentSemantic.Copy)] +// string Subtitle { get; set; } +// +// [Export("submitButtonTitle", ArgumentSemantic.Copy)] +// string SubmitButtonTitle { get; set; } +// +// [Export("cancelButtonTitle", ArgumentSemantic.Copy)] +// string CancelButtonTitle { get; set; } +// +// [Export("thankYouMessage", ArgumentSemantic.Copy)] +// string ThankYouMessage { get; set; } +// +// [Export("init")] +// [DesignatedInitializer] +// IntPtr Constructor(); +// } + +// @protocol SentryViewScreenshotProvider +[Protocol (Name = "_TtP6Sentry28SentryViewScreenshotProvider_")] +[Model] +[BaseType (typeof(NSObject), Name = "_TtP6Sentry28SentryViewScreenshotProvider_")] +[Internal] +interface SentryViewScreenshotProvider +{ + // @required -(void)imageWithView:(UIView * _Nonnull)view onComplete:(void (^ _Nonnull)(UIImage * _Nonnull))onComplete; + [Abstract] + [Export ("imageWithView:onComplete:")] + void OnComplete (UIView view, Action onComplete); +} diff --git a/src/Sentry.Bindings.Cocoa/SwiftStructsAndEnums.cs b/src/Sentry.Bindings.Cocoa/SwiftStructsAndEnums.cs new file mode 100644 index 0000000000..efd86efee2 --- /dev/null +++ b/src/Sentry.Bindings.Cocoa/SwiftStructsAndEnums.cs @@ -0,0 +1,65 @@ +/* + * This file defines iOS API contracts for enums we need from Sentry-Swift.h. + * Note that we are **not** using Objective Sharpie to generate these contracts (instead they're maintained manually). + */ +using System.Runtime.InteropServices; +using Foundation; +using ObjCRuntime; +using Sentry; + +namespace Sentry.CocoaSdk; + +[Native] +internal enum SentryFeedbackSource : long +{ + Unknown = 0, + User = 1, + System = 2, + Other = 3 +} + +[Native] +internal enum SentryLevel : ulong +{ + None = 0, + Debug = 1, + Info = 2, + Warning = 3, + Error = 4, + Fatal = 5 +} + +[Native] +internal enum SentryReplayQuality : long +{ + Low = 0, + Medium = 1, + High = 2 +} + +[Native] +internal enum SentryReplayType : long +{ + Session = 0, + Buffer = 1 +} + +[Native] +internal enum SentryRRWebEventType : long +{ + None = 0, + Touch = 3, + Meta = 4, + Custom = 5 +} + +[Native] +internal enum SentryTransactionNameSource : long +{ + Custom = 0, + Url = 1, + Route = 2, + View = 3, + Component = 4, + Task = 5 +} diff --git a/test/Sentry.Maui.Device.TestApp/Sentry.Maui.Device.TestApp.csproj b/test/Sentry.Maui.Device.TestApp/Sentry.Maui.Device.TestApp.csproj index 7d49fa1699..08f0a39f7f 100644 --- a/test/Sentry.Maui.Device.TestApp/Sentry.Maui.Device.TestApp.csproj +++ b/test/Sentry.Maui.Device.TestApp/Sentry.Maui.Device.TestApp.csproj @@ -34,6 +34,9 @@ 1.0 1 + 13.0 + 21.0 + true + + + + + + + true + + false From e72ca08d3b0d25cf09690090cd09187c235922b1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Apr 2025 16:23:36 +1200 Subject: [PATCH 205/363] build(deps): bump codecov/codecov-action from 5.4.0 to 5.4.2 (#4126) Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 5.4.0 to 5.4.2. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/0565863a31f2c772f9f0395002a31e3f06189574...ad3126e916f78f00edff4ed0317cf185271ccc2d) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-version: 5.4.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f10a025974..8fd0b9dfb8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -124,7 +124,7 @@ jobs: run: dotnet test Sentry-CI-Build-${{ runner.os }}.slnf -c Release --no-build --nologo -l GitHubActions -l "trx;LogFilePrefix=testresults_${{ runner.os }}" --collect "XPlat Code Coverage" - name: Upload code coverage - uses: codecov/codecov-action@0565863a31f2c772f9f0395002a31e3f06189574 + uses: codecov/codecov-action@ad3126e916f78f00edff4ed0317cf185271ccc2d - name: Upload build and test outputs if: failure() From b1d397211d0070665725ea003f1c4356b1748ff9 Mon Sep 17 00:00:00 2001 From: Stefan Jandl Date: Tue, 22 Apr 2025 16:30:46 +0200 Subject: [PATCH 206/363] chore: `InternalVisibleTo` Unity SDK tests (#4130) --- src/Sentry/Sentry.csproj | 1 + src/Sentry/SentrySdk.cs | 7 ------- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/Sentry/Sentry.csproj b/src/Sentry/Sentry.csproj index 49632a39aa..a1b36eec9b 100644 --- a/src/Sentry/Sentry.csproj +++ b/src/Sentry/Sentry.csproj @@ -22,6 +22,7 @@ + diff --git a/src/Sentry/SentrySdk.cs b/src/Sentry/SentrySdk.cs index 8c5efbd967..401a0fa6f0 100644 --- a/src/Sentry/SentrySdk.cs +++ b/src/Sentry/SentrySdk.cs @@ -209,13 +209,6 @@ internal static IDisposable UseHub(IHub hub) return new DisposeHandle(hub); } - /// - /// Allows to set the trace - /// - internal static void SetTrace(SentryId traceId, SpanId parentSpanId) => - CurrentHub.ConfigureScope(scope => - scope.SetPropagationContext(new SentryPropagationContext(traceId, parentSpanId))); - /// /// Flushes the queue of captured events until the timeout set in /// is reached. From 5ba1e2babab5ddb854167405852c139b85a61a8d Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Wed, 23 Apr 2025 20:19:46 +1200 Subject: [PATCH 207/363] chore: Upload build logs even for failed builds (#4129) --- .github/workflows/build.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8fd0b9dfb8..8f1970c21f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -112,9 +112,11 @@ jobs: run: dotnet restore Sentry-CI-Build-${{ runner.os }}.slnf --nologo - name: Build + id: build run: dotnet build Sentry-CI-Build-${{ runner.os }}.slnf -c Release --no-restore --nologo -v:minimal -flp:logfile=build.log -p:CopyLocalLockFileAssemblies=true -bl:build.binlog - name: Upload build logs + if: ${{ steps.build.outcome != 'skipped' }} uses: actions/upload-artifact@v4 with: name: ${{ runner.os }}-build-logs From 084e3b4bf2c043b42ad6b11892a604e027b3d1a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20P=C3=B6lz?= <38893694+Flash0ver@users.noreply.github.com> Date: Wed, 23 Apr 2025 11:57:05 +0200 Subject: [PATCH 208/363] fix: rename `DisableSentryNative` to `SentryNative` (#4134) --- CHANGELOG.md | 2 +- .../Platforms/Native/buildTransitive/Sentry.Native.targets | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 198f555c62..be90f5d69f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ ### Features -- Option to disable the SentryNative integration ([#4107](https://github.com/getsentry/sentry-dotnet/pull/4107)) +- Option to disable the SentryNative integration ([#4107](https://github.com/getsentry/sentry-dotnet/pull/4107), [#4134](https://github.com/getsentry/sentry-dotnet/pull/4134)) - Reintroduced experimental support for Session Replay on Android ([#4097](https://github.com/getsentry/sentry-dotnet/pull/4097)) ### Fixes diff --git a/src/Sentry/Platforms/Native/buildTransitive/Sentry.Native.targets b/src/Sentry/Platforms/Native/buildTransitive/Sentry.Native.targets index 8bdd87b869..5ab0088570 100644 --- a/src/Sentry/Platforms/Native/buildTransitive/Sentry.Native.targets +++ b/src/Sentry/Platforms/Native/buildTransitive/Sentry.Native.targets @@ -9,11 +9,11 @@ - + @@ -22,7 +22,7 @@ true - false + false From d86fe28d931aed859abc9bf95fa6888b6181ecaa Mon Sep 17 00:00:00 2001 From: Bruno Garcia Date: Wed, 23 Apr 2025 20:39:40 -0400 Subject: [PATCH 209/363] release: 5.6.0 (#4137) Co-authored-by: getsentry-bot --- CHANGELOG.md | 2 +- Directory.Build.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index be90f5d69f..9973d686d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## Unreleased +## 5.6.0 ### Features diff --git a/Directory.Build.props b/Directory.Build.props index be7695e161..287b6ac8f9 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,7 +1,7 @@ - 5.5.1 + 5.6.0 13 true true From bb765ee78439cff21569dd1ac3b043680d8b9c09 Mon Sep 17 00:00:00 2001 From: Bruno Garcia Date: Wed, 23 Apr 2025 21:19:11 -0400 Subject: [PATCH 210/363] add to changelog how to use SentryNative (#4138) --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9973d686d7..0b45fefa4d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Features - Option to disable the SentryNative integration ([#4107](https://github.com/getsentry/sentry-dotnet/pull/4107), [#4134](https://github.com/getsentry/sentry-dotnet/pull/4134)) + - To disable it, add this msbuild property: `false` - Reintroduced experimental support for Session Replay on Android ([#4097](https://github.com/getsentry/sentry-dotnet/pull/4097)) ### Fixes From 16fa8dcf486a5217f7b603b17f9c0d9ccd4ecc7e Mon Sep 17 00:00:00 2001 From: Ivan Dlugos <6349682+vaind@users.noreply.github.com> Date: Sun, 27 Apr 2025 03:16:52 +0200 Subject: [PATCH 211/363] test: skip profiling startup timeout failures in CI (#4144) --- .../SamplingTransactionProfilerTests.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/Sentry.Profiling.Tests/SamplingTransactionProfilerTests.cs b/test/Sentry.Profiling.Tests/SamplingTransactionProfilerTests.cs index af5914011d..c5aa9a83ed 100644 --- a/test/Sentry.Profiling.Tests/SamplingTransactionProfilerTests.cs +++ b/test/Sentry.Profiling.Tests/SamplingTransactionProfilerTests.cs @@ -273,6 +273,10 @@ async Task VerifyAsync(HttpRequestMessage message) { using var hub = new Hub(options); + var factory = (options.TransactionProfilerFactory as SamplingTransactionProfilerFactory)!; + Skip.If(TestEnvironment.IsGitHubActions && factory.StartupTimedOut, "Session sometimes takes too long to start in CI."); + Assert.False(factory.StartupTimedOut); + var clock = SentryStopwatch.StartNew(); var tx = hub.StartTransaction("name", "op"); RunForMs(RuntimeMs); From 85c56e2d2e72042889162b120ab89e2099141372 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Sun, 27 Apr 2025 15:15:33 +1200 Subject: [PATCH 212/363] Custom SessionReplay masks in MAUI Android apps (#4121) --- CHANGELOG.md | 6 ++ samples/Sentry.Samples.Maui/MainPage.xaml | 2 + samples/Sentry.Samples.Maui/MauiProgram.cs | 11 ++- .../Platforms/Android/MainActivity.cs | 3 + src/Sentry.Maui/AssemblyInfo.cs | 3 + .../Internal/MauiButtonEventsBinder.cs | 1 - src/Sentry.Maui/Internal/MauiEventsBinder.cs | 2 + .../Internal/MauiVisualElementEventsBinder.cs | 66 ++++++++++++++ .../SentryMauiAppBuilderExtensions.cs | 1 + src/Sentry.Maui/SentryMauiOptions.cs | 2 +- src/Sentry.Maui/SessionReplay.cs | 90 +++++++++++++++++++ src/Sentry.Maui/SessionReplayMaskMode.cs | 32 +++++++ src/Sentry/Platforms/Android/NativeOptions.cs | 12 +++ ...piApprovalTests.Run.DotNet8_0.verified.txt | 15 +++- ...piApprovalTests.Run.DotNet9_0.verified.txt | 15 +++- .../MauiEventsBinderTests.VisualElement.cs | 3 + .../MauiEventsBinderTests.cs | 4 + .../MauiVisualElementEventsBinderTests.cs | 59 ++++++++++++ 18 files changed, 320 insertions(+), 7 deletions(-) create mode 100644 src/Sentry.Maui/AssemblyInfo.cs create mode 100644 src/Sentry.Maui/Internal/MauiVisualElementEventsBinder.cs create mode 100644 src/Sentry.Maui/SessionReplay.cs create mode 100644 src/Sentry.Maui/SessionReplayMaskMode.cs create mode 100644 test/Sentry.Maui.Tests/MauiVisualElementEventsBinderTests.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b45fefa4d..cbdabbe783 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +### Features + +- Custom SessionReplay masks in MAUI Android apps ([#4121](https://github.com/getsentry/sentry-dotnet/pull/4121)) + ## 5.6.0 ### Features diff --git a/samples/Sentry.Samples.Maui/MainPage.xaml b/samples/Sentry.Samples.Maui/MainPage.xaml index 672b9d2347..276176d175 100644 --- a/samples/Sentry.Samples.Maui/MainPage.xaml +++ b/samples/Sentry.Samples.Maui/MainPage.xaml @@ -1,6 +1,7 @@ @@ -11,6 +12,7 @@ diff --git a/samples/Sentry.Samples.Maui/MauiProgram.cs b/samples/Sentry.Samples.Maui/MauiProgram.cs index b3f6df76b0..22c206bb8c 100644 --- a/samples/Sentry.Samples.Maui/MauiProgram.cs +++ b/samples/Sentry.Samples.Maui/MauiProgram.cs @@ -25,12 +25,17 @@ public static MauiApp CreateMauiApp() options.Debug = true; options.SampleRate = 1.0F; -#if ANDROID +#if __ANDROID__ // Currently experimental support is only available on Android options.Native.ExperimentalOptions.SessionReplay.OnErrorSampleRate = 1.0; options.Native.ExperimentalOptions.SessionReplay.SessionSampleRate = 1.0; - options.Native.ExperimentalOptions.SessionReplay.MaskAllImages = false; - options.Native.ExperimentalOptions.SessionReplay.MaskAllText = false; + // Mask all images and text by default. This can be overridden for individual view elements via the + // sentry:SessionReplay.Mask XML attribute (see MainPage.xaml for an example) + options.Native.ExperimentalOptions.SessionReplay.MaskAllImages = true; + options.Native.ExperimentalOptions.SessionReplay.MaskAllText = true; + // Alternatively the masking behaviour for entire classes of VisualElements can be configured here as + // an exception to the default behaviour. + options.Native.ExperimentalOptions.SessionReplay.UnmaskControlsOfType public const string Type = "response"; - internal Dictionary? InternalHeaders { get; private set; } + internal RedactedHeaders? InternalHeaders { get; private set; } /// /// Gets or sets the HTTP response body size. @@ -57,7 +57,7 @@ public sealed class Response : ISentryJsonSerializable, ICloneable, IU /// /// If a header appears multiple times it needs to be merged according to the HTTP standard for header merging. /// - public IDictionary Headers => InternalHeaders ??= new Dictionary(); + public IDictionary Headers => InternalHeaders ??= new(); /// /// Gets or sets the HTTP Status response code @@ -69,10 +69,13 @@ internal void AddHeaders(IEnumerable>> { foreach (var header in headers) { - Headers.Add( - header.Key, - string.Join("; ", header.Value) - ); + // Always redact the Authorization header + if (header.Key.Equals("Authorization", StringComparison.OrdinalIgnoreCase)) + { + Headers.Add(header.Key, PiiExtensions.RedactedText); + continue; + } + Headers.Add(header.Key, string.Join("; ", header.Value)); } } @@ -144,7 +147,7 @@ public static Response FromJson(JsonElement json) BodySize = bodySize, Cookies = cookies, Data = data, - InternalHeaders = headers?.WhereNotNullValue().ToDict(), + InternalHeaders = headers.ToRedactedHeaders(), StatusCode = statusCode }; } diff --git a/src/Sentry/SentryRequest.cs b/src/Sentry/SentryRequest.cs index e2a3ca432e..9f00a6b0f1 100644 --- a/src/Sentry/SentryRequest.cs +++ b/src/Sentry/SentryRequest.cs @@ -1,4 +1,5 @@ using Sentry.Extensibility; +using Sentry.Internal; using Sentry.Internal.Extensions; namespace Sentry; @@ -31,7 +32,7 @@ public sealed class SentryRequest : ISentryJsonSerializable internal Dictionary? InternalOther { get; private set; } - internal Dictionary? InternalHeaders { get; private set; } + internal RedactedHeaders? InternalHeaders { get; private set; } /// /// Gets or sets the full request URL, if available. @@ -81,7 +82,7 @@ public sealed class SentryRequest : ISentryJsonSerializable /// If a header appears multiple times it needs to be merged according to the HTTP standard for header merging. /// /// The headers. - public IDictionary Headers => InternalHeaders ??= new Dictionary(); + public IDictionary Headers => InternalHeaders ??= new(); /// /// Gets or sets the optional environment data. @@ -102,6 +103,12 @@ internal void AddHeaders(IEnumerable>> { foreach (var header in headers) { + // Always redact the Authorization header + if (header.Key.Equals("Authorization", StringComparison.OrdinalIgnoreCase)) + { + Headers.Add(header.Key, PiiExtensions.RedactedText); + continue; + } Headers.Add(header.Key, string.Join("; ", header.Value)); } } @@ -176,7 +183,7 @@ public static SentryRequest FromJson(JsonElement json) { InternalEnv = env?.WhereNotNullValue().ToDict(), InternalOther = other?.WhereNotNullValue().ToDict(), - InternalHeaders = headers?.WhereNotNullValue().ToDict(), + InternalHeaders = headers.ToRedactedHeaders(), Url = url, Method = method, Data = data, diff --git a/test/Sentry.Tests/Internals/RedactedHeadersTests.cs b/test/Sentry.Tests/Internals/RedactedHeadersTests.cs new file mode 100644 index 0000000000..1185e9c529 --- /dev/null +++ b/test/Sentry.Tests/Internals/RedactedHeadersTests.cs @@ -0,0 +1,118 @@ +namespace Sentry.Tests.Internals; + +public class RedactedHeadersTests +{ + [Fact] + public void Add_WithAuthorizationKey_ShouldStoreFilteredValue() + { + // Arrange + var headers = new RedactedHeaders(); + + // Act + headers.Add("Authorization", "Bearer 123"); + + // Assert + headers["Authorization"].Should().Be("[Filtered]"); + } + + [Fact] + public void IndexerSet_WithAuthorizationKey_ShouldStoreFilteredValue() + { + // Arrange + var headers = new RedactedHeaders(); + + // Act + headers["Authorization"] = "Bearer 456"; + + // Assert + headers["Authorization"].Should().Be("[Filtered]"); + } + + [Fact] + public void Add_WithOtherKey_ShouldStoreOriginalValue() + { + // Arrange + var headers = new RedactedHeaders(); + + // Act + headers.Add("User-Agent", "TestAgent"); + + // Assert + headers["User-Agent"].Should().Be("TestAgent"); + } + + [Fact] + public void IndexerGet_WithMissingKey_ShouldThrowKeyNotFoundException() + { + // Arrange + var headers = new RedactedHeaders(); + + // Act + var act = () => _ = headers["Missing"]; + + // Assert + act.Should().Throw(); + } + + [Fact] + public void TryGetValue_WithExistingKey_ShouldReturnTrueAndValue() + { + // Arrange + var headers = new RedactedHeaders + { + ["Authorization"] = "secret" + }; + + // Act + var success = headers.TryGetValue("Authorization", out var value); + + // Assert + success.Should().BeTrue(); + value.Should().Be("[Filtered]"); + } + + [Fact] + public void TryGetValue_WithMissingKey_ShouldReturnFalse() + { + // Arrange + var headers = new RedactedHeaders(); + + // Act + var success = headers.TryGetValue("Nonexistent", out var value); + + // Assert + success.Should().BeFalse(); + value.Should().BeNull(); // nullable context may allow this + } + + [Fact] + public void ImplicitConversion_FromDictionary_ShouldRedactAuthorization() + { + // Arrange + var dict = new Dictionary + { + { "Authorization", "Token xyz" }, + { "Custom", "Value" } + }; + + // Act + RedactedHeaders headers = dict; + + // Assert + headers["Authorization"].Should().Be("[Filtered]"); + headers["Custom"].Should().Be("Value"); + } + + [Fact] + public void CaseInsensitiveKeyMatching_ShouldRedactAuthorization() + { + // Arrange + var headers = new RedactedHeaders(); + + // Act + headers.Add("authorization", "should be filtered"); + + // Assert + headers["AUTHORIZATION"].Should().Be("[Filtered]"); + } +} From df30f770fe090cc6b65f506a9c125bbec3b5d940 Mon Sep 17 00:00:00 2001 From: Allan Ritchie Date: Thu, 8 May 2025 04:37:41 -0400 Subject: [PATCH 233/363] Remove Library Signing from Sentry.Hangfire (#4099) --------- Co-authored-by: James Crosswell --- CHANGELOG.md | 1 + src/Sentry.Hangfire/Sentry.Hangfire.csproj | 1 + src/Sentry.Hangfire/SentryServerFilter.cs | 4 +++- src/Sentry/Sentry.csproj | 1 - src/Sentry/SentryClientExtensions.cs | 14 ++++++++++++++ .../ApiApprovalTests.Run.DotNet8_0.verified.txt | 2 ++ .../ApiApprovalTests.Run.DotNet9_0.verified.txt | 2 ++ .../ApiApprovalTests.Run.Net4_8.verified.txt | 2 ++ test/Sentry.Tests/SentrySdkTests.cs | 10 +++++++--- 9 files changed, 32 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ee7ab5d37f..7fa2f077c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Fixes - Redact Authorization headers before sending events to Sentry ([#4164](https://github.com/getsentry/sentry-dotnet/pull/4164)) +- Remove Strong Naming from Sentry.Hangfire ([#4099](https://github.com/getsentry/sentry-dotnet/pull/4099)) ### Features diff --git a/src/Sentry.Hangfire/Sentry.Hangfire.csproj b/src/Sentry.Hangfire/Sentry.Hangfire.csproj index d2f0ba0aa1..941dd8c94d 100644 --- a/src/Sentry.Hangfire/Sentry.Hangfire.csproj +++ b/src/Sentry.Hangfire/Sentry.Hangfire.csproj @@ -5,6 +5,7 @@ $(PackageTags);Hangfire net9.0;net8.0;net462 enable + false diff --git a/src/Sentry.Hangfire/SentryServerFilter.cs b/src/Sentry.Hangfire/SentryServerFilter.cs index c4cb4f3551..fab9a64212 100644 --- a/src/Sentry.Hangfire/SentryServerFilter.cs +++ b/src/Sentry.Hangfire/SentryServerFilter.cs @@ -17,7 +17,9 @@ public SentryServerFilter() : this(null, null) internal SentryServerFilter(IHub? hub, IDiagnosticLogger? logger) { _hub = hub ?? HubAdapter.Instance; - _logger = logger ?? _hub.GetSentryOptions()?.DiagnosticLogger; +#pragma warning disable CS0618 // Type or member is obsolete + _logger = logger ?? _hub.GetInternalSentryOptions()?.DiagnosticLogger; +#pragma warning restore CS0618 // Type or member is obsolete } public void OnPerforming(PerformingContext context) diff --git a/src/Sentry/Sentry.csproj b/src/Sentry/Sentry.csproj index 256506c9f3..6db7048663 100644 --- a/src/Sentry/Sentry.csproj +++ b/src/Sentry/Sentry.csproj @@ -173,7 +173,6 @@ - diff --git a/src/Sentry/SentryClientExtensions.cs b/src/Sentry/SentryClientExtensions.cs index 2af8b85f8c..73d394f205 100644 --- a/src/Sentry/SentryClientExtensions.cs +++ b/src/Sentry/SentryClientExtensions.cs @@ -114,4 +114,18 @@ public static Task FlushAsync(this ISentryClient client) HubAdapter => SentrySdk.CurrentOptions, _ => SentryOptionsForTestingOnly }; + + /// + /// + /// Gets internal SentryOptions for integrations like Hangfire that don't support strong assembly names. + /// + /// + /// *** This is not meant for external use !!! *** + /// > + /// + /// + /// + [Obsolete("This method is meant for external usage only")] + public static SentryOptions? GetInternalSentryOptions(this ISentryClient clientOrHub) => + clientOrHub.GetSentryOptions(); } diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt index f70df81000..2b8f31a57a 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt @@ -484,6 +484,8 @@ namespace Sentry public static void Flush(this Sentry.ISentryClient client) { } public static void Flush(this Sentry.ISentryClient client, System.TimeSpan timeout) { } public static System.Threading.Tasks.Task FlushAsync(this Sentry.ISentryClient client) { } + [System.Obsolete("This method is meant for external usage only")] + public static Sentry.SentryOptions? GetInternalSentryOptions(this Sentry.ISentryClient clientOrHub) { } } public static class SentryConstants { diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt index f70df81000..2b8f31a57a 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt @@ -484,6 +484,8 @@ namespace Sentry public static void Flush(this Sentry.ISentryClient client) { } public static void Flush(this Sentry.ISentryClient client, System.TimeSpan timeout) { } public static System.Threading.Tasks.Task FlushAsync(this Sentry.ISentryClient client) { } + [System.Obsolete("This method is meant for external usage only")] + public static Sentry.SentryOptions? GetInternalSentryOptions(this Sentry.ISentryClient clientOrHub) { } } public static class SentryConstants { diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt index 3060d147e9..5f9c1d34fe 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt @@ -472,6 +472,8 @@ namespace Sentry public static void Flush(this Sentry.ISentryClient client) { } public static void Flush(this Sentry.ISentryClient client, System.TimeSpan timeout) { } public static System.Threading.Tasks.Task FlushAsync(this Sentry.ISentryClient client) { } + [System.Obsolete("This method is meant for external usage only")] + public static Sentry.SentryOptions? GetInternalSentryOptions(this Sentry.ISentryClient clientOrHub) { } } public static class SentryConstants { diff --git a/test/Sentry.Tests/SentrySdkTests.cs b/test/Sentry.Tests/SentrySdkTests.cs index ed99d7a746..fd6abadb00 100644 --- a/test/Sentry.Tests/SentrySdkTests.cs +++ b/test/Sentry.Tests/SentrySdkTests.cs @@ -676,14 +676,18 @@ public void Implements_Client() [Fact] public void Implements_ClientExtensions() { - var clientExtensions = typeof(SentryClientExtensions).GetMembers(BindingFlags.Public | BindingFlags.Static) + string[] excludedMembers = [nameof(SentryClientExtensions.GetSentryOptions), nameof(SentryClientExtensions.GetInternalSentryOptions)]; + var clientExtensions = typeof(SentryClientExtensions) + .GetMembers(BindingFlags.Public | BindingFlags.Static) + .Where(x => !excludedMembers.Contains(x.Name)) // Remove the extension argument: Method(this ISentryClient client, ... .Select(m => m.ToString()! .Replace($"({typeof(ISentryClient).FullName}", "(") .Replace("(, ", "(")); - var sentrySdk = typeof(SentrySdk).GetMembers(BindingFlags.Public | BindingFlags.Static); - Assert.Empty(clientExtensions.Except(sentrySdk.Select(m => m.ToString()))); + var sentrySdk = typeof(SentrySdk).GetMembers(BindingFlags.Public | BindingFlags.Static); + var values = clientExtensions.Except(sentrySdk.Select(m => m.ToString())); + Assert.Empty(values); } [Fact] From 8f509794fb97d2044d3f5b53e87269d22d3b67c2 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Sat, 10 May 2025 10:40:12 +1200 Subject: [PATCH 234/363] fix: Tweak FlushOnDispose_SendsEnvelope (#4175) Attempts to fix #2156: - https://github.com/getsentry/sentry-dotnet/issues/2156 This code makes use of the id returned by `CaptureEvent`. My theory is that might prevent code optimisations resulting in that code running in parallel to the assertion statement. I also changed the code to call `Dispose` explicitly. I don't think that makes any difference tbh but it does make the method that is being tested more explicit. #skip-changelog --- test/Sentry.Tests/HubTests.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/test/Sentry.Tests/HubTests.cs b/test/Sentry.Tests/HubTests.cs index 88d330f950..cc9c510a57 100644 --- a/test/Sentry.Tests/HubTests.cs +++ b/test/Sentry.Tests/HubTests.cs @@ -1753,18 +1753,19 @@ public async Task FlushOnDispose_SendsEnvelope(bool cachingEnabled) options.CacheDirectoryPath = cacheDirectory.Path; } + var hub = new Hub(options); + var id = hub.CaptureEvent(new SentryEvent()); + // Act // Disposing the hub should flush the client and send the envelope. // If caching is enabled, it should flush the cache as well. // Either way, the envelope should be sent. - using (var hub = new Hub(options)) - { - hub.CaptureEvent(new SentryEvent()); - } + hub.Dispose(); // Assert await transport.Received(1) - .SendEnvelopeAsync(Arg.Any(), Arg.Any()); + .SendEnvelopeAsync(Arg.Is(env => (string)env.Header["event_id"] == id.ToString()), + Arg.Any()); } private static Scope GetCurrentScope(Hub hub) => hub.ScopeManager.GetCurrent().Key; From f21135ebc0dab538e64520ac747f739aa1d6976c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 10 May 2025 11:55:05 +1200 Subject: [PATCH 235/363] chore(deps): update CLI to v2.45.0 (#4179) * chore: update scripts/update-cli.ps1 to 2.45.0 --------- Co-authored-by: GitHub Co-authored-by: James Crosswell --- CHANGELOG.md | 14 +++++++------- Directory.Build.props | 2 +- src/Sentry/Sentry.csproj | 14 +++++++------- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7fa2f077c9..458da7cd0a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,20 +2,20 @@ ## Unreleased +### Features + +- New source generator allows Sentry to see true build variables like PublishAot and PublishTrimmed to properly adapt checks in the Sentry SDK ([#4101](https://github.com/getsentry/sentry-dotnet/pull/4101)) + ### Fixes - Redact Authorization headers before sending events to Sentry ([#4164](https://github.com/getsentry/sentry-dotnet/pull/4164)) - Remove Strong Naming from Sentry.Hangfire ([#4099](https://github.com/getsentry/sentry-dotnet/pull/4099)) -### Features - -- New source generator allows Sentry to see true build variables like PublishAot and PublishTrimmed to properly adapt checks in the Sentry SDK ([#4101](https://github.com/getsentry/sentry-dotnet/pull/4101)) - ### Dependencies -- Bump CLI from v2.43.1 to v2.44.0 ([#4169](https://github.com/getsentry/sentry-dotnet/pull/4169)) - - [changelog](https://github.com/getsentry/sentry-cli/blob/master/CHANGELOG.md#2440) - - [diff](https://github.com/getsentry/sentry-cli/compare/2.43.1...2.44.0) +- Bump CLI from v2.43.1 to v2.45.0 ([#4169](https://github.com/getsentry/sentry-dotnet/pull/4169), [#4179](https://github.com/getsentry/sentry-dotnet/pull/4179)) + - [changelog](https://github.com/getsentry/sentry-cli/blob/master/CHANGELOG.md#2450) + - [diff](https://github.com/getsentry/sentry-cli/compare/2.43.1...2.45.0) ## 5.7.0-beta.0 diff --git a/Directory.Build.props b/Directory.Build.props index 2aaba9198e..280b94530a 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -83,7 +83,7 @@ - 2.44.0 + 2.45.0 $(MSBuildThisFileDirectory)tools\sentry-cli\$(SentryCLIVersion)\ diff --git a/src/Sentry/Sentry.csproj b/src/Sentry/Sentry.csproj index 6db7048663..bc6ec06048 100644 --- a/src/Sentry/Sentry.csproj +++ b/src/Sentry/Sentry.csproj @@ -123,13 +123,13 @@ <_OSArchitecture>$([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture) - - - - - - - + + + + + + + From f04930d34313b065528277b8e9cad5cd229eb522 Mon Sep 17 00:00:00 2001 From: Allan Ritchie Date: Mon, 12 May 2025 11:12:53 -0400 Subject: [PATCH 236/363] MAUI Gesture Recognizer Auto-Breadcrumbs (#4124) * Create MauiGestureRecognizerEventsBinder.cs * Wireup * Format code * Cleanup and fix code due stupid bot * I'm not an animal - I can save mem * Format code * Update MauiGestureRecognizerEventsBinder.cs * Create MauiEventsBinderTests.GestureRecognizers.cs * Update MauiEventsBinderTests.GestureRecognizers.cs * Update MauiEventsBinderTests.cs * Update CHANGELOG.md * Format code * WIP * Update CHANGELOG.md * Bring over device tests to improve setup, fix gesture tests * These run every time in the visual runner * If working strictly from slnf - these don't get built and are dependencies for tests * Finally got these to generate * Format code * Update MauiEventsBinderTests.GestureRecognizers.cs * Improve testing setup * Update src/Sentry.Maui/Internal/MauiGestureRecognizerEventsBinder.cs Co-authored-by: James Crosswell * Update MauiGestureRecognizerEventsBinder.cs * Simplified conditional compilation of visual tests * Simplified TFMs in Maui.Device.TestApp --------- Co-authored-by: Sentry Github Bot Co-authored-by: James Crosswell --- .generated.NoMobile.sln | 4 + CHANGELOG.md | 5 + Directory.Build.props | 5 + Sentry.sln | 4 + SentryMobile.slnf | 2 + samples/Sentry.Samples.Maui/MainPage.xaml | 6 +- samples/Sentry.Samples.Maui/MainPage.xaml.cs | 4 + scripts/generate-solution-filters-config.yaml | 2 + .../MauiGestureRecognizerEventsBinder.cs | 194 ++++++++++++++++++ .../SentryMauiAppBuilderExtensions.cs | 1 + test/Directory.Build.props | 4 + .../Sentry.Maui.Device.TestApp.csproj | 27 ++- test/Sentry.Maui.Device.TestApp/Startup.cs | 29 ++- ...piApprovalTests.Run.DotNet8_0.verified.txt | 6 + ...piApprovalTests.Run.DotNet9_0.verified.txt | 6 + ...auiEventsBinderTests.GestureRecognizers.cs | 133 ++++++++++++ .../MauiEventsBinderTests.cs | 3 +- .../Sentry.Maui.Tests.csproj | 4 + 18 files changed, 418 insertions(+), 21 deletions(-) create mode 100644 src/Sentry.Maui/Internal/MauiGestureRecognizerEventsBinder.cs create mode 100644 test/Sentry.Maui.Tests/MauiEventsBinderTests.GestureRecognizers.cs diff --git a/.generated.NoMobile.sln b/.generated.NoMobile.sln index ea50adc690..762d22068b 100644 --- a/.generated.NoMobile.sln +++ b/.generated.NoMobile.sln @@ -104,6 +104,10 @@ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry", "src\Sentry\Sentry.csproj", "{5F253D7F-BF27-46F5-9382-73A66EC247BF}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{6987A1CC-608E-4868-A02C-09D30C8B7B2D}" + ProjectSection(SolutionItems) = preProject + test\Directory.Build.props = test\Directory.Build.props + test\Directory.Build.targets = test\Directory.Build.targets + EndProjectSection EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AndroidTestApp", "test\AndroidTestApp\AndroidTestApp.csproj", "{99E2D1A4-1853-49F9-96B6-59C70FA3DE3A}" EndProject diff --git a/CHANGELOG.md b/CHANGELOG.md index 458da7cd0a..60a1313cf7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,11 @@ - Redact Authorization headers before sending events to Sentry ([#4164](https://github.com/getsentry/sentry-dotnet/pull/4164)) - Remove Strong Naming from Sentry.Hangfire ([#4099](https://github.com/getsentry/sentry-dotnet/pull/4099)) +### Features + +- New source generator allows Sentry to see true build variables like PublishAot and PublishTrimmed to properly adapt checks in the Sentry SDK ([#4101](https://github.com/getsentry/sentry-dotnet/pull/4101)) +- Auto breadcrumbs now include all .NET MAUI gesture recognizer events ([#4124](https://github.com/getsentry/sentry-dotnet/pull/4124)) + ### Dependencies - Bump CLI from v2.43.1 to v2.45.0 ([#4169](https://github.com/getsentry/sentry-dotnet/pull/4169), [#4179](https://github.com/getsentry/sentry-dotnet/pull/4179)) diff --git a/Directory.Build.props b/Directory.Build.props index 280b94530a..beea6f603c 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -106,4 +106,9 @@ + + + $([MSBuild]::GetTargetFrameworkVersion($(TargetFramework))) + true + diff --git a/Sentry.sln b/Sentry.sln index ea50adc690..762d22068b 100644 --- a/Sentry.sln +++ b/Sentry.sln @@ -104,6 +104,10 @@ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry", "src\Sentry\Sentry.csproj", "{5F253D7F-BF27-46F5-9382-73A66EC247BF}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{6987A1CC-608E-4868-A02C-09D30C8B7B2D}" + ProjectSection(SolutionItems) = preProject + test\Directory.Build.props = test\Directory.Build.props + test\Directory.Build.targets = test\Directory.Build.targets + EndProjectSection EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AndroidTestApp", "test\AndroidTestApp\AndroidTestApp.csproj", "{99E2D1A4-1853-49F9-96B6-59C70FA3DE3A}" EndProject diff --git a/SentryMobile.slnf b/SentryMobile.slnf index c38fa573cc..270552e1de 100644 --- a/SentryMobile.slnf +++ b/SentryMobile.slnf @@ -12,11 +12,13 @@ "src\\Sentry.Bindings.Cocoa\\Sentry.Bindings.Cocoa.csproj", "src\\Sentry.Extensions.Logging\\Sentry.Extensions.Logging.csproj", "src\\Sentry.Maui\\Sentry.Maui.csproj", + "src\\Sentry.SourceGenerators\\Sentry.SourceGenerators.csproj", "src\\Sentry\\Sentry.csproj", "test\\Sentry.Android.AssemblyReader.Tests\\Sentry.Android.AssemblyReader.Tests.csproj", "test\\Sentry.Extensions.Logging.Tests\\Sentry.Extensions.Logging.Tests.csproj", "test\\Sentry.Maui.Device.TestApp\\Sentry.Maui.Device.TestApp.csproj", "test\\Sentry.Maui.Tests\\Sentry.Maui.Tests.csproj", + "test\\Sentry.Testing.CrashableApp\\Sentry.Testing.CrashableApp.csproj", "test\\Sentry.Testing\\Sentry.Testing.csproj", "test\\Sentry.Tests\\Sentry.Tests.csproj" ] diff --git a/samples/Sentry.Samples.Maui/MainPage.xaml b/samples/Sentry.Samples.Maui/MainPage.xaml index 276176d175..a8bf13e5ba 100644 --- a/samples/Sentry.Samples.Maui/MainPage.xaml +++ b/samples/Sentry.Samples.Maui/MainPage.xaml @@ -15,7 +15,11 @@ sentry:SessionReplay.Mask="Unmask" SemanticProperties.Description="Cute dot net bot waving hi to you!" HeightRequest="200" - HorizontalOptions="Center" /> + HorizontalOptions="Center"> + + + + +public class MauiGestureRecognizerEventsBinder : IMauiElementEventBinder +{ + private static Action? _addBreadcrumb = null!; + + /// + /// Searches VisualElement for gesture recognizers to bind to + /// + public void Bind(VisualElement element, Action addBreadcrumb) + { + _addBreadcrumb ??= addBreadcrumb; // this is fine... it's the same callback for everyone and it never changes + TryBind(element, true); + } + + + /// + /// Searches VisualElement for gesture recognizers to unbind from + /// + /// + public void UnBind(VisualElement element) + { + _addBreadcrumb = null; + TryBind(element, false); + } + + private static void TryBind(VisualElement element, bool bind) + { + if (element is IGestureRecognizers recognizers) + { + foreach (var recognizer in recognizers.GestureRecognizers) + { + SetHooks(recognizer, bind); + } + } + } + + + private static void SetHooks(IGestureRecognizer recognizer, bool bind) + { + switch (recognizer) + { + case TapGestureRecognizer tap: + tap.Tapped -= OnTapGesture; + + if (bind) + { + tap.Tapped += OnTapGesture; + } + break; + + case SwipeGestureRecognizer swipe: + swipe.Swiped -= OnSwipeGesture; + + if (bind) + { + swipe.Swiped += OnSwipeGesture; + } + break; + + case PinchGestureRecognizer pinch: + pinch.PinchUpdated -= OnPinchGesture; + + if (bind) + { + pinch.PinchUpdated += OnPinchGesture; + } + break; + + case DragGestureRecognizer drag: + drag.DragStarting -= OnDragStartingGesture; + drag.DropCompleted -= OnDropCompletedGesture; + + if (bind) + { + drag.DragStarting += OnDragStartingGesture; + drag.DropCompleted += OnDropCompletedGesture; + } + break; + + case PanGestureRecognizer pan: + pan.PanUpdated -= OnPanGesture; + + if (bind) + { + pan.PanUpdated += OnPanGesture; + } + break; + + case PointerGestureRecognizer pointer: + pointer.PointerEntered -= OnPointerEnteredGesture; + pointer.PointerExited -= OnPointerExitedGesture; + pointer.PointerMoved -= OnPointerMovedGesture; + pointer.PointerPressed -= OnPointerPressedGesture; + pointer.PointerReleased -= OnPointerReleasedGesture; + + if (bind) + { + pointer.PointerEntered += OnPointerEnteredGesture; + pointer.PointerExited += OnPointerExitedGesture; + pointer.PointerMoved += OnPointerMovedGesture; + pointer.PointerPressed += OnPointerPressedGesture; + pointer.PointerReleased += OnPointerReleasedGesture; + } + break; + } + } + + private static void OnPointerReleasedGesture(object? sender, PointerEventArgs e) => _addBreadcrumb?.Invoke(new( + sender, + nameof(PointerGestureRecognizer.PointerReleased), + ToPointerData(e) + )); + + private static void OnPointerPressedGesture(object? sender, PointerEventArgs e) => _addBreadcrumb?.Invoke(new( + sender, + nameof(PointerGestureRecognizer.PointerPressed), + ToPointerData(e) + )); + + private static void OnPointerMovedGesture(object? sender, PointerEventArgs e) => _addBreadcrumb?.Invoke(new( + sender, + nameof(PointerGestureRecognizer.PointerMoved), + ToPointerData(e) + )); + + private static void OnPointerExitedGesture(object? sender, PointerEventArgs e) => _addBreadcrumb?.Invoke(new( + sender, + nameof(PointerGestureRecognizer.PointerExited), + ToPointerData(e) + )); + + private static void OnPointerEnteredGesture(object? sender, PointerEventArgs e) => _addBreadcrumb?.Invoke(new( + sender, + nameof(PointerGestureRecognizer.PointerEntered), + ToPointerData(e) + )); + + private static IEnumerable<(string Key, string Value)> ToPointerData(PointerEventArgs e) => + [ + #if ANDROID + ("MotionEventAction", e.PlatformArgs?.MotionEvent.Action.ToString() ?? string.Empty) + #elif IOS + ("State", e.PlatformArgs?.GestureRecognizer.State.ToString() ?? string.Empty) + #endif + ]; + + private static void OnPanGesture(object? sender, PanUpdatedEventArgs e) => _addBreadcrumb?.Invoke(new( + sender, + nameof(PanGestureRecognizer.PanUpdated), + [ + ("GestureId", e.GestureId.ToString()), + ("StatusType", e.StatusType.ToString()), + ("TotalX", e.TotalX.ToString()), + ("TotalY", e.TotalY.ToString()) + ] + )); + + private static void OnDropCompletedGesture(object? sender, DropCompletedEventArgs e) => _addBreadcrumb?.Invoke(new( + sender, + nameof(DragGestureRecognizer.DropCompleted) + )); + + private static void OnDragStartingGesture(object? sender, DragStartingEventArgs e) => _addBreadcrumb?.Invoke(new( + sender, + nameof(DragGestureRecognizer.DragStarting) + )); + + + private static void OnPinchGesture(object? sender, PinchGestureUpdatedEventArgs e) => _addBreadcrumb?.Invoke(new( + sender, + nameof(PinchGestureRecognizer.PinchUpdated), + [ + ("GestureStatus", e.Status.ToString()), + ("Scale", e.Scale.ToString()), + ("ScaleOrigin", e.ScaleOrigin.ToString()) + ] + )); + + private static void OnSwipeGesture(object? sender, SwipedEventArgs e) => _addBreadcrumb?.Invoke(new( + sender, + nameof(SwipeGestureRecognizer.Swiped), + [("Direction", e.Direction.ToString())] + )); + + private static void OnTapGesture(object? sender, TappedEventArgs e) => _addBreadcrumb?.Invoke(new( + sender, + nameof(TapGestureRecognizer.Tapped), + [("ButtonMask", e.Buttons.ToString())] + )); +} diff --git a/src/Sentry.Maui/SentryMauiAppBuilderExtensions.cs b/src/Sentry.Maui/SentryMauiAppBuilderExtensions.cs index f917cf29ca..c893e38d2f 100644 --- a/src/Sentry.Maui/SentryMauiAppBuilderExtensions.cs +++ b/src/Sentry.Maui/SentryMauiAppBuilderExtensions.cs @@ -57,6 +57,7 @@ public static MauiAppBuilder UseSentry(this MauiAppBuilder builder, services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); services.TryAddSingleton(); diff --git a/test/Directory.Build.props b/test/Directory.Build.props index 4cc80884d9..1a4cb79179 100644 --- a/test/Directory.Build.props +++ b/test/Directory.Build.props @@ -11,6 +11,10 @@ $(NoWarn);SYSLIB0005;SYSLIB0012 $(NoWarn);IDE1006 + + + false + true - 18 + $(TargetFrameworks);net8.0-android;net9.0-android + $(TargetFrameworks);net8.0-ios;net9.0-ios + $(DefineConstants);VISUAL_RUNNER + + + true - Exe true @@ -36,7 +32,10 @@ 13.0 21.0 - + + 18 + true + + + + + diff --git a/test/Sentry.Maui.Device.TestApp/Startup.cs b/test/Sentry.Maui.Device.TestApp/Startup.cs index 99d34b1605..f22f709547 100644 --- a/test/Sentry.Maui.Device.TestApp/Startup.cs +++ b/test/Sentry.Maui.Device.TestApp/Startup.cs @@ -1,3 +1,6 @@ +#if VISUAL_RUNNER +using DeviceRunners.VisualRunners; +#endif using DeviceRunners.XHarness; using Microsoft.Maui.LifecycleEvents; @@ -7,6 +10,15 @@ public static class MauiProgram { public static MauiApp CreateMauiApp() { + var assemblies = new List( + [ + typeof(Sentry.Tests.SentrySdkTests).Assembly, + typeof(Sentry.Extensions.Logging.Tests.LogLevelExtensionsTests).Assembly, + typeof(Sentry.Maui.Tests.SentryMauiOptionsTests).Assembly, +#if ANDROID + typeof(Sentry.Android.AssemblyReader.Tests.AndroidAssemblyReaderTests).Assembly, +#endif + ]); var appBuilder = MauiApp.CreateBuilder() .ConfigureLifecycleEvents(life => { @@ -19,17 +31,18 @@ public static MauiApp CreateMauiApp() }) .UseXHarnessTestRunner(conf => { - conf.AddTestAssemblies([ - typeof(Sentry.Tests.SentrySdkTests).Assembly, - typeof(Sentry.Extensions.Logging.Tests.LogLevelExtensionsTests).Assembly, - typeof(Sentry.Maui.Tests.SentryMauiOptionsTests).Assembly, -#if ANDROID - typeof(Sentry.Android.AssemblyReader.Tests.AndroidAssemblyReaderTests).Assembly, -#endif - ]); + conf.AddTestAssemblies(assemblies); conf.AddXunit(); }); +#if VISUAL_RUNNER + appBuilder.UseVisualTestRunner(conf => + { + conf.AddTestAssemblies(assemblies); + conf.AddXunit(); + }); +#endif + return appBuilder.Build(); } } diff --git a/test/Sentry.Maui.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt b/test/Sentry.Maui.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt index 4a194a0b94..f608570b94 100644 --- a/test/Sentry.Maui.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt +++ b/test/Sentry.Maui.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt @@ -58,6 +58,12 @@ namespace Sentry.Maui.Internal public void Bind(Microsoft.Maui.Controls.VisualElement element, System.Action addBreadcrumb) { } public void UnBind(Microsoft.Maui.Controls.VisualElement element) { } } + public class MauiGestureRecognizerEventsBinder : Sentry.Maui.IMauiElementEventBinder + { + public MauiGestureRecognizerEventsBinder() { } + public void Bind(Microsoft.Maui.Controls.VisualElement element, System.Action addBreadcrumb) { } + public void UnBind(Microsoft.Maui.Controls.VisualElement element) { } + } public class MauiImageButtonEventsBinder : Sentry.Maui.IMauiElementEventBinder { public MauiImageButtonEventsBinder() { } diff --git a/test/Sentry.Maui.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt b/test/Sentry.Maui.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt index 4a194a0b94..f608570b94 100644 --- a/test/Sentry.Maui.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt +++ b/test/Sentry.Maui.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt @@ -58,6 +58,12 @@ namespace Sentry.Maui.Internal public void Bind(Microsoft.Maui.Controls.VisualElement element, System.Action addBreadcrumb) { } public void UnBind(Microsoft.Maui.Controls.VisualElement element) { } } + public class MauiGestureRecognizerEventsBinder : Sentry.Maui.IMauiElementEventBinder + { + public MauiGestureRecognizerEventsBinder() { } + public void Bind(Microsoft.Maui.Controls.VisualElement element, System.Action addBreadcrumb) { } + public void UnBind(Microsoft.Maui.Controls.VisualElement element) { } + } public class MauiImageButtonEventsBinder : Sentry.Maui.IMauiElementEventBinder { public MauiImageButtonEventsBinder() { } diff --git a/test/Sentry.Maui.Tests/MauiEventsBinderTests.GestureRecognizers.cs b/test/Sentry.Maui.Tests/MauiEventsBinderTests.GestureRecognizers.cs new file mode 100644 index 0000000000..f9d8cefe45 --- /dev/null +++ b/test/Sentry.Maui.Tests/MauiEventsBinderTests.GestureRecognizers.cs @@ -0,0 +1,133 @@ +using Sentry.Maui.Internal; + +namespace Sentry.Maui.Tests; + +public partial class MauiEventsBinderTests +{ + [SkippableFact] + public void TapGestureRecognizer_LifecycleEvents_AddsBreadcrumb() + { +#if !VISUAL_RUNNER + Skip.If(true, "Visual runner disabled"); +#endif + var gesture = new TapGestureRecognizer(); + TestGestureRecognizer( + gesture, + nameof(TapGestureRecognizer.Tapped), + new TappedEventArgs(gesture) + ); + } + + [SkippableFact] + public void SwipeGestureRecognizer_LifecycleEvents_AddsBreadcrumb() + { +#if !VISUAL_RUNNER + Skip.If(true, "Visual runner disabled"); +#endif + var gesture = new SwipeGestureRecognizer(); + TestGestureRecognizer( + gesture, + nameof(SwipeGestureRecognizer.Swiped), + new SwipedEventArgs(gesture, SwipeDirection.Down) + ); + } + + [SkippableFact] + public void PinchGestureRecognizer_LifecycleEvents_AddsBreadcrumb() + { +#if !VISUAL_RUNNER + Skip.If(true, "Visual runner disabled"); +#endif + TestGestureRecognizer( + new PinchGestureRecognizer(), + nameof(PinchGestureRecognizer.PinchUpdated), + new PinchGestureUpdatedEventArgs(GestureStatus.Completed, 0, Point.Zero) + ); + } + + [SkippableFact] + public void DragGestureRecognizer_LifecycleEvents_AddsBreadcrumb_DragStarting() + { +#if !VISUAL_RUNNER + Skip.If(true, "Visual runner disabled"); +#endif + TestGestureRecognizer( + new DragGestureRecognizer(), + nameof(DragGestureRecognizer.DragStarting), + new DragStartingEventArgs() + ); + } + + [SkippableFact] + public void DragGestureRecognizer_LifecycleEvents_AddsBreadcrumb_DropFinished() + { +#if !VISUAL_RUNNER + Skip.If(true, "Visual runner disabled"); +#endif + TestGestureRecognizer( + new DragGestureRecognizer(), + nameof(DragGestureRecognizer.DropCompleted), + new DropCompletedEventArgs() + ); + } + + [SkippableFact] + public void PanGestureRecognizer_LifecycleEvents_AddsBreadcrumb() + { +#if !VISUAL_RUNNER + Skip.If(true, "Visual runner disabled"); +#endif + TestGestureRecognizer( + new PanGestureRecognizer(), + nameof(PanGestureRecognizer.PanUpdated), + new PanUpdatedEventArgs(GestureStatus.Completed, 1, 0, 0) + ); + } + + [SkippableTheory] + [InlineData(nameof(PointerGestureRecognizer.PointerEntered))] + [InlineData(nameof(PointerGestureRecognizer.PointerExited))] + [InlineData(nameof(PointerGestureRecognizer.PointerMoved))] + [InlineData(nameof(PointerGestureRecognizer.PointerPressed))] + [InlineData(nameof(PointerGestureRecognizer.PointerReleased))] + public void PointerGestureRecognizer_LifecycleEvents_AddsBreadcrumb(string eventName) + { +#if !VISUAL_RUNNER + Skip.If(true, "Visual runner disabled"); +#endif + TestGestureRecognizer( + new PointerGestureRecognizer(), + eventName, + new PointerEventArgs() + ); + } + + private void TestGestureRecognizer(GestureRecognizer gesture, string eventName, object eventArgs) + { + var image = new Image(); + image.GestureRecognizers.Add(gesture); + var args = new ElementEventArgs(image); + + try + { + _fixture.Binder.OnApplicationOnDescendantAdded(null, args); + + // Act + gesture.RaiseEvent(eventName, eventArgs); + + // Assert + var crumb = Assert.Single(_fixture.Scope.Breadcrumbs); + Assert.Equal($"{gesture.GetType().Name}.{eventName}", crumb.Message); + Assert.Equal(BreadcrumbLevel.Info, crumb.Level); + Assert.Equal(MauiEventsBinder.UserType, crumb.Type); + Assert.Equal(MauiEventsBinder.UserActionCategory, crumb.Category); + _fixture.Binder.OnApplicationOnDescendantRemoved(null, args); + } + catch + { + _fixture.Binder.OnApplicationOnDescendantRemoved(null, args); + throw; + } + // GC.WaitForPendingFinalizers(); + } +} diff --git a/test/Sentry.Maui.Tests/MauiEventsBinderTests.cs b/test/Sentry.Maui.Tests/MauiEventsBinderTests.cs index 484b0d445d..8871b74fe9 100644 --- a/test/Sentry.Maui.Tests/MauiEventsBinderTests.cs +++ b/test/Sentry.Maui.Tests/MauiEventsBinderTests.cs @@ -28,7 +28,8 @@ public Fixture() options, [ new MauiButtonEventsBinder(), - new MauiImageButtonEventsBinder() + new MauiImageButtonEventsBinder(), + new MauiGestureRecognizerEventsBinder() ] ); } diff --git a/test/Sentry.Maui.Tests/Sentry.Maui.Tests.csproj b/test/Sentry.Maui.Tests/Sentry.Maui.Tests.csproj index 6cec86a02a..749493ccf1 100644 --- a/test/Sentry.Maui.Tests/Sentry.Maui.Tests.csproj +++ b/test/Sentry.Maui.Tests/Sentry.Maui.Tests.csproj @@ -8,6 +8,10 @@ true + + $(DefineConstants);VISUAL_RUNNER + + From 483866061cc9c5a4fccf6bfc7f7fa0c8ec7013ca Mon Sep 17 00:00:00 2001 From: Bruno Garcia Date: Mon, 12 May 2025 18:57:13 -0400 Subject: [PATCH 237/363] fix CHANGELOG dupe entry (#4184) --- CHANGELOG.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 60a1313cf7..9a80b50cf6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,10 +2,6 @@ ## Unreleased -### Features - -- New source generator allows Sentry to see true build variables like PublishAot and PublishTrimmed to properly adapt checks in the Sentry SDK ([#4101](https://github.com/getsentry/sentry-dotnet/pull/4101)) - ### Fixes - Redact Authorization headers before sending events to Sentry ([#4164](https://github.com/getsentry/sentry-dotnet/pull/4164)) From 162f0d4d93c79b68142838f170518bd690c50e3e Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Tue, 13 May 2025 14:10:42 +1200 Subject: [PATCH 238/363] fix: Update request size thresholds to match other SDKs (#4177) Changed the threshold for small request bodies from 1k to 4k to align with other SDKs (per [the docs](https://docs.sentry.io/platforms/dotnet/guides/aspnetcore/configuration/options/#max-request-body-size)). --- CHANGELOG.md | 1 + .../ProtobufRequestExtractionDispatcher.cs | 2 +- src/Sentry/Extensibility/RequestBodyExtractionDispatcher.cs | 2 +- .../Extensibility/RequestBodyExtractionDispatcherTests.cs | 6 +++--- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a80b50cf6..156cc1d0c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - Redact Authorization headers before sending events to Sentry ([#4164](https://github.com/getsentry/sentry-dotnet/pull/4164)) - Remove Strong Naming from Sentry.Hangfire ([#4099](https://github.com/getsentry/sentry-dotnet/pull/4099)) +- Increase `RequestSize.Small` threshold from 1 kB to 4 kB to match other SDKs ([#4177](https://github.com/getsentry/sentry-dotnet/pull/4177)) ### Features diff --git a/src/Sentry.AspNetCore.Grpc/ProtobufRequestExtractionDispatcher.cs b/src/Sentry.AspNetCore.Grpc/ProtobufRequestExtractionDispatcher.cs index 618d467b88..39aa1c678f 100644 --- a/src/Sentry.AspNetCore.Grpc/ProtobufRequestExtractionDispatcher.cs +++ b/src/Sentry.AspNetCore.Grpc/ProtobufRequestExtractionDispatcher.cs @@ -46,7 +46,7 @@ public ProtobufRequestExtractionDispatcher(IEnumerable ext switch (size) { - case RequestSize.Small when request.ContentLength < 1_000: + case RequestSize.Small when request.ContentLength < 4_000: case RequestSize.Medium when request.ContentLength < 10_000: case RequestSize.Always: _options.LogDebug("Attempting to read request body of size: {0}, configured max: {1}.", diff --git a/test/Sentry.Tests/Extensibility/RequestBodyExtractionDispatcherTests.cs b/test/Sentry.Tests/Extensibility/RequestBodyExtractionDispatcherTests.cs index 84cded0f00..49655636a1 100644 --- a/test/Sentry.Tests/Extensibility/RequestBodyExtractionDispatcherTests.cs +++ b/test/Sentry.Tests/Extensibility/RequestBodyExtractionDispatcherTests.cs @@ -51,10 +51,10 @@ public void ExtractPayload_ExtractorNull_ReturnsNull() [Theory] [InlineData(RequestSize.None, 1, false)] - [InlineData(RequestSize.Small, 999, true)] - [InlineData(RequestSize.Small, 10_000, false)] + [InlineData(RequestSize.Small, 3_999, true)] + [InlineData(RequestSize.Small, 4_000, false)] [InlineData(RequestSize.Medium, 9999, true)] - [InlineData(RequestSize.Medium, 100_000, false)] + [InlineData(RequestSize.Medium, 10_000, false)] [InlineData(RequestSize.Always, int.MaxValue, true)] // 2 GB event? No problem... public void ExtractPayload_RequestSizeSmall_ContentLength(RequestSize requestSize, int contentLength, bool readBody) { From 2e1fff191a83a50dd113de845a4689a2d40fd654 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Tue, 13 May 2025 23:15:27 +1200 Subject: [PATCH 239/363] feat: Associate replays with errors and traces on Android (#4133) Part of #2136 Associate Errors and Traces with the active Session Replay (if one exists) on Android. --- CHANGELOG.md | 15 +-- src/Sentry.AspNet/HttpContextExtensions.cs | 3 +- .../SentryTracingMiddleware.cs | 2 +- .../SentrySpanProcessor.cs | 8 +- src/Sentry/DynamicSamplingContext.cs | 62 ++++++--- src/Sentry/Internal/Hub.cs | 23 ++-- src/Sentry/Internal/ReplaySession.cs | 33 +++++ src/Sentry/SentryPropagationContext.cs | 9 +- .../SentrySpanProcessorTests.cs | 7 +- .../DynamicSamplingContextTests.cs | 118 +++++++++++++----- .../EventProcessorTests.verify.cs | 2 + test/Sentry.Tests/HubTests.cs | 108 +++++++++++++--- .../Protocol/SentryTransactionTests.cs | 27 ++++ .../SentryPropagationContextTests.cs | 108 +++++++++++++--- 14 files changed, 411 insertions(+), 114 deletions(-) create mode 100644 src/Sentry/Internal/ReplaySession.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 156cc1d0c4..508cd01edb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,17 +2,18 @@ ## Unreleased +### Features + +- New source generator allows Sentry to see true build variables like PublishAot and PublishTrimmed to properly adapt checks in the Sentry SDK ([#4101](https://github.com/getsentry/sentry-dotnet/pull/4101)) +- Auto breadcrumbs now include all .NET MAUI gesture recognizer events ([#4124](https://github.com/getsentry/sentry-dotnet/pull/4124)) +- Associate replays with errors and traces on Android ([#4133](https://github.com/getsentry/sentry-dotnet/pull/4133)) + ### Fixes - Redact Authorization headers before sending events to Sentry ([#4164](https://github.com/getsentry/sentry-dotnet/pull/4164)) - Remove Strong Naming from Sentry.Hangfire ([#4099](https://github.com/getsentry/sentry-dotnet/pull/4099)) - Increase `RequestSize.Small` threshold from 1 kB to 4 kB to match other SDKs ([#4177](https://github.com/getsentry/sentry-dotnet/pull/4177)) -### Features - -- New source generator allows Sentry to see true build variables like PublishAot and PublishTrimmed to properly adapt checks in the Sentry SDK ([#4101](https://github.com/getsentry/sentry-dotnet/pull/4101)) -- Auto breadcrumbs now include all .NET MAUI gesture recognizer events ([#4124](https://github.com/getsentry/sentry-dotnet/pull/4124)) - ### Dependencies - Bump CLI from v2.43.1 to v2.45.0 ([#4169](https://github.com/getsentry/sentry-dotnet/pull/4169), [#4179](https://github.com/getsentry/sentry-dotnet/pull/4179)) @@ -23,7 +24,7 @@ ### Features -- When setting a transaction on the scope, the SDK will attempt to sync the transaction's trace context with the SDK on the native layer. Finishing a transaction will now also start a new trace ([#4153](https://github.com/getsentry/sentry-dotnet/pull/4153)) +- When setting a transaction on the scope, the SDK will attempt to sync the transaction's trace context with the SDK on the native layer. Finishing a transaction will now also start a new trace ([#4153](https://github.com/getsentry/sentry-dotnet/pull/4153)) - Added `CaptureFeedback` overload with `configureScope` parameter ([#4073](https://github.com/getsentry/sentry-dotnet/pull/4073)) - Custom SessionReplay masks in MAUI Android apps ([#4121](https://github.com/getsentry/sentry-dotnet/pull/4121)) @@ -45,7 +46,7 @@ ### Features - Option to disable the SentryNative integration ([#4107](https://github.com/getsentry/sentry-dotnet/pull/4107), [#4134](https://github.com/getsentry/sentry-dotnet/pull/4134)) - - To disable it, add this msbuild property: `false` + - To disable it, add this msbuild property: `false` - Reintroduced experimental support for Session Replay on Android ([#4097](https://github.com/getsentry/sentry-dotnet/pull/4097)) - If an incoming HTTP request has the `traceparent` header, it is now parsed and interpreted like the `sentry-trace` header. Outgoing requests now contain the `traceparent` header to facilitate integration with servesr that only support the [W3C Trace Context](https://www.w3.org/TR/trace-context/). ([#4084](https://github.com/getsentry/sentry-dotnet/pull/4084)) diff --git a/src/Sentry.AspNet/HttpContextExtensions.cs b/src/Sentry.AspNet/HttpContextExtensions.cs index 220413140f..b652062969 100644 --- a/src/Sentry.AspNet/HttpContextExtensions.cs +++ b/src/Sentry.AspNet/HttpContextExtensions.cs @@ -1,4 +1,5 @@ using Sentry.Extensibility; +using Sentry.Internal; using Sentry.Protocol; namespace Sentry.AspNet; @@ -125,7 +126,7 @@ public static ITransactionTracer StartSentryTransaction(this HttpContext httpCon ["__HttpContext"] = httpContext, }; - // Set the Dynamic Sampling Context from the baggage header, if it exists. + // Set the Dynamic Sampling Context from the baggage header, if it exists var dynamicSamplingContext = baggageHeader?.CreateDynamicSamplingContext(); if (traceHeader is not null && baggageHeader is null) diff --git a/src/Sentry.AspNetCore/SentryTracingMiddleware.cs b/src/Sentry.AspNetCore/SentryTracingMiddleware.cs index 60c43b2587..d878e1c00a 100644 --- a/src/Sentry.AspNetCore/SentryTracingMiddleware.cs +++ b/src/Sentry.AspNetCore/SentryTracingMiddleware.cs @@ -2,6 +2,7 @@ using Microsoft.Extensions.Options; using Sentry.AspNetCore.Extensions; using Sentry.Extensibility; +using Sentry.Internal; using Sentry.Internal.OpenTelemetry; namespace Sentry.AspNetCore; @@ -64,7 +65,6 @@ public SentryTracingMiddleware( ? traceHeaderObject as SentryTraceHeader : null; var baggageHeader = context.Items.TryGetValue(SentryMiddleware.BaggageHeaderItemKey, out var baggageHeaderObject) ? baggageHeaderObject as BaggageHeader : null; - var dynamicSamplingContext = baggageHeader?.CreateDynamicSamplingContext(); if (traceHeader is not null && baggageHeader is null) diff --git a/src/Sentry.OpenTelemetry/SentrySpanProcessor.cs b/src/Sentry.OpenTelemetry/SentrySpanProcessor.cs index 39230143a8..b7942bb21b 100644 --- a/src/Sentry.OpenTelemetry/SentrySpanProcessor.cs +++ b/src/Sentry.OpenTelemetry/SentrySpanProcessor.cs @@ -13,6 +13,7 @@ public class SentrySpanProcessor : BaseProcessor { private readonly IHub _hub; internal readonly IEnumerable _enrichers; + private readonly IReplaySession _replaySession; internal const string OpenTelemetryOrigin = "auto.otel"; // ReSharper disable once MemberCanBePrivate.Global - Used by tests @@ -38,7 +39,7 @@ public SentrySpanProcessor(IHub hub) : this(hub, null) { } - internal SentrySpanProcessor(IHub hub, IEnumerable? enrichers) + internal SentrySpanProcessor(IHub hub, IEnumerable? enrichers, IReplaySession? replaySession = null) { _hub = hub; _realHub = new Lazy(() => @@ -57,7 +58,8 @@ internal SentrySpanProcessor(IHub hub, IEnumerable? enri "You should use the TracerProviderBuilderExtensions to configure Sentry with OpenTelemetry"); } - _enrichers = enrichers ?? Enumerable.Empty(); + _enrichers = enrichers ?? []; + _replaySession = replaySession ?? ReplaySession.Instance; _options = hub.GetSentryOptions(); if (_options is null) @@ -158,7 +160,7 @@ private void CreateRootSpan(Activity data) }; var baggageHeader = data.Baggage.AsBaggageHeader(); - var dynamicSamplingContext = baggageHeader.CreateDynamicSamplingContext(); + var dynamicSamplingContext = baggageHeader.CreateDynamicSamplingContext(_replaySession); var transaction = (TransactionTracer)_hub.StartTransaction( transactionContext, new Dictionary(), dynamicSamplingContext ); diff --git a/src/Sentry/DynamicSamplingContext.cs b/src/Sentry/DynamicSamplingContext.cs index e85ec88b84..cda413c7bc 100644 --- a/src/Sentry/DynamicSamplingContext.cs +++ b/src/Sentry/DynamicSamplingContext.cs @@ -20,15 +20,15 @@ internal class DynamicSamplingContext /// public static readonly DynamicSamplingContext Empty = new(new Dictionary().AsReadOnly()); - private DynamicSamplingContext( - SentryId traceId, + private DynamicSamplingContext(SentryId traceId, string publicKey, bool? sampled, double? sampleRate = null, double? sampleRand = null, string? release = null, string? environment = null, - string? transactionName = null) + string? transactionName = null, + IReplaySession? replaySession = null) { // Validate and set required values if (traceId == SentryId.Empty) @@ -51,7 +51,7 @@ private DynamicSamplingContext( throw new ArgumentOutOfRangeException(nameof(sampleRand), "Arg invalid if < 0.0 or >= 1.0"); } - var items = new Dictionary(capacity: 8) + var items = new Dictionary(capacity: 9) { ["trace_id"] = traceId.ToString(), ["public_key"] = publicKey, @@ -88,12 +88,29 @@ private DynamicSamplingContext( items.Add("transaction", transactionName); } + if (replaySession?.ActiveReplayId is { } replayId && replayId != SentryId.Empty) + { + items.Add("replay_id", replayId.ToString()); + } + Items = items; } public BaggageHeader ToBaggageHeader() => BaggageHeader.Create(Items, useSentryPrefix: true); - public static DynamicSamplingContext? CreateFromBaggageHeader(BaggageHeader baggage) + public DynamicSamplingContext WithReplayId(IReplaySession? replaySession) + { + if (replaySession?.ActiveReplayId is not { } replayId || replayId == SentryId.Empty) + { + return this; + } + + var items = Items.ToDictionary(kvp => kvp.Key, kvp => kvp.Value); + items["replay_id"] = replayId.ToString(); + return new DynamicSamplingContext(items); + } + + public static DynamicSamplingContext? CreateFromBaggageHeader(BaggageHeader baggage, IReplaySession? replaySession) { var items = baggage.GetSentryMembers(); @@ -144,10 +161,19 @@ private DynamicSamplingContext( } items.Add("sample_rand", rand.ToString("N4", CultureInfo.InvariantCulture)); } + + if (replaySession?.ActiveReplayId is { } replayId) + { + // Any upstream replay_id will be propagated only if the current process hasn't started it's own replay session. + // Otherwise we have to overwrite this as it's the only way to communicate the replayId to Sentry Relay. + // In Mobile apps this should never be a problem. + items["replay_id"] = replayId.ToString(); + } + return new DynamicSamplingContext(items); } - public static DynamicSamplingContext CreateFromTransaction(TransactionTracer transaction, SentryOptions options) + public static DynamicSamplingContext CreateFromTransaction(TransactionTracer transaction, SentryOptions options, IReplaySession? replaySession) { // These should already be set on the transaction. var publicKey = options.ParsedDsn.PublicKey; @@ -161,18 +187,18 @@ public static DynamicSamplingContext CreateFromTransaction(TransactionTracer tra var release = options.SettingLocator.GetRelease(); var environment = options.SettingLocator.GetEnvironment(); - return new DynamicSamplingContext( - traceId, + return new DynamicSamplingContext(traceId, publicKey, sampled, sampleRate, sampleRand, release, environment, - transactionName); + transactionName, + replaySession); } - public static DynamicSamplingContext CreateFromPropagationContext(SentryPropagationContext propagationContext, SentryOptions options) + public static DynamicSamplingContext CreateFromPropagationContext(SentryPropagationContext propagationContext, SentryOptions options, IReplaySession? replaySession) { var traceId = propagationContext.TraceId; var publicKey = options.ParsedDsn.PublicKey; @@ -184,18 +210,20 @@ public static DynamicSamplingContext CreateFromPropagationContext(SentryPropagat publicKey, null, release: release, - environment: environment); + environment: environment, + replaySession: replaySession + ); } } internal static class DynamicSamplingContextExtensions { - public static DynamicSamplingContext? CreateDynamicSamplingContext(this BaggageHeader baggage) - => DynamicSamplingContext.CreateFromBaggageHeader(baggage); + public static DynamicSamplingContext? CreateDynamicSamplingContext(this BaggageHeader baggage, IReplaySession? replaySession = null) + => DynamicSamplingContext.CreateFromBaggageHeader(baggage, replaySession); - public static DynamicSamplingContext CreateDynamicSamplingContext(this TransactionTracer transaction, SentryOptions options) - => DynamicSamplingContext.CreateFromTransaction(transaction, options); + public static DynamicSamplingContext CreateDynamicSamplingContext(this TransactionTracer transaction, SentryOptions options, IReplaySession? replaySession) + => DynamicSamplingContext.CreateFromTransaction(transaction, options, replaySession); - public static DynamicSamplingContext CreateDynamicSamplingContext(this SentryPropagationContext propagationContext, SentryOptions options) - => DynamicSamplingContext.CreateFromPropagationContext(propagationContext, options); + public static DynamicSamplingContext CreateDynamicSamplingContext(this SentryPropagationContext propagationContext, SentryOptions options, IReplaySession? replaySession) + => DynamicSamplingContext.CreateFromPropagationContext(propagationContext, options, replaySession); } diff --git a/src/Sentry/Internal/Hub.cs b/src/Sentry/Internal/Hub.cs index cc9cbd3ef3..ce896d2600 100644 --- a/src/Sentry/Internal/Hub.cs +++ b/src/Sentry/Internal/Hub.cs @@ -13,6 +13,7 @@ internal class Hub : IHub, IDisposable private readonly ISessionManager _sessionManager; private readonly SentryOptions _options; private readonly RandomValuesFactory _randomValuesFactory; + private readonly IReplaySession _replaySession; #if MEMORY_DUMP_SUPPORTED private readonly MemoryMonitor? _memoryMonitor; @@ -39,7 +40,8 @@ internal Hub( ISessionManager? sessionManager = null, ISystemClock? clock = null, IInternalScopeManager? scopeManager = null, - RandomValuesFactory? randomValuesFactory = null) + RandomValuesFactory? randomValuesFactory = null, + IReplaySession? replaySession = null) { if (string.IsNullOrWhiteSpace(options.Dsn)) { @@ -55,7 +57,7 @@ internal Hub( _sessionManager = sessionManager ?? new GlobalSessionManager(options); _clock = clock ?? SystemClock.Clock; client ??= new SentryClient(options, randomValuesFactory: _randomValuesFactory, sessionManager: _sessionManager); - + _replaySession = replaySession ?? ReplaySession.Instance; ScopeManager = scopeManager ?? new SentryScopeManager(options, client); if (!options.IsGlobalModeEnabled) @@ -178,10 +180,10 @@ _options.TransactionProfilerFactory is { } profilerFactory && } } - // Use the provided DSC, or create one based on this transaction. + // Use the provided DSC (adding the active replayId if necessary), or create one based on this transaction. // DSC creation must be done AFTER the sampling decision has been made. - transaction.DynamicSamplingContext = - dynamicSamplingContext ?? transaction.CreateDynamicSamplingContext(_options); + transaction.DynamicSamplingContext = dynamicSamplingContext?.WithReplayId(_replaySession) + ?? transaction.CreateDynamicSamplingContext(_options, _replaySession); // A sampled out transaction still appears fully functional to the user // but will be dropped by the client and won't reach Sentry's servers. @@ -224,7 +226,7 @@ public BaggageHeader GetBaggage() } var propagationContext = CurrentScope.PropagationContext; - return propagationContext.GetOrCreateDynamicSamplingContext(_options).ToBaggageHeader(); + return propagationContext.GetOrCreateDynamicSamplingContext(_options, _replaySession).ToBaggageHeader(); } public TransactionContext ContinueTrace( @@ -254,7 +256,7 @@ public TransactionContext ContinueTrace( string? name = null, string? operation = null) { - var propagationContext = SentryPropagationContext.CreateFromHeaders(_options.DiagnosticLogger, traceHeader, baggageHeader); + var propagationContext = SentryPropagationContext.CreateFromHeaders(_options.DiagnosticLogger, traceHeader, baggageHeader, _replaySession); ConfigureScope(scope => scope.SetPropagationContext(propagationContext)); return new TransactionContext( @@ -382,7 +384,7 @@ private void ApplyTraceContextToEvent(SentryEvent evt, SentryPropagationContext evt.Contexts.Trace.TraceId = propagationContext.TraceId; evt.Contexts.Trace.SpanId = propagationContext.SpanId; evt.Contexts.Trace.ParentSpanId = propagationContext.ParentSpanId; - evt.DynamicSamplingContext = propagationContext.GetOrCreateDynamicSamplingContext(_options); + evt.DynamicSamplingContext = propagationContext.GetOrCreateDynamicSamplingContext(_options, _replaySession); } public bool CaptureEnvelope(Envelope envelope) => CurrentClient.CaptureEnvelope(envelope); @@ -473,10 +475,7 @@ private SentryId CaptureEvent(SentryEvent evt, SentryHint? hint, Scope scope) var span = GetLinkedSpan(evt) ?? scope.Span; if (span is not null) { - if (span.IsSampled is not false) - { - ApplyTraceContextToEvent(evt, span); - } + ApplyTraceContextToEvent(evt, span); } else { diff --git a/src/Sentry/Internal/ReplaySession.cs b/src/Sentry/Internal/ReplaySession.cs new file mode 100644 index 0000000000..30f151c894 --- /dev/null +++ b/src/Sentry/Internal/ReplaySession.cs @@ -0,0 +1,33 @@ +#if __ANDROID__ +using Sentry.Android.Extensions; +#endif + +namespace Sentry.Internal; + +internal interface IReplaySession +{ + public SentryId? ActiveReplayId { get; } +} + +internal class ReplaySession : IReplaySession +{ + public static readonly IReplaySession Instance = new ReplaySession(); + + private ReplaySession() + { + } + + public SentryId? ActiveReplayId + { + get + { +#if __ANDROID__ + // Check to see if a Replay ID is available + var replayId = JavaSdk.ScopesAdapter.Instance?.Options?.ReplayController?.ReplayId?.ToSentryId(); + return (replayId is { } id && id != SentryId.Empty) ? id : null; +#else + return null; +#endif + } + } +} diff --git a/src/Sentry/SentryPropagationContext.cs b/src/Sentry/SentryPropagationContext.cs index 19e9bd30d3..24f9550638 100644 --- a/src/Sentry/SentryPropagationContext.cs +++ b/src/Sentry/SentryPropagationContext.cs @@ -1,4 +1,5 @@ using Sentry.Extensibility; +using Sentry.Internal; namespace Sentry; @@ -10,12 +11,12 @@ internal class SentryPropagationContext internal DynamicSamplingContext? _dynamicSamplingContext; - public DynamicSamplingContext GetOrCreateDynamicSamplingContext(SentryOptions options) + public DynamicSamplingContext GetOrCreateDynamicSamplingContext(SentryOptions options, IReplaySession replaySession) { if (_dynamicSamplingContext is null) { options.LogDebug("Creating the Dynamic Sampling Context from the Propagation Context"); - _dynamicSamplingContext = this.CreateDynamicSamplingContext(options); + _dynamicSamplingContext = this.CreateDynamicSamplingContext(options, replaySession); } return _dynamicSamplingContext; @@ -47,7 +48,7 @@ public SentryPropagationContext(SentryPropagationContext? other) _dynamicSamplingContext = other?._dynamicSamplingContext; } - public static SentryPropagationContext CreateFromHeaders(IDiagnosticLogger? logger, SentryTraceHeader? traceHeader, BaggageHeader? baggageHeader) + public static SentryPropagationContext CreateFromHeaders(IDiagnosticLogger? logger, SentryTraceHeader? traceHeader, BaggageHeader? baggageHeader, IReplaySession replaySession) { logger?.LogDebug("Creating a propagation context from headers."); @@ -57,7 +58,7 @@ public static SentryPropagationContext CreateFromHeaders(IDiagnosticLogger? logg return new SentryPropagationContext(); } - var dynamicSamplingContext = baggageHeader?.CreateDynamicSamplingContext(); + var dynamicSamplingContext = baggageHeader?.CreateDynamicSamplingContext(replaySession); return new SentryPropagationContext(traceHeader.TraceId, traceHeader.SpanId, dynamicSamplingContext); } } diff --git a/test/Sentry.OpenTelemetry.Tests/SentrySpanProcessorTests.cs b/test/Sentry.OpenTelemetry.Tests/SentrySpanProcessorTests.cs index 1b199c9fc4..67a13d7da0 100644 --- a/test/Sentry.OpenTelemetry.Tests/SentrySpanProcessorTests.cs +++ b/test/Sentry.OpenTelemetry.Tests/SentrySpanProcessorTests.cs @@ -18,6 +18,8 @@ private class Fixture public List Enrichers { get; set; } = new(); + private IReplaySession ReplaySession { get; } = Substitute.For(); + public Fixture() { Options = new SentryOptions @@ -32,11 +34,11 @@ public Fixture() public Hub Hub { get; private set; } - public Hub GetHub() => Hub ??= new Hub(Options, Client, SessionManager, Clock, ScopeManager); + private Hub GetHub() => Hub ??= new Hub(Options, Client, SessionManager, Clock, ScopeManager, replaySession: ReplaySession); public SentrySpanProcessor GetSut(IHub hub = null) { - return new SentrySpanProcessor(hub ?? GetHub(), Enrichers); + return new SentrySpanProcessor(hub ?? GetHub(), Enrichers, ReplaySession); } } @@ -287,7 +289,6 @@ public void OnStart_WithoutParentSpanId_StartsNewTransaction() transaction.Description.Should().Be(data.DisplayName); transaction.Status.Should().BeNull(); transaction.StartTimestamp.Should().Be(data.StartTimeUtc); - _fixture.ScopeManager.Received(1).ConfigureScope(Arg.Any>()); } } diff --git a/test/Sentry.Tests/DynamicSamplingContextTests.cs b/test/Sentry.Tests/DynamicSamplingContextTests.cs index 374be0c371..a3fdb6a8ff 100644 --- a/test/Sentry.Tests/DynamicSamplingContextTests.cs +++ b/test/Sentry.Tests/DynamicSamplingContextTests.cs @@ -1,7 +1,27 @@ +using Sentry.Tests.Internals; + namespace Sentry.Tests; public class DynamicSamplingContextTests { + private class Fixture + { + public SentryId ActiveReplayId { get; } = SentryId.Create(); + public IReplaySession InactiveReplaySession { get; } + public IReplaySession ActiveReplaySession { get; } + + public Fixture() + { + ActiveReplaySession = Substitute.For(); + ActiveReplaySession.ActiveReplayId.Returns(ActiveReplayId); + + InactiveReplaySession = Substitute.For(); + InactiveReplaySession.ActiveReplayId.Returns((SentryId?)null); + } + } + + private Fixture _fixture = new(); + [Fact] public void EmptyContext() { @@ -19,7 +39,7 @@ public void CreateFromBaggage_TraceId_Missing() {"sentry-sample_rate", "1.0"} }); - var dsc = baggage.CreateDynamicSamplingContext(); + var dsc = baggage.CreateDynamicSamplingContext(_fixture.InactiveReplaySession); Assert.Null(dsc); } @@ -34,7 +54,7 @@ public void CreateFromBaggage_TraceId_EmptyGuid() {"sentry-sample_rate", "1.0"} }); - var dsc = baggage.CreateDynamicSamplingContext(); + var dsc = baggage.CreateDynamicSamplingContext(_fixture.InactiveReplaySession); Assert.Null(dsc); } @@ -49,7 +69,7 @@ public void CreateFromBaggage_TraceId_Invalid() {"sentry-sample_rate", "1.0"} }); - var dsc = baggage.CreateDynamicSamplingContext(); + var dsc = baggage.CreateDynamicSamplingContext(_fixture.InactiveReplaySession); Assert.Null(dsc); } @@ -63,7 +83,7 @@ public void CreateFromBaggage_PublicKey_Missing() {"sentry-sample_rate", "1.0"} }); - var dsc = baggage.CreateDynamicSamplingContext(); + var dsc = baggage.CreateDynamicSamplingContext(_fixture.InactiveReplaySession); Assert.Null(dsc); } @@ -78,7 +98,7 @@ public void CreateFromBaggage_PublicKey_Blank() {"sentry-sample_rate", "1.0"} }); - var dsc = baggage.CreateDynamicSamplingContext(); + var dsc = baggage.CreateDynamicSamplingContext(_fixture.InactiveReplaySession); Assert.Null(dsc); } @@ -92,7 +112,7 @@ public void CreateFromBaggage_SampleRate_Missing() {"sentry-public_key", "d4d82fc1c2c4032a83f3a29aa3a3aff"} }); - var dsc = baggage.CreateDynamicSamplingContext(); + var dsc = baggage.CreateDynamicSamplingContext(_fixture.InactiveReplaySession); Assert.Null(dsc); } @@ -107,7 +127,7 @@ public void CreateFromBaggage_SampleRate_Invalid() {"sentry-sample_rate", "not-a-number"} }); - var dsc = baggage.CreateDynamicSamplingContext(); + var dsc = baggage.CreateDynamicSamplingContext(_fixture.InactiveReplaySession); Assert.Null(dsc); } @@ -122,7 +142,7 @@ public void CreateFromBaggage_SampleRate_TooLow() {"sentry-sample_rate", "-0.1"} }); - var dsc = baggage.CreateDynamicSamplingContext(); + var dsc = baggage.CreateDynamicSamplingContext(_fixture.InactiveReplaySession); Assert.Null(dsc); } @@ -137,7 +157,7 @@ public void CreateFromBaggage_SampleRate_TooHigh() {"sentry-sample_rate", "1.1"} }); - var dsc = baggage.CreateDynamicSamplingContext(); + var dsc = baggage.CreateDynamicSamplingContext(_fixture.InactiveReplaySession); Assert.Null(dsc); } @@ -153,7 +173,7 @@ public void CreateFromBaggage_SampleRand_Invalid() {"sentry-sample_rand", "not-a-number"}, }); - var dsc = baggage.CreateDynamicSamplingContext(); + var dsc = baggage.CreateDynamicSamplingContext(_fixture.InactiveReplaySession); Assert.Null(dsc); } @@ -169,7 +189,7 @@ public void CreateFromBaggage_SampleRand_TooLow() {"sentry-sample_rand", "-0.1"} }); - var dsc = baggage.CreateDynamicSamplingContext(); + var dsc = baggage.CreateDynamicSamplingContext(_fixture.InactiveReplaySession); Assert.Null(dsc); } @@ -185,7 +205,7 @@ public void CreateFromBaggage_SampleRand_TooHigh() {"sentry-sample_rand", "1.0"} // Must be less than 1 }); - var dsc = baggage.CreateDynamicSamplingContext(); + var dsc = baggage.CreateDynamicSamplingContext(_fixture.InactiveReplaySession); Assert.Null(dsc); } @@ -200,7 +220,7 @@ public void CreateFromBaggage_NotSampledNoSampleRand_GeneratesSampleRand() {"sentry-sample_rate", "0.5"} }); - var dsc = baggage.CreateDynamicSamplingContext(); + var dsc = baggage.CreateDynamicSamplingContext(_fixture.InactiveReplaySession); using var scope = new AssertionScope(); Assert.NotNull(dsc); @@ -223,7 +243,7 @@ public void CreateFromBaggage_SampledNoSampleRand_GeneratesConsistentSampleRand( {"sentry-sampled", sampled}, }); - var dsc = baggage.CreateDynamicSamplingContext(); + var dsc = baggage.CreateDynamicSamplingContext(_fixture.InactiveReplaySession); using var scope = new AssertionScope(); Assert.NotNull(dsc); @@ -252,13 +272,15 @@ public void CreateFromBaggage_Sampled_MalFormed() {"sentry-sampled", "foo"}, }); - var dsc = baggage.CreateDynamicSamplingContext(); + var dsc = baggage.CreateDynamicSamplingContext(_fixture.InactiveReplaySession); Assert.Null(dsc); } - [Fact] - public void CreateFromBaggage_Valid_Minimum() + [Theory] + [InlineData(true)] + [InlineData(false)] + public void CreateFromBaggage_Valid_Minimum(bool replaySessionIsActive) { var baggage = BaggageHeader.Create(new List> { @@ -267,18 +289,29 @@ public void CreateFromBaggage_Valid_Minimum() {"sentry-sample_rate", "1.0"} }); - var dsc = baggage.CreateDynamicSamplingContext(); + var dsc = baggage.CreateDynamicSamplingContext(replaySessionIsActive ? _fixture.ActiveReplaySession : _fixture.InactiveReplaySession); Assert.NotNull(dsc); - Assert.Equal(4, dsc.Items.Count); + Assert.Equal(replaySessionIsActive ? 5 : 4, dsc.Items.Count); Assert.Equal("43365712692146d08ee11a729dfbcaca", Assert.Contains("trace_id", dsc.Items)); Assert.Equal("d4d82fc1c2c4032a83f3a29aa3a3aff", Assert.Contains("public_key", dsc.Items)); Assert.Equal("1.0", Assert.Contains("sample_rate", dsc.Items)); Assert.Contains("sample_rand", dsc.Items); + if (replaySessionIsActive) + { + // We add the replay_id automatically when we have an active replay session + Assert.Equal(_fixture.ActiveReplayId.ToString(), Assert.Contains("replay_id", dsc.Items)); + } + else + { + Assert.DoesNotContain("replay_id", dsc.Items); + } } - [Fact] - public void CreateFromBaggage_Valid_Complete() + [Theory] + [InlineData(true)] + [InlineData(false)] + public void CreateFromBaggage_Valid_Complete(bool replaySessionIsActive) { var baggage = BaggageHeader.Create(new List> { @@ -290,10 +323,11 @@ public void CreateFromBaggage_Valid_Complete() {"sentry-release", "test@1.0.0+abc"}, {"sentry-environment", "production"}, {"sentry-user_segment", "Group B"}, - {"sentry-transaction", "GET /person/{id}"} + {"sentry-transaction", "GET /person/{id}"}, + {"sentry-replay_id","bfd31b89a59d41c99d96dc2baf840ecd"} }); - var dsc = baggage.CreateDynamicSamplingContext(); + var dsc = baggage.CreateDynamicSamplingContext(replaySessionIsActive ? _fixture.ActiveReplaySession : _fixture.InactiveReplaySession); Assert.NotNull(dsc); Assert.Equal(baggage.Members.Count, dsc.Items.Count); @@ -306,6 +340,16 @@ public void CreateFromBaggage_Valid_Complete() Assert.Equal("production", Assert.Contains("environment", dsc.Items)); Assert.Equal("Group B", Assert.Contains("user_segment", dsc.Items)); Assert.Equal("GET /person/{id}", Assert.Contains("transaction", dsc.Items)); + if (replaySessionIsActive) + { + // We overwrite the replay_id when we have an active replay session + Assert.Equal(_fixture.ActiveReplayId.ToString(), Assert.Contains("replay_id", dsc.Items)); + } + else + { + // If we don't have any active replay session of our own then we propagate whatever was in the baggage header + Assert.Equal("bfd31b89a59d41c99d96dc2baf840ecd", Assert.Contains("replay_id", dsc.Items)); + } } [Fact] @@ -320,10 +364,11 @@ public void ToBaggageHeader() {"sentry-release", "test@1.0.0+abc"}, {"sentry-environment", "production"}, {"sentry-user_segment", "Group B"}, - {"sentry-transaction", "GET /person/{id}"} + {"sentry-transaction", "GET /person/{id}"}, + {"sentry-replay_id", _fixture.ActiveReplayId.ToString()} }); - var dsc = original.CreateDynamicSamplingContext(); + var dsc = original.CreateDynamicSamplingContext(_fixture.ActiveReplaySession); var result = dsc?.ToBaggageHeader(); @@ -362,10 +407,10 @@ public void CreateFromTransaction(bool? isSampled) }, }; - var dsc = transaction.CreateDynamicSamplingContext(options); + var dsc = transaction.CreateDynamicSamplingContext(options, _fixture.ActiveReplaySession); Assert.NotNull(dsc); - Assert.Equal(isSampled.HasValue ? 8 : 7, dsc.Items.Count); + Assert.Equal(isSampled.HasValue ? 9 : 8, dsc.Items.Count); Assert.Equal(traceId.ToString(), Assert.Contains("trace_id", dsc.Items)); Assert.Equal("d4d82fc1c2c4032a83f3a29aa3a3aff", Assert.Contains("public_key", dsc.Items)); if (transaction.IsSampled is { } sampled) @@ -381,21 +426,34 @@ public void CreateFromTransaction(bool? isSampled) Assert.Equal("foo@2.4.5", Assert.Contains("release", dsc.Items)); Assert.Equal("staging", Assert.Contains("environment", dsc.Items)); Assert.Equal("GET /person/{id}", Assert.Contains("transaction", dsc.Items)); + // We add the replay_id automatically when we have an active replay session + Assert.Equal(_fixture.ActiveReplayId.ToString(), Assert.Contains("replay_id", dsc.Items)); } - [Fact] - public void CreateFromPropagationContext_Valid_Complete() + [Theory] + [InlineData(false)] + [InlineData(true)] + public void CreateFromPropagationContext_Valid_Complete(bool replaySessionIsActive) { var options = new SentryOptions { Dsn = "https://a@sentry.io/1", Release = "test-release", Environment = "test-environment" }; var propagationContext = new SentryPropagationContext( SentryId.Parse("43365712692146d08ee11a729dfbcaca"), SpanId.Parse("1234")); - var dsc = propagationContext.CreateDynamicSamplingContext(options); + var dsc = propagationContext.CreateDynamicSamplingContext(options, replaySessionIsActive ? _fixture.ActiveReplaySession : _fixture.InactiveReplaySession); Assert.NotNull(dsc); Assert.Equal("43365712692146d08ee11a729dfbcaca", Assert.Contains("trace_id", dsc.Items)); Assert.Equal("a", Assert.Contains("public_key", dsc.Items)); Assert.Equal("test-release", Assert.Contains("release", dsc.Items)); Assert.Equal("test-environment", Assert.Contains("environment", dsc.Items)); + if (replaySessionIsActive) + { + // We add the replay_id automatically when we have an active replay session + Assert.Equal(_fixture.ActiveReplayId.ToString(), Assert.Contains("replay_id", dsc.Items)); + } + else + { + Assert.DoesNotContain("replay_id", dsc.Items); + } } } diff --git a/test/Sentry.Tests/EventProcessorTests.verify.cs b/test/Sentry.Tests/EventProcessorTests.verify.cs index ea324e8283..6dd6394fd5 100644 --- a/test/Sentry.Tests/EventProcessorTests.verify.cs +++ b/test/Sentry.Tests/EventProcessorTests.verify.cs @@ -1,3 +1,5 @@ +using Sentry.Tests.Internals; + namespace Sentry.Tests; public class EventProcessorTests diff --git a/test/Sentry.Tests/HubTests.cs b/test/Sentry.Tests/HubTests.cs index cc9c510a57..efbca9830b 100644 --- a/test/Sentry.Tests/HubTests.cs +++ b/test/Sentry.Tests/HubTests.cs @@ -1,5 +1,6 @@ using System.IO.Abstractions.TestingHelpers; using Sentry.Internal.Http; +using Sentry.Tests.Internals; namespace Sentry.Tests; @@ -10,14 +11,11 @@ public partial class HubTests private class Fixture { public SentryOptions Options { get; } - public ISentryClient Client { get; set; } - public ISessionManager SessionManager { get; set; } - public IInternalScopeManager ScopeManager { get; set; } - public ISystemClock Clock { get; set; } + public IReplaySession ReplaySession { get; } public Fixture() { @@ -29,9 +27,11 @@ public Fixture() }; Client = Substitute.For(); + + ReplaySession = Substitute.For(); } - public Hub GetSut() => new(Options, Client, SessionManager, Clock, ScopeManager); + public Hub GetSut() => new(Options, Client, SessionManager, Clock, ScopeManager, replaySession: ReplaySession); } private readonly Fixture _fixture = new(); @@ -173,7 +173,7 @@ public void CaptureException_TransactionFinished_Gets_DSC_From_LinkedSpan() {"sentry-trace_id", "75302ac48a024bde9a3b3734a82e36c8"}, {"sentry-public_key", "d4d82fc1c2c4032a83f3a29aa3a3aff"}, {"sentry-replay_id","bfd31b89a59d41c99d96dc2baf840ecd"} - }).CreateDynamicSamplingContext(); + }).CreateDynamicSamplingContext(_fixture.ReplaySession); var transaction = hub.StartTransaction( transactionContext, @@ -193,7 +193,7 @@ public void CaptureException_TransactionFinished_Gets_DSC_From_LinkedSpan() } [Fact] - public void CaptureException_ActiveSpanExistsOnScopeButIsSampledOut_EventIsNotLinkedToSpan() + public void CaptureException_ActiveSpanExistsOnScopeButIsSampledOut_EventIsLinkedToSpan() { // Arrange _fixture.Options.TracesSampleRate = 0.0; @@ -209,8 +209,8 @@ public void CaptureException_ActiveSpanExistsOnScopeButIsSampledOut_EventIsNotLi // Assert _fixture.Client.Received(1).CaptureEvent( Arg.Is(evt => - evt.Contexts.Trace.TraceId == default && - evt.Contexts.Trace.SpanId == default), + evt.Contexts.Trace.TraceId == transaction.TraceId && + evt.Contexts.Trace.SpanId == transaction.SpanId), Arg.Any(), Arg.Any()); } @@ -680,6 +680,82 @@ public void StartTransaction_SameInstrumenter_SampledIn() transaction.IsSampled.Should().BeTrue(); } + [Theory] + [InlineData(true)] + [InlineData(false)] + public void StartTransaction_DynamicSamplingContextWithReplayId_UsesActiveReplaySessionId(bool replaySessionIsActive) + { + // Arrange + var transactionContext = new TransactionContext("name", "operation"); + + var dummyReplaySession = Substitute.For(); + dummyReplaySession.ActiveReplayId.Returns((SentryId?)null); // So the replay id in the baggage header is used + var dsc = BaggageHeader.Create(new List> + { + {"sentry-trace_id", "43365712692146d08ee11a729dfbcaca"}, + {"sentry-public_key", "d4d82fc1c2c4032a83f3a29aa3a3aff"}, + {"sentry-sampled", "true"}, + {"sentry-sample_rate", "0.5"}, // Required in the baggage header, but ignored by sampling logic + {"sentry-replay_id","bfd31b89a59d41c99d96dc2baf840ecd"} + }).CreateDynamicSamplingContext(dummyReplaySession); + + _fixture.Options.TracesSampleRate = 1.0; + _fixture.ReplaySession.ActiveReplayId.Returns(replaySessionIsActive ? SentryId.Create() : null); // This one gets used by the SUT + var hub = _fixture.GetSut(); + + // Act + var transaction = hub.StartTransaction(transactionContext, new Dictionary(), dsc); + + // Assert + var transactionTracer = ((TransactionTracer)transaction); + transactionTracer.IsSampled.Should().Be(true); + transactionTracer.DynamicSamplingContext.Should().NotBeNull(); + foreach (var dscItem in dsc!.Items) + { + if (dscItem.Key == "replay_id") + { + transactionTracer.DynamicSamplingContext!.Items["replay_id"].Should().Be(replaySessionIsActive + // We overwrite the replay_id when we have an active replay session + ? _fixture.ReplaySession.ActiveReplayId.ToString() + // Otherwise we propagate whatever was in the baggage header + : dscItem.Value); + } + else + { + transactionTracer.DynamicSamplingContext!.Items.Should() + .Contain(kvp => kvp.Key == dscItem.Key && kvp.Value == dscItem.Value); + } + } + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void StartTransaction_NoDynamicSamplingContext_UsesActiveReplaySessionId(bool replaySessionIsActive) + { + // Arrange + var transactionContext = new TransactionContext("name", "operation"); + _fixture.ReplaySession.ActiveReplayId.Returns(replaySessionIsActive ? SentryId.Create() : null); + var hub = _fixture.GetSut(); + + // Act + var transaction = hub.StartTransaction(transactionContext, new Dictionary()); + + // Assert + var transactionTracer = ((TransactionTracer)transaction); + transactionTracer.SampleRand.Should().NotBeNull(); + transactionTracer.DynamicSamplingContext.Should().NotBeNull(); + if (replaySessionIsActive) + { + // We add the replay_id when we have an active replay session + transactionTracer.DynamicSamplingContext!.Items["replay_id"].Should().Be(_fixture.ReplaySession.ActiveReplayId.ToString()); + } + else + { + transactionTracer.DynamicSamplingContext!.Items.Should().NotContainKey("replay_id"); + } + } + [Fact] public void StartTransaction_NoDynamicSamplingContext_GeneratesSampleRand() { @@ -705,12 +781,11 @@ public void StartTransaction_DynamicSamplingContextWithoutSampleRand_SampleRandN { // Arrange var transactionContext = new TransactionContext("name", "operation"); - var customContext = new Dictionary(); var hub = _fixture.GetSut(); // Act - var transaction = hub.StartTransaction(transactionContext, customContext, DynamicSamplingContext.Empty); + var transaction = hub.StartTransaction(transactionContext, new Dictionary(), DynamicSamplingContext.Empty); // Assert var transactionTracer = ((TransactionTracer)transaction); @@ -725,7 +800,7 @@ public void StartTransaction_DynamicSamplingContextWithSampleRand_InheritsSample { // Arrange var transactionContext = new TransactionContext("name", "operation"); - var customContext = new Dictionary(); + var dummyReplaySession = Substitute.For(); var dsc = BaggageHeader.Create(new List> { {"sentry-trace_id", "43365712692146d08ee11a729dfbcaca"}, @@ -733,13 +808,13 @@ public void StartTransaction_DynamicSamplingContextWithSampleRand_InheritsSample {"sentry-sampled", "true"}, {"sentry-sample_rate", "0.5"}, // Required in the baggage header, but ignored by sampling logic {"sentry-sample_rand", "0.1234"} - }).CreateDynamicSamplingContext(); + }).CreateDynamicSamplingContext(dummyReplaySession); _fixture.Options.TracesSampleRate = 0.4; var hub = _fixture.GetSut(); // Act - var transaction = hub.StartTransaction(transactionContext, customContext, dsc); + var transaction = hub.StartTransaction(transactionContext, new Dictionary(), dsc); // Assert var transactionTracer = ((TransactionTracer)transaction); @@ -764,7 +839,7 @@ public void StartTransaction_TraceSampler_UsesSampleRand(double sampleRate, bool {"sentry-sampled", "true"}, {"sentry-sample_rate", "0.5"}, {"sentry-sample_rand", "0.1234"} - }).CreateDynamicSamplingContext(); + }).CreateDynamicSamplingContext(_fixture.ReplaySession); _fixture.Options.TracesSampler = _ => sampleRate; var hub = _fixture.GetSut(); @@ -788,13 +863,14 @@ public void StartTransaction_StaticSampler_UsesSampleRand(double sampleRate, boo // Arrange var transactionContext = new TransactionContext("name", "operation"); var customContext = new Dictionary(); + var dummyReplaySession = Substitute.For(); var dsc = BaggageHeader.Create(new List> { {"sentry-trace_id", "43365712692146d08ee11a729dfbcaca"}, {"sentry-public_key", "d4d82fc1c2c4032a83f3a29aa3a3aff"}, {"sentry-sample_rate", "0.5"}, // Static sampling ignores this and uses options.TracesSampleRate instead {"sentry-sample_rand", "0.1234"} - }).CreateDynamicSamplingContext(); + }).CreateDynamicSamplingContext(dummyReplaySession); _fixture.Options.TracesSampleRate = sampleRate; var hub = _fixture.GetSut(); diff --git a/test/Sentry.Tests/Protocol/SentryTransactionTests.cs b/test/Sentry.Tests/Protocol/SentryTransactionTests.cs index c4f075dd00..7a85611425 100644 --- a/test/Sentry.Tests/Protocol/SentryTransactionTests.cs +++ b/test/Sentry.Tests/Protocol/SentryTransactionTests.cs @@ -55,6 +55,33 @@ public async Task NewTransactionTracer_IdleTimeoutProvided_AutomaticallyFinishes transaction.IsFinished.Should().BeTrue(); } + [Fact] + public void NewTransactionTracer_PropagationContextHasReplayId_UsesActiveSessionReplayIdInstead() + { + // Arrange + var hub = Substitute.For(); + var traceHeader = new SentryTraceHeader(SentryId.Create(), SpanId.Create(), null); + var replayContext = Substitute.For(); + var baggageHeader = BaggageHeader.Create(new List> + { + { "sentry-sample_rate", "1.0" }, + { "sentry-sample_rand", "0.1234" }, + { "sentry-trace_id", "75302ac48a024bde9a3b3734a82e36c8" }, + { "sentry-public_key", "d4d82fc1c2c4032a83f3a29aa3a3aff" }, + { "sentry-replay_id", "bfd31b89a59d41c99d96dc2baf840ecd" } + }); + var propagationContext = SentryPropagationContext.CreateFromHeaders(null, traceHeader, baggageHeader, replayContext); + var scope = new Scope(hub.GetSentryOptions(), propagationContext); + hub.ConfigureScope(Arg.Do>(callback => callback(scope))); + var transactionContext = new TransactionContext("test-name", "test-operation"); + + // Act + var actualTransaction = new TransactionTracer(hub, transactionContext); + + // Assert + Assert.NotEqual(DateTimeOffset.MinValue, actualTransaction.StartTimestamp); + } + [Fact] public void Redact_Redacts_Urls() { diff --git a/test/Sentry.Tests/SentryPropagationContextTests.cs b/test/Sentry.Tests/SentryPropagationContextTests.cs index dc658d2ed9..bb90359d0a 100644 --- a/test/Sentry.Tests/SentryPropagationContextTests.cs +++ b/test/Sentry.Tests/SentryPropagationContextTests.cs @@ -2,50 +2,100 @@ namespace Sentry.Tests; public class SentryPropagationContextTests { - [Fact] - public void CopyConstructor_CreatesCopy() + private class Fixture + { + public SentryId ActiveReplayId { get; } = SentryId.Create(); + public IReplaySession InactiveReplaySession { get; } + public IReplaySession ActiveReplaySession { get; } + + public Fixture() + { + ActiveReplaySession = Substitute.For(); + ActiveReplaySession.ActiveReplayId.Returns(ActiveReplayId); + + InactiveReplaySession = Substitute.For(); + InactiveReplaySession.ActiveReplayId.Returns((SentryId?)null); + } + } + + private readonly Fixture _fixture = new(); + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void CopyConstructor_CreatesCopyWithReplayId(bool replaySessionIsActive) { var original = new SentryPropagationContext(); - original.GetOrCreateDynamicSamplingContext(new SentryOptions { Dsn = ValidDsn }); + original.GetOrCreateDynamicSamplingContext(new SentryOptions { Dsn = ValidDsn }, _fixture.InactiveReplaySession); var copy = new SentryPropagationContext(original); Assert.Equal(original.TraceId, copy.TraceId); Assert.Equal(original.SpanId, copy.SpanId); - Assert.Equal(original._dynamicSamplingContext, copy._dynamicSamplingContext); + Assert.Equal(original._dynamicSamplingContext!.Items.Count, copy._dynamicSamplingContext!.Items.Count); + foreach (var dscItem in original._dynamicSamplingContext!.Items) + { + if (dscItem.Key == "replay_id") + { + copy._dynamicSamplingContext!.Items["replay_id"].Should().Be(replaySessionIsActive + // We overwrite the replay_id when we have an active replay session + ? _fixture.ActiveReplayId.ToString() + // Otherwise we propagate whatever was in the baggage header + : dscItem.Value); + } + else + { + copy._dynamicSamplingContext!.Items.Should() + .Contain(kvp => kvp.Key == dscItem.Key && kvp.Value == dscItem.Value); + } + } } - [Fact] - public void GetOrCreateDynamicSamplingContext_DynamicSamplingContextIsNull_CreatesDynamicSamplingContext() + [Theory] + [InlineData(false)] + [InlineData(true)] + public void GetOrCreateDynamicSamplingContext_DynamicSamplingContextIsNull_CreatesDynamicSamplingContext(bool replaySessionIsActive) { var options = new SentryOptions { Dsn = ValidDsn }; var propagationContext = new SentryPropagationContext(); Assert.Null(propagationContext._dynamicSamplingContext); // Sanity check - _ = propagationContext.GetOrCreateDynamicSamplingContext(options); + _ = propagationContext.GetOrCreateDynamicSamplingContext(options, replaySessionIsActive ? _fixture.ActiveReplaySession : _fixture.InactiveReplaySession); Assert.NotNull(propagationContext._dynamicSamplingContext); + if (replaySessionIsActive) + { + // We add the replay_id automatically when we have an active replay session + Assert.Equal(_fixture.ActiveReplayId.ToString(), Assert.Contains("replay_id", propagationContext._dynamicSamplingContext.Items)); + } + else + { + Assert.DoesNotContain("replay_id", propagationContext._dynamicSamplingContext.Items); + } } - [Fact] - public void GetOrCreateDynamicSamplingContext_DynamicSamplingContextIsNotNull_ReturnsSameDynamicSamplingContext() + [Theory] + [InlineData(false)] + [InlineData(true)] + public void GetOrCreateDynamicSamplingContext_DynamicSamplingContextIsNotNull_ReturnsSameDynamicSamplingContext(bool replaySessionIsActive) { var options = new SentryOptions { Dsn = ValidDsn }; var propagationContext = new SentryPropagationContext(); - var firstDynamicSamplingContext = propagationContext.GetOrCreateDynamicSamplingContext(options); + var firstDynamicSamplingContext = propagationContext.GetOrCreateDynamicSamplingContext(options, replaySessionIsActive ? _fixture.ActiveReplaySession : _fixture.InactiveReplaySession); - var secondDynamicSamplingContext = propagationContext.GetOrCreateDynamicSamplingContext(options); + var secondDynamicSamplingContext = propagationContext.GetOrCreateDynamicSamplingContext(options, replaySessionIsActive ? _fixture.ActiveReplaySession : _fixture.InactiveReplaySession); Assert.Same(firstDynamicSamplingContext, secondDynamicSamplingContext); } [Fact] - public void CreateFromHeaders_HeadersNull_CreatesPropagationContextWithTraceAndSpanId() + public void CreateFromHeaders_HeadersNull_CreatesPropagationContextWithTraceAndSpanAndReplayId() { - var propagationContext = SentryPropagationContext.CreateFromHeaders(null, null, null); + var propagationContext = SentryPropagationContext.CreateFromHeaders(null, null, null, _fixture.ActiveReplaySession); Assert.NotEqual(propagationContext.TraceId, SentryId.Empty); Assert.NotEqual(propagationContext.SpanId, SpanId.Empty); + Assert.Null(propagationContext._dynamicSamplingContext); } [Fact] @@ -53,15 +103,16 @@ public void CreateFromHeaders_TraceHeaderNotNull_CreatesPropagationContextFromTr { var traceHeader = new SentryTraceHeader(SentryId.Create(), SpanId.Create(), null); - var propagationContext = SentryPropagationContext.CreateFromHeaders(null, traceHeader, null); + var propagationContext = SentryPropagationContext.CreateFromHeaders(null, traceHeader, null, _fixture.ActiveReplaySession); Assert.Equal(traceHeader.TraceId, propagationContext.TraceId); Assert.NotEqual(traceHeader.SpanId, propagationContext.SpanId); // Sanity check Assert.Equal(traceHeader.SpanId, propagationContext.ParentSpanId); + Assert.Null(propagationContext._dynamicSamplingContext); } [Fact] - public void CreateFromHeaders_TraceHeaderNullButBaggageExists_CreatesPropagationContextWithoutDynamicSamplingContext() + public void CreateFromHeaders_BaggageExistsButTraceHeaderNull_CreatesPropagationContextWithoutDynamicSamplingContext() { var baggageHeader = BaggageHeader.Create(new List> { @@ -71,14 +122,17 @@ public void CreateFromHeaders_TraceHeaderNullButBaggageExists_CreatesPropagation { "sentry-replay_id", "bfd31b89a59d41c99d96dc2baf840ecd" } }); - var propagationContext = SentryPropagationContext.CreateFromHeaders(null, null, baggageHeader); + var propagationContext = SentryPropagationContext.CreateFromHeaders(null, null, baggageHeader, _fixture.InactiveReplaySession); Assert.Null(propagationContext._dynamicSamplingContext); } - [Fact] - public void CreateFromHeaders_BaggageHeaderNotNull_CreatesPropagationContextWithDynamicSamplingContext() + [Theory] + [InlineData(false)] + [InlineData(true)] + public void CreateFromHeaders_BaggageHeaderNotNull_CreatesPropagationContextWithDynamicSamplingContext(bool replaySessionIsActive) { + // Arrange var traceHeader = new SentryTraceHeader(SentryId.Create(), SpanId.Create(), null); var baggageHeader = BaggageHeader.Create(new List> { @@ -88,9 +142,23 @@ public void CreateFromHeaders_BaggageHeaderNotNull_CreatesPropagationContextWith { "sentry-public_key", "d4d82fc1c2c4032a83f3a29aa3a3aff" }, { "sentry-replay_id", "bfd31b89a59d41c99d96dc2baf840ecd" } }); + var replaySession = replaySessionIsActive ? _fixture.ActiveReplaySession : _fixture.InactiveReplaySession; - var propagationContext = SentryPropagationContext.CreateFromHeaders(null, traceHeader, baggageHeader); + // Act + var propagationContext = SentryPropagationContext.CreateFromHeaders(null, traceHeader, baggageHeader, replaySession); - Assert.Equal(5, propagationContext.GetOrCreateDynamicSamplingContext(new SentryOptions()).Items.Count); + // Assert + var dsc = propagationContext.GetOrCreateDynamicSamplingContext(new SentryOptions(), replaySession); + Assert.Equal(5, dsc.Items.Count); + if (replaySessionIsActive) + { + // We add the replay_id automatically when we have an active replay session + Assert.Equal(_fixture.ActiveReplayId.ToString(), Assert.Contains("replay_id", dsc.Items)); + } + else + { + // Otherwise we inherit the replay_id from the baggage header + Assert.Equal("bfd31b89a59d41c99d96dc2baf840ecd", Assert.Contains("replay_id", dsc.Items)); + } } } From 845de33a4467fb616b17fe0df053fe210b0b2ff7 Mon Sep 17 00:00:00 2001 From: getsentry-bot Date: Tue, 13 May 2025 11:18:31 +0000 Subject: [PATCH 240/363] release: 5.7.0 --- CHANGELOG.md | 2 +- Directory.Build.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 508cd01edb..f5b1515a0f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## Unreleased +## 5.7.0 ### Features diff --git a/Directory.Build.props b/Directory.Build.props index beea6f603c..c6b2e14f95 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,7 +1,7 @@ - 5.7.0-beta.0 + 5.7.0 13 true true From 02bd16f00cf2b6fb716182c5e369c76d33cc92b0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 16 May 2025 11:03:00 -0400 Subject: [PATCH 241/363] chore: update modules/sentry-native to 0.8.5 (#4189) Co-authored-by: GitHub --- CHANGELOG.md | 8 ++++++++ modules/sentry-native | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f5b1515a0f..ab4e1b6a3f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## Unreleased + +### Dependencies + +- Bump Native SDK from v0.8.4 to v0.8.5 ([#4189](https://github.com/getsentry/sentry-dotnet/pull/4189)) + - [changelog](https://github.com/getsentry/sentry-native/blob/master/CHANGELOG.md#085) + - [diff](https://github.com/getsentry/sentry-native/compare/0.8.4...0.8.5) + ## 5.7.0 ### Features diff --git a/modules/sentry-native b/modules/sentry-native index 19d50848fb..70982f57b6 160000 --- a/modules/sentry-native +++ b/modules/sentry-native @@ -1 +1 @@ -Subproject commit 19d50848fb80a7f56f98fe30fef6731b1eedb8b3 +Subproject commit 70982f57b6407a1f3afe377d0c2a61ccbbfb2ce2 From c69110b9e7d5b4e875c206e3bd766394eb098370 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Sat, 17 May 2025 20:06:51 +0200 Subject: [PATCH 242/363] ci: Alpine Linux Docker image (musl) (#4190) --- .github/alpine/Dockerfile | 42 ++++++++++++++++++++++++++++++++++++ .github/workflows/alpine.yml | 38 ++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 .github/alpine/Dockerfile create mode 100644 .github/workflows/alpine.yml diff --git a/.github/alpine/Dockerfile b/.github/alpine/Dockerfile new file mode 100644 index 0000000000..21b74b6956 --- /dev/null +++ b/.github/alpine/Dockerfile @@ -0,0 +1,42 @@ +# docker build --build-arg BASE=alpine:3.21 +ARG BASE=alpine:latest +FROM ${BASE} + +RUN apk update + +# common +RUN apk add bash build-base cmake curl git icu lsb-release-minimal sudo tar wget + +# sentry-native +RUN apk add curl-dev libunwind-dev libunwind-static linux-headers openssl-dev zlib-dev xz-dev + +# sentry-dotnet +RUN apk add grpc-plugins openjdk11 powershell +ENV PROTOBUF_PROTOC=/usr/bin/protoc +ENV GRPC_PROTOC_PLUGIN=/usr/bin/grpc_csharp_plugin +RUN pwsh -Command Install-Module Pester -Scope AllUsers -Force + +# mono (3.22+) +RUN if ! apk add mono; then \ + sed -i.bak 's|/v3\.[0-9]\+|/edge|g' /etc/apk/repositories && \ + cat /etc/apk/repositories && \ + apk update && \ + apk add --no-cache mono && \ + mv /etc/apk/repositories.bak /etc/apk/repositories && \ + apk update; \ + fi +RUN mono --version + +# runner +RUN addgroup runner +RUN adduser -S -u 1001 -h /home/runner -G runner runner +RUN mkdir -p /home/runner /__e /__w /__w/_temp /__w/_actions /__w/_tool +RUN chown -R runner:runner /home/runner /__e /__w +RUN ln -s /__w /home/runner/work +RUN echo "runner ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/runner +RUN chmod 0440 /etc/sudoers.d/runner +RUN chmod -R 777 /opt +RUN chmod -R 777 /usr/share +USER runner +WORKDIR /__w +ENTRYPOINT ["/bin/bash"] diff --git a/.github/workflows/alpine.yml b/.github/workflows/alpine.yml new file mode 100644 index 0000000000..4317a82890 --- /dev/null +++ b/.github/workflows/alpine.yml @@ -0,0 +1,38 @@ +name: Build Alpine Linux Docker image + +on: + workflow_dispatch: + push: + paths: + - '.github/alpine/*' + +jobs: + build: + name: Build sentry-dotnet-alpine:${{ matrix.version }} + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + version: + - "3.21" + + permissions: + contents: read + packages: write + + steps: + - uses: actions/checkout@v4 + + - uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build ghcr.io/${{ github.repository_owner }}/sentry-dotnet-alpine:${{ matrix.version }} + run: docker build --build-arg BASE=alpine:${{ matrix.version }} -t ghcr.io/${{ github.repository_owner }}/sentry-dotnet-alpine:${{ matrix.version }} . + working-directory: .github/alpine + + - name: Push ghcr.io/${{ github.repository_owner }}/sentry-dotnet-alpine:${{ matrix.version }} + run: docker push ghcr.io/${{ github.repository_owner }}/sentry-dotnet-alpine:${{ matrix.version }} + working-directory: .github/alpine From 34d849038e3e23868a12d56ea256c36b0a7f3a61 Mon Sep 17 00:00:00 2001 From: Allan Ritchie Date: Sun, 18 May 2025 23:35:29 -0400 Subject: [PATCH 243/363] MAUI CommunityToolkit MVVM auto instrumentation of async commands (#4125) --------- Co-authored-by: James Crosswell --- .generated.NoMobile.sln | 718 +++++++++++++++++- CHANGELOG.md | 4 + Sentry-CI-Build-Linux.slnf | 2 + Sentry-CI-Build-Windows.slnf | 2 + Sentry-CI-Build-macOS.slnf | 2 + Sentry-CI-CodeQL.slnf | 1 + Sentry.sln | 718 +++++++++++++++++- SentryMobile.slnf | 1 + SentryNoSamples.slnf | 2 + samples/Sentry.Samples.Maui/AppShell.xaml.cs | 1 + samples/Sentry.Samples.Maui/CtMvvmPage.xaml | 18 + .../Sentry.Samples.Maui/CtMvvmPage.xaml.cs | 16 + .../Sentry.Samples.Maui/CtMvvmViewModel.cs | 15 + samples/Sentry.Samples.Maui/MainPage.xaml | 7 + samples/Sentry.Samples.Maui/MainPage.xaml.cs | 5 + samples/Sentry.Samples.Maui/MauiProgram.cs | 9 + .../Sentry.Samples.Maui.csproj | 3 + scripts/device-test.ps1 | 2 +- scripts/generate-solution-filters-config.yaml | 2 + .../MauiCommunityToolkitMvvmEventsBinder.cs | 162 ++++ .../Sentry.Maui.CommunityToolkit.Mvvm.csproj | 28 + .../SentryOptionsExtensions.cs | 18 + .../MauiElementEventBinderRegistration.cs | 15 + src/Sentry.Maui/Sentry.Maui.csproj | 2 + .../SentryMauiAppBuilderExtensions.cs | 15 +- src/Sentry.Maui/SentryMauiOptions.cs | 13 +- src/Sentry/HubExtensions.cs | 13 + src/Sentry/Internal/Hub.cs | 9 - src/Sentry/Scope.cs | 4 +- src/Sentry/Sentry.csproj | 2 + test/Directory.Build.props | 1 - ...uiCommunityToolkitMvvmEventsBinderTests.cs | 96 +++ ...ry.Maui.CommunityToolkit.Mvvm.Tests.csproj | 25 + .../Sentry.Maui.Device.TestApp.csproj | 1 + test/Sentry.Maui.Device.TestApp/Startup.cs | 3 + .../MauiEventsBinderTests.cs | 15 +- .../Sentry.Maui.Tests.csproj | 6 +- ...piApprovalTests.Run.DotNet8_0.verified.txt | 1 + ...piApprovalTests.Run.DotNet9_0.verified.txt | 1 + .../ApiApprovalTests.Run.Net4_8.verified.txt | 1 + 40 files changed, 1921 insertions(+), 38 deletions(-) create mode 100644 samples/Sentry.Samples.Maui/CtMvvmPage.xaml create mode 100644 samples/Sentry.Samples.Maui/CtMvvmPage.xaml.cs create mode 100644 samples/Sentry.Samples.Maui/CtMvvmViewModel.cs create mode 100644 src/Sentry.Maui.CommunityToolkit.Mvvm/MauiCommunityToolkitMvvmEventsBinder.cs create mode 100644 src/Sentry.Maui.CommunityToolkit.Mvvm/Sentry.Maui.CommunityToolkit.Mvvm.csproj create mode 100644 src/Sentry.Maui.CommunityToolkit.Mvvm/SentryOptionsExtensions.cs create mode 100644 src/Sentry.Maui/MauiElementEventBinderRegistration.cs create mode 100644 test/Sentry.Maui.CommunityToolkit.Mvvm.Tests/MauiCommunityToolkitMvvmEventsBinderTests.cs create mode 100644 test/Sentry.Maui.CommunityToolkit.Mvvm.Tests/Sentry.Maui.CommunityToolkit.Mvvm.Tests.csproj diff --git a/.generated.NoMobile.sln b/.generated.NoMobile.sln index 762d22068b..3730377a46 100644 --- a/.generated.NoMobile.sln +++ b/.generated.NoMobile.sln @@ -1,4 +1,4 @@ - + Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.0.31903.59 @@ -201,353 +201,1057 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.TrimTest", "test\Sen EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.MauiTrimTest", "test\Sentry.MauiTrimTest\Sentry.MauiTrimTest.csproj", "{DF92E098-822C-4B94-B96B-56BFB91FBB54}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Maui.CommunityToolkit.Mvvm", "src\Sentry.Maui.CommunityToolkit.Mvvm\Sentry.Maui.CommunityToolkit.Mvvm.csproj", "{E898F337-E982-46CC-8DA9-F8556AA7DD72}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.SourceGenerators", "src\Sentry.SourceGenerators\Sentry.SourceGenerators.csproj", "{C3CDF61C-3E28-441C-A9CE-011C89D11719}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.SourceGenerators.Tests", "test\Sentry.SourceGenerators.Tests\Sentry.SourceGenerators.Tests.csproj", "{3A76FF7D-2F32-4EA5-8999-2FFE3C7CB893}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Maui.CommunityToolkit.Mvvm.Tests", "test\Sentry.Maui.CommunityToolkit.Mvvm.Tests\Sentry.Maui.CommunityToolkit.Mvvm.Tests.csproj", "{ADC91A84-6054-42EC-8241-0D717E4C7194}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {8328B70C-B808-4ED1-BB16-8555B2752CB6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8328B70C-B808-4ED1-BB16-8555B2752CB6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8328B70C-B808-4ED1-BB16-8555B2752CB6}.Debug|x64.ActiveCfg = Debug|Any CPU + {8328B70C-B808-4ED1-BB16-8555B2752CB6}.Debug|x64.Build.0 = Debug|Any CPU + {8328B70C-B808-4ED1-BB16-8555B2752CB6}.Debug|x86.ActiveCfg = Debug|Any CPU + {8328B70C-B808-4ED1-BB16-8555B2752CB6}.Debug|x86.Build.0 = Debug|Any CPU {8328B70C-B808-4ED1-BB16-8555B2752CB6}.Release|Any CPU.ActiveCfg = Release|Any CPU {8328B70C-B808-4ED1-BB16-8555B2752CB6}.Release|Any CPU.Build.0 = Release|Any CPU + {8328B70C-B808-4ED1-BB16-8555B2752CB6}.Release|x64.ActiveCfg = Release|Any CPU + {8328B70C-B808-4ED1-BB16-8555B2752CB6}.Release|x64.Build.0 = Release|Any CPU + {8328B70C-B808-4ED1-BB16-8555B2752CB6}.Release|x86.ActiveCfg = Release|Any CPU + {8328B70C-B808-4ED1-BB16-8555B2752CB6}.Release|x86.Build.0 = Release|Any CPU {1368AFBF-BCB7-4585-B8A9-C0E767792BC1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1368AFBF-BCB7-4585-B8A9-C0E767792BC1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1368AFBF-BCB7-4585-B8A9-C0E767792BC1}.Debug|x64.ActiveCfg = Debug|Any CPU + {1368AFBF-BCB7-4585-B8A9-C0E767792BC1}.Debug|x64.Build.0 = Debug|Any CPU + {1368AFBF-BCB7-4585-B8A9-C0E767792BC1}.Debug|x86.ActiveCfg = Debug|Any CPU + {1368AFBF-BCB7-4585-B8A9-C0E767792BC1}.Debug|x86.Build.0 = Debug|Any CPU {1368AFBF-BCB7-4585-B8A9-C0E767792BC1}.Release|Any CPU.ActiveCfg = Release|Any CPU {1368AFBF-BCB7-4585-B8A9-C0E767792BC1}.Release|Any CPU.Build.0 = Release|Any CPU + {1368AFBF-BCB7-4585-B8A9-C0E767792BC1}.Release|x64.ActiveCfg = Release|Any CPU + {1368AFBF-BCB7-4585-B8A9-C0E767792BC1}.Release|x64.Build.0 = Release|Any CPU + {1368AFBF-BCB7-4585-B8A9-C0E767792BC1}.Release|x86.ActiveCfg = Release|Any CPU + {1368AFBF-BCB7-4585-B8A9-C0E767792BC1}.Release|x86.Build.0 = Release|Any CPU {3E5E5AAD-4563-4428-8577-2A2A46137BFC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3E5E5AAD-4563-4428-8577-2A2A46137BFC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3E5E5AAD-4563-4428-8577-2A2A46137BFC}.Debug|x64.ActiveCfg = Debug|Any CPU + {3E5E5AAD-4563-4428-8577-2A2A46137BFC}.Debug|x64.Build.0 = Debug|Any CPU + {3E5E5AAD-4563-4428-8577-2A2A46137BFC}.Debug|x86.ActiveCfg = Debug|Any CPU + {3E5E5AAD-4563-4428-8577-2A2A46137BFC}.Debug|x86.Build.0 = Debug|Any CPU {3E5E5AAD-4563-4428-8577-2A2A46137BFC}.Release|Any CPU.ActiveCfg = Release|Any CPU {3E5E5AAD-4563-4428-8577-2A2A46137BFC}.Release|Any CPU.Build.0 = Release|Any CPU + {3E5E5AAD-4563-4428-8577-2A2A46137BFC}.Release|x64.ActiveCfg = Release|Any CPU + {3E5E5AAD-4563-4428-8577-2A2A46137BFC}.Release|x64.Build.0 = Release|Any CPU + {3E5E5AAD-4563-4428-8577-2A2A46137BFC}.Release|x86.ActiveCfg = Release|Any CPU + {3E5E5AAD-4563-4428-8577-2A2A46137BFC}.Release|x86.Build.0 = Release|Any CPU {789A9F8A-08A9-4211-A4AB-F45633960D94}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {789A9F8A-08A9-4211-A4AB-F45633960D94}.Debug|Any CPU.Build.0 = Debug|Any CPU + {789A9F8A-08A9-4211-A4AB-F45633960D94}.Debug|x64.ActiveCfg = Debug|Any CPU + {789A9F8A-08A9-4211-A4AB-F45633960D94}.Debug|x64.Build.0 = Debug|Any CPU + {789A9F8A-08A9-4211-A4AB-F45633960D94}.Debug|x86.ActiveCfg = Debug|Any CPU + {789A9F8A-08A9-4211-A4AB-F45633960D94}.Debug|x86.Build.0 = Debug|Any CPU {789A9F8A-08A9-4211-A4AB-F45633960D94}.Release|Any CPU.ActiveCfg = Release|Any CPU {789A9F8A-08A9-4211-A4AB-F45633960D94}.Release|Any CPU.Build.0 = Release|Any CPU + {789A9F8A-08A9-4211-A4AB-F45633960D94}.Release|x64.ActiveCfg = Release|Any CPU + {789A9F8A-08A9-4211-A4AB-F45633960D94}.Release|x64.Build.0 = Release|Any CPU + {789A9F8A-08A9-4211-A4AB-F45633960D94}.Release|x86.ActiveCfg = Release|Any CPU + {789A9F8A-08A9-4211-A4AB-F45633960D94}.Release|x86.Build.0 = Release|Any CPU {E683FB73-A305-462A-8F76-880E7A2F7F74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E683FB73-A305-462A-8F76-880E7A2F7F74}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E683FB73-A305-462A-8F76-880E7A2F7F74}.Debug|x64.ActiveCfg = Debug|Any CPU + {E683FB73-A305-462A-8F76-880E7A2F7F74}.Debug|x64.Build.0 = Debug|Any CPU + {E683FB73-A305-462A-8F76-880E7A2F7F74}.Debug|x86.ActiveCfg = Debug|Any CPU + {E683FB73-A305-462A-8F76-880E7A2F7F74}.Debug|x86.Build.0 = Debug|Any CPU {E683FB73-A305-462A-8F76-880E7A2F7F74}.Release|Any CPU.ActiveCfg = Release|Any CPU {E683FB73-A305-462A-8F76-880E7A2F7F74}.Release|Any CPU.Build.0 = Release|Any CPU + {E683FB73-A305-462A-8F76-880E7A2F7F74}.Release|x64.ActiveCfg = Release|Any CPU + {E683FB73-A305-462A-8F76-880E7A2F7F74}.Release|x64.Build.0 = Release|Any CPU + {E683FB73-A305-462A-8F76-880E7A2F7F74}.Release|x86.ActiveCfg = Release|Any CPU + {E683FB73-A305-462A-8F76-880E7A2F7F74}.Release|x86.Build.0 = Release|Any CPU {E53C49EE-FC70-4B26-A62A-63CAD51DB399}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E53C49EE-FC70-4B26-A62A-63CAD51DB399}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E53C49EE-FC70-4B26-A62A-63CAD51DB399}.Debug|x64.ActiveCfg = Debug|Any CPU + {E53C49EE-FC70-4B26-A62A-63CAD51DB399}.Debug|x64.Build.0 = Debug|Any CPU + {E53C49EE-FC70-4B26-A62A-63CAD51DB399}.Debug|x86.ActiveCfg = Debug|Any CPU + {E53C49EE-FC70-4B26-A62A-63CAD51DB399}.Debug|x86.Build.0 = Debug|Any CPU {E53C49EE-FC70-4B26-A62A-63CAD51DB399}.Release|Any CPU.ActiveCfg = Release|Any CPU {E53C49EE-FC70-4B26-A62A-63CAD51DB399}.Release|Any CPU.Build.0 = Release|Any CPU + {E53C49EE-FC70-4B26-A62A-63CAD51DB399}.Release|x64.ActiveCfg = Release|Any CPU + {E53C49EE-FC70-4B26-A62A-63CAD51DB399}.Release|x64.Build.0 = Release|Any CPU + {E53C49EE-FC70-4B26-A62A-63CAD51DB399}.Release|x86.ActiveCfg = Release|Any CPU + {E53C49EE-FC70-4B26-A62A-63CAD51DB399}.Release|x86.Build.0 = Release|Any CPU {F749CC16-C32B-441D-9ACE-E8F748E977E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F749CC16-C32B-441D-9ACE-E8F748E977E0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F749CC16-C32B-441D-9ACE-E8F748E977E0}.Debug|x64.ActiveCfg = Debug|Any CPU + {F749CC16-C32B-441D-9ACE-E8F748E977E0}.Debug|x64.Build.0 = Debug|Any CPU + {F749CC16-C32B-441D-9ACE-E8F748E977E0}.Debug|x86.ActiveCfg = Debug|Any CPU + {F749CC16-C32B-441D-9ACE-E8F748E977E0}.Debug|x86.Build.0 = Debug|Any CPU {F749CC16-C32B-441D-9ACE-E8F748E977E0}.Release|Any CPU.ActiveCfg = Release|Any CPU {F749CC16-C32B-441D-9ACE-E8F748E977E0}.Release|Any CPU.Build.0 = Release|Any CPU + {F749CC16-C32B-441D-9ACE-E8F748E977E0}.Release|x64.ActiveCfg = Release|Any CPU + {F749CC16-C32B-441D-9ACE-E8F748E977E0}.Release|x64.Build.0 = Release|Any CPU + {F749CC16-C32B-441D-9ACE-E8F748E977E0}.Release|x86.ActiveCfg = Release|Any CPU + {F749CC16-C32B-441D-9ACE-E8F748E977E0}.Release|x86.Build.0 = Release|Any CPU {609D99ED-3E50-49DF-A3CC-2371FD02520A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {609D99ED-3E50-49DF-A3CC-2371FD02520A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {609D99ED-3E50-49DF-A3CC-2371FD02520A}.Debug|x64.ActiveCfg = Debug|Any CPU + {609D99ED-3E50-49DF-A3CC-2371FD02520A}.Debug|x64.Build.0 = Debug|Any CPU + {609D99ED-3E50-49DF-A3CC-2371FD02520A}.Debug|x86.ActiveCfg = Debug|Any CPU + {609D99ED-3E50-49DF-A3CC-2371FD02520A}.Debug|x86.Build.0 = Debug|Any CPU {609D99ED-3E50-49DF-A3CC-2371FD02520A}.Release|Any CPU.ActiveCfg = Release|Any CPU {609D99ED-3E50-49DF-A3CC-2371FD02520A}.Release|Any CPU.Build.0 = Release|Any CPU + {609D99ED-3E50-49DF-A3CC-2371FD02520A}.Release|x64.ActiveCfg = Release|Any CPU + {609D99ED-3E50-49DF-A3CC-2371FD02520A}.Release|x64.Build.0 = Release|Any CPU + {609D99ED-3E50-49DF-A3CC-2371FD02520A}.Release|x86.ActiveCfg = Release|Any CPU + {609D99ED-3E50-49DF-A3CC-2371FD02520A}.Release|x86.Build.0 = Release|Any CPU {50116F9A-646D-4BF7-9760-66E37CB9C459}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {50116F9A-646D-4BF7-9760-66E37CB9C459}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50116F9A-646D-4BF7-9760-66E37CB9C459}.Debug|x64.ActiveCfg = Debug|Any CPU + {50116F9A-646D-4BF7-9760-66E37CB9C459}.Debug|x64.Build.0 = Debug|Any CPU + {50116F9A-646D-4BF7-9760-66E37CB9C459}.Debug|x86.ActiveCfg = Debug|Any CPU + {50116F9A-646D-4BF7-9760-66E37CB9C459}.Debug|x86.Build.0 = Debug|Any CPU {50116F9A-646D-4BF7-9760-66E37CB9C459}.Release|Any CPU.ActiveCfg = Release|Any CPU {50116F9A-646D-4BF7-9760-66E37CB9C459}.Release|Any CPU.Build.0 = Release|Any CPU + {50116F9A-646D-4BF7-9760-66E37CB9C459}.Release|x64.ActiveCfg = Release|Any CPU + {50116F9A-646D-4BF7-9760-66E37CB9C459}.Release|x64.Build.0 = Release|Any CPU + {50116F9A-646D-4BF7-9760-66E37CB9C459}.Release|x86.ActiveCfg = Release|Any CPU + {50116F9A-646D-4BF7-9760-66E37CB9C459}.Release|x86.Build.0 = Release|Any CPU {1F44075F-ABD6-49A4-8EA1-DBB70304AD24}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1F44075F-ABD6-49A4-8EA1-DBB70304AD24}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1F44075F-ABD6-49A4-8EA1-DBB70304AD24}.Debug|x64.ActiveCfg = Debug|Any CPU + {1F44075F-ABD6-49A4-8EA1-DBB70304AD24}.Debug|x64.Build.0 = Debug|Any CPU + {1F44075F-ABD6-49A4-8EA1-DBB70304AD24}.Debug|x86.ActiveCfg = Debug|Any CPU + {1F44075F-ABD6-49A4-8EA1-DBB70304AD24}.Debug|x86.Build.0 = Debug|Any CPU {1F44075F-ABD6-49A4-8EA1-DBB70304AD24}.Release|Any CPU.ActiveCfg = Release|Any CPU {1F44075F-ABD6-49A4-8EA1-DBB70304AD24}.Release|Any CPU.Build.0 = Release|Any CPU + {1F44075F-ABD6-49A4-8EA1-DBB70304AD24}.Release|x64.ActiveCfg = Release|Any CPU + {1F44075F-ABD6-49A4-8EA1-DBB70304AD24}.Release|x64.Build.0 = Release|Any CPU + {1F44075F-ABD6-49A4-8EA1-DBB70304AD24}.Release|x86.ActiveCfg = Release|Any CPU + {1F44075F-ABD6-49A4-8EA1-DBB70304AD24}.Release|x86.Build.0 = Release|Any CPU {B793249D-3E52-4B0D-964F-BC022CD8A753}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B793249D-3E52-4B0D-964F-BC022CD8A753}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B793249D-3E52-4B0D-964F-BC022CD8A753}.Debug|x64.ActiveCfg = Debug|Any CPU + {B793249D-3E52-4B0D-964F-BC022CD8A753}.Debug|x64.Build.0 = Debug|Any CPU + {B793249D-3E52-4B0D-964F-BC022CD8A753}.Debug|x86.ActiveCfg = Debug|Any CPU + {B793249D-3E52-4B0D-964F-BC022CD8A753}.Debug|x86.Build.0 = Debug|Any CPU {B793249D-3E52-4B0D-964F-BC022CD8A753}.Release|Any CPU.ActiveCfg = Release|Any CPU {B793249D-3E52-4B0D-964F-BC022CD8A753}.Release|Any CPU.Build.0 = Release|Any CPU + {B793249D-3E52-4B0D-964F-BC022CD8A753}.Release|x64.ActiveCfg = Release|Any CPU + {B793249D-3E52-4B0D-964F-BC022CD8A753}.Release|x64.Build.0 = Release|Any CPU + {B793249D-3E52-4B0D-964F-BC022CD8A753}.Release|x86.ActiveCfg = Release|Any CPU + {B793249D-3E52-4B0D-964F-BC022CD8A753}.Release|x86.Build.0 = Release|Any CPU {8FA2FE59-9333-47B8-A226-E2B0CDBB5847}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8FA2FE59-9333-47B8-A226-E2B0CDBB5847}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8FA2FE59-9333-47B8-A226-E2B0CDBB5847}.Debug|x64.ActiveCfg = Debug|Any CPU + {8FA2FE59-9333-47B8-A226-E2B0CDBB5847}.Debug|x64.Build.0 = Debug|Any CPU + {8FA2FE59-9333-47B8-A226-E2B0CDBB5847}.Debug|x86.ActiveCfg = Debug|Any CPU + {8FA2FE59-9333-47B8-A226-E2B0CDBB5847}.Debug|x86.Build.0 = Debug|Any CPU {8FA2FE59-9333-47B8-A226-E2B0CDBB5847}.Release|Any CPU.ActiveCfg = Release|Any CPU {8FA2FE59-9333-47B8-A226-E2B0CDBB5847}.Release|Any CPU.Build.0 = Release|Any CPU + {8FA2FE59-9333-47B8-A226-E2B0CDBB5847}.Release|x64.ActiveCfg = Release|Any CPU + {8FA2FE59-9333-47B8-A226-E2B0CDBB5847}.Release|x64.Build.0 = Release|Any CPU + {8FA2FE59-9333-47B8-A226-E2B0CDBB5847}.Release|x86.ActiveCfg = Release|Any CPU + {8FA2FE59-9333-47B8-A226-E2B0CDBB5847}.Release|x86.Build.0 = Release|Any CPU {0D9861C5-D081-40F7-9DFC-7032840471AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0D9861C5-D081-40F7-9DFC-7032840471AB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0D9861C5-D081-40F7-9DFC-7032840471AB}.Debug|x64.ActiveCfg = Debug|Any CPU + {0D9861C5-D081-40F7-9DFC-7032840471AB}.Debug|x64.Build.0 = Debug|Any CPU + {0D9861C5-D081-40F7-9DFC-7032840471AB}.Debug|x86.ActiveCfg = Debug|Any CPU + {0D9861C5-D081-40F7-9DFC-7032840471AB}.Debug|x86.Build.0 = Debug|Any CPU {0D9861C5-D081-40F7-9DFC-7032840471AB}.Release|Any CPU.ActiveCfg = Release|Any CPU {0D9861C5-D081-40F7-9DFC-7032840471AB}.Release|Any CPU.Build.0 = Release|Any CPU + {0D9861C5-D081-40F7-9DFC-7032840471AB}.Release|x64.ActiveCfg = Release|Any CPU + {0D9861C5-D081-40F7-9DFC-7032840471AB}.Release|x64.Build.0 = Release|Any CPU + {0D9861C5-D081-40F7-9DFC-7032840471AB}.Release|x86.ActiveCfg = Release|Any CPU + {0D9861C5-D081-40F7-9DFC-7032840471AB}.Release|x86.Build.0 = Release|Any CPU {EEBE64CF-B1B5-4C54-B9B4-47ED7E5E760A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EEBE64CF-B1B5-4C54-B9B4-47ED7E5E760A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EEBE64CF-B1B5-4C54-B9B4-47ED7E5E760A}.Debug|x64.ActiveCfg = Debug|Any CPU + {EEBE64CF-B1B5-4C54-B9B4-47ED7E5E760A}.Debug|x64.Build.0 = Debug|Any CPU + {EEBE64CF-B1B5-4C54-B9B4-47ED7E5E760A}.Debug|x86.ActiveCfg = Debug|Any CPU + {EEBE64CF-B1B5-4C54-B9B4-47ED7E5E760A}.Debug|x86.Build.0 = Debug|Any CPU {EEBE64CF-B1B5-4C54-B9B4-47ED7E5E760A}.Release|Any CPU.ActiveCfg = Release|Any CPU {EEBE64CF-B1B5-4C54-B9B4-47ED7E5E760A}.Release|Any CPU.Build.0 = Release|Any CPU + {EEBE64CF-B1B5-4C54-B9B4-47ED7E5E760A}.Release|x64.ActiveCfg = Release|Any CPU + {EEBE64CF-B1B5-4C54-B9B4-47ED7E5E760A}.Release|x64.Build.0 = Release|Any CPU + {EEBE64CF-B1B5-4C54-B9B4-47ED7E5E760A}.Release|x86.ActiveCfg = Release|Any CPU + {EEBE64CF-B1B5-4C54-B9B4-47ED7E5E760A}.Release|x86.Build.0 = Release|Any CPU {9658457F-075C-4C44-A7AD-947365938B6C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9658457F-075C-4C44-A7AD-947365938B6C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9658457F-075C-4C44-A7AD-947365938B6C}.Debug|x64.ActiveCfg = Debug|Any CPU + {9658457F-075C-4C44-A7AD-947365938B6C}.Debug|x64.Build.0 = Debug|Any CPU + {9658457F-075C-4C44-A7AD-947365938B6C}.Debug|x86.ActiveCfg = Debug|Any CPU + {9658457F-075C-4C44-A7AD-947365938B6C}.Debug|x86.Build.0 = Debug|Any CPU {9658457F-075C-4C44-A7AD-947365938B6C}.Release|Any CPU.ActiveCfg = Release|Any CPU {9658457F-075C-4C44-A7AD-947365938B6C}.Release|Any CPU.Build.0 = Release|Any CPU + {9658457F-075C-4C44-A7AD-947365938B6C}.Release|x64.ActiveCfg = Release|Any CPU + {9658457F-075C-4C44-A7AD-947365938B6C}.Release|x64.Build.0 = Release|Any CPU + {9658457F-075C-4C44-A7AD-947365938B6C}.Release|x86.ActiveCfg = Release|Any CPU + {9658457F-075C-4C44-A7AD-947365938B6C}.Release|x86.Build.0 = Release|Any CPU {AB93DF75-C53F-43F7-B1EB-B679240D112B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {AB93DF75-C53F-43F7-B1EB-B679240D112B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AB93DF75-C53F-43F7-B1EB-B679240D112B}.Debug|x64.ActiveCfg = Debug|Any CPU + {AB93DF75-C53F-43F7-B1EB-B679240D112B}.Debug|x64.Build.0 = Debug|Any CPU + {AB93DF75-C53F-43F7-B1EB-B679240D112B}.Debug|x86.ActiveCfg = Debug|Any CPU + {AB93DF75-C53F-43F7-B1EB-B679240D112B}.Debug|x86.Build.0 = Debug|Any CPU {AB93DF75-C53F-43F7-B1EB-B679240D112B}.Release|Any CPU.ActiveCfg = Release|Any CPU {AB93DF75-C53F-43F7-B1EB-B679240D112B}.Release|Any CPU.Build.0 = Release|Any CPU + {AB93DF75-C53F-43F7-B1EB-B679240D112B}.Release|x64.ActiveCfg = Release|Any CPU + {AB93DF75-C53F-43F7-B1EB-B679240D112B}.Release|x64.Build.0 = Release|Any CPU + {AB93DF75-C53F-43F7-B1EB-B679240D112B}.Release|x86.ActiveCfg = Release|Any CPU + {AB93DF75-C53F-43F7-B1EB-B679240D112B}.Release|x86.Build.0 = Release|Any CPU {1E5E7A45-9826-4F06-8C68-B9F1DB05C4F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1E5E7A45-9826-4F06-8C68-B9F1DB05C4F2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1E5E7A45-9826-4F06-8C68-B9F1DB05C4F2}.Debug|x64.ActiveCfg = Debug|Any CPU + {1E5E7A45-9826-4F06-8C68-B9F1DB05C4F2}.Debug|x64.Build.0 = Debug|Any CPU + {1E5E7A45-9826-4F06-8C68-B9F1DB05C4F2}.Debug|x86.ActiveCfg = Debug|Any CPU + {1E5E7A45-9826-4F06-8C68-B9F1DB05C4F2}.Debug|x86.Build.0 = Debug|Any CPU {1E5E7A45-9826-4F06-8C68-B9F1DB05C4F2}.Release|Any CPU.ActiveCfg = Release|Any CPU {1E5E7A45-9826-4F06-8C68-B9F1DB05C4F2}.Release|Any CPU.Build.0 = Release|Any CPU + {1E5E7A45-9826-4F06-8C68-B9F1DB05C4F2}.Release|x64.ActiveCfg = Release|Any CPU + {1E5E7A45-9826-4F06-8C68-B9F1DB05C4F2}.Release|x64.Build.0 = Release|Any CPU + {1E5E7A45-9826-4F06-8C68-B9F1DB05C4F2}.Release|x86.ActiveCfg = Release|Any CPU + {1E5E7A45-9826-4F06-8C68-B9F1DB05C4F2}.Release|x86.Build.0 = Release|Any CPU {D58B814E-1F19-43AE-89D1-769BC7681A56}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D58B814E-1F19-43AE-89D1-769BC7681A56}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D58B814E-1F19-43AE-89D1-769BC7681A56}.Debug|x64.ActiveCfg = Debug|Any CPU + {D58B814E-1F19-43AE-89D1-769BC7681A56}.Debug|x64.Build.0 = Debug|Any CPU + {D58B814E-1F19-43AE-89D1-769BC7681A56}.Debug|x86.ActiveCfg = Debug|Any CPU + {D58B814E-1F19-43AE-89D1-769BC7681A56}.Debug|x86.Build.0 = Debug|Any CPU {D58B814E-1F19-43AE-89D1-769BC7681A56}.Release|Any CPU.ActiveCfg = Release|Any CPU {D58B814E-1F19-43AE-89D1-769BC7681A56}.Release|Any CPU.Build.0 = Release|Any CPU + {D58B814E-1F19-43AE-89D1-769BC7681A56}.Release|x64.ActiveCfg = Release|Any CPU + {D58B814E-1F19-43AE-89D1-769BC7681A56}.Release|x64.Build.0 = Release|Any CPU + {D58B814E-1F19-43AE-89D1-769BC7681A56}.Release|x86.ActiveCfg = Release|Any CPU + {D58B814E-1F19-43AE-89D1-769BC7681A56}.Release|x86.Build.0 = Release|Any CPU {C181C7D4-CA45-4FAE-8315-A9825F034D53}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C181C7D4-CA45-4FAE-8315-A9825F034D53}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C181C7D4-CA45-4FAE-8315-A9825F034D53}.Debug|x64.ActiveCfg = Debug|Any CPU + {C181C7D4-CA45-4FAE-8315-A9825F034D53}.Debug|x64.Build.0 = Debug|Any CPU + {C181C7D4-CA45-4FAE-8315-A9825F034D53}.Debug|x86.ActiveCfg = Debug|Any CPU + {C181C7D4-CA45-4FAE-8315-A9825F034D53}.Debug|x86.Build.0 = Debug|Any CPU {C181C7D4-CA45-4FAE-8315-A9825F034D53}.Release|Any CPU.ActiveCfg = Release|Any CPU {C181C7D4-CA45-4FAE-8315-A9825F034D53}.Release|Any CPU.Build.0 = Release|Any CPU + {C181C7D4-CA45-4FAE-8315-A9825F034D53}.Release|x64.ActiveCfg = Release|Any CPU + {C181C7D4-CA45-4FAE-8315-A9825F034D53}.Release|x64.Build.0 = Release|Any CPU + {C181C7D4-CA45-4FAE-8315-A9825F034D53}.Release|x86.ActiveCfg = Release|Any CPU + {C181C7D4-CA45-4FAE-8315-A9825F034D53}.Release|x86.Build.0 = Release|Any CPU {4056B8FD-F355-41A3-A34F-60B350598B33}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4056B8FD-F355-41A3-A34F-60B350598B33}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4056B8FD-F355-41A3-A34F-60B350598B33}.Debug|x64.ActiveCfg = Debug|Any CPU + {4056B8FD-F355-41A3-A34F-60B350598B33}.Debug|x64.Build.0 = Debug|Any CPU + {4056B8FD-F355-41A3-A34F-60B350598B33}.Debug|x86.ActiveCfg = Debug|Any CPU + {4056B8FD-F355-41A3-A34F-60B350598B33}.Debug|x86.Build.0 = Debug|Any CPU {4056B8FD-F355-41A3-A34F-60B350598B33}.Release|Any CPU.ActiveCfg = Release|Any CPU {4056B8FD-F355-41A3-A34F-60B350598B33}.Release|Any CPU.Build.0 = Release|Any CPU + {4056B8FD-F355-41A3-A34F-60B350598B33}.Release|x64.ActiveCfg = Release|Any CPU + {4056B8FD-F355-41A3-A34F-60B350598B33}.Release|x64.Build.0 = Release|Any CPU + {4056B8FD-F355-41A3-A34F-60B350598B33}.Release|x86.ActiveCfg = Release|Any CPU + {4056B8FD-F355-41A3-A34F-60B350598B33}.Release|x86.Build.0 = Release|Any CPU {6D383054-4712-4D03-A0B1-B9D4E1CC6F95}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6D383054-4712-4D03-A0B1-B9D4E1CC6F95}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6D383054-4712-4D03-A0B1-B9D4E1CC6F95}.Debug|x64.ActiveCfg = Debug|Any CPU + {6D383054-4712-4D03-A0B1-B9D4E1CC6F95}.Debug|x64.Build.0 = Debug|Any CPU + {6D383054-4712-4D03-A0B1-B9D4E1CC6F95}.Debug|x86.ActiveCfg = Debug|Any CPU + {6D383054-4712-4D03-A0B1-B9D4E1CC6F95}.Debug|x86.Build.0 = Debug|Any CPU {6D383054-4712-4D03-A0B1-B9D4E1CC6F95}.Release|Any CPU.ActiveCfg = Release|Any CPU {6D383054-4712-4D03-A0B1-B9D4E1CC6F95}.Release|Any CPU.Build.0 = Release|Any CPU + {6D383054-4712-4D03-A0B1-B9D4E1CC6F95}.Release|x64.ActiveCfg = Release|Any CPU + {6D383054-4712-4D03-A0B1-B9D4E1CC6F95}.Release|x64.Build.0 = Release|Any CPU + {6D383054-4712-4D03-A0B1-B9D4E1CC6F95}.Release|x86.ActiveCfg = Release|Any CPU + {6D383054-4712-4D03-A0B1-B9D4E1CC6F95}.Release|x86.Build.0 = Release|Any CPU {4E0DC405-C372-4396-A5DF-F6AA108DA01C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4E0DC405-C372-4396-A5DF-F6AA108DA01C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4E0DC405-C372-4396-A5DF-F6AA108DA01C}.Debug|x64.ActiveCfg = Debug|Any CPU + {4E0DC405-C372-4396-A5DF-F6AA108DA01C}.Debug|x64.Build.0 = Debug|Any CPU + {4E0DC405-C372-4396-A5DF-F6AA108DA01C}.Debug|x86.ActiveCfg = Debug|Any CPU + {4E0DC405-C372-4396-A5DF-F6AA108DA01C}.Debug|x86.Build.0 = Debug|Any CPU {4E0DC405-C372-4396-A5DF-F6AA108DA01C}.Release|Any CPU.ActiveCfg = Release|Any CPU {4E0DC405-C372-4396-A5DF-F6AA108DA01C}.Release|Any CPU.Build.0 = Release|Any CPU + {4E0DC405-C372-4396-A5DF-F6AA108DA01C}.Release|x64.ActiveCfg = Release|Any CPU + {4E0DC405-C372-4396-A5DF-F6AA108DA01C}.Release|x64.Build.0 = Release|Any CPU + {4E0DC405-C372-4396-A5DF-F6AA108DA01C}.Release|x86.ActiveCfg = Release|Any CPU + {4E0DC405-C372-4396-A5DF-F6AA108DA01C}.Release|x86.Build.0 = Release|Any CPU {9B175EC8-6B64-4345-A158-091CB8876077}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9B175EC8-6B64-4345-A158-091CB8876077}.Debug|Any CPU.Build.0 = Debug|Any CPU {9B175EC8-6B64-4345-A158-091CB8876077}.Debug|Any CPU.Deploy.0 = Debug|Any CPU + {9B175EC8-6B64-4345-A158-091CB8876077}.Debug|x64.ActiveCfg = Debug|Any CPU + {9B175EC8-6B64-4345-A158-091CB8876077}.Debug|x64.Build.0 = Debug|Any CPU + {9B175EC8-6B64-4345-A158-091CB8876077}.Debug|x86.ActiveCfg = Debug|Any CPU + {9B175EC8-6B64-4345-A158-091CB8876077}.Debug|x86.Build.0 = Debug|Any CPU {9B175EC8-6B64-4345-A158-091CB8876077}.Release|Any CPU.ActiveCfg = Release|Any CPU {9B175EC8-6B64-4345-A158-091CB8876077}.Release|Any CPU.Build.0 = Release|Any CPU + {9B175EC8-6B64-4345-A158-091CB8876077}.Release|x64.ActiveCfg = Release|Any CPU + {9B175EC8-6B64-4345-A158-091CB8876077}.Release|x64.Build.0 = Release|Any CPU + {9B175EC8-6B64-4345-A158-091CB8876077}.Release|x86.ActiveCfg = Release|Any CPU + {9B175EC8-6B64-4345-A158-091CB8876077}.Release|x86.Build.0 = Release|Any CPU {EE0DC846-52F3-46AF-BC0D-DEF81150CEC0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EE0DC846-52F3-46AF-BC0D-DEF81150CEC0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EE0DC846-52F3-46AF-BC0D-DEF81150CEC0}.Debug|x64.ActiveCfg = Debug|Any CPU + {EE0DC846-52F3-46AF-BC0D-DEF81150CEC0}.Debug|x64.Build.0 = Debug|Any CPU + {EE0DC846-52F3-46AF-BC0D-DEF81150CEC0}.Debug|x86.ActiveCfg = Debug|Any CPU + {EE0DC846-52F3-46AF-BC0D-DEF81150CEC0}.Debug|x86.Build.0 = Debug|Any CPU {EE0DC846-52F3-46AF-BC0D-DEF81150CEC0}.Release|Any CPU.ActiveCfg = Release|Any CPU {EE0DC846-52F3-46AF-BC0D-DEF81150CEC0}.Release|Any CPU.Build.0 = Release|Any CPU + {EE0DC846-52F3-46AF-BC0D-DEF81150CEC0}.Release|x64.ActiveCfg = Release|Any CPU + {EE0DC846-52F3-46AF-BC0D-DEF81150CEC0}.Release|x64.Build.0 = Release|Any CPU + {EE0DC846-52F3-46AF-BC0D-DEF81150CEC0}.Release|x86.ActiveCfg = Release|Any CPU + {EE0DC846-52F3-46AF-BC0D-DEF81150CEC0}.Release|x86.Build.0 = Release|Any CPU {E37818EC-03D2-4B97-BE46-4C3F61465004}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E37818EC-03D2-4B97-BE46-4C3F61465004}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E37818EC-03D2-4B97-BE46-4C3F61465004}.Debug|x64.ActiveCfg = Debug|Any CPU + {E37818EC-03D2-4B97-BE46-4C3F61465004}.Debug|x64.Build.0 = Debug|Any CPU + {E37818EC-03D2-4B97-BE46-4C3F61465004}.Debug|x86.ActiveCfg = Debug|Any CPU + {E37818EC-03D2-4B97-BE46-4C3F61465004}.Debug|x86.Build.0 = Debug|Any CPU {E37818EC-03D2-4B97-BE46-4C3F61465004}.Release|Any CPU.ActiveCfg = Release|Any CPU {E37818EC-03D2-4B97-BE46-4C3F61465004}.Release|Any CPU.Build.0 = Release|Any CPU + {E37818EC-03D2-4B97-BE46-4C3F61465004}.Release|x64.ActiveCfg = Release|Any CPU + {E37818EC-03D2-4B97-BE46-4C3F61465004}.Release|x64.Build.0 = Release|Any CPU + {E37818EC-03D2-4B97-BE46-4C3F61465004}.Release|x86.ActiveCfg = Release|Any CPU + {E37818EC-03D2-4B97-BE46-4C3F61465004}.Release|x86.Build.0 = Release|Any CPU {E5141EAC-9420-48EA-995F-848CBBF0BD4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E5141EAC-9420-48EA-995F-848CBBF0BD4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E5141EAC-9420-48EA-995F-848CBBF0BD4B}.Debug|x64.ActiveCfg = Debug|Any CPU + {E5141EAC-9420-48EA-995F-848CBBF0BD4B}.Debug|x64.Build.0 = Debug|Any CPU + {E5141EAC-9420-48EA-995F-848CBBF0BD4B}.Debug|x86.ActiveCfg = Debug|Any CPU + {E5141EAC-9420-48EA-995F-848CBBF0BD4B}.Debug|x86.Build.0 = Debug|Any CPU {E5141EAC-9420-48EA-995F-848CBBF0BD4B}.Release|Any CPU.ActiveCfg = Release|Any CPU {E5141EAC-9420-48EA-995F-848CBBF0BD4B}.Release|Any CPU.Build.0 = Release|Any CPU + {E5141EAC-9420-48EA-995F-848CBBF0BD4B}.Release|x64.ActiveCfg = Release|Any CPU + {E5141EAC-9420-48EA-995F-848CBBF0BD4B}.Release|x64.Build.0 = Release|Any CPU + {E5141EAC-9420-48EA-995F-848CBBF0BD4B}.Release|x86.ActiveCfg = Release|Any CPU + {E5141EAC-9420-48EA-995F-848CBBF0BD4B}.Release|x86.Build.0 = Release|Any CPU {AA98FD8D-1254-4B34-840C-06BB263933DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {AA98FD8D-1254-4B34-840C-06BB263933DE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AA98FD8D-1254-4B34-840C-06BB263933DE}.Debug|x64.ActiveCfg = Debug|Any CPU + {AA98FD8D-1254-4B34-840C-06BB263933DE}.Debug|x64.Build.0 = Debug|Any CPU + {AA98FD8D-1254-4B34-840C-06BB263933DE}.Debug|x86.ActiveCfg = Debug|Any CPU + {AA98FD8D-1254-4B34-840C-06BB263933DE}.Debug|x86.Build.0 = Debug|Any CPU {AA98FD8D-1254-4B34-840C-06BB263933DE}.Release|Any CPU.ActiveCfg = Release|Any CPU {AA98FD8D-1254-4B34-840C-06BB263933DE}.Release|Any CPU.Build.0 = Release|Any CPU + {AA98FD8D-1254-4B34-840C-06BB263933DE}.Release|x64.ActiveCfg = Release|Any CPU + {AA98FD8D-1254-4B34-840C-06BB263933DE}.Release|x64.Build.0 = Release|Any CPU + {AA98FD8D-1254-4B34-840C-06BB263933DE}.Release|x86.ActiveCfg = Release|Any CPU + {AA98FD8D-1254-4B34-840C-06BB263933DE}.Release|x86.Build.0 = Release|Any CPU {20386BBE-1F55-4503-9F5F-F2C6B29DE865}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {20386BBE-1F55-4503-9F5F-F2C6B29DE865}.Debug|Any CPU.Build.0 = Debug|Any CPU + {20386BBE-1F55-4503-9F5F-F2C6B29DE865}.Debug|x64.ActiveCfg = Debug|Any CPU + {20386BBE-1F55-4503-9F5F-F2C6B29DE865}.Debug|x64.Build.0 = Debug|Any CPU + {20386BBE-1F55-4503-9F5F-F2C6B29DE865}.Debug|x86.ActiveCfg = Debug|Any CPU + {20386BBE-1F55-4503-9F5F-F2C6B29DE865}.Debug|x86.Build.0 = Debug|Any CPU {20386BBE-1F55-4503-9F5F-F2C6B29DE865}.Release|Any CPU.ActiveCfg = Release|Any CPU {20386BBE-1F55-4503-9F5F-F2C6B29DE865}.Release|Any CPU.Build.0 = Release|Any CPU + {20386BBE-1F55-4503-9F5F-F2C6B29DE865}.Release|x64.ActiveCfg = Release|Any CPU + {20386BBE-1F55-4503-9F5F-F2C6B29DE865}.Release|x64.Build.0 = Release|Any CPU + {20386BBE-1F55-4503-9F5F-F2C6B29DE865}.Release|x86.ActiveCfg = Release|Any CPU + {20386BBE-1F55-4503-9F5F-F2C6B29DE865}.Release|x86.Build.0 = Release|Any CPU {A7F651AD-51D3-4473-9641-7C76D2A0E3B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A7F651AD-51D3-4473-9641-7C76D2A0E3B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A7F651AD-51D3-4473-9641-7C76D2A0E3B7}.Debug|x64.ActiveCfg = Debug|Any CPU + {A7F651AD-51D3-4473-9641-7C76D2A0E3B7}.Debug|x64.Build.0 = Debug|Any CPU + {A7F651AD-51D3-4473-9641-7C76D2A0E3B7}.Debug|x86.ActiveCfg = Debug|Any CPU + {A7F651AD-51D3-4473-9641-7C76D2A0E3B7}.Debug|x86.Build.0 = Debug|Any CPU {A7F651AD-51D3-4473-9641-7C76D2A0E3B7}.Release|Any CPU.ActiveCfg = Release|Any CPU {A7F651AD-51D3-4473-9641-7C76D2A0E3B7}.Release|Any CPU.Build.0 = Release|Any CPU + {A7F651AD-51D3-4473-9641-7C76D2A0E3B7}.Release|x64.ActiveCfg = Release|Any CPU + {A7F651AD-51D3-4473-9641-7C76D2A0E3B7}.Release|x64.Build.0 = Release|Any CPU + {A7F651AD-51D3-4473-9641-7C76D2A0E3B7}.Release|x86.ActiveCfg = Release|Any CPU + {A7F651AD-51D3-4473-9641-7C76D2A0E3B7}.Release|x86.Build.0 = Release|Any CPU {DF785142-3E65-4176-8A6E-CAAD6BBF771F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DF785142-3E65-4176-8A6E-CAAD6BBF771F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DF785142-3E65-4176-8A6E-CAAD6BBF771F}.Debug|x64.ActiveCfg = Debug|Any CPU + {DF785142-3E65-4176-8A6E-CAAD6BBF771F}.Debug|x64.Build.0 = Debug|Any CPU + {DF785142-3E65-4176-8A6E-CAAD6BBF771F}.Debug|x86.ActiveCfg = Debug|Any CPU + {DF785142-3E65-4176-8A6E-CAAD6BBF771F}.Debug|x86.Build.0 = Debug|Any CPU {DF785142-3E65-4176-8A6E-CAAD6BBF771F}.Release|Any CPU.ActiveCfg = Release|Any CPU {DF785142-3E65-4176-8A6E-CAAD6BBF771F}.Release|Any CPU.Build.0 = Release|Any CPU + {DF785142-3E65-4176-8A6E-CAAD6BBF771F}.Release|x64.ActiveCfg = Release|Any CPU + {DF785142-3E65-4176-8A6E-CAAD6BBF771F}.Release|x64.Build.0 = Release|Any CPU + {DF785142-3E65-4176-8A6E-CAAD6BBF771F}.Release|x86.ActiveCfg = Release|Any CPU + {DF785142-3E65-4176-8A6E-CAAD6BBF771F}.Release|x86.Build.0 = Release|Any CPU {4B323BCC-0E8D-43CE-9AC2-4B708278C7C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4B323BCC-0E8D-43CE-9AC2-4B708278C7C2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4B323BCC-0E8D-43CE-9AC2-4B708278C7C2}.Debug|x64.ActiveCfg = Debug|Any CPU + {4B323BCC-0E8D-43CE-9AC2-4B708278C7C2}.Debug|x64.Build.0 = Debug|Any CPU + {4B323BCC-0E8D-43CE-9AC2-4B708278C7C2}.Debug|x86.ActiveCfg = Debug|Any CPU + {4B323BCC-0E8D-43CE-9AC2-4B708278C7C2}.Debug|x86.Build.0 = Debug|Any CPU {4B323BCC-0E8D-43CE-9AC2-4B708278C7C2}.Release|Any CPU.ActiveCfg = Release|Any CPU {4B323BCC-0E8D-43CE-9AC2-4B708278C7C2}.Release|Any CPU.Build.0 = Release|Any CPU + {4B323BCC-0E8D-43CE-9AC2-4B708278C7C2}.Release|x64.ActiveCfg = Release|Any CPU + {4B323BCC-0E8D-43CE-9AC2-4B708278C7C2}.Release|x64.Build.0 = Release|Any CPU + {4B323BCC-0E8D-43CE-9AC2-4B708278C7C2}.Release|x86.ActiveCfg = Release|Any CPU + {4B323BCC-0E8D-43CE-9AC2-4B708278C7C2}.Release|x86.Build.0 = Release|Any CPU {52262FBB-40D0-4F08-B00F-B298185FF6BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {52262FBB-40D0-4F08-B00F-B298185FF6BD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {52262FBB-40D0-4F08-B00F-B298185FF6BD}.Debug|x64.ActiveCfg = Debug|Any CPU + {52262FBB-40D0-4F08-B00F-B298185FF6BD}.Debug|x64.Build.0 = Debug|Any CPU + {52262FBB-40D0-4F08-B00F-B298185FF6BD}.Debug|x86.ActiveCfg = Debug|Any CPU + {52262FBB-40D0-4F08-B00F-B298185FF6BD}.Debug|x86.Build.0 = Debug|Any CPU {52262FBB-40D0-4F08-B00F-B298185FF6BD}.Release|Any CPU.ActiveCfg = Release|Any CPU {52262FBB-40D0-4F08-B00F-B298185FF6BD}.Release|Any CPU.Build.0 = Release|Any CPU + {52262FBB-40D0-4F08-B00F-B298185FF6BD}.Release|x64.ActiveCfg = Release|Any CPU + {52262FBB-40D0-4F08-B00F-B298185FF6BD}.Release|x64.Build.0 = Release|Any CPU + {52262FBB-40D0-4F08-B00F-B298185FF6BD}.Release|x86.ActiveCfg = Release|Any CPU + {52262FBB-40D0-4F08-B00F-B298185FF6BD}.Release|x86.Build.0 = Release|Any CPU {759D9E53-4717-491D-9970-B3A3367C65E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {759D9E53-4717-491D-9970-B3A3367C65E7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {759D9E53-4717-491D-9970-B3A3367C65E7}.Debug|x64.ActiveCfg = Debug|Any CPU + {759D9E53-4717-491D-9970-B3A3367C65E7}.Debug|x64.Build.0 = Debug|Any CPU + {759D9E53-4717-491D-9970-B3A3367C65E7}.Debug|x86.ActiveCfg = Debug|Any CPU + {759D9E53-4717-491D-9970-B3A3367C65E7}.Debug|x86.Build.0 = Debug|Any CPU {759D9E53-4717-491D-9970-B3A3367C65E7}.Release|Any CPU.ActiveCfg = Release|Any CPU {759D9E53-4717-491D-9970-B3A3367C65E7}.Release|Any CPU.Build.0 = Release|Any CPU + {759D9E53-4717-491D-9970-B3A3367C65E7}.Release|x64.ActiveCfg = Release|Any CPU + {759D9E53-4717-491D-9970-B3A3367C65E7}.Release|x64.Build.0 = Release|Any CPU + {759D9E53-4717-491D-9970-B3A3367C65E7}.Release|x86.ActiveCfg = Release|Any CPU + {759D9E53-4717-491D-9970-B3A3367C65E7}.Release|x86.Build.0 = Release|Any CPU {33336B98-A69E-4F28-A71A-1D8753F3BA24}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {33336B98-A69E-4F28-A71A-1D8753F3BA24}.Debug|Any CPU.Build.0 = Debug|Any CPU + {33336B98-A69E-4F28-A71A-1D8753F3BA24}.Debug|x64.ActiveCfg = Debug|Any CPU + {33336B98-A69E-4F28-A71A-1D8753F3BA24}.Debug|x64.Build.0 = Debug|Any CPU + {33336B98-A69E-4F28-A71A-1D8753F3BA24}.Debug|x86.ActiveCfg = Debug|Any CPU + {33336B98-A69E-4F28-A71A-1D8753F3BA24}.Debug|x86.Build.0 = Debug|Any CPU {33336B98-A69E-4F28-A71A-1D8753F3BA24}.Release|Any CPU.ActiveCfg = Release|Any CPU {33336B98-A69E-4F28-A71A-1D8753F3BA24}.Release|Any CPU.Build.0 = Release|Any CPU + {33336B98-A69E-4F28-A71A-1D8753F3BA24}.Release|x64.ActiveCfg = Release|Any CPU + {33336B98-A69E-4F28-A71A-1D8753F3BA24}.Release|x64.Build.0 = Release|Any CPU + {33336B98-A69E-4F28-A71A-1D8753F3BA24}.Release|x86.ActiveCfg = Release|Any CPU + {33336B98-A69E-4F28-A71A-1D8753F3BA24}.Release|x86.Build.0 = Release|Any CPU {22E4E3F5-8D6E-433D-9456-F60DFB1EFC82}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {22E4E3F5-8D6E-433D-9456-F60DFB1EFC82}.Debug|Any CPU.Build.0 = Debug|Any CPU + {22E4E3F5-8D6E-433D-9456-F60DFB1EFC82}.Debug|x64.ActiveCfg = Debug|Any CPU + {22E4E3F5-8D6E-433D-9456-F60DFB1EFC82}.Debug|x64.Build.0 = Debug|Any CPU + {22E4E3F5-8D6E-433D-9456-F60DFB1EFC82}.Debug|x86.ActiveCfg = Debug|Any CPU + {22E4E3F5-8D6E-433D-9456-F60DFB1EFC82}.Debug|x86.Build.0 = Debug|Any CPU {22E4E3F5-8D6E-433D-9456-F60DFB1EFC82}.Release|Any CPU.ActiveCfg = Release|Any CPU {22E4E3F5-8D6E-433D-9456-F60DFB1EFC82}.Release|Any CPU.Build.0 = Release|Any CPU + {22E4E3F5-8D6E-433D-9456-F60DFB1EFC82}.Release|x64.ActiveCfg = Release|Any CPU + {22E4E3F5-8D6E-433D-9456-F60DFB1EFC82}.Release|x64.Build.0 = Release|Any CPU + {22E4E3F5-8D6E-433D-9456-F60DFB1EFC82}.Release|x86.ActiveCfg = Release|Any CPU + {22E4E3F5-8D6E-433D-9456-F60DFB1EFC82}.Release|x86.Build.0 = Release|Any CPU {2AEC3A6E-886F-43DA-8EA6-82EBF916E89A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2AEC3A6E-886F-43DA-8EA6-82EBF916E89A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2AEC3A6E-886F-43DA-8EA6-82EBF916E89A}.Debug|x64.ActiveCfg = Debug|Any CPU + {2AEC3A6E-886F-43DA-8EA6-82EBF916E89A}.Debug|x64.Build.0 = Debug|Any CPU + {2AEC3A6E-886F-43DA-8EA6-82EBF916E89A}.Debug|x86.ActiveCfg = Debug|Any CPU + {2AEC3A6E-886F-43DA-8EA6-82EBF916E89A}.Debug|x86.Build.0 = Debug|Any CPU {2AEC3A6E-886F-43DA-8EA6-82EBF916E89A}.Release|Any CPU.ActiveCfg = Release|Any CPU {2AEC3A6E-886F-43DA-8EA6-82EBF916E89A}.Release|Any CPU.Build.0 = Release|Any CPU + {2AEC3A6E-886F-43DA-8EA6-82EBF916E89A}.Release|x64.ActiveCfg = Release|Any CPU + {2AEC3A6E-886F-43DA-8EA6-82EBF916E89A}.Release|x64.Build.0 = Release|Any CPU + {2AEC3A6E-886F-43DA-8EA6-82EBF916E89A}.Release|x86.ActiveCfg = Release|Any CPU + {2AEC3A6E-886F-43DA-8EA6-82EBF916E89A}.Release|x86.Build.0 = Release|Any CPU {F97EE360-3733-4993-823A-A81D004D417E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F97EE360-3733-4993-823A-A81D004D417E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F97EE360-3733-4993-823A-A81D004D417E}.Debug|x64.ActiveCfg = Debug|Any CPU + {F97EE360-3733-4993-823A-A81D004D417E}.Debug|x64.Build.0 = Debug|Any CPU + {F97EE360-3733-4993-823A-A81D004D417E}.Debug|x86.ActiveCfg = Debug|Any CPU + {F97EE360-3733-4993-823A-A81D004D417E}.Debug|x86.Build.0 = Debug|Any CPU {F97EE360-3733-4993-823A-A81D004D417E}.Release|Any CPU.ActiveCfg = Release|Any CPU {F97EE360-3733-4993-823A-A81D004D417E}.Release|Any CPU.Build.0 = Release|Any CPU + {F97EE360-3733-4993-823A-A81D004D417E}.Release|x64.ActiveCfg = Release|Any CPU + {F97EE360-3733-4993-823A-A81D004D417E}.Release|x64.Build.0 = Release|Any CPU + {F97EE360-3733-4993-823A-A81D004D417E}.Release|x86.ActiveCfg = Release|Any CPU + {F97EE360-3733-4993-823A-A81D004D417E}.Release|x86.Build.0 = Release|Any CPU {1D66CB6F-6268-4595-AD49-09DFD1784DDB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1D66CB6F-6268-4595-AD49-09DFD1784DDB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1D66CB6F-6268-4595-AD49-09DFD1784DDB}.Debug|x64.ActiveCfg = Debug|Any CPU + {1D66CB6F-6268-4595-AD49-09DFD1784DDB}.Debug|x64.Build.0 = Debug|Any CPU + {1D66CB6F-6268-4595-AD49-09DFD1784DDB}.Debug|x86.ActiveCfg = Debug|Any CPU + {1D66CB6F-6268-4595-AD49-09DFD1784DDB}.Debug|x86.Build.0 = Debug|Any CPU {1D66CB6F-6268-4595-AD49-09DFD1784DDB}.Release|Any CPU.ActiveCfg = Release|Any CPU {1D66CB6F-6268-4595-AD49-09DFD1784DDB}.Release|Any CPU.Build.0 = Release|Any CPU + {1D66CB6F-6268-4595-AD49-09DFD1784DDB}.Release|x64.ActiveCfg = Release|Any CPU + {1D66CB6F-6268-4595-AD49-09DFD1784DDB}.Release|x64.Build.0 = Release|Any CPU + {1D66CB6F-6268-4595-AD49-09DFD1784DDB}.Release|x86.ActiveCfg = Release|Any CPU + {1D66CB6F-6268-4595-AD49-09DFD1784DDB}.Release|x86.Build.0 = Release|Any CPU {EA4BB6FE-57B5-4A2B-88AD-FCCD96ECE19F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EA4BB6FE-57B5-4A2B-88AD-FCCD96ECE19F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EA4BB6FE-57B5-4A2B-88AD-FCCD96ECE19F}.Debug|x64.ActiveCfg = Debug|Any CPU + {EA4BB6FE-57B5-4A2B-88AD-FCCD96ECE19F}.Debug|x64.Build.0 = Debug|Any CPU + {EA4BB6FE-57B5-4A2B-88AD-FCCD96ECE19F}.Debug|x86.ActiveCfg = Debug|Any CPU + {EA4BB6FE-57B5-4A2B-88AD-FCCD96ECE19F}.Debug|x86.Build.0 = Debug|Any CPU {EA4BB6FE-57B5-4A2B-88AD-FCCD96ECE19F}.Release|Any CPU.ActiveCfg = Release|Any CPU {EA4BB6FE-57B5-4A2B-88AD-FCCD96ECE19F}.Release|Any CPU.Build.0 = Release|Any CPU + {EA4BB6FE-57B5-4A2B-88AD-FCCD96ECE19F}.Release|x64.ActiveCfg = Release|Any CPU + {EA4BB6FE-57B5-4A2B-88AD-FCCD96ECE19F}.Release|x64.Build.0 = Release|Any CPU + {EA4BB6FE-57B5-4A2B-88AD-FCCD96ECE19F}.Release|x86.ActiveCfg = Release|Any CPU + {EA4BB6FE-57B5-4A2B-88AD-FCCD96ECE19F}.Release|x86.Build.0 = Release|Any CPU {DC89F322-155F-488B-8B80-3824B433F046}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DC89F322-155F-488B-8B80-3824B433F046}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DC89F322-155F-488B-8B80-3824B433F046}.Debug|x64.ActiveCfg = Debug|Any CPU + {DC89F322-155F-488B-8B80-3824B433F046}.Debug|x64.Build.0 = Debug|Any CPU + {DC89F322-155F-488B-8B80-3824B433F046}.Debug|x86.ActiveCfg = Debug|Any CPU + {DC89F322-155F-488B-8B80-3824B433F046}.Debug|x86.Build.0 = Debug|Any CPU {DC89F322-155F-488B-8B80-3824B433F046}.Release|Any CPU.ActiveCfg = Release|Any CPU {DC89F322-155F-488B-8B80-3824B433F046}.Release|Any CPU.Build.0 = Release|Any CPU + {DC89F322-155F-488B-8B80-3824B433F046}.Release|x64.ActiveCfg = Release|Any CPU + {DC89F322-155F-488B-8B80-3824B433F046}.Release|x64.Build.0 = Release|Any CPU + {DC89F322-155F-488B-8B80-3824B433F046}.Release|x86.ActiveCfg = Release|Any CPU + {DC89F322-155F-488B-8B80-3824B433F046}.Release|x86.Build.0 = Release|Any CPU {4E74CAAD-062F-4AF9-8B29-2ED4B2CC0E77}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4E74CAAD-062F-4AF9-8B29-2ED4B2CC0E77}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4E74CAAD-062F-4AF9-8B29-2ED4B2CC0E77}.Debug|x64.ActiveCfg = Debug|Any CPU + {4E74CAAD-062F-4AF9-8B29-2ED4B2CC0E77}.Debug|x64.Build.0 = Debug|Any CPU + {4E74CAAD-062F-4AF9-8B29-2ED4B2CC0E77}.Debug|x86.ActiveCfg = Debug|Any CPU + {4E74CAAD-062F-4AF9-8B29-2ED4B2CC0E77}.Debug|x86.Build.0 = Debug|Any CPU {4E74CAAD-062F-4AF9-8B29-2ED4B2CC0E77}.Release|Any CPU.ActiveCfg = Release|Any CPU {4E74CAAD-062F-4AF9-8B29-2ED4B2CC0E77}.Release|Any CPU.Build.0 = Release|Any CPU + {4E74CAAD-062F-4AF9-8B29-2ED4B2CC0E77}.Release|x64.ActiveCfg = Release|Any CPU + {4E74CAAD-062F-4AF9-8B29-2ED4B2CC0E77}.Release|x64.Build.0 = Release|Any CPU + {4E74CAAD-062F-4AF9-8B29-2ED4B2CC0E77}.Release|x86.ActiveCfg = Release|Any CPU + {4E74CAAD-062F-4AF9-8B29-2ED4B2CC0E77}.Release|x86.Build.0 = Release|Any CPU {957DFFC0-0FA3-48CC-AEDD-F9BBC3F0FADC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {957DFFC0-0FA3-48CC-AEDD-F9BBC3F0FADC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {957DFFC0-0FA3-48CC-AEDD-F9BBC3F0FADC}.Debug|x64.ActiveCfg = Debug|Any CPU + {957DFFC0-0FA3-48CC-AEDD-F9BBC3F0FADC}.Debug|x64.Build.0 = Debug|Any CPU + {957DFFC0-0FA3-48CC-AEDD-F9BBC3F0FADC}.Debug|x86.ActiveCfg = Debug|Any CPU + {957DFFC0-0FA3-48CC-AEDD-F9BBC3F0FADC}.Debug|x86.Build.0 = Debug|Any CPU {957DFFC0-0FA3-48CC-AEDD-F9BBC3F0FADC}.Release|Any CPU.ActiveCfg = Release|Any CPU {957DFFC0-0FA3-48CC-AEDD-F9BBC3F0FADC}.Release|Any CPU.Build.0 = Release|Any CPU + {957DFFC0-0FA3-48CC-AEDD-F9BBC3F0FADC}.Release|x64.ActiveCfg = Release|Any CPU + {957DFFC0-0FA3-48CC-AEDD-F9BBC3F0FADC}.Release|x64.Build.0 = Release|Any CPU + {957DFFC0-0FA3-48CC-AEDD-F9BBC3F0FADC}.Release|x86.ActiveCfg = Release|Any CPU + {957DFFC0-0FA3-48CC-AEDD-F9BBC3F0FADC}.Release|x86.Build.0 = Release|Any CPU {2E239FE2-F3B2-41B6-AE9E-561F6ACE5BA6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2E239FE2-F3B2-41B6-AE9E-561F6ACE5BA6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2E239FE2-F3B2-41B6-AE9E-561F6ACE5BA6}.Debug|x64.ActiveCfg = Debug|Any CPU + {2E239FE2-F3B2-41B6-AE9E-561F6ACE5BA6}.Debug|x64.Build.0 = Debug|Any CPU + {2E239FE2-F3B2-41B6-AE9E-561F6ACE5BA6}.Debug|x86.ActiveCfg = Debug|Any CPU + {2E239FE2-F3B2-41B6-AE9E-561F6ACE5BA6}.Debug|x86.Build.0 = Debug|Any CPU {2E239FE2-F3B2-41B6-AE9E-561F6ACE5BA6}.Release|Any CPU.ActiveCfg = Release|Any CPU {2E239FE2-F3B2-41B6-AE9E-561F6ACE5BA6}.Release|Any CPU.Build.0 = Release|Any CPU + {2E239FE2-F3B2-41B6-AE9E-561F6ACE5BA6}.Release|x64.ActiveCfg = Release|Any CPU + {2E239FE2-F3B2-41B6-AE9E-561F6ACE5BA6}.Release|x64.Build.0 = Release|Any CPU + {2E239FE2-F3B2-41B6-AE9E-561F6ACE5BA6}.Release|x86.ActiveCfg = Release|Any CPU + {2E239FE2-F3B2-41B6-AE9E-561F6ACE5BA6}.Release|x86.Build.0 = Release|Any CPU {854EBD1B-73EE-4B93-89DF-E70436FF14CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {854EBD1B-73EE-4B93-89DF-E70436FF14CB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {854EBD1B-73EE-4B93-89DF-E70436FF14CB}.Debug|x64.ActiveCfg = Debug|Any CPU + {854EBD1B-73EE-4B93-89DF-E70436FF14CB}.Debug|x64.Build.0 = Debug|Any CPU + {854EBD1B-73EE-4B93-89DF-E70436FF14CB}.Debug|x86.ActiveCfg = Debug|Any CPU + {854EBD1B-73EE-4B93-89DF-E70436FF14CB}.Debug|x86.Build.0 = Debug|Any CPU {854EBD1B-73EE-4B93-89DF-E70436FF14CB}.Release|Any CPU.ActiveCfg = Release|Any CPU {854EBD1B-73EE-4B93-89DF-E70436FF14CB}.Release|Any CPU.Build.0 = Release|Any CPU + {854EBD1B-73EE-4B93-89DF-E70436FF14CB}.Release|x64.ActiveCfg = Release|Any CPU + {854EBD1B-73EE-4B93-89DF-E70436FF14CB}.Release|x64.Build.0 = Release|Any CPU + {854EBD1B-73EE-4B93-89DF-E70436FF14CB}.Release|x86.ActiveCfg = Release|Any CPU + {854EBD1B-73EE-4B93-89DF-E70436FF14CB}.Release|x86.Build.0 = Release|Any CPU {5F253D7F-BF27-46F5-9382-73A66EC247BF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5F253D7F-BF27-46F5-9382-73A66EC247BF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5F253D7F-BF27-46F5-9382-73A66EC247BF}.Debug|x64.ActiveCfg = Debug|Any CPU + {5F253D7F-BF27-46F5-9382-73A66EC247BF}.Debug|x64.Build.0 = Debug|Any CPU + {5F253D7F-BF27-46F5-9382-73A66EC247BF}.Debug|x86.ActiveCfg = Debug|Any CPU + {5F253D7F-BF27-46F5-9382-73A66EC247BF}.Debug|x86.Build.0 = Debug|Any CPU {5F253D7F-BF27-46F5-9382-73A66EC247BF}.Release|Any CPU.ActiveCfg = Release|Any CPU {5F253D7F-BF27-46F5-9382-73A66EC247BF}.Release|Any CPU.Build.0 = Release|Any CPU + {5F253D7F-BF27-46F5-9382-73A66EC247BF}.Release|x64.ActiveCfg = Release|Any CPU + {5F253D7F-BF27-46F5-9382-73A66EC247BF}.Release|x64.Build.0 = Release|Any CPU + {5F253D7F-BF27-46F5-9382-73A66EC247BF}.Release|x86.ActiveCfg = Release|Any CPU + {5F253D7F-BF27-46F5-9382-73A66EC247BF}.Release|x86.Build.0 = Release|Any CPU {99E2D1A4-1853-49F9-96B6-59C70FA3DE3A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {99E2D1A4-1853-49F9-96B6-59C70FA3DE3A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {99E2D1A4-1853-49F9-96B6-59C70FA3DE3A}.Debug|x64.ActiveCfg = Debug|Any CPU + {99E2D1A4-1853-49F9-96B6-59C70FA3DE3A}.Debug|x64.Build.0 = Debug|Any CPU + {99E2D1A4-1853-49F9-96B6-59C70FA3DE3A}.Debug|x86.ActiveCfg = Debug|Any CPU + {99E2D1A4-1853-49F9-96B6-59C70FA3DE3A}.Debug|x86.Build.0 = Debug|Any CPU {99E2D1A4-1853-49F9-96B6-59C70FA3DE3A}.Release|Any CPU.ActiveCfg = Release|Any CPU {99E2D1A4-1853-49F9-96B6-59C70FA3DE3A}.Release|Any CPU.Build.0 = Release|Any CPU + {99E2D1A4-1853-49F9-96B6-59C70FA3DE3A}.Release|x64.ActiveCfg = Release|Any CPU + {99E2D1A4-1853-49F9-96B6-59C70FA3DE3A}.Release|x64.Build.0 = Release|Any CPU + {99E2D1A4-1853-49F9-96B6-59C70FA3DE3A}.Release|x86.ActiveCfg = Release|Any CPU + {99E2D1A4-1853-49F9-96B6-59C70FA3DE3A}.Release|x86.Build.0 = Release|Any CPU {3C543CED-F801-4843-BA48-76142843E1B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3C543CED-F801-4843-BA48-76142843E1B9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3C543CED-F801-4843-BA48-76142843E1B9}.Debug|x64.ActiveCfg = Debug|Any CPU + {3C543CED-F801-4843-BA48-76142843E1B9}.Debug|x64.Build.0 = Debug|Any CPU + {3C543CED-F801-4843-BA48-76142843E1B9}.Debug|x86.ActiveCfg = Debug|Any CPU + {3C543CED-F801-4843-BA48-76142843E1B9}.Debug|x86.Build.0 = Debug|Any CPU {3C543CED-F801-4843-BA48-76142843E1B9}.Release|Any CPU.ActiveCfg = Release|Any CPU {3C543CED-F801-4843-BA48-76142843E1B9}.Release|Any CPU.Build.0 = Release|Any CPU + {3C543CED-F801-4843-BA48-76142843E1B9}.Release|x64.ActiveCfg = Release|Any CPU + {3C543CED-F801-4843-BA48-76142843E1B9}.Release|x64.Build.0 = Release|Any CPU + {3C543CED-F801-4843-BA48-76142843E1B9}.Release|x86.ActiveCfg = Release|Any CPU + {3C543CED-F801-4843-BA48-76142843E1B9}.Release|x86.Build.0 = Release|Any CPU {7ABAA536-7BEC-46D7-9C61-82440DDC9AA8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7ABAA536-7BEC-46D7-9C61-82440DDC9AA8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7ABAA536-7BEC-46D7-9C61-82440DDC9AA8}.Debug|x64.ActiveCfg = Debug|Any CPU + {7ABAA536-7BEC-46D7-9C61-82440DDC9AA8}.Debug|x64.Build.0 = Debug|Any CPU + {7ABAA536-7BEC-46D7-9C61-82440DDC9AA8}.Debug|x86.ActiveCfg = Debug|Any CPU + {7ABAA536-7BEC-46D7-9C61-82440DDC9AA8}.Debug|x86.Build.0 = Debug|Any CPU {7ABAA536-7BEC-46D7-9C61-82440DDC9AA8}.Release|Any CPU.ActiveCfg = Release|Any CPU {7ABAA536-7BEC-46D7-9C61-82440DDC9AA8}.Release|Any CPU.Build.0 = Release|Any CPU + {7ABAA536-7BEC-46D7-9C61-82440DDC9AA8}.Release|x64.ActiveCfg = Release|Any CPU + {7ABAA536-7BEC-46D7-9C61-82440DDC9AA8}.Release|x64.Build.0 = Release|Any CPU + {7ABAA536-7BEC-46D7-9C61-82440DDC9AA8}.Release|x86.ActiveCfg = Release|Any CPU + {7ABAA536-7BEC-46D7-9C61-82440DDC9AA8}.Release|x86.Build.0 = Release|Any CPU {D7D1EA68-ACE3-4DF3-A33C-EBB5B74701F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D7D1EA68-ACE3-4DF3-A33C-EBB5B74701F5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D7D1EA68-ACE3-4DF3-A33C-EBB5B74701F5}.Debug|x64.ActiveCfg = Debug|Any CPU + {D7D1EA68-ACE3-4DF3-A33C-EBB5B74701F5}.Debug|x64.Build.0 = Debug|Any CPU + {D7D1EA68-ACE3-4DF3-A33C-EBB5B74701F5}.Debug|x86.ActiveCfg = Debug|Any CPU + {D7D1EA68-ACE3-4DF3-A33C-EBB5B74701F5}.Debug|x86.Build.0 = Debug|Any CPU {D7D1EA68-ACE3-4DF3-A33C-EBB5B74701F5}.Release|Any CPU.ActiveCfg = Release|Any CPU {D7D1EA68-ACE3-4DF3-A33C-EBB5B74701F5}.Release|Any CPU.Build.0 = Release|Any CPU + {D7D1EA68-ACE3-4DF3-A33C-EBB5B74701F5}.Release|x64.ActiveCfg = Release|Any CPU + {D7D1EA68-ACE3-4DF3-A33C-EBB5B74701F5}.Release|x64.Build.0 = Release|Any CPU + {D7D1EA68-ACE3-4DF3-A33C-EBB5B74701F5}.Release|x86.ActiveCfg = Release|Any CPU + {D7D1EA68-ACE3-4DF3-A33C-EBB5B74701F5}.Release|x86.Build.0 = Release|Any CPU {563FEA1C-FDAE-4EDE-9ABF-22D76F2DA048}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {563FEA1C-FDAE-4EDE-9ABF-22D76F2DA048}.Debug|Any CPU.Build.0 = Debug|Any CPU + {563FEA1C-FDAE-4EDE-9ABF-22D76F2DA048}.Debug|x64.ActiveCfg = Debug|Any CPU + {563FEA1C-FDAE-4EDE-9ABF-22D76F2DA048}.Debug|x64.Build.0 = Debug|Any CPU + {563FEA1C-FDAE-4EDE-9ABF-22D76F2DA048}.Debug|x86.ActiveCfg = Debug|Any CPU + {563FEA1C-FDAE-4EDE-9ABF-22D76F2DA048}.Debug|x86.Build.0 = Debug|Any CPU {563FEA1C-FDAE-4EDE-9ABF-22D76F2DA048}.Release|Any CPU.ActiveCfg = Release|Any CPU {563FEA1C-FDAE-4EDE-9ABF-22D76F2DA048}.Release|Any CPU.Build.0 = Release|Any CPU + {563FEA1C-FDAE-4EDE-9ABF-22D76F2DA048}.Release|x64.ActiveCfg = Release|Any CPU + {563FEA1C-FDAE-4EDE-9ABF-22D76F2DA048}.Release|x64.Build.0 = Release|Any CPU + {563FEA1C-FDAE-4EDE-9ABF-22D76F2DA048}.Release|x86.ActiveCfg = Release|Any CPU + {563FEA1C-FDAE-4EDE-9ABF-22D76F2DA048}.Release|x86.Build.0 = Release|Any CPU {6D9EB590-CED1-47C2-87B1-E65EE0EEBB9E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6D9EB590-CED1-47C2-87B1-E65EE0EEBB9E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6D9EB590-CED1-47C2-87B1-E65EE0EEBB9E}.Debug|x64.ActiveCfg = Debug|Any CPU + {6D9EB590-CED1-47C2-87B1-E65EE0EEBB9E}.Debug|x64.Build.0 = Debug|Any CPU + {6D9EB590-CED1-47C2-87B1-E65EE0EEBB9E}.Debug|x86.ActiveCfg = Debug|Any CPU + {6D9EB590-CED1-47C2-87B1-E65EE0EEBB9E}.Debug|x86.Build.0 = Debug|Any CPU {6D9EB590-CED1-47C2-87B1-E65EE0EEBB9E}.Release|Any CPU.ActiveCfg = Release|Any CPU {6D9EB590-CED1-47C2-87B1-E65EE0EEBB9E}.Release|Any CPU.Build.0 = Release|Any CPU + {6D9EB590-CED1-47C2-87B1-E65EE0EEBB9E}.Release|x64.ActiveCfg = Release|Any CPU + {6D9EB590-CED1-47C2-87B1-E65EE0EEBB9E}.Release|x64.Build.0 = Release|Any CPU + {6D9EB590-CED1-47C2-87B1-E65EE0EEBB9E}.Release|x86.ActiveCfg = Release|Any CPU + {6D9EB590-CED1-47C2-87B1-E65EE0EEBB9E}.Release|x86.Build.0 = Release|Any CPU {39F7BB08-908B-49F9-A96B-E14C16B69090}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {39F7BB08-908B-49F9-A96B-E14C16B69090}.Debug|Any CPU.Build.0 = Debug|Any CPU + {39F7BB08-908B-49F9-A96B-E14C16B69090}.Debug|x64.ActiveCfg = Debug|Any CPU + {39F7BB08-908B-49F9-A96B-E14C16B69090}.Debug|x64.Build.0 = Debug|Any CPU + {39F7BB08-908B-49F9-A96B-E14C16B69090}.Debug|x86.ActiveCfg = Debug|Any CPU + {39F7BB08-908B-49F9-A96B-E14C16B69090}.Debug|x86.Build.0 = Debug|Any CPU {39F7BB08-908B-49F9-A96B-E14C16B69090}.Release|Any CPU.ActiveCfg = Release|Any CPU {39F7BB08-908B-49F9-A96B-E14C16B69090}.Release|Any CPU.Build.0 = Release|Any CPU + {39F7BB08-908B-49F9-A96B-E14C16B69090}.Release|x64.ActiveCfg = Release|Any CPU + {39F7BB08-908B-49F9-A96B-E14C16B69090}.Release|x64.Build.0 = Release|Any CPU + {39F7BB08-908B-49F9-A96B-E14C16B69090}.Release|x86.ActiveCfg = Release|Any CPU + {39F7BB08-908B-49F9-A96B-E14C16B69090}.Release|x86.Build.0 = Release|Any CPU {59742E7E-4380-4B40-BCC8-5AB314290198}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {59742E7E-4380-4B40-BCC8-5AB314290198}.Debug|Any CPU.Build.0 = Debug|Any CPU + {59742E7E-4380-4B40-BCC8-5AB314290198}.Debug|x64.ActiveCfg = Debug|Any CPU + {59742E7E-4380-4B40-BCC8-5AB314290198}.Debug|x64.Build.0 = Debug|Any CPU + {59742E7E-4380-4B40-BCC8-5AB314290198}.Debug|x86.ActiveCfg = Debug|Any CPU + {59742E7E-4380-4B40-BCC8-5AB314290198}.Debug|x86.Build.0 = Debug|Any CPU {59742E7E-4380-4B40-BCC8-5AB314290198}.Release|Any CPU.ActiveCfg = Release|Any CPU {59742E7E-4380-4B40-BCC8-5AB314290198}.Release|Any CPU.Build.0 = Release|Any CPU + {59742E7E-4380-4B40-BCC8-5AB314290198}.Release|x64.ActiveCfg = Release|Any CPU + {59742E7E-4380-4B40-BCC8-5AB314290198}.Release|x64.Build.0 = Release|Any CPU + {59742E7E-4380-4B40-BCC8-5AB314290198}.Release|x86.ActiveCfg = Release|Any CPU + {59742E7E-4380-4B40-BCC8-5AB314290198}.Release|x86.Build.0 = Release|Any CPU {6222BF96-2F1F-42DA-AF43-388B20151A5A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6222BF96-2F1F-42DA-AF43-388B20151A5A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6222BF96-2F1F-42DA-AF43-388B20151A5A}.Debug|x64.ActiveCfg = Debug|Any CPU + {6222BF96-2F1F-42DA-AF43-388B20151A5A}.Debug|x64.Build.0 = Debug|Any CPU + {6222BF96-2F1F-42DA-AF43-388B20151A5A}.Debug|x86.ActiveCfg = Debug|Any CPU + {6222BF96-2F1F-42DA-AF43-388B20151A5A}.Debug|x86.Build.0 = Debug|Any CPU {6222BF96-2F1F-42DA-AF43-388B20151A5A}.Release|Any CPU.ActiveCfg = Release|Any CPU {6222BF96-2F1F-42DA-AF43-388B20151A5A}.Release|Any CPU.Build.0 = Release|Any CPU + {6222BF96-2F1F-42DA-AF43-388B20151A5A}.Release|x64.ActiveCfg = Release|Any CPU + {6222BF96-2F1F-42DA-AF43-388B20151A5A}.Release|x64.Build.0 = Release|Any CPU + {6222BF96-2F1F-42DA-AF43-388B20151A5A}.Release|x86.ActiveCfg = Release|Any CPU + {6222BF96-2F1F-42DA-AF43-388B20151A5A}.Release|x86.Build.0 = Release|Any CPU {18FDE2B5-6023-487C-A349-E492F3F34B4A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {18FDE2B5-6023-487C-A349-E492F3F34B4A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {18FDE2B5-6023-487C-A349-E492F3F34B4A}.Debug|x64.ActiveCfg = Debug|Any CPU + {18FDE2B5-6023-487C-A349-E492F3F34B4A}.Debug|x64.Build.0 = Debug|Any CPU + {18FDE2B5-6023-487C-A349-E492F3F34B4A}.Debug|x86.ActiveCfg = Debug|Any CPU + {18FDE2B5-6023-487C-A349-E492F3F34B4A}.Debug|x86.Build.0 = Debug|Any CPU {18FDE2B5-6023-487C-A349-E492F3F34B4A}.Release|Any CPU.ActiveCfg = Release|Any CPU {18FDE2B5-6023-487C-A349-E492F3F34B4A}.Release|Any CPU.Build.0 = Release|Any CPU + {18FDE2B5-6023-487C-A349-E492F3F34B4A}.Release|x64.ActiveCfg = Release|Any CPU + {18FDE2B5-6023-487C-A349-E492F3F34B4A}.Release|x64.Build.0 = Release|Any CPU + {18FDE2B5-6023-487C-A349-E492F3F34B4A}.Release|x86.ActiveCfg = Release|Any CPU + {18FDE2B5-6023-487C-A349-E492F3F34B4A}.Release|x86.Build.0 = Release|Any CPU {357D2522-4481-4135-8379-C228C743DD69}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {357D2522-4481-4135-8379-C228C743DD69}.Debug|Any CPU.Build.0 = Debug|Any CPU + {357D2522-4481-4135-8379-C228C743DD69}.Debug|x64.ActiveCfg = Debug|Any CPU + {357D2522-4481-4135-8379-C228C743DD69}.Debug|x64.Build.0 = Debug|Any CPU + {357D2522-4481-4135-8379-C228C743DD69}.Debug|x86.ActiveCfg = Debug|Any CPU + {357D2522-4481-4135-8379-C228C743DD69}.Debug|x86.Build.0 = Debug|Any CPU {357D2522-4481-4135-8379-C228C743DD69}.Release|Any CPU.ActiveCfg = Release|Any CPU {357D2522-4481-4135-8379-C228C743DD69}.Release|Any CPU.Build.0 = Release|Any CPU + {357D2522-4481-4135-8379-C228C743DD69}.Release|x64.ActiveCfg = Release|Any CPU + {357D2522-4481-4135-8379-C228C743DD69}.Release|x64.Build.0 = Release|Any CPU + {357D2522-4481-4135-8379-C228C743DD69}.Release|x86.ActiveCfg = Release|Any CPU + {357D2522-4481-4135-8379-C228C743DD69}.Release|x86.Build.0 = Release|Any CPU {321A2E5D-6A3E-44B6-BECE-D1FFA94D58B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {321A2E5D-6A3E-44B6-BECE-D1FFA94D58B9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {321A2E5D-6A3E-44B6-BECE-D1FFA94D58B9}.Debug|x64.ActiveCfg = Debug|Any CPU + {321A2E5D-6A3E-44B6-BECE-D1FFA94D58B9}.Debug|x64.Build.0 = Debug|Any CPU + {321A2E5D-6A3E-44B6-BECE-D1FFA94D58B9}.Debug|x86.ActiveCfg = Debug|Any CPU + {321A2E5D-6A3E-44B6-BECE-D1FFA94D58B9}.Debug|x86.Build.0 = Debug|Any CPU {321A2E5D-6A3E-44B6-BECE-D1FFA94D58B9}.Release|Any CPU.ActiveCfg = Release|Any CPU {321A2E5D-6A3E-44B6-BECE-D1FFA94D58B9}.Release|Any CPU.Build.0 = Release|Any CPU + {321A2E5D-6A3E-44B6-BECE-D1FFA94D58B9}.Release|x64.ActiveCfg = Release|Any CPU + {321A2E5D-6A3E-44B6-BECE-D1FFA94D58B9}.Release|x64.Build.0 = Release|Any CPU + {321A2E5D-6A3E-44B6-BECE-D1FFA94D58B9}.Release|x86.ActiveCfg = Release|Any CPU + {321A2E5D-6A3E-44B6-BECE-D1FFA94D58B9}.Release|x86.Build.0 = Release|Any CPU {A454B861-62B4-4F4D-8E31-725C4592D169}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A454B861-62B4-4F4D-8E31-725C4592D169}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A454B861-62B4-4F4D-8E31-725C4592D169}.Debug|x64.ActiveCfg = Debug|Any CPU + {A454B861-62B4-4F4D-8E31-725C4592D169}.Debug|x64.Build.0 = Debug|Any CPU + {A454B861-62B4-4F4D-8E31-725C4592D169}.Debug|x86.ActiveCfg = Debug|Any CPU + {A454B861-62B4-4F4D-8E31-725C4592D169}.Debug|x86.Build.0 = Debug|Any CPU {A454B861-62B4-4F4D-8E31-725C4592D169}.Release|Any CPU.ActiveCfg = Release|Any CPU {A454B861-62B4-4F4D-8E31-725C4592D169}.Release|Any CPU.Build.0 = Release|Any CPU + {A454B861-62B4-4F4D-8E31-725C4592D169}.Release|x64.ActiveCfg = Release|Any CPU + {A454B861-62B4-4F4D-8E31-725C4592D169}.Release|x64.Build.0 = Release|Any CPU + {A454B861-62B4-4F4D-8E31-725C4592D169}.Release|x86.ActiveCfg = Release|Any CPU + {A454B861-62B4-4F4D-8E31-725C4592D169}.Release|x86.Build.0 = Release|Any CPU {F89C3981-6D5A-4D63-8CD0-FB35E55C5835}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F89C3981-6D5A-4D63-8CD0-FB35E55C5835}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F89C3981-6D5A-4D63-8CD0-FB35E55C5835}.Debug|x64.ActiveCfg = Debug|Any CPU + {F89C3981-6D5A-4D63-8CD0-FB35E55C5835}.Debug|x64.Build.0 = Debug|Any CPU + {F89C3981-6D5A-4D63-8CD0-FB35E55C5835}.Debug|x86.ActiveCfg = Debug|Any CPU + {F89C3981-6D5A-4D63-8CD0-FB35E55C5835}.Debug|x86.Build.0 = Debug|Any CPU {F89C3981-6D5A-4D63-8CD0-FB35E55C5835}.Release|Any CPU.ActiveCfg = Release|Any CPU {F89C3981-6D5A-4D63-8CD0-FB35E55C5835}.Release|Any CPU.Build.0 = Release|Any CPU + {F89C3981-6D5A-4D63-8CD0-FB35E55C5835}.Release|x64.ActiveCfg = Release|Any CPU + {F89C3981-6D5A-4D63-8CD0-FB35E55C5835}.Release|x64.Build.0 = Release|Any CPU + {F89C3981-6D5A-4D63-8CD0-FB35E55C5835}.Release|x86.ActiveCfg = Release|Any CPU + {F89C3981-6D5A-4D63-8CD0-FB35E55C5835}.Release|x86.Build.0 = Release|Any CPU {A8FFC45B-7FC3-44B3-B8AF-CA9642E66546}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A8FFC45B-7FC3-44B3-B8AF-CA9642E66546}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A8FFC45B-7FC3-44B3-B8AF-CA9642E66546}.Debug|x64.ActiveCfg = Debug|Any CPU + {A8FFC45B-7FC3-44B3-B8AF-CA9642E66546}.Debug|x64.Build.0 = Debug|Any CPU + {A8FFC45B-7FC3-44B3-B8AF-CA9642E66546}.Debug|x86.ActiveCfg = Debug|Any CPU + {A8FFC45B-7FC3-44B3-B8AF-CA9642E66546}.Debug|x86.Build.0 = Debug|Any CPU {A8FFC45B-7FC3-44B3-B8AF-CA9642E66546}.Release|Any CPU.ActiveCfg = Release|Any CPU {A8FFC45B-7FC3-44B3-B8AF-CA9642E66546}.Release|Any CPU.Build.0 = Release|Any CPU + {A8FFC45B-7FC3-44B3-B8AF-CA9642E66546}.Release|x64.ActiveCfg = Release|Any CPU + {A8FFC45B-7FC3-44B3-B8AF-CA9642E66546}.Release|x64.Build.0 = Release|Any CPU + {A8FFC45B-7FC3-44B3-B8AF-CA9642E66546}.Release|x86.ActiveCfg = Release|Any CPU + {A8FFC45B-7FC3-44B3-B8AF-CA9642E66546}.Release|x86.Build.0 = Release|Any CPU {9D3631D0-C7DA-4283-BA24-A14424B0F7BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9D3631D0-C7DA-4283-BA24-A14424B0F7BE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9D3631D0-C7DA-4283-BA24-A14424B0F7BE}.Debug|x64.ActiveCfg = Debug|Any CPU + {9D3631D0-C7DA-4283-BA24-A14424B0F7BE}.Debug|x64.Build.0 = Debug|Any CPU + {9D3631D0-C7DA-4283-BA24-A14424B0F7BE}.Debug|x86.ActiveCfg = Debug|Any CPU + {9D3631D0-C7DA-4283-BA24-A14424B0F7BE}.Debug|x86.Build.0 = Debug|Any CPU {9D3631D0-C7DA-4283-BA24-A14424B0F7BE}.Release|Any CPU.ActiveCfg = Release|Any CPU {9D3631D0-C7DA-4283-BA24-A14424B0F7BE}.Release|Any CPU.Build.0 = Release|Any CPU + {9D3631D0-C7DA-4283-BA24-A14424B0F7BE}.Release|x64.ActiveCfg = Release|Any CPU + {9D3631D0-C7DA-4283-BA24-A14424B0F7BE}.Release|x64.Build.0 = Release|Any CPU + {9D3631D0-C7DA-4283-BA24-A14424B0F7BE}.Release|x86.ActiveCfg = Release|Any CPU + {9D3631D0-C7DA-4283-BA24-A14424B0F7BE}.Release|x86.Build.0 = Release|Any CPU {CDB5B2C4-4CD6-4AE0-9C4B-451B3170FB55}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CDB5B2C4-4CD6-4AE0-9C4B-451B3170FB55}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CDB5B2C4-4CD6-4AE0-9C4B-451B3170FB55}.Debug|x64.ActiveCfg = Debug|Any CPU + {CDB5B2C4-4CD6-4AE0-9C4B-451B3170FB55}.Debug|x64.Build.0 = Debug|Any CPU + {CDB5B2C4-4CD6-4AE0-9C4B-451B3170FB55}.Debug|x86.ActiveCfg = Debug|Any CPU + {CDB5B2C4-4CD6-4AE0-9C4B-451B3170FB55}.Debug|x86.Build.0 = Debug|Any CPU {CDB5B2C4-4CD6-4AE0-9C4B-451B3170FB55}.Release|Any CPU.ActiveCfg = Release|Any CPU {CDB5B2C4-4CD6-4AE0-9C4B-451B3170FB55}.Release|Any CPU.Build.0 = Release|Any CPU + {CDB5B2C4-4CD6-4AE0-9C4B-451B3170FB55}.Release|x64.ActiveCfg = Release|Any CPU + {CDB5B2C4-4CD6-4AE0-9C4B-451B3170FB55}.Release|x64.Build.0 = Release|Any CPU + {CDB5B2C4-4CD6-4AE0-9C4B-451B3170FB55}.Release|x86.ActiveCfg = Release|Any CPU + {CDB5B2C4-4CD6-4AE0-9C4B-451B3170FB55}.Release|x86.Build.0 = Release|Any CPU {CC0FE166-E649-4CA4-AACD-C0205ECDF896}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CC0FE166-E649-4CA4-AACD-C0205ECDF896}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CC0FE166-E649-4CA4-AACD-C0205ECDF896}.Debug|x64.ActiveCfg = Debug|Any CPU + {CC0FE166-E649-4CA4-AACD-C0205ECDF896}.Debug|x64.Build.0 = Debug|Any CPU + {CC0FE166-E649-4CA4-AACD-C0205ECDF896}.Debug|x86.ActiveCfg = Debug|Any CPU + {CC0FE166-E649-4CA4-AACD-C0205ECDF896}.Debug|x86.Build.0 = Debug|Any CPU {CC0FE166-E649-4CA4-AACD-C0205ECDF896}.Release|Any CPU.ActiveCfg = Release|Any CPU {CC0FE166-E649-4CA4-AACD-C0205ECDF896}.Release|Any CPU.Build.0 = Release|Any CPU + {CC0FE166-E649-4CA4-AACD-C0205ECDF896}.Release|x64.ActiveCfg = Release|Any CPU + {CC0FE166-E649-4CA4-AACD-C0205ECDF896}.Release|x64.Build.0 = Release|Any CPU + {CC0FE166-E649-4CA4-AACD-C0205ECDF896}.Release|x86.ActiveCfg = Release|Any CPU + {CC0FE166-E649-4CA4-AACD-C0205ECDF896}.Release|x86.Build.0 = Release|Any CPU {C97D62CF-A541-4393-A49A-92F53F1E1983}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C97D62CF-A541-4393-A49A-92F53F1E1983}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C97D62CF-A541-4393-A49A-92F53F1E1983}.Debug|x64.ActiveCfg = Debug|Any CPU + {C97D62CF-A541-4393-A49A-92F53F1E1983}.Debug|x64.Build.0 = Debug|Any CPU + {C97D62CF-A541-4393-A49A-92F53F1E1983}.Debug|x86.ActiveCfg = Debug|Any CPU + {C97D62CF-A541-4393-A49A-92F53F1E1983}.Debug|x86.Build.0 = Debug|Any CPU {C97D62CF-A541-4393-A49A-92F53F1E1983}.Release|Any CPU.ActiveCfg = Release|Any CPU {C97D62CF-A541-4393-A49A-92F53F1E1983}.Release|Any CPU.Build.0 = Release|Any CPU + {C97D62CF-A541-4393-A49A-92F53F1E1983}.Release|x64.ActiveCfg = Release|Any CPU + {C97D62CF-A541-4393-A49A-92F53F1E1983}.Release|x64.Build.0 = Release|Any CPU + {C97D62CF-A541-4393-A49A-92F53F1E1983}.Release|x86.ActiveCfg = Release|Any CPU + {C97D62CF-A541-4393-A49A-92F53F1E1983}.Release|x86.Build.0 = Release|Any CPU {36ADA62A-0AD5-461A-B28C-A5DBAF718B59}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {36ADA62A-0AD5-461A-B28C-A5DBAF718B59}.Debug|Any CPU.Build.0 = Debug|Any CPU + {36ADA62A-0AD5-461A-B28C-A5DBAF718B59}.Debug|x64.ActiveCfg = Debug|Any CPU + {36ADA62A-0AD5-461A-B28C-A5DBAF718B59}.Debug|x64.Build.0 = Debug|Any CPU + {36ADA62A-0AD5-461A-B28C-A5DBAF718B59}.Debug|x86.ActiveCfg = Debug|Any CPU + {36ADA62A-0AD5-461A-B28C-A5DBAF718B59}.Debug|x86.Build.0 = Debug|Any CPU {36ADA62A-0AD5-461A-B28C-A5DBAF718B59}.Release|Any CPU.ActiveCfg = Release|Any CPU {36ADA62A-0AD5-461A-B28C-A5DBAF718B59}.Release|Any CPU.Build.0 = Release|Any CPU + {36ADA62A-0AD5-461A-B28C-A5DBAF718B59}.Release|x64.ActiveCfg = Release|Any CPU + {36ADA62A-0AD5-461A-B28C-A5DBAF718B59}.Release|x64.Build.0 = Release|Any CPU + {36ADA62A-0AD5-461A-B28C-A5DBAF718B59}.Release|x86.ActiveCfg = Release|Any CPU + {36ADA62A-0AD5-461A-B28C-A5DBAF718B59}.Release|x86.Build.0 = Release|Any CPU {A2AD0EF1-6943-46E0-BEED-0704D362FA48}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A2AD0EF1-6943-46E0-BEED-0704D362FA48}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A2AD0EF1-6943-46E0-BEED-0704D362FA48}.Debug|x64.ActiveCfg = Debug|Any CPU + {A2AD0EF1-6943-46E0-BEED-0704D362FA48}.Debug|x64.Build.0 = Debug|Any CPU + {A2AD0EF1-6943-46E0-BEED-0704D362FA48}.Debug|x86.ActiveCfg = Debug|Any CPU + {A2AD0EF1-6943-46E0-BEED-0704D362FA48}.Debug|x86.Build.0 = Debug|Any CPU {A2AD0EF1-6943-46E0-BEED-0704D362FA48}.Release|Any CPU.ActiveCfg = Release|Any CPU {A2AD0EF1-6943-46E0-BEED-0704D362FA48}.Release|Any CPU.Build.0 = Release|Any CPU + {A2AD0EF1-6943-46E0-BEED-0704D362FA48}.Release|x64.ActiveCfg = Release|Any CPU + {A2AD0EF1-6943-46E0-BEED-0704D362FA48}.Release|x64.Build.0 = Release|Any CPU + {A2AD0EF1-6943-46E0-BEED-0704D362FA48}.Release|x86.ActiveCfg = Release|Any CPU + {A2AD0EF1-6943-46E0-BEED-0704D362FA48}.Release|x86.Build.0 = Release|Any CPU {9272A135-C1F7-4317-8AEA-1BFDEE2DC683}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9272A135-C1F7-4317-8AEA-1BFDEE2DC683}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9272A135-C1F7-4317-8AEA-1BFDEE2DC683}.Debug|x64.ActiveCfg = Debug|Any CPU + {9272A135-C1F7-4317-8AEA-1BFDEE2DC683}.Debug|x64.Build.0 = Debug|Any CPU + {9272A135-C1F7-4317-8AEA-1BFDEE2DC683}.Debug|x86.ActiveCfg = Debug|Any CPU + {9272A135-C1F7-4317-8AEA-1BFDEE2DC683}.Debug|x86.Build.0 = Debug|Any CPU {9272A135-C1F7-4317-8AEA-1BFDEE2DC683}.Release|Any CPU.ActiveCfg = Release|Any CPU {9272A135-C1F7-4317-8AEA-1BFDEE2DC683}.Release|Any CPU.Build.0 = Release|Any CPU + {9272A135-C1F7-4317-8AEA-1BFDEE2DC683}.Release|x64.ActiveCfg = Release|Any CPU + {9272A135-C1F7-4317-8AEA-1BFDEE2DC683}.Release|x64.Build.0 = Release|Any CPU + {9272A135-C1F7-4317-8AEA-1BFDEE2DC683}.Release|x86.ActiveCfg = Release|Any CPU + {9272A135-C1F7-4317-8AEA-1BFDEE2DC683}.Release|x86.Build.0 = Release|Any CPU {162A1CAE-ACEE-45CA-A6D0-7702ADE4D3DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {162A1CAE-ACEE-45CA-A6D0-7702ADE4D3DE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {162A1CAE-ACEE-45CA-A6D0-7702ADE4D3DE}.Debug|x64.ActiveCfg = Debug|Any CPU + {162A1CAE-ACEE-45CA-A6D0-7702ADE4D3DE}.Debug|x64.Build.0 = Debug|Any CPU + {162A1CAE-ACEE-45CA-A6D0-7702ADE4D3DE}.Debug|x86.ActiveCfg = Debug|Any CPU + {162A1CAE-ACEE-45CA-A6D0-7702ADE4D3DE}.Debug|x86.Build.0 = Debug|Any CPU {162A1CAE-ACEE-45CA-A6D0-7702ADE4D3DE}.Release|Any CPU.ActiveCfg = Release|Any CPU {162A1CAE-ACEE-45CA-A6D0-7702ADE4D3DE}.Release|Any CPU.Build.0 = Release|Any CPU + {162A1CAE-ACEE-45CA-A6D0-7702ADE4D3DE}.Release|x64.ActiveCfg = Release|Any CPU + {162A1CAE-ACEE-45CA-A6D0-7702ADE4D3DE}.Release|x64.Build.0 = Release|Any CPU + {162A1CAE-ACEE-45CA-A6D0-7702ADE4D3DE}.Release|x86.ActiveCfg = Release|Any CPU + {162A1CAE-ACEE-45CA-A6D0-7702ADE4D3DE}.Release|x86.Build.0 = Release|Any CPU {67269916-C417-4CEE-BD7D-CA66C3830AEE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {67269916-C417-4CEE-BD7D-CA66C3830AEE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {67269916-C417-4CEE-BD7D-CA66C3830AEE}.Debug|x64.ActiveCfg = Debug|Any CPU + {67269916-C417-4CEE-BD7D-CA66C3830AEE}.Debug|x64.Build.0 = Debug|Any CPU + {67269916-C417-4CEE-BD7D-CA66C3830AEE}.Debug|x86.ActiveCfg = Debug|Any CPU + {67269916-C417-4CEE-BD7D-CA66C3830AEE}.Debug|x86.Build.0 = Debug|Any CPU {67269916-C417-4CEE-BD7D-CA66C3830AEE}.Release|Any CPU.ActiveCfg = Release|Any CPU {67269916-C417-4CEE-BD7D-CA66C3830AEE}.Release|Any CPU.Build.0 = Release|Any CPU + {67269916-C417-4CEE-BD7D-CA66C3830AEE}.Release|x64.ActiveCfg = Release|Any CPU + {67269916-C417-4CEE-BD7D-CA66C3830AEE}.Release|x64.Build.0 = Release|Any CPU + {67269916-C417-4CEE-BD7D-CA66C3830AEE}.Release|x86.ActiveCfg = Release|Any CPU + {67269916-C417-4CEE-BD7D-CA66C3830AEE}.Release|x86.Build.0 = Release|Any CPU {8032310D-3C06-442C-A318-F365BCC4C804}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8032310D-3C06-442C-A318-F365BCC4C804}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8032310D-3C06-442C-A318-F365BCC4C804}.Debug|x64.ActiveCfg = Debug|Any CPU + {8032310D-3C06-442C-A318-F365BCC4C804}.Debug|x64.Build.0 = Debug|Any CPU + {8032310D-3C06-442C-A318-F365BCC4C804}.Debug|x86.ActiveCfg = Debug|Any CPU + {8032310D-3C06-442C-A318-F365BCC4C804}.Debug|x86.Build.0 = Debug|Any CPU {8032310D-3C06-442C-A318-F365BCC4C804}.Release|Any CPU.ActiveCfg = Release|Any CPU {8032310D-3C06-442C-A318-F365BCC4C804}.Release|Any CPU.Build.0 = Release|Any CPU + {8032310D-3C06-442C-A318-F365BCC4C804}.Release|x64.ActiveCfg = Release|Any CPU + {8032310D-3C06-442C-A318-F365BCC4C804}.Release|x64.Build.0 = Release|Any CPU + {8032310D-3C06-442C-A318-F365BCC4C804}.Release|x86.ActiveCfg = Release|Any CPU + {8032310D-3C06-442C-A318-F365BCC4C804}.Release|x86.Build.0 = Release|Any CPU {FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2}.Debug|x64.ActiveCfg = Debug|Any CPU + {FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2}.Debug|x64.Build.0 = Debug|Any CPU + {FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2}.Debug|x86.ActiveCfg = Debug|Any CPU + {FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2}.Debug|x86.Build.0 = Debug|Any CPU {FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2}.Release|Any CPU.ActiveCfg = Release|Any CPU {FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2}.Release|Any CPU.Build.0 = Release|Any CPU + {FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2}.Release|x64.ActiveCfg = Release|Any CPU + {FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2}.Release|x64.Build.0 = Release|Any CPU + {FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2}.Release|x86.ActiveCfg = Release|Any CPU + {FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2}.Release|x86.Build.0 = Release|Any CPU {5B100CC0-1A78-407E-A5A5-94BC06D67461}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5B100CC0-1A78-407E-A5A5-94BC06D67461}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5B100CC0-1A78-407E-A5A5-94BC06D67461}.Debug|x64.ActiveCfg = Debug|Any CPU + {5B100CC0-1A78-407E-A5A5-94BC06D67461}.Debug|x64.Build.0 = Debug|Any CPU + {5B100CC0-1A78-407E-A5A5-94BC06D67461}.Debug|x86.ActiveCfg = Debug|Any CPU + {5B100CC0-1A78-407E-A5A5-94BC06D67461}.Debug|x86.Build.0 = Debug|Any CPU {5B100CC0-1A78-407E-A5A5-94BC06D67461}.Release|Any CPU.ActiveCfg = Release|Any CPU {5B100CC0-1A78-407E-A5A5-94BC06D67461}.Release|Any CPU.Build.0 = Release|Any CPU + {5B100CC0-1A78-407E-A5A5-94BC06D67461}.Release|x64.ActiveCfg = Release|Any CPU + {5B100CC0-1A78-407E-A5A5-94BC06D67461}.Release|x64.Build.0 = Release|Any CPU + {5B100CC0-1A78-407E-A5A5-94BC06D67461}.Release|x86.ActiveCfg = Release|Any CPU + {5B100CC0-1A78-407E-A5A5-94BC06D67461}.Release|x86.Build.0 = Release|Any CPU {407C477D-69C0-4B02-8A68-EE6B5A81C696}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {407C477D-69C0-4B02-8A68-EE6B5A81C696}.Debug|Any CPU.Build.0 = Debug|Any CPU + {407C477D-69C0-4B02-8A68-EE6B5A81C696}.Debug|x64.ActiveCfg = Debug|Any CPU + {407C477D-69C0-4B02-8A68-EE6B5A81C696}.Debug|x64.Build.0 = Debug|Any CPU + {407C477D-69C0-4B02-8A68-EE6B5A81C696}.Debug|x86.ActiveCfg = Debug|Any CPU + {407C477D-69C0-4B02-8A68-EE6B5A81C696}.Debug|x86.Build.0 = Debug|Any CPU {407C477D-69C0-4B02-8A68-EE6B5A81C696}.Release|Any CPU.ActiveCfg = Release|Any CPU {407C477D-69C0-4B02-8A68-EE6B5A81C696}.Release|Any CPU.Build.0 = Release|Any CPU + {407C477D-69C0-4B02-8A68-EE6B5A81C696}.Release|x64.ActiveCfg = Release|Any CPU + {407C477D-69C0-4B02-8A68-EE6B5A81C696}.Release|x64.Build.0 = Release|Any CPU + {407C477D-69C0-4B02-8A68-EE6B5A81C696}.Release|x86.ActiveCfg = Release|Any CPU + {407C477D-69C0-4B02-8A68-EE6B5A81C696}.Release|x86.Build.0 = Release|Any CPU {EADF25F5-8D02-4747-AB54-5F2BAA648471}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EADF25F5-8D02-4747-AB54-5F2BAA648471}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EADF25F5-8D02-4747-AB54-5F2BAA648471}.Debug|x64.ActiveCfg = Debug|Any CPU + {EADF25F5-8D02-4747-AB54-5F2BAA648471}.Debug|x64.Build.0 = Debug|Any CPU + {EADF25F5-8D02-4747-AB54-5F2BAA648471}.Debug|x86.ActiveCfg = Debug|Any CPU + {EADF25F5-8D02-4747-AB54-5F2BAA648471}.Debug|x86.Build.0 = Debug|Any CPU {EADF25F5-8D02-4747-AB54-5F2BAA648471}.Release|Any CPU.ActiveCfg = Release|Any CPU {EADF25F5-8D02-4747-AB54-5F2BAA648471}.Release|Any CPU.Build.0 = Release|Any CPU + {EADF25F5-8D02-4747-AB54-5F2BAA648471}.Release|x64.ActiveCfg = Release|Any CPU + {EADF25F5-8D02-4747-AB54-5F2BAA648471}.Release|x64.Build.0 = Release|Any CPU + {EADF25F5-8D02-4747-AB54-5F2BAA648471}.Release|x86.ActiveCfg = Release|Any CPU + {EADF25F5-8D02-4747-AB54-5F2BAA648471}.Release|x86.Build.0 = Release|Any CPU {46E40BE8-1AB0-4846-B0A2-A40AD0272C64}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {46E40BE8-1AB0-4846-B0A2-A40AD0272C64}.Debug|Any CPU.Build.0 = Debug|Any CPU + {46E40BE8-1AB0-4846-B0A2-A40AD0272C64}.Debug|x64.ActiveCfg = Debug|Any CPU + {46E40BE8-1AB0-4846-B0A2-A40AD0272C64}.Debug|x64.Build.0 = Debug|Any CPU + {46E40BE8-1AB0-4846-B0A2-A40AD0272C64}.Debug|x86.ActiveCfg = Debug|Any CPU + {46E40BE8-1AB0-4846-B0A2-A40AD0272C64}.Debug|x86.Build.0 = Debug|Any CPU {46E40BE8-1AB0-4846-B0A2-A40AD0272C64}.Release|Any CPU.ActiveCfg = Release|Any CPU {46E40BE8-1AB0-4846-B0A2-A40AD0272C64}.Release|Any CPU.Build.0 = Release|Any CPU - {8298202C-9983-4D0A-851D-805539EE481A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8298202C-9983-4D0A-851D-805539EE481A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8298202C-9983-4D0A-851D-805539EE481A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8298202C-9983-4D0A-851D-805539EE481A}.Release|Any CPU.Build.0 = Release|Any CPU + {46E40BE8-1AB0-4846-B0A2-A40AD0272C64}.Release|x64.ActiveCfg = Release|Any CPU + {46E40BE8-1AB0-4846-B0A2-A40AD0272C64}.Release|x64.Build.0 = Release|Any CPU + {46E40BE8-1AB0-4846-B0A2-A40AD0272C64}.Release|x86.ActiveCfg = Release|Any CPU + {46E40BE8-1AB0-4846-B0A2-A40AD0272C64}.Release|x86.Build.0 = Release|Any CPU {A5B26C14-7313-4EDC-91E3-287F9374AB75}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A5B26C14-7313-4EDC-91E3-287F9374AB75}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A5B26C14-7313-4EDC-91E3-287F9374AB75}.Debug|x64.ActiveCfg = Debug|Any CPU + {A5B26C14-7313-4EDC-91E3-287F9374AB75}.Debug|x64.Build.0 = Debug|Any CPU + {A5B26C14-7313-4EDC-91E3-287F9374AB75}.Debug|x86.ActiveCfg = Debug|Any CPU + {A5B26C14-7313-4EDC-91E3-287F9374AB75}.Debug|x86.Build.0 = Debug|Any CPU {A5B26C14-7313-4EDC-91E3-287F9374AB75}.Release|Any CPU.ActiveCfg = Release|Any CPU {A5B26C14-7313-4EDC-91E3-287F9374AB75}.Release|Any CPU.Build.0 = Release|Any CPU + {A5B26C14-7313-4EDC-91E3-287F9374AB75}.Release|x64.ActiveCfg = Release|Any CPU + {A5B26C14-7313-4EDC-91E3-287F9374AB75}.Release|x64.Build.0 = Release|Any CPU + {A5B26C14-7313-4EDC-91E3-287F9374AB75}.Release|x86.ActiveCfg = Release|Any CPU + {A5B26C14-7313-4EDC-91E3-287F9374AB75}.Release|x86.Build.0 = Release|Any CPU + {8298202C-9983-4D0A-851D-805539EE481A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8298202C-9983-4D0A-851D-805539EE481A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8298202C-9983-4D0A-851D-805539EE481A}.Debug|x64.ActiveCfg = Debug|Any CPU + {8298202C-9983-4D0A-851D-805539EE481A}.Debug|x64.Build.0 = Debug|Any CPU + {8298202C-9983-4D0A-851D-805539EE481A}.Debug|x86.ActiveCfg = Debug|Any CPU + {8298202C-9983-4D0A-851D-805539EE481A}.Debug|x86.Build.0 = Debug|Any CPU + {8298202C-9983-4D0A-851D-805539EE481A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8298202C-9983-4D0A-851D-805539EE481A}.Release|Any CPU.Build.0 = Release|Any CPU + {8298202C-9983-4D0A-851D-805539EE481A}.Release|x64.ActiveCfg = Release|Any CPU + {8298202C-9983-4D0A-851D-805539EE481A}.Release|x64.Build.0 = Release|Any CPU + {8298202C-9983-4D0A-851D-805539EE481A}.Release|x86.ActiveCfg = Release|Any CPU + {8298202C-9983-4D0A-851D-805539EE481A}.Release|x86.Build.0 = Release|Any CPU {E36C8DCA-464E-41CB-B189-F58553AAA8D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E36C8DCA-464E-41CB-B189-F58553AAA8D4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E36C8DCA-464E-41CB-B189-F58553AAA8D4}.Debug|x64.ActiveCfg = Debug|Any CPU + {E36C8DCA-464E-41CB-B189-F58553AAA8D4}.Debug|x64.Build.0 = Debug|Any CPU + {E36C8DCA-464E-41CB-B189-F58553AAA8D4}.Debug|x86.ActiveCfg = Debug|Any CPU + {E36C8DCA-464E-41CB-B189-F58553AAA8D4}.Debug|x86.Build.0 = Debug|Any CPU {E36C8DCA-464E-41CB-B189-F58553AAA8D4}.Release|Any CPU.ActiveCfg = Release|Any CPU {E36C8DCA-464E-41CB-B189-F58553AAA8D4}.Release|Any CPU.Build.0 = Release|Any CPU + {E36C8DCA-464E-41CB-B189-F58553AAA8D4}.Release|x64.ActiveCfg = Release|Any CPU + {E36C8DCA-464E-41CB-B189-F58553AAA8D4}.Release|x64.Build.0 = Release|Any CPU + {E36C8DCA-464E-41CB-B189-F58553AAA8D4}.Release|x86.ActiveCfg = Release|Any CPU + {E36C8DCA-464E-41CB-B189-F58553AAA8D4}.Release|x86.Build.0 = Release|Any CPU {5A17FEF9-07BB-47B8-9883-9C2CC93F09E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5A17FEF9-07BB-47B8-9883-9C2CC93F09E8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5A17FEF9-07BB-47B8-9883-9C2CC93F09E8}.Debug|x64.ActiveCfg = Debug|Any CPU + {5A17FEF9-07BB-47B8-9883-9C2CC93F09E8}.Debug|x64.Build.0 = Debug|Any CPU + {5A17FEF9-07BB-47B8-9883-9C2CC93F09E8}.Debug|x86.ActiveCfg = Debug|Any CPU + {5A17FEF9-07BB-47B8-9883-9C2CC93F09E8}.Debug|x86.Build.0 = Debug|Any CPU {5A17FEF9-07BB-47B8-9883-9C2CC93F09E8}.Release|Any CPU.ActiveCfg = Release|Any CPU {5A17FEF9-07BB-47B8-9883-9C2CC93F09E8}.Release|Any CPU.Build.0 = Release|Any CPU + {5A17FEF9-07BB-47B8-9883-9C2CC93F09E8}.Release|x64.ActiveCfg = Release|Any CPU + {5A17FEF9-07BB-47B8-9883-9C2CC93F09E8}.Release|x64.Build.0 = Release|Any CPU + {5A17FEF9-07BB-47B8-9883-9C2CC93F09E8}.Release|x86.ActiveCfg = Release|Any CPU + {5A17FEF9-07BB-47B8-9883-9C2CC93F09E8}.Release|x86.Build.0 = Release|Any CPU {D7DF0B26-AD43-4F8B-9BFE-C4471CCC9821}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D7DF0B26-AD43-4F8B-9BFE-C4471CCC9821}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D7DF0B26-AD43-4F8B-9BFE-C4471CCC9821}.Debug|x64.ActiveCfg = Debug|Any CPU + {D7DF0B26-AD43-4F8B-9BFE-C4471CCC9821}.Debug|x64.Build.0 = Debug|Any CPU + {D7DF0B26-AD43-4F8B-9BFE-C4471CCC9821}.Debug|x86.ActiveCfg = Debug|Any CPU + {D7DF0B26-AD43-4F8B-9BFE-C4471CCC9821}.Debug|x86.Build.0 = Debug|Any CPU {D7DF0B26-AD43-4F8B-9BFE-C4471CCC9821}.Release|Any CPU.ActiveCfg = Release|Any CPU {D7DF0B26-AD43-4F8B-9BFE-C4471CCC9821}.Release|Any CPU.Build.0 = Release|Any CPU + {D7DF0B26-AD43-4F8B-9BFE-C4471CCC9821}.Release|x64.ActiveCfg = Release|Any CPU + {D7DF0B26-AD43-4F8B-9BFE-C4471CCC9821}.Release|x64.Build.0 = Release|Any CPU + {D7DF0B26-AD43-4F8B-9BFE-C4471CCC9821}.Release|x86.ActiveCfg = Release|Any CPU + {D7DF0B26-AD43-4F8B-9BFE-C4471CCC9821}.Release|x86.Build.0 = Release|Any CPU {6030B748-0000-43B5-B8A8-399AA42F5229}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6030B748-0000-43B5-B8A8-399AA42F5229}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6030B748-0000-43B5-B8A8-399AA42F5229}.Debug|x64.ActiveCfg = Debug|Any CPU + {6030B748-0000-43B5-B8A8-399AA42F5229}.Debug|x64.Build.0 = Debug|Any CPU + {6030B748-0000-43B5-B8A8-399AA42F5229}.Debug|x86.ActiveCfg = Debug|Any CPU + {6030B748-0000-43B5-B8A8-399AA42F5229}.Debug|x86.Build.0 = Debug|Any CPU {6030B748-0000-43B5-B8A8-399AA42F5229}.Release|Any CPU.ActiveCfg = Release|Any CPU {6030B748-0000-43B5-B8A8-399AA42F5229}.Release|Any CPU.Build.0 = Release|Any CPU + {6030B748-0000-43B5-B8A8-399AA42F5229}.Release|x64.ActiveCfg = Release|Any CPU + {6030B748-0000-43B5-B8A8-399AA42F5229}.Release|x64.Build.0 = Release|Any CPU + {6030B748-0000-43B5-B8A8-399AA42F5229}.Release|x86.ActiveCfg = Release|Any CPU + {6030B748-0000-43B5-B8A8-399AA42F5229}.Release|x86.Build.0 = Release|Any CPU {DF92E098-822C-4B94-B96B-56BFB91FBB54}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DF92E098-822C-4B94-B96B-56BFB91FBB54}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DF92E098-822C-4B94-B96B-56BFB91FBB54}.Debug|x64.ActiveCfg = Debug|Any CPU + {DF92E098-822C-4B94-B96B-56BFB91FBB54}.Debug|x64.Build.0 = Debug|Any CPU + {DF92E098-822C-4B94-B96B-56BFB91FBB54}.Debug|x86.ActiveCfg = Debug|Any CPU + {DF92E098-822C-4B94-B96B-56BFB91FBB54}.Debug|x86.Build.0 = Debug|Any CPU {DF92E098-822C-4B94-B96B-56BFB91FBB54}.Release|Any CPU.ActiveCfg = Release|Any CPU {DF92E098-822C-4B94-B96B-56BFB91FBB54}.Release|Any CPU.Build.0 = Release|Any CPU + {DF92E098-822C-4B94-B96B-56BFB91FBB54}.Release|x64.ActiveCfg = Release|Any CPU + {DF92E098-822C-4B94-B96B-56BFB91FBB54}.Release|x64.Build.0 = Release|Any CPU + {DF92E098-822C-4B94-B96B-56BFB91FBB54}.Release|x86.ActiveCfg = Release|Any CPU + {DF92E098-822C-4B94-B96B-56BFB91FBB54}.Release|x86.Build.0 = Release|Any CPU + {E898F337-E982-46CC-8DA9-F8556AA7DD72}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E898F337-E982-46CC-8DA9-F8556AA7DD72}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E898F337-E982-46CC-8DA9-F8556AA7DD72}.Debug|x64.ActiveCfg = Debug|Any CPU + {E898F337-E982-46CC-8DA9-F8556AA7DD72}.Debug|x64.Build.0 = Debug|Any CPU + {E898F337-E982-46CC-8DA9-F8556AA7DD72}.Debug|x86.ActiveCfg = Debug|Any CPU + {E898F337-E982-46CC-8DA9-F8556AA7DD72}.Debug|x86.Build.0 = Debug|Any CPU + {E898F337-E982-46CC-8DA9-F8556AA7DD72}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E898F337-E982-46CC-8DA9-F8556AA7DD72}.Release|Any CPU.Build.0 = Release|Any CPU + {E898F337-E982-46CC-8DA9-F8556AA7DD72}.Release|x64.ActiveCfg = Release|Any CPU + {E898F337-E982-46CC-8DA9-F8556AA7DD72}.Release|x64.Build.0 = Release|Any CPU + {E898F337-E982-46CC-8DA9-F8556AA7DD72}.Release|x86.ActiveCfg = Release|Any CPU + {E898F337-E982-46CC-8DA9-F8556AA7DD72}.Release|x86.Build.0 = Release|Any CPU {C3CDF61C-3E28-441C-A9CE-011C89D11719}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C3CDF61C-3E28-441C-A9CE-011C89D11719}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C3CDF61C-3E28-441C-A9CE-011C89D11719}.Debug|x64.ActiveCfg = Debug|Any CPU + {C3CDF61C-3E28-441C-A9CE-011C89D11719}.Debug|x64.Build.0 = Debug|Any CPU + {C3CDF61C-3E28-441C-A9CE-011C89D11719}.Debug|x86.ActiveCfg = Debug|Any CPU + {C3CDF61C-3E28-441C-A9CE-011C89D11719}.Debug|x86.Build.0 = Debug|Any CPU {C3CDF61C-3E28-441C-A9CE-011C89D11719}.Release|Any CPU.ActiveCfg = Release|Any CPU {C3CDF61C-3E28-441C-A9CE-011C89D11719}.Release|Any CPU.Build.0 = Release|Any CPU + {C3CDF61C-3E28-441C-A9CE-011C89D11719}.Release|x64.ActiveCfg = Release|Any CPU + {C3CDF61C-3E28-441C-A9CE-011C89D11719}.Release|x64.Build.0 = Release|Any CPU + {C3CDF61C-3E28-441C-A9CE-011C89D11719}.Release|x86.ActiveCfg = Release|Any CPU + {C3CDF61C-3E28-441C-A9CE-011C89D11719}.Release|x86.Build.0 = Release|Any CPU {3A76FF7D-2F32-4EA5-8999-2FFE3C7CB893}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3A76FF7D-2F32-4EA5-8999-2FFE3C7CB893}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3A76FF7D-2F32-4EA5-8999-2FFE3C7CB893}.Debug|x64.ActiveCfg = Debug|Any CPU + {3A76FF7D-2F32-4EA5-8999-2FFE3C7CB893}.Debug|x64.Build.0 = Debug|Any CPU + {3A76FF7D-2F32-4EA5-8999-2FFE3C7CB893}.Debug|x86.ActiveCfg = Debug|Any CPU + {3A76FF7D-2F32-4EA5-8999-2FFE3C7CB893}.Debug|x86.Build.0 = Debug|Any CPU {3A76FF7D-2F32-4EA5-8999-2FFE3C7CB893}.Release|Any CPU.ActiveCfg = Release|Any CPU {3A76FF7D-2F32-4EA5-8999-2FFE3C7CB893}.Release|Any CPU.Build.0 = Release|Any CPU + {3A76FF7D-2F32-4EA5-8999-2FFE3C7CB893}.Release|x64.ActiveCfg = Release|Any CPU + {3A76FF7D-2F32-4EA5-8999-2FFE3C7CB893}.Release|x64.Build.0 = Release|Any CPU + {3A76FF7D-2F32-4EA5-8999-2FFE3C7CB893}.Release|x86.ActiveCfg = Release|Any CPU + {3A76FF7D-2F32-4EA5-8999-2FFE3C7CB893}.Release|x86.Build.0 = Release|Any CPU + {ADC91A84-6054-42EC-8241-0D717E4C7194}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ADC91A84-6054-42EC-8241-0D717E4C7194}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ADC91A84-6054-42EC-8241-0D717E4C7194}.Debug|x64.ActiveCfg = Debug|Any CPU + {ADC91A84-6054-42EC-8241-0D717E4C7194}.Debug|x64.Build.0 = Debug|Any CPU + {ADC91A84-6054-42EC-8241-0D717E4C7194}.Debug|x86.ActiveCfg = Debug|Any CPU + {ADC91A84-6054-42EC-8241-0D717E4C7194}.Debug|x86.Build.0 = Debug|Any CPU + {ADC91A84-6054-42EC-8241-0D717E4C7194}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ADC91A84-6054-42EC-8241-0D717E4C7194}.Release|Any CPU.Build.0 = Release|Any CPU + {ADC91A84-6054-42EC-8241-0D717E4C7194}.Release|x64.ActiveCfg = Release|Any CPU + {ADC91A84-6054-42EC-8241-0D717E4C7194}.Release|x64.Build.0 = Release|Any CPU + {ADC91A84-6054-42EC-8241-0D717E4C7194}.Release|x86.ActiveCfg = Release|Any CPU + {ADC91A84-6054-42EC-8241-0D717E4C7194}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -628,14 +1332,16 @@ Global {407C477D-69C0-4B02-8A68-EE6B5A81C696} = {21B42F60-5802-404E-90F0-AEBCC56760C0} {EADF25F5-8D02-4747-AB54-5F2BAA648471} = {230B9384-90FD-4551-A5DE-1A5C197F25B6} {46E40BE8-1AB0-4846-B0A2-A40AD0272C64} = {6987A1CC-608E-4868-A02C-09D30C8B7B2D} - {8298202C-9983-4D0A-851D-805539EE481A} = {230B9384-90FD-4551-A5DE-1A5C197F25B6} {A5B26C14-7313-4EDC-91E3-287F9374AB75} = {21B42F60-5802-404E-90F0-AEBCC56760C0} + {8298202C-9983-4D0A-851D-805539EE481A} = {230B9384-90FD-4551-A5DE-1A5C197F25B6} {E36C8DCA-464E-41CB-B189-F58553AAA8D4} = {230B9384-90FD-4551-A5DE-1A5C197F25B6} {5A17FEF9-07BB-47B8-9883-9C2CC93F09E8} = {6987A1CC-608E-4868-A02C-09D30C8B7B2D} {D7DF0B26-AD43-4F8B-9BFE-C4471CCC9821} = {21B42F60-5802-404E-90F0-AEBCC56760C0} {6030B748-0000-43B5-B8A8-399AA42F5229} = {6987A1CC-608E-4868-A02C-09D30C8B7B2D} {DF92E098-822C-4B94-B96B-56BFB91FBB54} = {6987A1CC-608E-4868-A02C-09D30C8B7B2D} + {E898F337-E982-46CC-8DA9-F8556AA7DD72} = {230B9384-90FD-4551-A5DE-1A5C197F25B6} {C3CDF61C-3E28-441C-A9CE-011C89D11719} = {230B9384-90FD-4551-A5DE-1A5C197F25B6} {3A76FF7D-2F32-4EA5-8999-2FFE3C7CB893} = {6987A1CC-608E-4868-A02C-09D30C8B7B2D} + {ADC91A84-6054-42EC-8241-0D717E4C7194} = {6987A1CC-608E-4868-A02C-09D30C8B7B2D} EndGlobalSection EndGlobal diff --git a/CHANGELOG.md b/CHANGELOG.md index ab4e1b6a3f..23385ed7ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Features + +- .NET MAUI integration with CommunityToolkit.Mvvm Async Relay Commands can now be auto spanned with the new package Sentry.Maui.CommunityToolkit.Mvvm ([#4125](https://github.com/getsentry/sentry-dotnet/pull/4125)) + ### Dependencies - Bump Native SDK from v0.8.4 to v0.8.5 ([#4189](https://github.com/getsentry/sentry-dotnet/pull/4189)) diff --git a/Sentry-CI-Build-Linux.slnf b/Sentry-CI-Build-Linux.slnf index 70d49a65ef..8b3ca210c0 100644 --- a/Sentry-CI-Build-Linux.slnf +++ b/Sentry-CI-Build-Linux.slnf @@ -44,6 +44,7 @@ "src\\Sentry.Google.Cloud.Functions\\Sentry.Google.Cloud.Functions.csproj", "src\\Sentry.Hangfire\\Sentry.Hangfire.csproj", "src\\Sentry.Log4Net\\Sentry.Log4Net.csproj", + "src\\Sentry.Maui.CommunityToolkit.Mvvm\\Sentry.Maui.CommunityToolkit.Mvvm.csproj", "src\\Sentry.Maui\\Sentry.Maui.csproj", "src\\Sentry.NLog\\Sentry.NLog.csproj", "src\\Sentry.OpenTelemetry\\Sentry.OpenTelemetry.csproj", @@ -65,6 +66,7 @@ "test\\Sentry.Google.Cloud.Functions.Tests\\Sentry.Google.Cloud.Functions.Tests.csproj", "test\\Sentry.Hangfire.Tests\\Sentry.Hangfire.Tests.csproj", "test\\Sentry.Log4Net.Tests\\Sentry.Log4Net.Tests.csproj", + "test\\Sentry.Maui.CommunityToolkit.Mvvm.Tests\\Sentry.Maui.CommunityToolkit.Mvvm.Tests.csproj", "test\\Sentry.Maui.Tests\\Sentry.Maui.Tests.csproj", "test\\Sentry.NLog.Tests\\Sentry.NLog.Tests.csproj", "test\\Sentry.OpenTelemetry.Tests\\Sentry.OpenTelemetry.Tests.csproj", diff --git a/Sentry-CI-Build-Windows.slnf b/Sentry-CI-Build-Windows.slnf index 0e2a79aa0c..d5960f2212 100644 --- a/Sentry-CI-Build-Windows.slnf +++ b/Sentry-CI-Build-Windows.slnf @@ -46,6 +46,7 @@ "src\\Sentry.Google.Cloud.Functions\\Sentry.Google.Cloud.Functions.csproj", "src\\Sentry.Hangfire\\Sentry.Hangfire.csproj", "src\\Sentry.Log4Net\\Sentry.Log4Net.csproj", + "src\\Sentry.Maui.CommunityToolkit.Mvvm\\Sentry.Maui.CommunityToolkit.Mvvm.csproj", "src\\Sentry.Maui\\Sentry.Maui.csproj", "src\\Sentry.NLog\\Sentry.NLog.csproj", "src\\Sentry.OpenTelemetry\\Sentry.OpenTelemetry.csproj", @@ -67,6 +68,7 @@ "test\\Sentry.Google.Cloud.Functions.Tests\\Sentry.Google.Cloud.Functions.Tests.csproj", "test\\Sentry.Hangfire.Tests\\Sentry.Hangfire.Tests.csproj", "test\\Sentry.Log4Net.Tests\\Sentry.Log4Net.Tests.csproj", + "test\\Sentry.Maui.CommunityToolkit.Mvvm.Tests\\Sentry.Maui.CommunityToolkit.Mvvm.Tests.csproj", "test\\Sentry.Maui.Tests\\Sentry.Maui.Tests.csproj", "test\\Sentry.NLog.Tests\\Sentry.NLog.Tests.csproj", "test\\Sentry.OpenTelemetry.Tests\\Sentry.OpenTelemetry.Tests.csproj", diff --git a/Sentry-CI-Build-macOS.slnf b/Sentry-CI-Build-macOS.slnf index fa6698101a..e654c81e63 100644 --- a/Sentry-CI-Build-macOS.slnf +++ b/Sentry-CI-Build-macOS.slnf @@ -51,6 +51,7 @@ "src\\Sentry.Google.Cloud.Functions\\Sentry.Google.Cloud.Functions.csproj", "src\\Sentry.Hangfire\\Sentry.Hangfire.csproj", "src\\Sentry.Log4Net\\Sentry.Log4Net.csproj", + "src\\Sentry.Maui.CommunityToolkit.Mvvm\\Sentry.Maui.CommunityToolkit.Mvvm.csproj", "src\\Sentry.Maui\\Sentry.Maui.csproj", "src\\Sentry.NLog\\Sentry.NLog.csproj", "src\\Sentry.OpenTelemetry\\Sentry.OpenTelemetry.csproj", @@ -72,6 +73,7 @@ "test\\Sentry.Google.Cloud.Functions.Tests\\Sentry.Google.Cloud.Functions.Tests.csproj", "test\\Sentry.Hangfire.Tests\\Sentry.Hangfire.Tests.csproj", "test\\Sentry.Log4Net.Tests\\Sentry.Log4Net.Tests.csproj", + "test\\Sentry.Maui.CommunityToolkit.Mvvm.Tests\\Sentry.Maui.CommunityToolkit.Mvvm.Tests.csproj", "test\\Sentry.Maui.Device.TestApp\\Sentry.Maui.Device.TestApp.csproj", "test\\Sentry.Maui.Tests\\Sentry.Maui.Tests.csproj", "test\\Sentry.NLog.Tests\\Sentry.NLog.Tests.csproj", diff --git a/Sentry-CI-CodeQL.slnf b/Sentry-CI-CodeQL.slnf index be252d4fe2..0dbf4453dd 100644 --- a/Sentry-CI-CodeQL.slnf +++ b/Sentry-CI-CodeQL.slnf @@ -15,6 +15,7 @@ "src\\Sentry.Google.Cloud.Functions\\Sentry.Google.Cloud.Functions.csproj", "src\\Sentry.Hangfire\\Sentry.Hangfire.csproj", "src\\Sentry.Log4Net\\Sentry.Log4Net.csproj", + "src\\Sentry.Maui.CommunityToolkit.Mvvm\\Sentry.Maui.CommunityToolkit.Mvvm.csproj", "src\\Sentry.Maui\\Sentry.Maui.csproj", "src\\Sentry.NLog\\Sentry.NLog.csproj", "src\\Sentry.OpenTelemetry\\Sentry.OpenTelemetry.csproj", diff --git a/Sentry.sln b/Sentry.sln index 762d22068b..3730377a46 100644 --- a/Sentry.sln +++ b/Sentry.sln @@ -1,4 +1,4 @@ - + Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.0.31903.59 @@ -201,353 +201,1057 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.TrimTest", "test\Sen EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.MauiTrimTest", "test\Sentry.MauiTrimTest\Sentry.MauiTrimTest.csproj", "{DF92E098-822C-4B94-B96B-56BFB91FBB54}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Maui.CommunityToolkit.Mvvm", "src\Sentry.Maui.CommunityToolkit.Mvvm\Sentry.Maui.CommunityToolkit.Mvvm.csproj", "{E898F337-E982-46CC-8DA9-F8556AA7DD72}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.SourceGenerators", "src\Sentry.SourceGenerators\Sentry.SourceGenerators.csproj", "{C3CDF61C-3E28-441C-A9CE-011C89D11719}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.SourceGenerators.Tests", "test\Sentry.SourceGenerators.Tests\Sentry.SourceGenerators.Tests.csproj", "{3A76FF7D-2F32-4EA5-8999-2FFE3C7CB893}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Maui.CommunityToolkit.Mvvm.Tests", "test\Sentry.Maui.CommunityToolkit.Mvvm.Tests\Sentry.Maui.CommunityToolkit.Mvvm.Tests.csproj", "{ADC91A84-6054-42EC-8241-0D717E4C7194}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {8328B70C-B808-4ED1-BB16-8555B2752CB6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8328B70C-B808-4ED1-BB16-8555B2752CB6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8328B70C-B808-4ED1-BB16-8555B2752CB6}.Debug|x64.ActiveCfg = Debug|Any CPU + {8328B70C-B808-4ED1-BB16-8555B2752CB6}.Debug|x64.Build.0 = Debug|Any CPU + {8328B70C-B808-4ED1-BB16-8555B2752CB6}.Debug|x86.ActiveCfg = Debug|Any CPU + {8328B70C-B808-4ED1-BB16-8555B2752CB6}.Debug|x86.Build.0 = Debug|Any CPU {8328B70C-B808-4ED1-BB16-8555B2752CB6}.Release|Any CPU.ActiveCfg = Release|Any CPU {8328B70C-B808-4ED1-BB16-8555B2752CB6}.Release|Any CPU.Build.0 = Release|Any CPU + {8328B70C-B808-4ED1-BB16-8555B2752CB6}.Release|x64.ActiveCfg = Release|Any CPU + {8328B70C-B808-4ED1-BB16-8555B2752CB6}.Release|x64.Build.0 = Release|Any CPU + {8328B70C-B808-4ED1-BB16-8555B2752CB6}.Release|x86.ActiveCfg = Release|Any CPU + {8328B70C-B808-4ED1-BB16-8555B2752CB6}.Release|x86.Build.0 = Release|Any CPU {1368AFBF-BCB7-4585-B8A9-C0E767792BC1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1368AFBF-BCB7-4585-B8A9-C0E767792BC1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1368AFBF-BCB7-4585-B8A9-C0E767792BC1}.Debug|x64.ActiveCfg = Debug|Any CPU + {1368AFBF-BCB7-4585-B8A9-C0E767792BC1}.Debug|x64.Build.0 = Debug|Any CPU + {1368AFBF-BCB7-4585-B8A9-C0E767792BC1}.Debug|x86.ActiveCfg = Debug|Any CPU + {1368AFBF-BCB7-4585-B8A9-C0E767792BC1}.Debug|x86.Build.0 = Debug|Any CPU {1368AFBF-BCB7-4585-B8A9-C0E767792BC1}.Release|Any CPU.ActiveCfg = Release|Any CPU {1368AFBF-BCB7-4585-B8A9-C0E767792BC1}.Release|Any CPU.Build.0 = Release|Any CPU + {1368AFBF-BCB7-4585-B8A9-C0E767792BC1}.Release|x64.ActiveCfg = Release|Any CPU + {1368AFBF-BCB7-4585-B8A9-C0E767792BC1}.Release|x64.Build.0 = Release|Any CPU + {1368AFBF-BCB7-4585-B8A9-C0E767792BC1}.Release|x86.ActiveCfg = Release|Any CPU + {1368AFBF-BCB7-4585-B8A9-C0E767792BC1}.Release|x86.Build.0 = Release|Any CPU {3E5E5AAD-4563-4428-8577-2A2A46137BFC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3E5E5AAD-4563-4428-8577-2A2A46137BFC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3E5E5AAD-4563-4428-8577-2A2A46137BFC}.Debug|x64.ActiveCfg = Debug|Any CPU + {3E5E5AAD-4563-4428-8577-2A2A46137BFC}.Debug|x64.Build.0 = Debug|Any CPU + {3E5E5AAD-4563-4428-8577-2A2A46137BFC}.Debug|x86.ActiveCfg = Debug|Any CPU + {3E5E5AAD-4563-4428-8577-2A2A46137BFC}.Debug|x86.Build.0 = Debug|Any CPU {3E5E5AAD-4563-4428-8577-2A2A46137BFC}.Release|Any CPU.ActiveCfg = Release|Any CPU {3E5E5AAD-4563-4428-8577-2A2A46137BFC}.Release|Any CPU.Build.0 = Release|Any CPU + {3E5E5AAD-4563-4428-8577-2A2A46137BFC}.Release|x64.ActiveCfg = Release|Any CPU + {3E5E5AAD-4563-4428-8577-2A2A46137BFC}.Release|x64.Build.0 = Release|Any CPU + {3E5E5AAD-4563-4428-8577-2A2A46137BFC}.Release|x86.ActiveCfg = Release|Any CPU + {3E5E5AAD-4563-4428-8577-2A2A46137BFC}.Release|x86.Build.0 = Release|Any CPU {789A9F8A-08A9-4211-A4AB-F45633960D94}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {789A9F8A-08A9-4211-A4AB-F45633960D94}.Debug|Any CPU.Build.0 = Debug|Any CPU + {789A9F8A-08A9-4211-A4AB-F45633960D94}.Debug|x64.ActiveCfg = Debug|Any CPU + {789A9F8A-08A9-4211-A4AB-F45633960D94}.Debug|x64.Build.0 = Debug|Any CPU + {789A9F8A-08A9-4211-A4AB-F45633960D94}.Debug|x86.ActiveCfg = Debug|Any CPU + {789A9F8A-08A9-4211-A4AB-F45633960D94}.Debug|x86.Build.0 = Debug|Any CPU {789A9F8A-08A9-4211-A4AB-F45633960D94}.Release|Any CPU.ActiveCfg = Release|Any CPU {789A9F8A-08A9-4211-A4AB-F45633960D94}.Release|Any CPU.Build.0 = Release|Any CPU + {789A9F8A-08A9-4211-A4AB-F45633960D94}.Release|x64.ActiveCfg = Release|Any CPU + {789A9F8A-08A9-4211-A4AB-F45633960D94}.Release|x64.Build.0 = Release|Any CPU + {789A9F8A-08A9-4211-A4AB-F45633960D94}.Release|x86.ActiveCfg = Release|Any CPU + {789A9F8A-08A9-4211-A4AB-F45633960D94}.Release|x86.Build.0 = Release|Any CPU {E683FB73-A305-462A-8F76-880E7A2F7F74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E683FB73-A305-462A-8F76-880E7A2F7F74}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E683FB73-A305-462A-8F76-880E7A2F7F74}.Debug|x64.ActiveCfg = Debug|Any CPU + {E683FB73-A305-462A-8F76-880E7A2F7F74}.Debug|x64.Build.0 = Debug|Any CPU + {E683FB73-A305-462A-8F76-880E7A2F7F74}.Debug|x86.ActiveCfg = Debug|Any CPU + {E683FB73-A305-462A-8F76-880E7A2F7F74}.Debug|x86.Build.0 = Debug|Any CPU {E683FB73-A305-462A-8F76-880E7A2F7F74}.Release|Any CPU.ActiveCfg = Release|Any CPU {E683FB73-A305-462A-8F76-880E7A2F7F74}.Release|Any CPU.Build.0 = Release|Any CPU + {E683FB73-A305-462A-8F76-880E7A2F7F74}.Release|x64.ActiveCfg = Release|Any CPU + {E683FB73-A305-462A-8F76-880E7A2F7F74}.Release|x64.Build.0 = Release|Any CPU + {E683FB73-A305-462A-8F76-880E7A2F7F74}.Release|x86.ActiveCfg = Release|Any CPU + {E683FB73-A305-462A-8F76-880E7A2F7F74}.Release|x86.Build.0 = Release|Any CPU {E53C49EE-FC70-4B26-A62A-63CAD51DB399}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E53C49EE-FC70-4B26-A62A-63CAD51DB399}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E53C49EE-FC70-4B26-A62A-63CAD51DB399}.Debug|x64.ActiveCfg = Debug|Any CPU + {E53C49EE-FC70-4B26-A62A-63CAD51DB399}.Debug|x64.Build.0 = Debug|Any CPU + {E53C49EE-FC70-4B26-A62A-63CAD51DB399}.Debug|x86.ActiveCfg = Debug|Any CPU + {E53C49EE-FC70-4B26-A62A-63CAD51DB399}.Debug|x86.Build.0 = Debug|Any CPU {E53C49EE-FC70-4B26-A62A-63CAD51DB399}.Release|Any CPU.ActiveCfg = Release|Any CPU {E53C49EE-FC70-4B26-A62A-63CAD51DB399}.Release|Any CPU.Build.0 = Release|Any CPU + {E53C49EE-FC70-4B26-A62A-63CAD51DB399}.Release|x64.ActiveCfg = Release|Any CPU + {E53C49EE-FC70-4B26-A62A-63CAD51DB399}.Release|x64.Build.0 = Release|Any CPU + {E53C49EE-FC70-4B26-A62A-63CAD51DB399}.Release|x86.ActiveCfg = Release|Any CPU + {E53C49EE-FC70-4B26-A62A-63CAD51DB399}.Release|x86.Build.0 = Release|Any CPU {F749CC16-C32B-441D-9ACE-E8F748E977E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F749CC16-C32B-441D-9ACE-E8F748E977E0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F749CC16-C32B-441D-9ACE-E8F748E977E0}.Debug|x64.ActiveCfg = Debug|Any CPU + {F749CC16-C32B-441D-9ACE-E8F748E977E0}.Debug|x64.Build.0 = Debug|Any CPU + {F749CC16-C32B-441D-9ACE-E8F748E977E0}.Debug|x86.ActiveCfg = Debug|Any CPU + {F749CC16-C32B-441D-9ACE-E8F748E977E0}.Debug|x86.Build.0 = Debug|Any CPU {F749CC16-C32B-441D-9ACE-E8F748E977E0}.Release|Any CPU.ActiveCfg = Release|Any CPU {F749CC16-C32B-441D-9ACE-E8F748E977E0}.Release|Any CPU.Build.0 = Release|Any CPU + {F749CC16-C32B-441D-9ACE-E8F748E977E0}.Release|x64.ActiveCfg = Release|Any CPU + {F749CC16-C32B-441D-9ACE-E8F748E977E0}.Release|x64.Build.0 = Release|Any CPU + {F749CC16-C32B-441D-9ACE-E8F748E977E0}.Release|x86.ActiveCfg = Release|Any CPU + {F749CC16-C32B-441D-9ACE-E8F748E977E0}.Release|x86.Build.0 = Release|Any CPU {609D99ED-3E50-49DF-A3CC-2371FD02520A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {609D99ED-3E50-49DF-A3CC-2371FD02520A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {609D99ED-3E50-49DF-A3CC-2371FD02520A}.Debug|x64.ActiveCfg = Debug|Any CPU + {609D99ED-3E50-49DF-A3CC-2371FD02520A}.Debug|x64.Build.0 = Debug|Any CPU + {609D99ED-3E50-49DF-A3CC-2371FD02520A}.Debug|x86.ActiveCfg = Debug|Any CPU + {609D99ED-3E50-49DF-A3CC-2371FD02520A}.Debug|x86.Build.0 = Debug|Any CPU {609D99ED-3E50-49DF-A3CC-2371FD02520A}.Release|Any CPU.ActiveCfg = Release|Any CPU {609D99ED-3E50-49DF-A3CC-2371FD02520A}.Release|Any CPU.Build.0 = Release|Any CPU + {609D99ED-3E50-49DF-A3CC-2371FD02520A}.Release|x64.ActiveCfg = Release|Any CPU + {609D99ED-3E50-49DF-A3CC-2371FD02520A}.Release|x64.Build.0 = Release|Any CPU + {609D99ED-3E50-49DF-A3CC-2371FD02520A}.Release|x86.ActiveCfg = Release|Any CPU + {609D99ED-3E50-49DF-A3CC-2371FD02520A}.Release|x86.Build.0 = Release|Any CPU {50116F9A-646D-4BF7-9760-66E37CB9C459}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {50116F9A-646D-4BF7-9760-66E37CB9C459}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50116F9A-646D-4BF7-9760-66E37CB9C459}.Debug|x64.ActiveCfg = Debug|Any CPU + {50116F9A-646D-4BF7-9760-66E37CB9C459}.Debug|x64.Build.0 = Debug|Any CPU + {50116F9A-646D-4BF7-9760-66E37CB9C459}.Debug|x86.ActiveCfg = Debug|Any CPU + {50116F9A-646D-4BF7-9760-66E37CB9C459}.Debug|x86.Build.0 = Debug|Any CPU {50116F9A-646D-4BF7-9760-66E37CB9C459}.Release|Any CPU.ActiveCfg = Release|Any CPU {50116F9A-646D-4BF7-9760-66E37CB9C459}.Release|Any CPU.Build.0 = Release|Any CPU + {50116F9A-646D-4BF7-9760-66E37CB9C459}.Release|x64.ActiveCfg = Release|Any CPU + {50116F9A-646D-4BF7-9760-66E37CB9C459}.Release|x64.Build.0 = Release|Any CPU + {50116F9A-646D-4BF7-9760-66E37CB9C459}.Release|x86.ActiveCfg = Release|Any CPU + {50116F9A-646D-4BF7-9760-66E37CB9C459}.Release|x86.Build.0 = Release|Any CPU {1F44075F-ABD6-49A4-8EA1-DBB70304AD24}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1F44075F-ABD6-49A4-8EA1-DBB70304AD24}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1F44075F-ABD6-49A4-8EA1-DBB70304AD24}.Debug|x64.ActiveCfg = Debug|Any CPU + {1F44075F-ABD6-49A4-8EA1-DBB70304AD24}.Debug|x64.Build.0 = Debug|Any CPU + {1F44075F-ABD6-49A4-8EA1-DBB70304AD24}.Debug|x86.ActiveCfg = Debug|Any CPU + {1F44075F-ABD6-49A4-8EA1-DBB70304AD24}.Debug|x86.Build.0 = Debug|Any CPU {1F44075F-ABD6-49A4-8EA1-DBB70304AD24}.Release|Any CPU.ActiveCfg = Release|Any CPU {1F44075F-ABD6-49A4-8EA1-DBB70304AD24}.Release|Any CPU.Build.0 = Release|Any CPU + {1F44075F-ABD6-49A4-8EA1-DBB70304AD24}.Release|x64.ActiveCfg = Release|Any CPU + {1F44075F-ABD6-49A4-8EA1-DBB70304AD24}.Release|x64.Build.0 = Release|Any CPU + {1F44075F-ABD6-49A4-8EA1-DBB70304AD24}.Release|x86.ActiveCfg = Release|Any CPU + {1F44075F-ABD6-49A4-8EA1-DBB70304AD24}.Release|x86.Build.0 = Release|Any CPU {B793249D-3E52-4B0D-964F-BC022CD8A753}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B793249D-3E52-4B0D-964F-BC022CD8A753}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B793249D-3E52-4B0D-964F-BC022CD8A753}.Debug|x64.ActiveCfg = Debug|Any CPU + {B793249D-3E52-4B0D-964F-BC022CD8A753}.Debug|x64.Build.0 = Debug|Any CPU + {B793249D-3E52-4B0D-964F-BC022CD8A753}.Debug|x86.ActiveCfg = Debug|Any CPU + {B793249D-3E52-4B0D-964F-BC022CD8A753}.Debug|x86.Build.0 = Debug|Any CPU {B793249D-3E52-4B0D-964F-BC022CD8A753}.Release|Any CPU.ActiveCfg = Release|Any CPU {B793249D-3E52-4B0D-964F-BC022CD8A753}.Release|Any CPU.Build.0 = Release|Any CPU + {B793249D-3E52-4B0D-964F-BC022CD8A753}.Release|x64.ActiveCfg = Release|Any CPU + {B793249D-3E52-4B0D-964F-BC022CD8A753}.Release|x64.Build.0 = Release|Any CPU + {B793249D-3E52-4B0D-964F-BC022CD8A753}.Release|x86.ActiveCfg = Release|Any CPU + {B793249D-3E52-4B0D-964F-BC022CD8A753}.Release|x86.Build.0 = Release|Any CPU {8FA2FE59-9333-47B8-A226-E2B0CDBB5847}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8FA2FE59-9333-47B8-A226-E2B0CDBB5847}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8FA2FE59-9333-47B8-A226-E2B0CDBB5847}.Debug|x64.ActiveCfg = Debug|Any CPU + {8FA2FE59-9333-47B8-A226-E2B0CDBB5847}.Debug|x64.Build.0 = Debug|Any CPU + {8FA2FE59-9333-47B8-A226-E2B0CDBB5847}.Debug|x86.ActiveCfg = Debug|Any CPU + {8FA2FE59-9333-47B8-A226-E2B0CDBB5847}.Debug|x86.Build.0 = Debug|Any CPU {8FA2FE59-9333-47B8-A226-E2B0CDBB5847}.Release|Any CPU.ActiveCfg = Release|Any CPU {8FA2FE59-9333-47B8-A226-E2B0CDBB5847}.Release|Any CPU.Build.0 = Release|Any CPU + {8FA2FE59-9333-47B8-A226-E2B0CDBB5847}.Release|x64.ActiveCfg = Release|Any CPU + {8FA2FE59-9333-47B8-A226-E2B0CDBB5847}.Release|x64.Build.0 = Release|Any CPU + {8FA2FE59-9333-47B8-A226-E2B0CDBB5847}.Release|x86.ActiveCfg = Release|Any CPU + {8FA2FE59-9333-47B8-A226-E2B0CDBB5847}.Release|x86.Build.0 = Release|Any CPU {0D9861C5-D081-40F7-9DFC-7032840471AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0D9861C5-D081-40F7-9DFC-7032840471AB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0D9861C5-D081-40F7-9DFC-7032840471AB}.Debug|x64.ActiveCfg = Debug|Any CPU + {0D9861C5-D081-40F7-9DFC-7032840471AB}.Debug|x64.Build.0 = Debug|Any CPU + {0D9861C5-D081-40F7-9DFC-7032840471AB}.Debug|x86.ActiveCfg = Debug|Any CPU + {0D9861C5-D081-40F7-9DFC-7032840471AB}.Debug|x86.Build.0 = Debug|Any CPU {0D9861C5-D081-40F7-9DFC-7032840471AB}.Release|Any CPU.ActiveCfg = Release|Any CPU {0D9861C5-D081-40F7-9DFC-7032840471AB}.Release|Any CPU.Build.0 = Release|Any CPU + {0D9861C5-D081-40F7-9DFC-7032840471AB}.Release|x64.ActiveCfg = Release|Any CPU + {0D9861C5-D081-40F7-9DFC-7032840471AB}.Release|x64.Build.0 = Release|Any CPU + {0D9861C5-D081-40F7-9DFC-7032840471AB}.Release|x86.ActiveCfg = Release|Any CPU + {0D9861C5-D081-40F7-9DFC-7032840471AB}.Release|x86.Build.0 = Release|Any CPU {EEBE64CF-B1B5-4C54-B9B4-47ED7E5E760A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EEBE64CF-B1B5-4C54-B9B4-47ED7E5E760A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EEBE64CF-B1B5-4C54-B9B4-47ED7E5E760A}.Debug|x64.ActiveCfg = Debug|Any CPU + {EEBE64CF-B1B5-4C54-B9B4-47ED7E5E760A}.Debug|x64.Build.0 = Debug|Any CPU + {EEBE64CF-B1B5-4C54-B9B4-47ED7E5E760A}.Debug|x86.ActiveCfg = Debug|Any CPU + {EEBE64CF-B1B5-4C54-B9B4-47ED7E5E760A}.Debug|x86.Build.0 = Debug|Any CPU {EEBE64CF-B1B5-4C54-B9B4-47ED7E5E760A}.Release|Any CPU.ActiveCfg = Release|Any CPU {EEBE64CF-B1B5-4C54-B9B4-47ED7E5E760A}.Release|Any CPU.Build.0 = Release|Any CPU + {EEBE64CF-B1B5-4C54-B9B4-47ED7E5E760A}.Release|x64.ActiveCfg = Release|Any CPU + {EEBE64CF-B1B5-4C54-B9B4-47ED7E5E760A}.Release|x64.Build.0 = Release|Any CPU + {EEBE64CF-B1B5-4C54-B9B4-47ED7E5E760A}.Release|x86.ActiveCfg = Release|Any CPU + {EEBE64CF-B1B5-4C54-B9B4-47ED7E5E760A}.Release|x86.Build.0 = Release|Any CPU {9658457F-075C-4C44-A7AD-947365938B6C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9658457F-075C-4C44-A7AD-947365938B6C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9658457F-075C-4C44-A7AD-947365938B6C}.Debug|x64.ActiveCfg = Debug|Any CPU + {9658457F-075C-4C44-A7AD-947365938B6C}.Debug|x64.Build.0 = Debug|Any CPU + {9658457F-075C-4C44-A7AD-947365938B6C}.Debug|x86.ActiveCfg = Debug|Any CPU + {9658457F-075C-4C44-A7AD-947365938B6C}.Debug|x86.Build.0 = Debug|Any CPU {9658457F-075C-4C44-A7AD-947365938B6C}.Release|Any CPU.ActiveCfg = Release|Any CPU {9658457F-075C-4C44-A7AD-947365938B6C}.Release|Any CPU.Build.0 = Release|Any CPU + {9658457F-075C-4C44-A7AD-947365938B6C}.Release|x64.ActiveCfg = Release|Any CPU + {9658457F-075C-4C44-A7AD-947365938B6C}.Release|x64.Build.0 = Release|Any CPU + {9658457F-075C-4C44-A7AD-947365938B6C}.Release|x86.ActiveCfg = Release|Any CPU + {9658457F-075C-4C44-A7AD-947365938B6C}.Release|x86.Build.0 = Release|Any CPU {AB93DF75-C53F-43F7-B1EB-B679240D112B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {AB93DF75-C53F-43F7-B1EB-B679240D112B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AB93DF75-C53F-43F7-B1EB-B679240D112B}.Debug|x64.ActiveCfg = Debug|Any CPU + {AB93DF75-C53F-43F7-B1EB-B679240D112B}.Debug|x64.Build.0 = Debug|Any CPU + {AB93DF75-C53F-43F7-B1EB-B679240D112B}.Debug|x86.ActiveCfg = Debug|Any CPU + {AB93DF75-C53F-43F7-B1EB-B679240D112B}.Debug|x86.Build.0 = Debug|Any CPU {AB93DF75-C53F-43F7-B1EB-B679240D112B}.Release|Any CPU.ActiveCfg = Release|Any CPU {AB93DF75-C53F-43F7-B1EB-B679240D112B}.Release|Any CPU.Build.0 = Release|Any CPU + {AB93DF75-C53F-43F7-B1EB-B679240D112B}.Release|x64.ActiveCfg = Release|Any CPU + {AB93DF75-C53F-43F7-B1EB-B679240D112B}.Release|x64.Build.0 = Release|Any CPU + {AB93DF75-C53F-43F7-B1EB-B679240D112B}.Release|x86.ActiveCfg = Release|Any CPU + {AB93DF75-C53F-43F7-B1EB-B679240D112B}.Release|x86.Build.0 = Release|Any CPU {1E5E7A45-9826-4F06-8C68-B9F1DB05C4F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1E5E7A45-9826-4F06-8C68-B9F1DB05C4F2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1E5E7A45-9826-4F06-8C68-B9F1DB05C4F2}.Debug|x64.ActiveCfg = Debug|Any CPU + {1E5E7A45-9826-4F06-8C68-B9F1DB05C4F2}.Debug|x64.Build.0 = Debug|Any CPU + {1E5E7A45-9826-4F06-8C68-B9F1DB05C4F2}.Debug|x86.ActiveCfg = Debug|Any CPU + {1E5E7A45-9826-4F06-8C68-B9F1DB05C4F2}.Debug|x86.Build.0 = Debug|Any CPU {1E5E7A45-9826-4F06-8C68-B9F1DB05C4F2}.Release|Any CPU.ActiveCfg = Release|Any CPU {1E5E7A45-9826-4F06-8C68-B9F1DB05C4F2}.Release|Any CPU.Build.0 = Release|Any CPU + {1E5E7A45-9826-4F06-8C68-B9F1DB05C4F2}.Release|x64.ActiveCfg = Release|Any CPU + {1E5E7A45-9826-4F06-8C68-B9F1DB05C4F2}.Release|x64.Build.0 = Release|Any CPU + {1E5E7A45-9826-4F06-8C68-B9F1DB05C4F2}.Release|x86.ActiveCfg = Release|Any CPU + {1E5E7A45-9826-4F06-8C68-B9F1DB05C4F2}.Release|x86.Build.0 = Release|Any CPU {D58B814E-1F19-43AE-89D1-769BC7681A56}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D58B814E-1F19-43AE-89D1-769BC7681A56}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D58B814E-1F19-43AE-89D1-769BC7681A56}.Debug|x64.ActiveCfg = Debug|Any CPU + {D58B814E-1F19-43AE-89D1-769BC7681A56}.Debug|x64.Build.0 = Debug|Any CPU + {D58B814E-1F19-43AE-89D1-769BC7681A56}.Debug|x86.ActiveCfg = Debug|Any CPU + {D58B814E-1F19-43AE-89D1-769BC7681A56}.Debug|x86.Build.0 = Debug|Any CPU {D58B814E-1F19-43AE-89D1-769BC7681A56}.Release|Any CPU.ActiveCfg = Release|Any CPU {D58B814E-1F19-43AE-89D1-769BC7681A56}.Release|Any CPU.Build.0 = Release|Any CPU + {D58B814E-1F19-43AE-89D1-769BC7681A56}.Release|x64.ActiveCfg = Release|Any CPU + {D58B814E-1F19-43AE-89D1-769BC7681A56}.Release|x64.Build.0 = Release|Any CPU + {D58B814E-1F19-43AE-89D1-769BC7681A56}.Release|x86.ActiveCfg = Release|Any CPU + {D58B814E-1F19-43AE-89D1-769BC7681A56}.Release|x86.Build.0 = Release|Any CPU {C181C7D4-CA45-4FAE-8315-A9825F034D53}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C181C7D4-CA45-4FAE-8315-A9825F034D53}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C181C7D4-CA45-4FAE-8315-A9825F034D53}.Debug|x64.ActiveCfg = Debug|Any CPU + {C181C7D4-CA45-4FAE-8315-A9825F034D53}.Debug|x64.Build.0 = Debug|Any CPU + {C181C7D4-CA45-4FAE-8315-A9825F034D53}.Debug|x86.ActiveCfg = Debug|Any CPU + {C181C7D4-CA45-4FAE-8315-A9825F034D53}.Debug|x86.Build.0 = Debug|Any CPU {C181C7D4-CA45-4FAE-8315-A9825F034D53}.Release|Any CPU.ActiveCfg = Release|Any CPU {C181C7D4-CA45-4FAE-8315-A9825F034D53}.Release|Any CPU.Build.0 = Release|Any CPU + {C181C7D4-CA45-4FAE-8315-A9825F034D53}.Release|x64.ActiveCfg = Release|Any CPU + {C181C7D4-CA45-4FAE-8315-A9825F034D53}.Release|x64.Build.0 = Release|Any CPU + {C181C7D4-CA45-4FAE-8315-A9825F034D53}.Release|x86.ActiveCfg = Release|Any CPU + {C181C7D4-CA45-4FAE-8315-A9825F034D53}.Release|x86.Build.0 = Release|Any CPU {4056B8FD-F355-41A3-A34F-60B350598B33}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4056B8FD-F355-41A3-A34F-60B350598B33}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4056B8FD-F355-41A3-A34F-60B350598B33}.Debug|x64.ActiveCfg = Debug|Any CPU + {4056B8FD-F355-41A3-A34F-60B350598B33}.Debug|x64.Build.0 = Debug|Any CPU + {4056B8FD-F355-41A3-A34F-60B350598B33}.Debug|x86.ActiveCfg = Debug|Any CPU + {4056B8FD-F355-41A3-A34F-60B350598B33}.Debug|x86.Build.0 = Debug|Any CPU {4056B8FD-F355-41A3-A34F-60B350598B33}.Release|Any CPU.ActiveCfg = Release|Any CPU {4056B8FD-F355-41A3-A34F-60B350598B33}.Release|Any CPU.Build.0 = Release|Any CPU + {4056B8FD-F355-41A3-A34F-60B350598B33}.Release|x64.ActiveCfg = Release|Any CPU + {4056B8FD-F355-41A3-A34F-60B350598B33}.Release|x64.Build.0 = Release|Any CPU + {4056B8FD-F355-41A3-A34F-60B350598B33}.Release|x86.ActiveCfg = Release|Any CPU + {4056B8FD-F355-41A3-A34F-60B350598B33}.Release|x86.Build.0 = Release|Any CPU {6D383054-4712-4D03-A0B1-B9D4E1CC6F95}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6D383054-4712-4D03-A0B1-B9D4E1CC6F95}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6D383054-4712-4D03-A0B1-B9D4E1CC6F95}.Debug|x64.ActiveCfg = Debug|Any CPU + {6D383054-4712-4D03-A0B1-B9D4E1CC6F95}.Debug|x64.Build.0 = Debug|Any CPU + {6D383054-4712-4D03-A0B1-B9D4E1CC6F95}.Debug|x86.ActiveCfg = Debug|Any CPU + {6D383054-4712-4D03-A0B1-B9D4E1CC6F95}.Debug|x86.Build.0 = Debug|Any CPU {6D383054-4712-4D03-A0B1-B9D4E1CC6F95}.Release|Any CPU.ActiveCfg = Release|Any CPU {6D383054-4712-4D03-A0B1-B9D4E1CC6F95}.Release|Any CPU.Build.0 = Release|Any CPU + {6D383054-4712-4D03-A0B1-B9D4E1CC6F95}.Release|x64.ActiveCfg = Release|Any CPU + {6D383054-4712-4D03-A0B1-B9D4E1CC6F95}.Release|x64.Build.0 = Release|Any CPU + {6D383054-4712-4D03-A0B1-B9D4E1CC6F95}.Release|x86.ActiveCfg = Release|Any CPU + {6D383054-4712-4D03-A0B1-B9D4E1CC6F95}.Release|x86.Build.0 = Release|Any CPU {4E0DC405-C372-4396-A5DF-F6AA108DA01C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4E0DC405-C372-4396-A5DF-F6AA108DA01C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4E0DC405-C372-4396-A5DF-F6AA108DA01C}.Debug|x64.ActiveCfg = Debug|Any CPU + {4E0DC405-C372-4396-A5DF-F6AA108DA01C}.Debug|x64.Build.0 = Debug|Any CPU + {4E0DC405-C372-4396-A5DF-F6AA108DA01C}.Debug|x86.ActiveCfg = Debug|Any CPU + {4E0DC405-C372-4396-A5DF-F6AA108DA01C}.Debug|x86.Build.0 = Debug|Any CPU {4E0DC405-C372-4396-A5DF-F6AA108DA01C}.Release|Any CPU.ActiveCfg = Release|Any CPU {4E0DC405-C372-4396-A5DF-F6AA108DA01C}.Release|Any CPU.Build.0 = Release|Any CPU + {4E0DC405-C372-4396-A5DF-F6AA108DA01C}.Release|x64.ActiveCfg = Release|Any CPU + {4E0DC405-C372-4396-A5DF-F6AA108DA01C}.Release|x64.Build.0 = Release|Any CPU + {4E0DC405-C372-4396-A5DF-F6AA108DA01C}.Release|x86.ActiveCfg = Release|Any CPU + {4E0DC405-C372-4396-A5DF-F6AA108DA01C}.Release|x86.Build.0 = Release|Any CPU {9B175EC8-6B64-4345-A158-091CB8876077}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9B175EC8-6B64-4345-A158-091CB8876077}.Debug|Any CPU.Build.0 = Debug|Any CPU {9B175EC8-6B64-4345-A158-091CB8876077}.Debug|Any CPU.Deploy.0 = Debug|Any CPU + {9B175EC8-6B64-4345-A158-091CB8876077}.Debug|x64.ActiveCfg = Debug|Any CPU + {9B175EC8-6B64-4345-A158-091CB8876077}.Debug|x64.Build.0 = Debug|Any CPU + {9B175EC8-6B64-4345-A158-091CB8876077}.Debug|x86.ActiveCfg = Debug|Any CPU + {9B175EC8-6B64-4345-A158-091CB8876077}.Debug|x86.Build.0 = Debug|Any CPU {9B175EC8-6B64-4345-A158-091CB8876077}.Release|Any CPU.ActiveCfg = Release|Any CPU {9B175EC8-6B64-4345-A158-091CB8876077}.Release|Any CPU.Build.0 = Release|Any CPU + {9B175EC8-6B64-4345-A158-091CB8876077}.Release|x64.ActiveCfg = Release|Any CPU + {9B175EC8-6B64-4345-A158-091CB8876077}.Release|x64.Build.0 = Release|Any CPU + {9B175EC8-6B64-4345-A158-091CB8876077}.Release|x86.ActiveCfg = Release|Any CPU + {9B175EC8-6B64-4345-A158-091CB8876077}.Release|x86.Build.0 = Release|Any CPU {EE0DC846-52F3-46AF-BC0D-DEF81150CEC0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EE0DC846-52F3-46AF-BC0D-DEF81150CEC0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EE0DC846-52F3-46AF-BC0D-DEF81150CEC0}.Debug|x64.ActiveCfg = Debug|Any CPU + {EE0DC846-52F3-46AF-BC0D-DEF81150CEC0}.Debug|x64.Build.0 = Debug|Any CPU + {EE0DC846-52F3-46AF-BC0D-DEF81150CEC0}.Debug|x86.ActiveCfg = Debug|Any CPU + {EE0DC846-52F3-46AF-BC0D-DEF81150CEC0}.Debug|x86.Build.0 = Debug|Any CPU {EE0DC846-52F3-46AF-BC0D-DEF81150CEC0}.Release|Any CPU.ActiveCfg = Release|Any CPU {EE0DC846-52F3-46AF-BC0D-DEF81150CEC0}.Release|Any CPU.Build.0 = Release|Any CPU + {EE0DC846-52F3-46AF-BC0D-DEF81150CEC0}.Release|x64.ActiveCfg = Release|Any CPU + {EE0DC846-52F3-46AF-BC0D-DEF81150CEC0}.Release|x64.Build.0 = Release|Any CPU + {EE0DC846-52F3-46AF-BC0D-DEF81150CEC0}.Release|x86.ActiveCfg = Release|Any CPU + {EE0DC846-52F3-46AF-BC0D-DEF81150CEC0}.Release|x86.Build.0 = Release|Any CPU {E37818EC-03D2-4B97-BE46-4C3F61465004}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E37818EC-03D2-4B97-BE46-4C3F61465004}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E37818EC-03D2-4B97-BE46-4C3F61465004}.Debug|x64.ActiveCfg = Debug|Any CPU + {E37818EC-03D2-4B97-BE46-4C3F61465004}.Debug|x64.Build.0 = Debug|Any CPU + {E37818EC-03D2-4B97-BE46-4C3F61465004}.Debug|x86.ActiveCfg = Debug|Any CPU + {E37818EC-03D2-4B97-BE46-4C3F61465004}.Debug|x86.Build.0 = Debug|Any CPU {E37818EC-03D2-4B97-BE46-4C3F61465004}.Release|Any CPU.ActiveCfg = Release|Any CPU {E37818EC-03D2-4B97-BE46-4C3F61465004}.Release|Any CPU.Build.0 = Release|Any CPU + {E37818EC-03D2-4B97-BE46-4C3F61465004}.Release|x64.ActiveCfg = Release|Any CPU + {E37818EC-03D2-4B97-BE46-4C3F61465004}.Release|x64.Build.0 = Release|Any CPU + {E37818EC-03D2-4B97-BE46-4C3F61465004}.Release|x86.ActiveCfg = Release|Any CPU + {E37818EC-03D2-4B97-BE46-4C3F61465004}.Release|x86.Build.0 = Release|Any CPU {E5141EAC-9420-48EA-995F-848CBBF0BD4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E5141EAC-9420-48EA-995F-848CBBF0BD4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E5141EAC-9420-48EA-995F-848CBBF0BD4B}.Debug|x64.ActiveCfg = Debug|Any CPU + {E5141EAC-9420-48EA-995F-848CBBF0BD4B}.Debug|x64.Build.0 = Debug|Any CPU + {E5141EAC-9420-48EA-995F-848CBBF0BD4B}.Debug|x86.ActiveCfg = Debug|Any CPU + {E5141EAC-9420-48EA-995F-848CBBF0BD4B}.Debug|x86.Build.0 = Debug|Any CPU {E5141EAC-9420-48EA-995F-848CBBF0BD4B}.Release|Any CPU.ActiveCfg = Release|Any CPU {E5141EAC-9420-48EA-995F-848CBBF0BD4B}.Release|Any CPU.Build.0 = Release|Any CPU + {E5141EAC-9420-48EA-995F-848CBBF0BD4B}.Release|x64.ActiveCfg = Release|Any CPU + {E5141EAC-9420-48EA-995F-848CBBF0BD4B}.Release|x64.Build.0 = Release|Any CPU + {E5141EAC-9420-48EA-995F-848CBBF0BD4B}.Release|x86.ActiveCfg = Release|Any CPU + {E5141EAC-9420-48EA-995F-848CBBF0BD4B}.Release|x86.Build.0 = Release|Any CPU {AA98FD8D-1254-4B34-840C-06BB263933DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {AA98FD8D-1254-4B34-840C-06BB263933DE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AA98FD8D-1254-4B34-840C-06BB263933DE}.Debug|x64.ActiveCfg = Debug|Any CPU + {AA98FD8D-1254-4B34-840C-06BB263933DE}.Debug|x64.Build.0 = Debug|Any CPU + {AA98FD8D-1254-4B34-840C-06BB263933DE}.Debug|x86.ActiveCfg = Debug|Any CPU + {AA98FD8D-1254-4B34-840C-06BB263933DE}.Debug|x86.Build.0 = Debug|Any CPU {AA98FD8D-1254-4B34-840C-06BB263933DE}.Release|Any CPU.ActiveCfg = Release|Any CPU {AA98FD8D-1254-4B34-840C-06BB263933DE}.Release|Any CPU.Build.0 = Release|Any CPU + {AA98FD8D-1254-4B34-840C-06BB263933DE}.Release|x64.ActiveCfg = Release|Any CPU + {AA98FD8D-1254-4B34-840C-06BB263933DE}.Release|x64.Build.0 = Release|Any CPU + {AA98FD8D-1254-4B34-840C-06BB263933DE}.Release|x86.ActiveCfg = Release|Any CPU + {AA98FD8D-1254-4B34-840C-06BB263933DE}.Release|x86.Build.0 = Release|Any CPU {20386BBE-1F55-4503-9F5F-F2C6B29DE865}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {20386BBE-1F55-4503-9F5F-F2C6B29DE865}.Debug|Any CPU.Build.0 = Debug|Any CPU + {20386BBE-1F55-4503-9F5F-F2C6B29DE865}.Debug|x64.ActiveCfg = Debug|Any CPU + {20386BBE-1F55-4503-9F5F-F2C6B29DE865}.Debug|x64.Build.0 = Debug|Any CPU + {20386BBE-1F55-4503-9F5F-F2C6B29DE865}.Debug|x86.ActiveCfg = Debug|Any CPU + {20386BBE-1F55-4503-9F5F-F2C6B29DE865}.Debug|x86.Build.0 = Debug|Any CPU {20386BBE-1F55-4503-9F5F-F2C6B29DE865}.Release|Any CPU.ActiveCfg = Release|Any CPU {20386BBE-1F55-4503-9F5F-F2C6B29DE865}.Release|Any CPU.Build.0 = Release|Any CPU + {20386BBE-1F55-4503-9F5F-F2C6B29DE865}.Release|x64.ActiveCfg = Release|Any CPU + {20386BBE-1F55-4503-9F5F-F2C6B29DE865}.Release|x64.Build.0 = Release|Any CPU + {20386BBE-1F55-4503-9F5F-F2C6B29DE865}.Release|x86.ActiveCfg = Release|Any CPU + {20386BBE-1F55-4503-9F5F-F2C6B29DE865}.Release|x86.Build.0 = Release|Any CPU {A7F651AD-51D3-4473-9641-7C76D2A0E3B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A7F651AD-51D3-4473-9641-7C76D2A0E3B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A7F651AD-51D3-4473-9641-7C76D2A0E3B7}.Debug|x64.ActiveCfg = Debug|Any CPU + {A7F651AD-51D3-4473-9641-7C76D2A0E3B7}.Debug|x64.Build.0 = Debug|Any CPU + {A7F651AD-51D3-4473-9641-7C76D2A0E3B7}.Debug|x86.ActiveCfg = Debug|Any CPU + {A7F651AD-51D3-4473-9641-7C76D2A0E3B7}.Debug|x86.Build.0 = Debug|Any CPU {A7F651AD-51D3-4473-9641-7C76D2A0E3B7}.Release|Any CPU.ActiveCfg = Release|Any CPU {A7F651AD-51D3-4473-9641-7C76D2A0E3B7}.Release|Any CPU.Build.0 = Release|Any CPU + {A7F651AD-51D3-4473-9641-7C76D2A0E3B7}.Release|x64.ActiveCfg = Release|Any CPU + {A7F651AD-51D3-4473-9641-7C76D2A0E3B7}.Release|x64.Build.0 = Release|Any CPU + {A7F651AD-51D3-4473-9641-7C76D2A0E3B7}.Release|x86.ActiveCfg = Release|Any CPU + {A7F651AD-51D3-4473-9641-7C76D2A0E3B7}.Release|x86.Build.0 = Release|Any CPU {DF785142-3E65-4176-8A6E-CAAD6BBF771F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DF785142-3E65-4176-8A6E-CAAD6BBF771F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DF785142-3E65-4176-8A6E-CAAD6BBF771F}.Debug|x64.ActiveCfg = Debug|Any CPU + {DF785142-3E65-4176-8A6E-CAAD6BBF771F}.Debug|x64.Build.0 = Debug|Any CPU + {DF785142-3E65-4176-8A6E-CAAD6BBF771F}.Debug|x86.ActiveCfg = Debug|Any CPU + {DF785142-3E65-4176-8A6E-CAAD6BBF771F}.Debug|x86.Build.0 = Debug|Any CPU {DF785142-3E65-4176-8A6E-CAAD6BBF771F}.Release|Any CPU.ActiveCfg = Release|Any CPU {DF785142-3E65-4176-8A6E-CAAD6BBF771F}.Release|Any CPU.Build.0 = Release|Any CPU + {DF785142-3E65-4176-8A6E-CAAD6BBF771F}.Release|x64.ActiveCfg = Release|Any CPU + {DF785142-3E65-4176-8A6E-CAAD6BBF771F}.Release|x64.Build.0 = Release|Any CPU + {DF785142-3E65-4176-8A6E-CAAD6BBF771F}.Release|x86.ActiveCfg = Release|Any CPU + {DF785142-3E65-4176-8A6E-CAAD6BBF771F}.Release|x86.Build.0 = Release|Any CPU {4B323BCC-0E8D-43CE-9AC2-4B708278C7C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4B323BCC-0E8D-43CE-9AC2-4B708278C7C2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4B323BCC-0E8D-43CE-9AC2-4B708278C7C2}.Debug|x64.ActiveCfg = Debug|Any CPU + {4B323BCC-0E8D-43CE-9AC2-4B708278C7C2}.Debug|x64.Build.0 = Debug|Any CPU + {4B323BCC-0E8D-43CE-9AC2-4B708278C7C2}.Debug|x86.ActiveCfg = Debug|Any CPU + {4B323BCC-0E8D-43CE-9AC2-4B708278C7C2}.Debug|x86.Build.0 = Debug|Any CPU {4B323BCC-0E8D-43CE-9AC2-4B708278C7C2}.Release|Any CPU.ActiveCfg = Release|Any CPU {4B323BCC-0E8D-43CE-9AC2-4B708278C7C2}.Release|Any CPU.Build.0 = Release|Any CPU + {4B323BCC-0E8D-43CE-9AC2-4B708278C7C2}.Release|x64.ActiveCfg = Release|Any CPU + {4B323BCC-0E8D-43CE-9AC2-4B708278C7C2}.Release|x64.Build.0 = Release|Any CPU + {4B323BCC-0E8D-43CE-9AC2-4B708278C7C2}.Release|x86.ActiveCfg = Release|Any CPU + {4B323BCC-0E8D-43CE-9AC2-4B708278C7C2}.Release|x86.Build.0 = Release|Any CPU {52262FBB-40D0-4F08-B00F-B298185FF6BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {52262FBB-40D0-4F08-B00F-B298185FF6BD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {52262FBB-40D0-4F08-B00F-B298185FF6BD}.Debug|x64.ActiveCfg = Debug|Any CPU + {52262FBB-40D0-4F08-B00F-B298185FF6BD}.Debug|x64.Build.0 = Debug|Any CPU + {52262FBB-40D0-4F08-B00F-B298185FF6BD}.Debug|x86.ActiveCfg = Debug|Any CPU + {52262FBB-40D0-4F08-B00F-B298185FF6BD}.Debug|x86.Build.0 = Debug|Any CPU {52262FBB-40D0-4F08-B00F-B298185FF6BD}.Release|Any CPU.ActiveCfg = Release|Any CPU {52262FBB-40D0-4F08-B00F-B298185FF6BD}.Release|Any CPU.Build.0 = Release|Any CPU + {52262FBB-40D0-4F08-B00F-B298185FF6BD}.Release|x64.ActiveCfg = Release|Any CPU + {52262FBB-40D0-4F08-B00F-B298185FF6BD}.Release|x64.Build.0 = Release|Any CPU + {52262FBB-40D0-4F08-B00F-B298185FF6BD}.Release|x86.ActiveCfg = Release|Any CPU + {52262FBB-40D0-4F08-B00F-B298185FF6BD}.Release|x86.Build.0 = Release|Any CPU {759D9E53-4717-491D-9970-B3A3367C65E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {759D9E53-4717-491D-9970-B3A3367C65E7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {759D9E53-4717-491D-9970-B3A3367C65E7}.Debug|x64.ActiveCfg = Debug|Any CPU + {759D9E53-4717-491D-9970-B3A3367C65E7}.Debug|x64.Build.0 = Debug|Any CPU + {759D9E53-4717-491D-9970-B3A3367C65E7}.Debug|x86.ActiveCfg = Debug|Any CPU + {759D9E53-4717-491D-9970-B3A3367C65E7}.Debug|x86.Build.0 = Debug|Any CPU {759D9E53-4717-491D-9970-B3A3367C65E7}.Release|Any CPU.ActiveCfg = Release|Any CPU {759D9E53-4717-491D-9970-B3A3367C65E7}.Release|Any CPU.Build.0 = Release|Any CPU + {759D9E53-4717-491D-9970-B3A3367C65E7}.Release|x64.ActiveCfg = Release|Any CPU + {759D9E53-4717-491D-9970-B3A3367C65E7}.Release|x64.Build.0 = Release|Any CPU + {759D9E53-4717-491D-9970-B3A3367C65E7}.Release|x86.ActiveCfg = Release|Any CPU + {759D9E53-4717-491D-9970-B3A3367C65E7}.Release|x86.Build.0 = Release|Any CPU {33336B98-A69E-4F28-A71A-1D8753F3BA24}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {33336B98-A69E-4F28-A71A-1D8753F3BA24}.Debug|Any CPU.Build.0 = Debug|Any CPU + {33336B98-A69E-4F28-A71A-1D8753F3BA24}.Debug|x64.ActiveCfg = Debug|Any CPU + {33336B98-A69E-4F28-A71A-1D8753F3BA24}.Debug|x64.Build.0 = Debug|Any CPU + {33336B98-A69E-4F28-A71A-1D8753F3BA24}.Debug|x86.ActiveCfg = Debug|Any CPU + {33336B98-A69E-4F28-A71A-1D8753F3BA24}.Debug|x86.Build.0 = Debug|Any CPU {33336B98-A69E-4F28-A71A-1D8753F3BA24}.Release|Any CPU.ActiveCfg = Release|Any CPU {33336B98-A69E-4F28-A71A-1D8753F3BA24}.Release|Any CPU.Build.0 = Release|Any CPU + {33336B98-A69E-4F28-A71A-1D8753F3BA24}.Release|x64.ActiveCfg = Release|Any CPU + {33336B98-A69E-4F28-A71A-1D8753F3BA24}.Release|x64.Build.0 = Release|Any CPU + {33336B98-A69E-4F28-A71A-1D8753F3BA24}.Release|x86.ActiveCfg = Release|Any CPU + {33336B98-A69E-4F28-A71A-1D8753F3BA24}.Release|x86.Build.0 = Release|Any CPU {22E4E3F5-8D6E-433D-9456-F60DFB1EFC82}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {22E4E3F5-8D6E-433D-9456-F60DFB1EFC82}.Debug|Any CPU.Build.0 = Debug|Any CPU + {22E4E3F5-8D6E-433D-9456-F60DFB1EFC82}.Debug|x64.ActiveCfg = Debug|Any CPU + {22E4E3F5-8D6E-433D-9456-F60DFB1EFC82}.Debug|x64.Build.0 = Debug|Any CPU + {22E4E3F5-8D6E-433D-9456-F60DFB1EFC82}.Debug|x86.ActiveCfg = Debug|Any CPU + {22E4E3F5-8D6E-433D-9456-F60DFB1EFC82}.Debug|x86.Build.0 = Debug|Any CPU {22E4E3F5-8D6E-433D-9456-F60DFB1EFC82}.Release|Any CPU.ActiveCfg = Release|Any CPU {22E4E3F5-8D6E-433D-9456-F60DFB1EFC82}.Release|Any CPU.Build.0 = Release|Any CPU + {22E4E3F5-8D6E-433D-9456-F60DFB1EFC82}.Release|x64.ActiveCfg = Release|Any CPU + {22E4E3F5-8D6E-433D-9456-F60DFB1EFC82}.Release|x64.Build.0 = Release|Any CPU + {22E4E3F5-8D6E-433D-9456-F60DFB1EFC82}.Release|x86.ActiveCfg = Release|Any CPU + {22E4E3F5-8D6E-433D-9456-F60DFB1EFC82}.Release|x86.Build.0 = Release|Any CPU {2AEC3A6E-886F-43DA-8EA6-82EBF916E89A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2AEC3A6E-886F-43DA-8EA6-82EBF916E89A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2AEC3A6E-886F-43DA-8EA6-82EBF916E89A}.Debug|x64.ActiveCfg = Debug|Any CPU + {2AEC3A6E-886F-43DA-8EA6-82EBF916E89A}.Debug|x64.Build.0 = Debug|Any CPU + {2AEC3A6E-886F-43DA-8EA6-82EBF916E89A}.Debug|x86.ActiveCfg = Debug|Any CPU + {2AEC3A6E-886F-43DA-8EA6-82EBF916E89A}.Debug|x86.Build.0 = Debug|Any CPU {2AEC3A6E-886F-43DA-8EA6-82EBF916E89A}.Release|Any CPU.ActiveCfg = Release|Any CPU {2AEC3A6E-886F-43DA-8EA6-82EBF916E89A}.Release|Any CPU.Build.0 = Release|Any CPU + {2AEC3A6E-886F-43DA-8EA6-82EBF916E89A}.Release|x64.ActiveCfg = Release|Any CPU + {2AEC3A6E-886F-43DA-8EA6-82EBF916E89A}.Release|x64.Build.0 = Release|Any CPU + {2AEC3A6E-886F-43DA-8EA6-82EBF916E89A}.Release|x86.ActiveCfg = Release|Any CPU + {2AEC3A6E-886F-43DA-8EA6-82EBF916E89A}.Release|x86.Build.0 = Release|Any CPU {F97EE360-3733-4993-823A-A81D004D417E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F97EE360-3733-4993-823A-A81D004D417E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F97EE360-3733-4993-823A-A81D004D417E}.Debug|x64.ActiveCfg = Debug|Any CPU + {F97EE360-3733-4993-823A-A81D004D417E}.Debug|x64.Build.0 = Debug|Any CPU + {F97EE360-3733-4993-823A-A81D004D417E}.Debug|x86.ActiveCfg = Debug|Any CPU + {F97EE360-3733-4993-823A-A81D004D417E}.Debug|x86.Build.0 = Debug|Any CPU {F97EE360-3733-4993-823A-A81D004D417E}.Release|Any CPU.ActiveCfg = Release|Any CPU {F97EE360-3733-4993-823A-A81D004D417E}.Release|Any CPU.Build.0 = Release|Any CPU + {F97EE360-3733-4993-823A-A81D004D417E}.Release|x64.ActiveCfg = Release|Any CPU + {F97EE360-3733-4993-823A-A81D004D417E}.Release|x64.Build.0 = Release|Any CPU + {F97EE360-3733-4993-823A-A81D004D417E}.Release|x86.ActiveCfg = Release|Any CPU + {F97EE360-3733-4993-823A-A81D004D417E}.Release|x86.Build.0 = Release|Any CPU {1D66CB6F-6268-4595-AD49-09DFD1784DDB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1D66CB6F-6268-4595-AD49-09DFD1784DDB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1D66CB6F-6268-4595-AD49-09DFD1784DDB}.Debug|x64.ActiveCfg = Debug|Any CPU + {1D66CB6F-6268-4595-AD49-09DFD1784DDB}.Debug|x64.Build.0 = Debug|Any CPU + {1D66CB6F-6268-4595-AD49-09DFD1784DDB}.Debug|x86.ActiveCfg = Debug|Any CPU + {1D66CB6F-6268-4595-AD49-09DFD1784DDB}.Debug|x86.Build.0 = Debug|Any CPU {1D66CB6F-6268-4595-AD49-09DFD1784DDB}.Release|Any CPU.ActiveCfg = Release|Any CPU {1D66CB6F-6268-4595-AD49-09DFD1784DDB}.Release|Any CPU.Build.0 = Release|Any CPU + {1D66CB6F-6268-4595-AD49-09DFD1784DDB}.Release|x64.ActiveCfg = Release|Any CPU + {1D66CB6F-6268-4595-AD49-09DFD1784DDB}.Release|x64.Build.0 = Release|Any CPU + {1D66CB6F-6268-4595-AD49-09DFD1784DDB}.Release|x86.ActiveCfg = Release|Any CPU + {1D66CB6F-6268-4595-AD49-09DFD1784DDB}.Release|x86.Build.0 = Release|Any CPU {EA4BB6FE-57B5-4A2B-88AD-FCCD96ECE19F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EA4BB6FE-57B5-4A2B-88AD-FCCD96ECE19F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EA4BB6FE-57B5-4A2B-88AD-FCCD96ECE19F}.Debug|x64.ActiveCfg = Debug|Any CPU + {EA4BB6FE-57B5-4A2B-88AD-FCCD96ECE19F}.Debug|x64.Build.0 = Debug|Any CPU + {EA4BB6FE-57B5-4A2B-88AD-FCCD96ECE19F}.Debug|x86.ActiveCfg = Debug|Any CPU + {EA4BB6FE-57B5-4A2B-88AD-FCCD96ECE19F}.Debug|x86.Build.0 = Debug|Any CPU {EA4BB6FE-57B5-4A2B-88AD-FCCD96ECE19F}.Release|Any CPU.ActiveCfg = Release|Any CPU {EA4BB6FE-57B5-4A2B-88AD-FCCD96ECE19F}.Release|Any CPU.Build.0 = Release|Any CPU + {EA4BB6FE-57B5-4A2B-88AD-FCCD96ECE19F}.Release|x64.ActiveCfg = Release|Any CPU + {EA4BB6FE-57B5-4A2B-88AD-FCCD96ECE19F}.Release|x64.Build.0 = Release|Any CPU + {EA4BB6FE-57B5-4A2B-88AD-FCCD96ECE19F}.Release|x86.ActiveCfg = Release|Any CPU + {EA4BB6FE-57B5-4A2B-88AD-FCCD96ECE19F}.Release|x86.Build.0 = Release|Any CPU {DC89F322-155F-488B-8B80-3824B433F046}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DC89F322-155F-488B-8B80-3824B433F046}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DC89F322-155F-488B-8B80-3824B433F046}.Debug|x64.ActiveCfg = Debug|Any CPU + {DC89F322-155F-488B-8B80-3824B433F046}.Debug|x64.Build.0 = Debug|Any CPU + {DC89F322-155F-488B-8B80-3824B433F046}.Debug|x86.ActiveCfg = Debug|Any CPU + {DC89F322-155F-488B-8B80-3824B433F046}.Debug|x86.Build.0 = Debug|Any CPU {DC89F322-155F-488B-8B80-3824B433F046}.Release|Any CPU.ActiveCfg = Release|Any CPU {DC89F322-155F-488B-8B80-3824B433F046}.Release|Any CPU.Build.0 = Release|Any CPU + {DC89F322-155F-488B-8B80-3824B433F046}.Release|x64.ActiveCfg = Release|Any CPU + {DC89F322-155F-488B-8B80-3824B433F046}.Release|x64.Build.0 = Release|Any CPU + {DC89F322-155F-488B-8B80-3824B433F046}.Release|x86.ActiveCfg = Release|Any CPU + {DC89F322-155F-488B-8B80-3824B433F046}.Release|x86.Build.0 = Release|Any CPU {4E74CAAD-062F-4AF9-8B29-2ED4B2CC0E77}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4E74CAAD-062F-4AF9-8B29-2ED4B2CC0E77}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4E74CAAD-062F-4AF9-8B29-2ED4B2CC0E77}.Debug|x64.ActiveCfg = Debug|Any CPU + {4E74CAAD-062F-4AF9-8B29-2ED4B2CC0E77}.Debug|x64.Build.0 = Debug|Any CPU + {4E74CAAD-062F-4AF9-8B29-2ED4B2CC0E77}.Debug|x86.ActiveCfg = Debug|Any CPU + {4E74CAAD-062F-4AF9-8B29-2ED4B2CC0E77}.Debug|x86.Build.0 = Debug|Any CPU {4E74CAAD-062F-4AF9-8B29-2ED4B2CC0E77}.Release|Any CPU.ActiveCfg = Release|Any CPU {4E74CAAD-062F-4AF9-8B29-2ED4B2CC0E77}.Release|Any CPU.Build.0 = Release|Any CPU + {4E74CAAD-062F-4AF9-8B29-2ED4B2CC0E77}.Release|x64.ActiveCfg = Release|Any CPU + {4E74CAAD-062F-4AF9-8B29-2ED4B2CC0E77}.Release|x64.Build.0 = Release|Any CPU + {4E74CAAD-062F-4AF9-8B29-2ED4B2CC0E77}.Release|x86.ActiveCfg = Release|Any CPU + {4E74CAAD-062F-4AF9-8B29-2ED4B2CC0E77}.Release|x86.Build.0 = Release|Any CPU {957DFFC0-0FA3-48CC-AEDD-F9BBC3F0FADC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {957DFFC0-0FA3-48CC-AEDD-F9BBC3F0FADC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {957DFFC0-0FA3-48CC-AEDD-F9BBC3F0FADC}.Debug|x64.ActiveCfg = Debug|Any CPU + {957DFFC0-0FA3-48CC-AEDD-F9BBC3F0FADC}.Debug|x64.Build.0 = Debug|Any CPU + {957DFFC0-0FA3-48CC-AEDD-F9BBC3F0FADC}.Debug|x86.ActiveCfg = Debug|Any CPU + {957DFFC0-0FA3-48CC-AEDD-F9BBC3F0FADC}.Debug|x86.Build.0 = Debug|Any CPU {957DFFC0-0FA3-48CC-AEDD-F9BBC3F0FADC}.Release|Any CPU.ActiveCfg = Release|Any CPU {957DFFC0-0FA3-48CC-AEDD-F9BBC3F0FADC}.Release|Any CPU.Build.0 = Release|Any CPU + {957DFFC0-0FA3-48CC-AEDD-F9BBC3F0FADC}.Release|x64.ActiveCfg = Release|Any CPU + {957DFFC0-0FA3-48CC-AEDD-F9BBC3F0FADC}.Release|x64.Build.0 = Release|Any CPU + {957DFFC0-0FA3-48CC-AEDD-F9BBC3F0FADC}.Release|x86.ActiveCfg = Release|Any CPU + {957DFFC0-0FA3-48CC-AEDD-F9BBC3F0FADC}.Release|x86.Build.0 = Release|Any CPU {2E239FE2-F3B2-41B6-AE9E-561F6ACE5BA6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2E239FE2-F3B2-41B6-AE9E-561F6ACE5BA6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2E239FE2-F3B2-41B6-AE9E-561F6ACE5BA6}.Debug|x64.ActiveCfg = Debug|Any CPU + {2E239FE2-F3B2-41B6-AE9E-561F6ACE5BA6}.Debug|x64.Build.0 = Debug|Any CPU + {2E239FE2-F3B2-41B6-AE9E-561F6ACE5BA6}.Debug|x86.ActiveCfg = Debug|Any CPU + {2E239FE2-F3B2-41B6-AE9E-561F6ACE5BA6}.Debug|x86.Build.0 = Debug|Any CPU {2E239FE2-F3B2-41B6-AE9E-561F6ACE5BA6}.Release|Any CPU.ActiveCfg = Release|Any CPU {2E239FE2-F3B2-41B6-AE9E-561F6ACE5BA6}.Release|Any CPU.Build.0 = Release|Any CPU + {2E239FE2-F3B2-41B6-AE9E-561F6ACE5BA6}.Release|x64.ActiveCfg = Release|Any CPU + {2E239FE2-F3B2-41B6-AE9E-561F6ACE5BA6}.Release|x64.Build.0 = Release|Any CPU + {2E239FE2-F3B2-41B6-AE9E-561F6ACE5BA6}.Release|x86.ActiveCfg = Release|Any CPU + {2E239FE2-F3B2-41B6-AE9E-561F6ACE5BA6}.Release|x86.Build.0 = Release|Any CPU {854EBD1B-73EE-4B93-89DF-E70436FF14CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {854EBD1B-73EE-4B93-89DF-E70436FF14CB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {854EBD1B-73EE-4B93-89DF-E70436FF14CB}.Debug|x64.ActiveCfg = Debug|Any CPU + {854EBD1B-73EE-4B93-89DF-E70436FF14CB}.Debug|x64.Build.0 = Debug|Any CPU + {854EBD1B-73EE-4B93-89DF-E70436FF14CB}.Debug|x86.ActiveCfg = Debug|Any CPU + {854EBD1B-73EE-4B93-89DF-E70436FF14CB}.Debug|x86.Build.0 = Debug|Any CPU {854EBD1B-73EE-4B93-89DF-E70436FF14CB}.Release|Any CPU.ActiveCfg = Release|Any CPU {854EBD1B-73EE-4B93-89DF-E70436FF14CB}.Release|Any CPU.Build.0 = Release|Any CPU + {854EBD1B-73EE-4B93-89DF-E70436FF14CB}.Release|x64.ActiveCfg = Release|Any CPU + {854EBD1B-73EE-4B93-89DF-E70436FF14CB}.Release|x64.Build.0 = Release|Any CPU + {854EBD1B-73EE-4B93-89DF-E70436FF14CB}.Release|x86.ActiveCfg = Release|Any CPU + {854EBD1B-73EE-4B93-89DF-E70436FF14CB}.Release|x86.Build.0 = Release|Any CPU {5F253D7F-BF27-46F5-9382-73A66EC247BF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5F253D7F-BF27-46F5-9382-73A66EC247BF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5F253D7F-BF27-46F5-9382-73A66EC247BF}.Debug|x64.ActiveCfg = Debug|Any CPU + {5F253D7F-BF27-46F5-9382-73A66EC247BF}.Debug|x64.Build.0 = Debug|Any CPU + {5F253D7F-BF27-46F5-9382-73A66EC247BF}.Debug|x86.ActiveCfg = Debug|Any CPU + {5F253D7F-BF27-46F5-9382-73A66EC247BF}.Debug|x86.Build.0 = Debug|Any CPU {5F253D7F-BF27-46F5-9382-73A66EC247BF}.Release|Any CPU.ActiveCfg = Release|Any CPU {5F253D7F-BF27-46F5-9382-73A66EC247BF}.Release|Any CPU.Build.0 = Release|Any CPU + {5F253D7F-BF27-46F5-9382-73A66EC247BF}.Release|x64.ActiveCfg = Release|Any CPU + {5F253D7F-BF27-46F5-9382-73A66EC247BF}.Release|x64.Build.0 = Release|Any CPU + {5F253D7F-BF27-46F5-9382-73A66EC247BF}.Release|x86.ActiveCfg = Release|Any CPU + {5F253D7F-BF27-46F5-9382-73A66EC247BF}.Release|x86.Build.0 = Release|Any CPU {99E2D1A4-1853-49F9-96B6-59C70FA3DE3A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {99E2D1A4-1853-49F9-96B6-59C70FA3DE3A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {99E2D1A4-1853-49F9-96B6-59C70FA3DE3A}.Debug|x64.ActiveCfg = Debug|Any CPU + {99E2D1A4-1853-49F9-96B6-59C70FA3DE3A}.Debug|x64.Build.0 = Debug|Any CPU + {99E2D1A4-1853-49F9-96B6-59C70FA3DE3A}.Debug|x86.ActiveCfg = Debug|Any CPU + {99E2D1A4-1853-49F9-96B6-59C70FA3DE3A}.Debug|x86.Build.0 = Debug|Any CPU {99E2D1A4-1853-49F9-96B6-59C70FA3DE3A}.Release|Any CPU.ActiveCfg = Release|Any CPU {99E2D1A4-1853-49F9-96B6-59C70FA3DE3A}.Release|Any CPU.Build.0 = Release|Any CPU + {99E2D1A4-1853-49F9-96B6-59C70FA3DE3A}.Release|x64.ActiveCfg = Release|Any CPU + {99E2D1A4-1853-49F9-96B6-59C70FA3DE3A}.Release|x64.Build.0 = Release|Any CPU + {99E2D1A4-1853-49F9-96B6-59C70FA3DE3A}.Release|x86.ActiveCfg = Release|Any CPU + {99E2D1A4-1853-49F9-96B6-59C70FA3DE3A}.Release|x86.Build.0 = Release|Any CPU {3C543CED-F801-4843-BA48-76142843E1B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3C543CED-F801-4843-BA48-76142843E1B9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3C543CED-F801-4843-BA48-76142843E1B9}.Debug|x64.ActiveCfg = Debug|Any CPU + {3C543CED-F801-4843-BA48-76142843E1B9}.Debug|x64.Build.0 = Debug|Any CPU + {3C543CED-F801-4843-BA48-76142843E1B9}.Debug|x86.ActiveCfg = Debug|Any CPU + {3C543CED-F801-4843-BA48-76142843E1B9}.Debug|x86.Build.0 = Debug|Any CPU {3C543CED-F801-4843-BA48-76142843E1B9}.Release|Any CPU.ActiveCfg = Release|Any CPU {3C543CED-F801-4843-BA48-76142843E1B9}.Release|Any CPU.Build.0 = Release|Any CPU + {3C543CED-F801-4843-BA48-76142843E1B9}.Release|x64.ActiveCfg = Release|Any CPU + {3C543CED-F801-4843-BA48-76142843E1B9}.Release|x64.Build.0 = Release|Any CPU + {3C543CED-F801-4843-BA48-76142843E1B9}.Release|x86.ActiveCfg = Release|Any CPU + {3C543CED-F801-4843-BA48-76142843E1B9}.Release|x86.Build.0 = Release|Any CPU {7ABAA536-7BEC-46D7-9C61-82440DDC9AA8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7ABAA536-7BEC-46D7-9C61-82440DDC9AA8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7ABAA536-7BEC-46D7-9C61-82440DDC9AA8}.Debug|x64.ActiveCfg = Debug|Any CPU + {7ABAA536-7BEC-46D7-9C61-82440DDC9AA8}.Debug|x64.Build.0 = Debug|Any CPU + {7ABAA536-7BEC-46D7-9C61-82440DDC9AA8}.Debug|x86.ActiveCfg = Debug|Any CPU + {7ABAA536-7BEC-46D7-9C61-82440DDC9AA8}.Debug|x86.Build.0 = Debug|Any CPU {7ABAA536-7BEC-46D7-9C61-82440DDC9AA8}.Release|Any CPU.ActiveCfg = Release|Any CPU {7ABAA536-7BEC-46D7-9C61-82440DDC9AA8}.Release|Any CPU.Build.0 = Release|Any CPU + {7ABAA536-7BEC-46D7-9C61-82440DDC9AA8}.Release|x64.ActiveCfg = Release|Any CPU + {7ABAA536-7BEC-46D7-9C61-82440DDC9AA8}.Release|x64.Build.0 = Release|Any CPU + {7ABAA536-7BEC-46D7-9C61-82440DDC9AA8}.Release|x86.ActiveCfg = Release|Any CPU + {7ABAA536-7BEC-46D7-9C61-82440DDC9AA8}.Release|x86.Build.0 = Release|Any CPU {D7D1EA68-ACE3-4DF3-A33C-EBB5B74701F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D7D1EA68-ACE3-4DF3-A33C-EBB5B74701F5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D7D1EA68-ACE3-4DF3-A33C-EBB5B74701F5}.Debug|x64.ActiveCfg = Debug|Any CPU + {D7D1EA68-ACE3-4DF3-A33C-EBB5B74701F5}.Debug|x64.Build.0 = Debug|Any CPU + {D7D1EA68-ACE3-4DF3-A33C-EBB5B74701F5}.Debug|x86.ActiveCfg = Debug|Any CPU + {D7D1EA68-ACE3-4DF3-A33C-EBB5B74701F5}.Debug|x86.Build.0 = Debug|Any CPU {D7D1EA68-ACE3-4DF3-A33C-EBB5B74701F5}.Release|Any CPU.ActiveCfg = Release|Any CPU {D7D1EA68-ACE3-4DF3-A33C-EBB5B74701F5}.Release|Any CPU.Build.0 = Release|Any CPU + {D7D1EA68-ACE3-4DF3-A33C-EBB5B74701F5}.Release|x64.ActiveCfg = Release|Any CPU + {D7D1EA68-ACE3-4DF3-A33C-EBB5B74701F5}.Release|x64.Build.0 = Release|Any CPU + {D7D1EA68-ACE3-4DF3-A33C-EBB5B74701F5}.Release|x86.ActiveCfg = Release|Any CPU + {D7D1EA68-ACE3-4DF3-A33C-EBB5B74701F5}.Release|x86.Build.0 = Release|Any CPU {563FEA1C-FDAE-4EDE-9ABF-22D76F2DA048}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {563FEA1C-FDAE-4EDE-9ABF-22D76F2DA048}.Debug|Any CPU.Build.0 = Debug|Any CPU + {563FEA1C-FDAE-4EDE-9ABF-22D76F2DA048}.Debug|x64.ActiveCfg = Debug|Any CPU + {563FEA1C-FDAE-4EDE-9ABF-22D76F2DA048}.Debug|x64.Build.0 = Debug|Any CPU + {563FEA1C-FDAE-4EDE-9ABF-22D76F2DA048}.Debug|x86.ActiveCfg = Debug|Any CPU + {563FEA1C-FDAE-4EDE-9ABF-22D76F2DA048}.Debug|x86.Build.0 = Debug|Any CPU {563FEA1C-FDAE-4EDE-9ABF-22D76F2DA048}.Release|Any CPU.ActiveCfg = Release|Any CPU {563FEA1C-FDAE-4EDE-9ABF-22D76F2DA048}.Release|Any CPU.Build.0 = Release|Any CPU + {563FEA1C-FDAE-4EDE-9ABF-22D76F2DA048}.Release|x64.ActiveCfg = Release|Any CPU + {563FEA1C-FDAE-4EDE-9ABF-22D76F2DA048}.Release|x64.Build.0 = Release|Any CPU + {563FEA1C-FDAE-4EDE-9ABF-22D76F2DA048}.Release|x86.ActiveCfg = Release|Any CPU + {563FEA1C-FDAE-4EDE-9ABF-22D76F2DA048}.Release|x86.Build.0 = Release|Any CPU {6D9EB590-CED1-47C2-87B1-E65EE0EEBB9E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6D9EB590-CED1-47C2-87B1-E65EE0EEBB9E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6D9EB590-CED1-47C2-87B1-E65EE0EEBB9E}.Debug|x64.ActiveCfg = Debug|Any CPU + {6D9EB590-CED1-47C2-87B1-E65EE0EEBB9E}.Debug|x64.Build.0 = Debug|Any CPU + {6D9EB590-CED1-47C2-87B1-E65EE0EEBB9E}.Debug|x86.ActiveCfg = Debug|Any CPU + {6D9EB590-CED1-47C2-87B1-E65EE0EEBB9E}.Debug|x86.Build.0 = Debug|Any CPU {6D9EB590-CED1-47C2-87B1-E65EE0EEBB9E}.Release|Any CPU.ActiveCfg = Release|Any CPU {6D9EB590-CED1-47C2-87B1-E65EE0EEBB9E}.Release|Any CPU.Build.0 = Release|Any CPU + {6D9EB590-CED1-47C2-87B1-E65EE0EEBB9E}.Release|x64.ActiveCfg = Release|Any CPU + {6D9EB590-CED1-47C2-87B1-E65EE0EEBB9E}.Release|x64.Build.0 = Release|Any CPU + {6D9EB590-CED1-47C2-87B1-E65EE0EEBB9E}.Release|x86.ActiveCfg = Release|Any CPU + {6D9EB590-CED1-47C2-87B1-E65EE0EEBB9E}.Release|x86.Build.0 = Release|Any CPU {39F7BB08-908B-49F9-A96B-E14C16B69090}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {39F7BB08-908B-49F9-A96B-E14C16B69090}.Debug|Any CPU.Build.0 = Debug|Any CPU + {39F7BB08-908B-49F9-A96B-E14C16B69090}.Debug|x64.ActiveCfg = Debug|Any CPU + {39F7BB08-908B-49F9-A96B-E14C16B69090}.Debug|x64.Build.0 = Debug|Any CPU + {39F7BB08-908B-49F9-A96B-E14C16B69090}.Debug|x86.ActiveCfg = Debug|Any CPU + {39F7BB08-908B-49F9-A96B-E14C16B69090}.Debug|x86.Build.0 = Debug|Any CPU {39F7BB08-908B-49F9-A96B-E14C16B69090}.Release|Any CPU.ActiveCfg = Release|Any CPU {39F7BB08-908B-49F9-A96B-E14C16B69090}.Release|Any CPU.Build.0 = Release|Any CPU + {39F7BB08-908B-49F9-A96B-E14C16B69090}.Release|x64.ActiveCfg = Release|Any CPU + {39F7BB08-908B-49F9-A96B-E14C16B69090}.Release|x64.Build.0 = Release|Any CPU + {39F7BB08-908B-49F9-A96B-E14C16B69090}.Release|x86.ActiveCfg = Release|Any CPU + {39F7BB08-908B-49F9-A96B-E14C16B69090}.Release|x86.Build.0 = Release|Any CPU {59742E7E-4380-4B40-BCC8-5AB314290198}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {59742E7E-4380-4B40-BCC8-5AB314290198}.Debug|Any CPU.Build.0 = Debug|Any CPU + {59742E7E-4380-4B40-BCC8-5AB314290198}.Debug|x64.ActiveCfg = Debug|Any CPU + {59742E7E-4380-4B40-BCC8-5AB314290198}.Debug|x64.Build.0 = Debug|Any CPU + {59742E7E-4380-4B40-BCC8-5AB314290198}.Debug|x86.ActiveCfg = Debug|Any CPU + {59742E7E-4380-4B40-BCC8-5AB314290198}.Debug|x86.Build.0 = Debug|Any CPU {59742E7E-4380-4B40-BCC8-5AB314290198}.Release|Any CPU.ActiveCfg = Release|Any CPU {59742E7E-4380-4B40-BCC8-5AB314290198}.Release|Any CPU.Build.0 = Release|Any CPU + {59742E7E-4380-4B40-BCC8-5AB314290198}.Release|x64.ActiveCfg = Release|Any CPU + {59742E7E-4380-4B40-BCC8-5AB314290198}.Release|x64.Build.0 = Release|Any CPU + {59742E7E-4380-4B40-BCC8-5AB314290198}.Release|x86.ActiveCfg = Release|Any CPU + {59742E7E-4380-4B40-BCC8-5AB314290198}.Release|x86.Build.0 = Release|Any CPU {6222BF96-2F1F-42DA-AF43-388B20151A5A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6222BF96-2F1F-42DA-AF43-388B20151A5A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6222BF96-2F1F-42DA-AF43-388B20151A5A}.Debug|x64.ActiveCfg = Debug|Any CPU + {6222BF96-2F1F-42DA-AF43-388B20151A5A}.Debug|x64.Build.0 = Debug|Any CPU + {6222BF96-2F1F-42DA-AF43-388B20151A5A}.Debug|x86.ActiveCfg = Debug|Any CPU + {6222BF96-2F1F-42DA-AF43-388B20151A5A}.Debug|x86.Build.0 = Debug|Any CPU {6222BF96-2F1F-42DA-AF43-388B20151A5A}.Release|Any CPU.ActiveCfg = Release|Any CPU {6222BF96-2F1F-42DA-AF43-388B20151A5A}.Release|Any CPU.Build.0 = Release|Any CPU + {6222BF96-2F1F-42DA-AF43-388B20151A5A}.Release|x64.ActiveCfg = Release|Any CPU + {6222BF96-2F1F-42DA-AF43-388B20151A5A}.Release|x64.Build.0 = Release|Any CPU + {6222BF96-2F1F-42DA-AF43-388B20151A5A}.Release|x86.ActiveCfg = Release|Any CPU + {6222BF96-2F1F-42DA-AF43-388B20151A5A}.Release|x86.Build.0 = Release|Any CPU {18FDE2B5-6023-487C-A349-E492F3F34B4A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {18FDE2B5-6023-487C-A349-E492F3F34B4A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {18FDE2B5-6023-487C-A349-E492F3F34B4A}.Debug|x64.ActiveCfg = Debug|Any CPU + {18FDE2B5-6023-487C-A349-E492F3F34B4A}.Debug|x64.Build.0 = Debug|Any CPU + {18FDE2B5-6023-487C-A349-E492F3F34B4A}.Debug|x86.ActiveCfg = Debug|Any CPU + {18FDE2B5-6023-487C-A349-E492F3F34B4A}.Debug|x86.Build.0 = Debug|Any CPU {18FDE2B5-6023-487C-A349-E492F3F34B4A}.Release|Any CPU.ActiveCfg = Release|Any CPU {18FDE2B5-6023-487C-A349-E492F3F34B4A}.Release|Any CPU.Build.0 = Release|Any CPU + {18FDE2B5-6023-487C-A349-E492F3F34B4A}.Release|x64.ActiveCfg = Release|Any CPU + {18FDE2B5-6023-487C-A349-E492F3F34B4A}.Release|x64.Build.0 = Release|Any CPU + {18FDE2B5-6023-487C-A349-E492F3F34B4A}.Release|x86.ActiveCfg = Release|Any CPU + {18FDE2B5-6023-487C-A349-E492F3F34B4A}.Release|x86.Build.0 = Release|Any CPU {357D2522-4481-4135-8379-C228C743DD69}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {357D2522-4481-4135-8379-C228C743DD69}.Debug|Any CPU.Build.0 = Debug|Any CPU + {357D2522-4481-4135-8379-C228C743DD69}.Debug|x64.ActiveCfg = Debug|Any CPU + {357D2522-4481-4135-8379-C228C743DD69}.Debug|x64.Build.0 = Debug|Any CPU + {357D2522-4481-4135-8379-C228C743DD69}.Debug|x86.ActiveCfg = Debug|Any CPU + {357D2522-4481-4135-8379-C228C743DD69}.Debug|x86.Build.0 = Debug|Any CPU {357D2522-4481-4135-8379-C228C743DD69}.Release|Any CPU.ActiveCfg = Release|Any CPU {357D2522-4481-4135-8379-C228C743DD69}.Release|Any CPU.Build.0 = Release|Any CPU + {357D2522-4481-4135-8379-C228C743DD69}.Release|x64.ActiveCfg = Release|Any CPU + {357D2522-4481-4135-8379-C228C743DD69}.Release|x64.Build.0 = Release|Any CPU + {357D2522-4481-4135-8379-C228C743DD69}.Release|x86.ActiveCfg = Release|Any CPU + {357D2522-4481-4135-8379-C228C743DD69}.Release|x86.Build.0 = Release|Any CPU {321A2E5D-6A3E-44B6-BECE-D1FFA94D58B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {321A2E5D-6A3E-44B6-BECE-D1FFA94D58B9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {321A2E5D-6A3E-44B6-BECE-D1FFA94D58B9}.Debug|x64.ActiveCfg = Debug|Any CPU + {321A2E5D-6A3E-44B6-BECE-D1FFA94D58B9}.Debug|x64.Build.0 = Debug|Any CPU + {321A2E5D-6A3E-44B6-BECE-D1FFA94D58B9}.Debug|x86.ActiveCfg = Debug|Any CPU + {321A2E5D-6A3E-44B6-BECE-D1FFA94D58B9}.Debug|x86.Build.0 = Debug|Any CPU {321A2E5D-6A3E-44B6-BECE-D1FFA94D58B9}.Release|Any CPU.ActiveCfg = Release|Any CPU {321A2E5D-6A3E-44B6-BECE-D1FFA94D58B9}.Release|Any CPU.Build.0 = Release|Any CPU + {321A2E5D-6A3E-44B6-BECE-D1FFA94D58B9}.Release|x64.ActiveCfg = Release|Any CPU + {321A2E5D-6A3E-44B6-BECE-D1FFA94D58B9}.Release|x64.Build.0 = Release|Any CPU + {321A2E5D-6A3E-44B6-BECE-D1FFA94D58B9}.Release|x86.ActiveCfg = Release|Any CPU + {321A2E5D-6A3E-44B6-BECE-D1FFA94D58B9}.Release|x86.Build.0 = Release|Any CPU {A454B861-62B4-4F4D-8E31-725C4592D169}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A454B861-62B4-4F4D-8E31-725C4592D169}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A454B861-62B4-4F4D-8E31-725C4592D169}.Debug|x64.ActiveCfg = Debug|Any CPU + {A454B861-62B4-4F4D-8E31-725C4592D169}.Debug|x64.Build.0 = Debug|Any CPU + {A454B861-62B4-4F4D-8E31-725C4592D169}.Debug|x86.ActiveCfg = Debug|Any CPU + {A454B861-62B4-4F4D-8E31-725C4592D169}.Debug|x86.Build.0 = Debug|Any CPU {A454B861-62B4-4F4D-8E31-725C4592D169}.Release|Any CPU.ActiveCfg = Release|Any CPU {A454B861-62B4-4F4D-8E31-725C4592D169}.Release|Any CPU.Build.0 = Release|Any CPU + {A454B861-62B4-4F4D-8E31-725C4592D169}.Release|x64.ActiveCfg = Release|Any CPU + {A454B861-62B4-4F4D-8E31-725C4592D169}.Release|x64.Build.0 = Release|Any CPU + {A454B861-62B4-4F4D-8E31-725C4592D169}.Release|x86.ActiveCfg = Release|Any CPU + {A454B861-62B4-4F4D-8E31-725C4592D169}.Release|x86.Build.0 = Release|Any CPU {F89C3981-6D5A-4D63-8CD0-FB35E55C5835}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F89C3981-6D5A-4D63-8CD0-FB35E55C5835}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F89C3981-6D5A-4D63-8CD0-FB35E55C5835}.Debug|x64.ActiveCfg = Debug|Any CPU + {F89C3981-6D5A-4D63-8CD0-FB35E55C5835}.Debug|x64.Build.0 = Debug|Any CPU + {F89C3981-6D5A-4D63-8CD0-FB35E55C5835}.Debug|x86.ActiveCfg = Debug|Any CPU + {F89C3981-6D5A-4D63-8CD0-FB35E55C5835}.Debug|x86.Build.0 = Debug|Any CPU {F89C3981-6D5A-4D63-8CD0-FB35E55C5835}.Release|Any CPU.ActiveCfg = Release|Any CPU {F89C3981-6D5A-4D63-8CD0-FB35E55C5835}.Release|Any CPU.Build.0 = Release|Any CPU + {F89C3981-6D5A-4D63-8CD0-FB35E55C5835}.Release|x64.ActiveCfg = Release|Any CPU + {F89C3981-6D5A-4D63-8CD0-FB35E55C5835}.Release|x64.Build.0 = Release|Any CPU + {F89C3981-6D5A-4D63-8CD0-FB35E55C5835}.Release|x86.ActiveCfg = Release|Any CPU + {F89C3981-6D5A-4D63-8CD0-FB35E55C5835}.Release|x86.Build.0 = Release|Any CPU {A8FFC45B-7FC3-44B3-B8AF-CA9642E66546}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A8FFC45B-7FC3-44B3-B8AF-CA9642E66546}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A8FFC45B-7FC3-44B3-B8AF-CA9642E66546}.Debug|x64.ActiveCfg = Debug|Any CPU + {A8FFC45B-7FC3-44B3-B8AF-CA9642E66546}.Debug|x64.Build.0 = Debug|Any CPU + {A8FFC45B-7FC3-44B3-B8AF-CA9642E66546}.Debug|x86.ActiveCfg = Debug|Any CPU + {A8FFC45B-7FC3-44B3-B8AF-CA9642E66546}.Debug|x86.Build.0 = Debug|Any CPU {A8FFC45B-7FC3-44B3-B8AF-CA9642E66546}.Release|Any CPU.ActiveCfg = Release|Any CPU {A8FFC45B-7FC3-44B3-B8AF-CA9642E66546}.Release|Any CPU.Build.0 = Release|Any CPU + {A8FFC45B-7FC3-44B3-B8AF-CA9642E66546}.Release|x64.ActiveCfg = Release|Any CPU + {A8FFC45B-7FC3-44B3-B8AF-CA9642E66546}.Release|x64.Build.0 = Release|Any CPU + {A8FFC45B-7FC3-44B3-B8AF-CA9642E66546}.Release|x86.ActiveCfg = Release|Any CPU + {A8FFC45B-7FC3-44B3-B8AF-CA9642E66546}.Release|x86.Build.0 = Release|Any CPU {9D3631D0-C7DA-4283-BA24-A14424B0F7BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9D3631D0-C7DA-4283-BA24-A14424B0F7BE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9D3631D0-C7DA-4283-BA24-A14424B0F7BE}.Debug|x64.ActiveCfg = Debug|Any CPU + {9D3631D0-C7DA-4283-BA24-A14424B0F7BE}.Debug|x64.Build.0 = Debug|Any CPU + {9D3631D0-C7DA-4283-BA24-A14424B0F7BE}.Debug|x86.ActiveCfg = Debug|Any CPU + {9D3631D0-C7DA-4283-BA24-A14424B0F7BE}.Debug|x86.Build.0 = Debug|Any CPU {9D3631D0-C7DA-4283-BA24-A14424B0F7BE}.Release|Any CPU.ActiveCfg = Release|Any CPU {9D3631D0-C7DA-4283-BA24-A14424B0F7BE}.Release|Any CPU.Build.0 = Release|Any CPU + {9D3631D0-C7DA-4283-BA24-A14424B0F7BE}.Release|x64.ActiveCfg = Release|Any CPU + {9D3631D0-C7DA-4283-BA24-A14424B0F7BE}.Release|x64.Build.0 = Release|Any CPU + {9D3631D0-C7DA-4283-BA24-A14424B0F7BE}.Release|x86.ActiveCfg = Release|Any CPU + {9D3631D0-C7DA-4283-BA24-A14424B0F7BE}.Release|x86.Build.0 = Release|Any CPU {CDB5B2C4-4CD6-4AE0-9C4B-451B3170FB55}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CDB5B2C4-4CD6-4AE0-9C4B-451B3170FB55}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CDB5B2C4-4CD6-4AE0-9C4B-451B3170FB55}.Debug|x64.ActiveCfg = Debug|Any CPU + {CDB5B2C4-4CD6-4AE0-9C4B-451B3170FB55}.Debug|x64.Build.0 = Debug|Any CPU + {CDB5B2C4-4CD6-4AE0-9C4B-451B3170FB55}.Debug|x86.ActiveCfg = Debug|Any CPU + {CDB5B2C4-4CD6-4AE0-9C4B-451B3170FB55}.Debug|x86.Build.0 = Debug|Any CPU {CDB5B2C4-4CD6-4AE0-9C4B-451B3170FB55}.Release|Any CPU.ActiveCfg = Release|Any CPU {CDB5B2C4-4CD6-4AE0-9C4B-451B3170FB55}.Release|Any CPU.Build.0 = Release|Any CPU + {CDB5B2C4-4CD6-4AE0-9C4B-451B3170FB55}.Release|x64.ActiveCfg = Release|Any CPU + {CDB5B2C4-4CD6-4AE0-9C4B-451B3170FB55}.Release|x64.Build.0 = Release|Any CPU + {CDB5B2C4-4CD6-4AE0-9C4B-451B3170FB55}.Release|x86.ActiveCfg = Release|Any CPU + {CDB5B2C4-4CD6-4AE0-9C4B-451B3170FB55}.Release|x86.Build.0 = Release|Any CPU {CC0FE166-E649-4CA4-AACD-C0205ECDF896}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CC0FE166-E649-4CA4-AACD-C0205ECDF896}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CC0FE166-E649-4CA4-AACD-C0205ECDF896}.Debug|x64.ActiveCfg = Debug|Any CPU + {CC0FE166-E649-4CA4-AACD-C0205ECDF896}.Debug|x64.Build.0 = Debug|Any CPU + {CC0FE166-E649-4CA4-AACD-C0205ECDF896}.Debug|x86.ActiveCfg = Debug|Any CPU + {CC0FE166-E649-4CA4-AACD-C0205ECDF896}.Debug|x86.Build.0 = Debug|Any CPU {CC0FE166-E649-4CA4-AACD-C0205ECDF896}.Release|Any CPU.ActiveCfg = Release|Any CPU {CC0FE166-E649-4CA4-AACD-C0205ECDF896}.Release|Any CPU.Build.0 = Release|Any CPU + {CC0FE166-E649-4CA4-AACD-C0205ECDF896}.Release|x64.ActiveCfg = Release|Any CPU + {CC0FE166-E649-4CA4-AACD-C0205ECDF896}.Release|x64.Build.0 = Release|Any CPU + {CC0FE166-E649-4CA4-AACD-C0205ECDF896}.Release|x86.ActiveCfg = Release|Any CPU + {CC0FE166-E649-4CA4-AACD-C0205ECDF896}.Release|x86.Build.0 = Release|Any CPU {C97D62CF-A541-4393-A49A-92F53F1E1983}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C97D62CF-A541-4393-A49A-92F53F1E1983}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C97D62CF-A541-4393-A49A-92F53F1E1983}.Debug|x64.ActiveCfg = Debug|Any CPU + {C97D62CF-A541-4393-A49A-92F53F1E1983}.Debug|x64.Build.0 = Debug|Any CPU + {C97D62CF-A541-4393-A49A-92F53F1E1983}.Debug|x86.ActiveCfg = Debug|Any CPU + {C97D62CF-A541-4393-A49A-92F53F1E1983}.Debug|x86.Build.0 = Debug|Any CPU {C97D62CF-A541-4393-A49A-92F53F1E1983}.Release|Any CPU.ActiveCfg = Release|Any CPU {C97D62CF-A541-4393-A49A-92F53F1E1983}.Release|Any CPU.Build.0 = Release|Any CPU + {C97D62CF-A541-4393-A49A-92F53F1E1983}.Release|x64.ActiveCfg = Release|Any CPU + {C97D62CF-A541-4393-A49A-92F53F1E1983}.Release|x64.Build.0 = Release|Any CPU + {C97D62CF-A541-4393-A49A-92F53F1E1983}.Release|x86.ActiveCfg = Release|Any CPU + {C97D62CF-A541-4393-A49A-92F53F1E1983}.Release|x86.Build.0 = Release|Any CPU {36ADA62A-0AD5-461A-B28C-A5DBAF718B59}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {36ADA62A-0AD5-461A-B28C-A5DBAF718B59}.Debug|Any CPU.Build.0 = Debug|Any CPU + {36ADA62A-0AD5-461A-B28C-A5DBAF718B59}.Debug|x64.ActiveCfg = Debug|Any CPU + {36ADA62A-0AD5-461A-B28C-A5DBAF718B59}.Debug|x64.Build.0 = Debug|Any CPU + {36ADA62A-0AD5-461A-B28C-A5DBAF718B59}.Debug|x86.ActiveCfg = Debug|Any CPU + {36ADA62A-0AD5-461A-B28C-A5DBAF718B59}.Debug|x86.Build.0 = Debug|Any CPU {36ADA62A-0AD5-461A-B28C-A5DBAF718B59}.Release|Any CPU.ActiveCfg = Release|Any CPU {36ADA62A-0AD5-461A-B28C-A5DBAF718B59}.Release|Any CPU.Build.0 = Release|Any CPU + {36ADA62A-0AD5-461A-B28C-A5DBAF718B59}.Release|x64.ActiveCfg = Release|Any CPU + {36ADA62A-0AD5-461A-B28C-A5DBAF718B59}.Release|x64.Build.0 = Release|Any CPU + {36ADA62A-0AD5-461A-B28C-A5DBAF718B59}.Release|x86.ActiveCfg = Release|Any CPU + {36ADA62A-0AD5-461A-B28C-A5DBAF718B59}.Release|x86.Build.0 = Release|Any CPU {A2AD0EF1-6943-46E0-BEED-0704D362FA48}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A2AD0EF1-6943-46E0-BEED-0704D362FA48}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A2AD0EF1-6943-46E0-BEED-0704D362FA48}.Debug|x64.ActiveCfg = Debug|Any CPU + {A2AD0EF1-6943-46E0-BEED-0704D362FA48}.Debug|x64.Build.0 = Debug|Any CPU + {A2AD0EF1-6943-46E0-BEED-0704D362FA48}.Debug|x86.ActiveCfg = Debug|Any CPU + {A2AD0EF1-6943-46E0-BEED-0704D362FA48}.Debug|x86.Build.0 = Debug|Any CPU {A2AD0EF1-6943-46E0-BEED-0704D362FA48}.Release|Any CPU.ActiveCfg = Release|Any CPU {A2AD0EF1-6943-46E0-BEED-0704D362FA48}.Release|Any CPU.Build.0 = Release|Any CPU + {A2AD0EF1-6943-46E0-BEED-0704D362FA48}.Release|x64.ActiveCfg = Release|Any CPU + {A2AD0EF1-6943-46E0-BEED-0704D362FA48}.Release|x64.Build.0 = Release|Any CPU + {A2AD0EF1-6943-46E0-BEED-0704D362FA48}.Release|x86.ActiveCfg = Release|Any CPU + {A2AD0EF1-6943-46E0-BEED-0704D362FA48}.Release|x86.Build.0 = Release|Any CPU {9272A135-C1F7-4317-8AEA-1BFDEE2DC683}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9272A135-C1F7-4317-8AEA-1BFDEE2DC683}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9272A135-C1F7-4317-8AEA-1BFDEE2DC683}.Debug|x64.ActiveCfg = Debug|Any CPU + {9272A135-C1F7-4317-8AEA-1BFDEE2DC683}.Debug|x64.Build.0 = Debug|Any CPU + {9272A135-C1F7-4317-8AEA-1BFDEE2DC683}.Debug|x86.ActiveCfg = Debug|Any CPU + {9272A135-C1F7-4317-8AEA-1BFDEE2DC683}.Debug|x86.Build.0 = Debug|Any CPU {9272A135-C1F7-4317-8AEA-1BFDEE2DC683}.Release|Any CPU.ActiveCfg = Release|Any CPU {9272A135-C1F7-4317-8AEA-1BFDEE2DC683}.Release|Any CPU.Build.0 = Release|Any CPU + {9272A135-C1F7-4317-8AEA-1BFDEE2DC683}.Release|x64.ActiveCfg = Release|Any CPU + {9272A135-C1F7-4317-8AEA-1BFDEE2DC683}.Release|x64.Build.0 = Release|Any CPU + {9272A135-C1F7-4317-8AEA-1BFDEE2DC683}.Release|x86.ActiveCfg = Release|Any CPU + {9272A135-C1F7-4317-8AEA-1BFDEE2DC683}.Release|x86.Build.0 = Release|Any CPU {162A1CAE-ACEE-45CA-A6D0-7702ADE4D3DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {162A1CAE-ACEE-45CA-A6D0-7702ADE4D3DE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {162A1CAE-ACEE-45CA-A6D0-7702ADE4D3DE}.Debug|x64.ActiveCfg = Debug|Any CPU + {162A1CAE-ACEE-45CA-A6D0-7702ADE4D3DE}.Debug|x64.Build.0 = Debug|Any CPU + {162A1CAE-ACEE-45CA-A6D0-7702ADE4D3DE}.Debug|x86.ActiveCfg = Debug|Any CPU + {162A1CAE-ACEE-45CA-A6D0-7702ADE4D3DE}.Debug|x86.Build.0 = Debug|Any CPU {162A1CAE-ACEE-45CA-A6D0-7702ADE4D3DE}.Release|Any CPU.ActiveCfg = Release|Any CPU {162A1CAE-ACEE-45CA-A6D0-7702ADE4D3DE}.Release|Any CPU.Build.0 = Release|Any CPU + {162A1CAE-ACEE-45CA-A6D0-7702ADE4D3DE}.Release|x64.ActiveCfg = Release|Any CPU + {162A1CAE-ACEE-45CA-A6D0-7702ADE4D3DE}.Release|x64.Build.0 = Release|Any CPU + {162A1CAE-ACEE-45CA-A6D0-7702ADE4D3DE}.Release|x86.ActiveCfg = Release|Any CPU + {162A1CAE-ACEE-45CA-A6D0-7702ADE4D3DE}.Release|x86.Build.0 = Release|Any CPU {67269916-C417-4CEE-BD7D-CA66C3830AEE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {67269916-C417-4CEE-BD7D-CA66C3830AEE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {67269916-C417-4CEE-BD7D-CA66C3830AEE}.Debug|x64.ActiveCfg = Debug|Any CPU + {67269916-C417-4CEE-BD7D-CA66C3830AEE}.Debug|x64.Build.0 = Debug|Any CPU + {67269916-C417-4CEE-BD7D-CA66C3830AEE}.Debug|x86.ActiveCfg = Debug|Any CPU + {67269916-C417-4CEE-BD7D-CA66C3830AEE}.Debug|x86.Build.0 = Debug|Any CPU {67269916-C417-4CEE-BD7D-CA66C3830AEE}.Release|Any CPU.ActiveCfg = Release|Any CPU {67269916-C417-4CEE-BD7D-CA66C3830AEE}.Release|Any CPU.Build.0 = Release|Any CPU + {67269916-C417-4CEE-BD7D-CA66C3830AEE}.Release|x64.ActiveCfg = Release|Any CPU + {67269916-C417-4CEE-BD7D-CA66C3830AEE}.Release|x64.Build.0 = Release|Any CPU + {67269916-C417-4CEE-BD7D-CA66C3830AEE}.Release|x86.ActiveCfg = Release|Any CPU + {67269916-C417-4CEE-BD7D-CA66C3830AEE}.Release|x86.Build.0 = Release|Any CPU {8032310D-3C06-442C-A318-F365BCC4C804}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8032310D-3C06-442C-A318-F365BCC4C804}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8032310D-3C06-442C-A318-F365BCC4C804}.Debug|x64.ActiveCfg = Debug|Any CPU + {8032310D-3C06-442C-A318-F365BCC4C804}.Debug|x64.Build.0 = Debug|Any CPU + {8032310D-3C06-442C-A318-F365BCC4C804}.Debug|x86.ActiveCfg = Debug|Any CPU + {8032310D-3C06-442C-A318-F365BCC4C804}.Debug|x86.Build.0 = Debug|Any CPU {8032310D-3C06-442C-A318-F365BCC4C804}.Release|Any CPU.ActiveCfg = Release|Any CPU {8032310D-3C06-442C-A318-F365BCC4C804}.Release|Any CPU.Build.0 = Release|Any CPU + {8032310D-3C06-442C-A318-F365BCC4C804}.Release|x64.ActiveCfg = Release|Any CPU + {8032310D-3C06-442C-A318-F365BCC4C804}.Release|x64.Build.0 = Release|Any CPU + {8032310D-3C06-442C-A318-F365BCC4C804}.Release|x86.ActiveCfg = Release|Any CPU + {8032310D-3C06-442C-A318-F365BCC4C804}.Release|x86.Build.0 = Release|Any CPU {FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2}.Debug|x64.ActiveCfg = Debug|Any CPU + {FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2}.Debug|x64.Build.0 = Debug|Any CPU + {FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2}.Debug|x86.ActiveCfg = Debug|Any CPU + {FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2}.Debug|x86.Build.0 = Debug|Any CPU {FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2}.Release|Any CPU.ActiveCfg = Release|Any CPU {FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2}.Release|Any CPU.Build.0 = Release|Any CPU + {FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2}.Release|x64.ActiveCfg = Release|Any CPU + {FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2}.Release|x64.Build.0 = Release|Any CPU + {FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2}.Release|x86.ActiveCfg = Release|Any CPU + {FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2}.Release|x86.Build.0 = Release|Any CPU {5B100CC0-1A78-407E-A5A5-94BC06D67461}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5B100CC0-1A78-407E-A5A5-94BC06D67461}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5B100CC0-1A78-407E-A5A5-94BC06D67461}.Debug|x64.ActiveCfg = Debug|Any CPU + {5B100CC0-1A78-407E-A5A5-94BC06D67461}.Debug|x64.Build.0 = Debug|Any CPU + {5B100CC0-1A78-407E-A5A5-94BC06D67461}.Debug|x86.ActiveCfg = Debug|Any CPU + {5B100CC0-1A78-407E-A5A5-94BC06D67461}.Debug|x86.Build.0 = Debug|Any CPU {5B100CC0-1A78-407E-A5A5-94BC06D67461}.Release|Any CPU.ActiveCfg = Release|Any CPU {5B100CC0-1A78-407E-A5A5-94BC06D67461}.Release|Any CPU.Build.0 = Release|Any CPU + {5B100CC0-1A78-407E-A5A5-94BC06D67461}.Release|x64.ActiveCfg = Release|Any CPU + {5B100CC0-1A78-407E-A5A5-94BC06D67461}.Release|x64.Build.0 = Release|Any CPU + {5B100CC0-1A78-407E-A5A5-94BC06D67461}.Release|x86.ActiveCfg = Release|Any CPU + {5B100CC0-1A78-407E-A5A5-94BC06D67461}.Release|x86.Build.0 = Release|Any CPU {407C477D-69C0-4B02-8A68-EE6B5A81C696}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {407C477D-69C0-4B02-8A68-EE6B5A81C696}.Debug|Any CPU.Build.0 = Debug|Any CPU + {407C477D-69C0-4B02-8A68-EE6B5A81C696}.Debug|x64.ActiveCfg = Debug|Any CPU + {407C477D-69C0-4B02-8A68-EE6B5A81C696}.Debug|x64.Build.0 = Debug|Any CPU + {407C477D-69C0-4B02-8A68-EE6B5A81C696}.Debug|x86.ActiveCfg = Debug|Any CPU + {407C477D-69C0-4B02-8A68-EE6B5A81C696}.Debug|x86.Build.0 = Debug|Any CPU {407C477D-69C0-4B02-8A68-EE6B5A81C696}.Release|Any CPU.ActiveCfg = Release|Any CPU {407C477D-69C0-4B02-8A68-EE6B5A81C696}.Release|Any CPU.Build.0 = Release|Any CPU + {407C477D-69C0-4B02-8A68-EE6B5A81C696}.Release|x64.ActiveCfg = Release|Any CPU + {407C477D-69C0-4B02-8A68-EE6B5A81C696}.Release|x64.Build.0 = Release|Any CPU + {407C477D-69C0-4B02-8A68-EE6B5A81C696}.Release|x86.ActiveCfg = Release|Any CPU + {407C477D-69C0-4B02-8A68-EE6B5A81C696}.Release|x86.Build.0 = Release|Any CPU {EADF25F5-8D02-4747-AB54-5F2BAA648471}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EADF25F5-8D02-4747-AB54-5F2BAA648471}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EADF25F5-8D02-4747-AB54-5F2BAA648471}.Debug|x64.ActiveCfg = Debug|Any CPU + {EADF25F5-8D02-4747-AB54-5F2BAA648471}.Debug|x64.Build.0 = Debug|Any CPU + {EADF25F5-8D02-4747-AB54-5F2BAA648471}.Debug|x86.ActiveCfg = Debug|Any CPU + {EADF25F5-8D02-4747-AB54-5F2BAA648471}.Debug|x86.Build.0 = Debug|Any CPU {EADF25F5-8D02-4747-AB54-5F2BAA648471}.Release|Any CPU.ActiveCfg = Release|Any CPU {EADF25F5-8D02-4747-AB54-5F2BAA648471}.Release|Any CPU.Build.0 = Release|Any CPU + {EADF25F5-8D02-4747-AB54-5F2BAA648471}.Release|x64.ActiveCfg = Release|Any CPU + {EADF25F5-8D02-4747-AB54-5F2BAA648471}.Release|x64.Build.0 = Release|Any CPU + {EADF25F5-8D02-4747-AB54-5F2BAA648471}.Release|x86.ActiveCfg = Release|Any CPU + {EADF25F5-8D02-4747-AB54-5F2BAA648471}.Release|x86.Build.0 = Release|Any CPU {46E40BE8-1AB0-4846-B0A2-A40AD0272C64}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {46E40BE8-1AB0-4846-B0A2-A40AD0272C64}.Debug|Any CPU.Build.0 = Debug|Any CPU + {46E40BE8-1AB0-4846-B0A2-A40AD0272C64}.Debug|x64.ActiveCfg = Debug|Any CPU + {46E40BE8-1AB0-4846-B0A2-A40AD0272C64}.Debug|x64.Build.0 = Debug|Any CPU + {46E40BE8-1AB0-4846-B0A2-A40AD0272C64}.Debug|x86.ActiveCfg = Debug|Any CPU + {46E40BE8-1AB0-4846-B0A2-A40AD0272C64}.Debug|x86.Build.0 = Debug|Any CPU {46E40BE8-1AB0-4846-B0A2-A40AD0272C64}.Release|Any CPU.ActiveCfg = Release|Any CPU {46E40BE8-1AB0-4846-B0A2-A40AD0272C64}.Release|Any CPU.Build.0 = Release|Any CPU - {8298202C-9983-4D0A-851D-805539EE481A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8298202C-9983-4D0A-851D-805539EE481A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8298202C-9983-4D0A-851D-805539EE481A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8298202C-9983-4D0A-851D-805539EE481A}.Release|Any CPU.Build.0 = Release|Any CPU + {46E40BE8-1AB0-4846-B0A2-A40AD0272C64}.Release|x64.ActiveCfg = Release|Any CPU + {46E40BE8-1AB0-4846-B0A2-A40AD0272C64}.Release|x64.Build.0 = Release|Any CPU + {46E40BE8-1AB0-4846-B0A2-A40AD0272C64}.Release|x86.ActiveCfg = Release|Any CPU + {46E40BE8-1AB0-4846-B0A2-A40AD0272C64}.Release|x86.Build.0 = Release|Any CPU {A5B26C14-7313-4EDC-91E3-287F9374AB75}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A5B26C14-7313-4EDC-91E3-287F9374AB75}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A5B26C14-7313-4EDC-91E3-287F9374AB75}.Debug|x64.ActiveCfg = Debug|Any CPU + {A5B26C14-7313-4EDC-91E3-287F9374AB75}.Debug|x64.Build.0 = Debug|Any CPU + {A5B26C14-7313-4EDC-91E3-287F9374AB75}.Debug|x86.ActiveCfg = Debug|Any CPU + {A5B26C14-7313-4EDC-91E3-287F9374AB75}.Debug|x86.Build.0 = Debug|Any CPU {A5B26C14-7313-4EDC-91E3-287F9374AB75}.Release|Any CPU.ActiveCfg = Release|Any CPU {A5B26C14-7313-4EDC-91E3-287F9374AB75}.Release|Any CPU.Build.0 = Release|Any CPU + {A5B26C14-7313-4EDC-91E3-287F9374AB75}.Release|x64.ActiveCfg = Release|Any CPU + {A5B26C14-7313-4EDC-91E3-287F9374AB75}.Release|x64.Build.0 = Release|Any CPU + {A5B26C14-7313-4EDC-91E3-287F9374AB75}.Release|x86.ActiveCfg = Release|Any CPU + {A5B26C14-7313-4EDC-91E3-287F9374AB75}.Release|x86.Build.0 = Release|Any CPU + {8298202C-9983-4D0A-851D-805539EE481A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8298202C-9983-4D0A-851D-805539EE481A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8298202C-9983-4D0A-851D-805539EE481A}.Debug|x64.ActiveCfg = Debug|Any CPU + {8298202C-9983-4D0A-851D-805539EE481A}.Debug|x64.Build.0 = Debug|Any CPU + {8298202C-9983-4D0A-851D-805539EE481A}.Debug|x86.ActiveCfg = Debug|Any CPU + {8298202C-9983-4D0A-851D-805539EE481A}.Debug|x86.Build.0 = Debug|Any CPU + {8298202C-9983-4D0A-851D-805539EE481A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8298202C-9983-4D0A-851D-805539EE481A}.Release|Any CPU.Build.0 = Release|Any CPU + {8298202C-9983-4D0A-851D-805539EE481A}.Release|x64.ActiveCfg = Release|Any CPU + {8298202C-9983-4D0A-851D-805539EE481A}.Release|x64.Build.0 = Release|Any CPU + {8298202C-9983-4D0A-851D-805539EE481A}.Release|x86.ActiveCfg = Release|Any CPU + {8298202C-9983-4D0A-851D-805539EE481A}.Release|x86.Build.0 = Release|Any CPU {E36C8DCA-464E-41CB-B189-F58553AAA8D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E36C8DCA-464E-41CB-B189-F58553AAA8D4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E36C8DCA-464E-41CB-B189-F58553AAA8D4}.Debug|x64.ActiveCfg = Debug|Any CPU + {E36C8DCA-464E-41CB-B189-F58553AAA8D4}.Debug|x64.Build.0 = Debug|Any CPU + {E36C8DCA-464E-41CB-B189-F58553AAA8D4}.Debug|x86.ActiveCfg = Debug|Any CPU + {E36C8DCA-464E-41CB-B189-F58553AAA8D4}.Debug|x86.Build.0 = Debug|Any CPU {E36C8DCA-464E-41CB-B189-F58553AAA8D4}.Release|Any CPU.ActiveCfg = Release|Any CPU {E36C8DCA-464E-41CB-B189-F58553AAA8D4}.Release|Any CPU.Build.0 = Release|Any CPU + {E36C8DCA-464E-41CB-B189-F58553AAA8D4}.Release|x64.ActiveCfg = Release|Any CPU + {E36C8DCA-464E-41CB-B189-F58553AAA8D4}.Release|x64.Build.0 = Release|Any CPU + {E36C8DCA-464E-41CB-B189-F58553AAA8D4}.Release|x86.ActiveCfg = Release|Any CPU + {E36C8DCA-464E-41CB-B189-F58553AAA8D4}.Release|x86.Build.0 = Release|Any CPU {5A17FEF9-07BB-47B8-9883-9C2CC93F09E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5A17FEF9-07BB-47B8-9883-9C2CC93F09E8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5A17FEF9-07BB-47B8-9883-9C2CC93F09E8}.Debug|x64.ActiveCfg = Debug|Any CPU + {5A17FEF9-07BB-47B8-9883-9C2CC93F09E8}.Debug|x64.Build.0 = Debug|Any CPU + {5A17FEF9-07BB-47B8-9883-9C2CC93F09E8}.Debug|x86.ActiveCfg = Debug|Any CPU + {5A17FEF9-07BB-47B8-9883-9C2CC93F09E8}.Debug|x86.Build.0 = Debug|Any CPU {5A17FEF9-07BB-47B8-9883-9C2CC93F09E8}.Release|Any CPU.ActiveCfg = Release|Any CPU {5A17FEF9-07BB-47B8-9883-9C2CC93F09E8}.Release|Any CPU.Build.0 = Release|Any CPU + {5A17FEF9-07BB-47B8-9883-9C2CC93F09E8}.Release|x64.ActiveCfg = Release|Any CPU + {5A17FEF9-07BB-47B8-9883-9C2CC93F09E8}.Release|x64.Build.0 = Release|Any CPU + {5A17FEF9-07BB-47B8-9883-9C2CC93F09E8}.Release|x86.ActiveCfg = Release|Any CPU + {5A17FEF9-07BB-47B8-9883-9C2CC93F09E8}.Release|x86.Build.0 = Release|Any CPU {D7DF0B26-AD43-4F8B-9BFE-C4471CCC9821}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D7DF0B26-AD43-4F8B-9BFE-C4471CCC9821}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D7DF0B26-AD43-4F8B-9BFE-C4471CCC9821}.Debug|x64.ActiveCfg = Debug|Any CPU + {D7DF0B26-AD43-4F8B-9BFE-C4471CCC9821}.Debug|x64.Build.0 = Debug|Any CPU + {D7DF0B26-AD43-4F8B-9BFE-C4471CCC9821}.Debug|x86.ActiveCfg = Debug|Any CPU + {D7DF0B26-AD43-4F8B-9BFE-C4471CCC9821}.Debug|x86.Build.0 = Debug|Any CPU {D7DF0B26-AD43-4F8B-9BFE-C4471CCC9821}.Release|Any CPU.ActiveCfg = Release|Any CPU {D7DF0B26-AD43-4F8B-9BFE-C4471CCC9821}.Release|Any CPU.Build.0 = Release|Any CPU + {D7DF0B26-AD43-4F8B-9BFE-C4471CCC9821}.Release|x64.ActiveCfg = Release|Any CPU + {D7DF0B26-AD43-4F8B-9BFE-C4471CCC9821}.Release|x64.Build.0 = Release|Any CPU + {D7DF0B26-AD43-4F8B-9BFE-C4471CCC9821}.Release|x86.ActiveCfg = Release|Any CPU + {D7DF0B26-AD43-4F8B-9BFE-C4471CCC9821}.Release|x86.Build.0 = Release|Any CPU {6030B748-0000-43B5-B8A8-399AA42F5229}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6030B748-0000-43B5-B8A8-399AA42F5229}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6030B748-0000-43B5-B8A8-399AA42F5229}.Debug|x64.ActiveCfg = Debug|Any CPU + {6030B748-0000-43B5-B8A8-399AA42F5229}.Debug|x64.Build.0 = Debug|Any CPU + {6030B748-0000-43B5-B8A8-399AA42F5229}.Debug|x86.ActiveCfg = Debug|Any CPU + {6030B748-0000-43B5-B8A8-399AA42F5229}.Debug|x86.Build.0 = Debug|Any CPU {6030B748-0000-43B5-B8A8-399AA42F5229}.Release|Any CPU.ActiveCfg = Release|Any CPU {6030B748-0000-43B5-B8A8-399AA42F5229}.Release|Any CPU.Build.0 = Release|Any CPU + {6030B748-0000-43B5-B8A8-399AA42F5229}.Release|x64.ActiveCfg = Release|Any CPU + {6030B748-0000-43B5-B8A8-399AA42F5229}.Release|x64.Build.0 = Release|Any CPU + {6030B748-0000-43B5-B8A8-399AA42F5229}.Release|x86.ActiveCfg = Release|Any CPU + {6030B748-0000-43B5-B8A8-399AA42F5229}.Release|x86.Build.0 = Release|Any CPU {DF92E098-822C-4B94-B96B-56BFB91FBB54}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DF92E098-822C-4B94-B96B-56BFB91FBB54}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DF92E098-822C-4B94-B96B-56BFB91FBB54}.Debug|x64.ActiveCfg = Debug|Any CPU + {DF92E098-822C-4B94-B96B-56BFB91FBB54}.Debug|x64.Build.0 = Debug|Any CPU + {DF92E098-822C-4B94-B96B-56BFB91FBB54}.Debug|x86.ActiveCfg = Debug|Any CPU + {DF92E098-822C-4B94-B96B-56BFB91FBB54}.Debug|x86.Build.0 = Debug|Any CPU {DF92E098-822C-4B94-B96B-56BFB91FBB54}.Release|Any CPU.ActiveCfg = Release|Any CPU {DF92E098-822C-4B94-B96B-56BFB91FBB54}.Release|Any CPU.Build.0 = Release|Any CPU + {DF92E098-822C-4B94-B96B-56BFB91FBB54}.Release|x64.ActiveCfg = Release|Any CPU + {DF92E098-822C-4B94-B96B-56BFB91FBB54}.Release|x64.Build.0 = Release|Any CPU + {DF92E098-822C-4B94-B96B-56BFB91FBB54}.Release|x86.ActiveCfg = Release|Any CPU + {DF92E098-822C-4B94-B96B-56BFB91FBB54}.Release|x86.Build.0 = Release|Any CPU + {E898F337-E982-46CC-8DA9-F8556AA7DD72}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E898F337-E982-46CC-8DA9-F8556AA7DD72}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E898F337-E982-46CC-8DA9-F8556AA7DD72}.Debug|x64.ActiveCfg = Debug|Any CPU + {E898F337-E982-46CC-8DA9-F8556AA7DD72}.Debug|x64.Build.0 = Debug|Any CPU + {E898F337-E982-46CC-8DA9-F8556AA7DD72}.Debug|x86.ActiveCfg = Debug|Any CPU + {E898F337-E982-46CC-8DA9-F8556AA7DD72}.Debug|x86.Build.0 = Debug|Any CPU + {E898F337-E982-46CC-8DA9-F8556AA7DD72}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E898F337-E982-46CC-8DA9-F8556AA7DD72}.Release|Any CPU.Build.0 = Release|Any CPU + {E898F337-E982-46CC-8DA9-F8556AA7DD72}.Release|x64.ActiveCfg = Release|Any CPU + {E898F337-E982-46CC-8DA9-F8556AA7DD72}.Release|x64.Build.0 = Release|Any CPU + {E898F337-E982-46CC-8DA9-F8556AA7DD72}.Release|x86.ActiveCfg = Release|Any CPU + {E898F337-E982-46CC-8DA9-F8556AA7DD72}.Release|x86.Build.0 = Release|Any CPU {C3CDF61C-3E28-441C-A9CE-011C89D11719}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C3CDF61C-3E28-441C-A9CE-011C89D11719}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C3CDF61C-3E28-441C-A9CE-011C89D11719}.Debug|x64.ActiveCfg = Debug|Any CPU + {C3CDF61C-3E28-441C-A9CE-011C89D11719}.Debug|x64.Build.0 = Debug|Any CPU + {C3CDF61C-3E28-441C-A9CE-011C89D11719}.Debug|x86.ActiveCfg = Debug|Any CPU + {C3CDF61C-3E28-441C-A9CE-011C89D11719}.Debug|x86.Build.0 = Debug|Any CPU {C3CDF61C-3E28-441C-A9CE-011C89D11719}.Release|Any CPU.ActiveCfg = Release|Any CPU {C3CDF61C-3E28-441C-A9CE-011C89D11719}.Release|Any CPU.Build.0 = Release|Any CPU + {C3CDF61C-3E28-441C-A9CE-011C89D11719}.Release|x64.ActiveCfg = Release|Any CPU + {C3CDF61C-3E28-441C-A9CE-011C89D11719}.Release|x64.Build.0 = Release|Any CPU + {C3CDF61C-3E28-441C-A9CE-011C89D11719}.Release|x86.ActiveCfg = Release|Any CPU + {C3CDF61C-3E28-441C-A9CE-011C89D11719}.Release|x86.Build.0 = Release|Any CPU {3A76FF7D-2F32-4EA5-8999-2FFE3C7CB893}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3A76FF7D-2F32-4EA5-8999-2FFE3C7CB893}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3A76FF7D-2F32-4EA5-8999-2FFE3C7CB893}.Debug|x64.ActiveCfg = Debug|Any CPU + {3A76FF7D-2F32-4EA5-8999-2FFE3C7CB893}.Debug|x64.Build.0 = Debug|Any CPU + {3A76FF7D-2F32-4EA5-8999-2FFE3C7CB893}.Debug|x86.ActiveCfg = Debug|Any CPU + {3A76FF7D-2F32-4EA5-8999-2FFE3C7CB893}.Debug|x86.Build.0 = Debug|Any CPU {3A76FF7D-2F32-4EA5-8999-2FFE3C7CB893}.Release|Any CPU.ActiveCfg = Release|Any CPU {3A76FF7D-2F32-4EA5-8999-2FFE3C7CB893}.Release|Any CPU.Build.0 = Release|Any CPU + {3A76FF7D-2F32-4EA5-8999-2FFE3C7CB893}.Release|x64.ActiveCfg = Release|Any CPU + {3A76FF7D-2F32-4EA5-8999-2FFE3C7CB893}.Release|x64.Build.0 = Release|Any CPU + {3A76FF7D-2F32-4EA5-8999-2FFE3C7CB893}.Release|x86.ActiveCfg = Release|Any CPU + {3A76FF7D-2F32-4EA5-8999-2FFE3C7CB893}.Release|x86.Build.0 = Release|Any CPU + {ADC91A84-6054-42EC-8241-0D717E4C7194}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ADC91A84-6054-42EC-8241-0D717E4C7194}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ADC91A84-6054-42EC-8241-0D717E4C7194}.Debug|x64.ActiveCfg = Debug|Any CPU + {ADC91A84-6054-42EC-8241-0D717E4C7194}.Debug|x64.Build.0 = Debug|Any CPU + {ADC91A84-6054-42EC-8241-0D717E4C7194}.Debug|x86.ActiveCfg = Debug|Any CPU + {ADC91A84-6054-42EC-8241-0D717E4C7194}.Debug|x86.Build.0 = Debug|Any CPU + {ADC91A84-6054-42EC-8241-0D717E4C7194}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ADC91A84-6054-42EC-8241-0D717E4C7194}.Release|Any CPU.Build.0 = Release|Any CPU + {ADC91A84-6054-42EC-8241-0D717E4C7194}.Release|x64.ActiveCfg = Release|Any CPU + {ADC91A84-6054-42EC-8241-0D717E4C7194}.Release|x64.Build.0 = Release|Any CPU + {ADC91A84-6054-42EC-8241-0D717E4C7194}.Release|x86.ActiveCfg = Release|Any CPU + {ADC91A84-6054-42EC-8241-0D717E4C7194}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -628,14 +1332,16 @@ Global {407C477D-69C0-4B02-8A68-EE6B5A81C696} = {21B42F60-5802-404E-90F0-AEBCC56760C0} {EADF25F5-8D02-4747-AB54-5F2BAA648471} = {230B9384-90FD-4551-A5DE-1A5C197F25B6} {46E40BE8-1AB0-4846-B0A2-A40AD0272C64} = {6987A1CC-608E-4868-A02C-09D30C8B7B2D} - {8298202C-9983-4D0A-851D-805539EE481A} = {230B9384-90FD-4551-A5DE-1A5C197F25B6} {A5B26C14-7313-4EDC-91E3-287F9374AB75} = {21B42F60-5802-404E-90F0-AEBCC56760C0} + {8298202C-9983-4D0A-851D-805539EE481A} = {230B9384-90FD-4551-A5DE-1A5C197F25B6} {E36C8DCA-464E-41CB-B189-F58553AAA8D4} = {230B9384-90FD-4551-A5DE-1A5C197F25B6} {5A17FEF9-07BB-47B8-9883-9C2CC93F09E8} = {6987A1CC-608E-4868-A02C-09D30C8B7B2D} {D7DF0B26-AD43-4F8B-9BFE-C4471CCC9821} = {21B42F60-5802-404E-90F0-AEBCC56760C0} {6030B748-0000-43B5-B8A8-399AA42F5229} = {6987A1CC-608E-4868-A02C-09D30C8B7B2D} {DF92E098-822C-4B94-B96B-56BFB91FBB54} = {6987A1CC-608E-4868-A02C-09D30C8B7B2D} + {E898F337-E982-46CC-8DA9-F8556AA7DD72} = {230B9384-90FD-4551-A5DE-1A5C197F25B6} {C3CDF61C-3E28-441C-A9CE-011C89D11719} = {230B9384-90FD-4551-A5DE-1A5C197F25B6} {3A76FF7D-2F32-4EA5-8999-2FFE3C7CB893} = {6987A1CC-608E-4868-A02C-09D30C8B7B2D} + {ADC91A84-6054-42EC-8241-0D717E4C7194} = {6987A1CC-608E-4868-A02C-09D30C8B7B2D} EndGlobalSection EndGlobal diff --git a/SentryMobile.slnf b/SentryMobile.slnf index 270552e1de..fa04ac2511 100644 --- a/SentryMobile.slnf +++ b/SentryMobile.slnf @@ -11,6 +11,7 @@ "src\\Sentry.Bindings.Android\\Sentry.Bindings.Android.csproj", "src\\Sentry.Bindings.Cocoa\\Sentry.Bindings.Cocoa.csproj", "src\\Sentry.Extensions.Logging\\Sentry.Extensions.Logging.csproj", + "src\\Sentry.Maui.CommunityToolkit.Mvvm\\Sentry.Maui.CommunityToolkit.Mvvm.csproj", "src\\Sentry.Maui\\Sentry.Maui.csproj", "src\\Sentry.SourceGenerators\\Sentry.SourceGenerators.csproj", "src\\Sentry\\Sentry.csproj", diff --git a/SentryNoSamples.slnf b/SentryNoSamples.slnf index f823107fbf..d3b4e187ea 100644 --- a/SentryNoSamples.slnf +++ b/SentryNoSamples.slnf @@ -16,6 +16,7 @@ "src\\Sentry.Google.Cloud.Functions\\Sentry.Google.Cloud.Functions.csproj", "src\\Sentry.Hangfire\\Sentry.Hangfire.csproj", "src\\Sentry.Log4Net\\Sentry.Log4Net.csproj", + "src\\Sentry.Maui.CommunityToolkit.Mvvm\\Sentry.Maui.CommunityToolkit.Mvvm.csproj", "src\\Sentry.Maui\\Sentry.Maui.csproj", "src\\Sentry.NLog\\Sentry.NLog.csproj", "src\\Sentry.OpenTelemetry\\Sentry.OpenTelemetry.csproj", @@ -37,6 +38,7 @@ "test\\Sentry.Google.Cloud.Functions.Tests\\Sentry.Google.Cloud.Functions.Tests.csproj", "test\\Sentry.Hangfire.Tests\\Sentry.Hangfire.Tests.csproj", "test\\Sentry.Log4Net.Tests\\Sentry.Log4Net.Tests.csproj", + "test\\Sentry.Maui.CommunityToolkit.Mvvm.Tests\\Sentry.Maui.CommunityToolkit.Mvvm.Tests.csproj", "test\\Sentry.Maui.Tests\\Sentry.Maui.Tests.csproj", "test\\Sentry.NLog.Tests\\Sentry.NLog.Tests.csproj", "test\\Sentry.OpenTelemetry.Tests\\Sentry.OpenTelemetry.Tests.csproj", diff --git a/samples/Sentry.Samples.Maui/AppShell.xaml.cs b/samples/Sentry.Samples.Maui/AppShell.xaml.cs index 234ec733a9..ee6c2db9fb 100644 --- a/samples/Sentry.Samples.Maui/AppShell.xaml.cs +++ b/samples/Sentry.Samples.Maui/AppShell.xaml.cs @@ -5,5 +5,6 @@ public partial class AppShell public AppShell() { InitializeComponent(); + Routing.RegisterRoute("ctmvvm", typeof(CtMvvmPage)); } } diff --git a/samples/Sentry.Samples.Maui/CtMvvmPage.xaml b/samples/Sentry.Samples.Maui/CtMvvmPage.xaml new file mode 100644 index 0000000000..9e7a483abe --- /dev/null +++ b/samples/Sentry.Samples.Maui/CtMvvmPage.xaml @@ -0,0 +1,18 @@ + + + + + + + + + +