From 1540d8aa0eed04fcc503a1e2598c2f662c047119 Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Mon, 24 Apr 2023 16:19:01 -0700 Subject: [PATCH 001/142] Update Directory.Build.props (#2324) --- test/Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Directory.Build.props b/test/Directory.Build.props index f19928b1bd..b37a6fb3ce 100644 --- a/test/Directory.Build.props +++ b/test/Directory.Build.props @@ -57,7 +57,7 @@ - + From bb2d408f41332a9836da40c02741ad99f70e9be9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Apr 2023 17:22:42 -0700 Subject: [PATCH 002/142] Bump github/codeql-action from 2.2.12 to 2.3.0 (#2323) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2.2.12 to 2.3.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/7df0ce34898d659f95c0c4a09eaa8d4e32ee64db...b2c19fb9a2a485599ccf4ed5d65527d94bc57226) --- 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> Co-authored-by: Matt Johnson-Pint --- .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 4f0a7f7c41..794c2ad257 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -34,7 +34,7 @@ jobs: uses: ./.github/actions/environment - name: Initialize CodeQL - uses: github/codeql-action/init@7df0ce34898d659f95c0c4a09eaa8d4e32ee64db # pin@v2 + uses: github/codeql-action/init@b2c19fb9a2a485599ccf4ed5d65527d94bc57226 # pin@v2 with: languages: csharp @@ -45,6 +45,6 @@ jobs: run: dotnet build Sentry-CI-CodeQL.slnf --no-restore --nologo - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@7df0ce34898d659f95c0c4a09eaa8d4e32ee64db # pin@v2 + uses: github/codeql-action/analyze@b2c19fb9a2a485599ccf4ed5d65527d94bc57226 # pin@v2 with: category: '/language:csharp' From 8fc5f7903bc21de9e95504ca357596da91f22ed7 Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Wed, 26 Apr 2023 14:20:11 -0700 Subject: [PATCH 003/142] Include Dynamic Sampling Context with error events, when there's a transaction (#2332) --- CHANGELOG.md | 1 + src/Sentry/Internal/Hub.cs | 10 ++++++-- src/Sentry/Protocol/Envelopes/Envelope.cs | 25 +++++++++++-------- src/Sentry/SentryEvent.cs | 2 ++ ...rocessorTests.WithTransaction.verified.txt | 8 ++++++ .../EventProcessorTests.verify.cs | 4 ++- ...sactionEndedAsCrashed.Core3_1.verified.txt | 12 +++++++-- ...ctionEndedAsCrashed.DotNet6_0.verified.txt | 12 +++++++-- ...ctionEndedAsCrashed.DotNet7_0.verified.txt | 12 +++++++-- ...sactionEndedAsCrashed.Mono4_0.verified.txt | 12 +++++++-- ...nsactionEndedAsCrashed.Net4_8.verified.txt | 12 +++++++-- ...sactionProcessorTests.Discard.verified.txt | 8 ++++++ ...nsactionProcessorTests.Simple.verified.txt | 14 ++++++++--- .../Sentry.Tests/TransactionProcessorTests.cs | 2 +- 14 files changed, 106 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a3cb444348..b394dd7e0a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - Initial work to support profiling in a future release. ([#2206](https://github.com/getsentry/sentry-dotnet/pull/2206)) - Improve `WithScope` and add `WithScopeAsync` ([#2303](https://github.com/getsentry/sentry-dotnet/pull/2303)) ([#2309](https://github.com/getsentry/sentry-dotnet/pull/2309)) +- Include Dynamic Sampling Context with error events, when there's a transaction ([#2332](https://github.com/getsentry/sentry-dotnet/pull/2332)) ### Fixes diff --git a/src/Sentry/Internal/Hub.cs b/src/Sentry/Internal/Hub.cs index 66163cb388..e1f947a0e8 100644 --- a/src/Sentry/Internal/Hub.cs +++ b/src/Sentry/Internal/Hub.cs @@ -348,7 +348,13 @@ SentryId IHubEx.CaptureEventInternal(SentryEvent evt, Scope? scope) actualScope.SessionUpdate = _sessionManager.ReportError(); } - var id = currentScope.Value.CaptureEvent(evt, actualScope); + // When a transaction is present, copy its DSC to the event. + var transaction = actualScope.Transaction as TransactionTracer; + evt.DynamicSamplingContext = transaction?.DynamicSamplingContext; + + // Now capture the event with the Sentry client on the current scope. + var sentryClient = currentScope.Value; + var id = sentryClient.CaptureEvent(evt, actualScope); actualScope.LastEventId = id; actualScope.SessionUpdate = null; @@ -357,7 +363,7 @@ SentryId IHubEx.CaptureEventInternal(SentryEvent evt, Scope? scope) // Event contains a terminal exception -> finish any current transaction as aborted // Do this *after* the event was captured, so that the event is still linked to the transaction. _options.LogDebug("Ending transaction as Aborted, due to unhandled exception."); - actualScope.Transaction?.Finish(SpanStatus.Aborted); + transaction?.Finish(SpanStatus.Aborted); } return id; diff --git a/src/Sentry/Protocol/Envelopes/Envelope.cs b/src/Sentry/Protocol/Envelopes/Envelope.cs index 1934a3ac9f..e4c30ee318 100644 --- a/src/Sentry/Protocol/Envelopes/Envelope.cs +++ b/src/Sentry/Protocol/Envelopes/Envelope.cs @@ -207,6 +207,18 @@ internal void Serialize(Stream stream, IDiagnosticLogger? logger, ISystemClock c ["event_id"] = eventId.ToString() }; + private static Dictionary CreateHeader(SentryId eventId, DynamicSamplingContext? dsc) + { + if (dsc == null) + { + return CreateHeader(eventId); + } + + var header = CreateHeader(eventId, extraCapacity: 1); + header["trace"] = dsc.Items; + return header; + } + /// /// Creates an envelope that contains a single event. /// @@ -217,7 +229,7 @@ public static Envelope FromEvent( SessionUpdate? sessionUpdate = null) { var eventId = @event.EventId; - var header = CreateHeader(eventId); + var header = CreateHeader(eventId, @event.DynamicSamplingContext); var items = new List { @@ -285,16 +297,7 @@ public static Envelope FromUserFeedback(UserFeedback sentryUserFeedback) public static Envelope FromTransaction(Transaction transaction) { var eventId = transaction.EventId; - Dictionary header; - if (transaction.DynamicSamplingContext is { } dsc) - { - header = CreateHeader(eventId, extraCapacity: 1); - header["trace"] = dsc.Items; - } - else - { - header = CreateHeader(eventId); - } + var header = CreateHeader(eventId, transaction.DynamicSamplingContext); var items = new List { diff --git a/src/Sentry/SentryEvent.cs b/src/Sentry/SentryEvent.cs index 90bcdbc642..423a45ce60 100644 --- a/src/Sentry/SentryEvent.cs +++ b/src/Sentry/SentryEvent.cs @@ -197,6 +197,8 @@ internal bool HasTerminalException() ) ?? false; } + internal DynamicSamplingContext? DynamicSamplingContext { get; set; } + /// /// Creates a new instance of . /// diff --git a/test/Sentry.Tests/EventProcessorTests.WithTransaction.verified.txt b/test/Sentry.Tests/EventProcessorTests.WithTransaction.verified.txt index 97aa4f2d55..e142c331c3 100644 --- a/test/Sentry.Tests/EventProcessorTests.WithTransaction.verified.txt +++ b/test/Sentry.Tests/EventProcessorTests.WithTransaction.verified.txt @@ -4,6 +4,14 @@ event_id: Guid_1, sdk: { name: sentry.dotnet + }, + trace: { + environment: production, + public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, + release: release, + sample_rate: 1, + trace_id: Guid_2, + transaction: my transaction } }, Items: [ diff --git a/test/Sentry.Tests/EventProcessorTests.verify.cs b/test/Sentry.Tests/EventProcessorTests.verify.cs index 34ec32ca07..2ab9f4761a 100644 --- a/test/Sentry.Tests/EventProcessorTests.verify.cs +++ b/test/Sentry.Tests/EventProcessorTests.verify.cs @@ -86,6 +86,8 @@ private SentryOptions Options(ITransport transport) => Transport = transport, Dsn = ValidDsn, DiagnosticLogger = _logger, - AttachStacktrace = false + AttachStacktrace = false, + Release = "release", + InitNativeSdks = false }; } diff --git a/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.Core3_1.verified.txt b/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.Core3_1.verified.txt index 94ad8d2f59..4eeeeb9e1c 100644 --- a/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.Core3_1.verified.txt +++ b/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.Core3_1.verified.txt @@ -26,6 +26,14 @@ 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: [ @@ -126,7 +134,7 @@ }, { Header: { - event_id: Guid_3, + event_id: Guid_4, sdk: { name: sentry.dotnet }, @@ -135,7 +143,7 @@ public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, release: release, sample_rate: 1, - trace_id: Guid_4, + trace_id: Guid_3, transaction: my transaction } }, 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 index 8f7cbecdd9..ae69b386e8 100644 --- a/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.DotNet6_0.verified.txt +++ b/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.DotNet6_0.verified.txt @@ -26,6 +26,14 @@ 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: [ @@ -126,7 +134,7 @@ }, { Header: { - event_id: Guid_3, + event_id: Guid_4, sdk: { name: sentry.dotnet }, @@ -135,7 +143,7 @@ public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, release: release, sample_rate: 1, - trace_id: Guid_4, + trace_id: Guid_3, transaction: my transaction } }, 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 index ebd34cc341..2824ce63df 100644 --- a/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.DotNet7_0.verified.txt +++ b/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.DotNet7_0.verified.txt @@ -26,6 +26,14 @@ 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: [ @@ -126,7 +134,7 @@ }, { Header: { - event_id: Guid_3, + event_id: Guid_4, sdk: { name: sentry.dotnet }, @@ -135,7 +143,7 @@ public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, release: release, sample_rate: 1, - trace_id: Guid_4, + trace_id: Guid_3, transaction: my transaction } }, diff --git a/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.Mono4_0.verified.txt b/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.Mono4_0.verified.txt index 0844cbba5b..80f9e24e92 100644 --- a/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.Mono4_0.verified.txt +++ b/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.Mono4_0.verified.txt @@ -26,6 +26,14 @@ 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: [ @@ -126,7 +134,7 @@ }, { Header: { - event_id: Guid_3, + event_id: Guid_4, sdk: { name: sentry.dotnet }, @@ -135,7 +143,7 @@ public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, release: release, sample_rate: 1, - trace_id: Guid_4, + trace_id: Guid_3, transaction: my transaction } }, 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 7a12befc08..a1379db4ea 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 @@ -26,6 +26,14 @@ 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: [ @@ -126,7 +134,7 @@ }, { Header: { - event_id: Guid_3, + event_id: Guid_4, sdk: { name: sentry.dotnet }, @@ -135,7 +143,7 @@ public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, release: release, sample_rate: 1, - trace_id: Guid_4, + trace_id: Guid_3, transaction: my transaction } }, diff --git a/test/Sentry.Tests/TransactionProcessorTests.Discard.verified.txt b/test/Sentry.Tests/TransactionProcessorTests.Discard.verified.txt index 2410c6420a..bba787fc3f 100644 --- a/test/Sentry.Tests/TransactionProcessorTests.Discard.verified.txt +++ b/test/Sentry.Tests/TransactionProcessorTests.Discard.verified.txt @@ -4,6 +4,14 @@ event_id: Guid_1, sdk: { name: sentry.dotnet + }, + trace: { + environment: production, + public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, + release: release, + sample_rate: 1, + trace_id: Guid_2, + transaction: my transaction } }, Items: [ diff --git a/test/Sentry.Tests/TransactionProcessorTests.Simple.verified.txt b/test/Sentry.Tests/TransactionProcessorTests.Simple.verified.txt index faaa8073fe..5b2e472532 100644 --- a/test/Sentry.Tests/TransactionProcessorTests.Simple.verified.txt +++ b/test/Sentry.Tests/TransactionProcessorTests.Simple.verified.txt @@ -4,6 +4,14 @@ event_id: Guid_1, sdk: { name: sentry.dotnet + }, + trace: { + environment: production, + public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, + release: release, + sample_rate: 1, + trace_id: Guid_2, + transaction: my transaction } }, Items: [ @@ -34,16 +42,16 @@ }, { Header: { - event_id: Guid_2, + event_id: Guid_3, sdk: { name: sentry.dotnet }, trace: { environment: production, public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, - release: Sentry.Tests@1.0.0, + release: release, sample_rate: 1, - trace_id: Guid_3, + trace_id: Guid_2, transaction: my transaction } }, diff --git a/test/Sentry.Tests/TransactionProcessorTests.cs b/test/Sentry.Tests/TransactionProcessorTests.cs index b691cb8194..044beba7bb 100644 --- a/test/Sentry.Tests/TransactionProcessorTests.cs +++ b/test/Sentry.Tests/TransactionProcessorTests.cs @@ -62,7 +62,7 @@ private SentryOptions Options(ITransport transport) => Dsn = ValidDsn, DiagnosticLogger = _logger, AttachStacktrace = false, - Release = "Sentry.Tests@1.0.0", + Release = "release", InitNativeSdks = false }; } From c9147562d3fa3c526a0c8ea3561868c14c1fb1c8 Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Wed, 26 Apr 2023 14:47:12 -0700 Subject: [PATCH 004/142] Build .NET Standard 2.1 for Unity (#2328) --- CHANGELOG.md | 1 + src/Sentry/Sentry.csproj | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b394dd7e0a..fbbe9159dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - Initial work to support profiling in a future release. ([#2206](https://github.com/getsentry/sentry-dotnet/pull/2206)) - Improve `WithScope` and add `WithScopeAsync` ([#2303](https://github.com/getsentry/sentry-dotnet/pull/2303)) ([#2309](https://github.com/getsentry/sentry-dotnet/pull/2309)) +- Build .NET Standard 2.1 for Unity ([#2328](https://github.com/getsentry/sentry-dotnet/pull/2328)) - Include Dynamic Sampling Context with error events, when there's a transaction ([#2332](https://github.com/getsentry/sentry-dotnet/pull/2332)) ### Fixes diff --git a/src/Sentry/Sentry.csproj b/src/Sentry/Sentry.csproj index 2a822bb47b..4d1f662fff 100644 --- a/src/Sentry/Sentry.csproj +++ b/src/Sentry/Sentry.csproj @@ -13,7 +13,7 @@ - netstandard2.0 + netstandard2.0;netstandard2.1 From 0112079b4455986de63623eaa48051976cbbd31a Mon Sep 17 00:00:00 2001 From: Stefan Jandl Date: Thu, 27 Apr 2023 01:40:07 +0200 Subject: [PATCH 005/142] Add `RemoveExceptionFilter`, `RemoveEventProcessor` and `RemoveTransactionProcessor` extension methods on `SentryOptions` (#2331) * added extension method to remove filters * Updated CHANGELOG.md * Add RemoveEventProcessor and RemoveTransactionProcessor * Refactoring others * Update CHANGELOG.md * Fix tests * Update API tests --------- Co-authored-by: Matt Johnson-Pint --- CHANGELOG.md | 1 + src/Sentry/Internal/Hub.cs | 13 +++--- src/Sentry/SentryOptionsExtensions.cs | 44 +++++++++++++++---- .../ApiApprovalTests.Run.Core3_1.verified.txt | 6 +++ ...piApprovalTests.Run.DotNet6_0.verified.txt | 6 +++ ...piApprovalTests.Run.DotNet7_0.verified.txt | 6 +++ .../ApiApprovalTests.Run.Net4_8.verified.txt | 6 +++ 7 files changed, 66 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fbbe9159dc..39988a12d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - Initial work to support profiling in a future release. ([#2206](https://github.com/getsentry/sentry-dotnet/pull/2206)) - Improve `WithScope` and add `WithScopeAsync` ([#2303](https://github.com/getsentry/sentry-dotnet/pull/2303)) ([#2309](https://github.com/getsentry/sentry-dotnet/pull/2309)) - Build .NET Standard 2.1 for Unity ([#2328](https://github.com/getsentry/sentry-dotnet/pull/2328)) +- Add `RemoveExceptionFilter`, `RemoveEventProcessor` and `RemoveTransactionProcessor` extension methods on `SentryOptions` ([#2331](https://github.com/getsentry/sentry-dotnet/pull/2331)) - Include Dynamic Sampling Context with error events, when there's a transaction ([#2332](https://github.com/getsentry/sentry-dotnet/pull/2332)) ### Fixes diff --git a/src/Sentry/Internal/Hub.cs b/src/Sentry/Internal/Hub.cs index e1f947a0e8..81b21c7cf5 100644 --- a/src/Sentry/Internal/Hub.cs +++ b/src/Sentry/Internal/Hub.cs @@ -1,5 +1,6 @@ using Sentry.Extensibility; using Sentry.Infrastructure; +using Sentry.Integrations; namespace Sentry.Internal; @@ -58,14 +59,12 @@ internal Hub( _enricher = new Enricher(options); - var integrations = options.Integrations; - if (integrations?.Count > 0) + // An integration _can_ deregister itself, so make a copy of the list before iterating. + var integrations = options.Integrations?.ToList() ?? Enumerable.Empty(); + foreach (var integration in integrations) { - foreach (var integration in integrations) - { - options.LogDebug("Registering integration: '{0}'.", integration.GetType().Name); - integration.Register(this, options); - } + options.LogDebug("Registering integration: '{0}'.", integration.GetType().Name); + integration.Register(this, options); } } diff --git a/src/Sentry/SentryOptionsExtensions.cs b/src/Sentry/SentryOptionsExtensions.cs index 93d21524b1..0876d285d6 100644 --- a/src/Sentry/SentryOptionsExtensions.cs +++ b/src/Sentry/SentryOptionsExtensions.cs @@ -30,8 +30,7 @@ public static class SentryOptionsExtensions /// /// The SentryOptions to remove the processor from. public static void DisableDuplicateEventDetection(this SentryOptions options) - => options.EventProcessors = - options.EventProcessors?.Where(p => p.GetType() != typeof(DuplicateEventDetectionEventProcessor)).ToList(); + => options.RemoveEventProcessor(); /// /// Disables the capture of errors through . @@ -46,8 +45,7 @@ public static void DisableAppDomainUnhandledExceptionCapture(this SentryOptions /// /// The SentryOptions to remove the integration from. public static void DisableDiagnosticSourceIntegration(this SentryOptions options) - => options.Integrations = - options.Integrations?.Where(p => p.GetType() != typeof(SentryDiagnosticListenerIntegration)).ToList(); + => options.RemoveIntegration(); #endif /// @@ -72,8 +70,7 @@ public static void DisableUnobservedTaskExceptionCapture(this SentryOptions opti /// The SentryOptions to remove the integration from. public static void DisableNetFxInstallationsIntegration(this SentryOptions options) { - options.EventProcessors = - options.EventProcessors?.Where(p => p.GetType() != typeof(NetFxInstallationsEventProcessor)).ToList(); + options.RemoveEventProcessor(); options.RemoveIntegration(); } #endif @@ -108,8 +105,9 @@ public static void AddIntegration(this SentryOptions options, ISdkIntegration in /// /// The type of the integration(s) to remove. /// The SentryOptions to remove the integration(s) from. - public static void RemoveIntegration(this SentryOptions options) where TIntegration : ISdkIntegration - => options.Integrations = options.Integrations?.Where(p => p.GetType() != typeof(TIntegration)).ToList(); + public static void RemoveIntegration(this SentryOptions options) + where TIntegration : ISdkIntegration + => options.Integrations?.RemoveAll(integration => integration is TIntegration); /// /// Add an exception filter. @@ -128,12 +126,22 @@ public static void AddExceptionFilter(this SentryOptions options, IExceptionFilt } } + /// + /// Removes all filters of type + /// + /// The type of filter(s) to remove. + /// The SentryOptions to remove the filter(s) from. + public static void RemoveExceptionFilter(this SentryOptions options) + where TFilter : IExceptionFilter + => options.ExceptionFilters?.RemoveAll(filter => filter is TFilter); + /// /// Ignore exception of type or derived. /// /// The type of the exception to ignore. /// The SentryOptions to store the exceptions type ignore. - public static void AddExceptionFilterForType(this SentryOptions options) where TException : Exception + public static void AddExceptionFilterForType(this SentryOptions options) + where TException : Exception => options.AddExceptionFilter(new ExceptionTypeFilter()); /// @@ -254,6 +262,15 @@ public static void AddEventProcessors(this SentryOptions options, IEnumerable + /// Removes all event processors of type + /// + /// The type of processor(s) to remove. + /// The SentryOptions to remove the processor(s) from. + public static void RemoveEventProcessor(this SentryOptions options) + where TProcessor : ISentryEventProcessor + => options.EventProcessors?.RemoveAll(processor => processor is TProcessor); + /// /// Adds an event processor provider which is invoked when creating a . /// @@ -305,6 +322,15 @@ public static void AddTransactionProcessors(this SentryOptions options, IEnumera } } + /// + /// Removes all transaction processors of type + /// + /// The type of processor(s) to remove. + /// The SentryOptions to remove the processor(s) from. + public static void RemoveTransactionProcessor(this SentryOptions options) + where TProcessor : ISentryTransactionProcessor + => options.TransactionProcessors?.RemoveAll(processor => processor is TProcessor); + /// /// Adds an transaction processor provider which is invoked when creating a . /// diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.Core3_1.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.Core3_1.verified.txt index 119f28a63c..755a9b9e68 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.Core3_1.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.Core3_1.verified.txt @@ -628,8 +628,14 @@ namespace Sentry public static System.Collections.Generic.IEnumerable GetAllEventProcessors(this Sentry.SentryOptions options) { } public static System.Collections.Generic.IEnumerable GetAllExceptionProcessors(this Sentry.SentryOptions options) { } public static System.Collections.Generic.IEnumerable GetAllTransactionProcessors(this Sentry.SentryOptions options) { } + public static void RemoveEventProcessor(this Sentry.SentryOptions options) + where TProcessor : Sentry.Extensibility.ISentryEventProcessor { } + public static void RemoveExceptionFilter(this Sentry.SentryOptions options) + where TFilter : Sentry.Extensibility.IExceptionFilter { } public static void RemoveIntegration(this Sentry.SentryOptions options) where TIntegration : Sentry.Integrations.ISdkIntegration { } + public static void RemoveTransactionProcessor(this Sentry.SentryOptions options) + where TProcessor : Sentry.Extensibility.ISentryTransactionProcessor { } public static Sentry.SentryOptions UseStackTraceFactory(this Sentry.SentryOptions options, Sentry.Extensibility.ISentryStackTraceFactory sentryStackTraceFactory) { } } public static class SentryScopeManagerExtensions diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt index 119f28a63c..755a9b9e68 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt @@ -628,8 +628,14 @@ namespace Sentry public static System.Collections.Generic.IEnumerable GetAllEventProcessors(this Sentry.SentryOptions options) { } public static System.Collections.Generic.IEnumerable GetAllExceptionProcessors(this Sentry.SentryOptions options) { } public static System.Collections.Generic.IEnumerable GetAllTransactionProcessors(this Sentry.SentryOptions options) { } + public static void RemoveEventProcessor(this Sentry.SentryOptions options) + where TProcessor : Sentry.Extensibility.ISentryEventProcessor { } + public static void RemoveExceptionFilter(this Sentry.SentryOptions options) + where TFilter : Sentry.Extensibility.IExceptionFilter { } public static void RemoveIntegration(this Sentry.SentryOptions options) where TIntegration : Sentry.Integrations.ISdkIntegration { } + public static void RemoveTransactionProcessor(this Sentry.SentryOptions options) + where TProcessor : Sentry.Extensibility.ISentryTransactionProcessor { } public static Sentry.SentryOptions UseStackTraceFactory(this Sentry.SentryOptions options, Sentry.Extensibility.ISentryStackTraceFactory sentryStackTraceFactory) { } } public static class SentryScopeManagerExtensions diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt index 119f28a63c..755a9b9e68 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt @@ -628,8 +628,14 @@ namespace Sentry public static System.Collections.Generic.IEnumerable GetAllEventProcessors(this Sentry.SentryOptions options) { } public static System.Collections.Generic.IEnumerable GetAllExceptionProcessors(this Sentry.SentryOptions options) { } public static System.Collections.Generic.IEnumerable GetAllTransactionProcessors(this Sentry.SentryOptions options) { } + public static void RemoveEventProcessor(this Sentry.SentryOptions options) + where TProcessor : Sentry.Extensibility.ISentryEventProcessor { } + public static void RemoveExceptionFilter(this Sentry.SentryOptions options) + where TFilter : Sentry.Extensibility.IExceptionFilter { } public static void RemoveIntegration(this Sentry.SentryOptions options) where TIntegration : Sentry.Integrations.ISdkIntegration { } + public static void RemoveTransactionProcessor(this Sentry.SentryOptions options) + where TProcessor : Sentry.Extensibility.ISentryTransactionProcessor { } public static Sentry.SentryOptions UseStackTraceFactory(this Sentry.SentryOptions options, Sentry.Extensibility.ISentryStackTraceFactory sentryStackTraceFactory) { } } public static class SentryScopeManagerExtensions diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt index f7b68971fd..baa06cc504 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt @@ -627,8 +627,14 @@ namespace Sentry public static System.Collections.Generic.IEnumerable GetAllEventProcessors(this Sentry.SentryOptions options) { } public static System.Collections.Generic.IEnumerable GetAllExceptionProcessors(this Sentry.SentryOptions options) { } public static System.Collections.Generic.IEnumerable GetAllTransactionProcessors(this Sentry.SentryOptions options) { } + public static void RemoveEventProcessor(this Sentry.SentryOptions options) + where TProcessor : Sentry.Extensibility.ISentryEventProcessor { } + public static void RemoveExceptionFilter(this Sentry.SentryOptions options) + where TFilter : Sentry.Extensibility.IExceptionFilter { } public static void RemoveIntegration(this Sentry.SentryOptions options) where TIntegration : Sentry.Integrations.ISdkIntegration { } + public static void RemoveTransactionProcessor(this Sentry.SentryOptions options) + where TProcessor : Sentry.Extensibility.ISentryTransactionProcessor { } public static Sentry.SentryOptions UseStackTraceFactory(this Sentry.SentryOptions options, Sentry.Extensibility.ISentryStackTraceFactory sentryStackTraceFactory) { } } public static class SentryScopeManagerExtensions From 8a1335a8318f3f050f817483c2768fd46dd3bc3d Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Wed, 26 Apr 2023 17:13:24 -0700 Subject: [PATCH 006/142] Update test and profiling dependencies (#2334) * Bump Verify * Bump polyfills * Bump NSubstitute * Bump IO testing helpers * Bump profiling dependencies --- src/Sentry.Profiling/Sentry.Profiling.csproj | 4 ++-- src/Sentry/Sentry.csproj | 2 +- test/Directory.Build.props | 8 ++++---- test/Sentry.Profiling.Tests/Sentry.Profiling.Tests.csproj | 3 --- 4 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/Sentry.Profiling/Sentry.Profiling.csproj b/src/Sentry.Profiling/Sentry.Profiling.csproj index 1691102c5f..f85e4abd9d 100644 --- a/src/Sentry.Profiling/Sentry.Profiling.csproj +++ b/src/Sentry.Profiling/Sentry.Profiling.csproj @@ -11,8 +11,8 @@ - - + + diff --git a/src/Sentry/Sentry.csproj b/src/Sentry/Sentry.csproj index 4d1f662fff..37291b8f99 100644 --- a/src/Sentry/Sentry.csproj +++ b/src/Sentry/Sentry.csproj @@ -50,7 +50,7 @@ https://github.com/SimonCropp/Polyfill --> - + - - + + diff --git a/test/Sentry.Profiling.Tests/Sentry.Profiling.Tests.csproj b/test/Sentry.Profiling.Tests/Sentry.Profiling.Tests.csproj index f57e1869e1..f36a1ca1c1 100644 --- a/test/Sentry.Profiling.Tests/Sentry.Profiling.Tests.csproj +++ b/test/Sentry.Profiling.Tests/Sentry.Profiling.Tests.csproj @@ -7,9 +7,6 @@ - - - From 93c5fa8900b29a6a037ff3b285546b64b173f9f2 Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Wed, 26 Apr 2023 17:13:34 -0700 Subject: [PATCH 007/142] Remove session breadcrumbs (#2333) * Remove session breadcrumbs * Improve log messages * Minor cleanup * Update test * Update CHANGELOG.md --- CHANGELOG.md | 1 + src/Sentry/GlobalSessionManager.cs | 116 +++++++----------- .../Sentry.Tests/GlobalSessionManagerTests.cs | 2 +- 3 files changed, 47 insertions(+), 72 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 39988a12d3..f274dce6d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ - Buffer payloads asynchronously when appropriate ([#2297](https://github.com/getsentry/sentry-dotnet/pull/2297)) - Restore `System.Reflection.Metadata` dependency for .NET Core 3 ([#2302](https://github.com/getsentry/sentry-dotnet/pull/2302)) - Capture open transactions on disabled hubs ([#2319](https://github.com/getsentry/sentry-dotnet/pull/2319)) +- Remove session breadcrumbs ([#2333](https://github.com/getsentry/sentry-dotnet/pull/2333)) ### Dependencies diff --git a/src/Sentry/GlobalSessionManager.cs b/src/Sentry/GlobalSessionManager.cs index 77504a44e4..2a42f2b62d 100644 --- a/src/Sentry/GlobalSessionManager.cs +++ b/src/Sentry/GlobalSessionManager.cs @@ -55,8 +55,7 @@ public GlobalSessionManager( Directory.CreateDirectory(directoryPath); - _options.LogDebug("Created directory for installation ID file ({0}).", - directoryPath); + _options.LogDebug("Created directory for installation ID file ({0}).", directoryPath); var filePath = Path.Combine(directoryPath, ".installation"); @@ -67,24 +66,19 @@ public GlobalSessionManager( } catch (FileNotFoundException) { - _options.LogDebug("File containing installation ID does not exist ({0}).", - filePath); + _options.LogDebug("File containing installation ID does not exist ({0}).", filePath); } catch (DirectoryNotFoundException) { // on PS4 we're seeing CreateDirectory work but ReadAllText throw DirectoryNotFoundException - _options.LogDebug( - "Directory containing installation ID does not exist ({0}).", - filePath); + _options.LogDebug("Directory containing installation ID does not exist ({0}).", filePath); } // Generate new installation ID and store it in a file var id = Guid.NewGuid().ToString(); File.WriteAllText(filePath, id); - _options.LogDebug("Saved installation ID '{0}' to file '{1}'.", - id, filePath); - + _options.LogDebug("Saved installation ID '{0}' to file '{1}'.", id, filePath); return id; } // If there's no write permission or the platform doesn't support this, we handle @@ -92,7 +86,6 @@ public GlobalSessionManager( catch (Exception ex) { _options.LogError("Failed to resolve persistent installation ID.", ex); - return null; } } @@ -112,9 +105,7 @@ public GlobalSessionManager( if (string.IsNullOrWhiteSpace(installationId)) { - _options.LogError( - "Failed to find an appropriate network interface for installation ID."); - + _options.LogError("Failed to find an appropriate network interface for installation ID."); return null; } @@ -123,7 +114,6 @@ public GlobalSessionManager( catch (Exception ex) { _options.LogError("Failed to resolve hardware installation ID.", ex); - return null; } } @@ -155,7 +145,7 @@ internal static string GetMachineNameInstallationId() => var id = TryGetPersistentInstallationId() ?? TryGetHardwareInstallationId() ?? - GlobalSessionManager.GetMachineNameInstallationId(); + GetMachineNameInstallationId(); if (!string.IsNullOrWhiteSpace(id)) { @@ -186,9 +176,7 @@ private void PersistSession(SessionUpdate update, DateTimeOffset? pauseTimestamp { Directory.CreateDirectory(_persistenceDirectoryPath); - _options.LogDebug( - "Created persistence directory for session file '{0}'.", - _persistenceDirectoryPath); + _options.LogDebug("Created persistence directory for session file '{0}'.", _persistenceDirectoryPath); var filePath = Path.Combine(_persistenceDirectoryPath, PersistedSessionFileName); @@ -219,15 +207,12 @@ private void DeletePersistedSession() { try { - _options.LogDebug("Deleting persisted session file with contents: {0}", - File.ReadAllText(filePath)); + var contents = File.ReadAllText(filePath); + _options.LogDebug("Deleting persisted session file with contents: {0}", contents); } catch (Exception ex) { - _options.LogError( - "Failed to read the contents of persisted session file '{0}'.", - ex, - filePath); + _options.LogError("Failed to read the contents of persisted session file '{0}'.", ex, filePath); } } @@ -237,10 +222,7 @@ private void DeletePersistedSession() } catch (Exception ex) { - _options.LogError( - "Failed to delete persisted session from the file system: '{0}'", - ex, - filePath); + _options.LogError("Failed to delete persisted session from the file system: '{0}'", ex, filePath); } } @@ -316,8 +298,7 @@ private void DeletePersistedSession() if (string.IsNullOrWhiteSpace(release)) { // Release health without release is just health (useless) - _options.LogError( - "Failed to start a session because there is no release information."); + _options.LogError("Failed to start a session because there is no release information."); return null; } @@ -333,16 +314,13 @@ private void DeletePersistedSession() var previousSession = Interlocked.Exchange(ref _currentSession, session); if (previousSession is not null) { - _options.LogWarning( - "Starting a new session while an existing one is still active."); + _options.LogWarning("Starting a new session while an existing one is still active."); // End previous session EndSession(previousSession, _clock.GetUtcNow(), SessionEndStatus.Exited); } - AddSessionBreadcrumb("Starting Sentry Session"); - _options.LogInfo("Started new session (SID: {0}; DID: {1}).", - session.Id, session.DistinctId); + _options.LogInfo("Started new session (SID: {0}; DID: {1}).", session.Id, session.DistinctId); var update = session.CreateUpdate(true, _clock.GetUtcNow()); @@ -360,7 +338,6 @@ private SessionUpdate EndSession(Session session, DateTimeOffset timestamp, Sess session.ReportError(); } - AddSessionBreadcrumb("Ending Sentry Session"); _options.LogInfo("Ended session (SID: {0}; DID: {1}) with status '{2}'.", session.Id, session.DistinctId, status); @@ -376,8 +353,7 @@ private SessionUpdate EndSession(Session session, DateTimeOffset timestamp, Sess var session = Interlocked.Exchange(ref _currentSession, null); if (session is null) { - _options.LogDebug("Failed to end session because there is none active."); - + _options.LogWarning("Failed to end session because there is none active."); return null; } @@ -388,28 +364,35 @@ private SessionUpdate EndSession(Session session, DateTimeOffset timestamp, Sess public void PauseSession() { - if (_currentSession is { } session) + if (_currentSession is not { } session) { - AddSessionBreadcrumb("Pausing Sentry Session"); - - var now = _clock.GetUtcNow(); - _lastPauseTimestamp = now; - PersistSession(session.CreateUpdate(false, now), now); + _options.LogWarning("Attempted to pause a session, but a session has not been started."); + return; } + + _options.LogInfo("Pausing session (SID: {0}; DID: {1}).", session.Id, session.DistinctId); + + var now = _clock.GetUtcNow(); + _lastPauseTimestamp = now; + PersistSession(session.CreateUpdate(false, now), now); } public IReadOnlyList ResumeSession() { + if (_currentSession is not { } session) + { + _options.LogWarning("Attempted to resume a session, but a session has not been started."); + return Array.Empty(); + } + // Ensure a session has been paused before if (_lastPauseTimestamp is not { } sessionPauseTimestamp) { - _options.LogDebug( - "Attempted to resume a session, but the current session hasn't been paused."); - + _options.LogWarning("Attempted to resume a session, but the current session hasn't been paused."); return Array.Empty(); } - AddSessionBreadcrumb("Resuming Sentry Session"); + _options.LogInfo("Resuming session (SID: {0}; DID: {1}).", session.Id, session.DistinctId); // Reset the pause timestamp since the session is about to be resumed _lastPauseTimestamp = null; @@ -441,38 +424,29 @@ public IReadOnlyList ResumeSession() return updates; } - _options.LogDebug( - "Paused session has been paused for {0}, which is shorter than the configured timeout.", - pauseDuration); + _options.LogInfo("Resumed session (SID: {0}; DID: {1}) after being paused for {2}.", + session.Id, session.DistinctId, pauseDuration); return Array.Empty(); } public SessionUpdate? ReportError() { - if (_currentSession is { } session) + if (_currentSession is not { } session) { - session.ReportError(); + _options.LogDebug("Failed to report an error on a session because there is none active."); + return null; + } - // If we already have at least one error reported, the session update is pointless, - // so don't return anything. - if (session.ErrorCount > 1) - { - _options.LogDebug( - "Reported an error on a session that already contains errors. Not creating an update."); + session.ReportError(); - return null; - } - - return session.CreateUpdate(false, _clock.GetUtcNow()); + // If we already have at least one error reported, the session update is pointless, so don't return anything. + if (session.ErrorCount > 1) + { + _options.LogDebug("Reported an error on a session that already contains errors. Not creating an update."); + return null; } - _options.LogDebug( - "Failed to report an error on a session because there is none active."); - - return null; + return session.CreateUpdate(false, _clock.GetUtcNow()); } - - private static void AddSessionBreadcrumb(string message) - => SentrySdk.AddBreadcrumb(message, "app.lifecycle", "session"); } diff --git a/test/Sentry.Tests/GlobalSessionManagerTests.cs b/test/Sentry.Tests/GlobalSessionManagerTests.cs index 0851b116c2..617f37bfaf 100644 --- a/test/Sentry.Tests/GlobalSessionManagerTests.cs +++ b/test/Sentry.Tests/GlobalSessionManagerTests.cs @@ -213,7 +213,7 @@ public void EndSession_ActiveSessionDoesNotExist_DoesNothing() _fixture.Logger.Entries.Should().Contain(e => e.Message == "Failed to end session because there is none active." && - e.Level == SentryLevel.Debug); + e.Level == SentryLevel.Warning); } [Fact] From adf95d018b3d700d97e0666dbdad30509803e5e7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 27 Apr 2023 21:03:31 -0700 Subject: [PATCH 008/142] chore: update scripts/update-java.ps1 to 6.18.0 (#2338) 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 f274dce6d2..a38cb809c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,9 @@ - Bump Cocoa SDK from v8.4.0 to v8.5.0 ([#2310](https://github.com/getsentry/sentry-dotnet/pull/2310)) - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#850) - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.4.0...8.5.0) +- Bump Java SDK from v6.17.0 to v6.18.0 ([#2338](https://github.com/getsentry/sentry-dotnet/pull/2338)) + - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#6180) + - [diff](https://github.com/getsentry/sentry-java/compare/6.17.0...6.18.0) ## 3.30.0 diff --git a/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj b/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj index a60384b4b4..9ce754e523 100644 --- a/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj +++ b/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj @@ -3,7 +3,7 @@ net6.0-android $(NoWarn);BG8605;BG8606 - 6.17.0 + 6.18.0 $(BaseIntermediateOutputPath)sdks\Sentry\Android\$(SentryAndroidSdkVersion)\ .NET Bindings for the Sentry Android SDK From 4100c6ec0d6edb7cbae7cdcf6e18c8aafd5a98c4 Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Fri, 28 Apr 2023 10:38:49 -0600 Subject: [PATCH 009/142] Fix ASP.NET Core issue with missing context when using capture methods that configure scope (#2339) --- CHANGELOG.md | 1 + .../SentryGrpcInterceptor.cs | 8 ++-- src/Sentry.AspNetCore/SentryMiddleware.cs | 14 ++++--- src/Sentry/Scope.cs | 27 +++++++------ .../SentryMiddlewareTests.cs | 39 ++++++++++++++----- test/Sentry.Tests/SentryClientTests.cs | 5 ++- 6 files changed, 61 insertions(+), 33 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a38cb809c9..81fab183d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ - Restore `System.Reflection.Metadata` dependency for .NET Core 3 ([#2302](https://github.com/getsentry/sentry-dotnet/pull/2302)) - Capture open transactions on disabled hubs ([#2319](https://github.com/getsentry/sentry-dotnet/pull/2319)) - Remove session breadcrumbs ([#2333](https://github.com/getsentry/sentry-dotnet/pull/2333)) +- Fix ASP.NET Core issue with missing context when using capture methods that configure scope ([#2339](https://github.com/getsentry/sentry-dotnet/pull/2339)) ### Dependencies diff --git a/src/Sentry.AspNetCore.Grpc/SentryGrpcInterceptor.cs b/src/Sentry.AspNetCore.Grpc/SentryGrpcInterceptor.cs index 6123b8146c..dd26151d99 100644 --- a/src/Sentry.AspNetCore.Grpc/SentryGrpcInterceptor.cs +++ b/src/Sentry.AspNetCore.Grpc/SentryGrpcInterceptor.cs @@ -59,7 +59,7 @@ public override async Task UnaryServerHandler( { hub.ConfigureScope(scope => { - scope.OnEvaluating += (_, _) => scope.Populate(context, request, _options); + scope.OnEvaluating += (_, activeScope) => activeScope.Populate(context, request, _options); }); try @@ -102,7 +102,7 @@ public override async Task ServerStreamingServerHandler(TRe { hub.ConfigureScope(scope => { - scope.OnEvaluating += (_, _) => scope.Populate(context, request, _options); + scope.OnEvaluating += (_, activeScope) => activeScope.Populate(context, request, _options); }); try @@ -140,7 +140,7 @@ public override async Task ClientStreamingServerHandler { - scope.OnEvaluating += (_, _) => scope.Populate(context, null, _options); + scope.OnEvaluating += (_, activeScope) => activeScope.Populate(context, null, _options); }); try @@ -183,7 +183,7 @@ public override async Task DuplexStreamingServerHandler( { hub.ConfigureScope(scope => { - scope.OnEvaluating += (_, _) => scope.Populate(context, null, _options); + scope.OnEvaluating += (_, activeScope) => activeScope.Populate(context, null, _options); }); try diff --git a/src/Sentry.AspNetCore/SentryMiddleware.cs b/src/Sentry.AspNetCore/SentryMiddleware.cs index 577f25abab..f7dcf0ee10 100644 --- a/src/Sentry.AspNetCore/SentryMiddleware.cs +++ b/src/Sentry.AspNetCore/SentryMiddleware.cs @@ -8,7 +8,6 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Sentry.Extensibility; -using Sentry.Protocol; using Sentry.Reflection; namespace Sentry.AspNetCore; @@ -99,10 +98,13 @@ public async Task InvokeAsync(HttpContext context, RequestDelegate next) // sent to Sentry. This avoid the cost on error-free requests. // In case of event, all data made available through the HTTP Context at the time of the // event creation will be sent to Sentry - scope.OnEvaluating += (_, _) => + + // Important: The scope that the event is attached to is not necessarily the same one that is active + // when the event fires. Use `activeScope`, not `scope` or `hub`. + scope.OnEvaluating += (_, activeScope) => { - SyncOptionsScope(hub); - PopulateScope(context, scope); + SyncOptionsScope(activeScope); + PopulateScope(context, activeScope); }; }); @@ -167,11 +169,11 @@ void CaptureException(Exception e, SentryId evtId, string mechanism, string desc } } - private void SyncOptionsScope(IHub newHub) + private void SyncOptionsScope(Scope scope) { foreach (var callback in _options.ConfigureScopeCallbacks) { - newHub.ConfigureScope(callback); + callback.Invoke(scope); } } diff --git a/src/Sentry/Scope.cs b/src/Sentry/Scope.cs index 1d2dfffd6d..bd4db02382 100644 --- a/src/Sentry/Scope.cs +++ b/src/Sentry/Scope.cs @@ -81,7 +81,7 @@ internal SentryId LastEventId /// but execution at a later time, when more data is available. /// /// - internal event EventHandler? OnEvaluating; + internal event EventHandler? OnEvaluating; /// public SentryLevel? Level { get; set; } @@ -119,7 +119,10 @@ public Contexts Contexts /// public User User { - get => _user ??= new User { PropertyChanged = UserChanged }; + get => _user ??= new User + { + PropertyChanged = UserChanged + }; set { _user = value; @@ -127,6 +130,7 @@ public User User { _user.PropertyChanged = UserChanged; } + UserChanged.Invoke(_user); } } @@ -196,7 +200,7 @@ public ITransaction? Transaction public IReadOnlyList Fingerprint { get; set; } = Array.Empty(); #if NETSTANDARD2_0 || NETFRAMEWORK - private ConcurrentQueue _breadcrumbs = new(); + private ConcurrentQueue _breadcrumbs = new(); #else private readonly ConcurrentQueue _breadcrumbs = new(); #endif @@ -215,10 +219,9 @@ public ITransaction? Transaction public IReadOnlyDictionary Tags => _tags; #if NETSTANDARD2_0 || NETFRAMEWORK - private ConcurrentBag _attachments = new(); + private ConcurrentBag _attachments = new(); #else private readonly ConcurrentBag _attachments = new(); - #endif /// @@ -310,7 +313,7 @@ public void UnsetTag(string key) public void AddAttachment(Attachment attachment) => _attachments.Add(attachment); /// - /// Resets all the properties and collections within the scope to their default values. + /// Resets all the properties and collections within the scope to their default values. /// public void Clear() { @@ -337,7 +340,7 @@ public void Clear() public void ClearAttachments() { #if NETSTANDARD2_0 || NETFRAMEWORK - Interlocked.Exchange(ref _attachments, new()); + Interlocked.Exchange(ref _attachments, new()); #else _attachments.Clear(); #endif @@ -353,7 +356,7 @@ public void ClearBreadcrumbs() Interlocked.Exchange(ref _breadcrumbs, new()); #else _breadcrumbs.Clear(); -#endif +#endif } @@ -461,6 +464,8 @@ public void Apply(Scope other) public Scope Clone() { var clone = new Scope(Options); + clone.OnEvaluating = OnEvaluating; + Apply(clone); foreach (var processor in EventProcessors) @@ -497,13 +502,11 @@ internal void Evaluate() try { - OnEvaluating?.Invoke(this, EventArgs.Empty); + OnEvaluating?.Invoke(this, this); } catch (Exception ex) { - Options.DiagnosticLogger?.LogError( - "Failed invoking event handler.", - ex); + Options.DiagnosticLogger?.LogError("Failed invoking event handler.", ex); } finally { diff --git a/test/Sentry.AspNetCore.Tests/SentryMiddlewareTests.cs b/test/Sentry.AspNetCore.Tests/SentryMiddlewareTests.cs index 93d26365d7..60c12d5833 100644 --- a/test/Sentry.AspNetCore.Tests/SentryMiddlewareTests.cs +++ b/test/Sentry.AspNetCore.Tests/SentryMiddlewareTests.cs @@ -194,6 +194,26 @@ public async Task InvokeAsync_OnEvaluating_HttpContextDataSet() Assert.Equal(expectedTraceIdentifier, scope.Tags[nameof(HttpContext.TraceIdentifier)]); } + [Fact] + public async Task InvokeAsync_OnEvaluatingClonedScope_HttpContextDataSet() + { + const string expectedTraceIdentifier = "trace id"; + _ = _fixture.HttpContext.TraceIdentifier.Returns(expectedTraceIdentifier); + var scope = new Scope(); + _fixture.Hub.When(h => h.ConfigureScope(Arg.Any>())) + .Do(c => c.Arg>()(scope)); + + var sut = _fixture.GetSut(); + + await sut.InvokeAsync(_fixture.HttpContext, _fixture.RequestDelegate); + + var clonedScope = scope.Clone(); + + clonedScope.Evaluate(); + + Assert.Equal(expectedTraceIdentifier, clonedScope.Tags[nameof(HttpContext.TraceIdentifier)]); + } + [Fact] public async Task InvokeAsync_ScopePushedAndPopped_OnHappyPath() { @@ -522,8 +542,8 @@ public async Task InvokeAsync_FlushBeforeRequestCompletedTrue_RespectsTimeout() public async Task InvokeAsync_ScopeNotPopulated_CopyOptionsToScope() { // Arrange - var expectedAction = new Action(scope => scope.SetTag("A", "B")); - _fixture.Options.ConfigureScope(expectedAction); + bool configured = false; + _fixture.Options.ConfigureScope(_ => configured = true); var expectedExceptionMessage = "Expected Exception"; _fixture.RequestDelegate = _ => throw new Exception(expectedExceptionMessage); var sut = _fixture.GetSut(); @@ -537,17 +557,17 @@ public async Task InvokeAsync_ScopeNotPopulated_CopyOptionsToScope() { } // Assert - _fixture.Hub.Received(1).ConfigureScope(Arg.Is(expectedAction)); + Assert.True(configured); } [Fact] public async Task InvokeAsync_SameMiddleWareWithSameHubs_CopyOptionsOnce() { // Arrange - var expectedAction = new Action(scope => scope.SetTag("A", "B")); + bool configured = false; var expectedExceptionMessage = "Expected Exception"; _fixture.RequestDelegate = _ => throw new Exception(expectedExceptionMessage); - _fixture.Options.ConfigureScope(expectedAction); + _fixture.Options.ConfigureScope(_ => configured = true); var sut = _fixture.GetSut(); // Act @@ -566,18 +586,18 @@ public async Task InvokeAsync_SameMiddleWareWithSameHubs_CopyOptionsOnce() { } // Assert - _fixture.Hub.Received(1).ConfigureScope(Arg.Is(expectedAction)); + Assert.True(configured); } [Fact] public async Task InvokeAsync_SameMiddleWareWithDifferentHubs_CopyOptionsToAllHubs() { // Arrange + int count = 0; var firstHub = _fixture.Hub; var expectedExceptionMessage = "Expected Exception"; _fixture.RequestDelegate = _ => throw new Exception(expectedExceptionMessage); - var expectedAction = new Action(scope => scope.SetTag("A", "B")); - _fixture.Options.ConfigureScope(expectedAction); + _fixture.Options.ConfigureScope(_=> count++); var sut = _fixture.GetSut(); // Act @@ -604,8 +624,7 @@ public async Task InvokeAsync_SameMiddleWareWithDifferentHubs_CopyOptionsToAllHu } // Assert - firstHub.Received(1).ConfigureScope(Arg.Is(expectedAction)); - secondHub.Received(1).ConfigureScope(Arg.Is(expectedAction)); + Assert.Equal(2, count); } [Fact] diff --git a/test/Sentry.Tests/SentryClientTests.cs b/test/Sentry.Tests/SentryClientTests.cs index 588c1b8683..dd71edb322 100644 --- a/test/Sentry.Tests/SentryClientTests.cs +++ b/test/Sentry.Tests/SentryClientTests.cs @@ -217,9 +217,11 @@ public void CaptureEvent_EventAndScope_EvaluatesScope() var evaluated = false; object actualSender = null; - scope.OnEvaluating += (sender, _) => + object actualScope = null; + scope.OnEvaluating += (sender, activeScope) => { actualSender = sender; + actualScope = activeScope; evaluated = true; }; @@ -227,6 +229,7 @@ public void CaptureEvent_EventAndScope_EvaluatesScope() Assert.True(evaluated); Assert.Same(scope, actualSender); + Assert.Same(scope, actualScope); } [Fact] From 992357827851589be24cc9e451d9619b779411a5 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos <6349682+vaind@users.noreply.github.com> Date: Fri, 28 Apr 2023 21:43:40 +0200 Subject: [PATCH 010/142] chore: Profiling benchmarks (#2326) --- ...marks.ProfilingBenchmarks-report-github.md | 54 +++++ .../Sentry.Benchmarks/ProfilingBenchmarks.cs | 192 ++++++++++++++++++ .../Sentry.Benchmarks.csproj | 2 + .../Properties/AssemblyInfo.cs | 1 + src/Sentry.Profiling/SampleProfilerSession.cs | 42 ++-- .../SamplingTransactionProfiler.cs | 5 +- 6 files changed, 281 insertions(+), 15 deletions(-) create mode 100644 benchmarks/Sentry.Benchmarks/BenchmarkDotNet.Artifacts/results/Sentry.Benchmarks.ProfilingBenchmarks-report-github.md create mode 100644 benchmarks/Sentry.Benchmarks/ProfilingBenchmarks.cs diff --git a/benchmarks/Sentry.Benchmarks/BenchmarkDotNet.Artifacts/results/Sentry.Benchmarks.ProfilingBenchmarks-report-github.md b/benchmarks/Sentry.Benchmarks/BenchmarkDotNet.Artifacts/results/Sentry.Benchmarks.ProfilingBenchmarks-report-github.md new file mode 100644 index 0000000000..723000f439 --- /dev/null +++ b/benchmarks/Sentry.Benchmarks/BenchmarkDotNet.Artifacts/results/Sentry.Benchmarks.ProfilingBenchmarks-report-github.md @@ -0,0 +1,54 @@ +``` ini + +BenchmarkDotNet=v0.13.5, OS=Windows 11 (10.0.22621.1555/22H2/2022Update/SunValley2) +12th Gen Intel Core i7-12700K, 1 CPU, 20 logical and 12 physical cores +.NET SDK=7.0.203 + [Host] : .NET 6.0.16 (6.0.1623.17311), X64 RyuJIT AVX2 + Job-HRXEOC : .NET 6.0.16 (6.0.1623.17311), X64 RyuJIT AVX2 + +Runtime=.NET 6.0 + +``` +| Method | runtimeMs | processing | rundown | provider | n | Mean | Error | StdDev | Median | Ratio | RatioSD | Gen0 | Gen1 | Gen2 | Allocated | Alloc Ratio | +|----------------------------------------- |---------- |----------- |-------- |--------- |------- |--------------:|-----------:|-----------:|--------------:|------:|--------:|----------:|----------:|---------:|-----------:|------------:| +| **DiagnosticsSessionStartStop** | **?** | **?** | **?** | **?** | **?** | **11.002 ms** | **0.2181 ms** | **0.2142 ms** | **11.002 ms** | **?** | **?** | **-** | **-** | **-** | **9587 B** | **?** | +| SampleProfilerSessionStartStopFinishWait | ? | ? | ? | ? | ? | 118.284 ms | 4.0881 ms | 12.0539 ms | 123.832 ms | ? | ? | - | - | - | 3508694 B | ? | +| SampleProfilerSessionStartStop | ? | ? | ? | ? | ? | 115.846 ms | 4.5379 ms | 13.3802 ms | 121.593 ms | ? | ? | - | - | - | 3501074 B | ? | +| | | | | | | | | | | | | | | | | | +| **Transaction** | **25** | **False** | **?** | **?** | **?** | **121.313 ms** | **1.6881 ms** | **1.5790 ms** | **120.924 ms** | **?** | **?** | **-** | **-** | **-** | **3826061 B** | **?** | +| | | | | | | | | | | | | | | | | | +| **Transaction** | **25** | **True** | **?** | **?** | **?** | **148.345 ms** | **2.2705 ms** | **2.1238 ms** | **148.186 ms** | **?** | **?** | **750.0000** | **250.0000** | **-** | **31036228 B** | **?** | +| | | | | | | | | | | | | | | | | | +| **Transaction** | **100** | **False** | **?** | **?** | **?** | **219.679 ms** | **2.8684 ms** | **2.5428 ms** | **218.968 ms** | **?** | **?** | **-** | **-** | **-** | **4447075 B** | **?** | +| | | | | | | | | | | | | | | | | | +| **Transaction** | **100** | **True** | **?** | **?** | **?** | **244.955 ms** | **2.0558 ms** | **1.7167 ms** | **245.007 ms** | **?** | **?** | **500.0000** | **-** | **-** | **25737928 B** | **?** | +| | | | | | | | | | | | | | | | | | +| **Transaction** | **1000** | **False** | **?** | **?** | **?** | **1,035.830 ms** | **3.3787 ms** | **2.9952 ms** | **1,036.068 ms** | **?** | **?** | **-** | **-** | **-** | **4043360 B** | **?** | +| | | | | | | | | | | | | | | | | | +| **Transaction** | **1000** | **True** | **?** | **?** | **?** | **1,071.220 ms** | **3.8655 ms** | **3.0180 ms** | **1,070.841 ms** | **?** | **?** | **1000.0000** | **-** | **-** | **35168504 B** | **?** | +| | | | | | | | | | | | | | | | | | +| **Transaction** | **10000** | **False** | **?** | **?** | **?** | **10,171.186 ms** | **26.7803 ms** | **25.0503 ms** | **10,180.066 ms** | **?** | **?** | **-** | **-** | **-** | **10177136 B** | **?** | +| | | | | | | | | | | | | | | | | | +| **Transaction** | **10000** | **True** | **?** | **?** | **?** | **10,200.367 ms** | **9.7616 ms** | **9.1310 ms** | **10,199.573 ms** | **?** | **?** | **3000.0000** | **1000.0000** | **-** | **71216048 B** | **?** | +| | | | | | | | | | | | | | | | | | +| **DiagnosticsSessionStartCopyStop** | **?** | **?** | **False** | **all** | **?** | **110.255 ms** | **2.1973 ms** | **2.2565 ms** | **109.539 ms** | **?** | **?** | **-** | **-** | **-** | **35720 B** | **?** | +| | | | | | | | | | | | | | | | | | +| **DiagnosticsSessionStartCopyStop** | **?** | **?** | **False** | **runtime** | **?** | **9.025 ms** | **1.2790 ms** | **3.6491 ms** | **8.711 ms** | **?** | **?** | **-** | **-** | **-** | **14612 B** | **?** | +| | | | | | | | | | | | | | | | | | +| **DiagnosticsSessionStartCopyStop** | **?** | **?** | **False** | **sample** | **?** | **105.218 ms** | **3.3185 ms** | **9.7846 ms** | **109.640 ms** | **?** | **?** | **-** | **-** | **-** | **19185 B** | **?** | +| | | | | | | | | | | | | | | | | | +| **DiagnosticsSessionStartCopyStop** | **?** | **?** | **False** | **tpl** | **?** | **9.023 ms** | **1.4060 ms** | **4.0790 ms** | **10.077 ms** | **?** | **?** | **-** | **-** | **-** | **26514 B** | **?** | +| | | | | | | | | | | | | | | | | | +| **DiagnosticsSessionStartCopyStop** | **?** | **?** | **True** | **all** | **?** | **120.379 ms** | **3.1807 ms** | **9.3783 ms** | **123.728 ms** | **?** | **?** | **-** | **-** | **-** | **3492258 B** | **?** | +| | | | | | | | | | | | | | | | | | +| **DiagnosticsSessionStartCopyStop** | **?** | **?** | **True** | **runtime** | **?** | **12.597 ms** | **0.4213 ms** | **1.2021 ms** | **12.038 ms** | **?** | **?** | **500.0000** | **500.0000** | **500.0000** | **3266302 B** | **?** | +| | | | | | | | | | | | | | | | | | +| **DiagnosticsSessionStartCopyStop** | **?** | **?** | **True** | **sample** | **?** | **121.085 ms** | **2.4076 ms** | **6.9463 ms** | **122.342 ms** | **?** | **?** | **200.0000** | **200.0000** | **200.0000** | **3275838 B** | **?** | +| | | | | | | | | | | | | | | | | | +| **DiagnosticsSessionStartCopyStop** | **?** | **?** | **True** | **tpl** | **?** | **26.265 ms** | **4.1051 ms** | **11.8442 ms** | **23.362 ms** | **?** | **?** | **181.8182** | **181.8182** | **181.8182** | **3394838 B** | **?** | +| | | | | | | | | | | | | | | | | | +| **DoHardWork** | **?** | **?** | **?** | **?** | **10000** | **6.030 ms** | **0.0293 ms** | **0.0259 ms** | **6.031 ms** | **1.00** | **0.00** | **-** | **-** | **-** | **4 B** | **1.00** | +| DoHardWorkWhileProfiling | ? | ? | ? | ? | 10000 | 6.284 ms | 0.0201 ms | 0.0188 ms | 6.284 ms | 1.04 | 0.00 | - | - | - | 107 B | 26.75 | +| | | | | | | | | | | | | | | | | | +| **DoHardWork** | **?** | **?** | **?** | **?** | **100000** | **199.135 ms** | **1.4575 ms** | **1.3633 ms** | **199.928 ms** | **1.00** | **0.00** | **-** | **-** | **-** | **1739 B** | **1.00** | +| DoHardWorkWhileProfiling | ? | ? | ? | ? | 100000 | 206.346 ms | 0.3495 ms | 0.3269 ms | 206.320 ms | 1.04 | 0.01 | - | - | - | 2685 B | 1.54 | diff --git a/benchmarks/Sentry.Benchmarks/ProfilingBenchmarks.cs b/benchmarks/Sentry.Benchmarks/ProfilingBenchmarks.cs new file mode 100644 index 0000000000..60ae5b22d7 --- /dev/null +++ b/benchmarks/Sentry.Benchmarks/ProfilingBenchmarks.cs @@ -0,0 +1,192 @@ +using System.Diagnostics.Tracing; +using BenchmarkDotNet.Attributes; +using Microsoft.Diagnostics.NETCore.Client; +using Microsoft.Diagnostics.Tracing.Parsers; +using NSubstitute; +using Sentry.Internal; +using Sentry.Profiling; + +namespace Sentry.Benchmarks; + +public class ProfilingBenchmarks +{ + private IHub _hub = Substitute.For(); + private ITransactionProfilerFactory _factory = new SamplingTransactionProfilerFactory(Path.GetTempPath(), new()); + + #region full transaction profiling + public IEnumerable ProfilerArguments() + { + foreach (var runtimeMs in new[] { 25, 100, 1000, 10000 }) + { + foreach (var processing in new[] { true, false }) + { + yield return new object[] { runtimeMs, processing }; + } + } + } + + // Run a profiled transaction. Profiler starts and stops for each transaction separately. + [Benchmark] + [ArgumentsSource(nameof(ProfilerArguments))] + public long Transaction(int runtimeMs, bool processing) + { + var tt = new TransactionTracer(_hub, "test", ""); + tt.TransactionProfiler = _factory.Start(tt, CancellationToken.None); + var result = RunForMs(runtimeMs); + tt.TransactionProfiler?.Finish(); + var transaction = new Transaction(tt); + if (processing) + { + var collectTask = tt.TransactionProfiler.CollectAsync(transaction); + collectTask.Wait(); + } + return result; + } + #endregion + + #region utilities + + private long RunForMs(int milliseconds) + { + var clock = Stopwatch.StartNew(); + long result = 0; + while (clock.ElapsedMilliseconds < milliseconds) + { + // Rather arbitrary numnbers here, just to get the profiler to capture something. + result += FindPrimeNumber(milliseconds); + Thread.Sleep(milliseconds / 10); + } + return result; + } + + 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); + } + #endregion + + #region Profiling session, DiagnosticsClient, etc. + // Disabled because it skews the result table because it's in nanoseconds so everything else is printed as ns. + // [Benchmark] + public DiagnosticsClient DiagnosticsClientNew() + { + return new DiagnosticsClient(Process.GetCurrentProcess().Id); + } + + [Benchmark] + public void DiagnosticsSessionStartStop() + { + var session = DiagnosticsClientNew().StartEventPipeSession( + SampleProfilerSession.Providers, + SampleProfilerSession.RequestRundown, + SampleProfilerSession.CircularBufferMB + ); + session.EventStream.Dispose(); + session.Dispose(); + } + + public IEnumerable SessionArguments() + { + foreach (var rundown in new[] { true, false }) + { + // Note (ID): different buffer size doesn't make any difference in performance. + foreach (var provider in new[] { "runtime", "sample", "tpl", "all" }) + { + yield return new object[] { rundown, provider }; + } + } + } + + // Explore how different providers impact session startup time (manifests when EventStream.CopyToAsync() is added). + [Benchmark] + [ArgumentsSource(nameof(SessionArguments))] + public void DiagnosticsSessionStartCopyStop(bool rundown, string provider) + { + EventPipeProvider[] providers = provider switch + { + "runtime" => new[] { new EventPipeProvider("Microsoft-Windows-DotNETRuntime", EventLevel.Informational, (long)ClrTraceEventParser.Keywords.Default) }, + "sample" => new[] { new EventPipeProvider("Microsoft-DotNETCore-SampleProfiler", EventLevel.Informational) }, + "tpl" => new[] { new EventPipeProvider("System.Threading.Tasks.TplEventSource", EventLevel.Informational, (long)TplEtwProviderTraceEventParser.Keywords.Default) }, + "all" => SampleProfilerSession.Providers, + _ => throw new InvalidEnumArgumentException(nameof(provider)) + }; + var session = DiagnosticsClientNew().StartEventPipeSession(providers, rundown, SampleProfilerSession.CircularBufferMB); + var stream = new MemoryStream(); + var copyTask = session.EventStream.CopyToAsync(stream); + session.Stop(); + copyTask.Wait(); + session.Dispose(); + } + + // Same as DiagnosticsSessionStartCopyStop(rundown: true, provider: 'all') + [Benchmark] + public void SampleProfilerSessionStartStopFinishWait() + { + var session = SampleProfilerSession.StartNew(CancellationToken.None); + session.Stop(); + session.FinishAsync().Wait(); + } + + [Benchmark] + public void SampleProfilerSessionStartStop() + { + var session = SampleProfilerSession.StartNew(CancellationToken.None); + session.Stop(); + } + #endregion + + #region Measure overhead of having a profiler enabled while doing work. + public int[] OverheadRunArguments { get; } = new[] { 10_000, 100_000 }; + + [BenchmarkCategory("overhead"), Benchmark(Baseline = true)] + [ArgumentsSource(nameof(OverheadRunArguments))] + public long DoHardWork(int n) + { + return ProfilingBenchmarks.FindPrimeNumber(n); + } + + [BenchmarkCategory("overhead"), Benchmark] + [ArgumentsSource(nameof(OverheadRunArguments))] + public long DoHardWorkWhileProfiling(int n) + { + return ProfilingBenchmarks.FindPrimeNumber(n); + } + + private ITransactionProfiler _profiler; + + [GlobalSetup(Target = nameof(DoHardWorkWhileProfiling))] + public void StartProfiler() + { + _profiler = _factory.Start(new TransactionTracer(_hub, "", ""), CancellationToken.None); + } + + [GlobalCleanup(Target = nameof(DoHardWorkWhileProfiling))] + public void StopProfiler() + { + _profiler?.Finish(); + _profiler?.CollectAsync(new Transaction("", "")).Wait(); + _profiler = null; + } + #endregion +} diff --git a/benchmarks/Sentry.Benchmarks/Sentry.Benchmarks.csproj b/benchmarks/Sentry.Benchmarks/Sentry.Benchmarks.csproj index 123f94bb95..e17e95ab59 100644 --- a/benchmarks/Sentry.Benchmarks/Sentry.Benchmarks.csproj +++ b/benchmarks/Sentry.Benchmarks/Sentry.Benchmarks.csproj @@ -8,10 +8,12 @@ + + diff --git a/src/Sentry.Profiling/Properties/AssemblyInfo.cs b/src/Sentry.Profiling/Properties/AssemblyInfo.cs index 006463d7bc..00e73812b2 100644 --- a/src/Sentry.Profiling/Properties/AssemblyInfo.cs +++ b/src/Sentry.Profiling/Properties/AssemblyInfo.cs @@ -1,2 +1,3 @@ [assembly: InternalsVisibleTo("Sentry.Profiling.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] +[assembly: InternalsVisibleTo("Sentry.Benchmarks, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] diff --git a/src/Sentry.Profiling/SampleProfilerSession.cs b/src/Sentry.Profiling/SampleProfilerSession.cs index 7ac17f0274..f8811de3fd 100644 --- a/src/Sentry.Profiling/SampleProfilerSession.cs +++ b/src/Sentry.Profiling/SampleProfilerSession.cs @@ -11,6 +11,7 @@ internal class SampleProfilerSession private readonly Task _copyTask; private readonly CancellationTokenRegistration _stopRegistration; + private bool _stopped; private SampleProfilerSession(EventPipeSession session, MemoryStream stream, Task copyTask, CancellationTokenRegistration stopRegistration) { @@ -20,23 +21,27 @@ private SampleProfilerSession(EventPipeSession session, MemoryStream stream, Tas _stopRegistration = stopRegistration; } - public static SampleProfilerSession StartNew(CancellationToken cancellationToken) + // Exposed only for benchmarks. + internal static EventPipeProvider[] Providers = new[] { - var 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 - new EventPipeProvider("Microsoft-Windows-DotNETRuntime", EventLevel.Informational, (long)ClrTraceEventParser.Keywords.Default), - new EventPipeProvider("Microsoft-DotNETCore-SampleProfiler", EventLevel.Informational), - new EventPipeProvider("System.Threading.Tasks.TplEventSource", EventLevel.Informational, (long)TplEtwProviderTraceEventParser.Keywords.Default) - }; + // 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 + new EventPipeProvider("Microsoft-Windows-DotNETRuntime", EventLevel.Informational, (long) ClrTraceEventParser.Keywords.Default), + new EventPipeProvider("Microsoft-DotNETCore-SampleProfiler", EventLevel.Informational), + new EventPipeProvider("System.Threading.Tasks.TplEventSource", EventLevel.Informational, (long) TplEtwProviderTraceEventParser.Keywords.Default) + }; + // Exposed only for benchmarks. + internal static bool RequestRundown = true; - // The size of the runtime's buffer for collecting events in MB, same as the current default in StartEventPipeSession(). - var circularBufferMB = 256; + // Exposed only for benchmarks. + // The size of the runtime's buffer for collecting events in MB, same as the current default in StartEventPipeSession(). + internal static int CircularBufferMB = 256; + public static SampleProfilerSession StartNew(CancellationToken cancellationToken) + { var client = new DiagnosticsClient(Process.GetCurrentProcess().Id); - var session = client.StartEventPipeSession(providers, true, circularBufferMB); + var session = client.StartEventPipeSession(Providers, RequestRundown, CircularBufferMB); var stopRegistration = cancellationToken.Register(() => session.Stop(), false); var stream = new MemoryStream(); var copyTask = session.EventStream.CopyToAsync(stream, cancellationToken); @@ -44,10 +49,19 @@ public static SampleProfilerSession StartNew(CancellationToken cancellationToken return new SampleProfilerSession(session, stream, copyTask, stopRegistration); } + public void Stop() + { + if (!_stopped) + { + _stopRegistration.Unregister(); + _session.Stop(); + _stopped = true; + } + } + public async Task FinishAsync() { - _stopRegistration.Unregister(); - await _session.StopAsync(CancellationToken.None).ConfigureAwait(false); + Stop(); await _copyTask.ConfigureAwait(false); _session.Dispose(); _stream.Position = 0; diff --git a/src/Sentry.Profiling/SamplingTransactionProfiler.cs b/src/Sentry.Profiling/SamplingTransactionProfiler.cs index da92d415ac..b340916ca3 100644 --- a/src/Sentry.Profiling/SamplingTransactionProfiler.cs +++ b/src/Sentry.Profiling/SamplingTransactionProfiler.cs @@ -56,6 +56,10 @@ private bool Stop(TimeSpan? duration = null) _duration = duration ?? _stopwatch.Elapsed; try { + // Stop the session synchronously so we can let the factory know it can start a new one. + _session.Stop(); + OnFinish?.Invoke(); + // Then finish collecting the data asynchronously. _data = _session.FinishAsync(); } catch (Exception e) @@ -76,7 +80,6 @@ public void Finish() { _options.LogDebug("Profiling stopped on transaction finish."); } - OnFinish?.Invoke(); } /// From 7513600e2f9a3da153b349ad623447de95f3ef74 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Sat, 29 Apr 2023 08:31:44 +1200 Subject: [PATCH 011/142] Create a Sentry event for failed HTTP requests (#2320) --- CHANGELOG.md | 1 + src/Sentry/Contexts.cs | 9 + src/Sentry/Http/HttpTransportBase.cs | 1 - src/Sentry/HttpHeadersExtensions.cs | 9 + src/Sentry/HttpStatusCodeRange.cs | 95 ++++++++ src/Sentry/ISentryFailedRequestHandler.cs | 6 + src/Sentry/Protocol/Response.cs | 138 ++++++++++++ src/Sentry/Request.cs | 16 +- src/Sentry/SentryFailedRequestHandler.cs | 109 +++++++++ src/Sentry/SentryHttpMessageHandler.cs | 17 +- src/Sentry/SentryOptions.cs | 51 +++-- src/Sentry/SubstringOrRegexPattern.cs | 148 +++++++++++++ src/Sentry/TracePropagationTarget.cs | 67 ++---- .../ApiApprovalTests.Run.Core3_1.verified.txt | 48 +++- ...piApprovalTests.Run.DotNet6_0.verified.txt | 48 +++- ...piApprovalTests.Run.DotNet7_0.verified.txt | 48 +++- .../ApiApprovalTests.Run.Net4_8.verified.txt | 48 +++- .../Sentry.Tests/FailedRequestTargetsTests.cs | 124 +++++++++++ test/Sentry.Tests/HttpStatusCodeRangeTests.cs | 89 ++++++++ .../Protocol/Context/ContextsTests.cs | 19 ++ test/Sentry.Tests/Protocol/ResponseTests.cs | 71 ++++++ .../SentryFailedRequestHandlerTests.cs | 208 ++++++++++++++++++ .../SentryHttpMessageHandlerTests.cs | 27 ++- test/Sentry.Tests/SentryOptionsTests.cs | 21 ++ .../SubstringOrRegexPatternTests.cs | 94 ++++++++ .../TracePropagationTargetTests.cs | 108 +-------- 26 files changed, 1426 insertions(+), 194 deletions(-) create mode 100644 src/Sentry/HttpHeadersExtensions.cs create mode 100644 src/Sentry/HttpStatusCodeRange.cs create mode 100644 src/Sentry/ISentryFailedRequestHandler.cs create mode 100644 src/Sentry/Protocol/Response.cs create mode 100644 src/Sentry/SentryFailedRequestHandler.cs create mode 100644 src/Sentry/SubstringOrRegexPattern.cs create mode 100644 test/Sentry.Tests/FailedRequestTargetsTests.cs create mode 100644 test/Sentry.Tests/HttpStatusCodeRangeTests.cs create mode 100644 test/Sentry.Tests/Protocol/ResponseTests.cs create mode 100644 test/Sentry.Tests/SentryFailedRequestHandlerTests.cs create mode 100644 test/Sentry.Tests/SubstringOrRegexPatternTests.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 81fab183d7..977dd4c692 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Features - Initial work to support profiling in a future release. ([#2206](https://github.com/getsentry/sentry-dotnet/pull/2206)) +- Create a Sentry event for failed HTTP requests ([#2320](https://github.com/getsentry/sentry-dotnet/pull/2320)) - Improve `WithScope` and add `WithScopeAsync` ([#2303](https://github.com/getsentry/sentry-dotnet/pull/2303)) ([#2309](https://github.com/getsentry/sentry-dotnet/pull/2309)) - Build .NET Standard 2.1 for Unity ([#2328](https://github.com/getsentry/sentry-dotnet/pull/2328)) - Add `RemoveExceptionFilter`, `RemoveEventProcessor` and `RemoveTransactionProcessor` extension methods on `SentryOptions` ([#2331](https://github.com/getsentry/sentry-dotnet/pull/2331)) diff --git a/src/Sentry/Contexts.cs b/src/Sentry/Contexts.cs index 7c5f7c31fe..b4ab6d0335 100644 --- a/src/Sentry/Contexts.cs +++ b/src/Sentry/Contexts.cs @@ -36,6 +36,11 @@ public sealed class Contexts : ConcurrentDictionary, IJsonSerial /// public OperatingSystem OperatingSystem => this.GetOrCreate(OperatingSystem.Type); + /// + /// Response interface that contains information on any HTTP response related to the event. + /// + public Response Response => this.GetOrCreate(Response.Type); + /// /// This describes a runtime in more detail. /// @@ -144,6 +149,10 @@ public static Contexts FromJson(JsonElement json) { result[name] = OperatingSystem.FromJson(value); } + else if (string.Equals(type, Response.Type, StringComparison.OrdinalIgnoreCase)) + { + result[name] = Response.FromJson(value); + } else if (string.Equals(type, Runtime.Type, StringComparison.OrdinalIgnoreCase)) { result[name] = Runtime.FromJson(value); diff --git a/src/Sentry/Http/HttpTransportBase.cs b/src/Sentry/Http/HttpTransportBase.cs index 3e3a881ef4..2eb646b8bd 100644 --- a/src/Sentry/Http/HttpTransportBase.cs +++ b/src/Sentry/Http/HttpTransportBase.cs @@ -405,7 +405,6 @@ private async Task HandleFailureAsync(HttpResponseMessage response, Envelope env .SerializeToStringAsync(_options.DiagnosticLogger, _clock, cancellationToken).ConfigureAwait(false); _options.LogDebug("Failed envelope '{0}' has payload:\n{1}\n", eventId, payload); - // SDK is in debug mode, and envelope was too large. To help troubleshoot: const string persistLargeEnvelopePathEnvVar = "SENTRY_KEEP_LARGE_ENVELOPE_PATH"; if (response.StatusCode == HttpStatusCode.RequestEntityTooLarge diff --git a/src/Sentry/HttpHeadersExtensions.cs b/src/Sentry/HttpHeadersExtensions.cs new file mode 100644 index 0000000000..cb55f4e7ac --- /dev/null +++ b/src/Sentry/HttpHeadersExtensions.cs @@ -0,0 +1,9 @@ +namespace Sentry; + +internal static class HttpHeadersExtensions +{ + internal static string GetCookies(this HttpHeaders headers) => + headers.TryGetValues("Cookie", out var values) + ? string.Join("; ", values) + : string.Empty; +} diff --git a/src/Sentry/HttpStatusCodeRange.cs b/src/Sentry/HttpStatusCodeRange.cs new file mode 100644 index 0000000000..cecd95530a --- /dev/null +++ b/src/Sentry/HttpStatusCodeRange.cs @@ -0,0 +1,95 @@ +namespace Sentry; + +/// +/// Holds a fully-inclusive range of HTTP status codes. +/// e.g. Start = 500, End = 599 represents the range 500-599. +/// +public readonly record struct HttpStatusCodeRange +{ + /// + /// The inclusive start of the range. + /// + public int Start { get; init; } + + /// + /// The inclusive end of the range. + /// + public int End { get; init; } + + /// + /// Creates a range that will only match a single value. + /// + /// The value in the range. + public HttpStatusCodeRange(int statusCode) + { + Start = statusCode; + End = statusCode; + } + + /// + /// Creates a range that will match all values between and . + /// + /// The inclusive start of the range. + /// The inclusive end of the range. + /// + /// Thrown if is greater than . + /// + public HttpStatusCodeRange(int start, int end) + { + if (start > end) + { + throw new ArgumentOutOfRangeException(nameof(start), "Range start must be after range end"); + } + + Start = start; + End = end; + } + + /// + /// Implicitly converts a tuple of ints to a . + /// + /// A tuple of ints to convert. + public static implicit operator HttpStatusCodeRange((int Start, int End) range) => new(range.Start, range.End); + + /// + /// Implicitly converts an int to a . + /// + /// An int to convert. + public static implicit operator HttpStatusCodeRange(int statusCode) + { + return new HttpStatusCodeRange(statusCode); + } + + /// + /// Implicitly converts an to a . + /// + /// A status code to convert. + public static implicit operator HttpStatusCodeRange(HttpStatusCode statusCode) + { + return new HttpStatusCodeRange((int)statusCode); + } + + /// + /// Implicitly converts a tuple of to a . + /// + /// A tuple of status codes to convert. + public static implicit operator HttpStatusCodeRange((HttpStatusCode start, HttpStatusCode end) range) + { + return new HttpStatusCodeRange((int)range.start, (int)range.end); + } + + /// + /// Checks if a given status code is contained in the range. + /// + /// Status code to check. + /// True if the range contains the given status code. + public bool Contains(int statusCode) + => statusCode >= Start && statusCode <= End; + + /// + /// Checks if a given status code is contained in the range. + /// + /// Status code to check. + /// True if the range contains the given status code. + public bool Contains(HttpStatusCode statusCode) => Contains((int)statusCode); +} diff --git a/src/Sentry/ISentryFailedRequestHandler.cs b/src/Sentry/ISentryFailedRequestHandler.cs new file mode 100644 index 0000000000..dcde535338 --- /dev/null +++ b/src/Sentry/ISentryFailedRequestHandler.cs @@ -0,0 +1,6 @@ +namespace Sentry; + +internal interface ISentryFailedRequestHandler +{ + void HandleResponse(HttpResponseMessage response); +} diff --git a/src/Sentry/Protocol/Response.cs b/src/Sentry/Protocol/Response.cs new file mode 100644 index 0000000000..6b424fabc5 --- /dev/null +++ b/src/Sentry/Protocol/Response.cs @@ -0,0 +1,138 @@ +using Sentry.Extensibility; +using Sentry.Internal; +using Sentry.Internal.Extensions; + +namespace Sentry.Protocol; + +/// +/// Sentry Response context interface. +/// +/// +///{ +/// "contexts": { +/// "response": { +/// "cookies": "PHPSESSID=298zf09hf012fh2; csrftoken=u32t4o3tb3gg43; _gat=1;", +/// "headers": { +/// "content-type": "text/html" +/// /// ... +/// }, +/// "status_code": 500, +/// "body_size": 1000, // in bytes +/// } +/// } +///} +/// +/// +public sealed class Response : IJsonSerializable, ICloneable, IUpdatable +{ + /// + /// Tells Sentry which type of context this is. + /// + public const string Type = "response"; + + internal Dictionary? InternalHeaders { get; private set; } + + /// + /// Gets or sets the HTTP response body size. + /// + public long? BodySize { get; set; } + + /// + /// Gets or sets (optional) cookie values + /// + public string? Cookies { get; set; } + + /// + /// Gets or sets the headers. + /// + /// + /// 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(); + + /// + /// Gets or sets the HTTP Status response code + /// + /// The HTTP method. + public short? StatusCode { get; set; } + + internal void AddHeaders(IEnumerable>> headers) + { + foreach (var header in headers) + { + Headers.Add( + header.Key, + string.Join("; ", header.Value) + ); + } + } + + /// + /// Clones this instance. + /// + public Response Clone() + { + var response = new Response(); + + response.UpdateFrom(this); + + return response; + } + + /// + /// Updates this instance with data from the properties in the , + /// unless there is already a value in the existing property. + /// + public void UpdateFrom(Response source) + { + BodySize ??= source.BodySize; + Cookies ??= source.Cookies; + StatusCode ??= source.StatusCode; + source.InternalHeaders?.TryCopyTo(Headers); + } + + /// + /// Updates this instance with data from the properties in the , + /// unless there is already a value in the existing property. + /// + public void UpdateFrom(object source) + { + if (source is Response response) + { + UpdateFrom(response); + } + } + + /// + public void WriteTo(Utf8JsonWriter writer, IDiagnosticLogger? logger) + { + writer.WriteStartObject(); + + writer.WriteString("type", Type); + writer.WriteNumberIfNotNull("body_size", BodySize); + writer.WriteStringIfNotWhiteSpace("cookies", Cookies); + writer.WriteStringDictionaryIfNotEmpty("headers", InternalHeaders!); + writer.WriteNumberIfNotNull("status_code", StatusCode); + + writer.WriteEndObject(); + } + + /// + /// Parses from JSON. + /// + public static Response FromJson(JsonElement json) + { + var bodySize = json.GetPropertyOrNull("body_size")?.GetInt64(); + var cookies = json.GetPropertyOrNull("cookies")?.GetString(); + var headers = json.GetPropertyOrNull("headers")?.GetStringDictionaryOrNull(); + var statusCode = json.GetPropertyOrNull("status_code")?.GetInt16(); + + return new Response + { + BodySize = bodySize, + Cookies = cookies, + InternalHeaders = headers?.WhereNotNullValue().ToDictionary(), + StatusCode = statusCode + }; + } +} diff --git a/src/Sentry/Request.cs b/src/Sentry/Request.cs index 03f2a5a317..9cd8ded3f3 100644 --- a/src/Sentry/Request.cs +++ b/src/Sentry/Request.cs @@ -26,11 +26,11 @@ namespace Sentry; /// public sealed class Request : IJsonSerializable { - internal Dictionary? InternalEnv { get; set; } + internal Dictionary? InternalEnv { get; private set; } - internal Dictionary? InternalOther { get; set; } + internal Dictionary? InternalOther { get; private set; } - internal Dictionary? InternalHeaders { get; set; } + internal Dictionary? InternalHeaders { get; private set; } /// /// Gets or sets the full request URL, if available. @@ -91,6 +91,14 @@ public sealed class Request : IJsonSerializable /// The other. public IDictionary Other => InternalOther ??= new Dictionary(); + internal void AddHeaders(IEnumerable>> headers) + { + foreach (var header in headers) + { + Headers.Add(header.Key, string.Join("; ", header.Value)); + } + } + /// /// Clones this instance. /// @@ -158,7 +166,7 @@ public static Request FromJson(JsonElement json) return new Request { - InternalEnv = env?.WhereNotNullValue()?.ToDictionary(), + InternalEnv = env?.WhereNotNullValue().ToDictionary(), InternalOther = other?.WhereNotNullValue().ToDictionary(), InternalHeaders = headers?.WhereNotNullValue().ToDictionary(), Url = url, diff --git a/src/Sentry/SentryFailedRequestHandler.cs b/src/Sentry/SentryFailedRequestHandler.cs new file mode 100644 index 0000000000..283bab5387 --- /dev/null +++ b/src/Sentry/SentryFailedRequestHandler.cs @@ -0,0 +1,109 @@ +using Sentry.Protocol; + +namespace Sentry; + +internal class SentryFailedRequestHandler : ISentryFailedRequestHandler +{ + private readonly IHub _hub; + private readonly SentryOptions _options; + + public const string MechanismType = "SentryFailedRequestHandler"; + + internal SentryFailedRequestHandler(IHub hub, SentryOptions options) + { + _hub = hub; + _options = options; + } + + public void HandleResponse(HttpResponseMessage response) + { + // Ensure request is not null + if (response.RequestMessage is null) + { + return; + } + + // Don't capture if the option is disabled + if (!_options.CaptureFailedRequests) + { + return; + } + + // Don't capture events for successful requests + if (!_options.FailedRequestStatusCodes.Any(range => range.Contains(response.StatusCode))) + { + return; + } + + // Ignore requests to the Sentry DSN + var uri = response.RequestMessage.RequestUri; + if (uri != null) + { + if (_options.Dsn is { } dsn && new Uri(dsn).Host.Equals(uri.Host, StringComparison.OrdinalIgnoreCase)) + { + return; + } + + // Ignore requests that don't match the FailedRequestTargets + var requestString = uri.ToString(); + if (!_options.FailedRequestTargets.ContainsMatch(requestString)) + { + return; + } + } + +#if NET5_0_OR_GREATER + // Starting with .NET 5, the content and headers are guaranteed to not be null. + var bodySize = response.Content.Headers.ContentLength; +#else + // We have to get the content body size before calling EnsureSuccessStatusCode, + // because older implementations of EnsureSuccessStatusCode disposes the content. + // See https://github.com/dotnet/runtime/issues/24845 + + // The ContentLength might be null (but that's ok). + // See https://github.com/dotnet/runtime/issues/16162 + var bodySize = response.Content?.Headers?.ContentLength; +#endif + + // Capture the event + try + { + response.EnsureSuccessStatusCode(); + } + catch (HttpRequestException exception) + { + exception.SetSentryMechanism(MechanismType); + + var @event = new SentryEvent(exception); + + var sentryRequest = new Request + { + Url = uri?.AbsoluteUri, + QueryString = uri?.Query, + Method = response.RequestMessage.Method.Method, + }; + + if (_options.SendDefaultPii) + { + sentryRequest.Cookies = response.RequestMessage.Headers.GetCookies(); + sentryRequest.AddHeaders(response.RequestMessage.Headers); + } + + var responseContext = new Response { + StatusCode = (short)response.StatusCode, + BodySize = bodySize + }; + + if (_options.SendDefaultPii) + { + responseContext.Cookies = response.Headers.GetCookies(); + responseContext.AddHeaders(response.Headers); + } + + @event.Request = sentryRequest; + @event.Contexts[Response.Type] = responseContext; + + _hub.CaptureEvent(@event); + } + } +} diff --git a/src/Sentry/SentryHttpMessageHandler.cs b/src/Sentry/SentryHttpMessageHandler.cs index 68e1a1602c..4744287626 100644 --- a/src/Sentry/SentryHttpMessageHandler.cs +++ b/src/Sentry/SentryHttpMessageHandler.cs @@ -10,6 +10,7 @@ public class SentryHttpMessageHandler : DelegatingHandler { private readonly IHub _hub; private readonly SentryOptions? _options; + private readonly ISentryFailedRequestHandler? _failedRequestHandler; /// /// Initializes an instance of . @@ -18,12 +19,17 @@ public SentryHttpMessageHandler(IHub hub) { _hub = hub; _options = hub.GetSentryOptions(); + if (_options != null) + { + _failedRequestHandler = new SentryFailedRequestHandler(_hub, _options); + } } - internal SentryHttpMessageHandler(IHub hub, SentryOptions options) + internal SentryHttpMessageHandler(IHub hub, SentryOptions options, ISentryFailedRequestHandler? failedRequestHandler = null) { _hub = hub; _options = options; + _failedRequestHandler = failedRequestHandler; } /// @@ -35,8 +41,8 @@ public SentryHttpMessageHandler(HttpMessageHandler innerHandler, IHub hub) InnerHandler = innerHandler; } - internal SentryHttpMessageHandler(HttpMessageHandler innerHandler, IHub hub, SentryOptions options) - : this(hub, options) + internal SentryHttpMessageHandler(HttpMessageHandler innerHandler, IHub hub, SentryOptions options, ISentryFailedRequestHandler? failedRequestHandler = null) + : this(hub, options, failedRequestHandler) { InnerHandler = innerHandler; } @@ -65,7 +71,7 @@ protected override async Task SendAsync( var requestMethod = request.Method.Method.ToUpperInvariant(); var url = request.RequestUri?.ToString() ?? string.Empty; - if (_options?.TracePropagationTargets.ShouldPropagateTrace(url) is true or null) + if (_options?.TracePropagationTargets.ContainsMatch(url) is true or null) { AddSentryTraceHeader(request); AddBaggageHeader(request); @@ -90,6 +96,9 @@ protected override async Task SendAsync( }; _hub.AddBreadcrumb(string.Empty, "http", "http", breadcrumbData); + // Create events for failed requests + _failedRequestHandler?.HandleResponse(response); + // This will handle unsuccessful status codes as well span?.Finish(SpanStatusConverter.FromHttpStatusCode(response.StatusCode)); diff --git a/src/Sentry/SentryOptions.cs b/src/Sentry/SentryOptions.cs index 94d363be37..e27763249e 100644 --- a/src/Sentry/SentryOptions.cs +++ b/src/Sentry/SentryOptions.cs @@ -522,6 +522,34 @@ public bool ReportAssemblies /// public string? CacheDirectoryPath { get; set; } + /// + /// The SDK will only capture HTTP Client errors if it is enabled. + /// can be used to configure which requests will be treated as failed. + /// Also can be used to filter to match only certain request URLs. + /// Defaults to false due to PII reasons. + /// + public bool CaptureFailedRequests { get; set; } + + /// + /// The SDK will only capture HTTP Client errors if the HTTP Response status code is within these defined ranges. + /// Defaults to 500-599 (Server error responses only). + /// + public IList FailedRequestStatusCodes { get; set; } = new List { (500, 599) }; + + // The default failed request target list will match anything, but adding to the list should clear that. + private IList _failedRequestTargets = new AutoClearingList( + new[] {new SubstringOrRegexPattern(".*")}, 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 { + get => _failedRequestTargets; + set => _failedRequestTargets = value.SetWithConfigBinding(); + } + /// /// Sets the filesystem instance to use. Defaults to the actual . /// Used for testing. @@ -646,29 +674,8 @@ 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. // We have to handle both cases. - get => _tracePropagationTargets; - set - { - 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. - _tracePropagationTargets = new AutoClearingList(value, clearOnNextAdd: true); - break; - - case > 1: - // There's more than one item in the list. Remove the wildcard. - var targets = value.ToList(); - targets.RemoveAll(t => t.ToString() == ".*"); - _tracePropagationTargets = targets; - break; - - default: - _tracePropagationTargets = value; - break; - } - } + set => _tracePropagationTargets = value.SetWithConfigBinding(); } internal ITransactionProfilerFactory? TransactionProfilerFactory { get; set; } diff --git a/src/Sentry/SubstringOrRegexPattern.cs b/src/Sentry/SubstringOrRegexPattern.cs new file mode 100644 index 0000000000..3287eb6d86 --- /dev/null +++ b/src/Sentry/SubstringOrRegexPattern.cs @@ -0,0 +1,148 @@ +using Sentry.Internal; + +namespace Sentry; + +/// +/// 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); + } + + /// + 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 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 SetWithConfigBinding(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 TracePropagationTarget((string)value); +} diff --git a/src/Sentry/TracePropagationTarget.cs b/src/Sentry/TracePropagationTarget.cs index a17d3271f3..664c2e375c 100644 --- a/src/Sentry/TracePropagationTarget.cs +++ b/src/Sentry/TracePropagationTarget.cs @@ -1,5 +1,7 @@ namespace Sentry; +// TODO: Replace TracePropagationTarget with SubstringOrRegexPattern in next major version. + /// /// Provides a pattern that can be used to identify which destinations will have sentry-trace and /// baggage headers propagated to, for purposes of distributed tracing. @@ -7,12 +9,8 @@ namespace Sentry; /// /// [TypeConverter(typeof(TracePropagationTargetTypeConverter))] -public class TracePropagationTarget +public class TracePropagationTarget: SubstringOrRegexPattern { - private readonly Regex? _regex; - private readonly string? _substring; - private readonly StringComparison _stringComparison; - /// /// Constructs a instance that will match when the provided /// is either found as a substring within the outgoing request URL, @@ -23,10 +21,8 @@ public class TracePropagationTarget public TracePropagationTarget( string substringOrRegexPattern, StringComparison comparison = StringComparison.OrdinalIgnoreCase) + : base(substringOrRegexPattern, comparison) { - _substring = substringOrRegexPattern; - _stringComparison = comparison; - _regex = TryParseRegex(substringOrRegexPattern, comparison); } /// @@ -40,59 +36,22 @@ public TracePropagationTarget( /// (unless you have culture-specific matching needs). /// The constructor sets these by default. /// - public TracePropagationTarget(Regex regex) => _regex = regex; - - /// - public override string ToString() => _substring ?? _regex?.ToString() ?? ""; - - internal bool IsMatch(string url) => - _substring == ".*" || // perf shortcut - (_substring != null && url.Contains(_substring, _stringComparison)) || - _regex?.IsMatch(url) == true; - - private static Regex? TryParseRegex(string pattern, StringComparison comparison) + public TracePropagationTarget(Regex regex) : base (regex) { - 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 TracePropagationTargetExtensions -{ - public static bool ShouldPropagateTrace(this IEnumerable targets, string url) => - targets.Any(t => t.IsMatch(url)); -} +//internal static class TracePropagationTargetExtensions +//{ +// public static bool ShouldPropagateTrace(this IEnumerable targets, string url) => +// targets.Any(t => t.IsMatch(url)); +//} +/// +/// Allows the TracePropagationTargets option to be set from config, such as appSettings.json +/// internal class TracePropagationTargetTypeConverter : 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); diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.Core3_1.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.Core3_1.verified.txt index 755a9b9e68..bc4762d774 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.Core3_1.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.Core3_1.verified.txt @@ -65,6 +65,7 @@ namespace Sentry public Sentry.Protocol.Device Device { get; } public Sentry.Protocol.Gpu Gpu { 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 void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } @@ -137,6 +138,23 @@ namespace Sentry { public static void SetTags(this Sentry.IHasTags hasTags, System.Collections.Generic.IEnumerable> tags) { } } + 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, string message, string? category = null, string? type = null, System.Collections.Generic.IDictionary? data = null, Sentry.BreadcrumbLevel level = 0) { } @@ -551,6 +569,7 @@ namespace Sentry public System.Func? BeforeSend { get; set; } public System.Func? BeforeSendTransaction { 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? CreateHttpClientHandler { get; set; } @@ -566,6 +585,8 @@ namespace Sentry public bool EnableScopeSync { get; set; } public bool? EnableTracing { 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; } @@ -913,12 +934,21 @@ namespace Sentry public StreamAttachmentContent(System.IO.Stream stream) { } public System.IO.Stream GetStream() { } } + [System.ComponentModel.TypeConverter(typeof(Sentry.SubstringOrRegexPatternTypeConverter))] + public class SubstringOrRegexPattern + { + public SubstringOrRegexPattern(System.Text.RegularExpressions.Regex regex) { } + public SubstringOrRegexPattern(string substringOrRegexPattern, System.StringComparison comparison = 5) { } + public override bool Equals(object? obj) { } + public override int GetHashCode() { } + public override string ToString() { } + public static Sentry.SubstringOrRegexPattern op_Implicit(string substringOrRegexPattern) { } + } [System.ComponentModel.TypeConverter(typeof(Sentry.TracePropagationTargetTypeConverter))] - public class TracePropagationTarget + public class TracePropagationTarget : Sentry.SubstringOrRegexPattern { public TracePropagationTarget(System.Text.RegularExpressions.Regex regex) { } public TracePropagationTarget(string substringOrRegexPattern, System.StringComparison comparison = 5) { } - public override string ToString() { } } public class Transaction : Sentry.IEventLike, Sentry.IHasBreadcrumbs, Sentry.IHasExtra, Sentry.IHasTags, Sentry.IHasTransactionNameSource, Sentry.IJsonSerializable, Sentry.ISpanContext, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.Protocol.ITraceContext { @@ -1514,6 +1544,20 @@ namespace Sentry.Protocol 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.IJsonSerializable + { + public const string Type = "response"; + public Response() { } + public long? BodySize { get; set; } + public string? Cookies { 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.IJsonSerializable { public const string Type = "runtime"; diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt index 755a9b9e68..bc4762d774 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt @@ -65,6 +65,7 @@ namespace Sentry public Sentry.Protocol.Device Device { get; } public Sentry.Protocol.Gpu Gpu { 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 void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } @@ -137,6 +138,23 @@ namespace Sentry { public static void SetTags(this Sentry.IHasTags hasTags, System.Collections.Generic.IEnumerable> tags) { } } + 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, string message, string? category = null, string? type = null, System.Collections.Generic.IDictionary? data = null, Sentry.BreadcrumbLevel level = 0) { } @@ -551,6 +569,7 @@ namespace Sentry public System.Func? BeforeSend { get; set; } public System.Func? BeforeSendTransaction { 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? CreateHttpClientHandler { get; set; } @@ -566,6 +585,8 @@ namespace Sentry public bool EnableScopeSync { get; set; } public bool? EnableTracing { 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; } @@ -913,12 +934,21 @@ namespace Sentry public StreamAttachmentContent(System.IO.Stream stream) { } public System.IO.Stream GetStream() { } } + [System.ComponentModel.TypeConverter(typeof(Sentry.SubstringOrRegexPatternTypeConverter))] + public class SubstringOrRegexPattern + { + public SubstringOrRegexPattern(System.Text.RegularExpressions.Regex regex) { } + public SubstringOrRegexPattern(string substringOrRegexPattern, System.StringComparison comparison = 5) { } + public override bool Equals(object? obj) { } + public override int GetHashCode() { } + public override string ToString() { } + public static Sentry.SubstringOrRegexPattern op_Implicit(string substringOrRegexPattern) { } + } [System.ComponentModel.TypeConverter(typeof(Sentry.TracePropagationTargetTypeConverter))] - public class TracePropagationTarget + public class TracePropagationTarget : Sentry.SubstringOrRegexPattern { public TracePropagationTarget(System.Text.RegularExpressions.Regex regex) { } public TracePropagationTarget(string substringOrRegexPattern, System.StringComparison comparison = 5) { } - public override string ToString() { } } public class Transaction : Sentry.IEventLike, Sentry.IHasBreadcrumbs, Sentry.IHasExtra, Sentry.IHasTags, Sentry.IHasTransactionNameSource, Sentry.IJsonSerializable, Sentry.ISpanContext, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.Protocol.ITraceContext { @@ -1514,6 +1544,20 @@ namespace Sentry.Protocol 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.IJsonSerializable + { + public const string Type = "response"; + public Response() { } + public long? BodySize { get; set; } + public string? Cookies { 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.IJsonSerializable { public const string Type = "runtime"; diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt index 755a9b9e68..bc4762d774 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt @@ -65,6 +65,7 @@ namespace Sentry public Sentry.Protocol.Device Device { get; } public Sentry.Protocol.Gpu Gpu { 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 void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } @@ -137,6 +138,23 @@ namespace Sentry { public static void SetTags(this Sentry.IHasTags hasTags, System.Collections.Generic.IEnumerable> tags) { } } + 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, string message, string? category = null, string? type = null, System.Collections.Generic.IDictionary? data = null, Sentry.BreadcrumbLevel level = 0) { } @@ -551,6 +569,7 @@ namespace Sentry public System.Func? BeforeSend { get; set; } public System.Func? BeforeSendTransaction { 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? CreateHttpClientHandler { get; set; } @@ -566,6 +585,8 @@ namespace Sentry public bool EnableScopeSync { get; set; } public bool? EnableTracing { 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; } @@ -913,12 +934,21 @@ namespace Sentry public StreamAttachmentContent(System.IO.Stream stream) { } public System.IO.Stream GetStream() { } } + [System.ComponentModel.TypeConverter(typeof(Sentry.SubstringOrRegexPatternTypeConverter))] + public class SubstringOrRegexPattern + { + public SubstringOrRegexPattern(System.Text.RegularExpressions.Regex regex) { } + public SubstringOrRegexPattern(string substringOrRegexPattern, System.StringComparison comparison = 5) { } + public override bool Equals(object? obj) { } + public override int GetHashCode() { } + public override string ToString() { } + public static Sentry.SubstringOrRegexPattern op_Implicit(string substringOrRegexPattern) { } + } [System.ComponentModel.TypeConverter(typeof(Sentry.TracePropagationTargetTypeConverter))] - public class TracePropagationTarget + public class TracePropagationTarget : Sentry.SubstringOrRegexPattern { public TracePropagationTarget(System.Text.RegularExpressions.Regex regex) { } public TracePropagationTarget(string substringOrRegexPattern, System.StringComparison comparison = 5) { } - public override string ToString() { } } public class Transaction : Sentry.IEventLike, Sentry.IHasBreadcrumbs, Sentry.IHasExtra, Sentry.IHasTags, Sentry.IHasTransactionNameSource, Sentry.IJsonSerializable, Sentry.ISpanContext, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.Protocol.ITraceContext { @@ -1514,6 +1544,20 @@ namespace Sentry.Protocol 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.IJsonSerializable + { + public const string Type = "response"; + public Response() { } + public long? BodySize { get; set; } + public string? Cookies { 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.IJsonSerializable { public const string Type = "runtime"; diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt index baa06cc504..fa1b223ac0 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt @@ -65,6 +65,7 @@ namespace Sentry public Sentry.Protocol.Device Device { get; } public Sentry.Protocol.Gpu Gpu { 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 void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { } @@ -136,6 +137,23 @@ namespace Sentry { public static void SetTags(this Sentry.IHasTags hasTags, System.Collections.Generic.IEnumerable> tags) { } } + 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, string message, string? category = null, string? type = null, System.Collections.Generic.IDictionary? data = null, Sentry.BreadcrumbLevel level = 0) { } @@ -550,6 +568,7 @@ namespace Sentry public System.Func? BeforeSend { get; set; } public System.Func? BeforeSendTransaction { 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? CreateHttpClientHandler { get; set; } @@ -565,6 +584,8 @@ namespace Sentry public bool EnableScopeSync { get; set; } public bool? EnableTracing { 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; } @@ -912,12 +933,21 @@ namespace Sentry public StreamAttachmentContent(System.IO.Stream stream) { } public System.IO.Stream GetStream() { } } + [System.ComponentModel.TypeConverter(typeof(Sentry.SubstringOrRegexPatternTypeConverter))] + public class SubstringOrRegexPattern + { + public SubstringOrRegexPattern(System.Text.RegularExpressions.Regex regex) { } + public SubstringOrRegexPattern(string substringOrRegexPattern, System.StringComparison comparison = 5) { } + public override bool Equals(object? obj) { } + public override int GetHashCode() { } + public override string ToString() { } + public static Sentry.SubstringOrRegexPattern op_Implicit(string substringOrRegexPattern) { } + } [System.ComponentModel.TypeConverter(typeof(Sentry.TracePropagationTargetTypeConverter))] - public class TracePropagationTarget + public class TracePropagationTarget : Sentry.SubstringOrRegexPattern { public TracePropagationTarget(System.Text.RegularExpressions.Regex regex) { } public TracePropagationTarget(string substringOrRegexPattern, System.StringComparison comparison = 5) { } - public override string ToString() { } } public class Transaction : Sentry.IEventLike, Sentry.IHasBreadcrumbs, Sentry.IHasExtra, Sentry.IHasTags, Sentry.IHasTransactionNameSource, Sentry.IJsonSerializable, Sentry.ISpanContext, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.Protocol.ITraceContext { @@ -1514,6 +1544,20 @@ namespace Sentry.Protocol 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.IJsonSerializable + { + public const string Type = "response"; + public Response() { } + public long? BodySize { get; set; } + public string? Cookies { 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.IJsonSerializable { public const string Type = "runtime"; diff --git a/test/Sentry.Tests/FailedRequestTargetsTests.cs b/test/Sentry.Tests/FailedRequestTargetsTests.cs new file mode 100644 index 0000000000..7d722bac0b --- /dev/null +++ b/test/Sentry.Tests/FailedRequestTargetsTests.cs @@ -0,0 +1,124 @@ +namespace Sentry.Tests; + +public class FailedRequestTargetsTests +{ + [Fact] + public void SentryOptions_FailedRequestTargets_DefaultAll() + { + var options = new SentryOptions(); + Assert.Equal(1, options.FailedRequestTargets.Count); + Assert.Equal(".*", options.FailedRequestTargets[0].ToString()); + } + + [Fact] + public void SentryOptions_FailedRequestTargets_AddRemovesDefault() + { + var options = new SentryOptions(); + options.FailedRequestTargets.Add("foo"); + options.FailedRequestTargets.Add("bar"); + + Assert.Equal(2, options.FailedRequestTargets.Count); + Assert.Equal("foo", options.FailedRequestTargets[0].ToString()); + Assert.Equal("bar", options.FailedRequestTargets[1].ToString()); + } + + [Fact] + public void SentryOptions_FailedRequestTargets_SetRemovesDefault() + { + var options = new SentryOptions(); + var targets = new List + { + ".*", + "foo", + "bar" + }; + + options.FailedRequestTargets = targets; + + Assert.Equal(2, options.FailedRequestTargets.Count); + Assert.Equal("foo", options.FailedRequestTargets[0].ToString()); + Assert.Equal("bar", options.FailedRequestTargets[1].ToString()); + } + + [Fact] + 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!); + + Assert.True(result1); + Assert.True(result2); + Assert.True(result3); + } + + [Fact] + public void SentryOptions_FailedRequestTargets_EmptyMatchesNone() + { + var options = new SentryOptions + { + FailedRequestTargets = new List() + }; + + var result1 = options.FailedRequestTargets.ContainsMatch("foo"); + var result2 = options.FailedRequestTargets.ContainsMatch(""); + var result3 = options.FailedRequestTargets.ContainsMatch(null!); + + Assert.False(result1); + Assert.False(result2); + Assert.False(result3); + } + + [Fact] + public void SentryOptions_FailedRequestTargets_OneMatch() + { + var options = new SentryOptions + { + FailedRequestTargets = new List + { + "foo", + "localhost", + "bar" + } + }; + + var result = options.FailedRequestTargets.ContainsMatch("http://localhost/abc/123"); + Assert.True(result); + } + + [Fact] + public void SentryOptions_FailedRequestTargets_MultipleMatches() + { + var options = new SentryOptions + { + FailedRequestTargets = new List + { + "foo", + "localhost", + "bar" + } + }; + + var result = options.FailedRequestTargets.ContainsMatch("http://localhost/foo/123"); + Assert.True(result); + } + + [Fact] + public void SentryOptions_FailedRequestTargets_NoMatches() + { + var options = new SentryOptions + { + FailedRequestTargets = new List + { + "foo", + "localhost", + "bar" + } + }; + + var result = options.FailedRequestTargets.ContainsMatch("https://sentry.io/abc/123"); + Assert.False(result); + } +} diff --git a/test/Sentry.Tests/HttpStatusCodeRangeTests.cs b/test/Sentry.Tests/HttpStatusCodeRangeTests.cs new file mode 100644 index 0000000000..fa450ced8c --- /dev/null +++ b/test/Sentry.Tests/HttpStatusCodeRangeTests.cs @@ -0,0 +1,89 @@ +namespace Sentry.Tests; + +public class HttpStatusCodeRangeTests +{ + [Fact] + public void HttpStatusCodeRange_Includes_Start() + { + // Arrange + var start = 100; + var end = 200; + HttpStatusCodeRange sut = (start, end); + + // Act + var inRange = sut.Contains(100); + + // Assert + inRange.Should().BeTrue(); + } + + [Fact] + public void HttpStatusCodeRange_Includes_End() + { + // Arrange + var start = 100; + var end = 200; + + HttpStatusCodeRange sut = (start, end); + + // Act + var inRange = sut.Contains(200); + + // Assert + inRange.Should().BeTrue(); + } + + [Fact] + public void HttpStatusCodeRange_Excludes_BelowStart() + { + // Arrange + var start = 100; + var end = 200; + HttpStatusCodeRange sut = (start, end); + + // Act + var inRange = sut.Contains(99); + + // Assert + inRange.Should().BeFalse(); + } + + [Fact] + public void HttpStatusCodeRange_Excludes_AboveEnd() + { + // Arrange + var start = 100; + var end = 200; + HttpStatusCodeRange sut = (start, end); + + // Act + var inRange = sut.Contains(201); + + // Assert + inRange.Should().BeFalse(); + } + + [Fact] + public void HttpStatusCodeRange_Start_After_End_Throws() + { + // Arrange + var start = 200; + var end = 100; + + // Act & Assert + Assert.ThrowsAny(() => new HttpStatusCodeRange(start, end)); + } + + [Fact] + public void HttpStatusCodeRange_Matches_SingleStatusCode() + { + // Arrange + HttpStatusCodeRange sut = HttpStatusCode.InternalServerError; + + // Act + var inRange = sut.Contains(HttpStatusCode.InternalServerError); + + // Assert + inRange.Should().BeTrue(); + } +} diff --git a/test/Sentry.Tests/Protocol/Context/ContextsTests.cs b/test/Sentry.Tests/Protocol/Context/ContextsTests.cs index f3c2cd95f1..204cc264c0 100644 --- a/test/Sentry.Tests/Protocol/Context/ContextsTests.cs +++ b/test/Sentry.Tests/Protocol/Context/ContextsTests.cs @@ -99,6 +99,25 @@ public void SerializeObject_SingleGpuPropertySet_SerializeSingleProperty() Assert.Equal("""{"gpu":{"type":"gpu","name":"My.Gpu"}}""", actualString); } + [Fact] + public void SerializeObject_SingleResponsePropertySet_SerializeSingleProperty() + { + var sut = new Contexts + { + Response = + { + StatusCode = 200 + } + }; + + var actualString = sut.ToJsonString(_testOutputLogger); + + var actual = Json.Parse(actualString, Contexts.FromJson); + actual.Should().BeEquivalentTo(sut); + + Assert.Equal("""{"response":{"type":"response","status_code":200}}""", actualString); + } + [Fact] public void SerializeObject_SingleRuntimePropertySet_SerializeSingleProperty() { diff --git a/test/Sentry.Tests/Protocol/ResponseTests.cs b/test/Sentry.Tests/Protocol/ResponseTests.cs new file mode 100644 index 0000000000..c357bb41d2 --- /dev/null +++ b/test/Sentry.Tests/Protocol/ResponseTests.cs @@ -0,0 +1,71 @@ +namespace Sentry.Tests.Protocol; + +public class ResponseTests +{ + [Fact] + public void Clone_CopyValues() + { + // Arrange + var sut = new Response + { + BodySize = 42, + Cookies = "PHPSESSID=298zf09hf012fh2; csrftoken=u32t4o3tb3gg43; _gat=1;", + StatusCode = 500 + }; + sut.Headers.Add("X-Test", "header"); + + // Act + var clone = sut.Clone(); + + // Assert + clone.BodySize.Should().Be(sut.BodySize); + clone.Cookies.Should().Be(sut.Cookies); + clone.StatusCode.Should().Be(sut.StatusCode); + clone.InternalHeaders.Should().NotBeSameAs(sut.InternalHeaders); + clone.Headers.Should().BeEquivalentTo(sut.Headers); + } + + [Fact] + public void ToJson_CopyValues() + { + // Arrange + var expected = new Response + { + BodySize = 42, + Cookies = "PHPSESSID=298zf09hf012fh2; csrftoken=u32t4o3tb3gg43; _gat=1;", + StatusCode = 500 + }; + expected.Headers.Add("X-Test", "header"); + + // Act + var json = JsonDocument.Parse(expected.ToJsonString()); + var actual = Response.FromJson(json.RootElement); + + // Assert + actual.BodySize.Should().Be(expected.BodySize); + actual.Cookies.Should().Be(expected.Cookies); + actual.StatusCode.Should().Be(expected.StatusCode); + actual.InternalHeaders.Should().NotBeSameAs(expected.InternalHeaders); + actual.Headers.Should().BeEquivalentTo(expected.Headers); + } + + [Fact] + public void AddHeaders_CreatesOneEntryPerHeader() + { + // Arrange + var sut = new Response(); + var headers = new List>> + { + new("X-Test", new[] { "header1", "header2" }) + }; + + // Act + sut.AddHeaders(headers); + + // Assert + sut.Headers.Should().BeEquivalentTo(new Dictionary + { + { "X-Test", "header1; header2" } + }); + } +} diff --git a/test/Sentry.Tests/SentryFailedRequestHandlerTests.cs b/test/Sentry.Tests/SentryFailedRequestHandlerTests.cs new file mode 100644 index 0000000000..6582f82e23 --- /dev/null +++ b/test/Sentry.Tests/SentryFailedRequestHandlerTests.cs @@ -0,0 +1,208 @@ +using FluentAssertions.Execution; + +namespace Sentry.Tests; + +public class SentryFailedRequestHandlerTests +{ + private readonly IHub _hub; + + public SentryFailedRequestHandlerTests() + { + _hub = Substitute.For(); + } + + private SentryFailedRequestHandler GetSut(SentryOptions options) + { + return new SentryFailedRequestHandler(_hub, options); + } + + private static HttpResponseMessage ForbiddenResponse() + => new(HttpStatusCode.Forbidden); + + private static HttpResponseMessage InternalServerErrorResponse() + => new(HttpStatusCode.InternalServerError); + + [Fact] + public void HandleResponse_Disabled_DontCapture() + { + // Arrange + var sut = GetSut(new SentryOptions + { + CaptureFailedRequests = false + }); + + var response = InternalServerErrorResponse(); + response.RequestMessage = new HttpRequestMessage(); + + // Act + sut.HandleResponse(response); + + // Assert + _hub.DidNotReceive().CaptureEvent(Arg.Any()); + } + + [Fact] + public void HandleResponse_EnabledButNotInRange_DontCapture() + { + // Arrange + var sut = GetSut(new SentryOptions + { + CaptureFailedRequests = true + }); + + var response = ForbiddenResponse(); // 403 is not in default range (500-599) + response.RequestMessage = new HttpRequestMessage(); + + // Act + sut?.HandleResponse(response); + + // Assert + _hub?.DidNotReceive().CaptureEvent(Arg.Any()); + } + + [Fact] + public void HandleResponse_RequestsToSentryDsn_DontCapture() + { + // Arrange + var options = new SentryOptions + { + CaptureFailedRequests = true, + Dsn = "https://eb18e953812b41c3aeb042e666fd3b5c@o447951.ingest.sentry.io/5428537" + }; + var sut = GetSut(options); + + var response = InternalServerErrorResponse(); + response.RequestMessage = new HttpRequestMessage(HttpMethod.Post, options.Dsn); + + // Act + sut.HandleResponse(response); + + // Assert + _hub.DidNotReceive().CaptureEvent(Arg.Any()); + } + + [Fact] + public void HandleResponse_NoMatchingTarget_DontCapture() + { + // Arrange + var options = new SentryOptions + { + CaptureFailedRequests = true, + FailedRequestTargets = new List { "http://foo/" } + }; + var sut = GetSut(options); + + var response = InternalServerErrorResponse(); + response.RequestMessage = new HttpRequestMessage(HttpMethod.Get, "http://bar/"); + + // Act + sut.HandleResponse(response); + + // Assert + _hub.DidNotReceive().CaptureEvent(Arg.Any()); + } + + [Fact] + public void HandleResponse_Capture_FailedRequest() + { + // Arrange + var options = new SentryOptions + { + CaptureFailedRequests = true + }; + var sut = GetSut(options); + + var response = InternalServerErrorResponse(); + response.RequestMessage = new HttpRequestMessage(); + + // Act + sut.HandleResponse(response); + + // Assert + _hub.Received(1).CaptureEvent(Arg.Any(), Arg.Any()); + } + + [Fact] + public void HandleResponse_Capture_RequestAndResponse() + { + // Arrange + var options = new SentryOptions + { + CaptureFailedRequests = true, + SendDefaultPii = true + }; + + var sut = GetSut(options); + + var url = "http://foo/bar/hello"; + var queryString = "?myQuery=myValue"; + var fragment = "myFragment"; + var absoluteUri = $"{url}{queryString}#{fragment}"; + var response = InternalServerErrorResponse(); // This is in the range + response.RequestMessage = new HttpRequestMessage(HttpMethod.Post, absoluteUri); + response.Headers.Add("myHeader", "myValue"); + response.Content = new StringContent("Something broke!", Encoding.UTF8, "text/plain"); + + // Act + SentryEvent @event = null; + _hub.CaptureEvent(Arg.Do(e => @event = e)); + sut.HandleResponse(response); + + // Assert + using (new AssertionScope()) + { + @event.Should().NotBeNull(); + + // Ensure the mechanism is set + @event.Exception?.Data[Mechanism.MechanismKey].Should().Be(SentryFailedRequestHandler.MechanismType); + + // Ensure the request properties are captured + @event.Request.Method.Should().Be(HttpMethod.Post.ToString()); + @event.Request.Url.Should().Be(absoluteUri); + @event.Request.QueryString.Should().Be(queryString); + + // Ensure the response context is captured + @event.Contexts.Should().Contain(x => x.Key == Response.Type && x.Value is Response); + + var responseContext = @event.Contexts[Response.Type] as Response; + responseContext?.StatusCode.Should().Be((short)response.StatusCode); + responseContext?.BodySize.Should().Be(response.Content.Headers.ContentLength); + + @event.Contexts.Response.Headers.Should().ContainKey("myHeader"); + @event.Contexts.Response.Headers.Should().ContainValue("myValue"); + } + } + + [Fact] + public void HandleResponse_Capture_Default_SkipCookiesAndHeaders() + { + // Arrange + var options = new SentryOptions + { + CaptureFailedRequests = true, + SendDefaultPii = false + }; + var sut = GetSut(options); + + var response = InternalServerErrorResponse(); // This is in the range + response.RequestMessage = new HttpRequestMessage(HttpMethod.Post, "http://foo/bar"); + response.Headers.Add("myHeader", "myValue"); + response.Content = new StringContent("Something broke!", Encoding.UTF8, "text/plain"); + response.Headers.Add("Cookie", "myCookie=myValue"); + + // Act + SentryEvent @event = null; + _hub.CaptureEvent(Arg.Do(e => @event = e)); + sut.HandleResponse(response); + + // Assert + using (new AssertionScope()) + { + @event.Should().NotBeNull(); + + // Cookies and headers are not captured + @event.Contexts.Response.Headers.Should().BeNullOrEmpty(); + @event.Contexts.Response.Cookies.Should().BeNullOrEmpty(); + } + } +} diff --git a/test/Sentry.Tests/SentryHttpMessageHandlerTests.cs b/test/Sentry.Tests/SentryHttpMessageHandlerTests.cs index e8f7c3d2d5..ed4bc8483f 100644 --- a/test/Sentry.Tests/SentryHttpMessageHandlerTests.cs +++ b/test/Sentry.Tests/SentryHttpMessageHandlerTests.cs @@ -31,6 +31,7 @@ public async Task SendAsync_SentryTraceHeaderNotSet_SetsHeader_WhenUrlMatchesPro { // Arrange var hub = Substitute.For(); + var failedRequestHandler = Substitute.For(); var options = new SentryOptions { TracePropagationTargets = new List @@ -43,7 +44,7 @@ public async Task SendAsync_SentryTraceHeaderNotSet_SetsHeader_WhenUrlMatchesPro SentryTraceHeader.Parse("75302ac48a024bde9a3b3734a82e36c8-1000000000000000-0")); using var innerHandler = new RecordingHttpMessageHandler(new FakeHttpMessageHandler()); - using var sentryHandler = new SentryHttpMessageHandler(innerHandler, hub, options); + using var sentryHandler = new SentryHttpMessageHandler(innerHandler, hub, options, failedRequestHandler); using var client = new HttpClient(sentryHandler); // Act @@ -62,6 +63,7 @@ public async Task SendAsync_SentryTraceHeaderNotSet_DoesntSetHeader_WhenUrlDoesn { // Arrange var hub = Substitute.For(); + var failedRequestHandler = Substitute.For(); var options = new SentryOptions { TracePropagationTargets = new List @@ -74,7 +76,7 @@ public async Task SendAsync_SentryTraceHeaderNotSet_DoesntSetHeader_WhenUrlDoesn SentryTraceHeader.Parse("75302ac48a024bde9a3b3734a82e36c8-1000000000000000-0")); using var innerHandler = new RecordingHttpMessageHandler(new FakeHttpMessageHandler()); - using var sentryHandler = new SentryHttpMessageHandler(innerHandler, hub, options); + using var sentryHandler = new SentryHttpMessageHandler(innerHandler, hub, options, failedRequestHandler); using var client = new HttpClient(sentryHandler); // Act @@ -198,6 +200,7 @@ public async Task SendAsync_Executed_BreadcrumbCreated() // Assert Assert.Equal(expectedType, breadcrumbGenerated.Type); Assert.Equal(expectedCategory, breadcrumbGenerated.Category); + Assert.NotNull(breadcrumbGenerated.Data); Assert.True(breadcrumbGenerated.Data.ContainsKey(urlKey)); Assert.Equal(expectedBreadcrumbData[urlKey], breadcrumbGenerated.Data[urlKey]); @@ -208,4 +211,24 @@ public async Task SendAsync_Executed_BreadcrumbCreated() Assert.True(breadcrumbGenerated.Data.ContainsKey(statusKey)); Assert.Equal(expectedBreadcrumbData[statusKey], breadcrumbGenerated.Data[statusKey]); } + + [Fact] + public async Task SendAsync_Executed_FailedRequestsCaptured() + { + // Arrange + var hub = Substitute.For(); + var failedRequestHandler = Substitute.For(); + var options = new SentryOptions(); + var url = "https://localhost/"; + + using var sentryHandler = new SentryHttpMessageHandler(hub, options, failedRequestHandler); + sentryHandler.InnerHandler = new FakeHttpMessageHandler(); // No reason to reach the Internet here + using var client = new HttpClient(sentryHandler); + + // Act + await client.GetAsync(url); + + // Assert + failedRequestHandler.Received(1).HandleResponse(Arg.Any()); + } } diff --git a/test/Sentry.Tests/SentryOptionsTests.cs b/test/Sentry.Tests/SentryOptionsTests.cs index aeecae731b..a1a187a1ab 100644 --- a/test/Sentry.Tests/SentryOptionsTests.cs +++ b/test/Sentry.Tests/SentryOptionsTests.cs @@ -84,4 +84,25 @@ public void EnableTracing_WhenTrue() Assert.True(sut.EnableTracing); Assert.Equal(1.0, sut.TracesSampleRate); } + + [Fact] + public void CaptureFailedRequests_ByDefault_IsFalse() + { + var sut = new SentryOptions(); + Assert.False(sut.CaptureFailedRequests, "CaptureFailedRequests should be false by default to protect potentially PII (Privately Identifiable Information)"); + } + + [Fact] + public void FailedRequestStatusCodes_ByDefault_ShouldIncludeServerErrors() + { + var sut = new SentryOptions(); + Assert.Contains((500, 599), sut.FailedRequestStatusCodes); + } + + [Fact] + public void FailedRequestTargets_ByDefault_MatchesAnyUrl() + { + var sut = new SentryOptions(); + Assert.Contains(".*", sut.FailedRequestTargets); + } } diff --git a/test/Sentry.Tests/SubstringOrRegexPatternTests.cs b/test/Sentry.Tests/SubstringOrRegexPatternTests.cs new file mode 100644 index 0000000000..f8cd8d1cee --- /dev/null +++ b/test/Sentry.Tests/SubstringOrRegexPatternTests.cs @@ -0,0 +1,94 @@ +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); + } +} diff --git a/test/Sentry.Tests/TracePropagationTargetTests.cs b/test/Sentry.Tests/TracePropagationTargetTests.cs index 1c70914414..fca8772b3d 100644 --- a/test/Sentry.Tests/TracePropagationTargetTests.cs +++ b/test/Sentry.Tests/TracePropagationTargetTests.cs @@ -2,96 +2,6 @@ namespace Sentry.Tests; public class TracePropagationTargetTests { - [Fact] - public void Substring_Matches() - { - var target = new TracePropagationTarget("cde"); - var isMatch = target.IsMatch("abcdef"); - Assert.True(isMatch); - } - - [Fact] - public void Substring_Doesnt_Match() - { - var target = new TracePropagationTarget("xyz"); - var isMatch = target.IsMatch("abcdef"); - Assert.False(isMatch); - } - - [Fact] - public void Substring_Matches_CaseInsensitive_ByDefault() - { - var target = new TracePropagationTarget("cDe"); - var isMatch = target.IsMatch("ABCdEF"); - Assert.True(isMatch); - } - - [Fact] - public void Substring_Matches_CaseSensitive() - { - var target = new TracePropagationTarget("CdE", StringComparison.Ordinal); - var isMatch = target.IsMatch("ABCdEF"); - Assert.True(isMatch); - } - - [Fact] - public void Substring_Doesnt_Match_WhenCaseSensitive() - { - var target = new TracePropagationTarget("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 TracePropagationTarget(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 TracePropagationTarget(regex); - var isMatch = target.IsMatch("abcdef"); - Assert.False(isMatch); - } - - [Fact] - public void Regex_Pattern_Matches() - { - var target = new TracePropagationTarget("^abc.*ghi$"); - var isMatch = target.IsMatch("abcdefghi"); - Assert.True(isMatch); - } - - [Fact] - public void Regex_Pattern_Matches_CaseInsensitive_ByDefault() - { - var target = new TracePropagationTarget("^abc.*ghi$"); - var isMatch = target.IsMatch("aBcDeFgHi"); - Assert.True(isMatch); - } - - [Fact] - public void Regex_Pattern_Matches_CaseSensitive() - { - var target = new TracePropagationTarget("^aBc.*gHi$", StringComparison.Ordinal); - var isMatch = target.IsMatch("aBcDeFgHi"); - Assert.True(isMatch); - } - - [Fact] - public void Regex_Pattern_Doesnt_Match_WhenCaseSensitive() - { - var target = new TracePropagationTarget("^abc.*ghi$", StringComparison.Ordinal); - var isMatch = target.IsMatch("aBcDeFgHi"); - Assert.False(isMatch); - } - [Fact] public void SentryOptions_TracePropagationTargets_DefaultAll() { @@ -135,9 +45,9 @@ public void SentryOptions_TracePropagationTargets_DefaultPropagatesAll() { var options = new SentryOptions(); - var result1 = options.TracePropagationTargets.ShouldPropagateTrace("foo"); - var result2 = options.TracePropagationTargets.ShouldPropagateTrace(""); - var result3 = options.TracePropagationTargets.ShouldPropagateTrace(null!); + var result1 = options.TracePropagationTargets.ContainsMatch("foo"); + var result2 = options.TracePropagationTargets.ContainsMatch(""); + var result3 = options.TracePropagationTargets.ContainsMatch(null!); Assert.True(result1); Assert.True(result2); @@ -152,9 +62,9 @@ public void SentryOptions_TracePropagationTargets_EmptyPropagatesNone() TracePropagationTargets = new List() }; - var result1 = options.TracePropagationTargets.ShouldPropagateTrace("foo"); - var result2 = options.TracePropagationTargets.ShouldPropagateTrace(""); - var result3 = options.TracePropagationTargets.ShouldPropagateTrace(null!); + var result1 = options.TracePropagationTargets.ContainsMatch("foo"); + var result2 = options.TracePropagationTargets.ContainsMatch(""); + var result3 = options.TracePropagationTargets.ContainsMatch(null!); Assert.False(result1); Assert.False(result2); @@ -174,7 +84,7 @@ public void SentryOptions_TracePropagationTargets_OneMatchPropagates() } }; - var result = options.TracePropagationTargets.ShouldPropagateTrace("http://localhost/abc/123"); + var result = options.TracePropagationTargets.ContainsMatch("http://localhost/abc/123"); Assert.True(result); } @@ -191,7 +101,7 @@ public void SentryOptions_TracePropagationTargets_MultipleMatchesPropagates() } }; - var result = options.TracePropagationTargets.ShouldPropagateTrace("http://localhost/foo/123"); + var result = options.TracePropagationTargets.ContainsMatch("http://localhost/foo/123"); Assert.True(result); } @@ -208,7 +118,7 @@ public void SentryOptions_TracePropagationTargets_NoMatchesDoesntPropagates() } }; - var result = options.TracePropagationTargets.ShouldPropagateTrace("https://sentry.io/abc/123"); + var result = options.TracePropagationTargets.ContainsMatch("https://sentry.io/abc/123"); Assert.False(result); } } From 171d6ccb34e87384f300fbc4750111f3f9f4d608 Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Fri, 28 Apr 2023 15:49:33 -0600 Subject: [PATCH 012/142] Support synchronous `HttpClient.Send` in `SentryHttpMessageHandler` (#2336) --- CHANGELOG.md | 1 + src/Sentry/SentryHttpMessageHandler.cs | 156 ++++++----- ...tryHttpMessageHandlerBuilderFilterTests.cs | 9 +- test/Sentry.Testing/FakeHttpMessageHandler.cs | 16 +- test/Sentry.Testing/HttpClientExtensions.cs | 46 +++- .../RecordingHttpMessageHandler.cs | 21 +- ...piApprovalTests.Run.DotNet6_0.verified.txt | 1 + ...piApprovalTests.Run.DotNet7_0.verified.txt | 1 + .../SentryHttpMessageHandlerTests.cs | 251 +++++++++++++++++- 9 files changed, 408 insertions(+), 94 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 977dd4c692..b08bba12c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ - Restore `System.Reflection.Metadata` dependency for .NET Core 3 ([#2302](https://github.com/getsentry/sentry-dotnet/pull/2302)) - Capture open transactions on disabled hubs ([#2319](https://github.com/getsentry/sentry-dotnet/pull/2319)) - Remove session breadcrumbs ([#2333](https://github.com/getsentry/sentry-dotnet/pull/2333)) +- Support synchronous `HttpClient.Send` in `SentryHttpMessageHandler` ([#2336](https://github.com/getsentry/sentry-dotnet/pull/2336)) - Fix ASP.NET Core issue with missing context when using capture methods that configure scope ([#2339](https://github.com/getsentry/sentry-dotnet/pull/2339)) ### Dependencies diff --git a/src/Sentry/SentryHttpMessageHandler.cs b/src/Sentry/SentryHttpMessageHandler.cs index 4744287626..78ef415f52 100644 --- a/src/Sentry/SentryHttpMessageHandler.cs +++ b/src/Sentry/SentryHttpMessageHandler.cs @@ -13,62 +13,104 @@ public class SentryHttpMessageHandler : DelegatingHandler private readonly ISentryFailedRequestHandler? _failedRequestHandler; /// - /// Initializes an instance of . + /// Constructs an instance of . /// - public SentryHttpMessageHandler(IHub hub) - { - _hub = hub; - _options = hub.GetSentryOptions(); - if (_options != null) - { - _failedRequestHandler = new SentryFailedRequestHandler(_hub, _options); - } - } + public SentryHttpMessageHandler() + : this(default, default, default) { } + + /// + /// Constructs an instance of . + /// + /// An inner message handler to delegate calls to. + public SentryHttpMessageHandler(HttpMessageHandler innerHandler) + : this(default, default, innerHandler) { } - internal SentryHttpMessageHandler(IHub hub, SentryOptions options, ISentryFailedRequestHandler? failedRequestHandler = null) + /// + /// Constructs an instance of . + /// + /// The Sentry hub. + public SentryHttpMessageHandler(IHub hub) + : this(hub, default) { - _hub = hub; - _options = options; - _failedRequestHandler = failedRequestHandler; } /// - /// Initializes an instance of . + /// Constructs an instance of . /// + /// An inner message handler to delegate calls to. + /// The Sentry hub. public SentryHttpMessageHandler(HttpMessageHandler innerHandler, IHub hub) - : this(hub) + : this(hub, default, innerHandler) { - InnerHandler = innerHandler; } - internal SentryHttpMessageHandler(HttpMessageHandler innerHandler, IHub hub, SentryOptions options, ISentryFailedRequestHandler? failedRequestHandler = null) - : this(hub, options, failedRequestHandler) + internal SentryHttpMessageHandler(IHub? hub, SentryOptions? options, HttpMessageHandler? innerHandler = default, ISentryFailedRequestHandler? failedRequestHandler = null) { - InnerHandler = innerHandler; - } + _hub = hub ?? HubAdapter.Instance; + _options = options ?? _hub.GetSentryOptions(); + _failedRequestHandler = failedRequestHandler; - /// - /// Initializes an instance of . - /// - public SentryHttpMessageHandler(HttpMessageHandler innerHandler) - : this(innerHandler, HubAdapter.Instance) { } + // Only assign the inner handler if it is supplied. We can't assign null or it will throw. + // We also cannot assign a default value here, or it will throw when used with HttpMessageHandlerBuilderFilter. + if (innerHandler is not null) + { + InnerHandler = innerHandler; + } - /// - /// Initializes an instance of . - /// - public SentryHttpMessageHandler() - : this(HubAdapter.Instance) { } + // Use the default failed request handler if none was supplied - but options is required. + if (_failedRequestHandler == null && _options != null) + { + _failedRequestHandler = new SentryFailedRequestHandler(_hub, _options); + } + } /// protected override async Task SendAsync( HttpRequestMessage request, CancellationToken cancellationToken) { - // Prevent null reference exception in the following call - // in case the user didn't set an inner handler. + var (span, method, url) = ProcessRequest(request); + + try + { + var response = await base.SendAsync(request, cancellationToken).ConfigureAwait(false); + HandleResponse(response, span, method, url); + return response; + } + catch (Exception ex) + { + span?.Finish(ex); + throw; + } + } + +#if NET5_0_OR_GREATER + /// + protected override HttpResponseMessage Send(HttpRequestMessage request, CancellationToken cancellationToken) + { + var (span, method, url) = ProcessRequest(request); + + try + { + var response = base.Send(request, cancellationToken); + HandleResponse(response, span, method, url); + return response; + } + catch (Exception ex) + { + span?.Finish(ex); + throw; + } + } +#endif + + private (ISpan? Span, string Method, string Url) ProcessRequest(HttpRequestMessage request) + { + // Assign a default inner handler for convenience the first time this is used. + // We can't do this in a constructor, or it will throw when used with HttpMessageHandlerBuilderFilter. InnerHandler ??= new HttpClientHandler(); - var requestMethod = request.Method.Method.ToUpperInvariant(); + var method = request.Method.Method.ToUpperInvariant(); var url = request.RequestUri?.ToString() ?? string.Empty; if (_options?.TracePropagationTargets.ContainsMatch(url) is true or null) @@ -79,36 +121,28 @@ protected override async Task SendAsync( // Start a span that tracks this request // (may be null if transaction is not set on the scope) - var span = _hub.GetSpan()?.StartChild( - "http.client", - // e.g. "GET https://example.com" - $"{requestMethod} {url}"); - - try - { - var response = await base.SendAsync(request, cancellationToken).ConfigureAwait(false); - - var breadcrumbData = new Dictionary - { - { "url", url }, - { "method", requestMethod }, - { "status_code", ((int)response.StatusCode).ToString() } - }; - _hub.AddBreadcrumb(string.Empty, "http", "http", breadcrumbData); + // e.g. "GET https://example.com" + var span = _hub.GetSpan()?.StartChild("http.client", $"{method} {url}"); - // Create events for failed requests - _failedRequestHandler?.HandleResponse(response); - - // This will handle unsuccessful status codes as well - span?.Finish(SpanStatusConverter.FromHttpStatusCode(response.StatusCode)); + return (span, method, url); + } - return response; - } - catch (Exception ex) + private void HandleResponse(HttpResponseMessage response, ISpan? span, string method, string url) + { + var breadcrumbData = new Dictionary { - span?.Finish(ex); - throw; - } + {"url", url}, + {"method", method}, + {"status_code", ((int) response.StatusCode).ToString()} + }; + _hub.AddBreadcrumb(string.Empty, "http", "http", breadcrumbData); + + // Create events for failed requests + _failedRequestHandler?.HandleResponse(response); + + // This will handle unsuccessful status codes as well + var status = SpanStatusConverter.FromHttpStatusCode(response.StatusCode); + span?.Finish(status); } private void AddSentryTraceHeader(HttpRequestMessage request) diff --git a/test/Sentry.AspNetCore.Tests/SentryHttpMessageHandlerBuilderFilterTests.cs b/test/Sentry.AspNetCore.Tests/SentryHttpMessageHandlerBuilderFilterTests.cs index b05b493e1a..b326904d3a 100644 --- a/test/Sentry.AspNetCore.Tests/SentryHttpMessageHandlerBuilderFilterTests.cs +++ b/test/Sentry.AspNetCore.Tests/SentryHttpMessageHandlerBuilderFilterTests.cs @@ -21,6 +21,8 @@ private class RecordingHandlerBuilderFilter : IHttpMessageHandlerBuilderFilter public Action Configure(Action next) => handlerBuilder => { + // Set the fake handler to prevent outgoing HTTP requests in this test. + handlerBuilder.PrimaryHandler = new FakeHttpMessageHandler(); handlerBuilder.AdditionalHandlers.Add(_handler); next(handlerBuilder); }; @@ -34,7 +36,7 @@ public async Task Generated_client_sends_Sentry_trace_header_automatically() // Will use this to record outgoing requests using var recorder = new RecordingHttpMessageHandler(); - var hub = new Internal.Hub(new SentryOptions + var hub = new Hub(new SentryOptions { Dsn = ValidDsn }); @@ -65,11 +67,6 @@ public async Task Generated_client_sends_Sentry_trace_header_automatically() .GetRequiredService() .CreateClient(); - // The framework setup pipeline would end up adding an HttpClientHandler and this test - // would require access to the Internet. So overriding it here - // so the request stops at our stub: - recorder.InnerHandler = new FakeHttpMessageHandler(); - await httpClient.GetAsync("https://fake.tld"); }); }); diff --git a/test/Sentry.Testing/FakeHttpMessageHandler.cs b/test/Sentry.Testing/FakeHttpMessageHandler.cs index 812923752b..8440607dfa 100644 --- a/test/Sentry.Testing/FakeHttpMessageHandler.cs +++ b/test/Sentry.Testing/FakeHttpMessageHandler.cs @@ -12,13 +12,11 @@ public FakeHttpMessageHandler(Func getResponse) public FakeHttpMessageHandler() { } - protected override Task SendAsync( - HttpRequestMessage request, - CancellationToken cancellationToken) - { - return Task.FromResult( - _getResponse is not null - ? _getResponse(request) - : new HttpResponseMessage(HttpStatusCode.OK)); - } + protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) => + Task.FromResult(_getResponse?.Invoke(request) ?? new HttpResponseMessage(HttpStatusCode.OK)); + +#if NET5_0_OR_GREATER + protected override HttpResponseMessage Send(HttpRequestMessage request, CancellationToken cancellationToken) => + _getResponse?.Invoke(request) ?? new HttpResponseMessage(HttpStatusCode.OK); +#endif } diff --git a/test/Sentry.Testing/HttpClientExtensions.cs b/test/Sentry.Testing/HttpClientExtensions.cs index 4c515c34c3..d262ed7930 100644 --- a/test/Sentry.Testing/HttpClientExtensions.cs +++ b/test/Sentry.Testing/HttpClientExtensions.cs @@ -23,7 +23,8 @@ public static IEnumerable GetMessageHandlers(this HttpClient } while (handler != null); } - public static async Task CloneAsync(this HttpRequestMessage source) + public static async Task CloneAsync(this HttpRequestMessage source, + CancellationToken cancellationToken) { var clone = new HttpRequestMessage(source.Method, source.RequestUri) { Version = source.Version }; @@ -38,7 +39,11 @@ public static async Task CloneAsync(this HttpRequestMessage { var cloneContentStream = new MemoryStream(); +#if NET5_0_OR_GREATER + await source.Content.CopyToAsync(cloneContentStream, cancellationToken).ConfigureAwait(false); +#else await source.Content.CopyToAsync(cloneContentStream).ConfigureAwait(false); +#endif cloneContentStream.Position = 0; clone.Content = new StreamContent(cloneContentStream); @@ -52,4 +57,43 @@ public static async Task CloneAsync(this HttpRequestMessage return clone; } + +#if NET5_0_OR_GREATER + public static HttpRequestMessage Clone(this HttpRequestMessage source, CancellationToken cancellationToken) + { + var clone = new HttpRequestMessage(source.Method, source.RequestUri) { Version = source.Version }; + + // Headers + foreach (var (key, value) in source.Headers) + { + clone.Headers.TryAddWithoutValidation(key, value); + } + + // Content + if (source.Content != null) + { + var cloneContentStream = new MemoryStream(); + + source.Content.CopyTo(cloneContentStream, default, cancellationToken); + cloneContentStream.Position = 0; + + clone.Content = new StreamContent(cloneContentStream); + + // Content headers + foreach (var (key, value) in source.Content.Headers) + { + clone.Content.Headers.Add(key, value); + } + } + + return clone; + } + + public static HttpResponseMessage Get(this HttpClient client, string requestUri, + CancellationToken cancellationToken = default) + { + var message = new HttpRequestMessage(HttpMethod.Get, requestUri); + return client.Send(message, cancellationToken); + } +#endif } diff --git a/test/Sentry.Testing/RecordingHttpMessageHandler.cs b/test/Sentry.Testing/RecordingHttpMessageHandler.cs index bf4024130e..0d68e16ce6 100644 --- a/test/Sentry.Testing/RecordingHttpMessageHandler.cs +++ b/test/Sentry.Testing/RecordingHttpMessageHandler.cs @@ -6,21 +6,22 @@ public class RecordingHttpMessageHandler : DelegatingHandler public RecordingHttpMessageHandler() { } - public RecordingHttpMessageHandler(HttpMessageHandler innerHandler) => - InnerHandler = innerHandler; + public RecordingHttpMessageHandler(HttpMessageHandler innerHandler) => InnerHandler = innerHandler; - protected override async Task SendAsync( - HttpRequestMessage request, - CancellationToken cancellationToken) + protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { - // Clone the request to avoid ObjectDisposedException - _requests.Add(await request.CloneAsync()); - - InnerHandler ??= new FakeHttpMessageHandler(); - + _requests.Add(await request.CloneAsync(cancellationToken)); return await base.SendAsync(request, cancellationToken); } +#if NET5_0_OR_GREATER + protected override HttpResponseMessage Send(HttpRequestMessage request, CancellationToken cancellationToken) + { + _requests.Add(request.Clone(cancellationToken)); + return base.Send(request, cancellationToken); + } +#endif + public IReadOnlyList GetRequests() => _requests.ToArray(); protected override void Dispose(bool disposing) diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt index bc4762d774..9816e4fab2 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt @@ -514,6 +514,7 @@ namespace Sentry public SentryHttpMessageHandler(Sentry.IHub hub) { } public SentryHttpMessageHandler(System.Net.Http.HttpMessageHandler innerHandler) { } public SentryHttpMessageHandler(System.Net.Http.HttpMessageHandler innerHandler, Sentry.IHub hub) { } + 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 readonly struct SentryId : Sentry.IJsonSerializable, System.IEquatable diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt index bc4762d774..9816e4fab2 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt @@ -514,6 +514,7 @@ namespace Sentry public SentryHttpMessageHandler(Sentry.IHub hub) { } public SentryHttpMessageHandler(System.Net.Http.HttpMessageHandler innerHandler) { } public SentryHttpMessageHandler(System.Net.Http.HttpMessageHandler innerHandler, Sentry.IHub hub) { } + 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 readonly struct SentryId : Sentry.IJsonSerializable, System.IEquatable diff --git a/test/Sentry.Tests/SentryHttpMessageHandlerTests.cs b/test/Sentry.Tests/SentryHttpMessageHandlerTests.cs index ed4bc8483f..4786527726 100644 --- a/test/Sentry.Tests/SentryHttpMessageHandlerTests.cs +++ b/test/Sentry.Tests/SentryHttpMessageHandlerTests.cs @@ -1,5 +1,10 @@ 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] @@ -44,7 +49,7 @@ public async Task SendAsync_SentryTraceHeaderNotSet_SetsHeader_WhenUrlMatchesPro SentryTraceHeader.Parse("75302ac48a024bde9a3b3734a82e36c8-1000000000000000-0")); using var innerHandler = new RecordingHttpMessageHandler(new FakeHttpMessageHandler()); - using var sentryHandler = new SentryHttpMessageHandler(innerHandler, hub, options, failedRequestHandler); + using var sentryHandler = new SentryHttpMessageHandler(hub, options, innerHandler, failedRequestHandler); using var client = new HttpClient(sentryHandler); // Act @@ -76,7 +81,7 @@ public async Task SendAsync_SentryTraceHeaderNotSet_DoesntSetHeader_WhenUrlDoesn SentryTraceHeader.Parse("75302ac48a024bde9a3b3734a82e36c8-1000000000000000-0")); using var innerHandler = new RecordingHttpMessageHandler(new FakeHttpMessageHandler()); - using var sentryHandler = new SentryHttpMessageHandler(innerHandler, hub, options, failedRequestHandler); + using var sentryHandler = new SentryHttpMessageHandler(hub, options, innerHandler, failedRequestHandler); using var client = new HttpClient(sentryHandler); // Act @@ -183,9 +188,9 @@ public async Task SendAsync_Executed_BreadcrumbCreated() var statusKey = "status_code"; var expectedBreadcrumbData = new Dictionary { - { urlKey, url }, - { methodKey, "GET" }, - { statusKey, "200" } + {urlKey, url}, + {methodKey, "GET"}, + {statusKey, "200"} }; var expectedType = "http"; var expectedCategory = "http"; @@ -221,8 +226,8 @@ public async Task SendAsync_Executed_FailedRequestsCaptured() var options = new SentryOptions(); var url = "https://localhost/"; - using var sentryHandler = new SentryHttpMessageHandler(hub, options, failedRequestHandler); - sentryHandler.InnerHandler = new FakeHttpMessageHandler(); // No reason to reach the Internet here + using var innerHandler = new FakeHttpMessageHandler(); + using var sentryHandler = new SentryHttpMessageHandler(hub, options, innerHandler, failedRequestHandler); using var client = new HttpClient(sentryHandler); // Act @@ -231,4 +236,236 @@ public async Task SendAsync_Executed_FailedRequestsCaptured() // Assert failedRequestHandler.Received(1).HandleResponse(Arg.Any()); } + +#if NET5_0_OR_GREATER + [Fact] + public void Send_SentryTraceHeaderNotSet_SetsHeader_ByDefault() + { + // Arrange + var hub = Substitute.For(); + + hub.GetTraceHeader().ReturnsForAnyArgs( + SentryTraceHeader.Parse("75302ac48a024bde9a3b3734a82e36c8-1000000000000000-0")); + + using var innerHandler = new RecordingHttpMessageHandler(new FakeHttpMessageHandler()); + using var sentryHandler = new SentryHttpMessageHandler(innerHandler, hub); + using var client = new HttpClient(sentryHandler); + + // Act + client.Get("https://localhost/"); + + using var request = innerHandler.GetRequests().Single(); + + // Assert + request.Headers.Should().Contain(h => + h.Key == "sentry-trace" && + string.Concat(h.Value) == "75302ac48a024bde9a3b3734a82e36c8-1000000000000000-0"); + } + + [Fact] + public void Send_SentryTraceHeaderNotSet_SetsHeader_WhenUrlMatchesPropagationOptions() + { + // 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")); + + using var innerHandler = new RecordingHttpMessageHandler(new FakeHttpMessageHandler()); + using var sentryHandler = new SentryHttpMessageHandler(hub, options, innerHandler, failedRequestHandler); + using var client = new HttpClient(sentryHandler); + + // Act + client.Get("https://localhost/"); + + using var request = innerHandler.GetRequests().Single(); + + // Assert + request.Headers.Should().Contain(h => + h.Key == "sentry-trace" && + string.Concat(h.Value) == "75302ac48a024bde9a3b3734a82e36c8-1000000000000000-0"); + } + + [Fact] + public void Send_SentryTraceHeaderNotSet_DoesntSetHeader_WhenUrlDoesntMatchesPropagationOptions() + { + // Arrange + var hub = Substitute.For(); + var failedRequestHandler = Substitute.For(); + var options = new SentryOptions + { + TracePropagationTargets = new List + { + new("foo") + } + }; + + hub.GetTraceHeader().ReturnsForAnyArgs( + SentryTraceHeader.Parse("75302ac48a024bde9a3b3734a82e36c8-1000000000000000-0")); + + using var innerHandler = new RecordingHttpMessageHandler(new FakeHttpMessageHandler()); + using var sentryHandler = new SentryHttpMessageHandler(hub, options, innerHandler, failedRequestHandler); + using var client = new HttpClient(sentryHandler); + + // Act + client.Get("https://localhost/"); + + using var request = innerHandler.GetRequests().Single(); + + // Assert + request.Headers.Should().NotContain(h => h.Key == "sentry-trace"); + } + + [Fact] + public void Send_SentryTraceHeaderAlreadySet_NotOverwritten() + { + // Arrange + var hub = Substitute.For(); + + hub.GetTraceHeader().ReturnsForAnyArgs( + SentryTraceHeader.Parse("75302ac48a024bde9a3b3734a82e36c8-1000000000000000-0")); + + using var innerHandler = new RecordingHttpMessageHandler(new FakeHttpMessageHandler()); + using var sentryHandler = new SentryHttpMessageHandler(innerHandler, hub); + using var client = new HttpClient(sentryHandler); + + client.DefaultRequestHeaders.Add("sentry-trace", "foobar"); + + // Act + client.Get("https://localhost/"); + + using var request = innerHandler.GetRequests().Single(); + + // Assert + request.Headers.Should().Contain(h => + h.Key == "sentry-trace" && + string.Concat(h.Value) == "foobar"); + } + + [Fact] + public void Send_TransactionOnScope_StartsNewSpan() + { + // Arrange + var hub = Substitute.For(); + + var transaction = new TransactionTracer( + hub, + "foo", + "bar"); + + hub.GetSpan().ReturnsForAnyArgs(transaction); + + using var innerHandler = new FakeHttpMessageHandler(); + using var sentryHandler = new SentryHttpMessageHandler(innerHandler, hub); + using var client = new HttpClient(sentryHandler); + + // Act + client.Get("https://localhost/"); + + // Assert + transaction.Spans.Should().Contain(span => + span.Operation == "http.client" && + span.Description == "GET https://localhost/" && + span.IsFinished); + } + + [Fact] + public void Send_ExceptionThrown_ExceptionLinkedToSpan() + { + // Arrange + var hub = Substitute.For(); + + var transaction = new TransactionTracer( + hub, + "foo", + "bar"); + + hub.GetSpan().ReturnsForAnyArgs(transaction); + + var exception = new Exception(); + + using var innerHandler = new FakeHttpMessageHandler(() => throw exception); + using var sentryHandler = new SentryHttpMessageHandler(innerHandler, hub); + using var client = new HttpClient(sentryHandler); + + // Act + Assert.Throws(() => client.Get("https://localhost/")); + + // Assert + hub.Received(1).BindException(exception, Arg.Any()); // second argument is an implicitly created span + } + + [Fact] + public void Send_Executed_BreadcrumbCreated() + { + // Arrange + var scope = new Scope(); + var hub = Substitute.For(); + hub.When(h => h.ConfigureScope(Arg.Any>())) + .Do(c => c.Arg>()(scope)); + + var url = "https://localhost/"; + + var urlKey = "url"; + var methodKey = "method"; + var statusKey = "status_code"; + var expectedBreadcrumbData = new Dictionary + { + {urlKey, url}, + {methodKey, "GET"}, + {statusKey, "200"} + }; + var expectedType = "http"; + var expectedCategory = "http"; + using var sentryHandler = new SentryHttpMessageHandler(hub); + sentryHandler.InnerHandler = new FakeHttpMessageHandler(); // No reason to reach the Internet here + using var client = new HttpClient(sentryHandler); + + // Act + client.Get(url); + var breadcrumbGenerated = scope.Breadcrumbs.First(); + + // Assert + Assert.Equal(expectedType, breadcrumbGenerated.Type); + Assert.Equal(expectedCategory, breadcrumbGenerated.Category); + Assert.NotNull(breadcrumbGenerated.Data); + + Assert.True(breadcrumbGenerated.Data.ContainsKey(urlKey)); + Assert.Equal(expectedBreadcrumbData[urlKey], breadcrumbGenerated.Data[urlKey]); + + Assert.True(breadcrumbGenerated.Data.ContainsKey(methodKey)); + Assert.Equal(expectedBreadcrumbData[methodKey], breadcrumbGenerated.Data[methodKey]); + + Assert.True(breadcrumbGenerated.Data.ContainsKey(statusKey)); + Assert.Equal(expectedBreadcrumbData[statusKey], breadcrumbGenerated.Data[statusKey]); + } + + [Fact] + public void Send_Executed_FailedRequestsCaptured() + { + // Arrange + var hub = Substitute.For(); + var failedRequestHandler = Substitute.For(); + var options = new SentryOptions(); + var url = "https://localhost/"; + + using var innerHandler = new FakeHttpMessageHandler(); + using var sentryHandler = new SentryHttpMessageHandler(hub, options, innerHandler, failedRequestHandler); + using var client = new HttpClient(sentryHandler); + + // Act + client.Get(url); + + // Assert + failedRequestHandler.Received(1).HandleResponse(Arg.Any()); + } +#endif } From 752b35c9abd27d4bc984f8c86e294a258eef741b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 28 Apr 2023 20:11:15 -0700 Subject: [PATCH 013/142] chore: update scripts/update-cli.ps1 to 2.17.5 (#2345) 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 b08bba12c4..0f3102a347 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,9 +22,9 @@ ### Dependencies -- Bump CLI from v2.17.0 to v2.17.4 ([#2298](https://github.com/getsentry/sentry-dotnet/pull/2298), [#2318](https://github.com/getsentry/sentry-dotnet/pull/2318), [#2321](https://github.com/getsentry/sentry-dotnet/pull/2321)) - - [changelog](https://github.com/getsentry/sentry-cli/blob/master/CHANGELOG.md#2174) - - [diff](https://github.com/getsentry/sentry-cli/compare/2.17.0...2.17.4) +- Bump CLI from v2.17.0 to v2.17.5 ([#2298](https://github.com/getsentry/sentry-dotnet/pull/2298), [#2318](https://github.com/getsentry/sentry-dotnet/pull/2318), [#2321](https://github.com/getsentry/sentry-dotnet/pull/2321), [#2345](https://github.com/getsentry/sentry-dotnet/pull/2345)) + - [changelog](https://github.com/getsentry/sentry-cli/blob/master/CHANGELOG.md#2175) + - [diff](https://github.com/getsentry/sentry-cli/compare/2.17.0...2.17.5) - Bump Cocoa SDK from v8.4.0 to v8.5.0 ([#2310](https://github.com/getsentry/sentry-dotnet/pull/2310)) - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#850) - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.4.0...8.5.0) diff --git a/Directory.Build.props b/Directory.Build.props index c5071f539f..b35753aac0 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -86,7 +86,7 @@ - 2.17.4 + 2.17.5 $(MSBuildThisFileDirectory)tools\sentry-cli\$(SentryCLIVersion)\ diff --git a/src/Sentry/Sentry.csproj b/src/Sentry/Sentry.csproj index 37291b8f99..83a535c652 100644 --- a/src/Sentry/Sentry.csproj +++ b/src/Sentry/Sentry.csproj @@ -94,25 +94,25 @@ + Include="sentry-cli-Darwin-arm64" FileHash="ed1e4434ef62374ebc144cd7d6648fb3cc3222294390c90f27b092c30a064e07" /> + Include="sentry-cli-Darwin-x86_64" FileHash="c20df0c10eecedd0eaf14bbdc052c1dc2e999797c896a40e3ae9390223bd4d4f" /> + Include="sentry-cli-Linux-aarch64" FileHash="2071c03f870fcce6f9e82cefcff004e92f6d1af250a31d157ba921c671a5f9ec" /> + Include="sentry-cli-Linux-i686" FileHash="142824382e394f06a1a9b0bcb2276dd29fed17310fc01a24706724e6979749ff" /> + Include="sentry-cli-Linux-x86_64" FileHash="8200b8f0831535d5c21adfde947ca6d30930619eae36a650cbcf1005c68cd6dd" /> + Include="sentry-cli-Windows-i686.exe" FileHash="718209c411305dc4d86676dba6bef09e7c69bce03ce457c09f3b05ae08e1126e" /> + Include="sentry-cli-Windows-x86_64.exe" FileHash="4ee6d34cce58bed122acc1602d327c28c9b82fa9e64bcc73100012e644e6537a" /> From f54359a7355c5f1fbb33fb8590d133d5c40881ae Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 28 Apr 2023 21:20:04 -0700 Subject: [PATCH 014/142] chore: update scripts/update-java.ps1 to 6.18.1 (#2343) Co-authored-by: GitHub --- CHANGELOG.md | 6 +++--- src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f3102a347..147ecc3848 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,9 +28,9 @@ - Bump Cocoa SDK from v8.4.0 to v8.5.0 ([#2310](https://github.com/getsentry/sentry-dotnet/pull/2310)) - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#850) - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.4.0...8.5.0) -- Bump Java SDK from v6.17.0 to v6.18.0 ([#2338](https://github.com/getsentry/sentry-dotnet/pull/2338)) - - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#6180) - - [diff](https://github.com/getsentry/sentry-java/compare/6.17.0...6.18.0) +- Bump Java SDK from v6.17.0 to v6.18.1 ([#2338](https://github.com/getsentry/sentry-dotnet/pull/2338), [#2343](https://github.com/getsentry/sentry-dotnet/pull/2343)) + - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#6181) + - [diff](https://github.com/getsentry/sentry-java/compare/6.17.0...6.18.1) ## 3.30.0 diff --git a/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj b/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj index 9ce754e523..2ed395c33f 100644 --- a/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj +++ b/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj @@ -3,7 +3,7 @@ net6.0-android $(NoWarn);BG8605;BG8606 - 6.18.0 + 6.18.1 $(BaseIntermediateOutputPath)sdks\Sentry\Android\$(SentryAndroidSdkVersion)\ .NET Bindings for the Sentry Android SDK From 4cdd22fbc07bd97eaec45a3966bfff68c742d435 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 28 Apr 2023 21:25:24 -0700 Subject: [PATCH 015/142] chore(deps): update Cocoa SDK to v8.6.0 (#2344) * chore: update modules/sentry-cocoa to 8.6.0 * Update ApiDefinitions.cs --------- Co-authored-by: GitHub Co-authored-by: Matt Johnson-Pint --- CHANGELOG.md | 9 +++++++++ modules/sentry-cocoa | 2 +- src/Sentry.Bindings.Cocoa/ApiDefinitions.cs | 19 +++++++++++++++++++ 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 147ecc3848..9daf68e8ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,15 @@ ### Dependencies +- Bump CLI from v2.17.0 to v2.17.4 ([#2298](https://github.com/getsentry/sentry-dotnet/pull/2298), [#2318](https://github.com/getsentry/sentry-dotnet/pull/2318), [#2321](https://github.com/getsentry/sentry-dotnet/pull/2321)) + - [changelog](https://github.com/getsentry/sentry-cli/blob/master/CHANGELOG.md#2174) + - [diff](https://github.com/getsentry/sentry-cli/compare/2.17.0...2.17.4) +- Bump Cocoa SDK from v8.4.0 to v8.6.0 ([#2310](https://github.com/getsentry/sentry-dotnet/pull/2310), [#2344](https://github.com/getsentry/sentry-dotnet/pull/2344)) + - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#860) + - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.4.0...8.6.0) +- Bump Java SDK from v6.17.0 to v6.18.0 ([#2338](https://github.com/getsentry/sentry-dotnet/pull/2338)) + - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#6180) + - [diff](https://github.com/getsentry/sentry-java/compare/6.17.0...6.18.0) - Bump CLI from v2.17.0 to v2.17.5 ([#2298](https://github.com/getsentry/sentry-dotnet/pull/2298), [#2318](https://github.com/getsentry/sentry-dotnet/pull/2318), [#2321](https://github.com/getsentry/sentry-dotnet/pull/2321), [#2345](https://github.com/getsentry/sentry-dotnet/pull/2345)) - [changelog](https://github.com/getsentry/sentry-cli/blob/master/CHANGELOG.md#2175) - [diff](https://github.com/getsentry/sentry-cli/compare/2.17.0...2.17.5) diff --git a/modules/sentry-cocoa b/modules/sentry-cocoa index 2479b6f7ff..e6dcfba32f 160000 --- a/modules/sentry-cocoa +++ b/modules/sentry-cocoa @@ -1 +1 @@ -Subproject commit 2479b6f7ff69b66bcdea82184d097667e63828ed +Subproject commit e6dcfba32f2861438b82c7ad34e058b23c83daf6 diff --git a/src/Sentry.Bindings.Cocoa/ApiDefinitions.cs b/src/Sentry.Bindings.Cocoa/ApiDefinitions.cs index 4d3108f976..25f1836df7 100644 --- a/src/Sentry.Bindings.Cocoa/ApiDefinitions.cs +++ b/src/Sentry.Bindings.Cocoa/ApiDefinitions.cs @@ -910,6 +910,10 @@ interface SentrySpanContext : SentrySerializable [NullAllowed, Export ("spanDescription")] string SpanDescription { get; } + // @property (copy, nonatomic) NSString * _Nonnull origin; + [Export ("origin")] + string Origin { get; set; } + // -(instancetype _Nonnull)initWithOperation:(NSString * _Nonnull)operation; [Export ("initWithOperation:")] NativeHandle Constructor (string operation); @@ -959,6 +963,11 @@ interface SentrySpan : SentrySerializable [Export ("operation")] string Operation { get; set; } + // @required @property (copy, nonatomic) NSString * _Nonnull origin; + [Abstract] + [Export ("origin")] + string Origin { get; set; } + // @required @property (copy, nonatomic) NSString * _Nullable spanDescription; [Abstract] [NullAllowed, Export ("spanDescription")] @@ -2408,4 +2417,14 @@ interface PrivateSentrySDKOnly [Static] [Export ("captureViewHierarchy")] NSData CaptureViewHierarchy(); + + // +(SentryUser * _Nonnull)userWithDictionary:(NSDictionary * _Nonnull)dictionary; + [Static] + [Export ("userWithDictionary:")] + SentryUser UserWithDictionary (NSDictionary dictionary); + + // +(SentryBreadcrumb * _Nonnull)breadcrumbWithDictionary:(NSDictionary * _Nonnull)dictionary; + [Static] + [Export ("breadcrumbWithDictionary:")] + SentryBreadcrumb BreadcrumbWithDictionary (NSDictionary dictionary); } From e1da415505229c5ca6eeecdeead933bb4c2b258f Mon Sep 17 00:00:00 2001 From: Ivan Dlugos <6349682+vaind@users.noreply.github.com> Date: Mon, 1 May 2023 18:28:13 +0200 Subject: [PATCH 016/142] Update CHANGELOG.md (#2347) --- CHANGELOG.md | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9daf68e8ea..f91df3e0b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,21 +22,12 @@ ### Dependencies -- Bump CLI from v2.17.0 to v2.17.4 ([#2298](https://github.com/getsentry/sentry-dotnet/pull/2298), [#2318](https://github.com/getsentry/sentry-dotnet/pull/2318), [#2321](https://github.com/getsentry/sentry-dotnet/pull/2321)) - - [changelog](https://github.com/getsentry/sentry-cli/blob/master/CHANGELOG.md#2174) - - [diff](https://github.com/getsentry/sentry-cli/compare/2.17.0...2.17.4) -- Bump Cocoa SDK from v8.4.0 to v8.6.0 ([#2310](https://github.com/getsentry/sentry-dotnet/pull/2310), [#2344](https://github.com/getsentry/sentry-dotnet/pull/2344)) - - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#860) - - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.4.0...8.6.0) -- Bump Java SDK from v6.17.0 to v6.18.0 ([#2338](https://github.com/getsentry/sentry-dotnet/pull/2338)) - - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#6180) - - [diff](https://github.com/getsentry/sentry-java/compare/6.17.0...6.18.0) - Bump CLI from v2.17.0 to v2.17.5 ([#2298](https://github.com/getsentry/sentry-dotnet/pull/2298), [#2318](https://github.com/getsentry/sentry-dotnet/pull/2318), [#2321](https://github.com/getsentry/sentry-dotnet/pull/2321), [#2345](https://github.com/getsentry/sentry-dotnet/pull/2345)) - [changelog](https://github.com/getsentry/sentry-cli/blob/master/CHANGELOG.md#2175) - [diff](https://github.com/getsentry/sentry-cli/compare/2.17.0...2.17.5) -- Bump Cocoa SDK from v8.4.0 to v8.5.0 ([#2310](https://github.com/getsentry/sentry-dotnet/pull/2310)) - - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#850) - - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.4.0...8.5.0) +- Bump Cocoa SDK from v8.4.0 to v8.6.0 ([#2310](https://github.com/getsentry/sentry-dotnet/pull/2310), [#2344](https://github.com/getsentry/sentry-dotnet/pull/2344)) + - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#860) + - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.4.0...8.6.0) - Bump Java SDK from v6.17.0 to v6.18.1 ([#2338](https://github.com/getsentry/sentry-dotnet/pull/2338), [#2343](https://github.com/getsentry/sentry-dotnet/pull/2343)) - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#6181) - [diff](https://github.com/getsentry/sentry-java/compare/6.17.0...6.18.1) From b1bfe1efc04eb4c911a85f1cf4cd2e5a176d7c8a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 May 2023 09:28:46 -0700 Subject: [PATCH 017/142] Bump github/codeql-action from 2.3.0 to 2.3.2 (#2348) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2.3.0 to 2.3.2. - [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/b2c19fb9a2a485599ccf4ed5d65527d94bc57226...f3feb00acb00f31a6f60280e6ace9ca31d91c76a) --- 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 794c2ad257..cc8d9bbdbe 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -34,7 +34,7 @@ jobs: uses: ./.github/actions/environment - name: Initialize CodeQL - uses: github/codeql-action/init@b2c19fb9a2a485599ccf4ed5d65527d94bc57226 # pin@v2 + uses: github/codeql-action/init@f3feb00acb00f31a6f60280e6ace9ca31d91c76a # pin@v2 with: languages: csharp @@ -45,6 +45,6 @@ jobs: run: dotnet build Sentry-CI-CodeQL.slnf --no-restore --nologo - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@b2c19fb9a2a485599ccf4ed5d65527d94bc57226 # pin@v2 + uses: github/codeql-action/analyze@f3feb00acb00f31a6f60280e6ace9ca31d91c76a # pin@v2 with: category: '/language:csharp' From 69e79ef0bdaaf2dca243b63294c9f5aeecc84d99 Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Mon, 1 May 2023 20:36:44 -0600 Subject: [PATCH 018/142] Improve debug file upload handling (#2349) --- CHANGELOG.md | 1 + src/Sentry/buildTransitive/Sentry.targets | 31 ++-- test/sentry-cli-integration.Tests.ps1 | 212 +++++++++------------- 3 files changed, 94 insertions(+), 150 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f91df3e0b5..3fc51257e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ - Remove session breadcrumbs ([#2333](https://github.com/getsentry/sentry-dotnet/pull/2333)) - Support synchronous `HttpClient.Send` in `SentryHttpMessageHandler` ([#2336](https://github.com/getsentry/sentry-dotnet/pull/2336)) - Fix ASP.NET Core issue with missing context when using capture methods that configure scope ([#2339](https://github.com/getsentry/sentry-dotnet/pull/2339)) +- Improve debug file upload handling ([#2349](https://github.com/getsentry/sentry-dotnet/pull/2349)) ### Dependencies diff --git a/src/Sentry/buildTransitive/Sentry.targets b/src/Sentry/buildTransitive/Sentry.targets index 9501d4c7e9..1f7c61f6d4 100644 --- a/src/Sentry/buildTransitive/Sentry.targets +++ b/src/Sentry/buildTransitive/Sentry.targets @@ -85,7 +85,6 @@ $(SentryCLIUploadOptions) --org $(SentryOrg) $(SentryCLIUploadOptions) --project $(SentryProject) - $(SentryCLIUploadOptions) --include-sources $(SentryCLIBaseCommand) debug-files upload $(SentryCLIUploadCommand) $(SentryCLIUploadOptions.Trim()) @@ -110,11 +109,6 @@ - - - - @@ -122,29 +116,26 @@ false - + + + + + - - + - - - - + - + + Condition="'$(SentryUploadSources)' == 'true' And '$(SentryCLI)' != ''"> diff --git a/test/sentry-cli-integration.Tests.ps1 b/test/sentry-cli-integration.Tests.ps1 index e4b025c97e..e28d78ec52 100644 --- a/test/sentry-cli-integration.Tests.ps1 +++ b/test/sentry-cli-integration.Tests.ps1 @@ -1,3 +1,5 @@ +using namespace System.Runtime.InteropServices + Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' @@ -8,15 +10,18 @@ if (!(Test-Path env:CI )) } BeforeAll { - function DotnetBuild([string]$Sample) + function DotnetBuild([string]$Sample, [bool]$Symbols, [bool]$Sources, [string]$TargetFramework = '') { $rootDir = "$(Get-Item $PSScriptRoot/../../)" + $framework = $TargetFramework -eq '' ? '' : @('-f', $TargetFramework) Invoke-SentryServer { Param([string]$url) Write-Host "Building $Sample" - dotnet build "samples/$sample/$sample.csproj" -c Release --no-restore --nologo ` + dotnet build "samples/$sample/$sample.csproj" -c Release $framework --no-restore --nologo ` /p:UseSentryCLI=true ` + /p:SentryUploadSymbols=$Symbols ` + /p:SentryUploadSources=$Sources ` /p:SentryOrg=org ` /p:SentryProject=project ` /p:SentryUrl=$url ` @@ -46,141 +51,88 @@ BeforeAll { } Describe 'CLI-integration' { + + It "uploads symbols and sources for a console app build" { + $exe = [RuntimeInformation]::IsOSPlatform([OSPlatform]::Windows) ? '.exe' : '' + $result = DotnetBuild 'Sentry.Samples.Console.Basic' $True $True + $result.ScriptOutput | Should -Contain 'Build succeeded.' + $result.HasErrors() | Should -BeFalse + $result.UploadedDebugFiles() | Sort-Object -Unique | Should -Be @( + 'Sentry.pdb', + "Sentry.Samples.Console.Basic$exe", + 'Sentry.Samples.Console.Basic.pdb', + 'Sentry.Samples.Console.Basic.src.zip') + } + It "uploads symbols for a console app build" { - $result = DotnetBuild 'Sentry.Samples.Console.Basic' + $exe = [RuntimeInformation]::IsOSPlatform([OSPlatform]::Windows) ? '.exe' : '' + $result = DotnetBuild 'Sentry.Samples.Console.Basic' $True $False $result.ScriptOutput | Should -Contain 'Build succeeded.' $result.HasErrors() | Should -BeFalse - return; # TODO enable actual test, see https://github.com/getsentry/sentry-dotnet/issues/2260 - $result.UploadedDebugFiles() | Sort-Object -Unique | Should -Be @('apphost.exe', 'Sentry.pdb', 'Sentry.Samples.Console.Basic.pdb') + $result.UploadedDebugFiles() | Sort-Object -Unique | Should -Be @( + 'Sentry.pdb', + "Sentry.Samples.Console.Basic$exe", + 'Sentry.Samples.Console.Basic.pdb') + } + + It "uploads sources for a console app build" { + $result = DotnetBuild 'Sentry.Samples.Console.Basic' $False $True + $result.ScriptOutput | Should -Contain 'Build succeeded.' + $result.HasErrors() | Should -BeFalse + $result.UploadedDebugFiles() | Sort-Object -Unique | Should -Be @( + 'Sentry.Samples.Console.Basic.src.zip') + } + + It "uploads nothing for a console app build when disabled" { + $result = DotnetBuild 'Sentry.Samples.Console.Basic' $False $False + $result.ScriptOutput | Should -Contain 'Build succeeded.' + $result.HasErrors() | Should -BeFalse + $result.UploadedDebugFiles() | Sort-Object -Unique | Should -Be @() + } + + It "uploads symbols and sources for a MAUI Android app build" { + $result = DotnetBuild 'Sentry.Samples.Maui' $True $True 'net6.0-android' + $result.ScriptOutput | Should -Contain 'Build succeeded.' + $result.HasErrors() | Should -BeFalse + $result.UploadedDebugFiles() | Sort-Object -Unique | Should -Be @( + 'Sentry.Android.AssemblyReader.pdb', + 'Sentry.Bindings.Android.pdb', + 'Sentry.Extensions.Logging.pdb', + 'Sentry.Maui.pdb', + 'Sentry.pdb', + 'Sentry.Samples.Maui.pdb', + 'Sentry.Samples.Maui.src.zip' + ) + } + + if (![RuntimeInformation]::IsOSPlatform([OSPlatform]::OSX)) { + # Remaining tests run on macOS only + return } - It "uploads symbols for a MAUI app build" { - $result = DotnetBuild 'Sentry.Samples.Maui' + It "uploads symbols and sources for a MAUI iOS app build" { + $result = DotnetBuild 'Sentry.Samples.Maui' $True $True 'net6.0-ios' $result.ScriptOutput | Should -Contain 'Build succeeded.' $result.HasErrors() | Should -BeFalse - return; # TODO enable actual test, see https://github.com/getsentry/sentry-dotnet/issues/2260 - $actual = $result.UploadedDebugFiles() | Sort-Object -Unique - $expected = @( - 'apphost.exe', ` - 'Java.Interop.dll.so', ` - 'K4os.Compression.LZ4.dll.so', ` - 'libmonodroid.so', ` - 'libmonosgen-2.0.so', ` - 'libsentry-android.so', ` - 'libsentry.so', ` - 'libsentrysupplemental.so', ` - 'libSystem.IO.Compression.Native.so', ` - 'libSystem.Native.so', ` - 'libSystem.Security.Cryptography.Native.Android.so', ` - 'libxamarin-app.so', ` - 'Microsoft.Extensions.Configuration.Abstractions.dll.so', ` - 'Microsoft.Extensions.Configuration.Binder.dll.so', ` - 'Microsoft.Extensions.Configuration.dll.so', ` - 'Microsoft.Extensions.DependencyInjection.Abstractions.dll.so', ` - 'Microsoft.Extensions.DependencyInjection.dll.so', ` - 'Microsoft.Extensions.Http.dll.so', ` - 'Microsoft.Extensions.Logging.Abstractions.dll.so', ` - 'Microsoft.Extensions.Logging.Configuration.dll.so', ` - 'Microsoft.Extensions.Logging.dll.so', ` - 'Microsoft.Extensions.Options.ConfigurationExtensions.dll.so', ` - 'Microsoft.Extensions.Options.dll.so', ` - 'Microsoft.Extensions.Primitives.dll.so', ` - 'Microsoft.Maui.Controls.Compatibility.dll.so', ` - 'Microsoft.Maui.Controls.dll.so', ` - 'Microsoft.Maui.Controls.Xaml.dll.so', ` - 'Microsoft.Maui.dll.so', ` - 'Microsoft.Maui.Essentials.dll.so', ` - 'Microsoft.Maui.Graphics.dll.so', ` - 'Microsoft.Win32.Primitives.dll.so', ` - 'Mono.Android.dll.so', ` - 'Sentry.Android.AssemblyReader.dll.so', ` - 'Sentry.Android.AssemblyReader.pdb', ` - 'Sentry.Bindings.Android.dll.so', ` - 'Sentry.Bindings.Android.pdb', ` - 'Sentry.dll.so', ` - 'Sentry.Extensions.Logging.dll.so', ` - 'Sentry.Extensions.Logging.pdb', ` - 'Sentry.Maui.dll.so', ` - 'Sentry.Maui.pdb', ` - 'Sentry.pdb', ` - 'Sentry.Samples.Maui.dll.so', ` - 'Sentry.Samples.Maui.pdb', ` - 'System.Collections.Concurrent.dll.so', ` - 'System.Collections.dll.so', ` - 'System.Collections.Immutable.dll.so', ` - 'System.Collections.NonGeneric.dll.so', ` - 'System.Collections.Specialized.dll.so', ` - 'System.ComponentModel.dll.so', ` - 'System.ComponentModel.Primitives.dll.so', ` - 'System.ComponentModel.TypeConverter.dll.so', ` - 'System.Console.dll.so', ` - 'System.Diagnostics.DiagnosticSource.dll.so', ` - 'System.Diagnostics.StackTrace.dll.so', ` - 'System.Diagnostics.TraceSource.dll.so', ` - 'System.dll.so', ` - 'System.IO.Compression.dll.so', ` - 'System.IO.Compression.ZipFile.dll.so', ` - 'System.IO.FileSystem.DriveInfo.dll.so', ` - 'System.IO.MemoryMappedFiles.dll.so', ` - 'System.Linq.dll.so', ` - 'System.Linq.Expressions.dll.so', ` - 'System.Memory.dll.so', ` - 'System.Net.Http.dll.so', ` - 'System.Net.NetworkInformation.dll.so', ` - 'System.Net.Primitives.dll.so', ` - 'System.Net.Requests.dll.so', ` - 'System.Net.Security.dll.so', ` - 'System.Net.WebProxy.dll.so', ` - 'System.Numerics.Vectors.dll.so', ` - 'System.ObjectModel.dll.so', ` - 'System.Private.CoreLib.dll.so', ` - 'System.Private.Uri.dll.so', ` - 'System.Private.Xml.dll.so', ` - 'System.Reflection.Metadata.dll.so', ` - 'System.Reflection.Primitives.dll.so', ` - 'System.Runtime.CompilerServices.Unsafe.dll.so', ` - 'System.Runtime.dll.so', ` - 'System.Runtime.InteropServices.dll.so', ` - 'System.Runtime.InteropServices.RuntimeInformation.dll.so', ` - 'System.Runtime.Serialization.Primitives.dll.so', ` - 'System.Security.Cryptography.Algorithms.dll.so', ` - 'System.Security.Cryptography.Primitives.dll.so', ` - 'System.Text.Encodings.Web.dll.so', ` - 'System.Text.Json.dll.so', ` - 'System.Text.RegularExpressions.dll.so', ` - 'System.Threading.dll.so', ` - 'System.Threading.Thread.dll.so', ` - 'System.Threading.ThreadPool.dll.so', ` - 'System.Xml.ReaderWriter.dll.so', ` - 'Xamarin.AndroidX.Activity.dll.so', ` - 'Xamarin.AndroidX.AppCompat.AppCompatResources.dll.so', ` - 'Xamarin.AndroidX.AppCompat.dll.so', ` - 'Xamarin.AndroidX.CardView.dll.so', ` - 'Xamarin.AndroidX.Collection.dll.so', ` - 'Xamarin.AndroidX.CoordinatorLayout.dll.so', ` - 'Xamarin.AndroidX.Core.dll.so', ` - 'Xamarin.AndroidX.CursorAdapter.dll.so', ` - 'Xamarin.AndroidX.CustomView.dll.so', ` - 'Xamarin.AndroidX.DrawerLayout.dll.so', ` - 'Xamarin.AndroidX.Fragment.dll.so', ` - 'Xamarin.AndroidX.Lifecycle.Common.dll.so', ` - 'Xamarin.AndroidX.Lifecycle.LiveData.Core.dll.so', ` - 'Xamarin.AndroidX.Lifecycle.ViewModel.dll.so', ` - 'Xamarin.AndroidX.Lifecycle.ViewModelSavedState.dll.so', ` - 'Xamarin.AndroidX.Loader.dll.so', ` - 'Xamarin.AndroidX.Navigation.Common.dll.so', ` - 'Xamarin.AndroidX.Navigation.Fragment.dll.so', ` - 'Xamarin.AndroidX.Navigation.Runtime.dll.so', ` - 'Xamarin.AndroidX.Navigation.UI.dll.so', ` - 'Xamarin.AndroidX.RecyclerView.dll.so', ` - 'Xamarin.AndroidX.SavedState.dll.so', ` - 'Xamarin.AndroidX.SwipeRefreshLayout.dll.so', ` - 'Xamarin.AndroidX.ViewPager.dll.so', ` - 'Xamarin.AndroidX.ViewPager2.dll.so', ` - 'Xamarin.Google.Android.Material.dll.so', ` - 'Xamarin.Kotlin.StdLib.dll.so', ` - 'Xamarin.KotlinX.Coroutines.Core.Jvm.dll.so' ` + $result.UploadedDebugFiles() | Sort-Object -Unique | Should -Be @( + 'libmono-component-debugger.dylib', + 'libmono-component-diagnostics_tracing.dylib', + 'libmono-component-hot_reload.dylib', + 'libmonosgen-2.0.dylib', + 'libSystem.IO.Compression.Native.dylib', + 'libSystem.Native.dylib', + 'libSystem.Net.Security.Native.dylib', + 'libSystem.Security.Cryptography.Native.Apple.dylib', + 'libxamarin-dotnet-debug.dylib', + 'libxamarin-dotnet.dylib', + 'Sentry', + 'Sentry.Bindings.Cocoa.pdb', + 'Sentry.Extensions.Logging.pdb', + 'Sentry.Maui.pdb', + 'Sentry.pdb', + 'Sentry.Samples.Maui', + 'Sentry.Samples.Maui.pdb', + 'Sentry.Samples.Maui.src.zip' ) - @(Compare-Object -ReferenceObject $expected -DifferenceObject $actual -PassThru) | Should -Be @() } } From 0487a59d3386105c2c46d64566a73ab34a8fd46d Mon Sep 17 00:00:00 2001 From: getsentry-bot Date: Tue, 2 May 2023 02:37:47 +0000 Subject: [PATCH 019/142] release: 3.31.0 --- CHANGELOG.md | 2 +- Directory.Build.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3fc51257e3..a9b8943b9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## Unreleased +## 3.31.0 ### Features diff --git a/Directory.Build.props b/Directory.Build.props index b35753aac0..1bb7ad7195 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,7 +1,7 @@ - 3.30.0 + 3.31.0 11 true $(MSBuildThisFileDirectory).assets\Sentry.snk From d55a03eee8c27aa1a1788ebfc48b48c959f10e54 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Mon, 8 May 2023 11:41:11 +1200 Subject: [PATCH 020/142] Added section to CONTRIBUTING.md for targeting mobile platforms (#2350) * Added section to CONTRIBUTING.md for targeting mobile platforms Co-authored-by: Matt Johnson-Pint --- CONTRIBUTING.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index be8848bbf4..cd0e8c331b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -29,6 +29,20 @@ For big feature it's advised to raise an issue to discuss it first. To build any of `Sentry.Maui`, `Sentry.Maui.Tests`, or `Sentry.Samples.Maui`, you'll need to have .NET SDK 6.0.400 or greater installed, and have installed the MAUI workloads installed, either through Visual Studio setup, or by running `dotnet workload restore` (or `dotnet workload install maui`) from the Sentry source code root directory. You may also need other platform dependencies. See https://docs.microsoft.com/dotnet/maui/ for details. Basically, if you can build and run the "MyMauiApp" example you should also be able to build and run the Sentry MAUI sample app. +### Targeting Android, iOS and macCatalyst + +Although the files in `/src/Sentry/Platforms/` are part of the `Sentry` project, they are [conditionally targeted](https://github.com/getsentry/sentry-dotnet/blob/b1bfe1efc04eb4c911a85f1cf4cd2e5a176d7c8a/src/Sentry/Sentry.csproj#L19-L21) when the platform is Android, iOS or macCatalyst. We build for Android on all platforms, but currently compile iOS and macCatalyst _only when building on a Mac_. + +```xml + + + +``` + +These `*.props` files are used to add platform-specific files, such as references to the binding projects for each native SDK (which provide .NET wrappers around native Android or Cocoa functions). + +Also note `/Directory.Build.targets` contains some [convention based rules](https://github.com/getsentry/sentry-dotnet/blob/b1bfe1efc04eb4c911a85f1cf4cd2e5a176d7c8a/Directory.Build.targets#L17-L35) to exclude code that is not relevant for the target platform. Developers using Visual Studio will need to enable `Show All Files` in order to be able to see these files, when working with the solution. + ## API changes approval process This repository uses [Verify](https://github.com/VerifyTests/Verify) to store the public API diffs in snapshot files. @@ -97,4 +111,4 @@ dotnet test pwsh ./scripts/accept-verifier-changes.ps1 ``` -You may need to run this multiple times because `dotnet test` stops after a certain number of failures. \ No newline at end of file +You may need to run this multiple times because `dotnet test` stops after a certain number of failures. From df891d98ae585b64516955f9ddffa73efa2d400c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 May 2023 09:46:09 -0700 Subject: [PATCH 021/142] Bump github/codeql-action from 2.3.2 to 2.3.3 (#2353) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2.3.2 to 2.3.3. - [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/f3feb00acb00f31a6f60280e6ace9ca31d91c76a...29b1f65c5e92e24fe6b6647da1eaabe529cec70f) --- 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 cc8d9bbdbe..80252f849b 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -34,7 +34,7 @@ jobs: uses: ./.github/actions/environment - name: Initialize CodeQL - uses: github/codeql-action/init@f3feb00acb00f31a6f60280e6ace9ca31d91c76a # pin@v2 + uses: github/codeql-action/init@29b1f65c5e92e24fe6b6647da1eaabe529cec70f # pin@v2 with: languages: csharp @@ -45,6 +45,6 @@ jobs: run: dotnet build Sentry-CI-CodeQL.slnf --no-restore --nologo - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@f3feb00acb00f31a6f60280e6ace9ca31d91c76a # pin@v2 + uses: github/codeql-action/analyze@29b1f65c5e92e24fe6b6647da1eaabe529cec70f # pin@v2 with: category: '/language:csharp' From f946b6be97dc1c71fe66fad46afd8dbd7a7b5848 Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Mon, 8 May 2023 21:19:49 -0600 Subject: [PATCH 022/142] Require XML comments on public types for all build configurations (#2354) --- src/Directory.Build.props | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 26e0f2acec..a8e22854c2 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -6,9 +6,6 @@ true - - $(NoWarn);CS1591 - Sentry Team and Contributors Sentry.io Sentry From 55a9c5b6ff58b2c0145a9e03aa62f5e2534dafb8 Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Mon, 8 May 2023 21:22:47 -0600 Subject: [PATCH 023/142] Improve local builds (#2355) --- Directory.Build.props | 14 + Sentry-CI-Build-Linux.slnf | 2 +- Sentry-CI-Build-Windows.slnf | 2 +- Sentry-CI-Build-macOS.slnf | 2 +- Sentry-CI-CodeQL.slnf | 2 +- Sentry-CI-Pack.slnf | 2 +- Sentry-CI-Test.slnf | 2 +- Sentry.Full.sln | 509 +++++++++++++++++++++++++++ Sentry.Mobile.sln | 220 ++++++++++++ Sentry.sln | 89 ----- SentryMaui.slnf => SentryMobile.slnf | 2 +- SentryNoSamples.slnf | 1 - build.cmd | 3 + build.ps1 | 10 - build.sh | 15 +- 15 files changed, 756 insertions(+), 119 deletions(-) create mode 100644 Sentry.Full.sln create mode 100644 Sentry.Mobile.sln rename SentryMaui.slnf => SentryMobile.slnf (97%) create mode 100644 build.cmd delete mode 100644 build.ps1 diff --git a/Directory.Build.props b/Directory.Build.props index 1bb7ad7195..90f49c1bd9 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -14,6 +14,20 @@ $(NoWarn);CS8002 + + + true + + + + + true + true + true + true + true + + $(NoWarn);BG8605;BG8606 - 6.18.1 + 6.19.0 $(BaseIntermediateOutputPath)sdks\Sentry\Android\$(SentryAndroidSdkVersion)\ .NET Bindings for the Sentry Android SDK diff --git a/src/Sentry.Bindings.Android/Transforms/Metadata.xml b/src/Sentry.Bindings.Android/Transforms/Metadata.xml index 9525f460e1..60747904e6 100644 --- a/src/Sentry.Bindings.Android/Transforms/Metadata.xml +++ b/src/Sentry.Bindings.Android/Transforms/Metadata.xml @@ -21,6 +21,7 @@ Sentry.JavaSdk.Android.Core Sentry.JavaSdk.Android.Core.Internal.Gestures Sentry.JavaSdk.Android.Core.Internal.Modules + Sentry.JavaSdk.Android.Core.Internal.ThreadDump Sentry.JavaSdk.Android.Core.Internal.Util Sentry.JavaSdk.Android.Ndk Sentry.JavaSdk.Android.Supplemental @@ -78,6 +79,15 @@ --> + + + + + + + \ No newline at end of file diff --git a/samples/Sentry.Samples.AzureFunctions.Worker/host.json b/samples/Sentry.Samples.AzureFunctions.Worker/host.json new file mode 100644 index 0000000000..beb2e4020b --- /dev/null +++ b/samples/Sentry.Samples.AzureFunctions.Worker/host.json @@ -0,0 +1,11 @@ +{ + "version": "2.0", + "logging": { + "applicationInsights": { + "samplingSettings": { + "isEnabled": true, + "excludedTypes": "Request" + } + } + } +} \ No newline at end of file diff --git a/samples/Sentry.Samples.AzureFunctions.Worker/local.settings.json b/samples/Sentry.Samples.AzureFunctions.Worker/local.settings.json new file mode 100644 index 0000000000..70eb5dd490 --- /dev/null +++ b/samples/Sentry.Samples.AzureFunctions.Worker/local.settings.json @@ -0,0 +1,7 @@ +{ + "IsEncrypted": false, + "Values": { + "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated", + "AzureWebJobsStorage": "UseDevelopmentStorage=true" + } +} diff --git a/src/Sentry.AzureFunctions.Worker/Sentry.AzureFunctions.Worker.csproj b/src/Sentry.AzureFunctions.Worker/Sentry.AzureFunctions.Worker.csproj new file mode 100644 index 0000000000..b2eb21a87f --- /dev/null +++ b/src/Sentry.AzureFunctions.Worker/Sentry.AzureFunctions.Worker.csproj @@ -0,0 +1,23 @@ + + + + net6.0;netstandard2.0 + $(PackageTags);Azure;Functions;Worker + Official Azure Functions Worker SDK integration for Sentry - Open-source error tracking that helps developers monitor and fix crashes in real time. + $(Version)-beta.1 + + + + + + + + + + + + + + + + diff --git a/src/Sentry.AzureFunctions.Worker/SentryAzureFunctionsOptions.cs b/src/Sentry.AzureFunctions.Worker/SentryAzureFunctionsOptions.cs new file mode 100644 index 0000000000..9f140446a9 --- /dev/null +++ b/src/Sentry.AzureFunctions.Worker/SentryAzureFunctionsOptions.cs @@ -0,0 +1,10 @@ +using Sentry.Extensions.Logging; + +namespace Sentry.AzureFunctions.Worker; + +/// +/// Sentry Azure Functions integration options +/// +public class SentryAzureFunctionsOptions : SentryLoggingOptions +{ +} diff --git a/src/Sentry.AzureFunctions.Worker/SentryAzureFunctionsOptionsSetup.cs b/src/Sentry.AzureFunctions.Worker/SentryAzureFunctionsOptionsSetup.cs new file mode 100644 index 0000000000..4872860c5c --- /dev/null +++ b/src/Sentry.AzureFunctions.Worker/SentryAzureFunctionsOptionsSetup.cs @@ -0,0 +1,22 @@ +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Options; + +namespace Sentry.AzureFunctions.Worker; + +internal class SentryAzureFunctionsOptionsSetup : ConfigureFromConfigurationOptions +{ + public SentryAzureFunctionsOptionsSetup(IConfiguration config) : base(config) + { + } + + public override void Configure(SentryAzureFunctionsOptions options) + { + // Mutable by user options + + base.Configure(options); + + // Immutable by user options + + options.TagFilters.Add("AzureFunctions_"); + } +} diff --git a/src/Sentry.AzureFunctions.Worker/SentryFunctionsWorkerApplicationBuilderExtensions.cs b/src/Sentry.AzureFunctions.Worker/SentryFunctionsWorkerApplicationBuilderExtensions.cs new file mode 100644 index 0000000000..66c5f4080b --- /dev/null +++ b/src/Sentry.AzureFunctions.Worker/SentryFunctionsWorkerApplicationBuilderExtensions.cs @@ -0,0 +1,57 @@ +using Microsoft.Azure.Functions.Worker; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Sentry.Extensions.Logging; +using Sentry.Extensions.Logging.Extensions.DependencyInjection; + +namespace Sentry.AzureFunctions.Worker; + +/// +/// Sentry extension methods for Azure Functions with Isolated Worker SDK +/// +[EditorBrowsable(EditorBrowsableState.Never)] +public static class SentryFunctionsWorkerApplicationBuilderExtensions +{ + /// + /// Uses Sentry integration. + /// + public static IFunctionsWorkerApplicationBuilder UseSentry(this IFunctionsWorkerApplicationBuilder builder, HostBuilderContext context) + => UseSentry(builder, context, (Action?)null); + + /// + /// Uses Sentry integration. + /// + 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, + HostBuilderContext context, + Action? optionsConfiguration) + { + builder.UseMiddleware(); + + var services = builder.Services; + services.Configure(options => + context.Configuration.GetSection("Sentry").Bind(options)); + + if (optionsConfiguration != null) + { + services.Configure(optionsConfiguration); + } + + services.AddLogging(); + services.AddSingleton(); + services.AddSingleton, SentryAzureFunctionsOptionsSetup>(); + + services.AddSentry(); + + return builder; + } +} diff --git a/src/Sentry.AzureFunctions.Worker/SentryFunctionsWorkerMiddleware.cs b/src/Sentry.AzureFunctions.Worker/SentryFunctionsWorkerMiddleware.cs new file mode 100644 index 0000000000..a22b639959 --- /dev/null +++ b/src/Sentry.AzureFunctions.Worker/SentryFunctionsWorkerMiddleware.cs @@ -0,0 +1,114 @@ +using Microsoft.Azure.Functions.Worker; +using Microsoft.Azure.Functions.Worker.Middleware; + +namespace Sentry.AzureFunctions.Worker; + +internal class SentryFunctionsWorkerMiddleware : IFunctionsWorkerMiddleware +{ + private readonly IHub _hub; + private static readonly ConcurrentDictionary TransactionNameCache = new(); + + public SentryFunctionsWorkerMiddleware(IHub hub) + { + _hub = hub; + } + + public async Task Invoke(FunctionContext context, FunctionExecutionDelegate next) + { + var transactionName = await GetHttpTransactionNameAsync(context).ConfigureAwait(false) ?? context.FunctionDefinition.Name; + var transaction = _hub.StartTransaction(transactionName, "function"); + Exception? unhandledException = null; + + try + { + _hub.ConfigureScope(scope => + { + scope.Transaction = transaction; + + scope.Contexts["function"] = new Dictionary + { + { "name", context.FunctionDefinition.Name }, + { "entryPoint", context.FunctionDefinition.EntryPoint }, + { "invocationId", context.InvocationId } + }; + }); + + context.CancellationToken.ThrowIfCancellationRequested(); + + await next(context).ConfigureAwait(false); + } + catch (Exception exception) + { + exception.SetSentryMechanism(nameof(SentryFunctionsWorkerMiddleware), + "This exception was caught by the Sentry Functions middleware. " + + "The Function has thrown an exception that was not handled by the user code.", + handled: false); + + unhandledException = exception; + + throw; + } + finally + { + if (unhandledException is not null) + { + transaction.Finish(unhandledException); + } + else + { + var statusCode = context.GetHttpResponseData()?.StatusCode; + + // For HTTP triggered function, finish transaction with the returned HTTP status code + if (statusCode is not null) + { + var status = SpanStatusConverter.FromHttpStatusCode(statusCode.Value); + + transaction.Finish(status); + } + else + { + transaction.Finish(); + } + } + } + } + + private static async Task GetHttpTransactionNameAsync(FunctionContext context) + { + // Get the HTTP request data + var requestData = await context.GetHttpRequestDataAsync().ConfigureAwait(false); + if (requestData is null) + { + // not an HTTP trigger + return null; + } + + var httpMethod = requestData.Method.ToUpperInvariant(); + + var transactionNameKey = $"{context.FunctionDefinition.EntryPoint}-{httpMethod}"; + if (TransactionNameCache.TryGetValue(transactionNameKey, out var value)) + { + return value; + } + + // Find the HTTP Trigger attribute via reflection + var assembly = Assembly.LoadFrom(context.FunctionDefinition.PathToAssembly); + var entryPointName = context.FunctionDefinition.EntryPoint; + + var typeName = entryPointName[..entryPointName.LastIndexOf('.')]; + var methodName = entryPointName[(typeName.Length + 1)..]; + var attribute = assembly.GetType(typeName)?.GetMethod(methodName)?.GetParameters() + .Select(p => p.GetCustomAttribute()) + .FirstOrDefault(a => a is not null); + + var transactionName = attribute?.Route is { } route + // Compose the transaction name from the method and route + ? $"{httpMethod} /{route.TrimStart('/')}" + // There's no route provided, so use the absolute path of the URL + : $"{httpMethod} {requestData.Url.AbsolutePath}"; + + TransactionNameCache.TryAdd(transactionNameKey, transactionName); + + return transactionName; + } +} diff --git a/src/Sentry.Extensions.Logging/Properties/AssemblyInfo.cs b/src/Sentry.Extensions.Logging/Properties/AssemblyInfo.cs index e92dd5a1a8..127b03cbee 100644 --- a/src/Sentry.Extensions.Logging/Properties/AssemblyInfo.cs +++ b/src/Sentry.Extensions.Logging/Properties/AssemblyInfo.cs @@ -4,6 +4,8 @@ [assembly: InternalsVisibleTo("Sentry.AspNetCore, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] [assembly: InternalsVisibleTo("Sentry.AspNetCore.Grpc, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] [assembly: InternalsVisibleTo("Sentry.AspNetCore.Grpc.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] +[assembly: InternalsVisibleTo("Sentry.AzureFunctions.Worker, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] +[assembly: InternalsVisibleTo("Sentry.AzureFunctions.Worker.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] [assembly: InternalsVisibleTo("Sentry.Google.Cloud.Functions.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] [assembly: InternalsVisibleTo("Sentry.DiagnosticSource.IntegrationTests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] diff --git a/src/Sentry/Properties/AssemblyInfo.cs b/src/Sentry/Properties/AssemblyInfo.cs index bb561f7e39..2e87eced74 100644 --- a/src/Sentry/Properties/AssemblyInfo.cs +++ b/src/Sentry/Properties/AssemblyInfo.cs @@ -1,14 +1,3 @@ -[assembly: InternalsVisibleTo("Sentry.Benchmarks, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.Testing, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.Log4Net, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.Log4Net.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.Maui, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.Maui.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.NLog, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.NLog.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.Serilog, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.Serilog.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] [assembly: InternalsVisibleTo("Sentry.Android.AssemblyReader.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] [assembly: InternalsVisibleTo("Sentry.AspNet, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] [assembly: InternalsVisibleTo("Sentry.AspNet.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] @@ -17,6 +6,9 @@ [assembly: InternalsVisibleTo("Sentry.AspNetCore, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] [assembly: InternalsVisibleTo("Sentry.AspNetCore.Grpc, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] [assembly: InternalsVisibleTo("Sentry.AspNetCore.Grpc.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] +[assembly: InternalsVisibleTo("Sentry.AzureFunctions.Worker, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] +[assembly: InternalsVisibleTo("Sentry.AzureFunctions.Worker.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] +[assembly: InternalsVisibleTo("Sentry.Benchmarks, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] [assembly: InternalsVisibleTo("Sentry.Google.Cloud.Functions, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] [assembly: InternalsVisibleTo("Sentry.Google.Cloud.Functions.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] [assembly: InternalsVisibleTo("Sentry.DiagnosticSource, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] @@ -27,8 +19,18 @@ [assembly: InternalsVisibleTo("Sentry.Extensions.Logging, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] [assembly: InternalsVisibleTo("Sentry.Extensions.Logging.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] [assembly: InternalsVisibleTo("Sentry.Extensions.Logging.EfCore.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] +[assembly: InternalsVisibleTo("Sentry.Log4Net, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] +[assembly: InternalsVisibleTo("Sentry.Log4Net.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] +[assembly: InternalsVisibleTo("Sentry.Maui, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] +[assembly: InternalsVisibleTo("Sentry.Maui.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] +[assembly: InternalsVisibleTo("Sentry.NLog, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] +[assembly: InternalsVisibleTo("Sentry.NLog.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] [assembly: InternalsVisibleTo("Sentry.Profiling, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] [assembly: InternalsVisibleTo("Sentry.Profiling.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] +[assembly: InternalsVisibleTo("Sentry.Serilog, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] +[assembly: InternalsVisibleTo("Sentry.Serilog.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] +[assembly: InternalsVisibleTo("Sentry.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] +[assembly: InternalsVisibleTo("Sentry.Testing, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] #if ANDROID diff --git a/test/Sentry.AzureFunctions.Worker.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt b/test/Sentry.AzureFunctions.Worker.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt new file mode 100644 index 0000000000..6a3ae15d2a --- /dev/null +++ b/test/Sentry.AzureFunctions.Worker.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt @@ -0,0 +1,7 @@ +namespace Sentry.AzureFunctions.Worker +{ + public static class SentryFunctionsWorkerApplicationBuilderExtensions + { + public static Microsoft.Azure.Functions.Worker.IFunctionsWorkerApplicationBuilder UseSentry(this Microsoft.Azure.Functions.Worker.IFunctionsWorkerApplicationBuilder builder, System.Action? optionsConfiguration) { } + } +} \ No newline at end of file diff --git a/test/Sentry.AzureFunctions.Worker.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt b/test/Sentry.AzureFunctions.Worker.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt new file mode 100644 index 0000000000..6a3ae15d2a --- /dev/null +++ b/test/Sentry.AzureFunctions.Worker.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt @@ -0,0 +1,7 @@ +namespace Sentry.AzureFunctions.Worker +{ + public static class SentryFunctionsWorkerApplicationBuilderExtensions + { + public static Microsoft.Azure.Functions.Worker.IFunctionsWorkerApplicationBuilder UseSentry(this Microsoft.Azure.Functions.Worker.IFunctionsWorkerApplicationBuilder builder, System.Action? optionsConfiguration) { } + } +} \ No newline at end of file diff --git a/test/Sentry.AzureFunctions.Worker.Tests/ApiApprovalTests.Run.Net4_8.verified.txt b/test/Sentry.AzureFunctions.Worker.Tests/ApiApprovalTests.Run.Net4_8.verified.txt new file mode 100644 index 0000000000..6a3ae15d2a --- /dev/null +++ b/test/Sentry.AzureFunctions.Worker.Tests/ApiApprovalTests.Run.Net4_8.verified.txt @@ -0,0 +1,7 @@ +namespace Sentry.AzureFunctions.Worker +{ + public static class SentryFunctionsWorkerApplicationBuilderExtensions + { + public static Microsoft.Azure.Functions.Worker.IFunctionsWorkerApplicationBuilder UseSentry(this Microsoft.Azure.Functions.Worker.IFunctionsWorkerApplicationBuilder builder, System.Action? optionsConfiguration) { } + } +} \ No newline at end of file diff --git a/test/Sentry.AzureFunctions.Worker.Tests/ApiApprovalTests.verify.cs b/test/Sentry.AzureFunctions.Worker.Tests/ApiApprovalTests.verify.cs new file mode 100644 index 0000000000..c119cb76f6 --- /dev/null +++ b/test/Sentry.AzureFunctions.Worker.Tests/ApiApprovalTests.verify.cs @@ -0,0 +1,11 @@ +namespace Sentry.AzureFunctions.Worker.Tests; + +[UsesVerify] +public class ApiApprovalTests +{ + [Fact] + public Task Run() + { + return typeof(SentryFunctionsWorkerMiddleware).Assembly.CheckApproval(); + } +} diff --git a/test/Sentry.AzureFunctions.Worker.Tests/Sentry.AzureFunctions.Worker.Tests.csproj b/test/Sentry.AzureFunctions.Worker.Tests/Sentry.AzureFunctions.Worker.Tests.csproj new file mode 100644 index 0000000000..5cbd742e57 --- /dev/null +++ b/test/Sentry.AzureFunctions.Worker.Tests/Sentry.AzureFunctions.Worker.Tests.csproj @@ -0,0 +1,12 @@ + + + + net7.0;net6.0;net48 + + + + + + + + diff --git a/test/Sentry.AzureFunctions.Worker.Tests/SentryFunctionsWorkerMiddlewareTests.cs b/test/Sentry.AzureFunctions.Worker.Tests/SentryFunctionsWorkerMiddlewareTests.cs new file mode 100644 index 0000000000..7dc9c2aa53 --- /dev/null +++ b/test/Sentry.AzureFunctions.Worker.Tests/SentryFunctionsWorkerMiddlewareTests.cs @@ -0,0 +1,137 @@ +using Microsoft.Azure.Functions.Worker; +using Microsoft.Azure.Functions.Worker.Http; + +namespace Sentry.AzureFunctions.Worker.Tests; + +public class SentryFunctionsWorkerMiddlewareTests +{ + private class Fixture + { + public IHub Hub { get; set; } + public IInternalScopeManager ScopeManager { get; } + public Transaction Transaction { get; set; } + + public Fixture() + { + var options = new SentryOptions + { + Dsn = ValidDsn, + EnableTracing = true, + }; + + var client = Substitute.For(); + var sessionManager = Substitute.For(); + + client.When(x => x.CaptureTransaction(Arg.Any())) + .Do(callback => Transaction = callback.Arg()); + + ScopeManager = new SentryScopeManager(options, client); + Hub = new Hub(options, client, sessionManager, new MockClock(), ScopeManager); + } + + public SentryFunctionsWorkerMiddleware GetSut() => new(Hub); + } + + private readonly Fixture _fixture = new(); + + [Function(nameof(ThrowingHttpFunction))] + private Task ThrowingHttpFunction([HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequestData req, + FunctionContext executionContext) + { + throw new Exception("Kaboom, Riko!"); + } + + [Function(nameof(HttpFunction))] + private Task HttpFunction([HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequestData req, + FunctionContext executionContext) + { + return Task.FromResult(Substitute.For(executionContext)); + } + + [Fact] + public async Task Original_exception_rethrown() + { + var functionContext = Substitute.For(); + var expected = "Kaboom, Riko!"; + Task FunctionExecutionDelegate(FunctionContext context) => ThrowingHttpFunction(null, context); + + var sut = _fixture.GetSut(); + + var actual = await Assert.ThrowsAsync(async () => await sut.Invoke(functionContext, FunctionExecutionDelegate)); + + actual.Message.Should().Be(expected); + } + + [Fact] + public async Task Transaction_name_and_operation_set() + { + var functionContext = Substitute.For(); + var functionDefinition = Substitute.For(); + functionContext.FunctionDefinition.Returns(functionDefinition); + functionDefinition.Name.Returns(nameof(HttpFunction)); + + var sut = _fixture.GetSut(); + + await sut.Invoke(functionContext, context => HttpFunction(null, context)); + + var transaction = _fixture.Transaction; + + transaction.Should().NotBeNull(); + transaction.Name.Should().Be(functionDefinition.Name); + transaction.Operation.Should().Be("function"); + } + + [Fact] + public async Task Tags_set() + { + var functionContext = Substitute.For(); + var functionDefinition = Substitute.For(); + functionContext.FunctionDefinition.Returns(functionDefinition); + functionDefinition.Name.Returns(nameof(HttpFunction)); + + var sut = _fixture.GetSut(); + + await sut.Invoke(functionContext, context => HttpFunction(null, context)); + + var scope = _fixture.ScopeManager.GetCurrent().Key; + var context = scope.Contexts["function"].As>(); + + context.Should().NotBeNull(); + context.Count.Should().Be(3); + context["name"].Should().Be(functionDefinition.Name); + context["entryPoint"].Should().Be(functionDefinition.EntryPoint); + context["invocationId"].Should().Be(functionContext.InvocationId); + } + + [Fact] + public async Task Unhandled_exception_sets_mechanism() + { + var functionContext = Substitute.For(); + + var sut = _fixture.GetSut(); + + var actual = await Assert.ThrowsAsync(async () => await sut.Invoke(functionContext, context => ThrowingHttpFunction(null, context))); + + actual.Data[Mechanism.MechanismKey].Should().Be(nameof(SentryFunctionsWorkerMiddleware)); + actual.Data[Mechanism.HandledKey].Should().Be(false); + actual.Data[Mechanism.DescriptionKey].Should().NotBeNull(); + } + + [Fact] + public async Task Skips_function_invocation_when_cancellation_requested() + { + var functionContext = Substitute.For(); + functionContext.CancellationToken.Returns(new CancellationToken(canceled: true)); + var functionInvoked = false; + + var sut = _fixture.GetSut(); + + _ = await Assert.ThrowsAsync(() => sut.Invoke(functionContext, _ => + { + functionInvoked = true; + return Task.CompletedTask; + })); + + functionInvoked.Should().BeFalse(); + } +} diff --git a/test/Sentry.Testing/Properties/AssemblyInfo.cs b/test/Sentry.Testing/Properties/AssemblyInfo.cs index 0be669bae4..ee0dc5484b 100644 --- a/test/Sentry.Testing/Properties/AssemblyInfo.cs +++ b/test/Sentry.Testing/Properties/AssemblyInfo.cs @@ -1,5 +1,6 @@ [assembly: InternalsVisibleTo("Sentry.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] [assembly: InternalsVisibleTo("Sentry.AspNetCore.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] +[assembly: InternalsVisibleTo("Sentry.AzureFunctions.Worker.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] [assembly: InternalsVisibleTo("Sentry.Google.Cloud.Functions.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] [assembly: InternalsVisibleTo("Sentry.Maui.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] [assembly: InternalsVisibleTo("Sentry.Profiling.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] From c816a758cf2bfbe3c9b7ed6314873c3b498dcabf Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Thu, 18 May 2023 23:41:42 -0700 Subject: [PATCH 038/142] Update CI Solution Filters (#2379) * Update CI Solution Filters * Update Sentry.Full.sln * Update API Snapshots from Azure Functions * Fix broken test --- Sentry-CI-Build-Linux.slnf | 6 ++++++ Sentry-CI-Build-Windows.slnf | 6 ++++++ Sentry-CI-Build-macOS.slnf | 6 ++++++ Sentry-CI-CodeQL.slnf | 2 ++ Sentry-CI-Pack.slnf | 1 + Sentry-CI-Test.slnf | 1 + Sentry.Full.sln | 21 +++++++++++++++++++ ...piApprovalTests.Run.DotNet6_0.verified.txt | 8 ++++++- ...piApprovalTests.Run.DotNet7_0.verified.txt | 8 ++++++- .../ApiApprovalTests.Run.Net4_8.verified.txt | 8 ++++++- .../SentryFunctionsWorkerMiddlewareTests.cs | 2 +- 11 files changed, 65 insertions(+), 4 deletions(-) diff --git a/Sentry-CI-Build-Linux.slnf b/Sentry-CI-Build-Linux.slnf index 3fa053b788..f2e3269495 100644 --- a/Sentry-CI-Build-Linux.slnf +++ b/Sentry-CI-Build-Linux.slnf @@ -11,8 +11,10 @@ "samples\\Sentry.Samples.AspNetCore.Mvc\\Sentry.Samples.AspNetCore.Mvc.csproj", "samples\\Sentry.Samples.AspNetCore.Serilog\\Sentry.Samples.AspNetCore.Serilog.csproj", "samples\\Sentry.Samples.Aws.Lambda.AspNetCoreServer\\Sentry.Samples.Aws.Lambda.AspNetCoreServer.csproj", + "samples\\Sentry.Samples.AzureFunctions.Worker\\Sentry.Samples.AzureFunctions.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.Profiling\\Sentry.Samples.Console.Profiling.csproj", "samples\\Sentry.Samples.EntityFramework\\Sentry.Samples.EntityFramework.csproj", "samples\\Sentry.Samples.GenericHost\\Sentry.Samples.GenericHost.csproj", "samples\\Sentry.Samples.Google.Cloud.Functions\\Sentry.Samples.Google.Cloud.Functions.csproj", @@ -25,6 +27,7 @@ "src\\Sentry.AspNet\\Sentry.AspNet.csproj", "src\\Sentry.AspNetCore.Grpc\\Sentry.AspNetCore.Grpc.csproj", "src\\Sentry.AspNetCore\\Sentry.AspNetCore.csproj", + "src\\Sentry.AzureFunctions.Worker\\Sentry.AzureFunctions.Worker.csproj", "src\\Sentry.Bindings.Android\\Sentry.Bindings.Android.csproj", "src\\Sentry.DiagnosticSource\\Sentry.DiagnosticSource.csproj", "src\\Sentry.EntityFramework\\Sentry.EntityFramework.csproj", @@ -33,6 +36,7 @@ "src\\Sentry.Log4Net\\Sentry.Log4Net.csproj", "src\\Sentry.Maui\\Sentry.Maui.csproj", "src\\Sentry.NLog\\Sentry.NLog.csproj", + "src\\Sentry.Profiling\\Sentry.Profiling.csproj", "src\\Sentry.Serilog\\Sentry.Serilog.csproj", "src\\Sentry\\Sentry.csproj", "test\\Sentry.Android.AssemblyReader.Tests\\Sentry.Android.AssemblyReader.Tests.csproj", @@ -40,6 +44,7 @@ "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", + "test\\Sentry.AzureFunctions.Worker.Tests\\Sentry.AzureFunctions.Worker.Tests.csproj", "test\\Sentry.DiagnosticSource.IntegrationTests\\Sentry.DiagnosticSource.IntegrationTests.csproj", "test\\Sentry.DiagnosticSource.Tests\\Sentry.DiagnosticSource.Tests.csproj", "test\\Sentry.EntityFramework.Tests\\Sentry.EntityFramework.Tests.csproj", @@ -48,6 +53,7 @@ "test\\Sentry.Log4Net.Tests\\Sentry.Log4Net.Tests.csproj", "test\\Sentry.Maui.Tests\\Sentry.Maui.Tests.csproj", "test\\Sentry.NLog.Tests\\Sentry.NLog.Tests.csproj", + "test\\Sentry.Profiling.Tests\\Sentry.Profiling.Tests.csproj", "test\\Sentry.Serilog.Tests\\Sentry.Serilog.Tests.csproj", "test\\Sentry.Testing.CrashableApp\\Sentry.Testing.CrashableApp.csproj", "test\\Sentry.Testing\\Sentry.Testing.csproj", diff --git a/Sentry-CI-Build-Windows.slnf b/Sentry-CI-Build-Windows.slnf index 29f6997146..36b26e0cec 100644 --- a/Sentry-CI-Build-Windows.slnf +++ b/Sentry-CI-Build-Windows.slnf @@ -10,8 +10,10 @@ "samples\\Sentry.Samples.AspNetCore.Mvc\\Sentry.Samples.AspNetCore.Mvc.csproj", "samples\\Sentry.Samples.AspNetCore.Serilog\\Sentry.Samples.AspNetCore.Serilog.csproj", "samples\\Sentry.Samples.Aws.Lambda.AspNetCoreServer\\Sentry.Samples.Aws.Lambda.AspNetCoreServer.csproj", + "samples\\Sentry.Samples.AzureFunctions.Worker\\Sentry.Samples.AzureFunctions.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.Profiling\\Sentry.Samples.Console.Profiling.csproj", "samples\\Sentry.Samples.EntityFramework\\Sentry.Samples.EntityFramework.csproj", "samples\\Sentry.Samples.GenericHost\\Sentry.Samples.GenericHost.csproj", "samples\\Sentry.Samples.Google.Cloud.Functions\\Sentry.Samples.Google.Cloud.Functions.csproj", @@ -24,6 +26,7 @@ "src\\Sentry.AspNet\\Sentry.AspNet.csproj", "src\\Sentry.AspNetCore.Grpc\\Sentry.AspNetCore.Grpc.csproj", "src\\Sentry.AspNetCore\\Sentry.AspNetCore.csproj", + "src\\Sentry.AzureFunctions.Worker\\Sentry.AzureFunctions.Worker.csproj", "src\\Sentry.Bindings.Android\\Sentry.Bindings.Android.csproj", "src\\Sentry.DiagnosticSource\\Sentry.DiagnosticSource.csproj", "src\\Sentry.EntityFramework\\Sentry.EntityFramework.csproj", @@ -32,6 +35,7 @@ "src\\Sentry.Log4Net\\Sentry.Log4Net.csproj", "src\\Sentry.Maui\\Sentry.Maui.csproj", "src\\Sentry.NLog\\Sentry.NLog.csproj", + "src\\Sentry.Profiling\\Sentry.Profiling.csproj", "src\\Sentry.Serilog\\Sentry.Serilog.csproj", "src\\Sentry\\Sentry.csproj", "test\\Sentry.Android.AssemblyReader.Tests\\Sentry.Android.AssemblyReader.Tests.csproj", @@ -39,6 +43,7 @@ "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", + "test\\Sentry.AzureFunctions.Worker.Tests\\Sentry.AzureFunctions.Worker.Tests.csproj", "test\\Sentry.DiagnosticSource.IntegrationTests\\Sentry.DiagnosticSource.IntegrationTests.csproj", "test\\Sentry.DiagnosticSource.Tests\\Sentry.DiagnosticSource.Tests.csproj", "test\\Sentry.EntityFramework.Tests\\Sentry.EntityFramework.Tests.csproj", @@ -47,6 +52,7 @@ "test\\Sentry.Log4Net.Tests\\Sentry.Log4Net.Tests.csproj", "test\\Sentry.Maui.Tests\\Sentry.Maui.Tests.csproj", "test\\Sentry.NLog.Tests\\Sentry.NLog.Tests.csproj", + "test\\Sentry.Profiling.Tests\\Sentry.Profiling.Tests.csproj", "test\\Sentry.Serilog.Tests\\Sentry.Serilog.Tests.csproj", "test\\Sentry.Testing.CrashableApp\\Sentry.Testing.CrashableApp.csproj", "test\\Sentry.Testing\\Sentry.Testing.csproj", diff --git a/Sentry-CI-Build-macOS.slnf b/Sentry-CI-Build-macOS.slnf index f2f4343047..d0f04ce844 100644 --- a/Sentry-CI-Build-macOS.slnf +++ b/Sentry-CI-Build-macOS.slnf @@ -10,8 +10,10 @@ "samples\\Sentry.Samples.AspNetCore.Mvc\\Sentry.Samples.AspNetCore.Mvc.csproj", "samples\\Sentry.Samples.AspNetCore.Serilog\\Sentry.Samples.AspNetCore.Serilog.csproj", "samples\\Sentry.Samples.Aws.Lambda.AspNetCoreServer\\Sentry.Samples.Aws.Lambda.AspNetCoreServer.csproj", + "samples\\Sentry.Samples.AzureFunctions.Worker\\Sentry.Samples.AzureFunctions.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.Profiling\\Sentry.Samples.Console.Profiling.csproj", "samples\\Sentry.Samples.EntityFramework\\Sentry.Samples.EntityFramework.csproj", "samples\\Sentry.Samples.GenericHost\\Sentry.Samples.GenericHost.csproj", "samples\\Sentry.Samples.Google.Cloud.Functions\\Sentry.Samples.Google.Cloud.Functions.csproj", @@ -26,6 +28,7 @@ "src\\Sentry.AspNet\\Sentry.AspNet.csproj", "src\\Sentry.AspNetCore.Grpc\\Sentry.AspNetCore.Grpc.csproj", "src\\Sentry.AspNetCore\\Sentry.AspNetCore.csproj", + "src\\Sentry.AzureFunctions.Worker\\Sentry.AzureFunctions.Worker.csproj", "src\\Sentry.Bindings.Android\\Sentry.Bindings.Android.csproj", "src\\Sentry.Bindings.Cocoa\\Sentry.Bindings.Cocoa.csproj", "src\\Sentry.DiagnosticSource\\Sentry.DiagnosticSource.csproj", @@ -35,6 +38,7 @@ "src\\Sentry.Log4Net\\Sentry.Log4Net.csproj", "src\\Sentry.Maui\\Sentry.Maui.csproj", "src\\Sentry.NLog\\Sentry.NLog.csproj", + "src\\Sentry.Profiling\\Sentry.Profiling.csproj", "src\\Sentry.Serilog\\Sentry.Serilog.csproj", "src\\Sentry\\Sentry.csproj", "test\\Sentry.Android.AssemblyReader.Tests\\Sentry.Android.AssemblyReader.Tests.csproj", @@ -42,6 +46,7 @@ "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", + "test\\Sentry.AzureFunctions.Worker.Tests\\Sentry.AzureFunctions.Worker.Tests.csproj", "test\\Sentry.DiagnosticSource.IntegrationTests\\Sentry.DiagnosticSource.IntegrationTests.csproj", "test\\Sentry.DiagnosticSource.Tests\\Sentry.DiagnosticSource.Tests.csproj", "test\\Sentry.EntityFramework.Tests\\Sentry.EntityFramework.Tests.csproj", @@ -50,6 +55,7 @@ "test\\Sentry.Log4Net.Tests\\Sentry.Log4Net.Tests.csproj", "test\\Sentry.Maui.Tests\\Sentry.Maui.Tests.csproj", "test\\Sentry.NLog.Tests\\Sentry.NLog.Tests.csproj", + "test\\Sentry.Profiling.Tests\\Sentry.Profiling.Tests.csproj", "test\\Sentry.Serilog.Tests\\Sentry.Serilog.Tests.csproj", "test\\Sentry.Testing.CrashableApp\\Sentry.Testing.CrashableApp.csproj", "test\\Sentry.Testing\\Sentry.Testing.csproj", diff --git a/Sentry-CI-CodeQL.slnf b/Sentry-CI-CodeQL.slnf index c58a8c2bef..bb902fc307 100644 --- a/Sentry-CI-CodeQL.slnf +++ b/Sentry-CI-CodeQL.slnf @@ -6,6 +6,7 @@ "src\\Sentry.AspNetCore.Grpc\\Sentry.AspNetCore.Grpc.csproj", "src\\Sentry.AspNetCore\\Sentry.AspNetCore.csproj", "src\\Sentry.AspNet\\Sentry.AspNet.csproj", + "src\\Sentry.AzureFunctions.Worker\\Sentry.AzureFunctions.Worker.csproj", "src\\Sentry.DiagnosticSource\\Sentry.DiagnosticSource.csproj", "src\\Sentry.EntityFramework\\Sentry.EntityFramework.csproj", "src\\Sentry.Extensions.Logging\\Sentry.Extensions.Logging.csproj", @@ -13,6 +14,7 @@ "src\\Sentry.Log4Net\\Sentry.Log4Net.csproj", "src\\Sentry.Maui\\Sentry.Maui.csproj", "src\\Sentry.NLog\\Sentry.NLog.csproj", + "src\\Sentry.Profiling\\Sentry.Profiling.csproj", "src\\Sentry.Serilog\\Sentry.Serilog.csproj", "src\\Sentry\\Sentry.csproj" ] diff --git a/Sentry-CI-Pack.slnf b/Sentry-CI-Pack.slnf index 1d3f436ba7..7d692c9452 100644 --- a/Sentry-CI-Pack.slnf +++ b/Sentry-CI-Pack.slnf @@ -6,6 +6,7 @@ "src\\Sentry.AspNetCore.Grpc\\Sentry.AspNetCore.Grpc.csproj", "src\\Sentry.AspNetCore\\Sentry.AspNetCore.csproj", "src\\Sentry.AspNet\\Sentry.AspNet.csproj", + "src\\Sentry.AzureFunctions.Worker\\Sentry.AzureFunctions.Worker.csproj", "src\\Sentry.Bindings.Android\\Sentry.Bindings.Android.csproj", "src\\Sentry.Bindings.Cocoa\\Sentry.Bindings.Cocoa.csproj", "src\\Sentry.DiagnosticSource\\Sentry.DiagnosticSource.csproj", diff --git a/Sentry-CI-Test.slnf b/Sentry-CI-Test.slnf index 1cf8c18c32..edbe64616d 100644 --- a/Sentry-CI-Test.slnf +++ b/Sentry-CI-Test.slnf @@ -3,6 +3,7 @@ "path": "Sentry.Full.sln", "projects": [ "test\\Sentry.Android.AssemblyReader.Tests\\Sentry.Android.AssemblyReader.Tests.csproj", + "test\\Sentry.AzureFunctions.Worker.Tests\\Sentry.AzureFunctions.Worker.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/Sentry.Full.sln b/Sentry.Full.sln index 1f14b5508e..366ebfe990 100644 --- a/Sentry.Full.sln +++ b/Sentry.Full.sln @@ -178,6 +178,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.Profiling.Tests", "t EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.Samples.Console.Profiling", "samples\Sentry.Samples.Console.Profiling\Sentry.Samples.Console.Profiling.csproj", "{0F84C0BB-FDD4-43A9-B594-923EB10C8E3F}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.AzureFunctions.Worker", "src\Sentry.AzureFunctions.Worker\Sentry.AzureFunctions.Worker.csproj", "{DDDF66BE-EC0E-4488-8A35-3D8BEB63F2DB}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Samples.AzureFunctions.Worker", "samples\Sentry.Samples.AzureFunctions.Worker\Sentry.Samples.AzureFunctions.Worker.csproj", "{C38B2A7A-0B21-41BA-8AE4-5D0A2AB6B029}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.AzureFunctions.Worker.Tests", "test\Sentry.AzureFunctions.Worker.Tests\Sentry.AzureFunctions.Worker.Tests.csproj", "{CCABBDB9-CDEF-4DE6-9264-69797E8831DF}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -433,6 +439,18 @@ Global {0F84C0BB-FDD4-43A9-B594-923EB10C8E3F}.Debug|Any CPU.Build.0 = Debug|Any CPU {0F84C0BB-FDD4-43A9-B594-923EB10C8E3F}.Release|Any CPU.ActiveCfg = Release|Any CPU {0F84C0BB-FDD4-43A9-B594-923EB10C8E3F}.Release|Any CPU.Build.0 = Release|Any CPU + {DDDF66BE-EC0E-4488-8A35-3D8BEB63F2DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DDDF66BE-EC0E-4488-8A35-3D8BEB63F2DB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DDDF66BE-EC0E-4488-8A35-3D8BEB63F2DB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DDDF66BE-EC0E-4488-8A35-3D8BEB63F2DB}.Release|Any CPU.Build.0 = Release|Any CPU + {C38B2A7A-0B21-41BA-8AE4-5D0A2AB6B029}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C38B2A7A-0B21-41BA-8AE4-5D0A2AB6B029}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C38B2A7A-0B21-41BA-8AE4-5D0A2AB6B029}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C38B2A7A-0B21-41BA-8AE4-5D0A2AB6B029}.Release|Any CPU.Build.0 = Release|Any CPU + {CCABBDB9-CDEF-4DE6-9264-69797E8831DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CCABBDB9-CDEF-4DE6-9264-69797E8831DF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CCABBDB9-CDEF-4DE6-9264-69797E8831DF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CCABBDB9-CDEF-4DE6-9264-69797E8831DF}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -502,6 +520,9 @@ Global {BD6CEF44-E05E-4C22-8D2F-0558A93DD2D6} = {AF6AF4C7-8AA2-4D59-8064-2D79560904EB} {2E750A7C-561D-4959-B967-042755139D84} = {83263231-1A2A-4733-B759-EEFF14E8C5D5} {0F84C0BB-FDD4-43A9-B594-923EB10C8E3F} = {77454495-55EE-4B40-A089-71B9E8F82E89} + {DDDF66BE-EC0E-4488-8A35-3D8BEB63F2DB} = {AF6AF4C7-8AA2-4D59-8064-2D79560904EB} + {C38B2A7A-0B21-41BA-8AE4-5D0A2AB6B029} = {77454495-55EE-4B40-A089-71B9E8F82E89} + {CCABBDB9-CDEF-4DE6-9264-69797E8831DF} = {83263231-1A2A-4733-B759-EEFF14E8C5D5} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {0C652B1A-DF72-4EE5-A98B-194FE2C054F6} diff --git a/test/Sentry.AzureFunctions.Worker.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt b/test/Sentry.AzureFunctions.Worker.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt index 6a3ae15d2a..313d900c70 100644 --- a/test/Sentry.AzureFunctions.Worker.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt +++ b/test/Sentry.AzureFunctions.Worker.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt @@ -1,7 +1,13 @@ namespace Sentry.AzureFunctions.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, System.Action? optionsConfiguration) { } + 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.AzureFunctions.Worker.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt b/test/Sentry.AzureFunctions.Worker.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt index 6a3ae15d2a..313d900c70 100644 --- a/test/Sentry.AzureFunctions.Worker.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt +++ b/test/Sentry.AzureFunctions.Worker.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt @@ -1,7 +1,13 @@ namespace Sentry.AzureFunctions.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, System.Action? optionsConfiguration) { } + 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.AzureFunctions.Worker.Tests/ApiApprovalTests.Run.Net4_8.verified.txt b/test/Sentry.AzureFunctions.Worker.Tests/ApiApprovalTests.Run.Net4_8.verified.txt index 6a3ae15d2a..313d900c70 100644 --- a/test/Sentry.AzureFunctions.Worker.Tests/ApiApprovalTests.Run.Net4_8.verified.txt +++ b/test/Sentry.AzureFunctions.Worker.Tests/ApiApprovalTests.Run.Net4_8.verified.txt @@ -1,7 +1,13 @@ namespace Sentry.AzureFunctions.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, System.Action? optionsConfiguration) { } + 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.AzureFunctions.Worker.Tests/SentryFunctionsWorkerMiddlewareTests.cs b/test/Sentry.AzureFunctions.Worker.Tests/SentryFunctionsWorkerMiddlewareTests.cs index 7dc9c2aa53..b6ed452831 100644 --- a/test/Sentry.AzureFunctions.Worker.Tests/SentryFunctionsWorkerMiddlewareTests.cs +++ b/test/Sentry.AzureFunctions.Worker.Tests/SentryFunctionsWorkerMiddlewareTests.cs @@ -22,7 +22,7 @@ public Fixture() var client = Substitute.For(); var sessionManager = Substitute.For(); - client.When(x => x.CaptureTransaction(Arg.Any())) + client.When(x => x.CaptureTransaction(Arg.Any(), Arg.Any())) .Do(callback => Transaction = callback.Arg()); ScopeManager = new SentryScopeManager(options, client); From 35e6de37a3750c92af7dcc5e661a8454ee9d7efa Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 20 May 2023 09:31:50 -0700 Subject: [PATCH 039/142] chore: update scripts/update-java.ps1 to 6.19.1 (#2381) Co-authored-by: GitHub --- CHANGELOG.md | 6 +++--- src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bd6454d833..f87a21fea0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,9 +33,9 @@ - Bump Cocoa SDK from v8.6.0 to v8.7.1 ([#2359](https://github.com/getsentry/sentry-dotnet/pull/2359), [#2370](https://github.com/getsentry/sentry-dotnet/pull/2370)) - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#871) - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.6.0...8.7.1) -- Bump Java SDK from v6.18.1 to v6.19.0 ([#2374](https://github.com/getsentry/sentry-dotnet/pull/2374)) - - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#6190) - - [diff](https://github.com/getsentry/sentry-java/compare/6.18.1...6.19.0) +- Bump Java SDK from v6.18.1 to v6.19.1 ([#2374](https://github.com/getsentry/sentry-dotnet/pull/2374), [#2381](https://github.com/getsentry/sentry-dotnet/pull/2381)) + - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#6191) + - [diff](https://github.com/getsentry/sentry-java/compare/6.18.1...6.19.1) - Bump Cocoa SDK from v8.6.0 to v8.7.2 ([#2359](https://github.com/getsentry/sentry-dotnet/pull/2359), [#2370](https://github.com/getsentry/sentry-dotnet/pull/2370), [#2375](https://github.com/getsentry/sentry-dotnet/pull/2375)) - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#872) - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.6.0...8.7.2) diff --git a/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj b/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj index 134afaf31a..a606510870 100644 --- a/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj +++ b/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj @@ -3,7 +3,7 @@ net6.0-android $(NoWarn);BG8605;BG8606 - 6.19.0 + 6.19.1 $(BaseIntermediateOutputPath)sdks\Sentry\Android\$(SentryAndroidSdkVersion)\ .NET Bindings for the Sentry Android SDK From acb9de4e524ddff669d7efbc5549679bf6545ff6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 20 May 2023 09:32:20 -0700 Subject: [PATCH 040/142] chore: update scripts/update-cli.ps1 to 2.18.0 (#2380) 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 f87a21fea0..f04dbc850e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,9 @@ - Bump Cocoa SDK from v8.6.0 to v8.7.2 ([#2359](https://github.com/getsentry/sentry-dotnet/pull/2359), [#2370](https://github.com/getsentry/sentry-dotnet/pull/2370), [#2375](https://github.com/getsentry/sentry-dotnet/pull/2375)) - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#872) - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.6.0...8.7.2) +- Bump CLI from v2.17.5 to v2.18.0 ([#2380](https://github.com/getsentry/sentry-dotnet/pull/2380)) + - [changelog](https://github.com/getsentry/sentry-cli/blob/master/CHANGELOG.md#2180) + - [diff](https://github.com/getsentry/sentry-cli/compare/2.17.5...2.18.0) ## 3.31.0 diff --git a/Directory.Build.props b/Directory.Build.props index 90f49c1bd9..538e158482 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -100,7 +100,7 @@ - 2.17.5 + 2.18.0 $(MSBuildThisFileDirectory)tools\sentry-cli\$(SentryCLIVersion)\ diff --git a/src/Sentry/Sentry.csproj b/src/Sentry/Sentry.csproj index 83a535c652..f5187afe22 100644 --- a/src/Sentry/Sentry.csproj +++ b/src/Sentry/Sentry.csproj @@ -94,25 +94,25 @@ + Include="sentry-cli-Darwin-arm64" FileHash="0f7160dbc04911944608f6337ac9ca75b9844280aea34e5cf5f2da5adf76bfd9" /> + Include="sentry-cli-Darwin-x86_64" FileHash="0aba34546a9d38ccd6598a7da83fcbfdaba3a1ed4457de7ee7e3506bb3799227" /> + Include="sentry-cli-Linux-aarch64" FileHash="c13c8ce3c407401dfd29f17b9770d67ced70415b027a9325ed25685fc0c7ef6f" /> + Include="sentry-cli-Linux-i686" FileHash="68f7b1972300b4b9652745765163caa3a86609c4f2686ce8d85fa27becc63407" /> + Include="sentry-cli-Linux-x86_64" FileHash="3eafddcc1affcc97afb210b30c60bb85eda3a67eb658e1ee6fa9230aa717e244" /> + Include="sentry-cli-Windows-i686.exe" FileHash="1043dcc85a1fe35c857776cf27cd9a7b4ec68b03e160e262d18471dbb11b20b7" /> + Include="sentry-cli-Windows-x86_64.exe" FileHash="23ebc233a2e085249b0bfeff2b11617e6212d81525363978fbfa5679687881a2" /> From f1a07b5488b5647d31d632dc32e6b4c4ea7a9fec Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Sun, 21 May 2023 20:36:41 -0700 Subject: [PATCH 041/142] Cleanup (#2382) * Relocate CLSCompliant attribute to msbuild property * Move Android attributes * Relocate InternalsVisibleTo attributes to MSBuild properties * Remove old style attributes * Bump Polyfill library to latest * Bump Verify library to latest --- Directory.Build.props | 10 ++++ Directory.Build.targets | 7 +++ .../Properties/GlobalSuppressions.cs | 1 - .../Properties/AssemblyInfo.cs | 1 - .../Sentry.Android.AssemblyReader.csproj | 4 ++ src/Sentry.AspNet/Properties/AssemblyInfo.cs | 1 - src/Sentry.AspNet/Sentry.AspNet.csproj | 4 ++ .../Properties/AssemblyInfo.cs | 2 - .../Sentry.AspNetCore.Grpc.csproj | 4 ++ .../Properties/AssemblyInfo.cs | 5 -- .../Sentry.AspNetCore.csproj | 7 +++ .../Sentry.AzureFunctions.Worker.csproj | 2 +- .../Properties/AssemblyInfo.cs | 5 -- .../Sentry.Bindings.Android.csproj | 8 +++ .../Properties/AssemblyInfo.cs | 5 -- .../Sentry.Bindings.Cocoa.csproj | 8 +++ .../Properties/AssemblyInfo.cs | 2 - .../Sentry.DiagnosticSource.csproj | 8 +++ .../Properties/AssemblyInfo.cs | 1 - .../Sentry.EntityFramework.csproj | 7 ++- .../Properties/AssemblyInfo.cs | 11 ---- .../Sentry.Extensions.Logging.csproj | 14 +++++ src/Sentry.Log4Net/Properties/AssemblyInfo.cs | 3 -- src/Sentry.Log4Net/Sentry.Log4Net.csproj | 13 ++++- src/Sentry.Maui/Properties/AssemblyInfo.cs | 2 - src/Sentry.Maui/Sentry.Maui.csproj | 4 ++ src/Sentry.NLog/Properties/AssemblyInfo.cs | 3 -- src/Sentry.NLog/Sentry.NLog.csproj | 9 ++++ .../Properties/AssemblyInfo.cs | 3 -- src/Sentry.Profiling/Sentry.Profiling.csproj | 5 ++ src/Sentry.Serilog/Properties/AssemblyInfo.cs | 3 -- src/Sentry.Serilog/Sentry.Serilog.csproj | 7 +++ src/Sentry/Platforms/Android/SentrySdk.cs | 8 +++ src/Sentry/Properties/AssemblyInfo.cs | 52 ------------------- src/Sentry/Sentry.csproj | 39 +++++++++++++- test/Directory.Build.props | 2 +- .../Properties/AssemblyInfo.cs | 1 - .../Properties/AssemblyInfo.cs | 1 - .../Properties/AssemblyInfo.cs | 1 - .../Properties/AssemblyInfo.cs | 1 - .../Properties/AssemblyInfo.cs | 1 - .../Properties/AssemblyInfo.cs | 1 - .../Properties/AssemblyInfo.cs | 1 - .../Properties/AssemblyInfo.cs | 1 - .../Properties/AssemblyInfo.cs | 1 - .../Properties/AssemblyInfo.cs | 1 - .../Properties/AssemblyInfo.cs | 1 - .../Sentry.Testing/Properties/AssemblyInfo.cs | 7 --- test/Sentry.Testing/Sentry.Testing.csproj | 9 ++++ test/Sentry.Tests/Properties/AssemblyInfo.cs | 1 - 50 files changed, 173 insertions(+), 125 deletions(-) delete mode 100644 benchmarks/Sentry.Benchmarks/Properties/GlobalSuppressions.cs delete mode 100644 src/Sentry.Android.AssemblyReader/Properties/AssemblyInfo.cs delete mode 100644 src/Sentry.AspNet/Properties/AssemblyInfo.cs delete mode 100644 src/Sentry.AspNetCore.Grpc/Properties/AssemblyInfo.cs delete mode 100644 src/Sentry.AspNetCore/Properties/AssemblyInfo.cs delete mode 100644 src/Sentry.Bindings.Android/Properties/AssemblyInfo.cs delete mode 100644 src/Sentry.Bindings.Cocoa/Properties/AssemblyInfo.cs delete mode 100644 src/Sentry.DiagnosticSource/Properties/AssemblyInfo.cs delete mode 100644 src/Sentry.EntityFramework/Properties/AssemblyInfo.cs delete mode 100644 src/Sentry.Extensions.Logging/Properties/AssemblyInfo.cs delete mode 100644 src/Sentry.Log4Net/Properties/AssemblyInfo.cs delete mode 100644 src/Sentry.Maui/Properties/AssemblyInfo.cs delete mode 100644 src/Sentry.NLog/Properties/AssemblyInfo.cs delete mode 100644 src/Sentry.Profiling/Properties/AssemblyInfo.cs delete mode 100644 src/Sentry.Serilog/Properties/AssemblyInfo.cs delete mode 100644 src/Sentry/Properties/AssemblyInfo.cs delete mode 100644 test/Sentry.AspNet.Tests/Properties/AssemblyInfo.cs delete mode 100644 test/Sentry.AspNetCore.Grpc.Tests/Properties/AssemblyInfo.cs delete mode 100644 test/Sentry.AspNetCore.Tests/Properties/AssemblyInfo.cs delete mode 100644 test/Sentry.DiagnosticSource.Tests/Properties/AssemblyInfo.cs delete mode 100644 test/Sentry.EntityFramework.Tests/Properties/AssemblyInfo.cs delete mode 100644 test/Sentry.Extensions.Logging.Tests/Properties/AssemblyInfo.cs delete mode 100644 test/Sentry.Google.Cloud.Functions.Tests/Properties/AssemblyInfo.cs delete mode 100644 test/Sentry.Log4Net.Tests/Properties/AssemblyInfo.cs delete mode 100644 test/Sentry.NLog.Tests/Properties/AssemblyInfo.cs delete mode 100644 test/Sentry.Profiling.Tests/Properties/AssemblyInfo.cs delete mode 100644 test/Sentry.Serilog.Tests/Properties/AssemblyInfo.cs delete mode 100644 test/Sentry.Testing/Properties/AssemblyInfo.cs delete mode 100644 test/Sentry.Tests/Properties/AssemblyInfo.cs diff --git a/Directory.Build.props b/Directory.Build.props index 538e158482..9d92e79e00 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -104,4 +104,14 @@ $(MSBuildThisFileDirectory)tools\sentry-cli\$(SentryCLIVersion)\ + + + 002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8 + + + + + + + diff --git a/Directory.Build.targets b/Directory.Build.targets index 842d79b735..a4ae282d39 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -34,6 +34,13 @@ + + + + <_Parameter1>true + + + + + + + diff --git a/src/Sentry.AspNet/Properties/AssemblyInfo.cs b/src/Sentry.AspNet/Properties/AssemblyInfo.cs deleted file mode 100644 index de9b9c1d08..0000000000 --- a/src/Sentry.AspNet/Properties/AssemblyInfo.cs +++ /dev/null @@ -1 +0,0 @@ -[assembly: InternalsVisibleTo("Sentry.AspNet.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] diff --git a/src/Sentry.AspNet/Sentry.AspNet.csproj b/src/Sentry.AspNet/Sentry.AspNet.csproj index 2bbb4ddf1c..58aaa712ba 100644 --- a/src/Sentry.AspNet/Sentry.AspNet.csproj +++ b/src/Sentry.AspNet/Sentry.AspNet.csproj @@ -16,4 +16,8 @@ + + + + diff --git a/src/Sentry.AspNetCore.Grpc/Properties/AssemblyInfo.cs b/src/Sentry.AspNetCore.Grpc/Properties/AssemblyInfo.cs deleted file mode 100644 index 92ec37b759..0000000000 --- a/src/Sentry.AspNetCore.Grpc/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,2 +0,0 @@ -[assembly: InternalsVisibleTo("Sentry.AspNetCore.Grpc.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] diff --git a/src/Sentry.AspNetCore.Grpc/Sentry.AspNetCore.Grpc.csproj b/src/Sentry.AspNetCore.Grpc/Sentry.AspNetCore.Grpc.csproj index 9da41db8f8..cc14bb5f3f 100644 --- a/src/Sentry.AspNetCore.Grpc/Sentry.AspNetCore.Grpc.csproj +++ b/src/Sentry.AspNetCore.Grpc/Sentry.AspNetCore.Grpc.csproj @@ -15,4 +15,8 @@ + + + + diff --git a/src/Sentry.AspNetCore/Properties/AssemblyInfo.cs b/src/Sentry.AspNetCore/Properties/AssemblyInfo.cs deleted file mode 100644 index 7d2fcaca96..0000000000 --- a/src/Sentry.AspNetCore/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,5 +0,0 @@ -[assembly: InternalsVisibleTo("Sentry.AspNetCore.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.AspNetCore.Grpc, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.Google.Cloud.Functions, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.Google.Cloud.Functions.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] diff --git a/src/Sentry.AspNetCore/Sentry.AspNetCore.csproj b/src/Sentry.AspNetCore/Sentry.AspNetCore.csproj index ffa904e888..a63694bb01 100644 --- a/src/Sentry.AspNetCore/Sentry.AspNetCore.csproj +++ b/src/Sentry.AspNetCore/Sentry.AspNetCore.csproj @@ -28,4 +28,11 @@ + + + + + + + diff --git a/src/Sentry.AzureFunctions.Worker/Sentry.AzureFunctions.Worker.csproj b/src/Sentry.AzureFunctions.Worker/Sentry.AzureFunctions.Worker.csproj index b2eb21a87f..d82057796b 100644 --- a/src/Sentry.AzureFunctions.Worker/Sentry.AzureFunctions.Worker.csproj +++ b/src/Sentry.AzureFunctions.Worker/Sentry.AzureFunctions.Worker.csproj @@ -17,7 +17,7 @@ - + diff --git a/src/Sentry.Bindings.Android/Properties/AssemblyInfo.cs b/src/Sentry.Bindings.Android/Properties/AssemblyInfo.cs deleted file mode 100644 index 730c98124e..0000000000 --- a/src/Sentry.Bindings.Android/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,5 +0,0 @@ -[assembly: InternalsVisibleTo("Sentry, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.Testing, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.Extensions.Logging.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.Maui.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] diff --git a/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj b/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj index a606510870..fc77b422f6 100644 --- a/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj +++ b/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj @@ -15,6 +15,14 @@ + + + + + + + + diff --git a/src/Sentry.Bindings.Cocoa/Properties/AssemblyInfo.cs b/src/Sentry.Bindings.Cocoa/Properties/AssemblyInfo.cs deleted file mode 100644 index 730c98124e..0000000000 --- a/src/Sentry.Bindings.Cocoa/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,5 +0,0 @@ -[assembly: InternalsVisibleTo("Sentry, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.Testing, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.Extensions.Logging.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.Maui.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] diff --git a/src/Sentry.Bindings.Cocoa/Sentry.Bindings.Cocoa.csproj b/src/Sentry.Bindings.Cocoa/Sentry.Bindings.Cocoa.csproj index 09f337deef..eae4991eb4 100644 --- a/src/Sentry.Bindings.Cocoa/Sentry.Bindings.Cocoa.csproj +++ b/src/Sentry.Bindings.Cocoa/Sentry.Bindings.Cocoa.csproj @@ -17,6 +17,14 @@ + + + + + + + + diff --git a/src/Sentry.DiagnosticSource/Properties/AssemblyInfo.cs b/src/Sentry.DiagnosticSource/Properties/AssemblyInfo.cs deleted file mode 100644 index 03c3b03207..0000000000 --- a/src/Sentry.DiagnosticSource/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,2 +0,0 @@ -[assembly: InternalsVisibleTo("Sentry.DiagnosticSource.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.DiagnosticSource.IntegrationTests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] diff --git a/src/Sentry.DiagnosticSource/Sentry.DiagnosticSource.csproj b/src/Sentry.DiagnosticSource/Sentry.DiagnosticSource.csproj index beb8201b3c..75ccb002d1 100644 --- a/src/Sentry.DiagnosticSource/Sentry.DiagnosticSource.csproj +++ b/src/Sentry.DiagnosticSource/Sentry.DiagnosticSource.csproj @@ -11,7 +11,15 @@ + + + + + + + + diff --git a/src/Sentry.EntityFramework/Properties/AssemblyInfo.cs b/src/Sentry.EntityFramework/Properties/AssemblyInfo.cs deleted file mode 100644 index 5997af05f3..0000000000 --- a/src/Sentry.EntityFramework/Properties/AssemblyInfo.cs +++ /dev/null @@ -1 +0,0 @@ -[assembly: InternalsVisibleTo("Sentry.EntityFramework.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] diff --git a/src/Sentry.EntityFramework/Sentry.EntityFramework.csproj b/src/Sentry.EntityFramework/Sentry.EntityFramework.csproj index db4f9e10dd..50b60172db 100644 --- a/src/Sentry.EntityFramework/Sentry.EntityFramework.csproj +++ b/src/Sentry.EntityFramework/Sentry.EntityFramework.csproj @@ -8,7 +8,9 @@ + + @@ -17,7 +19,6 @@ - @@ -31,4 +32,8 @@ + + + + diff --git a/src/Sentry.Extensions.Logging/Properties/AssemblyInfo.cs b/src/Sentry.Extensions.Logging/Properties/AssemblyInfo.cs deleted file mode 100644 index 127b03cbee..0000000000 --- a/src/Sentry.Extensions.Logging/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,11 +0,0 @@ -[assembly: InternalsVisibleTo("Sentry.Extensions.Logging.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.Extensions.Logging.EfCore.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.AspNetCore.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.AspNetCore, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.AspNetCore.Grpc, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.AspNetCore.Grpc.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.AzureFunctions.Worker, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.AzureFunctions.Worker.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.Google.Cloud.Functions.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.DiagnosticSource.IntegrationTests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] - diff --git a/src/Sentry.Extensions.Logging/Sentry.Extensions.Logging.csproj b/src/Sentry.Extensions.Logging/Sentry.Extensions.Logging.csproj index 91990757d1..e012e562dc 100644 --- a/src/Sentry.Extensions.Logging/Sentry.Extensions.Logging.csproj +++ b/src/Sentry.Extensions.Logging/Sentry.Extensions.Logging.csproj @@ -30,4 +30,18 @@ + + + + + + + + + + + + + + diff --git a/src/Sentry.Log4Net/Properties/AssemblyInfo.cs b/src/Sentry.Log4Net/Properties/AssemblyInfo.cs deleted file mode 100644 index 3f8f6f4d3e..0000000000 --- a/src/Sentry.Log4Net/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,3 +0,0 @@ -[assembly: InternalsVisibleTo("Sentry.Log4Net.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] - -[assembly: CLSCompliant(true)] diff --git a/src/Sentry.Log4Net/Sentry.Log4Net.csproj b/src/Sentry.Log4Net/Sentry.Log4Net.csproj index 0158f23a4d..0063ee57a0 100644 --- a/src/Sentry.Log4Net/Sentry.Log4Net.csproj +++ b/src/Sentry.Log4Net/Sentry.Log4Net.csproj @@ -4,17 +4,26 @@ netstandard2.1;netstandard2.0;net461 $(PackageTags);Logging;log4net Official log4net integration for Sentry - Open-source error tracking that helps developers monitor and fix crashes in real time. + true - + + + - + + + + + + + diff --git a/src/Sentry.Maui/Properties/AssemblyInfo.cs b/src/Sentry.Maui/Properties/AssemblyInfo.cs deleted file mode 100644 index 00108f9b1d..0000000000 --- a/src/Sentry.Maui/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,2 +0,0 @@ -[assembly: InternalsVisibleTo("Sentry.Maui.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] diff --git a/src/Sentry.Maui/Sentry.Maui.csproj b/src/Sentry.Maui/Sentry.Maui.csproj index 4954daa00b..86b8e2371d 100644 --- a/src/Sentry.Maui/Sentry.Maui.csproj +++ b/src/Sentry.Maui/Sentry.Maui.csproj @@ -37,4 +37,8 @@ + + + + diff --git a/src/Sentry.NLog/Properties/AssemblyInfo.cs b/src/Sentry.NLog/Properties/AssemblyInfo.cs deleted file mode 100644 index b46ab16c6e..0000000000 --- a/src/Sentry.NLog/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,3 +0,0 @@ -[assembly: InternalsVisibleTo("Sentry.NLog.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] - -[assembly: CLSCompliant(true)] diff --git a/src/Sentry.NLog/Sentry.NLog.csproj b/src/Sentry.NLog/Sentry.NLog.csproj index a7e78ae2c8..15e4f387c4 100644 --- a/src/Sentry.NLog/Sentry.NLog.csproj +++ b/src/Sentry.NLog/Sentry.NLog.csproj @@ -4,13 +4,18 @@ net6.0;net5.0;netstandard2.1;netstandard2.0;net461 $(PackageTags);Logging;NLog Official NLog integration for Sentry - Open-source error tracking that helps developers monitor and fix crashes in real time. + true + + + + @@ -22,4 +27,8 @@ + + + + diff --git a/src/Sentry.Profiling/Properties/AssemblyInfo.cs b/src/Sentry.Profiling/Properties/AssemblyInfo.cs deleted file mode 100644 index 00e73812b2..0000000000 --- a/src/Sentry.Profiling/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,3 +0,0 @@ -[assembly: InternalsVisibleTo("Sentry.Profiling.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.Benchmarks, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] - diff --git a/src/Sentry.Profiling/Sentry.Profiling.csproj b/src/Sentry.Profiling/Sentry.Profiling.csproj index f85e4abd9d..59703ed9df 100644 --- a/src/Sentry.Profiling/Sentry.Profiling.csproj +++ b/src/Sentry.Profiling/Sentry.Profiling.csproj @@ -15,4 +15,9 @@ + + + + + diff --git a/src/Sentry.Serilog/Properties/AssemblyInfo.cs b/src/Sentry.Serilog/Properties/AssemblyInfo.cs deleted file mode 100644 index ea43a61f95..0000000000 --- a/src/Sentry.Serilog/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,3 +0,0 @@ -[assembly: InternalsVisibleTo("Sentry.Serilog.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] - -[assembly: CLSCompliant(true)] diff --git a/src/Sentry.Serilog/Sentry.Serilog.csproj b/src/Sentry.Serilog/Sentry.Serilog.csproj index a6f1e88438..d6ce1133a6 100644 --- a/src/Sentry.Serilog/Sentry.Serilog.csproj +++ b/src/Sentry.Serilog/Sentry.Serilog.csproj @@ -4,11 +4,14 @@ net6.0;net5.0;netstandard2.1;netstandard2.0;net461 $(PackageTags);Logging;Serilog Official Serilog integration for Sentry - Open-source error tracking that helps developers monitor and fix crashes in real time. + true + + @@ -28,4 +31,8 @@ + + + + diff --git a/src/Sentry/Platforms/Android/SentrySdk.cs b/src/Sentry/Platforms/Android/SentrySdk.cs index bad53c0039..01c25f5efc 100644 --- a/src/Sentry/Platforms/Android/SentrySdk.cs +++ b/src/Sentry/Platforms/Android/SentrySdk.cs @@ -5,6 +5,14 @@ using Sentry.Android.Extensions; using Sentry.JavaSdk.Android.Core; +// Don't let the Sentry Android SDK auto-init, as we do that manually in SentrySdk.Init +// See https://docs.sentry.io/platforms/android/configuration/manual-init/ +// This attribute automatically adds the metadata to the final AndroidManifest.xml +[assembly: MetaData("io.sentry.auto-init", Value = "false")] + +// Set the hybrid SDK name +[assembly: MetaData("io.sentry.sdk.name", Value = "sentry.java.android.dotnet")] + // ReSharper disable once CheckNamespace namespace Sentry; diff --git a/src/Sentry/Properties/AssemblyInfo.cs b/src/Sentry/Properties/AssemblyInfo.cs deleted file mode 100644 index 2e87eced74..0000000000 --- a/src/Sentry/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,52 +0,0 @@ -[assembly: InternalsVisibleTo("Sentry.Android.AssemblyReader.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.AspNet, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.AspNet.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.AspNetCore.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.AspNetCore.TestUtils, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.AspNetCore, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.AspNetCore.Grpc, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.AspNetCore.Grpc.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.AzureFunctions.Worker, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.AzureFunctions.Worker.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.Benchmarks, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.Google.Cloud.Functions, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.Google.Cloud.Functions.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.DiagnosticSource, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.DiagnosticSource.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.DiagnosticSource.IntegrationTests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.EntityFramework, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.EntityFramework.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.Extensions.Logging, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.Extensions.Logging.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.Extensions.Logging.EfCore.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.Log4Net, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.Log4Net.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.Maui, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.Maui.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.NLog, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.NLog.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.Profiling, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.Profiling.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.Serilog, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.Serilog.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.Testing, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] - -#if ANDROID - -// Don't let the Sentry Android SDK auto-init, as we do that manually in SentrySdk.Init -// See https://docs.sentry.io/platforms/android/configuration/manual-init/ -// This attribute automatically adds the metadata to the final AndroidManifest.xml -[assembly: MetaData("io.sentry.auto-init", Value = "false")] - -// Set the hybrid SDK name -[assembly: MetaData("io.sentry.sdk.name", Value = "sentry.java.android.dotnet")] - -#endif - - -// The targets for these platforms are not CLS Compliant -#if !__MOBILE__ -[assembly: CLSCompliant(true)] -#endif diff --git a/src/Sentry/Sentry.csproj b/src/Sentry/Sentry.csproj index f5187afe22..d12cde5b8c 100644 --- a/src/Sentry/Sentry.csproj +++ b/src/Sentry/Sentry.csproj @@ -3,6 +3,7 @@ Official SDK for Sentry - Open-source error tracking that helps developers monitor and fix crashes in real time. $(NoWarn);RS0017 + true @@ -50,7 +51,7 @@ https://github.com/SimonCropp/Polyfill --> - + - + diff --git a/test/Sentry.AspNet.Tests/Properties/AssemblyInfo.cs b/test/Sentry.AspNet.Tests/Properties/AssemblyInfo.cs deleted file mode 100644 index 35ec132bc3..0000000000 --- a/test/Sentry.AspNet.Tests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1 +0,0 @@ -[assembly: SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Test project")] diff --git a/test/Sentry.AspNetCore.Grpc.Tests/Properties/AssemblyInfo.cs b/test/Sentry.AspNetCore.Grpc.Tests/Properties/AssemblyInfo.cs deleted file mode 100644 index 35ec132bc3..0000000000 --- a/test/Sentry.AspNetCore.Grpc.Tests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1 +0,0 @@ -[assembly: SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Test project")] diff --git a/test/Sentry.AspNetCore.Tests/Properties/AssemblyInfo.cs b/test/Sentry.AspNetCore.Tests/Properties/AssemblyInfo.cs deleted file mode 100644 index 35ec132bc3..0000000000 --- a/test/Sentry.AspNetCore.Tests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1 +0,0 @@ -[assembly: SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Test project")] diff --git a/test/Sentry.DiagnosticSource.Tests/Properties/AssemblyInfo.cs b/test/Sentry.DiagnosticSource.Tests/Properties/AssemblyInfo.cs deleted file mode 100644 index 35ec132bc3..0000000000 --- a/test/Sentry.DiagnosticSource.Tests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1 +0,0 @@ -[assembly: SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Test project")] diff --git a/test/Sentry.EntityFramework.Tests/Properties/AssemblyInfo.cs b/test/Sentry.EntityFramework.Tests/Properties/AssemblyInfo.cs deleted file mode 100644 index 35ec132bc3..0000000000 --- a/test/Sentry.EntityFramework.Tests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1 +0,0 @@ -[assembly: SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Test project")] diff --git a/test/Sentry.Extensions.Logging.Tests/Properties/AssemblyInfo.cs b/test/Sentry.Extensions.Logging.Tests/Properties/AssemblyInfo.cs deleted file mode 100644 index 35ec132bc3..0000000000 --- a/test/Sentry.Extensions.Logging.Tests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1 +0,0 @@ -[assembly: SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Test project")] diff --git a/test/Sentry.Google.Cloud.Functions.Tests/Properties/AssemblyInfo.cs b/test/Sentry.Google.Cloud.Functions.Tests/Properties/AssemblyInfo.cs deleted file mode 100644 index 35ec132bc3..0000000000 --- a/test/Sentry.Google.Cloud.Functions.Tests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1 +0,0 @@ -[assembly: SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Test project")] diff --git a/test/Sentry.Log4Net.Tests/Properties/AssemblyInfo.cs b/test/Sentry.Log4Net.Tests/Properties/AssemblyInfo.cs deleted file mode 100644 index 35ec132bc3..0000000000 --- a/test/Sentry.Log4Net.Tests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1 +0,0 @@ -[assembly: SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Test project")] diff --git a/test/Sentry.NLog.Tests/Properties/AssemblyInfo.cs b/test/Sentry.NLog.Tests/Properties/AssemblyInfo.cs deleted file mode 100644 index 35ec132bc3..0000000000 --- a/test/Sentry.NLog.Tests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1 +0,0 @@ -[assembly: SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Test project")] diff --git a/test/Sentry.Profiling.Tests/Properties/AssemblyInfo.cs b/test/Sentry.Profiling.Tests/Properties/AssemblyInfo.cs deleted file mode 100644 index 35ec132bc3..0000000000 --- a/test/Sentry.Profiling.Tests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1 +0,0 @@ -[assembly: SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Test project")] diff --git a/test/Sentry.Serilog.Tests/Properties/AssemblyInfo.cs b/test/Sentry.Serilog.Tests/Properties/AssemblyInfo.cs deleted file mode 100644 index 35ec132bc3..0000000000 --- a/test/Sentry.Serilog.Tests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1 +0,0 @@ -[assembly: SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Test project")] diff --git a/test/Sentry.Testing/Properties/AssemblyInfo.cs b/test/Sentry.Testing/Properties/AssemblyInfo.cs deleted file mode 100644 index ee0dc5484b..0000000000 --- a/test/Sentry.Testing/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,7 +0,0 @@ -[assembly: InternalsVisibleTo("Sentry.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.AspNetCore.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.AzureFunctions.Worker.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.Google.Cloud.Functions.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.Maui.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("Sentry.Profiling.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] -[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] diff --git a/test/Sentry.Testing/Sentry.Testing.csproj b/test/Sentry.Testing/Sentry.Testing.csproj index ff91b863bf..7712a12e27 100644 --- a/test/Sentry.Testing/Sentry.Testing.csproj +++ b/test/Sentry.Testing/Sentry.Testing.csproj @@ -12,4 +12,13 @@ + + + + + + + + + diff --git a/test/Sentry.Tests/Properties/AssemblyInfo.cs b/test/Sentry.Tests/Properties/AssemblyInfo.cs deleted file mode 100644 index 35ec132bc3..0000000000 --- a/test/Sentry.Tests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1 +0,0 @@ -[assembly: SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Test project")] From ffd186ac7df5daa535b9943c727fd32df537b275 Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Sun, 21 May 2023 21:02:28 -0700 Subject: [PATCH 042/142] Remove ios simulator resources when building in Hot Restart mode (#2384) * Remove simulator resources when building for Hot Restart * Update CHANGELOG.md --- CHANGELOG.md | 2 ++ .../buildTransitive/Sentry.Bindings.Cocoa.targets | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f04dbc850e..dc41837091 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,8 @@ However, there is no change to the behavior or _typical_ usage of either of these properties. - CachedTransport gracefully handles malformed envelopes during processing ([#2371](https://github.com/getsentry/sentry-dotnet/pull/2371)) +- Remove extraneous iOS simulator resources when building MAUI apps using Visual Studio "Hot Restart" mode, to avoid hitting Windows max path ([#2384](https://github.com/getsentry/sentry-dotnet/pull/2384)) + ### Dependencies diff --git a/src/Sentry.Bindings.Cocoa/buildTransitive/Sentry.Bindings.Cocoa.targets b/src/Sentry.Bindings.Cocoa/buildTransitive/Sentry.Bindings.Cocoa.targets index d5b3800e92..4b74ad10fe 100644 --- a/src/Sentry.Bindings.Cocoa/buildTransitive/Sentry.Bindings.Cocoa.targets +++ b/src/Sentry.Bindings.Cocoa/buildTransitive/Sentry.Bindings.Cocoa.targets @@ -39,4 +39,14 @@ + + + + + + + From 666526b20a3a213d3011a449fd1ce9c92c112f84 Mon Sep 17 00:00:00 2001 From: getsentry-bot Date: Mon, 22 May 2023 08:33:04 +0000 Subject: [PATCH 043/142] release: 3.32.0 --- CHANGELOG.md | 2 +- Directory.Build.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc41837091..eed6a9b30a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## Unreleased +## 3.32.0 ### Features diff --git a/Directory.Build.props b/Directory.Build.props index 9d92e79e00..685db1d1c8 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,7 +1,7 @@ - 3.31.0 + 3.32.0 11 true $(MSBuildThisFileDirectory).assets\Sentry.snk From 2a8813ab6725caac4bc3a76f71af542ffce7f588 Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Mon, 22 May 2023 10:56:02 -0700 Subject: [PATCH 044/142] Rework handling of `AggregateException` to align with Sentry Exception Groups (#2287) --- CHANGELOG.md | 10 ++ .../Internal/Extensions/MiscExtensions.cs | 15 +++ src/Sentry/Internal/MainExceptionProcessor.cs | 104 +++++++++++++----- src/Sentry/Protocol/Mechanism.cs | 51 +++++++++ src/Sentry/SentryClient.cs | 14 ++- src/Sentry/SentryExceptionExtensions.cs | 37 ------- src/Sentry/SentryOptions.cs | 10 +- ...tegrationTests.Simple.Core3_1.verified.txt | 3 +- ...grationTests.Simple.DotNet6_0.verified.txt | 3 +- ...grationTests.Simple.DotNet7_0.verified.txt | 3 +- ...tegrationTests.Simple.Mono4_0.verified.txt | 3 +- ...ntegrationTests.Simple.Net4_8.verified.txt | 3 +- ...tegrationTests.Simple.Core3_1.verified.txt | 1 + ...grationTests.Simple.DotNet6_0.verified.txt | 1 + ...grationTests.Simple.DotNet7_0.verified.txt | 1 + ...tegrationTests.Simple.Mono4_0.verified.txt | 1 + ...ntegrationTests.Simple.Net4_8.verified.txt | 1 + .../ApiApprovalTests.Run.Core3_1.verified.txt | 6 +- ...piApprovalTests.Run.DotNet6_0.verified.txt | 6 +- ...piApprovalTests.Run.DotNet7_0.verified.txt | 6 +- .../ApiApprovalTests.Run.Net4_8.verified.txt | 6 +- ...sactionEndedAsCrashed.Core3_1.verified.txt | 3 +- ...ctionEndedAsCrashed.DotNet6_0.verified.txt | 3 +- ...ctionEndedAsCrashed.DotNet7_0.verified.txt | 3 +- ...sactionEndedAsCrashed.Mono4_0.verified.txt | 3 +- ...nsactionEndedAsCrashed.Net4_8.verified.txt | 3 +- .../Internals/AgggregateExceptionTests.cs | 52 +++++++++ ...eateSentryException_Aggregate.verified.txt | 26 ++++- ...entryException_Aggregate_Keep.verified.txt | 22 ---- .../Internals/MainExceptionProcessorTests.cs | 20 ---- .../MainExceptionProcessorTests.verify.cs | 13 --- .../Protocol/Exceptions/MechanismTests.cs | 10 +- ...UnobservedTaskExceptionIntegrationTests.cs | 31 +++++- 33 files changed, 323 insertions(+), 151 deletions(-) create mode 100644 test/Sentry.Tests/Internals/AgggregateExceptionTests.cs delete mode 100644 test/Sentry.Tests/Internals/MainExceptionProcessorTests.CreateSentryException_Aggregate_Keep.verified.txt diff --git a/CHANGELOG.md b/CHANGELOG.md index eed6a9b30a..5533c15f87 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## Unreleased + +### Features + +- .NET SDK changes for exception groups ([#2287](https://github.com/getsentry/sentry-dotnet/pull/2287)) + - This changes how `AggregateException` is handled. Instead of filtering them out client-side, the SDK marks them as an "exception group", + and adds includes data that represents the hierarchical structure of inner exceptions. Sentry now recognizes this server-side, + improving the accuracy of the issue detail page. + - Accordingly, the `KeepAggregateException` option is now obsolete and does nothing. Please remove any usages of `KeepAggregateException`. + ## 3.32.0 ### Features diff --git a/src/Sentry/Internal/Extensions/MiscExtensions.cs b/src/Sentry/Internal/Extensions/MiscExtensions.cs index 7180cda722..9fe0e44813 100644 --- a/src/Sentry/Internal/Extensions/MiscExtensions.cs +++ b/src/Sentry/Internal/Extensions/MiscExtensions.cs @@ -82,4 +82,19 @@ public static void Add( TKey key, TValue value) => collection.Add(new KeyValuePair(key, value)); + + internal static string GetRawMessage(this AggregateException exception) + { + var message = exception.Message; + if (exception.InnerException is { } inner) + { + var i = message.IndexOf($" ({inner.Message})", StringComparison.Ordinal); + if (i > 0) + { + return message[..i]; + } + } + + return message; + } } diff --git a/src/Sentry/Internal/MainExceptionProcessor.cs b/src/Sentry/Internal/MainExceptionProcessor.cs index 5cb9963a94..90ec5e649d 100644 --- a/src/Sentry/Internal/MainExceptionProcessor.cs +++ b/src/Sentry/Internal/MainExceptionProcessor.cs @@ -30,6 +30,68 @@ public void Process(Exception exception, SentryEvent sentryEvent) sentryEvent.SentryExceptions = sentryExceptions; } + // Sentry exceptions are sorted oldest to newest. + // See https://develop.sentry.dev/sdk/event-payloads/exception + internal IReadOnlyList CreateSentryExceptions(Exception exception) + { + var exceptions = WalkExceptions(exception).Reverse().ToList(); + + // In the case of only one exception, ExceptionId and ParentId are useless. + if (exceptions.Count == 1 && exceptions[0].Mechanism is { } mechanism) + { + mechanism.ExceptionId = null; + mechanism.ParentId = null; + if (mechanism.IsDefaultOrEmpty()) + { + // No need to convey an empty mechanism. + exceptions[0].Mechanism = null; + } + } + + return exceptions; + } + + private class Counter + { + private int _value; + + public int GetNextValue() => _value++; + } + + private IEnumerable WalkExceptions(Exception exception) => + WalkExceptions(exception, new Counter(), null, null); + + private IEnumerable WalkExceptions(Exception exception, Counter counter, int? parentId, string? source) + { + var ex = exception; + while (ex is not null) + { + var id = counter.GetNextValue(); + + yield return BuildSentryException(ex, id, parentId, source); + + if (ex is AggregateException aex) + { + for (var i = 0; i < aex.InnerExceptions.Count; i++) + { + ex = aex.InnerExceptions[i]; + source = $"{nameof(AggregateException.InnerExceptions)}[{i}]"; + var sentryExceptions = WalkExceptions(ex, counter, id, source); + foreach (var sentryException in sentryExceptions) + { + yield return sentryException; + } + } + + break; + } + + ex = ex.InnerException; + parentId = id; + source = nameof(AggregateException.InnerException); + } + } + private static void MoveExceptionDataToEvent(SentryEvent sentryEvent, IEnumerable sentryExceptions) { var keysToRemove = new List(); @@ -77,41 +139,17 @@ value is string stringValue && } } - internal List CreateSentryExceptions(Exception exception) - { - var exceptions = exception - .EnumerateChainedExceptions(_options) - .Select(BuildSentryException) - .ToList(); - - // If we've filtered out the aggregate exception, we'll need to copy over details from it. - if (exception is AggregateException && !_options.KeepAggregateException) - { - var original = BuildSentryException(exception); - - // Exceptions are sent from oldest to newest, so the details belong on the LAST exception. - var last = exceptions.Last(); - last.Mechanism = original.Mechanism; - - // In some cases the stack trace is already positioned on the inner exception. - // Only copy it over when it is missing. - last.Stacktrace ??= original.Stacktrace; - } - - return exceptions; - } - - private SentryException BuildSentryException(Exception exception) + private SentryException BuildSentryException(Exception exception, int id, int? parentId, string? source) { var sentryEx = new SentryException { Type = exception.GetType().FullName, Module = exception.GetType().Assembly.FullName, - Value = exception.Message, + Value = exception is AggregateException agg ? agg.GetRawMessage() : exception.Message, ThreadId = Environment.CurrentManagedThreadId }; - var mechanism = GetMechanism(exception); + var mechanism = GetMechanism(exception, id, parentId, source); if (!mechanism.IsDefaultOrEmpty()) { sentryEx.Mechanism = mechanism; @@ -121,7 +159,7 @@ private SentryException BuildSentryException(Exception exception) return sentryEx; } - private static Mechanism GetMechanism(Exception exception) + private static Mechanism GetMechanism(Exception exception, int id, int? parentId, string? source) { var mechanism = new Mechanism(); @@ -167,6 +205,16 @@ private static Mechanism GetMechanism(Exception exception) mechanism.Data[key] = exception.Data[key]!; } + mechanism.ExceptionId = id; + mechanism.ParentId = parentId; + mechanism.Source = source; + mechanism.IsExceptionGroup = exception is AggregateException; + + if (source != null) + { + mechanism.Type = "chained"; + } + return mechanism; } } diff --git a/src/Sentry/Protocol/Mechanism.cs b/src/Sentry/Protocol/Mechanism.cs index 81a63de190..6eba1b608e 100644 --- a/src/Sentry/Protocol/Mechanism.cs +++ b/src/Sentry/Protocol/Mechanism.cs @@ -56,6 +56,16 @@ public string Type /// public string? Description { get; set; } + /// + /// An optional value to explain the source of the exception. + /// + /// + /// For chained exceptions, this should be the property name where the exception was retrieved from its parent + /// exception. In .NET, either "" or "InnerExceptions[i]" + /// (where i is replaced with the numeric index within ). + /// + public string? Source { get; set; } + /// /// Optional fully qualified URL to an online help resource, possible interpolated with error parameters. /// @@ -71,6 +81,31 @@ public string Type /// public bool Synthetic { get; set; } + /// + /// Whether the exception represents an exception group. + /// In .NET, an . + /// + public bool IsExceptionGroup { get; set; } + + /// + /// A numeric identifier assigned to the exception by the SDK. + /// + /// + /// The SDK should assign a different ID to each exception in an event, starting with the root exception as 0, + /// and incrementing thereafter. This ID can be used with to reconstruct the logical + /// structure of an exception group. When null, Sentry will assume that all exceptions in an event are + /// in a single chain. + /// + public int? ExceptionId { get; set; } + + /// + /// The parent exception's identifier, or null for the root exception. + /// + /// + /// This ID can be used with to reconstruct the logical structure of an exception group. + /// + public int? ParentId { get; set; } + /// /// Optional information from the operating system or runtime on the exception mechanism. /// @@ -95,9 +130,13 @@ public void WriteTo(Utf8JsonWriter writer, IDiagnosticLogger? logger) writer.WriteString("type", Type); writer.WriteStringIfNotWhiteSpace("description", Description); + writer.WriteStringIfNotWhiteSpace("source", Source); writer.WriteStringIfNotWhiteSpace("help_link", HelpLink); writer.WriteBooleanIfNotNull("handled", Handled); writer.WriteBooleanIfTrue("synthetic", Synthetic); + writer.WriteBooleanIfTrue("is_exception_group", IsExceptionGroup); + writer.WriteNumberIfNotNull("exception_id", ExceptionId); + writer.WriteNumberIfNotNull("parent_id", ParentId); writer.WriteDictionaryIfNotEmpty("data", InternalData!, logger); writer.WriteDictionaryIfNotEmpty("meta", InternalMeta!, logger); @@ -111,9 +150,13 @@ public static Mechanism FromJson(JsonElement json) { var type = json.GetPropertyOrNull("type")?.GetString(); var description = json.GetPropertyOrNull("description")?.GetString(); + var source = json.GetPropertyOrNull("source")?.GetString(); var helpLink = json.GetPropertyOrNull("help_link")?.GetString(); var handled = json.GetPropertyOrNull("handled")?.GetBoolean(); var synthetic = json.GetPropertyOrNull("synthetic")?.GetBoolean() ?? false; + var isExceptionGroup = json.GetPropertyOrNull("is_exception_group")?.GetBoolean() ?? false; + var exceptionId = json.GetPropertyOrNull("exception_id")?.GetInt32(); + var parentId = json.GetPropertyOrNull("parent_id")?.GetInt32(); var data = json.GetPropertyOrNull("data")?.GetDictionaryOrNull(); var meta = json.GetPropertyOrNull("meta")?.GetDictionaryOrNull(); @@ -121,9 +164,13 @@ public static Mechanism FromJson(JsonElement json) { Type = type, Description = description, + Source = source, HelpLink = helpLink, Handled = handled, Synthetic = synthetic, + IsExceptionGroup = isExceptionGroup, + ExceptionId = exceptionId, + ParentId = parentId, InternalData = data?.WhereNotNullValue().ToDictionary(), InternalMeta = meta?.WhereNotNullValue().ToDictionary() }; @@ -132,9 +179,13 @@ public static Mechanism FromJson(JsonElement json) internal bool IsDefaultOrEmpty() => Handled is null && Synthetic == false && + IsExceptionGroup == false && + ExceptionId is null && + ParentId is null && Type == DefaultType && string.IsNullOrWhiteSpace(Description) && string.IsNullOrWhiteSpace(HelpLink) && + string.IsNullOrWhiteSpace(Source) && !(InternalData?.Count > 0) && !(InternalMeta?.Count > 0); } diff --git a/src/Sentry/SentryClient.cs b/src/Sentry/SentryClient.cs index 25f0648b34..f48dac09f5 100644 --- a/src/Sentry/SentryClient.cs +++ b/src/Sentry/SentryClient.cs @@ -306,13 +306,15 @@ private SentryId DoSendEvent(SentryEvent @event, Hint? hint, Scope? scope) return new[] { exception }; } - if (exception is AggregateException aggregate && - aggregate.InnerExceptions.All(e => ApplyExceptionFilters(e) != null)) + if (exception is AggregateException aggregate) { - // All inner exceptions of the aggregate matched a filter, so the event should be filtered. - // Note that _options.KeepAggregateException is not relevant here. Even if we want to keep aggregate - // exceptions, we would still never send one if all of its children are supposed to be filtered. - return aggregate.InnerExceptions; + // Flatten the tree of aggregates such that all the inner exceptions are non-aggregates. + var innerExceptions = aggregate.Flatten().InnerExceptions; + if (innerExceptions.All(e => ApplyExceptionFilters(e) != null)) + { + // All inner exceptions matched a filter, so the event should be filtered. + return innerExceptions; + } } // The event should not be filtered. diff --git a/src/Sentry/SentryExceptionExtensions.cs b/src/Sentry/SentryExceptionExtensions.cs index 80c7917790..4ecac3239b 100644 --- a/src/Sentry/SentryExceptionExtensions.cs +++ b/src/Sentry/SentryExceptionExtensions.cs @@ -1,4 +1,3 @@ -using Sentry; using Sentry.Internal; using Sentry.Protocol; @@ -56,40 +55,4 @@ public static void SetSentryMechanism(this Exception ex, string type, string? de ex.Data[Mechanism.HandledKey] = handled; } } - - /// - /// Recursively enumerates all and - /// Not for public use. - /// - [EditorBrowsable(EditorBrowsableState.Never)] - public static IEnumerable EnumerateChainedExceptions(this Exception exception, SentryOptions options) - { - if (exception is AggregateException aggregateException) - { - foreach (var inner in EnumerateInner(options, aggregateException)) - { - yield return inner; - } - - if (!options.KeepAggregateException) - { - yield break; - } - } - else if (exception.InnerException != null) - { - foreach (var inner in exception.InnerException.EnumerateChainedExceptions(options)) - { - yield return inner; - } - } - - yield return exception; - } - - private static IEnumerable EnumerateInner(SentryOptions options, AggregateException aggregateException) - { - return aggregateException.InnerExceptions - .SelectMany(exception => exception.EnumerateChainedExceptions(options)); - } } diff --git a/src/Sentry/SentryOptions.cs b/src/Sentry/SentryOptions.cs index 559e51a8d5..ead29854a9 100644 --- a/src/Sentry/SentryOptions.cs +++ b/src/Sentry/SentryOptions.cs @@ -900,10 +900,14 @@ public StackTraceMode StackTraceMode public Func? CrashedLastRun { get; set; } /// - /// Keep in sentry logging. - /// The default behaviour is to only log and not include the root . - /// Set KeepAggregateException to true to include the root . + /// This property is no longer used. It will be removed in a future version. /// + /// + /// All exceptions are now sent to Sentry, including s. + /// The issue grouping rules in Sentry have been updated to accomodate "exception groups", + /// such as in .NET. + /// + [Obsolete("This property is no longer used. It will be removed in a future version.")] public bool KeepAggregateException { get; set; } /// diff --git a/test/Sentry.NLog.Tests/IntegrationTests.Simple.Core3_1.verified.txt b/test/Sentry.NLog.Tests/IntegrationTests.Simple.Core3_1.verified.txt index 3a0da44bc2..8a61fb4a81 100644 --- a/test/Sentry.NLog.Tests/IntegrationTests.Simple.Core3_1.verified.txt +++ b/test/Sentry.NLog.Tests/IntegrationTests.Simple.Core3_1.verified.txt @@ -53,7 +53,8 @@ Mechanism: { Type: generic, Handled: true, - Synthetic: false + Synthetic: false, + IsExceptionGroup: false } } ], diff --git a/test/Sentry.NLog.Tests/IntegrationTests.Simple.DotNet6_0.verified.txt b/test/Sentry.NLog.Tests/IntegrationTests.Simple.DotNet6_0.verified.txt index 3a0da44bc2..8a61fb4a81 100644 --- a/test/Sentry.NLog.Tests/IntegrationTests.Simple.DotNet6_0.verified.txt +++ b/test/Sentry.NLog.Tests/IntegrationTests.Simple.DotNet6_0.verified.txt @@ -53,7 +53,8 @@ Mechanism: { Type: generic, Handled: true, - Synthetic: false + Synthetic: false, + IsExceptionGroup: false } } ], diff --git a/test/Sentry.NLog.Tests/IntegrationTests.Simple.DotNet7_0.verified.txt b/test/Sentry.NLog.Tests/IntegrationTests.Simple.DotNet7_0.verified.txt index 3a0da44bc2..8a61fb4a81 100644 --- a/test/Sentry.NLog.Tests/IntegrationTests.Simple.DotNet7_0.verified.txt +++ b/test/Sentry.NLog.Tests/IntegrationTests.Simple.DotNet7_0.verified.txt @@ -53,7 +53,8 @@ Mechanism: { Type: generic, Handled: true, - Synthetic: false + Synthetic: false, + IsExceptionGroup: false } } ], 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 2401051364..ef9f70ec01 100644 --- a/test/Sentry.NLog.Tests/IntegrationTests.Simple.Mono4_0.verified.txt +++ b/test/Sentry.NLog.Tests/IntegrationTests.Simple.Mono4_0.verified.txt @@ -53,7 +53,8 @@ Mechanism: { Type: generic, Handled: true, - Synthetic: false + Synthetic: false, + IsExceptionGroup: false } } ], 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 3a0da44bc2..8a61fb4a81 100644 --- a/test/Sentry.NLog.Tests/IntegrationTests.Simple.Net4_8.verified.txt +++ b/test/Sentry.NLog.Tests/IntegrationTests.Simple.Net4_8.verified.txt @@ -53,7 +53,8 @@ Mechanism: { Type: generic, Handled: true, - Synthetic: false + Synthetic: false, + IsExceptionGroup: false } } ], diff --git a/test/Sentry.Serilog.Tests/IntegrationTests.Simple.Core3_1.verified.txt b/test/Sentry.Serilog.Tests/IntegrationTests.Simple.Core3_1.verified.txt index c31e0aa524..ebf1fb5c3e 100644 --- a/test/Sentry.Serilog.Tests/IntegrationTests.Simple.Core3_1.verified.txt +++ b/test/Sentry.Serilog.Tests/IntegrationTests.Simple.Core3_1.verified.txt @@ -184,6 +184,7 @@ Type: generic, Handled: true, Synthetic: false, + IsExceptionGroup: false, Data: { details: Do work always throws. } diff --git a/test/Sentry.Serilog.Tests/IntegrationTests.Simple.DotNet6_0.verified.txt b/test/Sentry.Serilog.Tests/IntegrationTests.Simple.DotNet6_0.verified.txt index c31e0aa524..ebf1fb5c3e 100644 --- a/test/Sentry.Serilog.Tests/IntegrationTests.Simple.DotNet6_0.verified.txt +++ b/test/Sentry.Serilog.Tests/IntegrationTests.Simple.DotNet6_0.verified.txt @@ -184,6 +184,7 @@ Type: generic, Handled: true, Synthetic: false, + IsExceptionGroup: false, Data: { details: Do work always throws. } diff --git a/test/Sentry.Serilog.Tests/IntegrationTests.Simple.DotNet7_0.verified.txt b/test/Sentry.Serilog.Tests/IntegrationTests.Simple.DotNet7_0.verified.txt index c31e0aa524..ebf1fb5c3e 100644 --- a/test/Sentry.Serilog.Tests/IntegrationTests.Simple.DotNet7_0.verified.txt +++ b/test/Sentry.Serilog.Tests/IntegrationTests.Simple.DotNet7_0.verified.txt @@ -184,6 +184,7 @@ Type: generic, Handled: true, Synthetic: false, + IsExceptionGroup: false, Data: { details: Do work always throws. } diff --git a/test/Sentry.Serilog.Tests/IntegrationTests.Simple.Mono4_0.verified.txt b/test/Sentry.Serilog.Tests/IntegrationTests.Simple.Mono4_0.verified.txt index b1b2b4f8ea..92a4deb728 100644 --- a/test/Sentry.Serilog.Tests/IntegrationTests.Simple.Mono4_0.verified.txt +++ b/test/Sentry.Serilog.Tests/IntegrationTests.Simple.Mono4_0.verified.txt @@ -184,6 +184,7 @@ Type: generic, Handled: true, Synthetic: false, + IsExceptionGroup: false, Data: { details: Do work always throws. } diff --git a/test/Sentry.Serilog.Tests/IntegrationTests.Simple.Net4_8.verified.txt b/test/Sentry.Serilog.Tests/IntegrationTests.Simple.Net4_8.verified.txt index c31e0aa524..ebf1fb5c3e 100644 --- a/test/Sentry.Serilog.Tests/IntegrationTests.Simple.Net4_8.verified.txt +++ b/test/Sentry.Serilog.Tests/IntegrationTests.Simple.Net4_8.verified.txt @@ -184,6 +184,7 @@ Type: generic, Handled: true, Synthetic: false, + IsExceptionGroup: false, Data: { details: Do work always throws. } diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.Core3_1.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.Core3_1.verified.txt index 22230b0b0d..49f2c268f9 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.Core3_1.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.Core3_1.verified.txt @@ -619,6 +619,7 @@ namespace Sentry public bool IsEnvironmentUser { get; set; } public bool IsGlobalModeEnabled { get; set; } public bool JsonPreserveReferences { get; set; } + [System.Obsolete("This property is no longer used. It will be removed in a future version.")] public bool KeepAggregateException { get; set; } public long MaxAttachmentSize { get; set; } public int MaxBreadcrumbs { get; set; } @@ -1571,9 +1572,13 @@ namespace Sentry.Protocol 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) { } @@ -1703,6 +1708,5 @@ 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 System.Collections.Generic.IEnumerable EnumerateChainedExceptions(this System.Exception exception, Sentry.SentryOptions options) { } 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.DotNet6_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt index 41046b99a2..953422b32d 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt @@ -620,6 +620,7 @@ namespace Sentry public bool IsEnvironmentUser { get; set; } public bool IsGlobalModeEnabled { get; set; } public bool JsonPreserveReferences { get; set; } + [System.Obsolete("This property is no longer used. It will be removed in a future version.")] public bool KeepAggregateException { get; set; } public long MaxAttachmentSize { get; set; } public int MaxBreadcrumbs { get; set; } @@ -1572,9 +1573,13 @@ namespace Sentry.Protocol 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) { } @@ -1704,6 +1709,5 @@ 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 System.Collections.Generic.IEnumerable EnumerateChainedExceptions(this System.Exception exception, Sentry.SentryOptions options) { } 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 index 41046b99a2..953422b32d 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt @@ -620,6 +620,7 @@ namespace Sentry public bool IsEnvironmentUser { get; set; } public bool IsGlobalModeEnabled { get; set; } public bool JsonPreserveReferences { get; set; } + [System.Obsolete("This property is no longer used. It will be removed in a future version.")] public bool KeepAggregateException { get; set; } public long MaxAttachmentSize { get; set; } public int MaxBreadcrumbs { get; set; } @@ -1572,9 +1573,13 @@ namespace Sentry.Protocol 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) { } @@ -1704,6 +1709,5 @@ 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 System.Collections.Generic.IEnumerable EnumerateChainedExceptions(this System.Exception exception, Sentry.SentryOptions options) { } 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.Net4_8.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt index afd1f161ad..50f87e8406 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt @@ -618,6 +618,7 @@ namespace Sentry public bool IsEnvironmentUser { get; set; } public bool IsGlobalModeEnabled { get; set; } public bool JsonPreserveReferences { get; set; } + [System.Obsolete("This property is no longer used. It will be removed in a future version.")] public bool KeepAggregateException { get; set; } public long MaxAttachmentSize { get; set; } public int MaxBreadcrumbs { get; set; } @@ -1571,9 +1572,13 @@ namespace Sentry.Protocol 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) { } @@ -1703,6 +1708,5 @@ 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 System.Collections.Generic.IEnumerable EnumerateChainedExceptions(this System.Exception exception, Sentry.SentryOptions options) { } 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.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.Core3_1.verified.txt b/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.Core3_1.verified.txt index 4eeeeb9e1c..c572a61273 100644 --- a/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.Core3_1.verified.txt +++ b/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.Core3_1.verified.txt @@ -49,7 +49,8 @@ Mechanism: { Type: generic, Handled: false, - Synthetic: false + Synthetic: false, + IsExceptionGroup: false } } ], 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 index ae69b386e8..50892ef63a 100644 --- a/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.DotNet6_0.verified.txt +++ b/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.DotNet6_0.verified.txt @@ -49,7 +49,8 @@ Mechanism: { Type: generic, Handled: false, - Synthetic: false + Synthetic: false, + IsExceptionGroup: false } } ], 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 index 2824ce63df..09055037ef 100644 --- a/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.DotNet7_0.verified.txt +++ b/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.DotNet7_0.verified.txt @@ -49,7 +49,8 @@ Mechanism: { Type: generic, Handled: false, - Synthetic: false + Synthetic: false, + IsExceptionGroup: false } } ], diff --git a/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.Mono4_0.verified.txt b/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.Mono4_0.verified.txt index 80f9e24e92..f879896db4 100644 --- a/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.Mono4_0.verified.txt +++ b/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.Mono4_0.verified.txt @@ -49,7 +49,8 @@ Mechanism: { Type: generic, Handled: false, - Synthetic: false + Synthetic: false, + IsExceptionGroup: false } } ], 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 a1379db4ea..2b2d328805 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 @@ -49,7 +49,8 @@ Mechanism: { Type: generic, Handled: false, - Synthetic: false + Synthetic: false, + IsExceptionGroup: false } } ], diff --git a/test/Sentry.Tests/Internals/AgggregateExceptionTests.cs b/test/Sentry.Tests/Internals/AgggregateExceptionTests.cs new file mode 100644 index 0000000000..49f9468d9f --- /dev/null +++ b/test/Sentry.Tests/Internals/AgggregateExceptionTests.cs @@ -0,0 +1,52 @@ +using Sentry.PlatformAbstractions; + +namespace Sentry.Tests.Internals; + +public class AgggregateExceptionTests +{ + private static readonly string DefaultAggregateExceptionMessage = new AggregateException().Message; + + [Fact] + public void AggregateException_GetRawMessage_Empty() + { + var exception = new AggregateException(); + + var rawMessage = exception.GetRawMessage(); + + Assert.Equal(DefaultAggregateExceptionMessage, rawMessage); + } + + [Fact] + public void AggregateException_GetRawMessage_WithInnerExceptions() + { + var exception = GetTestAggregateException(); + + var rawMessage = exception.GetRawMessage(); + + Assert.Equal(DefaultAggregateExceptionMessage, rawMessage); + } + + [SkippableFact] + public void AggregateException_GetRawMessage_DiffersFromMessage() + { + // Sanity check: The message should be different than the raw message, except on full .NET Framework. + // .NET, .NET Core, and Mono all override the Message property to append messages from the inner exceptions. + // .NET Framework does not. + + Skip.If(RuntimeInfo.GetRuntime().IsNetFx()); + + var exception = GetTestAggregateException(); + + var rawMessage = exception.GetRawMessage(); + + Assert.NotEqual(exception.Message, rawMessage); + } + + private static AggregateException GetTestAggregateException() => + Assert.Throws(() => + { + var t1 = Task.Run(() => throw new Exception("Test 1")); + var t2 = Task.Run(() => throw new Exception("Test 2")); + Task.WaitAll(t1, t2); + }); +} diff --git a/test/Sentry.Tests/Internals/MainExceptionProcessorTests.CreateSentryException_Aggregate.verified.txt b/test/Sentry.Tests/Internals/MainExceptionProcessorTests.CreateSentryException_Aggregate.verified.txt index 10e61995c3..d48b44f301 100644 --- a/test/Sentry.Tests/Internals/MainExceptionProcessorTests.CreateSentryException_Aggregate.verified.txt +++ b/test/Sentry.Tests/Internals/MainExceptionProcessorTests.CreateSentryException_Aggregate.verified.txt @@ -1,15 +1,37 @@ [ { Type: System.Exception, - Value: Inner message1 + Value: Inner message2, + Mechanism: { + Type: chained, + Source: InnerExceptions[1], + Synthetic: false, + IsExceptionGroup: false, + ExceptionId: 2, + ParentId: 0 + } }, { Type: System.Exception, - Value: Inner message2, + Value: Inner message1, + Mechanism: { + Type: chained, + Source: InnerExceptions[0], + Synthetic: false, + IsExceptionGroup: false, + ExceptionId: 1, + ParentId: 0 + } + }, + { + Type: System.AggregateException, + Value: One or more errors occurred., Mechanism: { Type: AppDomain.UnhandledException, Handled: false, Synthetic: false, + IsExceptionGroup: true, + ExceptionId: 0, Data: { foo: bar } diff --git a/test/Sentry.Tests/Internals/MainExceptionProcessorTests.CreateSentryException_Aggregate_Keep.verified.txt b/test/Sentry.Tests/Internals/MainExceptionProcessorTests.CreateSentryException_Aggregate_Keep.verified.txt deleted file mode 100644 index 1ec6b9a0d6..0000000000 --- a/test/Sentry.Tests/Internals/MainExceptionProcessorTests.CreateSentryException_Aggregate_Keep.verified.txt +++ /dev/null @@ -1,22 +0,0 @@ -[ - { - Type: System.Exception, - Value: Inner message1 - }, - { - Type: System.Exception, - Value: Inner message2 - }, - { - Type: System.AggregateException, - Value: , - Mechanism: { - Type: AppDomain.UnhandledException, - Handled: false, - Synthetic: false, - Data: { - foo: bar - } - } - } -] \ No newline at end of file diff --git a/test/Sentry.Tests/Internals/MainExceptionProcessorTests.cs b/test/Sentry.Tests/Internals/MainExceptionProcessorTests.cs index bf1c090dc2..ccb2b02a8a 100644 --- a/test/Sentry.Tests/Internals/MainExceptionProcessorTests.cs +++ b/test/Sentry.Tests/Internals/MainExceptionProcessorTests.cs @@ -118,26 +118,6 @@ public void Process_AggregateException() var last = evt.SentryExceptions!.Last(); Assert.NotNull(last.Stacktrace); - - var mechanism = last.Mechanism; - Assert.NotNull(mechanism); - Assert.False(mechanism.Handled); - Assert.NotNull(mechanism.Type); - Assert.NotEmpty(mechanism.Data); - } - - [Fact] - public void Process_AggregateException_Keep() - { - _fixture.SentryOptions.KeepAggregateException = true; - _fixture.SentryStackTraceFactory = _fixture.SentryOptions.SentryStackTraceFactory; - var sut = _fixture.GetSut(); - var evt = new SentryEvent(); - sut.Process(BuildAggregateException(), evt); - - var last = evt.SentryExceptions!.Last(); - Assert.NotNull(last.Stacktrace); - var mechanism = last.Mechanism; Assert.NotNull(mechanism); Assert.False(mechanism.Handled); diff --git a/test/Sentry.Tests/Internals/MainExceptionProcessorTests.verify.cs b/test/Sentry.Tests/Internals/MainExceptionProcessorTests.verify.cs index 89158ecae5..68305ebb73 100644 --- a/test/Sentry.Tests/Internals/MainExceptionProcessorTests.verify.cs +++ b/test/Sentry.Tests/Internals/MainExceptionProcessorTests.verify.cs @@ -13,17 +13,4 @@ public Task CreateSentryException_Aggregate() return Verify(sentryException); } - - [Fact] - public Task CreateSentryException_Aggregate_Keep() - { - _fixture.SentryOptions.KeepAggregateException = true; - var sut = _fixture.GetSut(); - var aggregateException = BuildAggregateException(); - - var sentryException = sut.CreateSentryExceptions(aggregateException); - - return Verify(sentryException) - .ScrubLines(x => x.Contains("One or more errors occurred")); - } } diff --git a/test/Sentry.Tests/Protocol/Exceptions/MechanismTests.cs b/test/Sentry.Tests/Protocol/Exceptions/MechanismTests.cs index 641b37a20b..e5ae10a885 100644 --- a/test/Sentry.Tests/Protocol/Exceptions/MechanismTests.cs +++ b/test/Sentry.Tests/Protocol/Exceptions/MechanismTests.cs @@ -16,9 +16,13 @@ public void SerializeObject_AllPropertiesSetToNonDefault_SerializesValidObject() { Type = "mechanism type", Description = "mechanism description", + Source = "exception source", Handled = true, + HelpLink = "https://helplink", Synthetic = true, - HelpLink = "https://helplink" + IsExceptionGroup = true, + ExceptionId = 123, + ParentId = 456 }; sut.Data.Add("data-key", "data-value"); @@ -30,9 +34,13 @@ public void SerializeObject_AllPropertiesSetToNonDefault_SerializesValidObject() { "type": "mechanism type", "description": "mechanism description", + "source": "exception source", "help_link": "https://helplink", "handled": true, "synthetic": true, + "is_exception_group": true, + "exception_id": 123, + "parent_id": 456, "data": { "data-key": "data-value" }, diff --git a/test/Sentry.Tests/UnobservedTaskExceptionIntegrationTests.cs b/test/Sentry.Tests/UnobservedTaskExceptionIntegrationTests.cs index 9a09d6689f..58b803bdff 100644 --- a/test/Sentry.Tests/UnobservedTaskExceptionIntegrationTests.cs +++ b/test/Sentry.Tests/UnobservedTaskExceptionIntegrationTests.cs @@ -79,11 +79,32 @@ public void Handle_UnobservedTaskException_CaptureEvent() processor.Process(capturedException, capturedEvent); } - // We should have a stack trace and mechanism on the final reported exception - var reportedException = capturedEvent.SentryExceptions?.LastOrDefault(); - Assert.NotNull(reportedException); - Assert.NotNull(reportedException.Stacktrace); - Assert.NotNull(reportedException.Mechanism); + // There should be two reported exceptions + var exceptions = capturedEvent.SentryExceptions?.ToList(); + Assert.NotNull(exceptions); + Assert.Equal(2, exceptions.Count); + + // The first should be the actual exception that was unobserved. + var actualException = exceptions[0]; + Assert.NotNull(actualException.Stacktrace); + Assert.NotNull(actualException.Mechanism); + Assert.Equal("chained", actualException.Mechanism.Type); + Assert.Equal("InnerExceptions[0]", actualException.Mechanism.Source); + Assert.Equal(1, actualException.Mechanism.ExceptionId); + Assert.Equal(0, actualException.Mechanism.ParentId); + Assert.False(actualException.Mechanism.IsExceptionGroup); + Assert.False(actualException.Mechanism.Synthetic); + + // The last should be the aggregate exception that raised the UnobservedTaskException event. + var aggregateException = exceptions[1]; + Assert.Null(aggregateException.Stacktrace); + Assert.NotNull(aggregateException.Mechanism); + Assert.Equal("UnobservedTaskException", aggregateException.Mechanism.Type); + Assert.Null(aggregateException.Mechanism.Source); + Assert.Equal(0, aggregateException.Mechanism.ExceptionId); + Assert.Null(aggregateException.Mechanism.ParentId); + Assert.True(aggregateException.Mechanism.IsExceptionGroup); + Assert.False(aggregateException.Mechanism.Synthetic); } #endif From 5eb89f4e777b09260c9c7a58032265893f56d3f5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 22 May 2023 11:09:14 -0700 Subject: [PATCH 045/142] chore: update scripts/update-cli.ps1 to 2.18.1 (#2386) 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 5533c15f87..1f290e0fc5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,12 @@ improving the accuracy of the issue detail page. - Accordingly, the `KeepAggregateException` option is now obsolete and does nothing. Please remove any usages of `KeepAggregateException`. +### Dependencies + +- Bump CLI from v2.18.0 to v2.18.1 ([#2386](https://github.com/getsentry/sentry-dotnet/pull/2386)) + - [changelog](https://github.com/getsentry/sentry-cli/blob/master/CHANGELOG.md#2181) + - [diff](https://github.com/getsentry/sentry-cli/compare/2.18.0...2.18.1) + ## 3.32.0 ### Features diff --git a/Directory.Build.props b/Directory.Build.props index 685db1d1c8..79b6838811 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -100,7 +100,7 @@ - 2.18.0 + 2.18.1 $(MSBuildThisFileDirectory)tools\sentry-cli\$(SentryCLIVersion)\ diff --git a/src/Sentry/Sentry.csproj b/src/Sentry/Sentry.csproj index d12cde5b8c..d36cee6003 100644 --- a/src/Sentry/Sentry.csproj +++ b/src/Sentry/Sentry.csproj @@ -95,25 +95,25 @@ + Include="sentry-cli-Darwin-arm64" FileHash="ea7d526282fabbaed267ac7504bfb227a67c372bc9c5ada9434489d659a09384" /> + Include="sentry-cli-Darwin-x86_64" FileHash="b3007f699e5c75e4a03706914ebeae9506de0dcafa4b136eacde28b8906db317" /> + Include="sentry-cli-Linux-aarch64" FileHash="1e85b33e06706274cba807627cf7d57960e13d90aa9a8a529c241665591f6a00" /> + Include="sentry-cli-Linux-i686" FileHash="152547d8e93d1fd3c06f03d640e056261f908195014ccc9cb5592d3911342260" /> + Include="sentry-cli-Linux-x86_64" FileHash="551d26ad4067b82e7a63139c4898f4d5294112746fcd7f0c7e61d08dff37ea33" /> + Include="sentry-cli-Windows-i686.exe" FileHash="5083108cdfe437b5d33d7a07214c09b6bce2fc802c800e86519b711de22d6400" /> + Include="sentry-cli-Windows-x86_64.exe" FileHash="ecac161350b928081ec748a831bd687a60d42fcbbed059a6c96927084504d225" /> From 9e7f961cfa1b32b8e1e8335a7b432bcfde648ab1 Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Mon, 22 May 2023 12:08:27 -0700 Subject: [PATCH 046/142] Update README.md (#2387) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index b0f82cd0dc..0a6f6fe108 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ Sentry SDK for .NET | **Sentry.AspNet** | [![Downloads](https://img.shields.io/nuget/dt/Sentry.AspNet.svg)](https://www.nuget.org/packages/Sentry.AspNet) | [![NuGet](https://img.shields.io/nuget/v/Sentry.AspNet.svg)](https://www.nuget.org/packages/Sentry.AspNet) | [![NuGet](https://img.shields.io/nuget/vpre/Sentry.AspNet.svg)](https://www.nuget.org/packages/Sentry.AspNet) | [![Documentation](https://img.shields.io/badge/documentation-sentry.io-green.svg)](https://docs.sentry.io/platforms/dotnet/guides/aspnet) | | **Sentry.AspNetCore** | [![Downloads](https://img.shields.io/nuget/dt/Sentry.AspNetCore.svg)](https://www.nuget.org/packages/Sentry.AspNetCore) | [![NuGet](https://img.shields.io/nuget/v/Sentry.AspNetCore.svg)](https://www.nuget.org/packages/Sentry.AspNetCore) | [![NuGet](https://img.shields.io/nuget/vpre/Sentry.AspNetCore.svg)](https://www.nuget.org/packages/Sentry.AspNetCore) | [![Documentation](https://img.shields.io/badge/documentation-sentry.io-green.svg)](https://docs.sentry.io/platforms/dotnet/guides/aspnetcore/) | | **Sentry.AspNetCore.Grpc** | [![Downloads](https://img.shields.io/nuget/dt/Sentry.AspNetCore.Grpc.svg)](https://www.nuget.org/packages/Sentry.AspNetCore.Grpc) | [![NuGet](https://img.shields.io/nuget/v/Sentry.AspNetCore.Grpc.svg)](https://www.nuget.org/packages/Sentry.AspNetCore.Grpc) | [![NuGet](https://img.shields.io/nuget/vpre/Sentry.AspNetCore.Grpc.svg)](https://www.nuget.org/packages/Sentry.AspNetCore.Grpc) | [![Documentation](https://img.shields.io/badge/documentation-sentry.io-green.svg)](https://docs.sentry.io/platforms/dotnet/guides/aspnetcore/) | +| **Sentry.AzureFunctions.Worker** | [![Downloads](https://img.shields.io/nuget/dt/Sentry.AzureFunctions.Worker.svg)](https://www.nuget.org/packages/Sentry.AzureFunctions.Worker.Grpc) | [![NuGet](https://img.shields.io/nuget/v/Sentry.AzureFunctions.Worker.svg)](https://www.nuget.org/packages/Sentry.AzureFunctions.Worker) | [![NuGet](https://img.shields.io/nuget/vpre/Sentry.AzureFunctions.Worker.svg)](https://www.nuget.org/packages/Sentry.AzureFunctions.Worker) | Documentation TBD | | **Sentry.DiagnosticSource** | [![Downloads](https://img.shields.io/nuget/dt/Sentry.DiagnosticSource.svg)](https://www.nuget.org/packages/Sentry.DiagnosticSource) | [![NuGet](https://img.shields.io/nuget/v/Sentry.DiagnosticSource.svg)](https://www.nuget.org/packages/Sentry.DiagnosticSource) | [![NuGet](https://img.shields.io/nuget/vpre/Sentry.DiagnosticSource.svg)](https://www.nuget.org/packages/Sentry.DiagnosticSource) | [![Documentation](https://img.shields.io/badge/documentation-sentry.io-green.svg)](https://docs.sentry.io/platforms/dotnet/performance/instrumentation/automatic-instrumentation/#diagnosticsource-integration) | | **Sentry.EntityFramework** | [![Downloads](https://img.shields.io/nuget/dt/Sentry.EntityFramework.svg)](https://www.nuget.org/packages/Sentry.EntityFramework) | [![NuGet](https://img.shields.io/nuget/v/Sentry.EntityFramework.svg)](https://www.nuget.org/packages/Sentry.EntityFramework) | [![NuGet](https://img.shields.io/nuget/vpre/Sentry.EntityFramework.svg)](https://www.nuget.org/packages/Sentry.EntityFramework) | [![Documentation](https://img.shields.io/badge/documentation-sentry.io-green.svg)](https://docs.sentry.io/platforms/dotnet/guides/entityframework) | | **Sentry.Extensions.Logging** | [![Downloads](https://img.shields.io/nuget/dt/Sentry.Extensions.Logging.svg)](https://www.nuget.org/packages/Sentry.Extensions.Logging) | [![NuGet](https://img.shields.io/nuget/v/Sentry.Extensions.Logging.svg)](https://www.nuget.org/packages/Sentry.Extensions.Logging) | [![NuGet](https://img.shields.io/nuget/vpre/Sentry.Extensions.Logging.svg)](https://www.nuget.org/packages/Sentry.Extensions.Logging) | [![Documentation](https://img.shields.io/badge/documentation-sentry.io-green.svg)](https://docs.sentry.io/platforms/dotnet/guides/extensions-logging/) | From 3b1b4d6a45dfd870e6b9c69ccaf6277f93bf7cba Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Mon, 22 May 2023 12:08:37 -0700 Subject: [PATCH 047/142] Changelog updates (#2388) * Update CHANGELOG.md * Update CHANGELOG.md --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f290e0fc5..6a53472e79 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,12 @@ and adds includes data that represents the hierarchical structure of inner exceptions. Sentry now recognizes this server-side, improving the accuracy of the issue detail page. - Accordingly, the `KeepAggregateException` option is now obsolete and does nothing. Please remove any usages of `KeepAggregateException`. + - NOTE: If running Self-Hosted Sentry, you should wait to adopt this SDK update until after updating to the 23.6.0 (est. June 2023) release of Sentry. + The effect of updating the SDK early will be as if `KeepAggregateException = true` was set. That will not break anything, but may affect issue grouping and alerts. + +### Fixes + + - Status messages when uploading symbols or sources are improved. ([#2307](https://github.com/getsentry/sentry-dotnet/issues/2307)) ### Dependencies From e2ee25947aac4665546c01614099239de46c6b31 Mon Sep 17 00:00:00 2001 From: getsentry-bot Date: Mon, 22 May 2023 19:18:26 +0000 Subject: [PATCH 048/142] release: 3.33.0 --- CHANGELOG.md | 2 +- Directory.Build.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a53472e79..8368836ea7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## Unreleased +## 3.33.0 ### Features diff --git a/Directory.Build.props b/Directory.Build.props index 79b6838811..2010b193a7 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,7 +1,7 @@ - 3.32.0 + 3.33.0 11 true $(MSBuildThisFileDirectory).assets\Sentry.snk From 3e8edb7e9f1f4e947a54feadd3903c5e93fd9859 Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Tue, 23 May 2023 16:29:33 +0000 Subject: [PATCH 049/142] Set the native sdk name for Android (#2389) * Set Native SDK name on Android * Update CHANGELOG.md --- CHANGELOG.md | 6 ++++++ src/Sentry/Platforms/Android/SentrySdk.cs | 1 + 2 files changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8368836ea7..71b1efe35f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +## Fixes + +- Set the native sdk name for Android ([#2389](https://github.com/getsentry/sentry-dotnet/pull/2389)) + ## 3.33.0 ### Features diff --git a/src/Sentry/Platforms/Android/SentrySdk.cs b/src/Sentry/Platforms/Android/SentrySdk.cs index 01c25f5efc..232ec39df2 100644 --- a/src/Sentry/Platforms/Android/SentrySdk.cs +++ b/src/Sentry/Platforms/Android/SentrySdk.cs @@ -163,6 +163,7 @@ private static void InitSentryAndroidSdk(SentryOptions options) o.EnableExternalConfiguration = false; o.EnableDeduplication = false; o.AttachServerName = false; + o.NativeSdkName = "sentry.native.dotnet"; // These options are intentionally not expose or modified //o.MaxRequestBodySize // N/A for Android apps From dcc32b22b29fe77f1129f9cb848388af6093ece5 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Wed, 24 May 2023 16:36:36 +1200 Subject: [PATCH 050/142] Fixed: SentryHttpMessageHandler added when AddHttpClient is before UseSentry (#2390) * Fixed #2373: SentryHttpMessageHandler not being added when AddHttpClient is before UseSentry Fixes #2373 * Updated changelog --- CHANGELOG.md | 3 ++- .../DependencyInjection/ServiceCollectionExtensions.cs | 3 ++- .../SentryHttpMessageHandlerBuilderFilter.cs | 8 +++++++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 71b1efe35f..9051b78506 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,9 @@ ## Unreleased -## Fixes +### Fixes +- SentryHttpMessageHandler added when AddHttpClient is before UseSentry ([#2390](https://github.com/getsentry/sentry-dotnet/pull/2390)) - Set the native sdk name for Android ([#2389](https://github.com/getsentry/sentry-dotnet/pull/2389)) ## 3.33.0 diff --git a/src/Sentry.Extensions.Logging/Extensions/DependencyInjection/ServiceCollectionExtensions.cs b/src/Sentry.Extensions.Logging/Extensions/DependencyInjection/ServiceCollectionExtensions.cs index 839a34ddc6..2c19f126a0 100644 --- a/src/Sentry.Extensions.Logging/Extensions/DependencyInjection/ServiceCollectionExtensions.cs +++ b/src/Sentry.Extensions.Logging/Extensions/DependencyInjection/ServiceCollectionExtensions.cs @@ -40,7 +40,8 @@ public static IServiceCollection AddSentry(this IServiceCollection ser // Custom handler for HttpClientFactory. // Must be singleton: https://github.com/getsentry/sentry-dotnet/issues/785 - services.TryAddSingleton(); + // Must use AddSingleton: https://github.com/getsentry/sentry-dotnet/issues/2373 + services.AddSingleton(); return services; } diff --git a/src/Sentry.Extensions.Logging/SentryHttpMessageHandlerBuilderFilter.cs b/src/Sentry.Extensions.Logging/SentryHttpMessageHandlerBuilderFilter.cs index 82858cc1d6..f35cad05d9 100644 --- a/src/Sentry.Extensions.Logging/SentryHttpMessageHandlerBuilderFilter.cs +++ b/src/Sentry.Extensions.Logging/SentryHttpMessageHandlerBuilderFilter.cs @@ -14,7 +14,13 @@ public Action Configure(Action { var hub = _getHub(); - handlerBuilder.AdditionalHandlers.Add(new SentryHttpMessageHandler(hub)); + if (!handlerBuilder.AdditionalHandlers.Any(h => h is SentryHttpMessageHandler)) + { + handlerBuilder.AdditionalHandlers.Add( + new SentryHttpMessageHandler(hub) + ); + } + next(handlerBuilder); }; } From 7a2092e3dd4ad160904cdb7ca48f6a20843dc0f7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 27 May 2023 11:56:01 +0200 Subject: [PATCH 051/142] chore(deps): update Cocoa SDK to v8.7.3 (#2394) * chore: update modules/sentry-cocoa to 8.7.3 * Update ApiDefinitions.cs --------- Co-authored-by: GitHub Co-authored-by: Matt Johnson-Pint --- CHANGELOG.md | 6 ++++++ modules/sentry-cocoa | 2 +- src/Sentry.Bindings.Cocoa/ApiDefinitions.cs | 23 ++++++++++++++++++--- 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9051b78506..22a457bbb0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ - SentryHttpMessageHandler added when AddHttpClient is before UseSentry ([#2390](https://github.com/getsentry/sentry-dotnet/pull/2390)) - Set the native sdk name for Android ([#2389](https://github.com/getsentry/sentry-dotnet/pull/2389)) +### Dependencies + +- Bump Cocoa SDK from v8.7.2 to v8.7.3 ([#2394](https://github.com/getsentry/sentry-dotnet/pull/2394)) + - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#873) + - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.7.2...8.7.3) + ## 3.33.0 ### Features diff --git a/modules/sentry-cocoa b/modules/sentry-cocoa index 8dab665edf..9cf7d2e514 160000 --- a/modules/sentry-cocoa +++ b/modules/sentry-cocoa @@ -1 +1 @@ -Subproject commit 8dab665edf492580977d353f8f8bf2da57c04420 +Subproject commit 9cf7d2e514af1600cc2b3c5592e2848c6c5a76d6 diff --git a/src/Sentry.Bindings.Cocoa/ApiDefinitions.cs b/src/Sentry.Bindings.Cocoa/ApiDefinitions.cs index d06407a988..5852fc5c3b 100644 --- a/src/Sentry.Bindings.Cocoa/ApiDefinitions.cs +++ b/src/Sentry.Bindings.Cocoa/ApiDefinitions.cs @@ -237,17 +237,29 @@ interface SentryCrashExceptionApplication [Internal] interface SentryDebugImageProvider { - // -(NSArray * _Nonnull)getDebugImagesForThreads:(NSArray * _Nonnull)threads; + // -(NSArray * _Nonnull)getDebugImagesForThreads:(NSArray * _Nonnull)threads __attribute__((deprecated("Use -[getDebugImagesForThreads:isCrash:] instead."))); [Export ("getDebugImagesForThreads:")] SentryDebugMeta[] GetDebugImagesForThreads (SentryThread[] threads); - // -(NSArray * _Nonnull)getDebugImagesForFrames:(NSArray * _Nonnull)frames; + // -(NSArray * _Nonnull)getDebugImagesForThreads:(NSArray * _Nonnull)threads isCrash:(BOOL)isCrash; + [Export ("getDebugImagesForThreads:isCrash:")] + SentryDebugMeta[] GetDebugImagesForThreads (SentryThread[] threads, bool isCrash); + + // -(NSArray * _Nonnull)getDebugImagesForFrames:(NSArray * _Nonnull)frames __attribute__((deprecated("Use -[getDebugImagesForFrames:isCrash:] instead."))); [Export ("getDebugImagesForFrames:")] SentryDebugMeta[] GetDebugImagesForFrames (SentryFrame[] frames); - // -(NSArray * _Nonnull)getDebugImages; + // -(NSArray * _Nonnull)getDebugImagesForFrames:(NSArray * _Nonnull)frames isCrash:(BOOL)isCrash; + [Export ("getDebugImagesForFrames:isCrash:")] + SentryDebugMeta[] GetDebugImagesForFrames (SentryFrame[] frames, bool isCrash); + + // -(NSArray * _Nonnull)getDebugImages __attribute__((deprecated("Use -[getDebugImagesCrashed:] instead."))); [Export ("getDebugImages")] SentryDebugMeta[] DebugImages { get; } + + // -(NSArray * _Nonnull)getDebugImagesCrashed:(BOOL)isCrash; + [Export ("getDebugImagesCrashed:")] + SentryDebugMeta[] GetDebugImagesCrashed (bool isCrash); } // @interface SentryDebugMeta : NSObject @@ -2347,6 +2359,11 @@ interface PrivateSentrySDKOnly [Export ("getDebugImages")] SentryDebugMeta[] DebugImages { get; } + // +(NSArray * _Nonnull)getDebugImagesCrashed:(BOOL)isCrash; + [Static] + [Export ("getDebugImagesCrashed:")] + SentryDebugMeta[] GetDebugImagesCrashed (bool isCrash); + // +(void)setSdkName:(NSString * _Nonnull)sdkName andVersionString:(NSString * _Nonnull)versionString; [Static] [Export ("setSdkName:andVersionString:")] From a674815a4ed0fe807b0bec4205c4ea48c085a988 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 May 2023 10:27:49 -0700 Subject: [PATCH 052/142] Bump github/codeql-action from 2.3.3 to 2.3.5 (#2396) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2.3.3 to 2.3.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/29b1f65c5e92e24fe6b6647da1eaabe529cec70f...0225834cc549ee0ca93cb085b92954821a145866) --- 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 80252f849b..df4eff310e 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -34,7 +34,7 @@ jobs: uses: ./.github/actions/environment - name: Initialize CodeQL - uses: github/codeql-action/init@29b1f65c5e92e24fe6b6647da1eaabe529cec70f # pin@v2 + uses: github/codeql-action/init@0225834cc549ee0ca93cb085b92954821a145866 # pin@v2 with: languages: csharp @@ -45,6 +45,6 @@ jobs: run: dotnet build Sentry-CI-CodeQL.slnf --no-restore --nologo - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@29b1f65c5e92e24fe6b6647da1eaabe529cec70f # pin@v2 + uses: github/codeql-action/analyze@0225834cc549ee0ca93cb085b92954821a145866 # pin@v2 with: category: '/language:csharp' From 1e07cd1a25d6df3a9c0742502d9757f82a2143ff Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Tue, 30 May 2023 10:33:45 -0700 Subject: [PATCH 053/142] Don't cancel previous runs on main or release branches (#2400) --- .github/workflows/build.yml | 1 + .github/workflows/codeql-analysis.yml | 1 + .github/workflows/device-tests-android.yml | 1 + .github/workflows/device-tests-ios.yml | 1 + 4 files changed, 4 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ff3480e3f0..1b3c03b2fe 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -22,6 +22,7 @@ jobs: steps: - name: Cancel Previous Runs + if: github.ref_name != 'main' && !startsWith(github.ref_name, 'release/') uses: styfle/cancel-workflow-action@b173b6ec0100793626c2d9e6b90435061f4fc3e5 # Tag: 0.11.0 - name: Checkout diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index df4eff310e..0aeb1ad409 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -23,6 +23,7 @@ jobs: steps: - name: Cancel Previous Runs + if: github.ref_name != 'main' && !startsWith(github.ref_name, 'release/') uses: styfle/cancel-workflow-action@b173b6ec0100793626c2d9e6b90435061f4fc3e5 # Tag: 0.11.0 - name: Checkout repository diff --git a/.github/workflows/device-tests-android.yml b/.github/workflows/device-tests-android.yml index 59731a2cde..ef9728912d 100644 --- a/.github/workflows/device-tests-android.yml +++ b/.github/workflows/device-tests-android.yml @@ -15,6 +15,7 @@ jobs: DOTNET_NOLOGO: 1 steps: - name: Cancel Previous Runs + if: github.ref_name != 'main' && !startsWith(github.ref_name, 'release/') uses: styfle/cancel-workflow-action@b173b6ec0100793626c2d9e6b90435061f4fc3e5 # Tag: 0.11.0 - name: Checkout diff --git a/.github/workflows/device-tests-ios.yml b/.github/workflows/device-tests-ios.yml index d7887816ec..65ea56d67c 100644 --- a/.github/workflows/device-tests-ios.yml +++ b/.github/workflows/device-tests-ios.yml @@ -17,6 +17,7 @@ jobs: NO_MACCATALYST: true steps: - name: Cancel Previous Runs + if: github.ref_name != 'main' && !startsWith(github.ref_name, 'release/') uses: styfle/cancel-workflow-action@b173b6ec0100793626c2d9e6b90435061f4fc3e5 # Tag: 0.11.0 - name: Checkout From ba3ae1cbc8d2d1d3b56971adb85b3ef7749d538a Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Tue, 30 May 2023 16:18:57 -0700 Subject: [PATCH 054/142] Fix NLog tests (#2402) --- test/Sentry.NLog.Tests/SentryTargetTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Sentry.NLog.Tests/SentryTargetTests.cs b/test/Sentry.NLog.Tests/SentryTargetTests.cs index b1ee45d29f..946c7b38e4 100644 --- a/test/Sentry.NLog.Tests/SentryTargetTests.cs +++ b/test/Sentry.NLog.Tests/SentryTargetTests.cs @@ -140,7 +140,7 @@ public void Shutdown_DisposesSdk() { _fixture.Options.InitializeSdk = false; var target = _fixture.GetTarget(); - SimpleConfigurator.ConfigureForTargetLogging(target); + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(target)); var sut = LogManager.GetCurrentClassLogger(); From 1f8da20f3bbd24086764236dff2230b9021ff817 Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Wed, 31 May 2023 08:52:31 -0700 Subject: [PATCH 055/142] Update mobile samples (#2401) --- samples/Directory.Build.props | 5 +++++ .../Sentry.Samples.Android.csproj | 12 +++++++++++- .../Sentry.Samples.Ios.csproj | 11 ++++++++++- .../Sentry.Samples.MacCatalyst.csproj | 14 ++++++++++++-- .../Sentry.Samples.Maui.csproj | 17 +++++++++++++---- test/sentry-cli-integration.Tests.ps1 | 4 ++-- 6 files changed, 53 insertions(+), 10 deletions(-) diff --git a/samples/Directory.Build.props b/samples/Directory.Build.props index 924ea817b1..59e07989bb 100644 --- a/samples/Directory.Build.props +++ b/samples/Directory.Build.props @@ -33,4 +33,9 @@ + + + false + + diff --git a/samples/Sentry.Samples.Android/Sentry.Samples.Android.csproj b/samples/Sentry.Samples.Android/Sentry.Samples.Android.csproj index 23a08edc27..a2a2bbd458 100644 --- a/samples/Sentry.Samples.Android/Sentry.Samples.Android.csproj +++ b/samples/Sentry.Samples.Android/Sentry.Samples.Android.csproj @@ -1,6 +1,6 @@ - net6.0-android + net7.0-android 21 Exe enable @@ -13,4 +13,14 @@ + + + + true + true + + diff --git a/samples/Sentry.Samples.Ios/Sentry.Samples.Ios.csproj b/samples/Sentry.Samples.Ios/Sentry.Samples.Ios.csproj index 5f776029aa..678df6737d 100644 --- a/samples/Sentry.Samples.Ios/Sentry.Samples.Ios.csproj +++ b/samples/Sentry.Samples.Ios/Sentry.Samples.Ios.csproj @@ -1,7 +1,7 @@ - net6.0-ios + net7.0-ios Exe enable true @@ -27,4 +27,13 @@ iossimulator-arm64 + + + true + true + + diff --git a/samples/Sentry.Samples.MacCatalyst/Sentry.Samples.MacCatalyst.csproj b/samples/Sentry.Samples.MacCatalyst/Sentry.Samples.MacCatalyst.csproj index 655fc66eb0..1ccbf6ea9e 100644 --- a/samples/Sentry.Samples.MacCatalyst/Sentry.Samples.MacCatalyst.csproj +++ b/samples/Sentry.Samples.MacCatalyst/Sentry.Samples.MacCatalyst.csproj @@ -1,6 +1,6 @@ - net6.0-maccatalyst + net7.0-maccatalyst Exe enable true @@ -18,12 +18,22 @@ $([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture) maccatalyst-arm64 + maccatalyst-x64 + + + + + true + true diff --git a/samples/Sentry.Samples.Maui/Sentry.Samples.Maui.csproj b/samples/Sentry.Samples.Maui/Sentry.Samples.Maui.csproj index 2444654b3f..8e6ad090e7 100644 --- a/samples/Sentry.Samples.Maui/Sentry.Samples.Maui.csproj +++ b/samples/Sentry.Samples.Maui/Sentry.Samples.Maui.csproj @@ -6,14 +6,15 @@ On Mac, we'll also build for iOS and MacCatalyst. On Windows, we'll also build for Windows 10. --> - net6.0-android - $(TargetFrameworks);net6.0-windows10.0.19041.0 - $(TargetFrameworks);net6.0-ios;net6.0-maccatalyst + net7.0-android + $(TargetFrameworks);net7.0-windows10.0.19041.0 + $(TargetFrameworks);net7.0-ios;net7.0-maccatalyst Exe Sentry.Samples.Maui true true enable + false Sentry.Samples.Maui @@ -38,10 +39,17 @@ --> $(DefineConstants);DISABLE_XAML_GENERATED_BREAK_ON_UNHANDLED_EXCEPTION + + true + true + @@ -49,6 +57,7 @@ $([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) iossimulator-arm64 maccatalyst-arm64 + maccatalyst-x64 diff --git a/test/sentry-cli-integration.Tests.ps1 b/test/sentry-cli-integration.Tests.ps1 index e28d78ec52..2d6a0ae860 100644 --- a/test/sentry-cli-integration.Tests.ps1 +++ b/test/sentry-cli-integration.Tests.ps1 @@ -91,7 +91,7 @@ Describe 'CLI-integration' { } It "uploads symbols and sources for a MAUI Android app build" { - $result = DotnetBuild 'Sentry.Samples.Maui' $True $True 'net6.0-android' + $result = DotnetBuild 'Sentry.Samples.Maui' $True $True 'net7.0-android' $result.ScriptOutput | Should -Contain 'Build succeeded.' $result.HasErrors() | Should -BeFalse $result.UploadedDebugFiles() | Sort-Object -Unique | Should -Be @( @@ -111,7 +111,7 @@ Describe 'CLI-integration' { } It "uploads symbols and sources for a MAUI iOS app build" { - $result = DotnetBuild 'Sentry.Samples.Maui' $True $True 'net6.0-ios' + $result = DotnetBuild 'Sentry.Samples.Maui' $True $True 'net7.0-ios' $result.ScriptOutput | Should -Contain 'Build succeeded.' $result.HasErrors() | Should -BeFalse $result.UploadedDebugFiles() | Sort-Object -Unique | Should -Be @( From 80c3227a8f3f989640d22ddece4d78d9ff0415e0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 31 May 2023 09:19:49 -0700 Subject: [PATCH 056/142] chore(deps): update Java SDK to v6.20.0 (#2395) * chore: update scripts/update-java.ps1 to 6.20.0 * Add option to enable/disable the Android root check --------- Co-authored-by: GitHub Co-authored-by: Matt Johnson-Pint --- CHANGELOG.md | 3 +++ src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj | 2 +- src/Sentry/Platforms/Android/SentryOptions.cs | 7 +++++++ src/Sentry/Platforms/Android/SentrySdk.cs | 1 + 4 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 22a457bbb0..ecbdf91b2b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,9 @@ - Bump Cocoa SDK from v8.7.2 to v8.7.3 ([#2394](https://github.com/getsentry/sentry-dotnet/pull/2394)) - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#873) - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.7.2...8.7.3) +- Bump Java SDK from v6.19.1 to v6.20.0 ([#2395](https://github.com/getsentry/sentry-dotnet/pull/2395)) + - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#6200) + - [diff](https://github.com/getsentry/sentry-java/compare/6.19.1...6.20.0) ## 3.33.0 diff --git a/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj b/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj index fc77b422f6..d79c3be3fa 100644 --- a/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj +++ b/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj @@ -3,7 +3,7 @@ net6.0-android $(NoWarn);BG8605;BG8606 - 6.19.1 + 6.20.0 $(BaseIntermediateOutputPath)sdks\Sentry\Android\$(SentryAndroidSdkVersion)\ .NET Bindings for the Sentry Android SDK diff --git a/src/Sentry/Platforms/Android/SentryOptions.cs b/src/Sentry/Platforms/Android/SentryOptions.cs index 459b3ad408..2c352c729c 100644 --- a/src/Sentry/Platforms/Android/SentryOptions.cs +++ b/src/Sentry/Platforms/Android/SentryOptions.cs @@ -87,6 +87,13 @@ internal AndroidOptions(SentryOptions options) /// public bool EnableAppLifecycleBreadcrumbs { get; set; } = true; + /// + /// Gets or sets a value that controls checking whether the device has been rooted. The check itself may cause app stores to flag + /// an application as harmful, in which case this property can be set false to disable the check. + /// The default value is true (enabled). + /// + public bool EnableRootCheck { get; set; } = true; + /// /// Gets or sets a value that indicates if automatic breadcrumbs for system events are enabled. /// The default value is true (enabled). diff --git a/src/Sentry/Platforms/Android/SentrySdk.cs b/src/Sentry/Platforms/Android/SentrySdk.cs index 232ec39df2..09019f418a 100644 --- a/src/Sentry/Platforms/Android/SentrySdk.cs +++ b/src/Sentry/Platforms/Android/SentrySdk.cs @@ -141,6 +141,7 @@ private static void InitSentryAndroidSdk(SentryOptions options) o.EnableActivityLifecycleTracingAutoFinish = options.Android.EnableActivityLifecycleTracingAutoFinish; o.EnableAppComponentBreadcrumbs = options.Android.EnableAppComponentBreadcrumbs; o.EnableAppLifecycleBreadcrumbs = options.Android.EnableAppLifecycleBreadcrumbs; + o.EnableRootCheck = options.Android.EnableRootCheck; o.EnableSystemEventBreadcrumbs = options.Android.EnableSystemEventBreadcrumbs; o.EnableUserInteractionBreadcrumbs = options.Android.EnableUserInteractionBreadcrumbs; o.EnableUserInteractionTracing = options.Android.EnableUserInteractionTracing; From f551ffe4987a530b8594cd71abe5c121e911589f Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Wed, 31 May 2023 22:21:45 -0700 Subject: [PATCH 057/142] Various .NET MAUI fixes / improvements (#2403) * Fix battery level * Set OS architecture * Fix Windows OS version info * Fix in-app frames for Windows * Fix in-app frames for iOS/MacCatalyst * Update CHANGELOG.md --- CHANGELOG.md | 5 +++++ src/Sentry.Maui/Internal/MauiDeviceData.cs | 4 ++-- src/Sentry.Maui/Internal/MauiOsData.cs | 24 ++++++++++++++++------ src/Sentry/SentryOptions.cs | 3 +++ 4 files changed, 28 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ecbdf91b2b..a4874cabe5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,11 @@ - SentryHttpMessageHandler added when AddHttpClient is before UseSentry ([#2390](https://github.com/getsentry/sentry-dotnet/pull/2390)) - Set the native sdk name for Android ([#2389](https://github.com/getsentry/sentry-dotnet/pull/2389)) +- Various .NET MAUI fixes / improvements ([#2403](https://github.com/getsentry/sentry-dotnet/pull/2403)) + - The battery level was being reported incorrectly due to percentage multiplier. + - The device architecture (x64, arm64, etc.) is now reported + - On Windows, the OS type is now reported as "Windows" instead of "WinUI". Additionally, the OS display version (ex, "22H2") is now included. + - `UIKit`, `ABI.Microsoft` and `WinRT` frames are now marked "system" instead of "in app". ### Dependencies diff --git a/src/Sentry.Maui/Internal/MauiDeviceData.cs b/src/Sentry.Maui/Internal/MauiDeviceData.cs index d9c4b5b489..377feccff3 100644 --- a/src/Sentry.Maui/Internal/MauiDeviceData.cs +++ b/src/Sentry.Maui/Internal/MauiDeviceData.cs @@ -32,7 +32,7 @@ public static void ApplyMauiDeviceData(this Device device, IDiagnosticLogger? lo // device.Brand ??= ? // device.Family ??= ? // device.ModelId ??= ? - // device.Architecture ??= ? + device.Architecture ??= RuntimeInformation.OSArchitecture.ToString(); // ? = deviceInfo.Platform; // ? = deviceInfo.VersionString; @@ -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; + device.BatteryLevel ??= battery.ChargeLevel < 0 ? null : (short)(battery.ChargeLevel * 100.0); device.BatteryStatus ??= battery.State.ToString(); device.IsCharging ??= battery.State switch { diff --git a/src/Sentry.Maui/Internal/MauiOsData.cs b/src/Sentry.Maui/Internal/MauiOsData.cs index 1c34c817fb..e49759ee5f 100644 --- a/src/Sentry.Maui/Internal/MauiOsData.cs +++ b/src/Sentry.Maui/Internal/MauiOsData.cs @@ -1,3 +1,4 @@ +using Microsoft.Win32; using Sentry.Extensibility; using OperatingSystem = Sentry.Protocol.OperatingSystem; @@ -17,13 +18,24 @@ public static void ApplyMauiOsData(this OperatingSystem os, IDiagnosticLogger? l return; } - os.Name ??= deviceInfo.Platform.ToString(); - os.Version ??= deviceInfo.VersionString; + os.Version = deviceInfo.VersionString; + +#if WINDOWS + os.Name ??= "Windows"; + + using var key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion"); + if (key?.GetValue("DisplayVersion") is string displayVersion) + { + os.Build = displayVersion; + } + else if (key?.GetValue("ReleaseId") is string releaseId) + { + os.Build = releaseId; + } +#else + os.Name = deviceInfo.Platform.ToString(); +#endif - // TODO: fill in these - // os.Build ??= ? - // os.KernelVersion ??= ? - // os.Rooted ??= ? } catch (Exception ex) { diff --git a/src/Sentry/SentryOptions.cs b/src/Sentry/SentryOptions.cs index ead29854a9..07119b05d4 100644 --- a/src/Sentry/SentryOptions.cs +++ b/src/Sentry/SentryOptions.cs @@ -1054,6 +1054,9 @@ public SentryOptions() "Sentry", "Microsoft", "MS", // MS.Win32, MS.Internal, etc: Desktop apps + "ABI.Microsoft", // MAUI + "WinRT", // WinRT, UWP, WinUI + "UIKit", // iOS / MacCatalyst "Newtonsoft.Json", "FSharp", "Serilog", From 4df94c6adedce1bd7c2ea16de193b6355acf7b1f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 31 May 2023 22:25:30 -0700 Subject: [PATCH 058/142] chore: update scripts/update-java.ps1 to 6.21.0 (#2405) Co-authored-by: GitHub --- CHANGELOG.md | 6 +++--- src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a4874cabe5..6de3c34d06 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,9 +17,9 @@ - Bump Cocoa SDK from v8.7.2 to v8.7.3 ([#2394](https://github.com/getsentry/sentry-dotnet/pull/2394)) - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#873) - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.7.2...8.7.3) -- Bump Java SDK from v6.19.1 to v6.20.0 ([#2395](https://github.com/getsentry/sentry-dotnet/pull/2395)) - - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#6200) - - [diff](https://github.com/getsentry/sentry-java/compare/6.19.1...6.20.0) +- Bump Java SDK from v6.19.1 to v6.21.0 ([#2395](https://github.com/getsentry/sentry-dotnet/pull/2395), [#2405](https://github.com/getsentry/sentry-dotnet/pull/2405)) + - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#6210) + - [diff](https://github.com/getsentry/sentry-java/compare/6.19.1...6.21.0) ## 3.33.0 diff --git a/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj b/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj index d79c3be3fa..28f219fe4e 100644 --- a/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj +++ b/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj @@ -3,7 +3,7 @@ net6.0-android $(NoWarn);BG8605;BG8606 - 6.20.0 + 6.21.0 $(BaseIntermediateOutputPath)sdks\Sentry\Android\$(SentryAndroidSdkVersion)\ .NET Bindings for the Sentry Android SDK From a1f79078940e60090d4bde0b0ddd8272ba23ed5f Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Thu, 1 Jun 2023 14:54:47 -0700 Subject: [PATCH 059/142] Fix system frames being marked as "in-app" (#2408) * Ensure namespace is not stripped from module name * Add Test * Update CHANGELOG.md --- CHANGELOG.md | 2 ++ src/Sentry/Internal/DebugStackTrace.cs | 9 +++++++- .../Internals/DebugStackTraceTests.cs | 22 +++++++++++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6de3c34d06..7a9c266c44 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ - The device architecture (x64, arm64, etc.) is now reported - On Windows, the OS type is now reported as "Windows" instead of "WinUI". Additionally, the OS display version (ex, "22H2") is now included. - `UIKit`, `ABI.Microsoft` and `WinRT` frames are now marked "system" instead of "in app". +- Fix system frames being marked as "in-app" ([#2408](https://github.com/getsentry/sentry-dotnet/pull/2408)) + - NOTE: This important fix corrects a value that is used during issue grouping, so you may receive new alerts for existing issues after deploying this update. ### Dependencies diff --git a/src/Sentry/Internal/DebugStackTrace.cs b/src/Sentry/Internal/DebugStackTrace.cs index 8d7c5a8c69..c6301933ad 100644 --- a/src/Sentry/Internal/DebugStackTrace.cs +++ b/src/Sentry/Internal/DebugStackTrace.cs @@ -195,7 +195,14 @@ private SentryStackFrame InternalCreateFrame(StackFrame stackFrame, bool demangl { stringBuilder.Clear(); stringBuilder.AppendTypeDisplayName(declaringType); - frame.Module = stringBuilder.ToString(); + + // Ben.Demystifier doesn't always include the namespace, even when fullName==true. + // It's important that the module name always be fully qualified, so that in-app frame + // detection works correctly. + var module = stringBuilder.ToString(); + frame.Module = declaringType.Namespace is { } ns && !module.StartsWith(ns) + ? $"{ns}.{module}" + : module; } } else diff --git a/test/Sentry.Tests/Internals/DebugStackTraceTests.cs b/test/Sentry.Tests/Internals/DebugStackTraceTests.cs index f61bda8a36..62e68c4524 100644 --- a/test/Sentry.Tests/Internals/DebugStackTraceTests.cs +++ b/test/Sentry.Tests/Internals/DebugStackTraceTests.cs @@ -36,6 +36,28 @@ public void CreateSentryStackFrame_AppNamespaceExcluded_NotInAppFrame() Assert.False(actual.InApp); } + [Theory] + [InlineData(true)] + [InlineData(false)] + public void CreateSentryStackFrame_SystemType_NotInAppFrame(bool useEnhancedStackTrace) + { + // Arrange + var sut = _fixture.GetSut(); + var exception = Assert.ThrowsAny(() => _ = Convert.FromBase64String("This will throw.")); + var stackTrace = new StackTrace(exception); + var frame = useEnhancedStackTrace ? EnhancedStackTrace.GetFrames(stackTrace)[0] : stackTrace.GetFrame(0); + + // Sanity Check + Assert.NotNull(frame); + Assert.Equal(typeof(Convert), frame.GetMethod()?.DeclaringType); + + // Act + var actual = sut.CreateFrame(frame); + + // Assert + Assert.False(actual.InApp); + } + [Fact] public void CreateSentryStackFrame_NamespaceIncludedAndExcluded_IncludesTakesPrecedence() { From 3a9222753316f49fc5114c8b0e8d1f298ad82c4b Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Thu, 1 Jun 2023 14:57:36 -0700 Subject: [PATCH 060/142] Reduce debug files uploaded (#2404) --- CHANGELOG.md | 1 + src/Sentry/buildTransitive/Sentry.targets | 9 ++++++++- test/sentry-cli-integration.Tests.ps1 | 4 ---- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a9c266c44..8dcf725835 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - The device architecture (x64, arm64, etc.) is now reported - On Windows, the OS type is now reported as "Windows" instead of "WinUI". Additionally, the OS display version (ex, "22H2") is now included. - `UIKit`, `ABI.Microsoft` and `WinRT` frames are now marked "system" instead of "in app". +- Reduce debug files uploaded ([#2404](https://github.com/getsentry/sentry-dotnet/pull/2404)) - Fix system frames being marked as "in-app" ([#2408](https://github.com/getsentry/sentry-dotnet/pull/2408)) - NOTE: This important fix corrects a value that is used during issue grouping, so you may receive new alerts for existing issues after deploying this update. diff --git a/src/Sentry/buildTransitive/Sentry.targets b/src/Sentry/buildTransitive/Sentry.targets index 1f7c61f6d4..4b24d90adf 100644 --- a/src/Sentry/buildTransitive/Sentry.targets +++ b/src/Sentry/buildTransitive/Sentry.targets @@ -125,7 +125,14 @@ - + + + + + + + + diff --git a/test/sentry-cli-integration.Tests.ps1 b/test/sentry-cli-integration.Tests.ps1 index 2d6a0ae860..99f1840c0d 100644 --- a/test/sentry-cli-integration.Tests.ps1 +++ b/test/sentry-cli-integration.Tests.ps1 @@ -53,25 +53,21 @@ BeforeAll { Describe 'CLI-integration' { It "uploads symbols and sources for a console app build" { - $exe = [RuntimeInformation]::IsOSPlatform([OSPlatform]::Windows) ? '.exe' : '' $result = DotnetBuild 'Sentry.Samples.Console.Basic' $True $True $result.ScriptOutput | Should -Contain 'Build succeeded.' $result.HasErrors() | Should -BeFalse $result.UploadedDebugFiles() | Sort-Object -Unique | Should -Be @( 'Sentry.pdb', - "Sentry.Samples.Console.Basic$exe", 'Sentry.Samples.Console.Basic.pdb', 'Sentry.Samples.Console.Basic.src.zip') } It "uploads symbols for a console app build" { - $exe = [RuntimeInformation]::IsOSPlatform([OSPlatform]::Windows) ? '.exe' : '' $result = DotnetBuild 'Sentry.Samples.Console.Basic' $True $False $result.ScriptOutput | Should -Contain 'Build succeeded.' $result.HasErrors() | Should -BeFalse $result.UploadedDebugFiles() | Sort-Object -Unique | Should -Be @( 'Sentry.pdb', - "Sentry.Samples.Console.Basic$exe", 'Sentry.Samples.Console.Basic.pdb') } From 00299c7a950f7229b31153db5cff82001527b0d7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Jun 2023 10:30:13 -0700 Subject: [PATCH 061/142] Bump github/codeql-action from 2.3.5 to 2.3.6 (#2412) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2.3.5 to 2.3.6. - [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/0225834cc549ee0ca93cb085b92954821a145866...83f0fe6c4988d98a455712a27f0255212bba9bd4) --- 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 0aeb1ad409..8086290c61 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@0225834cc549ee0ca93cb085b92954821a145866 # pin@v2 + uses: github/codeql-action/init@83f0fe6c4988d98a455712a27f0255212bba9bd4 # 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@0225834cc549ee0ca93cb085b92954821a145866 # pin@v2 + uses: github/codeql-action/analyze@83f0fe6c4988d98a455712a27f0255212bba9bd4 # pin@v2 with: category: '/language:csharp' From cb74dcde29d6e0430b2507ba72b35f2f486f2588 Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Mon, 5 Jun 2023 17:20:32 -0700 Subject: [PATCH 062/142] Update README.md (#2413) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0a6f6fe108..165364ac0f 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Sentry SDK for .NET | **Sentry.AspNet** | [![Downloads](https://img.shields.io/nuget/dt/Sentry.AspNet.svg)](https://www.nuget.org/packages/Sentry.AspNet) | [![NuGet](https://img.shields.io/nuget/v/Sentry.AspNet.svg)](https://www.nuget.org/packages/Sentry.AspNet) | [![NuGet](https://img.shields.io/nuget/vpre/Sentry.AspNet.svg)](https://www.nuget.org/packages/Sentry.AspNet) | [![Documentation](https://img.shields.io/badge/documentation-sentry.io-green.svg)](https://docs.sentry.io/platforms/dotnet/guides/aspnet) | | **Sentry.AspNetCore** | [![Downloads](https://img.shields.io/nuget/dt/Sentry.AspNetCore.svg)](https://www.nuget.org/packages/Sentry.AspNetCore) | [![NuGet](https://img.shields.io/nuget/v/Sentry.AspNetCore.svg)](https://www.nuget.org/packages/Sentry.AspNetCore) | [![NuGet](https://img.shields.io/nuget/vpre/Sentry.AspNetCore.svg)](https://www.nuget.org/packages/Sentry.AspNetCore) | [![Documentation](https://img.shields.io/badge/documentation-sentry.io-green.svg)](https://docs.sentry.io/platforms/dotnet/guides/aspnetcore/) | | **Sentry.AspNetCore.Grpc** | [![Downloads](https://img.shields.io/nuget/dt/Sentry.AspNetCore.Grpc.svg)](https://www.nuget.org/packages/Sentry.AspNetCore.Grpc) | [![NuGet](https://img.shields.io/nuget/v/Sentry.AspNetCore.Grpc.svg)](https://www.nuget.org/packages/Sentry.AspNetCore.Grpc) | [![NuGet](https://img.shields.io/nuget/vpre/Sentry.AspNetCore.Grpc.svg)](https://www.nuget.org/packages/Sentry.AspNetCore.Grpc) | [![Documentation](https://img.shields.io/badge/documentation-sentry.io-green.svg)](https://docs.sentry.io/platforms/dotnet/guides/aspnetcore/) | -| **Sentry.AzureFunctions.Worker** | [![Downloads](https://img.shields.io/nuget/dt/Sentry.AzureFunctions.Worker.svg)](https://www.nuget.org/packages/Sentry.AzureFunctions.Worker.Grpc) | [![NuGet](https://img.shields.io/nuget/v/Sentry.AzureFunctions.Worker.svg)](https://www.nuget.org/packages/Sentry.AzureFunctions.Worker) | [![NuGet](https://img.shields.io/nuget/vpre/Sentry.AzureFunctions.Worker.svg)](https://www.nuget.org/packages/Sentry.AzureFunctions.Worker) | Documentation TBD | +| **Sentry.AzureFunctions.Worker** | [![Downloads](https://img.shields.io/nuget/dt/Sentry.AzureFunctions.Worker.svg)](https://www.nuget.org/packages/Sentry.AzureFunctions.Worker.Grpc) | [![NuGet](https://img.shields.io/nuget/v/Sentry.AzureFunctions.Worker.svg)](https://www.nuget.org/packages/Sentry.AzureFunctions.Worker) | [![NuGet](https://img.shields.io/nuget/vpre/Sentry.AzureFunctions.Worker.svg)](https://www.nuget.org/packages/Sentry.AzureFunctions.Worker) | [![Documentation](https://img.shields.io/badge/documentation-sentry.io-green.svg)](https://docs.sentry.io/platforms/dotnet/guides/azure-functions-worker/) | | **Sentry.DiagnosticSource** | [![Downloads](https://img.shields.io/nuget/dt/Sentry.DiagnosticSource.svg)](https://www.nuget.org/packages/Sentry.DiagnosticSource) | [![NuGet](https://img.shields.io/nuget/v/Sentry.DiagnosticSource.svg)](https://www.nuget.org/packages/Sentry.DiagnosticSource) | [![NuGet](https://img.shields.io/nuget/vpre/Sentry.DiagnosticSource.svg)](https://www.nuget.org/packages/Sentry.DiagnosticSource) | [![Documentation](https://img.shields.io/badge/documentation-sentry.io-green.svg)](https://docs.sentry.io/platforms/dotnet/performance/instrumentation/automatic-instrumentation/#diagnosticsource-integration) | | **Sentry.EntityFramework** | [![Downloads](https://img.shields.io/nuget/dt/Sentry.EntityFramework.svg)](https://www.nuget.org/packages/Sentry.EntityFramework) | [![NuGet](https://img.shields.io/nuget/v/Sentry.EntityFramework.svg)](https://www.nuget.org/packages/Sentry.EntityFramework) | [![NuGet](https://img.shields.io/nuget/vpre/Sentry.EntityFramework.svg)](https://www.nuget.org/packages/Sentry.EntityFramework) | [![Documentation](https://img.shields.io/badge/documentation-sentry.io-green.svg)](https://docs.sentry.io/platforms/dotnet/guides/entityframework) | | **Sentry.Extensions.Logging** | [![Downloads](https://img.shields.io/nuget/dt/Sentry.Extensions.Logging.svg)](https://www.nuget.org/packages/Sentry.Extensions.Logging) | [![NuGet](https://img.shields.io/nuget/v/Sentry.Extensions.Logging.svg)](https://www.nuget.org/packages/Sentry.Extensions.Logging) | [![NuGet](https://img.shields.io/nuget/vpre/Sentry.Extensions.Logging.svg)](https://www.nuget.org/packages/Sentry.Extensions.Logging) | [![Documentation](https://img.shields.io/badge/documentation-sentry.io-green.svg)](https://docs.sentry.io/platforms/dotnet/guides/extensions-logging/) | From f90018a3586379234b6b5550a113c932d2f30355 Mon Sep 17 00:00:00 2001 From: Sean Feldman Date: Wed, 7 Jun 2023 15:51:44 -0600 Subject: [PATCH 063/142] Populate scope's Cookies property (#2411) * Populate scope's Cookies property for ASP.NET Core * Populate scope's Cookies property for classic ASP.NET * Update CHANGELOG.md * Cover edge cases * Use pattern matching * Update CHANGELOG.md --------- Co-authored-by: Matt Johnson-Pint --- CHANGELOG.md | 1 + src/Sentry.AspNet/HttpContextExtensions.cs | 5 ++ src/Sentry.AspNetCore/ScopeExtensions.cs | 5 ++ .../Sentry.AspNet.Tests/HttpContextBuilder.cs | 20 ++++++ .../HttpContextExtensionsTests.cs | 69 +++++++++++++++++++ .../ScopeExtensionsTests.cs | 2 + 6 files changed, 102 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8dcf725835..42c68c2123 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ - Reduce debug files uploaded ([#2404](https://github.com/getsentry/sentry-dotnet/pull/2404)) - Fix system frames being marked as "in-app" ([#2408](https://github.com/getsentry/sentry-dotnet/pull/2408)) - NOTE: This important fix corrects a value that is used during issue grouping, so you may receive new alerts for existing issues after deploying this update. +- Populate scope's Cookies property ([#2411](https://github.com/getsentry/sentry-dotnet/pull/2411)) ### Dependencies diff --git a/src/Sentry.AspNet/HttpContextExtensions.cs b/src/Sentry.AspNet/HttpContextExtensions.cs index 9e0f86fe67..754cac419d 100644 --- a/src/Sentry.AspNet/HttpContextExtensions.cs +++ b/src/Sentry.AspNet/HttpContextExtensions.cs @@ -100,6 +100,11 @@ public static ITransaction StartSentryTransaction(this HttpContext httpContext) SentrySdk.ConfigureScope(scope => scope.Transaction = transaction); httpContext.Items[HttpContextTransactionItemName] = transaction; + if (options?.SendDefaultPii is true) + { + transaction.Request.Cookies = string.Join("; ", httpContext.Request.Cookies.AllKeys.Select(x => $"{x}={httpContext.Request.Cookies[x]?.Value}")); + } + return transaction; } diff --git a/src/Sentry.AspNetCore/ScopeExtensions.cs b/src/Sentry.AspNetCore/ScopeExtensions.cs index 61ba134177..6f53afa8be 100644 --- a/src/Sentry.AspNetCore/ScopeExtensions.cs +++ b/src/Sentry.AspNetCore/ScopeExtensions.cs @@ -141,6 +141,11 @@ private static void SetEnv(Scope scope, HttpContext context, SentryAspNetCoreOpt } scope.Request.Headers[requestHeader.Key] = requestHeader.Value; + + if (requestHeader.Key == HeaderNames.Cookie) + { + scope.Request.Cookies = requestHeader.Value; + } } // TODO: Hide these 'Env' behind some extension method as diff --git a/test/Sentry.AspNet.Tests/HttpContextBuilder.cs b/test/Sentry.AspNet.Tests/HttpContextBuilder.cs index a64d28ec8a..624ac46493 100644 --- a/test/Sentry.AspNet.Tests/HttpContextBuilder.cs +++ b/test/Sentry.AspNet.Tests/HttpContextBuilder.cs @@ -14,4 +14,24 @@ public static HttpContext Build(int responseStatusCode = 200) ApplicationInstance = new HttpApplication() }; } + + public static HttpContext BuildWithCookies(HttpCookie[] cookies, int responseStatusCode = 200) + { + var httpRequest = new HttpRequest("test", "http://test/the/path", null); + foreach (var cookie in cookies) + { + httpRequest.Cookies.Add(cookie); + } + + return new HttpContext( + httpRequest, + new HttpResponse(TextWriter.Null) + { + StatusCode = responseStatusCode + }) + { + ApplicationInstance = new HttpApplication() + }; + } + } diff --git a/test/Sentry.AspNet.Tests/HttpContextExtensionsTests.cs b/test/Sentry.AspNet.Tests/HttpContextExtensionsTests.cs index 30dd3cbab4..ef87155717 100644 --- a/test/Sentry.AspNet.Tests/HttpContextExtensionsTests.cs +++ b/test/Sentry.AspNet.Tests/HttpContextExtensionsTests.cs @@ -1,3 +1,5 @@ +using HttpCookie = System.Web.HttpCookie; + namespace Sentry.AspNet.Tests; public class HttpContextExtensionsTests @@ -61,4 +63,71 @@ public void FinishSentryTransaction_FinishesTransaction() transaction.IsFinished.Should().BeTrue(); transaction.Status.Should().Be(SpanStatus.NotFound); } + + [Fact] + public void StartSentryTransaction_SendDefaultPii_set_to_true_sets_cookies() + { + // Arrange + var context = HttpContextBuilder.BuildWithCookies(new []{ new HttpCookie("foo", "bar") }); + + SentryClientExtensions.SentryOptionsForTestingOnly = new SentryOptions + { + SendDefaultPii = true + }; + + // Act + var transaction = context.StartSentryTransaction(); + + // Assert + transaction.Request.Cookies.Should().Be("foo=bar"); + } + + [Fact] + public void StartSentryTransaction_SendDefaultPii_set_to_true_does_not_set_cookies_if_none_found() + { + // Arrange + var context = HttpContextBuilder.BuildWithCookies(new HttpCookie[] { }); + + SentryClientExtensions.SentryOptionsForTestingOnly = new SentryOptions + { + SendDefaultPii = true + }; + + // Act + var transaction = context.StartSentryTransaction(); + + // Assert + transaction.Request.Cookies.Should().BeEmpty(); + } + + [Fact] + public void StartSentryTransaction_SendDefaultPii_set_to_false_does_not_set_cookies() + { + // Arrange + var context = HttpContextBuilder.BuildWithCookies(new []{ new HttpCookie("foo", "bar") }); + + SentryClientExtensions.SentryOptionsForTestingOnly = new SentryOptions + { + SendDefaultPii = false + }; + + // Act + var transaction = context.StartSentryTransaction(); + + // Assert + transaction.Request.Cookies.Should().BeNull(); + } + + [Fact] + public void StartSentryTransaction_missing_options_does_not_set_cookies() + { + // Arrange + var context = HttpContextBuilder.BuildWithCookies(new []{ new HttpCookie("foo", "bar") }); + + // Act + var transaction = context.StartSentryTransaction(); + + // Assert + transaction.Request.Cookies.Should().BeNull(); + } } diff --git a/test/Sentry.AspNetCore.Tests/ScopeExtensionsTests.cs b/test/Sentry.AspNetCore.Tests/ScopeExtensionsTests.cs index dac958fafd..56b2b5dd8d 100644 --- a/test/Sentry.AspNetCore.Tests/ScopeExtensionsTests.cs +++ b/test/Sentry.AspNetCore.Tests/ScopeExtensionsTests.cs @@ -208,6 +208,8 @@ public void Populate_SendDefaultPiiTrueRequestCookies_SetToScope() _sut.Populate(_httpContext, SentryAspNetCoreOptions); Assert.Equal(headers[firstKey], _sut.Request.Headers[firstKey]); + + _sut.Request.Cookies.Should().Be(headers[firstKey]); } [Fact] From e01334e12615a83aecc9dff53334821f38dba9df Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Thu, 8 Jun 2023 09:57:51 +1200 Subject: [PATCH 064/142] Fix/db spans not finishing (#2398) * Refactored Add/Finish spans logic into Helper classes * Implemented logic to correlate Add/Finish spans using Diagnostic ConnectionId and CommandId * Replaced AsyncLocl with Correlation IDs extracted from EF diagnostic events * Refactored to use Extra instead of TraceData, for consistency with SentrySqlListener * Update CHANGELOG.md * Update SqlListenerTests.RecordsEf.Net4_8.verified.txt * Undo csproj whitespace * Apply suggestions from code review Co-authored-by: Matt Johnson-Pint * Refactored SentryEFCoreListener so DiagnosticSourceHelpers are created only once --------- Co-authored-by: Matt Johnson-Pint --- CHANGELOG.md | 1 + .../EFCommandDiagnosticSourceHelper.cs | 51 +++++++ .../EFConnectionDiagnosticSourceHelper.cs | 51 +++++++ .../EFDiagnosticSourceHelper.cs | 96 +++++++++++++ .../EFQueryCompilerDiagnosticSourceHelper.cs | 22 +++ .../DiagnosticSource/SentryEFCoreListener.cs | 90 ++---------- ...istenerTests.RecordsEf.Net4_8.verified.txt | 10 +- .../DiagnosticSourceHelperTests.cs | 58 ++++++++ .../SentryEFCoreListenerTests.cs | 132 ++++++++---------- 9 files changed, 357 insertions(+), 154 deletions(-) create mode 100644 src/Sentry.DiagnosticSource/Internal/DiagnosticSource/EFCommandDiagnosticSourceHelper.cs create mode 100644 src/Sentry.DiagnosticSource/Internal/DiagnosticSource/EFConnectionDiagnosticSourceHelper.cs create mode 100644 src/Sentry.DiagnosticSource/Internal/DiagnosticSource/EFDiagnosticSourceHelper.cs create mode 100644 src/Sentry.DiagnosticSource/Internal/DiagnosticSource/EFQueryCompilerDiagnosticSourceHelper.cs create mode 100644 test/Sentry.DiagnosticSource.Tests/DiagnosticSourceHelperTests.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 42c68c2123..cd97408eec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - SentryHttpMessageHandler added when AddHttpClient is before UseSentry ([#2390](https://github.com/getsentry/sentry-dotnet/pull/2390)) - Set the native sdk name for Android ([#2389](https://github.com/getsentry/sentry-dotnet/pull/2389)) +- Fix db connection spans not finishing ([#2398](https://github.com/getsentry/sentry-dotnet/pull/2398)) - Various .NET MAUI fixes / improvements ([#2403](https://github.com/getsentry/sentry-dotnet/pull/2403)) - The battery level was being reported incorrectly due to percentage multiplier. - The device architecture (x64, arm64, etc.) is now reported diff --git a/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/EFCommandDiagnosticSourceHelper.cs b/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/EFCommandDiagnosticSourceHelper.cs new file mode 100644 index 0000000000..4f30397a87 --- /dev/null +++ b/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/EFCommandDiagnosticSourceHelper.cs @@ -0,0 +1,51 @@ +using Sentry.Extensibility; +using Sentry.Internal.Extensions; + +namespace Sentry.Internal.DiagnosticSource; + +internal class EFCommandDiagnosticSourceHelper : EFDiagnosticSourceHelper +{ + internal EFCommandDiagnosticSourceHelper(IHub hub, SentryOptions options) : base(hub, options) + { + } + + protected override string Operation => "db.query"; + protected override string Description(object? diagnosticSourceValue) => FilterNewLineValue(diagnosticSourceValue) ?? string.Empty; + private static Guid? GetCommandId(object? diagnosticSourceValue) => diagnosticSourceValue?.GetGuidProperty("CommandId"); + + private static void SetCommandId(ISpan span, Guid? commandId) + { + Debug.Assert(commandId != Guid.Empty); + + span.SetExtra(CommandExtraKey, commandId); + } + + private static Guid? TryGetCommandId(ISpan span) => + span.Extra.TryGetValue(CommandExtraKey, out var key) && key is Guid guid + ? guid + : null; + + protected override ISpan? GetSpanReference(ITransaction transaction, object? diagnosticSourceValue) + { + if (GetCommandId(diagnosticSourceValue) is { } commandId) + { + return transaction.Spans + .FirstOrDefault(span => + !span.IsFinished && + span.Operation == Operation && + TryGetCommandId(span) == commandId); + } + Options.LogWarning("No correlation id found for {1}.", Operation); + return null; + } + + protected override void SetSpanReference(ISpan span, object? diagnosticSourceValue) + { + if (GetCommandId(diagnosticSourceValue) is { } commandId) + { + SetCommandId(span, commandId); + return; + } + Options.LogWarning("No correlation id can be set for {1}.", Operation); + } +} diff --git a/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/EFConnectionDiagnosticSourceHelper.cs b/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/EFConnectionDiagnosticSourceHelper.cs new file mode 100644 index 0000000000..07a5cd94dc --- /dev/null +++ b/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/EFConnectionDiagnosticSourceHelper.cs @@ -0,0 +1,51 @@ +using Sentry.Extensibility; +using Sentry.Internal.Extensions; + +namespace Sentry.Internal.DiagnosticSource; + +internal class EFConnectionDiagnosticSourceHelper : EFDiagnosticSourceHelper +{ + internal EFConnectionDiagnosticSourceHelper(IHub hub, SentryOptions options) : base(hub, options) + { + } + + protected override string Operation => "db.connection"; + protected override string Description(object? diagnosticSourceValue) => null!; + private static Guid? GetConnectionId(object? diagnosticSourceValue) => diagnosticSourceValue?.GetGuidProperty("ConnectionId"); + + private static void SetConnectionId(ISpan span, Guid? connectionId) + { + Debug.Assert(connectionId != Guid.Empty); + + span.SetExtra(ConnectionExtraKey, connectionId); + } + + private static Guid? TryGetConnectionId(ISpan span) => + span.Extra.TryGetValue(ConnectionExtraKey, out var key) && key is Guid guid + ? guid + : null; + + protected override ISpan? GetSpanReference(ITransaction transaction, object? diagnosticSourceValue) + { + if (GetConnectionId(diagnosticSourceValue) is { } connectionId) + { + return transaction.Spans + .FirstOrDefault(span => + !span.IsFinished && + span.Operation == Operation && + TryGetConnectionId(span) == connectionId); + } + Options.LogWarning("No correlation id found for {1}.", Operation); + return null; + } + + protected override void SetSpanReference(ISpan span, object? diagnosticSourceValue) + { + if (GetConnectionId(diagnosticSourceValue) is { } connectionId) + { + SetConnectionId(span, connectionId); + return; + } + Options.LogWarning("No {0} found when adding {1} Span.", "ConnectionId", Operation); + } +} diff --git a/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/EFDiagnosticSourceHelper.cs b/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/EFDiagnosticSourceHelper.cs new file mode 100644 index 0000000000..643f4705f1 --- /dev/null +++ b/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/EFDiagnosticSourceHelper.cs @@ -0,0 +1,96 @@ +using Sentry.Extensibility; + +namespace Sentry.Internal.DiagnosticSource; + +internal abstract class EFDiagnosticSourceHelper +{ + internal const string ConnectionExtraKey = "db.connection_id"; + internal const string CommandExtraKey = "db.command_id"; + + protected SentryOptions Options { get; } + //protected object? DiagnosticSourceValue { get; } + private ITransaction? Transaction { get; } + protected abstract string Operation { get; } + protected abstract string Description(object? diagnosticSourceValue); + + internal EFDiagnosticSourceHelper(IHub hub, SentryOptions options) + { + Options = options; + Transaction = hub.GetTransactionIfSampled(); + } + + internal void AddSpan(object? diagnosticSourceValue) + { + Options.LogDebug($"(Sentry add span {Operation})"); + LogTransactionSpans(); + if (Transaction == null) + { + return; + } + + // We "flatten" the EF spans so that they all have the same parent span, for two reasons: + // 1. Each command typically gets it's own connection, which makes the resulting waterfall diagram hard to read. + // 2. Sentry's performance errors functionality only works when all queries have the same parent span. + var parent = Transaction.GetDbParentSpan(); + var child = parent.StartChild(Operation, Description(diagnosticSourceValue)); + + SetSpanReference(child, diagnosticSourceValue); + } + + internal void FinishSpan(object? diagnosticSourceValue, SpanStatus status) + { + if (Transaction == null) + { + return; + } + + Options.LogDebug($"(Sentry finish span {Operation})"); + LogTransactionSpans(); + + var sourceSpan = GetSpanReference(Transaction, diagnosticSourceValue); + if (sourceSpan == null) + { + Options.LogWarning("Tried to close {0} span but no matching span could be found.", Operation); + return; + } + + sourceSpan.Finish(status); + } + + private void LogTransactionSpans() + { + if (Transaction == null) + { + Options.LogDebug($"(Sentry transaction is null)"); + return; + } + + Options.LogDebug("Transaction Spans"); + Options.LogDebug("-----------------"); + foreach (var span in Transaction.Spans) + { + Options.LogDebug($"id: {span.SpanId} operation: {span.Operation}"); + } + } + + /// + /// Get the Query with error message and remove the unneeded values. + /// + /// + /// Compiling query model: + /// EF initialize...\r\nEF Query... + /// becomes: + /// EF Query... + /// + /// the query to be parsed value + /// the filtered query + internal static string? FilterNewLineValue(object? value) + { + var str = value?.ToString(); + return str?[(str.IndexOf('\n') + 1)..]; + } + + protected abstract void SetSpanReference(ISpan span, object? diagnosticSourceValue); + + protected abstract ISpan? GetSpanReference(ITransaction transaction, object? diagnosticSourceValue); +} diff --git a/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/EFQueryCompilerDiagnosticSourceHelper.cs b/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/EFQueryCompilerDiagnosticSourceHelper.cs new file mode 100644 index 0000000000..70045587f3 --- /dev/null +++ b/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/EFQueryCompilerDiagnosticSourceHelper.cs @@ -0,0 +1,22 @@ +namespace Sentry.Internal.DiagnosticSource; + +internal class EFQueryCompilerDiagnosticSourceHelper : EFDiagnosticSourceHelper +{ + internal EFQueryCompilerDiagnosticSourceHelper(IHub hub, SentryOptions options) : base(hub, options) + { + } + + protected override string Operation => "db.query.compile"; + protected override string Description(object? diagnosticSourceValue) => FilterNewLineValue(diagnosticSourceValue) ?? string.Empty; + + /// + /// We don't have a correlation id for compiled query events. We just return the first unfinished query compile span. + /// + protected override ISpan? GetSpanReference(ITransaction transaction, object? diagnosticSourceValue) => + transaction.Spans .FirstOrDefault(span => !span.IsFinished && span.Operation == Operation); + + protected override void SetSpanReference(ISpan span, object? diagnosticSourceValue) + { + // We don't have a correlation id for compiled query events. + } +} diff --git a/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/SentryEFCoreListener.cs b/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/SentryEFCoreListener.cs index 04e50a3f8f..1d9f689762 100644 --- a/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/SentryEFCoreListener.cs +++ b/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/SentryEFCoreListener.cs @@ -7,13 +7,6 @@ namespace Sentry.Internal.DiagnosticSource; /// internal class SentryEFCoreListener : IObserver> { - private enum SentryEFSpanType - { - Connection, - QueryExecution, - QueryCompiler - } - internal const string EFConnectionOpening = "Microsoft.EntityFrameworkCore.Database.Connection.ConnectionOpening"; internal const string EFConnectionClosed = "Microsoft.EntityFrameworkCore.Database.Connection.ConnectionClosed"; internal const string EFCommandExecuting = "Microsoft.EntityFrameworkCore.Database.Command.CommandExecuting"; @@ -35,10 +28,6 @@ private enum SentryEFSpanType private readonly IHub _hub; private readonly SentryOptions _options; - private readonly AsyncLocal> _spansCompilerLocal = new(); - private readonly AsyncLocal> _spansQueryLocal = new(); - private readonly AsyncLocal> _spansConnectionLocal = new(); - private bool _logConnectionEnabled = true; private bool _logQueryEnabled = true; @@ -52,53 +41,15 @@ public SentryEFCoreListener(IHub hub, SentryOptions options) internal void DisableQuerySpan() => _logQueryEnabled = false; - private void AddSpan(SentryEFSpanType type, string operation, string? description) - { - var transaction = _hub.GetTransactionIfSampled(); - if (transaction == null) - { - return; - } - - var parent = type == SentryEFSpanType.QueryExecution - ? transaction.GetLastActiveSpan() ?? transaction.GetDbParentSpan() - : transaction.GetDbParentSpan(); - - var child = parent.StartChild(operation, description); - - var asyncLocalSpan = GetSpanBucket(type); - asyncLocalSpan.Value = new WeakReference(child); - } - - private ISpan? TakeSpan(SentryEFSpanType type) - { - var transaction = _hub.GetTransactionIfSampled(); - if (transaction == null) - { - return null; - } - - if (GetSpanBucket(type).Value is { } reference && reference.TryGetTarget(out var startedSpan)) - { - return startedSpan; - } + public void OnCompleted() { } - _options.LogWarning("Trying to close a span that was already garbage collected. {0}", type); - return null; - } + public void OnError(Exception error) { } - private AsyncLocal> GetSpanBucket(SentryEFSpanType type) - => type switch - { - SentryEFSpanType.QueryCompiler => _spansCompilerLocal, - SentryEFSpanType.QueryExecution => _spansQueryLocal, - SentryEFSpanType.Connection => _spansConnectionLocal, - _ => throw new($"Unknown SentryEFSpanType: {type}") - }; + private EFQueryCompilerDiagnosticSourceHelper QueryCompilerDiagnosticSourceHelper => new(_hub, _options); - public void OnCompleted() { } + private EFConnectionDiagnosticSourceHelper ConnectionDiagnosticSourceHelper => new(_hub, _options); - public void OnError(Exception error) { } + private EFCommandDiagnosticSourceHelper CommandDiagnosticSourceHelper => new(_hub, _options); public void OnNext(KeyValuePair value) { @@ -108,29 +59,29 @@ public void OnNext(KeyValuePair value) { // Query compiler span case EFQueryStartCompiling or EFQueryCompiling: - AddSpan(SentryEFSpanType.QueryCompiler, "db.query.compile", FilterNewLineValue(value.Value)); + QueryCompilerDiagnosticSourceHelper.AddSpan(value.Value); break; case EFQueryCompiled: - TakeSpan(SentryEFSpanType.QueryCompiler)?.Finish(SpanStatus.Ok); + QueryCompilerDiagnosticSourceHelper.FinishSpan(value.Value, SpanStatus.Ok); break; // Connection span (A transaction may or may not show a connection with it.) case EFConnectionOpening when _logConnectionEnabled: - AddSpan(SentryEFSpanType.Connection, "db.connection", null); + ConnectionDiagnosticSourceHelper.AddSpan(value.Value); break; case EFConnectionClosed when _logConnectionEnabled: - TakeSpan(SentryEFSpanType.Connection)?.Finish(SpanStatus.Ok); + ConnectionDiagnosticSourceHelper.FinishSpan(value.Value, SpanStatus.Ok); break; // Query Execution span case EFCommandExecuting when _logQueryEnabled: - AddSpan(SentryEFSpanType.QueryExecution, "db.query", FilterNewLineValue(value.Value)); + CommandDiagnosticSourceHelper.AddSpan(value.Value); break; case EFCommandFailed when _logQueryEnabled: - TakeSpan(SentryEFSpanType.QueryExecution)?.Finish(SpanStatus.InternalError); + CommandDiagnosticSourceHelper.FinishSpan(value.Value, SpanStatus.InternalError); break; case EFCommandExecuted when _logQueryEnabled: - TakeSpan(SentryEFSpanType.QueryExecution)?.Finish(SpanStatus.Ok); + CommandDiagnosticSourceHelper.FinishSpan(value.Value, SpanStatus.Ok); break; } } @@ -139,21 +90,4 @@ public void OnNext(KeyValuePair value) _options.LogError("Failed to intercept EF Core event.", ex); } } - - /// - /// Get the Query with error message and remove the unneeded values. - /// - /// - /// Compiling query model: - /// EF initialize...\r\nEF Query... - /// becomes: - /// EF Query... - /// - /// the query to be parsed value - /// the filtered query - internal static string? FilterNewLineValue(object? value) - { - var str = value?.ToString(); - return str?[(str.IndexOf('\n') + 1)..]; - } } diff --git a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEf.Net4_8.verified.txt b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEf.Net4_8.verified.txt index be78366e86..5c2e93bf15 100644 --- a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEf.Net4_8.verified.txt +++ b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEf.Net4_8.verified.txt @@ -55,7 +55,10 @@ SELECT [Id] FROM [TestEntities] WHERE @@ROWCOUNT = 1 AND [Id] = scope_identity();, Status: Ok, - IsSampled: true + IsSampled: true, + Extra: { + db.command_id: Guid_1 + } }, { IsFinished: true, @@ -64,7 +67,10 @@ WHERE @@ROWCOUNT = 1 AND [Id] = scope_identity();, SELECT [t].[Id], [t].[Property] FROM [TestEntities] AS [t], Status: Ok, - IsSampled: true + IsSampled: true, + Extra: { + db.command_id: Guid_2 + } } ], IsFinished: true diff --git a/test/Sentry.DiagnosticSource.Tests/DiagnosticSourceHelperTests.cs b/test/Sentry.DiagnosticSource.Tests/DiagnosticSourceHelperTests.cs new file mode 100644 index 0000000000..d82a011d72 --- /dev/null +++ b/test/Sentry.DiagnosticSource.Tests/DiagnosticSourceHelperTests.cs @@ -0,0 +1,58 @@ +using Sentry.Internal.DiagnosticSource; + +namespace Sentry.DiagnosticSource.Tests; + +public class DiagnosticSourceHelperTests +{ + [Fact] + public void FilterNewLineValue_StringWithNewLine_SubStringAfterNewLine() + { + // Arrange + var text = "1234\r\nSELECT *...\n FROM ..."; + var expectedText = "SELECT *...\n FROM ..."; + + // Act + var value = EFDiagnosticSourceHelper.FilterNewLineValue(text); + + // Assert + Assert.Equal(expectedText, value); + } + + [Fact] + public void FilterNewLineValue_NullObject_NullString() + { + // Act + var value = EFDiagnosticSourceHelper.FilterNewLineValue(null); + + // Assert + Assert.Null(value); + } + + [Fact] + public void FilterNewLineValue_OneLineString_OneLineString() + { + // Arrange + var text = "1234"; + var expectedText = "1234"; + + // Act + var value = EFDiagnosticSourceHelper.FilterNewLineValue(text); + + // Assert + Assert.Equal(expectedText, value); + } + + [Fact] + public void FilterNewLineValue_EmptyString_EmptyString() + { + // Arrange + var text = ""; + var expectedText = ""; + + // Act + var value = EFDiagnosticSourceHelper.FilterNewLineValue(text); + + // Assert + Assert.Equal(expectedText, value); + } +} diff --git a/test/Sentry.DiagnosticSource.Tests/SentryEFCoreListenerTests.cs b/test/Sentry.DiagnosticSource.Tests/SentryEFCoreListenerTests.cs index 25631bff17..e8b43a9e22 100644 --- a/test/Sentry.DiagnosticSource.Tests/SentryEFCoreListenerTests.cs +++ b/test/Sentry.DiagnosticSource.Tests/SentryEFCoreListenerTests.cs @@ -1,3 +1,4 @@ +using Microsoft.EntityFrameworkCore.Diagnostics; using Sentry.Internal.DiagnosticSource; using static Sentry.Internal.DiagnosticSource.SentryEFCoreListener; @@ -188,6 +189,26 @@ public void OnNext_ConfigureScopeInvokedOnce(string key) hub.Received(1).ConfigureScope(Arg.Any>()); } + private class FakeDiagnosticEventData + { + public FakeDiagnosticEventData(string value) { _value = value; } + private readonly string _value; + public override string ToString()=> _value; + } + + private class FakeDiagnosticConnectionEventData : FakeDiagnosticEventData + { + public FakeDiagnosticConnectionEventData(string value) : base(value) { } + public Guid ConnectionId { get; set; } = Guid.NewGuid(); + } + + private class FakeDiagnosticCommandEventData : FakeDiagnosticEventData + { + public FakeDiagnosticCommandEventData(string value) : base(value) { } + public Guid ConnectionId { get; set; } = Guid.NewGuid(); + public Guid CommandId { get; set; } = Guid.NewGuid(); + } + [Fact] public void OnNext_HappyPath_IsValid() { @@ -196,14 +217,19 @@ public void OnNext_HappyPath_IsValid() var interceptor = new SentryEFCoreListener(hub, _fixture.Options); var expectedSql = "SELECT * FROM ..."; var efSql = "ef Junk\r\nSELECT * FROM ..."; + var efConn = "db username : password"; + + var queryEventData = new FakeDiagnosticEventData(efSql); + var connectionEventData = new FakeDiagnosticConnectionEventData(efConn); + var commandEventData = new FakeDiagnosticCommandEventData(efSql); // Act - interceptor.OnNext(new(EFQueryCompiling, efSql)); - interceptor.OnNext(new(EFQueryCompiled, efSql)); - interceptor.OnNext(new(EFConnectionOpening, null)); - interceptor.OnNext(new(EFCommandExecuting, efSql)); - interceptor.OnNext(new(EFCommandExecuted, efSql)); - interceptor.OnNext(new(EFConnectionClosed, efSql)); + interceptor.OnNext(new(EFQueryCompiling, queryEventData)); + interceptor.OnNext(new(EFQueryCompiled, queryEventData)); + interceptor.OnNext(new(EFConnectionOpening, connectionEventData)); + interceptor.OnNext(new(EFCommandExecuting, commandEventData)); + interceptor.OnNext(new(EFCommandExecuted, commandEventData)); + interceptor.OnNext(new(EFConnectionClosed, connectionEventData)); // Assert var compilerSpan = _fixture.Spans.First(s => GetValidator(EFQueryCompiling)(s)); @@ -226,7 +252,7 @@ public void OnNext_HappyPath_IsValid() // Check connections between spans. Assert.Equal(_fixture.Tracer.SpanId, compilerSpan.ParentSpanId); Assert.Equal(_fixture.Tracer.SpanId, connectionSpan.ParentSpanId); - Assert.Equal(connectionSpan.SpanId, commandSpan.ParentSpanId); + Assert.Equal(_fixture.Tracer.SpanId, commandSpan.ParentSpanId); _fixture.Options.DiagnosticLogger.DidNotReceive()? .Log(Arg.Is(SentryLevel.Warning), Arg.Is("Trying to close a span that was already garbage collected. {0}"), null, Arg.Any()); @@ -240,15 +266,20 @@ public void OnNext_HappyPathInsideChildSpan_IsValid() var interceptor = new SentryEFCoreListener(hub, _fixture.Options); var expectedSql = "SELECT * FROM ..."; var efSql = "ef Junk\r\nSELECT * FROM ..."; + var efConn = "db username : password"; + + var queryEventData = new FakeDiagnosticEventData(efSql); + var connectionEventData = new FakeDiagnosticConnectionEventData(efConn); + var commandEventData = new FakeDiagnosticCommandEventData(efSql); // Act var childSpan = _fixture.Tracer.StartChild("Child Span"); - interceptor.OnNext(new(EFQueryCompiling, efSql)); - interceptor.OnNext(new(EFQueryCompiled, efSql)); - interceptor.OnNext(new(EFConnectionOpening, null)); - interceptor.OnNext(new(EFCommandExecuting, efSql)); - interceptor.OnNext(new(EFCommandExecuted, efSql)); - interceptor.OnNext(new(EFConnectionClosed, efSql)); + interceptor.OnNext(new(EFQueryCompiling, queryEventData)); + interceptor.OnNext(new(EFQueryCompiled, queryEventData)); + interceptor.OnNext(new(EFConnectionOpening, connectionEventData)); + interceptor.OnNext(new(EFCommandExecuting, commandEventData)); + interceptor.OnNext(new(EFCommandExecuted, commandEventData)); + interceptor.OnNext(new(EFConnectionClosed, connectionEventData)); childSpan.Finish(); // Assert @@ -272,7 +303,7 @@ public void OnNext_HappyPathInsideChildSpan_IsValid() // Check connections between spans. Assert.Equal(childSpan.SpanId, compilerSpan.ParentSpanId); Assert.Equal(childSpan.SpanId, connectionSpan.ParentSpanId); - Assert.Equal(connectionSpan.SpanId, commandSpan.ParentSpanId); + Assert.Equal(childSpan.SpanId, commandSpan.ParentSpanId); _fixture.Options.DiagnosticLogger.DidNotReceive()? .Log(Arg.Is(SentryLevel.Warning), Arg.Is("Trying to close a span that was already garbage collected. {0}"), null, Arg.Any()); @@ -286,14 +317,19 @@ public void OnNext_HappyPathWithError_TransactionWithErroredCommand() var interceptor = new SentryEFCoreListener(hub, _fixture.Options); var expectedSql = "SELECT * FROM ..."; var efSql = "ef Junk\r\nSELECT * FROM ..."; + var efConn = "db username : password"; + + var queryEventData = new FakeDiagnosticEventData(efSql); + var connectionEventData = new FakeDiagnosticConnectionEventData(efConn); + var commandEventData = new FakeDiagnosticCommandEventData(efSql); // Act - interceptor.OnNext(new(EFQueryCompiling, efSql)); - interceptor.OnNext(new(EFQueryCompiled, efSql)); - interceptor.OnNext(new(EFConnectionOpening, null)); - interceptor.OnNext(new(EFCommandExecuting, efSql)); - interceptor.OnNext(new(EFCommandFailed, efSql)); - interceptor.OnNext(new(EFConnectionClosed, efSql)); + interceptor.OnNext(new(EFQueryCompiling, queryEventData)); + interceptor.OnNext(new(EFQueryCompiled, queryEventData)); + interceptor.OnNext(new(EFConnectionOpening, connectionEventData)); + interceptor.OnNext(new(EFCommandExecuting, commandEventData)); + interceptor.OnNext(new(EFCommandFailed, commandEventData)); + interceptor.OnNext(new(EFConnectionClosed, connectionEventData)); // Assert var compilerSpan = _fixture.Spans.First(s => GetValidator(EFQueryCompiling)(s)); @@ -313,7 +349,7 @@ public void OnNext_HappyPathWithError_TransactionWithErroredCommand() // Check connections between spans. Assert.Equal(_fixture.Tracer.SpanId, compilerSpan.ParentSpanId); Assert.Equal(_fixture.Tracer.SpanId, connectionSpan.ParentSpanId); - Assert.Equal(connectionSpan.SpanId, commandSpan.ParentSpanId); + Assert.Equal(_fixture.Tracer.SpanId, commandSpan.ParentSpanId); Assert.Equal(expectedSql, commandSpan.Description); } @@ -379,59 +415,7 @@ public void OnNext_TakeSpanWithoutSpan_ShowsGarbageCollectorError(string operati // Assert _fixture.Options.DiagnosticLogger.Received(1)? - .Log(Arg.Is(SentryLevel.Warning), Arg.Is("Trying to close a span that was already garbage collected. {0}"), + .Log(Arg.Is(SentryLevel.Warning), Arg.Is("Tried to close {0} span but no matching span could be found."), null, Arg.Any()); } - - [Fact] - public void FilterNewLineValue_StringWithNewLine_SubStringAfterNewLine() - { - // Arrange - var text = "1234\r\nSELECT *...\n FROM ..."; - var expectedText = "SELECT *...\n FROM ..."; - - // Act - var value = FilterNewLineValue(text); - - // Assert - Assert.Equal(expectedText, value); - } - - [Fact] - public void FilterNewLineValue_NullObject_NullString() - { - // Act - var value = FilterNewLineValue(null); - - // Assert - Assert.Null(value); - } - - [Fact] - public void FilterNewLineValue_OneLineString_OneLineString() - { - // Arrange - var text = "1234"; - var expectedText = "1234"; - - // Act - var value = FilterNewLineValue(text); - - // Assert - Assert.Equal(expectedText, value); - } - - [Fact] - public void FilterNewLineValue_EmptyString_EmptyString() - { - // Arrange - var text = ""; - var expectedText = ""; - - // Act - var value = FilterNewLineValue(text); - - // Assert - Assert.Equal(expectedText, value); - } } From af9cc5977397e9f2285e94da6fd6dd4f4451cb0e Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Wed, 7 Jun 2023 17:29:51 -0700 Subject: [PATCH 065/142] Fix UWP GateKeeper errors (#2415) --- CHANGELOG.md | 1 + .../Internal/FastSerialization/GrowableArray.cs | 13 +------------ src/Sentry/Internal/SparseArray.cs | 8 +------- test/Sentry.Tests/Protocol/ProfilerTests.verify.cs | 2 +- 4 files changed, 4 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cd97408eec..10e0e23249 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ - Fix system frames being marked as "in-app" ([#2408](https://github.com/getsentry/sentry-dotnet/pull/2408)) - NOTE: This important fix corrects a value that is used during issue grouping, so you may receive new alerts for existing issues after deploying this update. - Populate scope's Cookies property ([#2411](https://github.com/getsentry/sentry-dotnet/pull/2411)) +- Fix UWP GateKeeper errors ([#2415](https://github.com/getsentry/sentry-dotnet/pull/2415)) ### Dependencies diff --git a/src/Sentry/Internal/FastSerialization/GrowableArray.cs b/src/Sentry/Internal/FastSerialization/GrowableArray.cs index 1c99541b67..5c606afeef 100644 --- a/src/Sentry/Internal/FastSerialization/GrowableArray.cs +++ b/src/Sentry/Internal/FastSerialization/GrowableArray.cs @@ -10,11 +10,6 @@ internal struct HashableGrowableArray : IReadOnlyList, IEquatable(); - } - public HashableGrowableArray(int capacity) { _items = new GrowableArray(capacity); @@ -95,13 +90,7 @@ public bool Equals(HashableGrowableArray other) internal struct GrowableArray : IReadOnlyList { /// - /// Create an empty growable array. - /// - public GrowableArray() : this(0) { } - - /// - /// Create a growable array with the given initial size it will grow as needed. There is also the - /// default constructor that assumes initialSize of 0 (and does not actually allocate the array. + /// Create a growable array with the given initial size it will grow as needed. /// /// public GrowableArray(int initialSize) diff --git a/src/Sentry/Internal/SparseArray.cs b/src/Sentry/Internal/SparseArray.cs index e4becdfae5..4441ce85f7 100644 --- a/src/Sentry/Internal/SparseArray.cs +++ b/src/Sentry/Internal/SparseArray.cs @@ -9,13 +9,7 @@ internal sealed class SparseScalarArray where T : IEquatable private GrowableArray _items; private T _uninitializedValue; - public SparseScalarArray(T uninitializedValue) - { - _items = new GrowableArray(); - _uninitializedValue = uninitializedValue; - } - - public SparseScalarArray(T uninitializedValue, int capacity) + public SparseScalarArray(T uninitializedValue, int capacity = 0) { _items = new GrowableArray(capacity); _uninitializedValue = uninitializedValue; diff --git a/test/Sentry.Tests/Protocol/ProfilerTests.verify.cs b/test/Sentry.Tests/Protocol/ProfilerTests.verify.cs index fbf28e37ea..e1c31cd147 100644 --- a/test/Sentry.Tests/Protocol/ProfilerTests.verify.cs +++ b/test/Sentry.Tests/Protocol/ProfilerTests.verify.cs @@ -12,7 +12,7 @@ public ProfilerTests(ITestOutputHelper output) private static void AddStack(SampleProfile sut, List frames) { - var stack = new HashableGrowableArray(); + var stack = new HashableGrowableArray(0); foreach (var frame in frames) { stack.Add(frame); From 936a34ebb517489a8e81c603fa3bbddc2bdb4dfc Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 7 Jun 2023 20:47:28 -0700 Subject: [PATCH 066/142] chore: update scripts/update-java.ps1 to 6.22.0 (#2417) Co-authored-by: GitHub --- CHANGELOG.md | 6 +++--- src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 10e0e23249..c848264bb7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,9 +23,9 @@ - Bump Cocoa SDK from v8.7.2 to v8.7.3 ([#2394](https://github.com/getsentry/sentry-dotnet/pull/2394)) - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#873) - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.7.2...8.7.3) -- Bump Java SDK from v6.19.1 to v6.21.0 ([#2395](https://github.com/getsentry/sentry-dotnet/pull/2395), [#2405](https://github.com/getsentry/sentry-dotnet/pull/2405)) - - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#6210) - - [diff](https://github.com/getsentry/sentry-java/compare/6.19.1...6.21.0) +- Bump Java SDK from v6.19.1 to v6.22.0 ([#2395](https://github.com/getsentry/sentry-dotnet/pull/2395), [#2405](https://github.com/getsentry/sentry-dotnet/pull/2405), [#2417](https://github.com/getsentry/sentry-dotnet/pull/2417)) + - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#6220) + - [diff](https://github.com/getsentry/sentry-java/compare/6.19.1...6.22.0) ## 3.33.0 diff --git a/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj b/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj index 28f219fe4e..a85b44b7d1 100644 --- a/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj +++ b/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj @@ -3,7 +3,7 @@ net6.0-android $(NoWarn);BG8605;BG8606 - 6.21.0 + 6.22.0 $(BaseIntermediateOutputPath)sdks\Sentry\Android\$(SentryAndroidSdkVersion)\ .NET Bindings for the Sentry Android SDK From f583cde154b0638f4a979e22f8c48bb1654e3900 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Fri, 9 Jun 2023 12:02:56 +1200 Subject: [PATCH 067/142] Fix/DB Connection spans presented poorly (#2409) * Refactored Add/Finish spans logic into Helper classes * Implemented logic to correlate Add/Finish spans using Diagnostic ConnectionId and CommandId * Replaced AsyncLocl with Correlation IDs extracted from EF diagnostic events * Refactored to use Extra instead of TraceData, for consistency with SentrySqlListener * Update CHANGELOG.md * Update SqlListenerTests.RecordsEf.Net4_8.verified.txt * Undo csproj whitespace * Apply suggestions from code review Co-authored-by: Matt Johnson-Pint * Refactored SentryEFCoreListener so DiagnosticSourceHelpers are created only once * Implemented fix for DB Connection spans presented poorly #2144 * Update CHANGELOG.md * Fixed timing in OnNext_Same_Connections_Consolidated so we can reliably compare TimeStamps --------- Co-authored-by: Matt Johnson-Pint --- CHANGELOG.md | 1 + .../DiagnosticSourceConstants.cs | 29 ++++ .../EFCommandDiagnosticSourceHelper.cs | 20 ++- .../EFConnectionDiagnosticSourceHelper.cs | 56 +++++-- .../EFDiagnosticSourceHelper.cs | 28 +++- .../EFQueryCompilerDiagnosticSourceHelper.cs | 3 +- .../DiagnosticSource/SentryEFCoreListener.cs | 3 +- .../DiagnosticSource/SentrySqlListener.cs | 96 +++++------- .../Internal/Extensions/MiscExtensions.cs | 20 ++- .../ReadOnlyDictionaryExtensions.cs | 10 ++ src/Sentry/SpanTracer.cs | 10 ++ ...Tests.LoggingAsync.DotNet6_0.verified.txt} | 2 + ...Tests.LoggingAsync.DotNet7_0.verified.txt} | 2 + ...Tests.RecordsEfAsync.Core3_1.verified.txt} | 4 +- ...sts.RecordsEfAsync.DotNet6_0.verified.txt} | 4 +- ...sts.RecordsEfAsync.DotNet7_0.verified.txt} | 4 +- ...rTests.RecordsEfAsync.Net4_8.verified.txt} | 10 +- ...istenerTests.RecordsSqlAsync.verified.txt} | 4 +- .../SqlListenerTests.verify.cs | 6 +- .../SentryEFCoreListenerTests.cs | 141 ++++++++++++++++-- .../SentrySqlListenerTests.cs | 61 +++++--- .../Extensions/MiscExtensionsTests.cs | 74 +++++++++ .../ReadOnlyDictionaryExtensionsTests.cs | 49 ++++++ 23 files changed, 501 insertions(+), 136 deletions(-) create mode 100644 src/Sentry.DiagnosticSource/Internal/DiagnosticSource/DiagnosticSourceConstants.cs create mode 100644 src/Sentry/Internal/Extensions/ReadOnlyDictionaryExtensions.cs rename test/Sentry.DiagnosticSource.IntegrationTests/{SqlListenerTests.Logging.DotNet6_0.verified.txt => SqlListenerTests.LoggingAsync.DotNet6_0.verified.txt} (95%) rename test/Sentry.DiagnosticSource.IntegrationTests/{SqlListenerTests.Logging.DotNet7_0.verified.txt => SqlListenerTests.LoggingAsync.DotNet7_0.verified.txt} (95%) rename test/Sentry.DiagnosticSource.IntegrationTests/{SqlListenerTests.RecordsEf.DotNet7_0.verified.txt => SqlListenerTests.RecordsEfAsync.Core3_1.verified.txt} (94%) rename test/Sentry.DiagnosticSource.IntegrationTests/{SqlListenerTests.RecordsEf.Core3_1.verified.txt => SqlListenerTests.RecordsEfAsync.DotNet6_0.verified.txt} (94%) rename test/Sentry.DiagnosticSource.IntegrationTests/{SqlListenerTests.RecordsEf.DotNet6_0.verified.txt => SqlListenerTests.RecordsEfAsync.DotNet7_0.verified.txt} (94%) rename test/Sentry.DiagnosticSource.IntegrationTests/{SqlListenerTests.RecordsEf.Net4_8.verified.txt => SqlListenerTests.RecordsEfAsync.Net4_8.verified.txt} (84%) rename test/Sentry.DiagnosticSource.IntegrationTests/{SqlListenerTests.RecordsSql.verified.txt => SqlListenerTests.RecordsSqlAsync.verified.txt} (93%) create mode 100644 test/Sentry.Tests/Internals/Extensions/MiscExtensionsTests.cs create mode 100644 test/Sentry.Tests/Internals/Extensions/ReadOnlyDictionaryExtensionsTests.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index c848264bb7..95cef3fd90 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ - Reduce debug files uploaded ([#2404](https://github.com/getsentry/sentry-dotnet/pull/2404)) - Fix system frames being marked as "in-app" ([#2408](https://github.com/getsentry/sentry-dotnet/pull/2408)) - NOTE: This important fix corrects a value that is used during issue grouping, so you may receive new alerts for existing issues after deploying this update. +- DB Connection spans presented poorly ([#2409](https://github.com/getsentry/sentry-dotnet/pull/2409)) - Populate scope's Cookies property ([#2411](https://github.com/getsentry/sentry-dotnet/pull/2411)) - Fix UWP GateKeeper errors ([#2415](https://github.com/getsentry/sentry-dotnet/pull/2415)) diff --git a/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/DiagnosticSourceConstants.cs b/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/DiagnosticSourceConstants.cs new file mode 100644 index 0000000000..20c8e70ff0 --- /dev/null +++ b/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/DiagnosticSourceConstants.cs @@ -0,0 +1,29 @@ +namespace Sentry.Internal.DiagnosticSource; + +/// +/// Open Telemetry Keys +/// https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/database.md +/// +internal static class OTelKeys +{ + internal const string DbName = "db.name"; +} + +/// +/// Keys specific to the SqlClient listener +/// +internal static class SqlKeys +{ + internal const string DbConnectionId = "db.connection_id"; + internal const string DbOperationId = "db.operation_id"; +} + +/// +/// Keys specific to the Entity Framework listener +/// +internal static class EFKeys +{ + // Entity Framework specific + internal const string DbConnectionId = "db.connection_id"; + internal const string DbCommandId = "db.command_id"; +} diff --git a/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/EFCommandDiagnosticSourceHelper.cs b/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/EFCommandDiagnosticSourceHelper.cs index 4f30397a87..a710b12767 100644 --- a/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/EFCommandDiagnosticSourceHelper.cs +++ b/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/EFCommandDiagnosticSourceHelper.cs @@ -10,20 +10,19 @@ internal EFCommandDiagnosticSourceHelper(IHub hub, SentryOptions options) : base } protected override string Operation => "db.query"; - protected override string Description(object? diagnosticSourceValue) => FilterNewLineValue(diagnosticSourceValue) ?? string.Empty; + + protected override string GetDescription(object? diagnosticSourceValue) => FilterNewLineValue(diagnosticSourceValue) ?? string.Empty; + private static Guid? GetCommandId(object? diagnosticSourceValue) => diagnosticSourceValue?.GetGuidProperty("CommandId"); private static void SetCommandId(ISpan span, Guid? commandId) { Debug.Assert(commandId != Guid.Empty); - span.SetExtra(CommandExtraKey, commandId); + span.SetExtra(EFKeys.DbCommandId, commandId); } - private static Guid? TryGetCommandId(ISpan span) => - span.Extra.TryGetValue(CommandExtraKey, out var key) && key is Guid guid - ? guid - : null; + private static Guid? TryGetCommandId(ISpan span) => span.Extra.TryGetValue(EFKeys.DbCommandId); protected override ISpan? GetSpanReference(ITransaction transaction, object? diagnosticSourceValue) { @@ -41,9 +40,18 @@ private static void SetCommandId(ISpan span, Guid? commandId) protected override void SetSpanReference(ISpan span, object? diagnosticSourceValue) { + if (GetDatabaseName(diagnosticSourceValue) is { } databaseName) + { + span.SetExtra(OTelKeys.DbName, databaseName); + } + if (GetCommandId(diagnosticSourceValue) is { } commandId) { SetCommandId(span, commandId); + if (GetConnectionId(diagnosticSourceValue) is { } connectionId) + { + SetConnectionId(span, connectionId); + } return; } Options.LogWarning("No correlation id can be set for {1}.", Operation); diff --git a/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/EFConnectionDiagnosticSourceHelper.cs b/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/EFConnectionDiagnosticSourceHelper.cs index 07a5cd94dc..a62376a1cf 100644 --- a/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/EFConnectionDiagnosticSourceHelper.cs +++ b/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/EFConnectionDiagnosticSourceHelper.cs @@ -10,20 +10,8 @@ internal EFConnectionDiagnosticSourceHelper(IHub hub, SentryOptions options) : b } protected override string Operation => "db.connection"; - protected override string Description(object? diagnosticSourceValue) => null!; - private static Guid? GetConnectionId(object? diagnosticSourceValue) => diagnosticSourceValue?.GetGuidProperty("ConnectionId"); - private static void SetConnectionId(ISpan span, Guid? connectionId) - { - Debug.Assert(connectionId != Guid.Empty); - - span.SetExtra(ConnectionExtraKey, connectionId); - } - - private static Guid? TryGetConnectionId(ISpan span) => - span.Extra.TryGetValue(ConnectionExtraKey, out var key) && key is Guid guid - ? guid - : null; + protected override string? GetDescription(object? diagnosticSourceValue) => GetDatabaseName(diagnosticSourceValue); protected override ISpan? GetSpanReference(ITransaction transaction, object? diagnosticSourceValue) { @@ -41,11 +29,49 @@ private static void SetConnectionId(ISpan span, Guid? connectionId) protected override void SetSpanReference(ISpan span, object? diagnosticSourceValue) { + if (GetDatabaseName(diagnosticSourceValue) is { } databaseName) + { + span.SetExtra(OTelKeys.DbName, databaseName); + } + if (GetConnectionId(diagnosticSourceValue) is { } connectionId) { SetConnectionId(span, connectionId); - return; } - Options.LogWarning("No {0} found when adding {1} Span.", "ConnectionId", Operation); + else + { + Options.LogWarning("No {0} found when adding {1} Span.", "ConnectionId", Operation); + } + } + + /// + /// EF Connections are often pooled. If we see the same connection multiple times, we reuse the span so that it + /// shows as a single connection in the resulting waterfall chart on Sentry. + /// + /// + internal void AddOrReuseSpan(object? diagnosticSourceValue) + { + if (GetConnectionId(diagnosticSourceValue) is { } connectionId) + { + Options.LogDebug($"Checking for span to reuse for {Operation} with connection id {connectionId}"); + LogTransactionSpans(); + if (Transaction is { } transaction) + { + var spanWithConnectionId = transaction.Spans + .FirstOrDefault(span => + span.Operation == Operation && + TryGetConnectionId(span) == connectionId); + if (spanWithConnectionId is SpanTracer existingSpan) + { + // OK we've seen this connection before... let's reuse it + Options.LogDebug($"Reusing span for {Operation} with connection id {connectionId}"); + existingSpan.Unfinish(); + return; + } + } + } + + // If we can't find a span to reuse then we'll add a new one instead + AddSpan(diagnosticSourceValue); } } diff --git a/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/EFDiagnosticSourceHelper.cs b/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/EFDiagnosticSourceHelper.cs index 643f4705f1..f87e84ccd8 100644 --- a/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/EFDiagnosticSourceHelper.cs +++ b/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/EFDiagnosticSourceHelper.cs @@ -1,17 +1,18 @@ using Sentry.Extensibility; +using Sentry.Internal.Extensions; namespace Sentry.Internal.DiagnosticSource; internal abstract class EFDiagnosticSourceHelper { - internal const string ConnectionExtraKey = "db.connection_id"; - internal const string CommandExtraKey = "db.command_id"; - protected SentryOptions Options { get; } - //protected object? DiagnosticSourceValue { get; } - private ITransaction? Transaction { get; } + protected ITransaction? Transaction { get; } protected abstract string Operation { get; } - protected abstract string Description(object? diagnosticSourceValue); + + protected abstract string? GetDescription(object? diagnosticSourceValue); + + protected static string? GetDatabaseName(object? diagnosticSourceValue) => + diagnosticSourceValue?.GetStringProperty("Connection.Database"); internal EFDiagnosticSourceHelper(IHub hub, SentryOptions options) { @@ -19,6 +20,17 @@ internal EFDiagnosticSourceHelper(IHub hub, SentryOptions options) Transaction = hub.GetTransactionIfSampled(); } + protected static Guid? TryGetConnectionId(ISpan span) => span.Extra.TryGetValue(EFKeys.DbConnectionId); + + protected static Guid? GetConnectionId(object? diagnosticSourceValue) => diagnosticSourceValue?.GetGuidProperty("ConnectionId"); + + protected static void SetConnectionId(ISpan span, Guid? connectionId) + { + Debug.Assert(connectionId != Guid.Empty); + + span.SetExtra(EFKeys.DbConnectionId, connectionId); + } + internal void AddSpan(object? diagnosticSourceValue) { Options.LogDebug($"(Sentry add span {Operation})"); @@ -32,7 +44,7 @@ internal void AddSpan(object? diagnosticSourceValue) // 1. Each command typically gets it's own connection, which makes the resulting waterfall diagram hard to read. // 2. Sentry's performance errors functionality only works when all queries have the same parent span. var parent = Transaction.GetDbParentSpan(); - var child = parent.StartChild(Operation, Description(diagnosticSourceValue)); + var child = parent.StartChild(Operation, GetDescription(diagnosticSourceValue)); SetSpanReference(child, diagnosticSourceValue); } @@ -57,7 +69,7 @@ internal void FinishSpan(object? diagnosticSourceValue, SpanStatus status) sourceSpan.Finish(status); } - private void LogTransactionSpans() + protected void LogTransactionSpans() { if (Transaction == null) { diff --git a/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/EFQueryCompilerDiagnosticSourceHelper.cs b/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/EFQueryCompilerDiagnosticSourceHelper.cs index 70045587f3..c056ab0da0 100644 --- a/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/EFQueryCompilerDiagnosticSourceHelper.cs +++ b/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/EFQueryCompilerDiagnosticSourceHelper.cs @@ -7,7 +7,8 @@ internal EFQueryCompilerDiagnosticSourceHelper(IHub hub, SentryOptions options) } protected override string Operation => "db.query.compile"; - protected override string Description(object? diagnosticSourceValue) => FilterNewLineValue(diagnosticSourceValue) ?? string.Empty; + + protected override string GetDescription(object? diagnosticSourceValue) => FilterNewLineValue(diagnosticSourceValue) ?? string.Empty; /// /// We don't have a correlation id for compiled query events. We just return the first unfinished query compile span. diff --git a/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/SentryEFCoreListener.cs b/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/SentryEFCoreListener.cs index 1d9f689762..626c5a6f7c 100644 --- a/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/SentryEFCoreListener.cs +++ b/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/SentryEFCoreListener.cs @@ -18,6 +18,7 @@ internal class SentryEFCoreListener : IObserver> /// /// internal const string EFQueryStartCompiling = "Microsoft.EntityFrameworkCore.Query.QueryCompilationStarting"; + /// /// Used for EF Core 2.X and 3.X. /// @@ -67,7 +68,7 @@ public void OnNext(KeyValuePair value) // Connection span (A transaction may or may not show a connection with it.) case EFConnectionOpening when _logConnectionEnabled: - ConnectionDiagnosticSourceHelper.AddSpan(value.Value); + ConnectionDiagnosticSourceHelper.AddOrReuseSpan(value.Value); break; case EFConnectionClosed when _logConnectionEnabled: ConnectionDiagnosticSourceHelper.FinishSpan(value.Value, SpanStatus.Ok); diff --git a/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/SentrySqlListener.cs b/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/SentrySqlListener.cs index bf63b85224..a83ba965a9 100644 --- a/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/SentrySqlListener.cs +++ b/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/SentrySqlListener.cs @@ -11,9 +11,6 @@ private enum SentrySqlSpanType Execution }; - internal const string ConnectionExtraKey = "db.connection_id"; - internal const string OperationExtraKey = "db.operation_id"; - internal const string SqlDataWriteConnectionOpenBeforeCommand = "System.Data.SqlClient.WriteConnectionOpenBefore"; internal const string SqlMicrosoftWriteConnectionOpenBeforeCommand = "Microsoft.Data.SqlClient.WriteConnectionOpenBefore"; @@ -41,31 +38,32 @@ public SentrySqlListener(IHub hub, SentryOptions options) _options = options; } + private static void SetDatabaseName(ISpan span, string databaseName) + { + Debug.Assert(databaseName != string.Empty); + + span.SetExtra(OTelKeys.DbName, databaseName); + } + private static void SetConnectionId(ISpan span, Guid? connectionId) { Debug.Assert(connectionId != Guid.Empty); - span.SetExtra(ConnectionExtraKey, connectionId); + span.SetExtra(SqlKeys.DbConnectionId, connectionId); } private static void SetOperationId(ISpan span, Guid? operationId) { Debug.Assert(operationId != Guid.Empty); - span.SetExtra(OperationExtraKey, operationId); + span.SetExtra(SqlKeys.DbOperationId, operationId); } - private static Guid? TryGetOperationId(ISpan span) => - span.Extra.TryGetValue(OperationExtraKey, out var key) && key is Guid guid - ? guid - : null; + private static Guid? TryGetOperationId(ISpan span) => span.Extra.TryGetValue(SqlKeys.DbOperationId); - private static Guid? TryGetConnectionId(ISpan span) => - span.Extra.TryGetValue(ConnectionExtraKey, out var key) && key is Guid guid - ? guid - : null; + private static Guid? TryGetConnectionId(ISpan span) => span.Extra.TryGetValue(SqlKeys.DbConnectionId); - private void AddSpan(SentrySqlSpanType type, string operation, object? value) + private void AddSpan(string operation, object? value) { var transaction = _hub.GetTransactionIfSampled(); if (transaction == null) @@ -74,25 +72,11 @@ private void AddSpan(SentrySqlSpanType type, string operation, object? value) } var operationId = value?.GetGuidProperty("OperationId"); - - switch (type) - { - case SentrySqlSpanType.Connection when transaction.GetDbParentSpan().StartChild(operation) is - { } connectionSpan: - SetOperationId(connectionSpan, operationId); - break; - - case SentrySqlSpanType.Execution when value?.GetGuidProperty("ConnectionId") is { } connectionId: - var parent = TryGetConnectionSpan(transaction, connectionId) ?? transaction.GetDbParentSpan(); - var span = TryStartChild(parent, operation, null); - if (span != null) - { - SetOperationId(span, operationId); - SetConnectionId(span, connectionId); - } - - break; - } + var connectionId = value?.GetGuidProperty("ConnectionId"); + var parent = transaction.GetDbParentSpan(); + var span = parent.StartChild(operation); + SetOperationId(span, operationId); + SetConnectionId(span, connectionId); } private ISpan? GetSpan(SentrySqlSpanType type, KeyValuePair kvp) @@ -107,24 +91,15 @@ private void AddSpan(SentrySqlSpanType type, string operation, object? value) { case SentrySqlSpanType.Execution: var operationId = kvp.Value?.GetGuidProperty("OperationId"); - if (TryGetQuerySpan(transaction, operationId) is not { } querySpan) + if (TryGetQuerySpan(transaction, operationId) is { } querySpan) { - _options.LogWarning( - "Trying to get an execution span with operation id {0}, but it was not found.", - operationId); - return null; + return querySpan; } - if (querySpan.ParentSpanId == transaction.SpanId && - TryGetConnectionId(querySpan) is { } spanConnectionId && - querySpan is SpanTracer executionTracer && - TryGetConnectionSpan(transaction, spanConnectionId) is { } spanConnectionRef) - { - // Connection Span exist but wasn't set as the parent of the current Span. - executionTracer.ParentSpanId = spanConnectionRef.SpanId; - } - - return querySpan; + _options.LogWarning( + "Trying to get an execution span with operation id {0}, but it was not found.", + operationId); + return null; case SentrySqlSpanType.Connection: var connectionId = kvp.Value?.GetGuidProperty("ConnectionId"); @@ -143,9 +118,6 @@ querySpan is SpanTracer executionTracer && } } - private static ISpan? TryStartChild(ISpan? parent, string operation, string? description) => - parent?.StartChild(operation, description); - private static ISpan? TryGetConnectionSpan(ITransaction transaction, Guid? connectionId) => connectionId == null ? null @@ -173,8 +145,7 @@ private void UpdateConnectionSpan(object? value) } var operationId = value.GetGuidProperty("OperationId"); - var connectionId = value.GetGuidProperty("ConnectionId"); - if (operationId == null || connectionId == null) + if (operationId == null) { return; } @@ -182,7 +153,16 @@ private void UpdateConnectionSpan(object? value) var spans = transaction.Spans.Where(span => span.Operation is "db.connection").ToList(); if (spans.Find(span => !span.IsFinished && TryGetOperationId(span) == operationId) is { } connectionSpan) { - SetConnectionId(connectionSpan, connectionId); + if (value.GetGuidProperty("ConnectionId") is { } connectionId) + { + SetConnectionId(connectionSpan, connectionId); + } + + if (value.GetStringProperty("Connection.Database") is { } dbName) + { + connectionSpan.Description = dbName; + SetDatabaseName(connectionSpan, dbName); + } } } @@ -198,22 +178,22 @@ public void OnNext(KeyValuePair kvp) { // Query case SqlMicrosoftBeforeExecuteCommand or SqlDataBeforeExecuteCommand: - AddSpan(SentrySqlSpanType.Execution, "db.query", kvp.Value); + AddSpan("db.query", kvp.Value); return; case SqlMicrosoftAfterExecuteCommand or SqlDataAfterExecuteCommand when GetSpan(SentrySqlSpanType.Execution, kvp) is { } commandSpan: - commandSpan.Description = kvp.Value?.GetProperty("Command")?.GetStringProperty("CommandText"); + commandSpan.Description = kvp.Value?.GetStringProperty("Command.CommandText"); commandSpan.Finish(SpanStatus.Ok); return; case SqlMicrosoftWriteCommandError or SqlDataWriteCommandError when GetSpan(SentrySqlSpanType.Execution, kvp) is { } errorSpan: - errorSpan.Description = kvp.Value?.GetProperty("Command")?.GetStringProperty("CommandText"); + errorSpan.Description = kvp.Value?.GetStringProperty("Command.CommandText"); errorSpan.Finish(SpanStatus.InternalError); return; // Connection case SqlMicrosoftWriteConnectionOpenBeforeCommand or SqlDataWriteConnectionOpenBeforeCommand: - AddSpan(SentrySqlSpanType.Connection, "db.connection", kvp.Value); + AddSpan("db.connection", kvp.Value); return; case SqlMicrosoftWriteConnectionOpenAfterCommand or SqlDataWriteConnectionOpenAfterCommand: UpdateConnectionSpan(kvp.Value); diff --git a/src/Sentry/Internal/Extensions/MiscExtensions.cs b/src/Sentry/Internal/Extensions/MiscExtensions.cs index 9fe0e44813..25bce31157 100644 --- a/src/Sentry/Internal/Extensions/MiscExtensions.cs +++ b/src/Sentry/Internal/Extensions/MiscExtensions.cs @@ -68,8 +68,24 @@ public static void CancelAfterSafe(this CancellationTokenSource cts, TimeSpan ti /// public static bool IsNull(this object? o) => o is null; - public static object? GetProperty(this object obj, string name) => - obj.GetType().GetProperty(name)?.GetValue(obj); + public static object? GetProperty(this object obj, string name) + { + var propertyNames = name.Split('.'); + var currentObj = obj; + + foreach (var propertyName in propertyNames) + { + var property = currentObj?.GetType().GetProperty(propertyName); + if (property == null) + { + return null; + } + + currentObj = property.GetValue(currentObj); + } + + return currentObj; + } public static Guid? GetGuidProperty(this object obj, string name) => obj.GetProperty(name) as Guid?; diff --git a/src/Sentry/Internal/Extensions/ReadOnlyDictionaryExtensions.cs b/src/Sentry/Internal/Extensions/ReadOnlyDictionaryExtensions.cs new file mode 100644 index 0000000000..a91ed787f5 --- /dev/null +++ b/src/Sentry/Internal/Extensions/ReadOnlyDictionaryExtensions.cs @@ -0,0 +1,10 @@ +namespace Sentry.Internal.Extensions; + +internal static class ReadOnlyDictionaryExtensions +{ + public static TValue? TryGetValue(this IReadOnlyDictionary dictionary, TKey key) + where TKey : notnull + => dictionary.TryGetValue(key, out var value) && value is TValue typedValue + ? typedValue + : default; +} diff --git a/src/Sentry/SpanTracer.cs b/src/Sentry/SpanTracer.cs index 66d4aa10ae..876cc6126a 100644 --- a/src/Sentry/SpanTracer.cs +++ b/src/Sentry/SpanTracer.cs @@ -84,6 +84,16 @@ public SpanTracer( /// public ISpan StartChild(string operation) => Transaction.StartChild(SpanId, operation); + /// + /// Used to mark a span as unfinished when it was previously marked as finished. This allows us to reuse spans for + /// DB Connections that get reused by the underlying connection pool + /// + internal void Unfinish() + { + Status = null; + EndTimestamp = null; + } + /// public void Finish() { diff --git a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.Logging.DotNet6_0.verified.txt b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.LoggingAsync.DotNet6_0.verified.txt similarity index 95% rename from test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.Logging.DotNet6_0.verified.txt rename to test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.LoggingAsync.DotNet6_0.verified.txt index 02bec71325..361d7ff6c2 100644 --- a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.Logging.DotNet6_0.verified.txt +++ b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.LoggingAsync.DotNet6_0.verified.txt @@ -67,11 +67,13 @@ WHERE @@ROWCOUNT = 1 AND [Id] = scope_identity();, { IsFinished: true, Operation: db.connection, + Description: SqlListenerTests.verify_LoggingAsync, Status: Ok, IsSampled: true, Extra: { bytes_sent : 510, db.connection_id: Guid_1, + db.name: SqlListenerTests.verify_LoggingAsync, db.operation_id: Guid_2, rows_sent: 0 } diff --git a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.Logging.DotNet7_0.verified.txt b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.LoggingAsync.DotNet7_0.verified.txt similarity index 95% rename from test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.Logging.DotNet7_0.verified.txt rename to test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.LoggingAsync.DotNet7_0.verified.txt index 02bec71325..361d7ff6c2 100644 --- a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.Logging.DotNet7_0.verified.txt +++ b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.LoggingAsync.DotNet7_0.verified.txt @@ -67,11 +67,13 @@ WHERE @@ROWCOUNT = 1 AND [Id] = scope_identity();, { IsFinished: true, Operation: db.connection, + Description: SqlListenerTests.verify_LoggingAsync, Status: Ok, IsSampled: true, Extra: { bytes_sent : 510, db.connection_id: Guid_1, + db.name: SqlListenerTests.verify_LoggingAsync, db.operation_id: Guid_2, rows_sent: 0 } diff --git a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEf.DotNet7_0.verified.txt b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.Core3_1.verified.txt similarity index 94% rename from test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEf.DotNet7_0.verified.txt rename to test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.Core3_1.verified.txt index 7b7852c1a8..522bbd4d67 100644 --- a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEf.DotNet7_0.verified.txt +++ b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.Core3_1.verified.txt @@ -47,12 +47,14 @@ { IsFinished: true, Operation: db.connection, + Description: SqlListenerTests.verify_RecordsEfAsync, Status: Ok, IsSampled: true, Extra: { bytes_received: 304, bytes_sent : 704, db.connection_id: Guid_1, + db.name: SqlListenerTests.verify_RecordsEfAsync, db.operation_id: Guid_2, rows_sent: 1 } @@ -100,4 +102,4 @@ FROM [TestEntities] AS [t], IsFinished: true } } -] \ No newline at end of file +] diff --git a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEf.Core3_1.verified.txt b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.DotNet6_0.verified.txt similarity index 94% rename from test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEf.Core3_1.verified.txt rename to test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.DotNet6_0.verified.txt index 7b7852c1a8..522bbd4d67 100644 --- a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEf.Core3_1.verified.txt +++ b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.DotNet6_0.verified.txt @@ -47,12 +47,14 @@ { IsFinished: true, Operation: db.connection, + Description: SqlListenerTests.verify_RecordsEfAsync, Status: Ok, IsSampled: true, Extra: { bytes_received: 304, bytes_sent : 704, db.connection_id: Guid_1, + db.name: SqlListenerTests.verify_RecordsEfAsync, db.operation_id: Guid_2, rows_sent: 1 } @@ -100,4 +102,4 @@ FROM [TestEntities] AS [t], IsFinished: true } } -] \ No newline at end of file +] diff --git a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEf.DotNet6_0.verified.txt b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.DotNet7_0.verified.txt similarity index 94% rename from test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEf.DotNet6_0.verified.txt rename to test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.DotNet7_0.verified.txt index 7b7852c1a8..522bbd4d67 100644 --- a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEf.DotNet6_0.verified.txt +++ b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.DotNet7_0.verified.txt @@ -47,12 +47,14 @@ { IsFinished: true, Operation: db.connection, + Description: SqlListenerTests.verify_RecordsEfAsync, Status: Ok, IsSampled: true, Extra: { bytes_received: 304, bytes_sent : 704, db.connection_id: Guid_1, + db.name: SqlListenerTests.verify_RecordsEfAsync, db.operation_id: Guid_2, rows_sent: 1 } @@ -100,4 +102,4 @@ FROM [TestEntities] AS [t], IsFinished: true } } -] \ No newline at end of file +] diff --git a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEf.Net4_8.verified.txt b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.Net4_8.verified.txt similarity index 84% rename from test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEf.Net4_8.verified.txt rename to test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.Net4_8.verified.txt index 5c2e93bf15..2458b63427 100644 --- a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEf.Net4_8.verified.txt +++ b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.Net4_8.verified.txt @@ -57,7 +57,9 @@ WHERE @@ROWCOUNT = 1 AND [Id] = scope_identity();, Status: Ok, IsSampled: true, Extra: { - db.command_id: Guid_1 + db.command_id: Guid_1, + db.connection_id: Guid_2, + db.name: SqlListenerTests.verify_RecordsEfAsync } }, { @@ -69,11 +71,13 @@ FROM [TestEntities] AS [t], Status: Ok, IsSampled: true, Extra: { - db.command_id: Guid_2 + db.command_id: Guid_3, + db.connection_id: Guid_4, + db.name: SqlListenerTests.verify_RecordsEfAsync } } ], IsFinished: true } } -] \ No newline at end of file +] diff --git a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsSql.verified.txt b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsSqlAsync.verified.txt similarity index 93% rename from test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsSql.verified.txt rename to test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsSqlAsync.verified.txt index 253f4058f4..bc32616206 100644 --- a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsSql.verified.txt +++ b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsSqlAsync.verified.txt @@ -47,12 +47,14 @@ { IsFinished: true, Operation: db.connection, + Description: SqlListenerTests.verify_RecordsSqlAsync, Status: Ok, IsSampled: true, Extra: { bytes_received: 167, bytes_sent : 536, db.connection_id: Guid_1, + db.name: SqlListenerTests.verify_RecordsSqlAsync, db.operation_id: Guid_2, rows_sent: 1 } @@ -83,4 +85,4 @@ IsFinished: true } } -] \ No newline at end of file +] diff --git a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.verify.cs b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.verify.cs index 7b333767f4..2706cb42fe 100644 --- a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.verify.cs +++ b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.verify.cs @@ -14,7 +14,7 @@ public SqlListenerTests(LocalDbFixture fixture, ITestOutputHelper output) #if !NETFRAMEWORK [SkippableFact] - public async Task RecordsSql() + public async Task RecordsSqlAsync() { Skip.If(!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)); var transport = new RecordingTransport(); @@ -57,7 +57,7 @@ public async Task RecordsSql() #if NET6_0_OR_GREATER [SkippableFact] - public async Task Logging() + public async Task LoggingAsync() { Skip.If(!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)); var transport = new RecordingTransport(); @@ -170,7 +170,7 @@ public void ShouldIgnoreAllErrorAndExceptionIds() } [SkippableFact] - public async Task RecordsEf() + public async Task RecordsEfAsync() { Skip.If(!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)); var transport = new RecordingTransport(); diff --git a/test/Sentry.DiagnosticSource.Tests/SentryEFCoreListenerTests.cs b/test/Sentry.DiagnosticSource.Tests/SentryEFCoreListenerTests.cs index e8b43a9e22..ccd75ebf5e 100644 --- a/test/Sentry.DiagnosticSource.Tests/SentryEFCoreListenerTests.cs +++ b/test/Sentry.DiagnosticSource.Tests/SentryEFCoreListenerTests.cs @@ -1,4 +1,3 @@ -using Microsoft.EntityFrameworkCore.Diagnostics; using Sentry.Internal.DiagnosticSource; using static Sentry.Internal.DiagnosticSource.SentryEFCoreListener; @@ -10,14 +9,11 @@ private static Func GetValidator(string type) => type switch { EFQueryCompiling or EFQueryCompiled => - span => span.Description != null && - span.Operation == "db.query.compile", + span => span.Operation == "db.query.compile", EFConnectionOpening or EFConnectionClosed => - span => span.Description == null && - span.Operation == "db.connection", + span => span.Operation == "db.connection", EFCommandExecuting or EFCommandExecuting or EFCommandFailed => - span => span.Description != null && - span.Operation == "db.query", + span => span.Operation == "db.query", _ => throw new NotSupportedException() }; @@ -194,18 +190,28 @@ private class FakeDiagnosticEventData public FakeDiagnosticEventData(string value) { _value = value; } private readonly string _value; public override string ToString()=> _value; + + public class ConnectionInfo + { + public string Database { get; } = "rentals"; + } + public ConnectionInfo Connection { get; } = new(); } private class FakeDiagnosticConnectionEventData : FakeDiagnosticEventData { public FakeDiagnosticConnectionEventData(string value) : base(value) { } - public Guid ConnectionId { get; set; } = Guid.NewGuid(); + public Guid ConnectionId { get; } = Guid.NewGuid(); } private class FakeDiagnosticCommandEventData : FakeDiagnosticEventData { - public FakeDiagnosticCommandEventData(string value) : base(value) { } - public Guid ConnectionId { get; set; } = Guid.NewGuid(); + public FakeDiagnosticCommandEventData(FakeDiagnosticConnectionEventData connection, string value) : base(value) + { + ConnectionId = connection.ConnectionId; + } + + public Guid ConnectionId { get; set; } public Guid CommandId { get; set; } = Guid.NewGuid(); } @@ -218,10 +224,11 @@ public void OnNext_HappyPath_IsValid() var expectedSql = "SELECT * FROM ..."; var efSql = "ef Junk\r\nSELECT * FROM ..."; var efConn = "db username : password"; + var expectedDbName = "rentals"; var queryEventData = new FakeDiagnosticEventData(efSql); var connectionEventData = new FakeDiagnosticConnectionEventData(efConn); - var commandEventData = new FakeDiagnosticCommandEventData(efSql); + var commandEventData = new FakeDiagnosticCommandEventData(connectionEventData, efSql); // Act interceptor.OnNext(new(EFQueryCompiling, queryEventData)); @@ -245,10 +252,18 @@ public void OnNext_HappyPath_IsValid() }); // Assert span descriptions - Assert.Null(connectionSpan.Description); + Assert.Equal(expectedDbName,connectionSpan.Description); Assert.Equal(expectedSql, compilerSpan.Description); Assert.Equal(expectedSql, commandSpan.Description); + // Check DB Name is stored correctly + var connectionDbName = + connectionSpan.Extra.TryGetValue(OTelKeys.DbName); + Assert.Equal(expectedDbName, connectionDbName); + var commandDbName = + commandSpan.Extra.TryGetValue(OTelKeys.DbName); + Assert.Equal(expectedDbName, commandDbName); + // Check connections between spans. Assert.Equal(_fixture.Tracer.SpanId, compilerSpan.ParentSpanId); Assert.Equal(_fixture.Tracer.SpanId, connectionSpan.ParentSpanId); @@ -267,10 +282,11 @@ public void OnNext_HappyPathInsideChildSpan_IsValid() var expectedSql = "SELECT * FROM ..."; var efSql = "ef Junk\r\nSELECT * FROM ..."; var efConn = "db username : password"; + var expectedDbName = "rentals"; var queryEventData = new FakeDiagnosticEventData(efSql); var connectionEventData = new FakeDiagnosticConnectionEventData(efConn); - var commandEventData = new FakeDiagnosticCommandEventData(efSql); + var commandEventData = new FakeDiagnosticCommandEventData(connectionEventData, efSql); // Act var childSpan = _fixture.Tracer.StartChild("Child Span"); @@ -296,10 +312,15 @@ public void OnNext_HappyPathInsideChildSpan_IsValid() }); // Assert span descriptions - Assert.Null(connectionSpan.Description); + Assert.Equal(expectedDbName, connectionSpan.Description); Assert.Equal(expectedSql, compilerSpan.Description); Assert.Equal(expectedSql, commandSpan.Description); + // Check DB Name is stored correctly + var dbName = + connectionSpan.Extra.TryGetValue(OTelKeys.DbName); + Assert.Equal(expectedDbName, dbName); + // Check connections between spans. Assert.Equal(childSpan.SpanId, compilerSpan.ParentSpanId); Assert.Equal(childSpan.SpanId, connectionSpan.ParentSpanId); @@ -321,7 +342,7 @@ public void OnNext_HappyPathWithError_TransactionWithErroredCommand() var queryEventData = new FakeDiagnosticEventData(efSql); var connectionEventData = new FakeDiagnosticConnectionEventData(efConn); - var commandEventData = new FakeDiagnosticCommandEventData(efSql); + var commandEventData = new FakeDiagnosticCommandEventData(connectionEventData, efSql); // Act interceptor.OnNext(new(EFQueryCompiling, queryEventData)); @@ -378,6 +399,96 @@ public void OnNext_HappyPathWithError_TransactionWithErroredCompiler() Assert.Equal(expectedSql, compilerSpan.Description); } + [Fact] + public void OnNext_Same_Connections_Consolidated() + { + // Arrange + var hub = _fixture.Hub; + var interceptor = new SentryEFCoreListener(hub, _fixture.Options); + var efSql = "ef Junk\r\nSELECT * FROM ..."; + var efConn = "db username : password"; + + // Fake a connection pool with two connections + var connectionA = new FakeDiagnosticConnectionEventData(efConn); + var connectionB = new FakeDiagnosticConnectionEventData(efConn); + var commandA = new FakeDiagnosticCommandEventData(connectionA, efSql); + var commandB = new FakeDiagnosticCommandEventData(connectionB, efSql); + var commandC = new FakeDiagnosticCommandEventData(connectionA, efSql); + + void Pause() => Thread.Sleep(200); + + // Act + interceptor.OnNext(new(EFConnectionOpening, connectionA)); + Pause(); + interceptor.OnNext(new(EFCommandExecuting, commandA)); + interceptor.OnNext(new(EFCommandExecuted, commandA)); + interceptor.OnNext(new(EFConnectionClosed, connectionA)); + + interceptor.OnNext(new(EFConnectionOpening, connectionA)); + + // These are for Connection B... interleaved somewhat + interceptor.OnNext(new(EFConnectionOpening, connectionB)); + Pause(); + interceptor.OnNext(new(EFCommandExecuting, commandB)); + interceptor.OnNext(new(EFCommandExecuted, commandB)); + + interceptor.OnNext(new(EFCommandExecuting, commandC)); + + // These are for Connection B... interleaved somewhat + Pause(); + interceptor.OnNext(new(EFConnectionClosed, connectionB)); + + interceptor.OnNext(new(EFCommandExecuted, commandC)); + Pause(); + interceptor.OnNext(new(EFConnectionClosed, connectionA)); + + // Assert + bool IsDbSpan(ISpan s) => s.Operation == "db.connection"; + bool IsCommandSpan(ISpan s) => s.Operation == "db.query"; + Func forConnection = (s, e) => + s.Extra.ContainsKey(EFKeys.DbConnectionId) + && s.Extra[EFKeys.DbConnectionId] is Guid connectionId + && connectionId == e.ConnectionId; + Func forCommand = (s, e) => + s.Extra.ContainsKey(EFKeys.DbCommandId) + && s.Extra[EFKeys.DbCommandId] is Guid commandId + && commandId == e.CommandId; + + using (new AssertionScope()) + { + var dbSpans = _fixture.Spans.Where(IsDbSpan).ToArray(); + dbSpans.Count().Should().Be(2); + dbSpans.Should().Contain(s => forConnection(s, connectionA)); + dbSpans.Should().Contain(s => forConnection(s, connectionB)); + + var commandSpans = _fixture.Spans.Where(IsCommandSpan).ToArray(); + commandSpans.Count().Should().Be(3); + commandSpans.Should().Contain(s => forCommand(s, commandA)); + commandSpans.Should().Contain(s => forCommand(s, commandB)); + commandSpans.Should().Contain(s => forCommand(s, commandC)); + + var connectionASpan = dbSpans.Single(s => forConnection(s, connectionA)); + var connectionBSpan = dbSpans.Single(s => forConnection(s, connectionB)); + var commandASpan = commandSpans.Single(s => forCommand(s, commandA)); + var commandBSpan = commandSpans.Single(s => forCommand(s, commandB)); + var commandCSpan = commandSpans.Single(s => forCommand(s, commandC)); + + // Commands for connectionA should take place after it starts and before it finishes + connectionASpan.StartTimestamp.Should().BeBefore(commandASpan.StartTimestamp); + connectionASpan.StartTimestamp.Should().BeBefore(commandCSpan.StartTimestamp); + connectionASpan.EndTimestamp.Should().BeAfter(commandASpan.EndTimestamp ?? DateTimeOffset.MinValue); + connectionASpan.EndTimestamp.Should().BeAfter(commandCSpan.EndTimestamp ?? DateTimeOffset.MinValue); + + // Commands for connectionB should take place after it starts and before it finishes + connectionBSpan.StartTimestamp.Should().BeBefore(commandBSpan.StartTimestamp); + connectionBSpan.EndTimestamp.Should().BeAfter(commandBSpan.EndTimestamp ?? DateTimeOffset.MinValue); + + // Connection B starts after Connection A and finishes before Connection A + connectionBSpan.StartTimestamp.Should().BeAfter(connectionASpan.StartTimestamp); + connectionBSpan.EndTimestamp.Should().BeBefore(connectionASpan.EndTimestamp ?? DateTimeOffset.MinValue); + } + } + [Fact] public void OnNext_ThrowsException_ExceptionIsolated() { diff --git a/test/Sentry.DiagnosticSource.Tests/SentrySqlListenerTests.cs b/test/Sentry.DiagnosticSource.Tests/SentrySqlListenerTests.cs index a0808c024e..9b110b475f 100644 --- a/test/Sentry.DiagnosticSource.Tests/SentrySqlListenerTests.cs +++ b/test/Sentry.DiagnosticSource.Tests/SentrySqlListenerTests.cs @@ -1,4 +1,6 @@ +using Microsoft.EntityFrameworkCore.Storage; using Sentry.Internal.DiagnosticSource; +using Sentry.Internal.Extensions; using static Sentry.Internal.DiagnosticSource.SentrySqlListener; namespace Sentry.DiagnosticSource.Tests; @@ -14,8 +16,7 @@ SqlMicrosoftWriteConnectionOpenAfterCommand or SqlDataWriteConnectionOpenAfterCommand or SqlMicrosoftWriteConnectionCloseAfterCommand or SqlDataWriteConnectionCloseAfterCommand => - span => span.Description is null && - span.Operation == "db.connection", + span => span.Operation == "db.connection", SqlDataBeforeExecuteCommand or SqlMicrosoftBeforeExecuteCommand or @@ -103,7 +104,7 @@ public void OnNext_KnownKey_GetSpanInvoked(string key, bool addConnectionSpan) var interceptor = new SentrySqlListener(hub, new SentryOptions()); if (addConnectionSpan) { - _fixture.Tracer.StartChild("abc").SetExtra(ConnectionExtraKey, Guid.Empty); + _fixture.Tracer.StartChild("abc").SetExtra(SqlKeys.DbConnectionId, Guid.Empty); } // Act @@ -169,20 +170,29 @@ public void OnNext_HappyPaths_IsValid(string connectionOpenKey, string connectio var connectionOperationId = Guid.NewGuid(); var connectionOperationIdClosed = Guid.NewGuid(); var queryOperationId = Guid.NewGuid(); + var dbName = "rentals"; // Act interceptor.OnNext( new(connectionOpenKey, new { - OperationId = connectionOperationId + OperationId = connectionOperationId, + Connection = new + { + Database = dbName + } })); interceptor.OnNext( new(connectionUpdateKey, new { OperationId = connectionOperationId, - ConnectionId = connectionId + ConnectionId = connectionId, + Connection = new + { + Database = dbName + } })); interceptor.OnNext( new(queryStartKey, @@ -207,7 +217,11 @@ public void OnNext_HappyPaths_IsValid(string connectionOpenKey, string connectio new { OperationId = connectionOperationIdClosed, - ConnectionId = connectionId + ConnectionId = connectionId, + Connection = new + { + Database = dbName + } })); // Assert @@ -225,9 +239,17 @@ public void OnNext_HappyPaths_IsValid(string connectionOpenKey, string connectio // Check connections between spans. Assert.Equal(_fixture.Tracer.SpanId, connectionSpan.ParentSpanId); - Assert.Equal(connectionSpan.SpanId, commandSpan.ParentSpanId); + Assert.Equal(_fixture.Tracer.SpanId, commandSpan.ParentSpanId); + // Validate descriptions and extra data is set correctly Assert.Equal(query, commandSpan.Description); + Assert.Equal(queryOperationId, commandSpan.Extra.TryGetValue(SqlKeys.DbOperationId)); + Assert.Equal(connectionId, commandSpan.Extra.TryGetValue(SqlKeys.DbConnectionId)); + + Assert.Equal(dbName, connectionSpan.Description); + Assert.Equal(connectionOperationId, connectionSpan.Extra.TryGetValue(SqlKeys.DbOperationId)); + Assert.Equal(connectionId, connectionSpan.Extra.TryGetValue(SqlKeys.DbConnectionId)); + Assert.Equal(dbName, connectionSpan.Extra.TryGetValue(OTelKeys.DbName)); } [Theory] @@ -305,7 +327,7 @@ public void OnNext_HappyPathsInsideChildSpan_IsValid(string connectionOpenKey, s // Check connections between spans. Assert.Equal(childSpan.SpanId, connectionSpan.ParentSpanId); - Assert.Equal(connectionSpan.SpanId, commandSpan.ParentSpanId); + Assert.Equal(childSpan.SpanId, commandSpan.ParentSpanId); Assert.Equal(query, commandSpan.Description); } @@ -363,7 +385,7 @@ public void OnNext_TwoConnectionSpansWithSameId_FinishBothWithOk(string connecti { Assert.True(span.IsFinished); Assert.Equal(SpanStatus.Ok, span.Status); - Assert.Equal(connectionId, (Guid)span.Extra[ConnectionExtraKey]!); + Assert.Equal(connectionId, (Guid)span.Extra[SqlKeys.DbConnectionId]!); }); } @@ -442,7 +464,7 @@ public void OnNext_ExecuteQueryCalledBeforeConnectionId_ExecuteParentIsConnectio // Check connections between spans. Assert.Equal(_fixture.Tracer.SpanId, connectionSpan.ParentSpanId); - Assert.Equal(connectionSpan.SpanId, commandSpan.ParentSpanId); + Assert.Equal(_fixture.Tracer.SpanId, commandSpan.ParentSpanId); Assert.Equal(query, commandSpan.Description); } @@ -467,7 +489,7 @@ public void OnNext_ExecuteQueryCalledBeforeConnectionId_ExecuteParentIsConnectio [InlineData(17)] [InlineData(18)] [InlineData(19)] - public async Task OnNext_ParallelExecution_IsValid(int testNumber) + public async Task OnNext_ParallelExecution_IsValidAsync(int testNumber) { _ = testNumber; // Arrange @@ -530,27 +552,26 @@ void SimulateDbRequest(List connectionOperationIds, List queryOperat querySpans.Should().HaveCount(2 * maxItems); // Open Spans should not have any Connection key. - Assert.All(openSpans, span => Assert.False(span.Extra.ContainsKey(ConnectionExtraKey))); + Assert.All(openSpans, span => Assert.False(span.Extra.ContainsKey(SqlKeys.DbConnectionId))); Assert.All(closedSpans, span => Assert.Equal(SpanStatus.Ok, span.Status)); // Assert that all connectionIds is set and ParentId set to Trace. // ReSharper disable once ParameterOnlyUsedForPreconditionCheck.Local Assert.All(closedConnectionSpans, connectionSpan => { - Assert.NotNull(connectionSpan.Extra[ConnectionExtraKey]); - Assert.NotNull(connectionSpan.Extra[OperationExtraKey]); + Assert.NotNull(connectionSpan.Extra[SqlKeys.DbConnectionId]); + Assert.NotNull(connectionSpan.Extra[SqlKeys.DbOperationId]); Assert.Equal(_fixture.Tracer.SpanId, connectionSpan.ParentSpanId); }); - // Assert all Query spans have the correct ParentId Set and not the Transaction Id. + // Assert all Query spans have the correct ParentId Set and record the Connection Id. Assert.All(querySpans, querySpan => { Assert.True(querySpan.IsFinished); - var connectionId = (Guid)querySpan.Extra[ConnectionExtraKey]!; - var connectionSpan = connectionSpans.Single(span => span.SpanId == querySpan.ParentSpanId); + Assert.Equal(_fixture.Tracer.SpanId, querySpan.ParentSpanId); - Assert.NotEqual(_fixture.Tracer.SpanId, querySpan.ParentSpanId); - Assert.Equal((Guid)connectionSpan.Extra[ConnectionExtraKey]!, connectionId); + var queryConnectionId = querySpan.Extra.TryGetValue(SqlKeys.DbConnectionId); + queryConnectionId.Should().NotBeNull(); }); _fixture.Logger.Entries.Should().BeEmpty(); @@ -624,7 +645,7 @@ public void OnNext_HappyPathWithError_TransactionWithErroredCommand() // Check connections between spans. Assert.Equal(_fixture.Tracer.SpanId, connectionSpan.ParentSpanId); - Assert.Equal(connectionSpan.SpanId, commandSpan.ParentSpanId); + Assert.Equal(_fixture.Tracer.SpanId, commandSpan.ParentSpanId); Assert.Equal(query, commandSpan.Description); } diff --git a/test/Sentry.Tests/Internals/Extensions/MiscExtensionsTests.cs b/test/Sentry.Tests/Internals/Extensions/MiscExtensionsTests.cs new file mode 100644 index 0000000000..ba8499af66 --- /dev/null +++ b/test/Sentry.Tests/Internals/Extensions/MiscExtensionsTests.cs @@ -0,0 +1,74 @@ +namespace Sentry.Tests.Internals.Extensions; + +public class MiscExtensionsTests +{ + public class MyClass { + public string SomeString { get; set; } + public MySubclass Subclass { get; set; } + } + + public class MySubclass { + public string AnotherString { get; set; } + } + + [Fact] + public void GetStringProperty_ShouldReturnTopLevelProperty() + { + var myClass = new MyClass + { + SomeString = "Hello" + }; + + var result = myClass.GetStringProperty("SomeString"); + + Assert.Equal("Hello", result); + } + + [Fact] + public void GetStringProperty_ShouldReturnNestedProperty() + { + var myClass = new MyClass + { + Subclass = new MySubclass + { + AnotherString = "World" + } + }; + + var result = myClass.GetStringProperty("Subclass.AnotherString"); + + Assert.Equal("World", result); + } + + [Fact] + public void GetStringProperty_ShouldReturnNull_WhenPropertyNotFound() + { + var myClass = new MyClass(); + + var result = myClass.GetStringProperty("NonExistentProperty"); + + Assert.Null(result); + } + + [Fact] + public void GetStringProperty_ShouldReturnNull_WhenIntermediatePropertyIsNull() + { + var myClass = new MyClass(); + + var result = myClass.GetStringProperty("Subclass.AnotherString"); + + Assert.Null(result); + } + + [Fact] + public void GetStringProperty_ShouldReturnNull_WhenNestedPropertyNotString() + { + var myClass = new MyClass(); + myClass.Subclass = new MySubclass(); + myClass.Subclass.AnotherString = "World"; + + var result = myClass.GetStringProperty("Subclass"); + + Assert.Null(result); + } +} diff --git a/test/Sentry.Tests/Internals/Extensions/ReadOnlyDictionaryExtensionsTests.cs b/test/Sentry.Tests/Internals/Extensions/ReadOnlyDictionaryExtensionsTests.cs new file mode 100644 index 0000000000..9ca1d5ab86 --- /dev/null +++ b/test/Sentry.Tests/Internals/Extensions/ReadOnlyDictionaryExtensionsTests.cs @@ -0,0 +1,49 @@ +namespace Sentry.Tests.Internals.Extensions; + +public class ReadOnlyDictionaryExtensionsTests +{ + [Fact] + public void TryGetValue_ShouldReturnNull_WhenKeyNotFound() + { + // Arrange + var dictionary = new Dictionary(); // Empty dictionary + + // Act + var result = dictionary.TryGetValue("key"); + + // Assert + result.Should().BeNull(); + } + + [Fact] + public void TryGetValue_ShouldReturnValue_WhenKeyFoundAndTypeMatches() + { + // Arrange + var dictionary = new Dictionary + { + { "key", "value" } + }; + + // Act + var result = dictionary.TryGetValue("key"); + + // Assert + result.Should().Be("value"); + } + + [Fact] + public void TryGetValue_ShouldReturnNull_WhenKeyFoundButTypeDoesNotMatch() + { + // Arrange + var dictionary = new Dictionary + { + { "key", 123 } + }; + + // Act + var result = dictionary.TryGetValue("key"); + + // Assert + result.Should().BeNull(); + } +} From 204d4f98a8463a1a5267f4bd11d0401bc4f4805e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Jun 2023 09:02:41 -0700 Subject: [PATCH 068/142] Bump github/codeql-action from 2.3.6 to 2.13.4 (#2419) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2.3.6 to 2.13.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/83f0fe6c4988d98a455712a27f0255212bba9bd4...cdcdbb579706841c47f7063dda365e292e5cad7a) --- 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 8086290c61..30ec842412 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@83f0fe6c4988d98a455712a27f0255212bba9bd4 # pin@v2 + uses: github/codeql-action/init@cdcdbb579706841c47f7063dda365e292e5cad7a # 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@83f0fe6c4988d98a455712a27f0255212bba9bd4 # pin@v2 + uses: github/codeql-action/analyze@cdcdbb579706841c47f7063dda365e292e5cad7a # pin@v2 with: category: '/language:csharp' From 333163ffdf98776fd7003a9d399bc26171c5e404 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Tue, 13 Jun 2023 04:08:31 +1200 Subject: [PATCH 069/142] Fix/sql client db name (#2418) --- CHANGELOG.md | 1 + .../DiagnosticSource/SentrySqlListener.cs | 56 +++++++++++++------ ...rTests.LoggingAsync.DotNet6_0.verified.txt | 1 + ...rTests.LoggingAsync.DotNet7_0.verified.txt | 1 + ...rTests.RecordsEfAsync.Core3_1.verified.txt | 2 + ...ests.RecordsEfAsync.DotNet6_0.verified.txt | 2 + ...ests.RecordsEfAsync.DotNet7_0.verified.txt | 2 + ...ListenerTests.RecordsSqlAsync.verified.txt | 2 + .../SentrySqlListenerTests.cs | 7 +-- 9 files changed, 53 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 95cef3fd90..b3294b7a23 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ - DB Connection spans presented poorly ([#2409](https://github.com/getsentry/sentry-dotnet/pull/2409)) - Populate scope's Cookies property ([#2411](https://github.com/getsentry/sentry-dotnet/pull/2411)) - Fix UWP GateKeeper errors ([#2415](https://github.com/getsentry/sentry-dotnet/pull/2415)) +- Fix sql client db name ([#2418](https://github.com/getsentry/sentry-dotnet/pull/2418)) ### Dependencies diff --git a/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/SentrySqlListener.cs b/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/SentrySqlListener.cs index a83ba965a9..326d60a509 100644 --- a/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/SentrySqlListener.cs +++ b/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/SentrySqlListener.cs @@ -71,15 +71,13 @@ private void AddSpan(string operation, object? value) return; } - var operationId = value?.GetGuidProperty("OperationId"); - var connectionId = value?.GetGuidProperty("ConnectionId"); var parent = transaction.GetDbParentSpan(); var span = parent.StartChild(operation); - SetOperationId(span, operationId); - SetConnectionId(span, connectionId); + SetOperationId(span, value?.GetGuidProperty("OperationId")); + SetConnectionId(span, value?.GetGuidProperty("ConnectionId")); } - private ISpan? GetSpan(SentrySqlSpanType type, KeyValuePair kvp) + private ISpan? GetSpan(SentrySqlSpanType type, object? value) { var transaction = _hub.GetTransactionIfSampled(); if (transaction == null) @@ -90,7 +88,7 @@ private void AddSpan(string operation, object? value) switch (type) { case SentrySqlSpanType.Execution: - var operationId = kvp.Value?.GetGuidProperty("OperationId"); + var operationId = value?.GetGuidProperty("OperationId"); if (TryGetQuerySpan(transaction, operationId) is { } querySpan) { return querySpan; @@ -102,7 +100,7 @@ private void AddSpan(string operation, object? value) return null; case SentrySqlSpanType.Connection: - var connectionId = kvp.Value?.GetGuidProperty("ConnectionId"); + var connectionId = value?.GetGuidProperty("ConnectionId"); if (TryGetConnectionSpan(transaction, connectionId) is { } connectionSpan) { return connectionSpan; @@ -166,6 +164,36 @@ private void UpdateConnectionSpan(object? value) } } + private void FinishCommandSpan(object? value, SpanStatus spanStatus) + { + if (value == null) + { + return; + } + + + if (GetSpan(SentrySqlSpanType.Execution, value) is not { } commandSpan) + { + return; + } + + // Try to lookup the associated connection span so that we can store the db.name in + // the command span as well. This will be easier for users to read/identify than the + // ConnectionId (which is a Guid) + var connectionId = value.GetGuidProperty("ConnectionId"); + var transaction = _hub.GetTransactionIfSampled(); + if (TryGetConnectionSpan(transaction!, connectionId) is { } connectionSpan) + { + if (connectionSpan.Extra.TryGetValue(OTelKeys.DbName) is { } dbName) + { + SetDatabaseName(commandSpan, dbName); + } + } + + commandSpan.Description = value.GetStringProperty("Command.CommandText"); + commandSpan.Finish(spanStatus); + } + public void OnCompleted() { } public void OnError(Exception error) { } @@ -180,15 +208,11 @@ public void OnNext(KeyValuePair kvp) case SqlMicrosoftBeforeExecuteCommand or SqlDataBeforeExecuteCommand: AddSpan("db.query", kvp.Value); return; - case SqlMicrosoftAfterExecuteCommand or SqlDataAfterExecuteCommand - when GetSpan(SentrySqlSpanType.Execution, kvp) is { } commandSpan: - commandSpan.Description = kvp.Value?.GetStringProperty("Command.CommandText"); - commandSpan.Finish(SpanStatus.Ok); + case SqlMicrosoftAfterExecuteCommand or SqlDataAfterExecuteCommand: + FinishCommandSpan(kvp.Value, SpanStatus.Ok); return; - case SqlMicrosoftWriteCommandError or SqlDataWriteCommandError - when GetSpan(SentrySqlSpanType.Execution, kvp) is { } errorSpan: - errorSpan.Description = kvp.Value?.GetStringProperty("Command.CommandText"); - errorSpan.Finish(SpanStatus.InternalError); + case SqlMicrosoftWriteCommandError or SqlDataWriteCommandError: + FinishCommandSpan(kvp.Value, SpanStatus.InternalError); return; // Connection @@ -199,7 +223,7 @@ when GetSpan(SentrySqlSpanType.Execution, kvp) is { } errorSpan: UpdateConnectionSpan(kvp.Value); return; case SqlMicrosoftWriteConnectionCloseAfterCommand or SqlDataWriteConnectionCloseAfterCommand - when GetSpan(SentrySqlSpanType.Connection, kvp) is { } closeSpan: + when GetSpan(SentrySqlSpanType.Connection, kvp.Value) is { } closeSpan: TrySetConnectionStatistics(closeSpan, kvp.Value); closeSpan.Finish(SpanStatus.Ok); return; diff --git a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.LoggingAsync.DotNet6_0.verified.txt b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.LoggingAsync.DotNet6_0.verified.txt index 361d7ff6c2..976514b4d6 100644 --- a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.LoggingAsync.DotNet6_0.verified.txt +++ b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.LoggingAsync.DotNet6_0.verified.txt @@ -94,6 +94,7 @@ WHERE @@ROWCOUNT = 1 AND [Id] = scope_identity(); IsSampled: true, Extra: { db.connection_id: Guid_1, + db.name: SqlListenerTests.verify_LoggingAsync, db.operation_id: Guid_3 } } diff --git a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.LoggingAsync.DotNet7_0.verified.txt b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.LoggingAsync.DotNet7_0.verified.txt index 361d7ff6c2..976514b4d6 100644 --- a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.LoggingAsync.DotNet7_0.verified.txt +++ b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.LoggingAsync.DotNet7_0.verified.txt @@ -94,6 +94,7 @@ WHERE @@ROWCOUNT = 1 AND [Id] = scope_identity(); IsSampled: true, Extra: { db.connection_id: Guid_1, + db.name: SqlListenerTests.verify_LoggingAsync, db.operation_id: Guid_3 } } diff --git a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.Core3_1.verified.txt b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.Core3_1.verified.txt index 522bbd4d67..fe0379b158 100644 --- a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.Core3_1.verified.txt +++ b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.Core3_1.verified.txt @@ -75,6 +75,7 @@ WHERE @@ROWCOUNT = 1 AND [Id] = scope_identity(); IsSampled: true, Extra: { db.connection_id: Guid_1, + db.name: SqlListenerTests.verify_RecordsEfAsync, db.operation_id: Guid_3 } }, @@ -95,6 +96,7 @@ FROM [TestEntities] AS [t], IsSampled: true, Extra: { db.connection_id: Guid_1, + db.name: SqlListenerTests.verify_RecordsEfAsync, db.operation_id: Guid_4 } } diff --git a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.DotNet6_0.verified.txt b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.DotNet6_0.verified.txt index 522bbd4d67..fe0379b158 100644 --- a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.DotNet6_0.verified.txt +++ b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.DotNet6_0.verified.txt @@ -75,6 +75,7 @@ WHERE @@ROWCOUNT = 1 AND [Id] = scope_identity(); IsSampled: true, Extra: { db.connection_id: Guid_1, + db.name: SqlListenerTests.verify_RecordsEfAsync, db.operation_id: Guid_3 } }, @@ -95,6 +96,7 @@ FROM [TestEntities] AS [t], IsSampled: true, Extra: { db.connection_id: Guid_1, + db.name: SqlListenerTests.verify_RecordsEfAsync, db.operation_id: Guid_4 } } diff --git a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.DotNet7_0.verified.txt b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.DotNet7_0.verified.txt index 522bbd4d67..fe0379b158 100644 --- a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.DotNet7_0.verified.txt +++ b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.DotNet7_0.verified.txt @@ -75,6 +75,7 @@ WHERE @@ROWCOUNT = 1 AND [Id] = scope_identity(); IsSampled: true, Extra: { db.connection_id: Guid_1, + db.name: SqlListenerTests.verify_RecordsEfAsync, db.operation_id: Guid_3 } }, @@ -95,6 +96,7 @@ FROM [TestEntities] AS [t], IsSampled: true, Extra: { db.connection_id: Guid_1, + db.name: SqlListenerTests.verify_RecordsEfAsync, db.operation_id: Guid_4 } } diff --git a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsSqlAsync.verified.txt b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsSqlAsync.verified.txt index bc32616206..cd64fd0e6d 100644 --- a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsSqlAsync.verified.txt +++ b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsSqlAsync.verified.txt @@ -67,6 +67,7 @@ IsSampled: true, Extra: { db.connection_id: Guid_1, + db.name: SqlListenerTests.verify_RecordsSqlAsync, db.operation_id: Guid_3 } }, @@ -78,6 +79,7 @@ IsSampled: true, Extra: { db.connection_id: Guid_1, + db.name: SqlListenerTests.verify_RecordsSqlAsync, db.operation_id: Guid_4 } } diff --git a/test/Sentry.DiagnosticSource.Tests/SentrySqlListenerTests.cs b/test/Sentry.DiagnosticSource.Tests/SentrySqlListenerTests.cs index 9b110b475f..32bcc61b36 100644 --- a/test/Sentry.DiagnosticSource.Tests/SentrySqlListenerTests.cs +++ b/test/Sentry.DiagnosticSource.Tests/SentrySqlListenerTests.cs @@ -217,11 +217,7 @@ public void OnNext_HappyPaths_IsValid(string connectionOpenKey, string connectio new { OperationId = connectionOperationIdClosed, - ConnectionId = connectionId, - Connection = new - { - Database = dbName - } + ConnectionId = connectionId })); // Assert @@ -245,6 +241,7 @@ public void OnNext_HappyPaths_IsValid(string connectionOpenKey, string connectio Assert.Equal(query, commandSpan.Description); Assert.Equal(queryOperationId, commandSpan.Extra.TryGetValue(SqlKeys.DbOperationId)); Assert.Equal(connectionId, commandSpan.Extra.TryGetValue(SqlKeys.DbConnectionId)); + Assert.Equal(dbName, commandSpan.Extra.TryGetValue(OTelKeys.DbName)); Assert.Equal(dbName, connectionSpan.Description); Assert.Equal(connectionOperationId, connectionSpan.Extra.TryGetValue(SqlKeys.DbOperationId)); From b8ff423db49129b908fa6399ab160300fc890c05 Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Mon, 12 Jun 2023 18:21:52 -0700 Subject: [PATCH 070/142] Workaround Tizen build issue (#2421) --- .github/actions/environment/action.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/actions/environment/action.yml b/.github/actions/environment/action.yml index e37ab04cc9..ce57471e35 100644 --- a/.github/actions/environment/action.yml +++ b/.github/actions/environment/action.yml @@ -33,7 +33,13 @@ runs: - name: Install .NET 7 SDK uses: actions/setup-dotnet@v3 with: - dotnet-version: 7.x.x + dotnet-version: 7.0.203 # switch back to 7.x.x after resolving the below Tizen issue. + + # Workaround for Tizen issue + # See https://github.com/dotnet/sdk/issues/33192 + - name: Pin to .NET SDK 7.0.203 + run: dotnet new globaljson --sdk-version 7.0.203 --force + shell: bash - name: Dependency Caching uses: actions/cache@v3 From 2d6b2eb429da501e3da5cc7eb362345532058f09 Mon Sep 17 00:00:00 2001 From: getsentry-bot Date: Tue, 13 Jun 2023 21:53:16 +0000 Subject: [PATCH 071/142] release: 3.33.1 --- CHANGELOG.md | 2 +- Directory.Build.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b3294b7a23..a381bbe0ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## Unreleased +## 3.33.1 ### Fixes diff --git a/Directory.Build.props b/Directory.Build.props index 2010b193a7..01d26d4ab8 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,7 +1,7 @@ - 3.33.0 + 3.33.1 11 true $(MSBuildThisFileDirectory).assets\Sentry.snk From d53cf8de97a43f6aa084588d949d9f72757ef09d Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Wed, 14 Jun 2023 16:08:30 -0700 Subject: [PATCH 072/142] Fix Sentry logger options for MAUI and Azure Functions (#2423) * Add logger provider for MAUI * Add logger provider for Azure Functions * Update CHANGELOG.md --- CHANGELOG.md | 6 ++++++ .../SentryAzureFunctionsLoggerProvider.cs | 14 ++++++++++++++ ...yFunctionsWorkerApplicationBuilderExtensions.cs | 3 +-- .../Internal/SentryMauiLoggerProvider.cs | 14 ++++++++++++++ src/Sentry.Maui/SentryMauiAppBuilderExtensions.cs | 3 +-- 5 files changed, 36 insertions(+), 4 deletions(-) create mode 100644 src/Sentry.AzureFunctions.Worker/SentryAzureFunctionsLoggerProvider.cs create mode 100644 src/Sentry.Maui/Internal/SentryMauiLoggerProvider.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index a381bbe0ef..4dc77fe307 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +### Fixes + +- Fix Sentry logger options for MAUI and Azure Functions ([#2423](https://github.com/getsentry/sentry-dotnet/pull/2423)) + ## 3.33.1 ### Fixes diff --git a/src/Sentry.AzureFunctions.Worker/SentryAzureFunctionsLoggerProvider.cs b/src/Sentry.AzureFunctions.Worker/SentryAzureFunctionsLoggerProvider.cs new file mode 100644 index 0000000000..595d49ce9f --- /dev/null +++ b/src/Sentry.AzureFunctions.Worker/SentryAzureFunctionsLoggerProvider.cs @@ -0,0 +1,14 @@ +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Sentry.Extensions.Logging; + +namespace Sentry.AzureFunctions.Worker; + +[ProviderAlias("Sentry")] +internal class SentryAzureFunctionsLoggerProvider : SentryLoggerProvider +{ + public SentryAzureFunctionsLoggerProvider(IOptions options, IHub hub) + : base(options, hub) + { + } +} diff --git a/src/Sentry.AzureFunctions.Worker/SentryFunctionsWorkerApplicationBuilderExtensions.cs b/src/Sentry.AzureFunctions.Worker/SentryFunctionsWorkerApplicationBuilderExtensions.cs index 66c5f4080b..c6ced4562e 100644 --- a/src/Sentry.AzureFunctions.Worker/SentryFunctionsWorkerApplicationBuilderExtensions.cs +++ b/src/Sentry.AzureFunctions.Worker/SentryFunctionsWorkerApplicationBuilderExtensions.cs @@ -4,7 +4,6 @@ using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -using Sentry.Extensions.Logging; using Sentry.Extensions.Logging.Extensions.DependencyInjection; namespace Sentry.AzureFunctions.Worker; @@ -47,7 +46,7 @@ public static IFunctionsWorkerApplicationBuilder UseSentry( } services.AddLogging(); - services.AddSingleton(); + services.AddSingleton(); services.AddSingleton, SentryAzureFunctionsOptionsSetup>(); services.AddSentry(); diff --git a/src/Sentry.Maui/Internal/SentryMauiLoggerProvider.cs b/src/Sentry.Maui/Internal/SentryMauiLoggerProvider.cs new file mode 100644 index 0000000000..df33ef2342 --- /dev/null +++ b/src/Sentry.Maui/Internal/SentryMauiLoggerProvider.cs @@ -0,0 +1,14 @@ +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Sentry.Extensions.Logging; + +namespace Sentry.Maui.Internal; + +[ProviderAlias("Sentry")] +internal class SentryMauiLoggerProvider : SentryLoggerProvider +{ + public SentryMauiLoggerProvider(IOptions options, IHub hub) + : base(options, hub) + { + } +} diff --git a/src/Sentry.Maui/SentryMauiAppBuilderExtensions.cs b/src/Sentry.Maui/SentryMauiAppBuilderExtensions.cs index e3dc3965f6..8a39b1427f 100644 --- a/src/Sentry.Maui/SentryMauiAppBuilderExtensions.cs +++ b/src/Sentry.Maui/SentryMauiAppBuilderExtensions.cs @@ -4,7 +4,6 @@ using Microsoft.Extensions.Options; using Microsoft.Maui.LifecycleEvents; using Sentry.Extensibility; -using Sentry.Extensions.Logging; using Sentry.Extensions.Logging.Extensions.DependencyInjection; using Sentry.Maui; using Sentry.Maui.Internal; @@ -54,7 +53,7 @@ public static MauiAppBuilder UseSentry(this MauiAppBuilder builder, } services.AddLogging(); - services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); services.AddSingleton, SentryMauiOptionsSetup>(); services.AddSingleton(); From dd87865ccef228d80e233911f2bd831127e4fe19 Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Wed, 14 Jun 2023 18:54:33 -0700 Subject: [PATCH 073/142] Update Sentry.sln.DotSettings (#2426) --- Sentry.sln.DotSettings | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Sentry.sln.DotSettings b/Sentry.sln.DotSettings index d6301f56f5..95f44cab85 100644 --- a/Sentry.sln.DotSettings +++ b/Sentry.sln.DotSettings @@ -1,3 +1,5 @@  ExplicitlyExcluded - True \ No newline at end of file + True + 2.1.7 + From 7138ffb71c3cfc291cb761c8ee49625dd49753fa Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 14 Jun 2023 21:09:19 -0700 Subject: [PATCH 074/142] chore: update scripts/update-cli.ps1 to 2.19.0 (#2428) 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 4dc77fe307..68f0becb65 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,12 @@ - Fix Sentry logger options for MAUI and Azure Functions ([#2423](https://github.com/getsentry/sentry-dotnet/pull/2423)) +### Dependencies + +- Bump CLI from v2.18.1 to v2.19.0 ([#2428](https://github.com/getsentry/sentry-dotnet/pull/2428)) + - [changelog](https://github.com/getsentry/sentry-cli/blob/master/CHANGELOG.md#2190) + - [diff](https://github.com/getsentry/sentry-cli/compare/2.18.1...2.19.0) + ## 3.33.1 ### Fixes diff --git a/Directory.Build.props b/Directory.Build.props index 01d26d4ab8..35ad2e8e5b 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -100,7 +100,7 @@ - 2.18.1 + 2.19.0 $(MSBuildThisFileDirectory)tools\sentry-cli\$(SentryCLIVersion)\ diff --git a/src/Sentry/Sentry.csproj b/src/Sentry/Sentry.csproj index d36cee6003..702ad69fab 100644 --- a/src/Sentry/Sentry.csproj +++ b/src/Sentry/Sentry.csproj @@ -95,25 +95,25 @@ + Include="sentry-cli-Darwin-arm64" FileHash="8dabeae65f888a1c862162d9e5b572853105294e39452ed93e095b9a0cd60b96" /> + Include="sentry-cli-Darwin-x86_64" FileHash="fb07594121dbe019e670838b4b8a937d32c0320631ffa5a6eb1a063ee10b9c97" /> + Include="sentry-cli-Linux-aarch64" FileHash="b958399bafe6e5f74a4064a1d11d9225bfcc228643422fb5a2e1183bf36baeb7" /> + Include="sentry-cli-Linux-i686" FileHash="67eb8b2cbef84cc307371eaeb95e144f6ea106eda3e1e9016092b873935d8932" /> + Include="sentry-cli-Linux-x86_64" FileHash="83a9e51d8c164edc1e1180a5c7c10a3d653e02beb2db417927be73b4e43afa56" /> + Include="sentry-cli-Windows-i686.exe" FileHash="958923439229452a96bfaa7248e4f7bf1f53ac7fa9325796ee846aab04936217" /> + Include="sentry-cli-Windows-x86_64.exe" FileHash="d21af130ced7f1fd135c22eb5b92d4e9f74d0a52fac984019d042fd625d7c103" /> From 97a5214b37023e0e264833287473ea83dd42b8d2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 14 Jun 2023 21:46:47 -0700 Subject: [PATCH 075/142] chore: update modules/sentry-cocoa to 8.7.4 (#2427) Co-authored-by: GitHub Co-authored-by: Matt Johnson-Pint --- CHANGELOG.md | 3 +++ modules/sentry-cocoa | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 68f0becb65..c9f6e18df1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,9 @@ ### Dependencies +- Bump Cocoa SDK from v8.7.3 to v8.7.4 ([#2427](https://github.com/getsentry/sentry-dotnet/pull/2427)) + - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#874) + - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.7.3...8.7.4) - Bump CLI from v2.18.1 to v2.19.0 ([#2428](https://github.com/getsentry/sentry-dotnet/pull/2428)) - [changelog](https://github.com/getsentry/sentry-cli/blob/master/CHANGELOG.md#2190) - [diff](https://github.com/getsentry/sentry-cli/compare/2.18.1...2.19.0) diff --git a/modules/sentry-cocoa b/modules/sentry-cocoa index 9cf7d2e514..584a5fc997 160000 --- a/modules/sentry-cocoa +++ b/modules/sentry-cocoa @@ -1 +1 @@ -Subproject commit 9cf7d2e514af1600cc2b3c5592e2848c6c5a76d6 +Subproject commit 584a5fc997172c843f2c341d02dd520b944cda41 From 3ce234a1693262433d68bca49eb16a2df3032f36 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 15 Jun 2023 20:51:45 -0700 Subject: [PATCH 076/142] chore: update scripts/update-java.ps1 to 6.23.0 (#2429) 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 c9f6e18df1..67481eb9b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,9 @@ - Bump CLI from v2.18.1 to v2.19.0 ([#2428](https://github.com/getsentry/sentry-dotnet/pull/2428)) - [changelog](https://github.com/getsentry/sentry-cli/blob/master/CHANGELOG.md#2190) - [diff](https://github.com/getsentry/sentry-cli/compare/2.18.1...2.19.0) +- Bump Java SDK from v6.22.0 to v6.23.0 ([#2429](https://github.com/getsentry/sentry-dotnet/pull/2429)) + - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#6230) + - [diff](https://github.com/getsentry/sentry-java/compare/6.22.0...6.23.0) ## 3.33.1 diff --git a/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj b/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj index a85b44b7d1..2da2af2bae 100644 --- a/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj +++ b/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj @@ -3,7 +3,7 @@ net6.0-android $(NoWarn);BG8605;BG8606 - 6.22.0 + 6.23.0 $(BaseIntermediateOutputPath)sdks\Sentry\Android\$(SentryAndroidSdkVersion)\ .NET Bindings for the Sentry Android SDK From 12e0acbd0ea9effd7f2fbeb0badd65ee548f9c7a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 15 Jun 2023 21:24:55 -0700 Subject: [PATCH 077/142] chore: update scripts/update-cli.ps1 to 2.19.1 (#2431) Co-authored-by: GitHub Co-authored-by: Matt Johnson-Pint --- 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 67481eb9b8..3ba581a9d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,9 +11,9 @@ - Bump Cocoa SDK from v8.7.3 to v8.7.4 ([#2427](https://github.com/getsentry/sentry-dotnet/pull/2427)) - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#874) - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.7.3...8.7.4) -- Bump CLI from v2.18.1 to v2.19.0 ([#2428](https://github.com/getsentry/sentry-dotnet/pull/2428)) - - [changelog](https://github.com/getsentry/sentry-cli/blob/master/CHANGELOG.md#2190) - - [diff](https://github.com/getsentry/sentry-cli/compare/2.18.1...2.19.0) +- Bump CLI from v2.18.1 to v2.19.1 ([#2428](https://github.com/getsentry/sentry-dotnet/pull/2428), [#2431](https://github.com/getsentry/sentry-dotnet/pull/2431)) + - [changelog](https://github.com/getsentry/sentry-cli/blob/master/CHANGELOG.md#2191) + - [diff](https://github.com/getsentry/sentry-cli/compare/2.18.1...2.19.1) - Bump Java SDK from v6.22.0 to v6.23.0 ([#2429](https://github.com/getsentry/sentry-dotnet/pull/2429)) - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#6230) - [diff](https://github.com/getsentry/sentry-java/compare/6.22.0...6.23.0) diff --git a/Directory.Build.props b/Directory.Build.props index 35ad2e8e5b..d1c95eabdd 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -100,7 +100,7 @@ - 2.19.0 + 2.19.1 $(MSBuildThisFileDirectory)tools\sentry-cli\$(SentryCLIVersion)\ diff --git a/src/Sentry/Sentry.csproj b/src/Sentry/Sentry.csproj index 702ad69fab..7a2a04a6bd 100644 --- a/src/Sentry/Sentry.csproj +++ b/src/Sentry/Sentry.csproj @@ -95,25 +95,25 @@ + Include="sentry-cli-Darwin-arm64" FileHash="46fafa37b53610196bb93d46a53ee07d74f7f3348259a6bd6dabb86c57e8429f" /> + Include="sentry-cli-Darwin-x86_64" FileHash="947701ac572a0c566bd00d3c655f11b3d8a139b281e40a5d344e15fad4dec018" /> + Include="sentry-cli-Linux-aarch64" FileHash="3b3188f52ac9f132db60ec59d896f475d9914d0af7da0b8236af12aac4097007" /> + Include="sentry-cli-Linux-i686" FileHash="0e120b96dc565eb6d770809d7a21be4f5bce30ef432e482403e50aa24ffa7327" /> + Include="sentry-cli-Linux-x86_64" FileHash="88277b96230fd6d0257aaf373d4108abee6882e67fca012721a0cd5de7c63b5c" /> + Include="sentry-cli-Windows-i686.exe" FileHash="4c433677c22ce337c768a82a924b95986f1508dae64aab8d60053faad114291d" /> + Include="sentry-cli-Windows-x86_64.exe" FileHash="87d1d8d9b0c99b9cf35a25b1e6fa21ba11af6604fea0b825fa2b175f9a9b36bc" /> From a0b0a3f26ccdc3b8dc724a18828bf456a69549de Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 16 Jun 2023 09:15:02 -0700 Subject: [PATCH 078/142] chore(deps): update Cocoa SDK to v8.8.0 (#2430) * chore: update modules/sentry-cocoa to 8.8.0 * Update build-sentry-cocoa.sh --------- Co-authored-by: GitHub Co-authored-by: Dhiogo Ramos Brustolin Co-authored-by: Dhiogo Brustolin --- CHANGELOG.md | 6 +++--- modules/sentry-cocoa | 2 +- scripts/build-sentry-cocoa.sh | 6 ++++++ 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ba581a9d3..86c9997648 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,9 +8,9 @@ ### Dependencies -- Bump Cocoa SDK from v8.7.3 to v8.7.4 ([#2427](https://github.com/getsentry/sentry-dotnet/pull/2427)) - - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#874) - - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.7.3...8.7.4) +- Bump Cocoa SDK from v8.7.3 to v8.8.0 ([#2427](https://github.com/getsentry/sentry-dotnet/pull/2427), [#2430](https://github.com/getsentry/sentry-dotnet/pull/2430)) + - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#880) + - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.7.3...8.8.0) - Bump CLI from v2.18.1 to v2.19.1 ([#2428](https://github.com/getsentry/sentry-dotnet/pull/2428), [#2431](https://github.com/getsentry/sentry-dotnet/pull/2431)) - [changelog](https://github.com/getsentry/sentry-cli/blob/master/CHANGELOG.md#2191) - [diff](https://github.com/getsentry/sentry-cli/compare/2.18.1...2.19.1) diff --git a/modules/sentry-cocoa b/modules/sentry-cocoa index 584a5fc997..d277532e1c 160000 --- a/modules/sentry-cocoa +++ b/modules/sentry-cocoa @@ -1 +1 @@ -Subproject commit 584a5fc997172c843f2c341d02dd520b944cda41 +Subproject commit d277532e1c8af813981ba01f591b15bbdd735615 diff --git a/scripts/build-sentry-cocoa.sh b/scripts/build-sentry-cocoa.sh index dfaa64aa51..8353ac5e21 100755 --- a/scripts/build-sentry-cocoa.sh +++ b/scripts/build-sentry-cocoa.sh @@ -34,6 +34,12 @@ then echo "---------- Building Sentry Cocoa SDK $VERSION ---------- " rm -rf Carthage + # Delete SentryPrivate and SentrySwiftUI schemes + # we dont want to build them + + rm Sentry.xcodeproj/xcshareddata/xcschemes/SentryPrivate.xcscheme + rm Sentry.xcodeproj/xcshareddata/xcschemes/SentrySwiftUI.xcscheme + # Note - We keep the build output in separate directories so that .NET # bundles iOS with net6.0-ios and Mac Catalyst with net6.0-maccatalyst. # The lack of symlinks in the ios builds, means we should also be able From f2fe486fb3d6859e6c107614b6ecc286babf5c23 Mon Sep 17 00:00:00 2001 From: Bruno Garcia Date: Mon, 19 Jun 2023 16:42:39 -0400 Subject: [PATCH 079/142] update codeowners (#2432) --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 7f64306778..6120d3db82 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1 @@ -* @mattjohnsonpint @bitsandfoxes +* @bitsandfoxes @jamescrosswell @SeanFeldman From a4f221ff2023add4a6854752b2d3bda46438969f Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Tue, 20 Jun 2023 13:27:15 +1200 Subject: [PATCH 080/142] Symbolication for Single File Apps (#2425) Added support to capture Symbolication for Single File Apps targeting net5.0 and later --- CHANGELOG.md | 4 + GlobalUsings.cs | 3 +- Sentry.sln | 29 +-- src/Sentry/Internal/DebugStackTrace.cs | 120 ++++--------- .../Internal/Extensions/PEReaderExtensions.cs | 123 +++++++++++++ src/Sentry/Internal/ILSpy/ATTRIBUTION.txt | 25 +++ src/Sentry/Internal/ILSpy/SingleFileApp.cs | 150 ++++++++++++++++ src/Sentry/Internal/ILSpy/SingleFileBundle.cs | 170 ++++++++++++++++++ src/Sentry/Sentry.csproj | 1 + test/Sentry.Testing/StringExtensions.cs | 9 + .../Internals/ILSpy/SingleFileAppTests.cs | 105 +++++++++++ test/SingleFileTestApp/Directory.Build.props | 3 + test/SingleFileTestApp/Program.cs | 6 + .../SingleFileTestApp.csproj | 24 +++ 14 files changed, 678 insertions(+), 94 deletions(-) create mode 100644 src/Sentry/Internal/Extensions/PEReaderExtensions.cs create mode 100644 src/Sentry/Internal/ILSpy/ATTRIBUTION.txt create mode 100644 src/Sentry/Internal/ILSpy/SingleFileApp.cs create mode 100644 src/Sentry/Internal/ILSpy/SingleFileBundle.cs create mode 100644 test/Sentry.Testing/StringExtensions.cs create mode 100644 test/Sentry.Tests/Internals/ILSpy/SingleFileAppTests.cs create mode 100644 test/SingleFileTestApp/Directory.Build.props create mode 100644 test/SingleFileTestApp/Program.cs create mode 100644 test/SingleFileTestApp/SingleFileTestApp.csproj diff --git a/CHANGELOG.md b/CHANGELOG.md index 86c9997648..bf0c7d75fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,10 @@ - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#6230) - [diff](https://github.com/getsentry/sentry-java/compare/6.22.0...6.23.0) +### Features + +- Symbolication for Single File Apps ([#2425](https://github.com/getsentry/sentry-dotnet/pull/2425)) + ## 3.33.1 ### Fixes diff --git a/GlobalUsings.cs b/GlobalUsings.cs index 17601d29a6..297e6f2656 100644 --- a/GlobalUsings.cs +++ b/GlobalUsings.cs @@ -10,6 +10,7 @@ global using System.Diagnostics.CodeAnalysis; global using System.Globalization; global using System.IO.Compression; +global using System.IO.MemoryMappedFiles; global using System.Net; global using System.Net.Http.Headers; global using System.Net.NetworkInformation; @@ -22,8 +23,8 @@ global using System.Runtime.Serialization; global using System.Security; global using System.Security.Authentication; -global using System.Security.Principal; global using System.Security.Claims; global using System.Security.Cryptography; +global using System.Security.Principal; global using System.Text; global using System.Text.RegularExpressions; diff --git a/Sentry.sln b/Sentry.sln index a5d904fe89..1f8262bba0 100644 --- a/Sentry.sln +++ b/Sentry.sln @@ -28,9 +28,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".root", ".root", "{9D7E2F87 CONTRIBUTING.md = CONTRIBUTING.md Directory.Build.props = Directory.Build.props Directory.Build.targets = Directory.Build.targets - README.md = README.md global.json = global.json GlobalUsings.cs = GlobalUsings.cs + README.md = README.md EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{77454495-55EE-4B40-A089-71B9E8F82E89}" @@ -134,19 +134,19 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.DiagnosticSource", " EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.DiagnosticSource.Tests", "test\Sentry.DiagnosticSource.Tests\Sentry.DiagnosticSource.Tests.csproj", "{D870B028-16ED-4551-8B0F-5529479D04C9}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.DiagnosticSource.IntegrationTests", "test\Sentry.DiagnosticSource.IntegrationTests\Sentry.DiagnosticSource.IntegrationTests.csproj", "{F8120B9C-D4CA-43DA-B5E1-1CFBA7C36E3B}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.DiagnosticSource.IntegrationTests", "test\Sentry.DiagnosticSource.IntegrationTests\Sentry.DiagnosticSource.IntegrationTests.csproj", "{F8120B9C-D4CA-43DA-B5E1-1CFBA7C36E3B}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.Maui", "src\Sentry.Maui\Sentry.Maui.csproj", "{FFFC74C5-680B-43E3-9C42-A7A23B589CB6}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.Maui.Tests", "test\Sentry.Maui.Tests\Sentry.Maui.Tests.csproj", "{143076C0-8D6B-4054-9F45-06B21655F417}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Testing.CrashableApp", "test\Sentry.Testing.CrashableApp\Sentry.Testing.CrashableApp.csproj", "{DA3CECBB-83BE-441A-852C-077809C48307}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.Testing.CrashableApp", "test\Sentry.Testing.CrashableApp\Sentry.Testing.CrashableApp.csproj", "{DA3CECBB-83BE-441A-852C-077809C48307}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Samples.AspNetCore.Blazor.Server", "samples\Sentry.Samples.AspNetCore.Blazor.Server\Sentry.Samples.AspNetCore.Blazor.Server.csproj", "{70066C6C-0A18-4322-A02A-9A0DFE59C02B}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.Samples.AspNetCore.Blazor.Server", "samples\Sentry.Samples.AspNetCore.Blazor.Server\Sentry.Samples.AspNetCore.Blazor.Server.csproj", "{70066C6C-0A18-4322-A02A-9A0DFE59C02B}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.AspNetCore.TestUtils", "test\Sentry.AspNetCore.TestUtils\Sentry.AspNetCore.TestUtils.csproj", "{C96CB65D-3D2D-404E-85C0-69A3FC03B48F}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.AspNetCore.TestUtils", "test\Sentry.AspNetCore.TestUtils\Sentry.AspNetCore.TestUtils.csproj", "{C96CB65D-3D2D-404E-85C0-69A3FC03B48F}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Android.AssemblyReader", "src\Sentry.Android.AssemblyReader\Sentry.Android.AssemblyReader.csproj", "{ED5E4F7E-8267-4F3C-BD2A-779AC9BF3D7C}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.Android.AssemblyReader", "src\Sentry.Android.AssemblyReader\Sentry.Android.AssemblyReader.csproj", "{ED5E4F7E-8267-4F3C-BD2A-779AC9BF3D7C}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.Profiling", "src\Sentry.Profiling\Sentry.Profiling.csproj", "{BD6CEF44-E05E-4C22-8D2F-0558A93DD2D6}" EndProject @@ -154,11 +154,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.Profiling.Tests", "t EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.Samples.Console.Profiling", "samples\Sentry.Samples.Console.Profiling\Sentry.Samples.Console.Profiling.csproj", "{0F84C0BB-FDD4-43A9-B594-923EB10C8E3F}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.AzureFunctions.Worker", "src\Sentry.AzureFunctions.Worker\Sentry.AzureFunctions.Worker.csproj", "{203C4556-16DE-46CE-9FAF-C93D8B30D04A}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.AzureFunctions.Worker", "src\Sentry.AzureFunctions.Worker\Sentry.AzureFunctions.Worker.csproj", "{203C4556-16DE-46CE-9FAF-C93D8B30D04A}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Samples.AzureFunctions.Worker", "samples\Sentry.Samples.AzureFunctions.Worker\Sentry.Samples.AzureFunctions.Worker.csproj", "{243B75FF-1501-4DB7-B933-EE43D960EB90}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.Samples.AzureFunctions.Worker", "samples\Sentry.Samples.AzureFunctions.Worker\Sentry.Samples.AzureFunctions.Worker.csproj", "{243B75FF-1501-4DB7-B933-EE43D960EB90}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.AzureFunctions.Worker.Tests", "test\Sentry.AzureFunctions.Worker.Tests\Sentry.AzureFunctions.Worker.Tests.csproj", "{8639DB06-1F74-4890-8974-613305C1B6C5}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.AzureFunctions.Worker.Tests", "test\Sentry.AzureFunctions.Worker.Tests\Sentry.AzureFunctions.Worker.Tests.csproj", "{8639DB06-1F74-4890-8974-613305C1B6C5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SingleFileTestApp", "test\SingleFileTestApp\SingleFileTestApp.csproj", "{08C99C2F-08D8-44A3-981F-768DC84DCEC7}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -334,10 +336,10 @@ Global {143076C0-8D6B-4054-9F45-06B21655F417}.Debug|Any CPU.Build.0 = Debug|Any CPU {143076C0-8D6B-4054-9F45-06B21655F417}.Release|Any CPU.ActiveCfg = Release|Any CPU {143076C0-8D6B-4054-9F45-06B21655F417}.Release|Any CPU.Build.0 = Release|Any CPU - {DA3CECBB-83BE-441A-852C-077809C48307}.Release|Any CPU.ActiveCfg = Release|Any CPU - {DA3CECBB-83BE-441A-852C-077809C48307}.Release|Any CPU.Build.0 = Release|Any CPU {DA3CECBB-83BE-441A-852C-077809C48307}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DA3CECBB-83BE-441A-852C-077809C48307}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DA3CECBB-83BE-441A-852C-077809C48307}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DA3CECBB-83BE-441A-852C-077809C48307}.Release|Any CPU.Build.0 = Release|Any CPU {70066C6C-0A18-4322-A02A-9A0DFE59C02B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {70066C6C-0A18-4322-A02A-9A0DFE59C02B}.Debug|Any CPU.Build.0 = Debug|Any CPU {70066C6C-0A18-4322-A02A-9A0DFE59C02B}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -374,6 +376,10 @@ Global {8639DB06-1F74-4890-8974-613305C1B6C5}.Debug|Any CPU.Build.0 = Debug|Any CPU {8639DB06-1F74-4890-8974-613305C1B6C5}.Release|Any CPU.ActiveCfg = Release|Any CPU {8639DB06-1F74-4890-8974-613305C1B6C5}.Release|Any CPU.Build.0 = Release|Any CPU + {08C99C2F-08D8-44A3-981F-768DC84DCEC7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {08C99C2F-08D8-44A3-981F-768DC84DCEC7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {08C99C2F-08D8-44A3-981F-768DC84DCEC7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {08C99C2F-08D8-44A3-981F-768DC84DCEC7}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -434,6 +440,7 @@ Global {203C4556-16DE-46CE-9FAF-C93D8B30D04A} = {AF6AF4C7-8AA2-4D59-8064-2D79560904EB} {243B75FF-1501-4DB7-B933-EE43D960EB90} = {77454495-55EE-4B40-A089-71B9E8F82E89} {8639DB06-1F74-4890-8974-613305C1B6C5} = {83263231-1A2A-4733-B759-EEFF14E8C5D5} + {08C99C2F-08D8-44A3-981F-768DC84DCEC7} = {83263231-1A2A-4733-B759-EEFF14E8C5D5} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {0C652B1A-DF72-4EE5-A98B-194FE2C054F6} diff --git a/src/Sentry/Internal/DebugStackTrace.cs b/src/Sentry/Internal/DebugStackTrace.cs index c6301933ad..0e1517fe20 100644 --- a/src/Sentry/Internal/DebugStackTrace.cs +++ b/src/Sentry/Internal/DebugStackTrace.cs @@ -1,5 +1,6 @@ using Sentry.Internal.Extensions; using Sentry.Extensibility; +using Sentry.Internal.ILSpy; namespace Sentry.Internal; @@ -377,14 +378,23 @@ private static void DemangleLambdaReturnType(SentryStackFrame frame) } } - private static PEReader? TryReadAssembly(string assemblyName, SentryOptions options) + private static PEReader? TryReadAssemblyFromDisk(Module module, SentryOptions options, out string? assemblyName) { + assemblyName = module.FullyQualifiedName; if (options.AssemblyReader is { } reader) { return reader.Invoke(assemblyName); } - return File.Exists(assemblyName) ? new PEReader(File.OpenRead(assemblyName)) : null; + try + { + var assembly = File.OpenRead(assemblyName); + return new PEReader(assembly); + } + catch (Exception) + { + return null; + } } private int? AddDebugImage(Module module) @@ -412,94 +422,40 @@ private static void DemangleLambdaReturnType(SentryStackFrame frame) internal static DebugImage? GetDebugImage(Module module, SentryOptions options) { - var assemblyName = module.FullyQualifiedName; - using var peReader = TryReadAssembly(assemblyName, options); - if (peReader is null) - { - options.LogDebug("Skipping debug image for module '{0}' because assembly wasn't found: '{1}'", - module.Name, assemblyName); - return null; - } - - var headers = peReader.PEHeaders; - var codeId = headers.PEHeader is { } peHeader - ? $"{headers.CoffHeader.TimeDateStamp:X8}{peHeader.SizeOfImage:x}" - : null; - - string? debugId = null; - string? debugFile = null; - string? debugChecksum = null; - - var debugDirectoryEntries = peReader.ReadDebugDirectory(); - - foreach (var entry in debugDirectoryEntries) + // Try to get it from disk (most common use case) + var moduleName = module.GetNameOrScopeName(); + using var peDiskReader = TryReadAssemblyFromDisk(module, options, out var assemblyName); + if (peDiskReader is not null) { - switch (entry.Type) - { - case DebugDirectoryEntryType.PdbChecksum: - { - var checksum = peReader.ReadPdbChecksumDebugDirectoryData(entry); - var checksumHex = checksum.Checksum.AsSpan().ToHexString(); - debugChecksum = $"{checksum.AlgorithmName}:{checksumHex}"; - break; - } - - case DebugDirectoryEntryType.CodeView: - { - var codeView = peReader.ReadCodeViewDebugDirectoryData(entry); - debugFile = codeView.Path; - - // Specification: - // https://github.com/dotnet/runtime/blob/main/docs/design/specs/PE-COFF.md#codeview-debug-directory-entry-type-2 - // - // See also: - // https://learn.microsoft.com/dotnet/csharp/language-reference/compiler-options/code-generation#debugtype - // - // Note: Matching PDB ID is stored in the #Pdb stream of the .pdb file. - - if (entry.IsPortableCodeView) - { - // Portable PDB Format - // Version Major=any, Minor=0x504d - debugId = $"{codeView.Guid}-{entry.Stamp:x8}"; - } - else - { - // Full PDB Format (Windows only) - // Version Major=0, Minor=0 - debugId = $"{codeView.Guid}-{codeView.Age}"; - } - - break; - } - } - - if (debugId != null && debugChecksum != null) + if (peDiskReader.TryGetPEDebugImageData().ToDebugImage(assemblyName, module.ModuleVersionId) is not { } debugImage) { - // No need to keep looking, once we have both. - break; + options.LogInfo("Skipping debug image for module '{0}' because the Debug ID couldn't be determined", moduleName); + return null; } - } - if (debugId == null) - { - options.LogInfo("Skipping debug image for module '{0}' because the Debug ID couldn't be determined", module.Name); - return null; + options.LogDebug("Got debug image for '{0}' having Debug ID: {1}", moduleName, debugImage.DebugId); + return debugImage; } - var debugImage = new DebugImage +#if NET5_0_OR_GREATER && PLATFORM_NEUTRAL + // Maybe we're dealing with a single file assembly + // https://github.com/getsentry/sentry-dotnet/issues/2362 + if (SingleFileApp.MainModule.IsBundle()) { - Type = "pe_dotnet", - CodeId = codeId, - CodeFile = assemblyName, - DebugId = debugId, - DebugChecksum = debugChecksum, - DebugFile = debugFile, - ModuleVersionId = module.ModuleVersionId, - }; + if (SingleFileApp.MainModule?.GetDebugImage(module) is not { } embeddedDebugImage) + { + options.LogInfo("Skipping embedded debug image for module '{0}' because the Debug ID couldn't be determined", moduleName); + return null; + } - options.LogDebug("Got debug image for '{0}' having Debug ID: {1}", module.Name, debugId); + options.LogDebug("Got embedded debug image for '{0}' having Debug ID: {1}", moduleName, embeddedDebugImage.DebugId); + return embeddedDebugImage; + } +#endif - return debugImage; + // Finally, admit defeat + options.LogDebug("Skipping debug image for module '{0}' because assembly wasn't found: '{1}'", + moduleName, assemblyName); + return null; } } diff --git a/src/Sentry/Internal/Extensions/PEReaderExtensions.cs b/src/Sentry/Internal/Extensions/PEReaderExtensions.cs new file mode 100644 index 0000000000..ae612ac6d7 --- /dev/null +++ b/src/Sentry/Internal/Extensions/PEReaderExtensions.cs @@ -0,0 +1,123 @@ +using Sentry.Extensibility; + +namespace Sentry.Internal.Extensions; + +internal static class PEReaderExtensions +{ + public static PEDebugImageData? TryGetPEDebugImageData(this PEReader peReader) + { + try + { + return peReader.GetPEDebugImageData(); + } + catch + { + return null; + } + } + + private static PEDebugImageData GetPEDebugImageData(this PEReader peReader) + { + var headers = peReader.PEHeaders; + var codeId = headers.PEHeader is { } peHeader + ? $"{headers.CoffHeader.TimeDateStamp:X8}{peHeader.SizeOfImage:x}" + : null; + + string? debugId = null; + string? debugFile = null; + string? debugChecksum = null; + + var debugDirectoryEntries = peReader.ReadDebugDirectory(); + + foreach (var entry in debugDirectoryEntries) + { + switch (entry.Type) + { + case DebugDirectoryEntryType.PdbChecksum: + { + var checksum = peReader.ReadPdbChecksumDebugDirectoryData(entry); + var checksumHex = checksum.Checksum.AsSpan().ToHexString(); + debugChecksum = $"{checksum.AlgorithmName}:{checksumHex}"; + break; + } + + case DebugDirectoryEntryType.CodeView: + { + var codeView = peReader.ReadCodeViewDebugDirectoryData(entry); + debugFile = codeView.Path; + + // Specification: + // https://github.com/dotnet/runtime/blob/main/docs/design/specs/PE-COFF.md#codeview-debug-directory-entry-type-2 + // + // See also: + // https://learn.microsoft.com/dotnet/csharp/language-reference/compiler-options/code-generation#debugtype + // + // Note: Matching PDB ID is stored in the #Pdb stream of the .pdb file. + + if (entry.IsPortableCodeView) + { + // Portable PDB Format + // Version Major=any, Minor=0x504d + debugId = $"{codeView.Guid}-{entry.Stamp:x8}"; + } + else + { + // Full PDB Format (Windows only) + // Version Major=0, Minor=0 + debugId = $"{codeView.Guid}-{codeView.Age}"; + } + + break; + } + } + + if (debugId != null && debugChecksum != null) + { + // No need to keep looking, once we have both. + break; + } + } + + return new PEDebugImageData + { + CodeId = codeId, + DebugId = debugId, + DebugChecksum = debugChecksum, + DebugFile = debugFile + }; + } +} + +/// +/// The subset of information about a DebugImage that we can obtain from a PE file. +/// This rest of the information is obtained from the Module. +/// +internal sealed class PEDebugImageData +{ + public string Type => "pe_dotnet"; + public string? ImageAddress { get; set; } + public long? ImageSize { get; set; } + public string? DebugId { get; set; } + public string? DebugChecksum { get; set; } + public string? DebugFile { get; set; } + public string? CodeId { get; set; } +} + +internal static class PEDebugImageDataExtensions +{ + internal static DebugImage? ToDebugImage(this PEDebugImageData? imageData, string? codeFile, Guid? moduleVersionId) + { + return imageData?.DebugId == null + ? null + : new DebugImage + { + Type = imageData.Type, + CodeId = imageData.CodeId, + CodeFile = codeFile, + DebugId = imageData.DebugId, + DebugChecksum = imageData.DebugChecksum, + DebugFile = imageData.DebugFile, + ModuleVersionId = moduleVersionId + }; + } +} diff --git a/src/Sentry/Internal/ILSpy/ATTRIBUTION.txt b/src/Sentry/Internal/ILSpy/ATTRIBUTION.txt new file mode 100644 index 0000000000..5b95d3d097 --- /dev/null +++ b/src/Sentry/Internal/ILSpy/ATTRIBUTION.txt @@ -0,0 +1,25 @@ +Parts of the code in this subdirectory have been adapted from +https://github.com/icsharpcode/ILSpy + +The original license is as follows: + + +MIT license + +Copyright (c) 2011-2023 AlphaSierraPapa for the ILSpy team + +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/Internal/ILSpy/SingleFileApp.cs b/src/Sentry/Internal/ILSpy/SingleFileApp.cs new file mode 100644 index 0000000000..e73190ca42 --- /dev/null +++ b/src/Sentry/Internal/ILSpy/SingleFileApp.cs @@ -0,0 +1,150 @@ +using Sentry.Extensibility; +using Sentry.Internal.Extensions; + +namespace Sentry.Internal.ILSpy; + +#if NET5_0_OR_GREATER && PLATFORM_NEUTRAL + +internal sealed class SingleFileApp +{ + private SingleFileApp(SingleFileBundle.Header manifest, BundleEntries entries) + { + BundleHeader = manifest; + _entries = new ConcurrentDictionary(entries.ToDictionary( + e => e.Key, + e => e.Value + ), StringComparer.OrdinalIgnoreCase); + } + + private static readonly Lazy LazyMainModule = new(FromMainModule, LazyThreadSafetyMode.ExecutionAndPublication); + public static SingleFileApp? MainModule => LazyMainModule.Value; + + public SingleFileBundle.Header BundleHeader { get; } + + private readonly ConcurrentDictionary _entries; + + public DebugImage? GetDebugImage(Module module) + { + return (_entries.TryGetValue(module.ScopeName, out var debugImageData)) + ? debugImageData?.ToDebugImage(module.ScopeName, module.ModuleVersionId) + : null; + } + + /// + /// Adapted from the LoadedPackage.FromBundle method in ILSpy: + /// https://github.com/icsharpcode/ILSpy/blob/311658c7109c3872e020cba2525b1b3a371d5813/ICSharpCode.ILSpyX/LoadedPackage.cs#L111 + /// commit a929fcb5202824e3c061f4824c7fc9ba867d55af + /// + /// Load a .NET single-file bundle. + /// + internal static SingleFileApp? FromFile(string fileName) + { + try + { + // Load the file into memory, check if it's a SingleFileBundle, extract the manifest and entries if so + using var memoryMappedFile = MemoryMappedFile.CreateFromFile(fileName, FileMode.Open, null, 0, MemoryMappedFileAccess.Read); + var view = memoryMappedFile.CreateViewAccessor(0, 0, MemoryMappedFileAccess.Read); + try + { + if (!SingleFileBundle.IsBundle(view, out var bundleHeaderOffset)) + { + return null; + } + + var manifest = SingleFileBundle.ReadManifest(view, bundleHeaderOffset); + var entries = new BundleEntries(); + foreach (var entry in manifest.Entries) + { + using var stream = TryOpenEntryStream(entry, view); + using var peReader = new PEReader(stream, PEStreamOptions.PrefetchEntireImage); + if (peReader.TryGetPEDebugImageData() is { } debugImageData) + { + entries.Add(entry.RelativePath, debugImageData); + } + } + var result = new SingleFileApp(manifest, entries); + return result; + } + finally + { + view?.Dispose(); + } + } + catch (Exception ex) + { + SentrySdk.CurrentOptions?.LogDebug("Error loading Module from bundle {0}: {1}", fileName, ex.Message); + return null; + } + } + + private static SingleFileApp? FromMainModule() + { + // Get the current process + var currentProcess = Process.GetCurrentProcess(); + + // Access the main module of the process + var mainModule = currentProcess.MainModule; + + // Retrieve the path and filename of the main module + if (mainModule?.FileName is not { } fileName) + { + Debug.WriteLine("Could not get main module file name."); + return null; + } + + return FromFile(fileName); + } + + /// + /// Adapted from BundleEntryDebugData.TryOpenStream in ILSpy: + /// https://github.com/icsharpcode/ILSpy/blob/311658c7109c3872e020cba2525b1b3a371d5813/ICSharpCode.ILSpyX/LoadedPackage.cs#L208 + /// commit a929fcb5202824e3c061f4824c7fc9ba867d55af + /// + /// + private static Stream TryOpenEntryStream(SingleFileBundle.Entry entry, MemoryMappedViewAccessor view) + { + if (entry.CompressedSize == 0) + { + return new UnmanagedMemoryStream(view.SafeMemoryMappedViewHandle, entry.Offset, entry.Size); + } + else + { + Stream compressedStream = new UnmanagedMemoryStream(view.SafeMemoryMappedViewHandle, entry.Offset, entry.CompressedSize); + using var deflateStream = new DeflateStream(compressedStream, CompressionMode.Decompress); + Stream decompressedStream = new MemoryStream((int)entry.Size); + deflateStream.CopyTo(decompressedStream); + if (decompressedStream.Length != entry.Size) + { + throw new InvalidDataException($"Corrupted single-file entry '{entry.RelativePath}'. Declared decompressed size '{entry.Size}' is not the same as actual decompressed size '{decompressedStream.Length}'."); + } + + decompressedStream.Seek(0, SeekOrigin.Begin); + return decompressedStream; + } + } + + private sealed class BundleEntries: List> + { + } +} + +internal static class SingleFileAppExtensions +{ + internal static bool IsBundle(this SingleFileApp? singleFileApp) => singleFileApp is not null; +} + +#endif + +internal static class ModuleExtensions +{ + /// + /// The Module.Name for Modules that are embedded in SingleFileApps will be null + /// or <Unknown>, in that case we can use Module.ScopeName instead + /// + /// A Module instance + /// module.Name, if this is available. module.ScopeName otherwise + public static string? GetNameOrScopeName(this Module module) => + (module?.Name is null || module.Name.Equals("")) + ? module?.ScopeName + : module?.Name; +} diff --git a/src/Sentry/Internal/ILSpy/SingleFileBundle.cs b/src/Sentry/Internal/ILSpy/SingleFileBundle.cs new file mode 100644 index 0000000000..eace8d7f1e --- /dev/null +++ b/src/Sentry/Internal/ILSpy/SingleFileBundle.cs @@ -0,0 +1,170 @@ +// Adapted from the SingleFileBundle class in ILSpy: +// https://github.com/icsharpcode/ILSpy/blob/311658c7109c3872e020cba2525b1b3a371d5813/ICSharpCode.Decompiler/SingleFileBundle.cs +// commit a929fcb5202824e3c061f4824c7fc9ba867d55af +// +// The only changes are namespaces, conditional compilation directives & minor tweaks to satisfy our linters. + +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Sentry.Internal.ILSpy; + +#if NET5_0_OR_GREATER && PLATFORM_NEUTRAL + +/// +/// Class for dealing with .NET 5 single-file bundles. +/// Based on code from Microsoft.NET.HostModel. +/// +internal static class SingleFileBundle +{ + /// + /// Check if the memory-mapped data is a single-file bundle + /// + public static unsafe bool IsBundle(MemoryMappedViewAccessor view, out long bundleHeaderOffset) + { + var buffer = view.SafeMemoryMappedViewHandle; + byte* ptr = null; + buffer.AcquirePointer(ref ptr); + try + { + return IsBundle(ptr, checked((long)buffer.ByteLength), out bundleHeaderOffset); + } + finally + { + buffer.ReleasePointer(); + } + } + + private static unsafe bool IsBundle(byte* data, long size, out long bundleHeaderOffset) + { + ReadOnlySpan bundleSignature = new byte[] { + // 32 bytes represent the bundle signature: SHA-256 for ".net core bundle" + 0x8b, 0x12, 0x02, 0xb9, 0x6a, 0x61, 0x20, 0x38, + 0x72, 0x7b, 0x93, 0x02, 0x14, 0xd7, 0xa0, 0x32, + 0x13, 0xf5, 0xb9, 0xe6, 0xef, 0xae, 0x33, 0x18, + 0xee, 0x3b, 0x2d, 0xce, 0x24, 0xb3, 0x6a, 0xae + }; + + var end = data + (size - bundleSignature.Length); + for (var ptr = data; ptr < end; ptr++) + { + if (*ptr == 0x8b && bundleSignature.SequenceEqual(new ReadOnlySpan(ptr, bundleSignature.Length))) + { + bundleHeaderOffset = Unsafe.ReadUnaligned(ptr - sizeof(long)); + if (bundleHeaderOffset > 0 && bundleHeaderOffset < size) + { + return true; + } + } + } + + bundleHeaderOffset = 0; + return false; + } + + public struct Header + { + public uint MajorVersion; + public uint MinorVersion; + public int FileCount; + public string BundleID; + + // Fields introduced with v2: + public long DepsJsonOffset; + public long DepsJsonSize; + public long RuntimeConfigJsonOffset; + public long RuntimeConfigJsonSize; + public ulong Flags; + + public ImmutableArray Entries; + } + + /// + /// FileType: Identifies the type of file embedded into the bundle. + /// + /// The bundler differentiates a few kinds of files via the manifest, + /// with respect to the way in which they'll be used by the runtime. + /// + /// + public enum FileType : byte + { + Unknown, // Type not determined. + Assembly, // IL and R2R Assemblies + NativeBinary, // NativeBinaries + DepsJson, // .deps.json configuration file + RuntimeConfigJson, // .runtimeconfig.json configuration file + Symbols // PDB Files + }; + + public struct Entry + { + public long Offset; + public long Size; + public long CompressedSize; // 0 if not compressed, otherwise the compressed size in the bundle + public FileType Type; + public string RelativePath; // Path of an embedded file, relative to the Bundle source-directory. + } + + private static UnmanagedMemoryStream AsStream(MemoryMappedViewAccessor view) + { + var size = checked((long)view.SafeMemoryMappedViewHandle.ByteLength); + return new UnmanagedMemoryStream(view.SafeMemoryMappedViewHandle, 0, size); + } + + /// + /// Reads the manifest header from the memory mapping. + /// + public static Header ReadManifest(MemoryMappedViewAccessor view, long bundleHeaderOffset) + { + using var stream = AsStream(view); + stream.Seek(bundleHeaderOffset, SeekOrigin.Begin); + return ReadManifest(stream); + } + + /// + /// Reads the manifest header from the stream. + /// + private static Header ReadManifest(Stream stream) + { + var header = new Header(); + using var reader = new BinaryReader(stream, Encoding.UTF8, leaveOpen: true); + header.MajorVersion = reader.ReadUInt32(); + header.MinorVersion = reader.ReadUInt32(); + + // Major versions 3, 4 and 5 were skipped to align bundle versioning with .NET versioning scheme + if (header.MajorVersion < 1 || header.MajorVersion > 6) + { + throw new InvalidDataException($"Unsupported manifest version: {header.MajorVersion}.{header.MinorVersion}"); + } + header.FileCount = reader.ReadInt32(); + header.BundleID = reader.ReadString(); + if (header.MajorVersion >= 2) + { + header.DepsJsonOffset = reader.ReadInt64(); + header.DepsJsonSize = reader.ReadInt64(); + header.RuntimeConfigJsonOffset = reader.ReadInt64(); + header.RuntimeConfigJsonSize = reader.ReadInt64(); + header.Flags = reader.ReadUInt64(); + } + var entries = ImmutableArray.CreateBuilder(header.FileCount); + for (var i = 0; i < header.FileCount; i++) + { + entries.Add(ReadEntry(reader, header.MajorVersion)); + } + header.Entries = entries.MoveToImmutable(); + return header; + } + + private static Entry ReadEntry(BinaryReader reader, uint bundleMajorVersion) + { + Entry entry; + entry.Offset = reader.ReadInt64(); + entry.Size = reader.ReadInt64(); + entry.CompressedSize = bundleMajorVersion >= 6 ? reader.ReadInt64() : 0; + entry.Type = (FileType)reader.ReadByte(); + entry.RelativePath = reader.ReadString(); + return entry; + } +} + +#endif diff --git a/src/Sentry/Sentry.csproj b/src/Sentry/Sentry.csproj index 7a2a04a6bd..01dc527ceb 100644 --- a/src/Sentry/Sentry.csproj +++ b/src/Sentry/Sentry.csproj @@ -4,6 +4,7 @@ Official SDK for Sentry - Open-source error tracking that helps developers monitor and fix crashes in real time. $(NoWarn);RS0017 true + true diff --git a/test/Sentry.Testing/StringExtensions.cs b/test/Sentry.Testing/StringExtensions.cs new file mode 100644 index 0000000000..6dbd79ca4d --- /dev/null +++ b/test/Sentry.Testing/StringExtensions.cs @@ -0,0 +1,9 @@ +namespace Sentry.Testing; + +public static class StringExtensions +{ + private static readonly string PathSeparator = Path.DirectorySeparatorChar.ToString(); + + public static string OsAgnostic(this string path) => path.Replace("/", PathSeparator); + public static string TrimLeadingPathSeparator(this string path) => path[..1] == PathSeparator ? path[1..] : path; +} diff --git a/test/Sentry.Tests/Internals/ILSpy/SingleFileAppTests.cs b/test/Sentry.Tests/Internals/ILSpy/SingleFileAppTests.cs new file mode 100644 index 0000000000..a895365b9e --- /dev/null +++ b/test/Sentry.Tests/Internals/ILSpy/SingleFileAppTests.cs @@ -0,0 +1,105 @@ +using Sentry.Internal.ILSpy; + +namespace Sentry.Tests.Internals.ILSpy; + +#if NET5_0_OR_GREATER && PLATFORM_NEUTRAL + +/// +/// Note the tests in this class rely on the SingleFileTestApp having being built. This will be done automatically if +/// SingleFileTestApp is included in the solution that dotnet test is running against. Otherwise, tests are skipped. +/// +public class SingleFileAppTests +{ + private static readonly string ValidBundleFile; + private static readonly string InValidBundleFile; + static SingleFileAppTests() + { + var currentDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!; + var debugConfigMarker = "/bin/Debug/".OsAgnostic(); + var buildConfig = currentDirectory.Contains(debugConfigMarker) ? "Debug" : "Release"; + + // Get the test root + var folderMarker = "/sentry-dotnet/test/".OsAgnostic(); + var cutoff = currentDirectory.IndexOf(folderMarker, StringComparison.Ordinal) + folderMarker.Length; + var testRoot = currentDirectory[..cutoff]; + + // Note that these files will only exist if the SingleFileTestApp has been built. + var validBundle = $"SingleFileTestApp/bin/{buildConfig}/{TargetFramework}/{RuntimeIdentifier}/publish/{SingleFileAppName}".OsAgnostic(); + ValidBundleFile = Path.Combine(testRoot, validBundle); + + var invalidBundle = $"SingleFileTestApp/bin/{buildConfig}/{TargetFramework}/{RuntimeIdentifier}/{SingleFileAppName}".OsAgnostic(); + InValidBundleFile = Path.Combine(testRoot, invalidBundle); + } + +#if NET7_0 + private static string TargetFramework => "net7.0"; +#elif NET6_0 + private static string TargetFramework => "net6.0"; +#elif NET5_0 + 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"); +#endif + + private static string SingleFileAppName => RuntimeInformation.IsOSPlatform(OSPlatform.Windows) + ? "SingleFileTestApp.exe" + : "SingleFileTestApp"; + + private static string RuntimeIdentifier => + RuntimeInformation.OSArchitecture switch + { + Architecture.Arm64 => $"{OsPlatform}-arm64", + Architecture.X64 => $"{OsPlatform}-x64", + Architecture.X86 => $"{OsPlatform}-x86", + _ => throw new Exception("Unknown Architecture") + }; + + private static string OsPlatform => + RuntimeInformation.IsOSPlatform(OSPlatform.Windows) + ? "win" + : RuntimeInformation.IsOSPlatform(OSPlatform.Linux) + ? "linux" + : RuntimeInformation.IsOSPlatform(OSPlatform.OSX) + ? "osx" + : throw new Exception("Unknown OS"); + + [SkippableFact] + public void ValidBundleFile_KnownModule_Returns_DebugInfo() + { + Skip.If(!File.Exists(ValidBundleFile)); + + // Act + var singleFileApp = SingleFileApp.FromFile(ValidBundleFile); + + // Assert + singleFileApp.Should().NotBeNull(); + + var knownTypeModule = typeof(int).Module; + var debugImage = singleFileApp!.GetDebugImage(knownTypeModule); + debugImage.Should().NotBeNull(); + using (new AssertionScope()) + { + debugImage!.CodeFile.Should().NotBeNull(); + debugImage.CodeId.Should().NotBeNull(); + debugImage.DebugId.Should().NotBeNull(); + debugImage.DebugFile.Should().NotBeNull(); + debugImage.Type.Should().NotBeNull(); + debugImage.ModuleVersionId.Should().NotBeNull(); + } + } + + [SkippableFact] + public void InvalidBundleFile_ReturnsNull() + { + Skip.If(!File.Exists(InValidBundleFile)); + + // Act + var singleFileApp = SingleFileApp.FromFile(InValidBundleFile); + + // Assert + Assert.Null(singleFileApp); + } +} + +#endif diff --git a/test/SingleFileTestApp/Directory.Build.props b/test/SingleFileTestApp/Directory.Build.props new file mode 100644 index 0000000000..e49e8345c3 --- /dev/null +++ b/test/SingleFileTestApp/Directory.Build.props @@ -0,0 +1,3 @@ + + + diff --git a/test/SingleFileTestApp/Program.cs b/test/SingleFileTestApp/Program.cs new file mode 100644 index 0000000000..7ace1941e2 --- /dev/null +++ b/test/SingleFileTestApp/Program.cs @@ -0,0 +1,6 @@ +// This can be used to test the functionality of Sentry functionality in a single-file app. +// +// The Directory.Build.props file for this project sets DefaultTargets="Build;Publish", so this project automatically +// gets published whenever it's built using the dotnet build or dotnet test commands. A RuntimeIdentifier is required +// for publishing, which gets set in the csproj file. +Console.WriteLine("Hello, World!"); diff --git a/test/SingleFileTestApp/SingleFileTestApp.csproj b/test/SingleFileTestApp/SingleFileTestApp.csproj new file mode 100644 index 0000000000..65158a7e42 --- /dev/null +++ b/test/SingleFileTestApp/SingleFileTestApp.csproj @@ -0,0 +1,24 @@ + + + + false + Exe + net6.0 + 11 + true + true + + + + <_OSArchitecture>$([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture) + + osx-arm64 + osx-x64 + linux-arm64 + linux-x86 + linux-x64 + win-x86 + win-x64 + + + From cf89997598dc3eaea7f9761c02f05ca9beb688af Mon Sep 17 00:00:00 2001 From: Bruno Garcia Date: Wed, 21 Jun 2023 03:34:02 -0400 Subject: [PATCH 081/142] more details on contributing (#2435) --- CONTRIBUTING.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cd0e8c331b..cce2804c07 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,8 +6,10 @@ For big feature it's advised to raise an issue to discuss it first. ## TLDR * Install the .NET SDKs -* To quickly get up and running, you can just run `dotnet build` -* To run a full build and test locally before pushing, run `./build.sh` or `./buld.ps1` +* Install PowerShell +* Restore workloads with `dotnet workload restore` (needs `sudo` on a Mac) +* To quickly get up and running, you can just run `dotnet build Sentry.sln` +* To run a full build in Release mode and test, before pushing, run `./build.sh` or `./buld.cmd` ## Dependencies @@ -29,9 +31,9 @@ For big feature it's advised to raise an issue to discuss it first. To build any of `Sentry.Maui`, `Sentry.Maui.Tests`, or `Sentry.Samples.Maui`, you'll need to have .NET SDK 6.0.400 or greater installed, and have installed the MAUI workloads installed, either through Visual Studio setup, or by running `dotnet workload restore` (or `dotnet workload install maui`) from the Sentry source code root directory. You may also need other platform dependencies. See https://docs.microsoft.com/dotnet/maui/ for details. Basically, if you can build and run the "MyMauiApp" example you should also be able to build and run the Sentry MAUI sample app. -### Targeting Android, iOS and macCatalyst +### Targeting Android, iOS and Mac Catalyst -Although the files in `/src/Sentry/Platforms/` are part of the `Sentry` project, they are [conditionally targeted](https://github.com/getsentry/sentry-dotnet/blob/b1bfe1efc04eb4c911a85f1cf4cd2e5a176d7c8a/src/Sentry/Sentry.csproj#L19-L21) when the platform is Android, iOS or macCatalyst. We build for Android on all platforms, but currently compile iOS and macCatalyst _only when building on a Mac_. +Although the files in `/src/Sentry/Platforms/` are part of the `Sentry` project, they are [conditionally targeted](https://github.com/getsentry/sentry-dotnet/blob/b1bfe1efc04eb4c911a85f1cf4cd2e5a176d7c8a/src/Sentry/Sentry.csproj#L19-L21) when the platform is Android, iOS or Mac Catalyst. We build for Android on all platforms, but currently compile iOS and Mac Catalyst _only when building on a Mac_. ```xml @@ -49,7 +51,7 @@ This repository uses [Verify](https://github.com/VerifyTests/Verify) to store th When a change involves modifying the public API area (by for example adding a public method), that change will need to be approved, otherwise the CI process will fail. -To do that, run the build locally (i.e: `./build.sh` or `pwsh .\build.ps1`) +To do that, run the build locally (i.e: `./build.sh` or `build.cmd`) and commit the `verify` files that were changed. @@ -61,7 +63,7 @@ Apple Silicon processors (such as the "M1") are arm64 processosr. While .NET 6 a - Always install the x64 release of .NET Core 3.1 (it is not avaialable for arm64). - If prompted, you will need to allow Apple's [Rosetta](https://support.apple.com/HT211861) to be installed. If you have previously done this for another app, you won't be prompted again. -If you are only running `dotnet test` on the command line, you don't need to do anything else. +If you are only running `dotnet test Sentry.sln` on the command line, you don't need to do anything else. If you are using JetBrains Rider as your IDE, you should install the arm64 version of Rider. Within Rider, the .NET SDK used for build and tests is selected under `Preferences` -> `Build, Execution, Deployment` -> `Toolset and Build`. From ea1c0cf5bb6a843c84930511a5278d3522c9d081 Mon Sep 17 00:00:00 2001 From: Bruno Garcia Date: Wed, 21 Jun 2023 03:39:25 -0400 Subject: [PATCH 082/142] ios: missing binding method (#2436) --- CHANGELOG.md | 9 +++++---- src/Sentry.Bindings.Cocoa/ApiDefinitions.cs | 4 ++++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bf0c7d75fe..2d9afcc9ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ ## Unreleased +### Features + +- Symbolication for Single File Apps ([#2425](https://github.com/getsentry/sentry-dotnet/pull/2425)) +- Add binding to `SwiftAsyncStacktraces` on iOS ([#2436](https://github.com/getsentry/sentry-dotnet/pull/2436)) + ### Fixes - Fix Sentry logger options for MAUI and Azure Functions ([#2423](https://github.com/getsentry/sentry-dotnet/pull/2423)) @@ -18,10 +23,6 @@ - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#6230) - [diff](https://github.com/getsentry/sentry-java/compare/6.22.0...6.23.0) -### Features - -- Symbolication for Single File Apps ([#2425](https://github.com/getsentry/sentry-dotnet/pull/2425)) - ## 3.33.1 ### Fixes diff --git a/src/Sentry.Bindings.Cocoa/ApiDefinitions.cs b/src/Sentry.Bindings.Cocoa/ApiDefinitions.cs index 5852fc5c3b..91eb0f1a1c 100644 --- a/src/Sentry.Bindings.Cocoa/ApiDefinitions.cs +++ b/src/Sentry.Bindings.Cocoa/ApiDefinitions.cs @@ -874,6 +874,10 @@ interface SentryOptions // @property (nonatomic) BOOL enableTimeToFullDisplay; [Export ("enableTimeToFullDisplay")] bool EnableTimeToFullDisplay { get; set; } + + // @property (assign, nonatomic) BOOL swiftAsyncStacktraces; + [Export ("swiftAsyncStacktraces")] + bool SwiftAsyncStacktraces { get; set; } } // @protocol SentryIntegrationProtocol From 060480e1dfb42ca2044a8445c1d8e71b84b23f73 Mon Sep 17 00:00:00 2001 From: Dhiogo Brustolin Date: Thu, 22 Jun 2023 17:09:30 +0200 Subject: [PATCH 083/142] chore: Building Cocoa SDK wont change the module (#2439) --- scripts/build-sentry-cocoa.sh | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/scripts/build-sentry-cocoa.sh b/scripts/build-sentry-cocoa.sh index 8353ac5e21..6acf2d49b7 100755 --- a/scripts/build-sentry-cocoa.sh +++ b/scripts/build-sentry-cocoa.sh @@ -18,6 +18,11 @@ if [ ! -f $CARTHAGE ]; then cd .. fi +rollbackSchemes() { + mv Sentry.xcodeproj/xcshareddata/xcschemes/SentryPrivate.xcscheme.bak Sentry.xcodeproj/xcshareddata/xcschemes/SentryPrivate.xcscheme + mv Sentry.xcodeproj/xcshareddata/xcschemes/SentrySwiftUI.xcscheme.bak Sentry.xcodeproj/xcshareddata/xcschemes/SentrySwiftUI.xcscheme +} + if [ $? -eq 0 ] then cd sentry-cocoa @@ -37,8 +42,10 @@ then # Delete SentryPrivate and SentrySwiftUI schemes # we dont want to build them - rm Sentry.xcodeproj/xcshareddata/xcschemes/SentryPrivate.xcscheme - rm Sentry.xcodeproj/xcshareddata/xcschemes/SentrySwiftUI.xcscheme + mv Sentry.xcodeproj/xcshareddata/xcschemes/SentryPrivate.xcscheme Sentry.xcodeproj/xcshareddata/xcschemes/SentryPrivate.xcscheme.bak + mv Sentry.xcodeproj/xcshareddata/xcschemes/SentrySwiftUI.xcscheme Sentry.xcodeproj/xcshareddata/xcschemes/SentrySwiftUI.xcscheme.bak + + trap 'rollbackSchemes; exit 1' SIGINT # Note - We keep the build output in separate directories so that .NET # bundles iOS with net6.0-ios and Mac Catalyst with net6.0-maccatalyst. @@ -63,6 +70,8 @@ then # Remove anything we don't want to bundle in the nuget package. find Carthage/Build* \( -name Headers -o -name PrivateHeaders -o -name Modules \) -exec rm -rf {} + + + rollbackSchemes fi popd > /dev/null From 410b30d2ce0c4db93c050778249b0d812aff089c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 23 Jun 2023 11:10:06 +0200 Subject: [PATCH 084/142] chore: update scripts/update-java.ps1 to 6.24.0 (#2440) --- CHANGELOG.md | 6 +++--- src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d9afcc9ee..ca0b1b2e22 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,9 +19,9 @@ - Bump CLI from v2.18.1 to v2.19.1 ([#2428](https://github.com/getsentry/sentry-dotnet/pull/2428), [#2431](https://github.com/getsentry/sentry-dotnet/pull/2431)) - [changelog](https://github.com/getsentry/sentry-cli/blob/master/CHANGELOG.md#2191) - [diff](https://github.com/getsentry/sentry-cli/compare/2.18.1...2.19.1) -- Bump Java SDK from v6.22.0 to v6.23.0 ([#2429](https://github.com/getsentry/sentry-dotnet/pull/2429)) - - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#6230) - - [diff](https://github.com/getsentry/sentry-java/compare/6.22.0...6.23.0) +- Bump Java SDK from v6.22.0 to v6.24.0 ([#2429](https://github.com/getsentry/sentry-dotnet/pull/2429), [#2440](https://github.com/getsentry/sentry-dotnet/pull/2440)) + - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#6240) + - [diff](https://github.com/getsentry/sentry-java/compare/6.22.0...6.24.0) ## 3.33.1 diff --git a/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj b/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj index 2da2af2bae..a2cf074fa1 100644 --- a/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj +++ b/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj @@ -3,7 +3,7 @@ net6.0-android $(NoWarn);BG8605;BG8606 - 6.23.0 + 6.24.0 $(BaseIntermediateOutputPath)sdks\Sentry\Android\$(SentryAndroidSdkVersion)\ .NET Bindings for the Sentry Android SDK From fbf463e73d81f6e9a75968cd92e8b5a37ff1f158 Mon Sep 17 00:00:00 2001 From: Stefan Jandl Date: Fri, 23 Jun 2023 21:48:10 +0200 Subject: [PATCH 085/142] fix: Pinning dependency versions in tests (#2443) --- .github/dependabot.yml | 6 +++++ .../Sentry.AspNetCore.Grpc.Tests.csproj | 6 ++--- .../Sentry.AspNetCore.TestUtils.csproj | 26 +++++++++---------- ...y.DiagnosticSource.IntegrationTests.csproj | 24 ++++++++--------- .../Sentry.DiagnosticSource.Tests.csproj | 16 ++++++------ .../Sentry.EntityFramework.Tests.csproj | 8 +++--- .../Sentry.Extensions.Logging.Tests.csproj | 4 +-- .../Sentry.Serilog.Tests.csproj | 4 +-- 8 files changed, 50 insertions(+), 44 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index c96b493052..83c909e7fd 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -5,6 +5,12 @@ updates: schedule: interval: monthly open-pull-requests-limit: 2 + +- package-ecosystem: nuget + directory: "/test" + schedule: + interval: weekly + open-pull-requests-limit: 2 - package-ecosystem: "github-actions" directory: "/" diff --git a/test/Sentry.AspNetCore.Grpc.Tests/Sentry.AspNetCore.Grpc.Tests.csproj b/test/Sentry.AspNetCore.Grpc.Tests/Sentry.AspNetCore.Grpc.Tests.csproj index 19347038b1..0f504d1a6f 100644 --- a/test/Sentry.AspNetCore.Grpc.Tests/Sentry.AspNetCore.Grpc.Tests.csproj +++ b/test/Sentry.AspNetCore.Grpc.Tests/Sentry.AspNetCore.Grpc.Tests.csproj @@ -5,9 +5,9 @@ - - - + + + diff --git a/test/Sentry.AspNetCore.TestUtils/Sentry.AspNetCore.TestUtils.csproj b/test/Sentry.AspNetCore.TestUtils/Sentry.AspNetCore.TestUtils.csproj index f2fff37053..4bf16a8d4e 100644 --- a/test/Sentry.AspNetCore.TestUtils/Sentry.AspNetCore.TestUtils.csproj +++ b/test/Sentry.AspNetCore.TestUtils/Sentry.AspNetCore.TestUtils.csproj @@ -26,30 +26,30 @@ See https://github.com/dotnet/aspnetcore/issues/15423 --> - - - + + + - + - + - - - - + + + + - - - - + + + + diff --git a/test/Sentry.DiagnosticSource.IntegrationTests/Sentry.DiagnosticSource.IntegrationTests.csproj b/test/Sentry.DiagnosticSource.IntegrationTests/Sentry.DiagnosticSource.IntegrationTests.csproj index deafeb0b86..e2a8eebe54 100644 --- a/test/Sentry.DiagnosticSource.IntegrationTests/Sentry.DiagnosticSource.IntegrationTests.csproj +++ b/test/Sentry.DiagnosticSource.IntegrationTests/Sentry.DiagnosticSource.IntegrationTests.csproj @@ -11,29 +11,29 @@ - - - + + + - - + + - - + + - + diff --git a/test/Sentry.DiagnosticSource.Tests/Sentry.DiagnosticSource.Tests.csproj b/test/Sentry.DiagnosticSource.Tests/Sentry.DiagnosticSource.Tests.csproj index 3c59ded9af..fef3577f1b 100644 --- a/test/Sentry.DiagnosticSource.Tests/Sentry.DiagnosticSource.Tests.csproj +++ b/test/Sentry.DiagnosticSource.Tests/Sentry.DiagnosticSource.Tests.csproj @@ -7,22 +7,22 @@ - - + + - - + + - - + + @@ -32,8 +32,8 @@ - - + + diff --git a/test/Sentry.EntityFramework.Tests/Sentry.EntityFramework.Tests.csproj b/test/Sentry.EntityFramework.Tests/Sentry.EntityFramework.Tests.csproj index 37d81374ba..a4cb7ed32e 100644 --- a/test/Sentry.EntityFramework.Tests/Sentry.EntityFramework.Tests.csproj +++ b/test/Sentry.EntityFramework.Tests/Sentry.EntityFramework.Tests.csproj @@ -10,12 +10,12 @@ - - - + + + - + 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 1be8d035b8..85a9e8f7a2 100644 --- a/test/Sentry.Extensions.Logging.Tests/Sentry.Extensions.Logging.Tests.csproj +++ b/test/Sentry.Extensions.Logging.Tests/Sentry.Extensions.Logging.Tests.csproj @@ -8,13 +8,13 @@ - + - + diff --git a/test/Sentry.Serilog.Tests/Sentry.Serilog.Tests.csproj b/test/Sentry.Serilog.Tests/Sentry.Serilog.Tests.csproj index fbe28cf00d..f74486f102 100644 --- a/test/Sentry.Serilog.Tests/Sentry.Serilog.Tests.csproj +++ b/test/Sentry.Serilog.Tests/Sentry.Serilog.Tests.csproj @@ -5,8 +5,8 @@ - - + + From 90e9f04453d4e0e8818c0e85ef909a278bfd14e2 Mon Sep 17 00:00:00 2001 From: Jonas Nyrup Date: Mon, 26 Jun 2023 10:21:27 +0200 Subject: [PATCH 086/142] fix name of build script (#2446) --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cce2804c07..0ffe15b279 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -9,7 +9,7 @@ For big feature it's advised to raise an issue to discuss it first. * Install PowerShell * Restore workloads with `dotnet workload restore` (needs `sudo` on a Mac) * To quickly get up and running, you can just run `dotnet build Sentry.sln` -* To run a full build in Release mode and test, before pushing, run `./build.sh` or `./buld.cmd` +* To run a full build in Release mode and test, before pushing, run `./build.sh` or `./build.cmd` ## Dependencies From 605eb5867a47faeab2d8b267a46f885813da963a Mon Sep 17 00:00:00 2001 From: Jonas Nyrup Date: Mon, 26 Jun 2023 10:24:06 +0200 Subject: [PATCH 087/142] Always use '.' as decimal separator (#2445) --- src/Sentry/Internal/Extensions/JsonExtensions.cs | 2 +- src/Sentry/Internal/Http/RetryAfterHandler.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Sentry/Internal/Extensions/JsonExtensions.cs b/src/Sentry/Internal/Extensions/JsonExtensions.cs index 82afb79233..500cf71aaa 100644 --- a/src/Sentry/Internal/Extensions/JsonExtensions.cs +++ b/src/Sentry/Internal/Extensions/JsonExtensions.cs @@ -180,7 +180,7 @@ public static void Deconstruct(this JsonProperty jsonProperty, out string name, // Otherwise, let's get the value as a string and parse it ourselves. // Note that we already know this will succeed due to JsonValueKind.Number - return double.Parse(json.ToString()!); + return double.Parse(json.ToString()!, CultureInfo.InvariantCulture); } public static long? GetAddressAsLong(this JsonElement json) diff --git a/src/Sentry/Internal/Http/RetryAfterHandler.cs b/src/Sentry/Internal/Http/RetryAfterHandler.cs index 656220d794..8478011d4d 100644 --- a/src/Sentry/Internal/Http/RetryAfterHandler.cs +++ b/src/Sentry/Internal/Http/RetryAfterHandler.cs @@ -85,7 +85,7 @@ private DateTimeOffset GetRetryAfterTimestamp(HttpResponseMessage response) // Sentry was sending floating point numbers which are not handled by RetryConditionHeaderValue // To be compatible with older versions of sentry on premise: https://github.com/getsentry/sentry/issues/7919 if (response.Headers.TryGetValues("Retry-After", out var values) - && double.TryParse(values.FirstOrDefault(), out var retryAfterSeconds)) + && double.TryParse(values.FirstOrDefault(), NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out var retryAfterSeconds)) { return _clock.GetUtcNow().AddTicks((long)(retryAfterSeconds * TimeSpan.TicksPerSecond)); } From 5401fed146c2219f2eae25cf26b96419e59b804e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 27 Jun 2023 10:54:50 +0200 Subject: [PATCH 088/142] chore: update scripts/update-cli.ps1 to 2.19.2 (#2451) --- 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 ca0b1b2e22..764efe3a8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,9 +16,9 @@ - Bump Cocoa SDK from v8.7.3 to v8.8.0 ([#2427](https://github.com/getsentry/sentry-dotnet/pull/2427), [#2430](https://github.com/getsentry/sentry-dotnet/pull/2430)) - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#880) - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.7.3...8.8.0) -- Bump CLI from v2.18.1 to v2.19.1 ([#2428](https://github.com/getsentry/sentry-dotnet/pull/2428), [#2431](https://github.com/getsentry/sentry-dotnet/pull/2431)) - - [changelog](https://github.com/getsentry/sentry-cli/blob/master/CHANGELOG.md#2191) - - [diff](https://github.com/getsentry/sentry-cli/compare/2.18.1...2.19.1) +- Bump CLI from v2.18.1 to v2.19.2 ([#2428](https://github.com/getsentry/sentry-dotnet/pull/2428), [#2431](https://github.com/getsentry/sentry-dotnet/pull/2431), [#2451](https://github.com/getsentry/sentry-dotnet/pull/2451)) + - [changelog](https://github.com/getsentry/sentry-cli/blob/master/CHANGELOG.md#2192) + - [diff](https://github.com/getsentry/sentry-cli/compare/2.18.1...2.19.2) - Bump Java SDK from v6.22.0 to v6.24.0 ([#2429](https://github.com/getsentry/sentry-dotnet/pull/2429), [#2440](https://github.com/getsentry/sentry-dotnet/pull/2440)) - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#6240) - [diff](https://github.com/getsentry/sentry-java/compare/6.22.0...6.24.0) diff --git a/Directory.Build.props b/Directory.Build.props index d1c95eabdd..ea2438a99f 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -100,7 +100,7 @@ - 2.19.1 + 2.19.2 $(MSBuildThisFileDirectory)tools\sentry-cli\$(SentryCLIVersion)\ diff --git a/src/Sentry/Sentry.csproj b/src/Sentry/Sentry.csproj index 01dc527ceb..2636d83497 100644 --- a/src/Sentry/Sentry.csproj +++ b/src/Sentry/Sentry.csproj @@ -96,25 +96,25 @@ + Include="sentry-cli-Darwin-arm64" FileHash="a669d9e0df2c7afa1308d05cbc2e78a9b458b0c94f5c111c9e299911bb870308" /> + Include="sentry-cli-Darwin-x86_64" FileHash="4bd15cb308746d7f1cbed00948c7470bbdb498936f364779defb52b1e27e540b" /> + Include="sentry-cli-Linux-aarch64" FileHash="e8f2153c23bc5b4a44e36981d6b19102e7db78f1472a1d99ea2bdc33a4ee8d7a" /> + Include="sentry-cli-Linux-i686" FileHash="3fe02091eb319242e24f55097404b3b9e767918ab16b4431ad7b3e1ff4b3d14b" /> + Include="sentry-cli-Linux-x86_64" FileHash="12ffbaaa930ea3e5883fd1065b2ecf6167f90f5e4d8ea38925f56ae3eeccc4e7" /> + Include="sentry-cli-Windows-i686.exe" FileHash="d3879a7adb933978d99c53b28f1d274836e2f6489a3ca642d48358e87fb269b4" /> + Include="sentry-cli-Windows-x86_64.exe" FileHash="046a8bdf70a02b23820d0fa197d7638b57f31dcc8dedc95e3ffdaf3541fe34cb" /> From 7db3f1b9e8569986d3929e27310f41140fa19723 Mon Sep 17 00:00:00 2001 From: Stefan Jandl Date: Tue, 27 Jun 2023 15:33:03 +0200 Subject: [PATCH 089/142] fix: Adding proguard rules to `Sentry.Bindings.Android` (#2450) --- CHANGELOG.md | 1 + .../Sentry.Samples.Android/Sentry.Samples.Android.csproj | 6 ++++++ .../Sentry.Bindings.Android.csproj | 2 ++ .../build/Sentry.Bindings.Android.targets | 5 +++++ src/Sentry.Bindings.Android/sentry-proguard.cfg | 8 ++++++++ 5 files changed, 22 insertions(+) create mode 100644 src/Sentry.Bindings.Android/build/Sentry.Bindings.Android.targets create mode 100644 src/Sentry.Bindings.Android/sentry-proguard.cfg diff --git a/CHANGELOG.md b/CHANGELOG.md index 764efe3a8b..832f2513e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ ### Fixes +- Builds targeting Android with `r8` enabled no longer crash during SDK init. The package now contains the required proguard rules ([#2450]https://github.com/getsentry/sentry-dotnet/pull/2450) - Fix Sentry logger options for MAUI and Azure Functions ([#2423](https://github.com/getsentry/sentry-dotnet/pull/2423)) ### Dependencies diff --git a/samples/Sentry.Samples.Android/Sentry.Samples.Android.csproj b/samples/Sentry.Samples.Android/Sentry.Samples.Android.csproj index a2a2bbd458..15515ca88d 100644 --- a/samples/Sentry.Samples.Android/Sentry.Samples.Android.csproj +++ b/samples/Sentry.Samples.Android/Sentry.Samples.Android.csproj @@ -12,6 +12,12 @@ + + + - 2.19.2 + 2.19.4 $(MSBuildThisFileDirectory)tools\sentry-cli\$(SentryCLIVersion)\ diff --git a/src/Sentry/Sentry.csproj b/src/Sentry/Sentry.csproj index 2636d83497..8e32479f43 100644 --- a/src/Sentry/Sentry.csproj +++ b/src/Sentry/Sentry.csproj @@ -96,25 +96,25 @@ + Include="sentry-cli-Darwin-arm64" FileHash="a2bd6322b774b9ecfdc0adab53d9d76914bb10eab2ec7d2380b25c0300c428f8" /> + Include="sentry-cli-Darwin-x86_64" FileHash="76ab3a03bf9e8bf0641ee4e7a4cd25cd8d625271db1cd8a821a1d313392fb682" /> + Include="sentry-cli-Linux-aarch64" FileHash="ea0021c6c69cf91c7050be105b8faa40d29c252b6d8c63d2aa33460196a41897" /> + Include="sentry-cli-Linux-i686" FileHash="0bf706665ea0f5a5bad751a405895913f66cbc65cbae0c988be8c2996b422972" /> + Include="sentry-cli-Linux-x86_64" FileHash="a9fb79e44c5bae6ca8dfd2c66ac918c7e0405e3456edeb100d698961842f057f" /> + Include="sentry-cli-Windows-i686.exe" FileHash="db51f4ce5cddaea762b54cd5527166f0045842e4558e315fc971f63687850cb6" /> + Include="sentry-cli-Windows-x86_64.exe" FileHash="769cace3b97e583a185b849b4a47bd9590d0ecfaea94022135301b8783558aa7" /> From 1e0a5e7028556074b5b3efb4460fd39906610330 Mon Sep 17 00:00:00 2001 From: Stefan Jandl Date: Mon, 3 Jul 2023 10:09:15 +0200 Subject: [PATCH 091/142] Feat/upload proguard mapping (#2455) --- CHANGELOG.md | 1 + samples/Sentry.Samples.Android/AndroidManifest.xml | 4 ++-- .../Sentry.Samples.Android.csproj | 3 ++- src/Sentry/buildTransitive/Sentry.targets | 10 ++++++++++ 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d2038fa8c4..eb165c41be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Features +- Added a MSBuild property `SentryUploadAndroidProguardMapping` to automatically upload the Proguard mapping file when targeting Android ([#2455](https://github.com/getsentry/sentry-dotnet/pull/2455)) - Symbolication for Single File Apps ([#2425](https://github.com/getsentry/sentry-dotnet/pull/2425)) - Add binding to `SwiftAsyncStacktraces` on iOS ([#2436](https://github.com/getsentry/sentry-dotnet/pull/2436)) diff --git a/samples/Sentry.Samples.Android/AndroidManifest.xml b/samples/Sentry.Samples.Android/AndroidManifest.xml index 380e3ab64a..c2775a380e 100644 --- a/samples/Sentry.Samples.Android/AndroidManifest.xml +++ b/samples/Sentry.Samples.Android/AndroidManifest.xml @@ -1,5 +1,5 @@ - - + + diff --git a/samples/Sentry.Samples.Android/Sentry.Samples.Android.csproj b/samples/Sentry.Samples.Android/Sentry.Samples.Android.csproj index 15515ca88d..65fd8df917 100644 --- a/samples/Sentry.Samples.Android/Sentry.Samples.Android.csproj +++ b/samples/Sentry.Samples.Android/Sentry.Samples.Android.csproj @@ -16,7 +16,7 @@ + --> @@ -27,6 +27,7 @@ true true + true diff --git a/src/Sentry/buildTransitive/Sentry.targets b/src/Sentry/buildTransitive/Sentry.targets index 4b24d90adf..1f11b18135 100644 --- a/src/Sentry/buildTransitive/Sentry.targets +++ b/src/Sentry/buildTransitive/Sentry.targets @@ -161,4 +161,14 @@ + + + + + + + + + From af5aac5a3d0e5faddda57d68a6ff7d4f169aab88 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Jul 2023 13:13:32 +0200 Subject: [PATCH 092/142] Bump gradle/gradle-build-action from 2.4.2 to 2.5.1 (#2456) --- .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 ef9728912d..4276e7d987 100644 --- a/.github/workflows/device-tests-android.yml +++ b/.github/workflows/device-tests-android.yml @@ -84,7 +84,7 @@ jobs: run: dotnet tool install Microsoft.DotNet.XHarness.CLI --global --version "1.*-*" - name: Setup Gradle - uses: gradle/gradle-build-action@749f47bda3e44aa060e82d7b3ef7e40d953bd629 # pin@v2 + uses: gradle/gradle-build-action@40b6781dcdec2762ad36556682ac74e31030cfe2 # pin@v2 # Cached AVD setup per https://github.com/ReactiveCircus/android-emulator-runner/blob/main/README.md From 46490d770d5d9b4539f51365b1b10738561cffa5 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Wed, 5 Jul 2023 17:31:21 +1200 Subject: [PATCH 093/142] Feature: Open Telemetry Support (#2453) OpenTelemetry Integration and ASP.NET Core sample Co-authored-by: Matt Johnson-Pint --- CHANGELOG.md | 1 + Sentry-CI-Build-Linux.slnf | 5 +- Sentry-CI-Build-Windows.slnf | 5 +- Sentry-CI-Build-macOS.slnf | 5 +- Sentry-CI-CodeQL.slnf | 1 + Sentry-CI-Pack.slnf | 1 + Sentry-CI-Test.slnf | 5 +- Sentry.Full.sln | 21 ++ Sentry.sln | 21 ++ SentryAspNetCore.slnf | 5 +- SentryNoSamples.slnf | 1 + .../DiagnosticsConfig.cs | 9 + .../Program.cs | 56 ++++ .../Properties/launchSettings.json | 14 + ...ry.Samples.OpenTelemetry.AspNetCore.csproj | 20 ++ .../appsettings.Development.json | 8 + .../appsettings.json | 9 + .../Extensions/HttpContextExtensions.cs | 47 +++ .../SentryTracingMiddleware.cs | 60 +--- .../OpenTelemetryExtensions.cs | 19 ++ .../OpenTelemetryTransactionProcessor.cs | 19 ++ .../Sentry.OpenTelemetry.csproj | 20 ++ .../SentryOptionsExtensions.cs | 19 ++ src/Sentry.OpenTelemetry/SentryPropagator.cs | 137 ++++++++ .../SentrySpanProcessor.cs | 306 +++++++++++++++++ .../TracerProviderBuilderExtensions.cs | 34 ++ src/Sentry/BaggageHeader.cs | 4 +- src/Sentry/ISpan.cs | 15 + src/Sentry/Instrumenter.cs | 17 + .../Extensions/CollectionsExtensions.cs | 13 + src/Sentry/Internal/Hub.cs | 11 + src/Sentry/Internal/NoOpSpan.cs | 73 +++++ src/Sentry/Internal/NoOpTransaction.cs | 85 +++++ src/Sentry/Sentry.csproj | 8 +- src/Sentry/SentryOptions.cs | 31 ++ src/Sentry/SentrySdk.cs | 2 +- src/Sentry/SentryTraceHeader.cs | 2 + src/Sentry/SpanContext.cs | 5 + src/Sentry/SpanStatus.cs | 31 +- src/Sentry/SpanTracer.cs | 38 ++- src/Sentry/Transaction.cs | 4 +- src/Sentry/TransactionTracer.cs | 77 +++-- .../ActivitySourceTests.cs | 26 ++ .../OpenTelemetryExtensionsTests.cs | 21 ++ .../OpenTelemetryTransactionProcessorTests.cs | 37 +++ .../Sentry.OpenTelemetry.Tests.csproj | 19 ++ .../SentryPropagatorTests.cs | 171 ++++++++++ .../SentrySpanProcessorTests.cs | 310 ++++++++++++++++++ .../ApiApprovalTests.Run.Core3_1.verified.txt | 6 + ...piApprovalTests.Run.DotNet6_0.verified.txt | 6 + ...piApprovalTests.Run.DotNet7_0.verified.txt | 6 + .../ApiApprovalTests.Run.Net4_8.verified.txt | 6 + test/Sentry.Tests/HubTests.cs | 44 ++- .../Sentry.Tests/Protocol/TransactionTests.cs | 22 ++ test/Sentry.Tests/SentryOptionsTests.cs | 46 +++ 55 files changed, 1878 insertions(+), 106 deletions(-) create mode 100644 samples/Sentry.Samples.OpenTelemetry.AspNetCore/DiagnosticsConfig.cs create mode 100644 samples/Sentry.Samples.OpenTelemetry.AspNetCore/Program.cs create mode 100644 samples/Sentry.Samples.OpenTelemetry.AspNetCore/Properties/launchSettings.json create mode 100644 samples/Sentry.Samples.OpenTelemetry.AspNetCore/Sentry.Samples.OpenTelemetry.AspNetCore.csproj create mode 100644 samples/Sentry.Samples.OpenTelemetry.AspNetCore/appsettings.Development.json create mode 100644 samples/Sentry.Samples.OpenTelemetry.AspNetCore/appsettings.json create mode 100644 src/Sentry.OpenTelemetry/OpenTelemetryExtensions.cs create mode 100644 src/Sentry.OpenTelemetry/OpenTelemetryTransactionProcessor.cs create mode 100644 src/Sentry.OpenTelemetry/Sentry.OpenTelemetry.csproj create mode 100644 src/Sentry.OpenTelemetry/SentryOptionsExtensions.cs create mode 100644 src/Sentry.OpenTelemetry/SentryPropagator.cs create mode 100644 src/Sentry.OpenTelemetry/SentrySpanProcessor.cs create mode 100644 src/Sentry.OpenTelemetry/TracerProviderBuilderExtensions.cs create mode 100644 src/Sentry/Instrumenter.cs create mode 100644 src/Sentry/Internal/NoOpSpan.cs create mode 100644 src/Sentry/Internal/NoOpTransaction.cs create mode 100644 test/Sentry.OpenTelemetry.Tests/ActivitySourceTests.cs create mode 100644 test/Sentry.OpenTelemetry.Tests/OpenTelemetryExtensionsTests.cs create mode 100644 test/Sentry.OpenTelemetry.Tests/OpenTelemetryTransactionProcessorTests.cs create mode 100644 test/Sentry.OpenTelemetry.Tests/Sentry.OpenTelemetry.Tests.csproj create mode 100644 test/Sentry.OpenTelemetry.Tests/SentryPropagatorTests.cs create mode 100644 test/Sentry.OpenTelemetry.Tests/SentrySpanProcessorTests.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index eb165c41be..3f54bc03e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Features +- Open Telemetry Support ([#2453](https://github.com/getsentry/sentry-dotnet/pull/2453)) - Added a MSBuild property `SentryUploadAndroidProguardMapping` to automatically upload the Proguard mapping file when targeting Android ([#2455](https://github.com/getsentry/sentry-dotnet/pull/2455)) - Symbolication for Single File Apps ([#2425](https://github.com/getsentry/sentry-dotnet/pull/2425)) - Add binding to `SwiftAsyncStacktraces` on iOS ([#2436](https://github.com/getsentry/sentry-dotnet/pull/2436)) diff --git a/Sentry-CI-Build-Linux.slnf b/Sentry-CI-Build-Linux.slnf index f2e3269495..34ab999cb6 100644 --- a/Sentry-CI-Build-Linux.slnf +++ b/Sentry-CI-Build-Linux.slnf @@ -22,11 +22,12 @@ "samples\\Sentry.Samples.Maui\\Sentry.Samples.Maui.csproj", "samples\\Sentry.Samples.ME.Logging\\Sentry.Samples.ME.Logging.csproj", "samples\\Sentry.Samples.NLog\\Sentry.Samples.NLog.csproj", + "samples\\Sentry.Samples.OpenTelemetry.AspNetCore\\Sentry.Samples.OpenTelemetry.AspNetCore.csproj", "samples\\Sentry.Samples.Serilog\\Sentry.Samples.Serilog.csproj", "src\\Sentry.Android.AssemblyReader\\Sentry.Android.AssemblyReader.csproj", - "src\\Sentry.AspNet\\Sentry.AspNet.csproj", "src\\Sentry.AspNetCore.Grpc\\Sentry.AspNetCore.Grpc.csproj", "src\\Sentry.AspNetCore\\Sentry.AspNetCore.csproj", + "src\\Sentry.AspNet\\Sentry.AspNet.csproj", "src\\Sentry.AzureFunctions.Worker\\Sentry.AzureFunctions.Worker.csproj", "src\\Sentry.Bindings.Android\\Sentry.Bindings.Android.csproj", "src\\Sentry.DiagnosticSource\\Sentry.DiagnosticSource.csproj", @@ -36,6 +37,7 @@ "src\\Sentry.Log4Net\\Sentry.Log4Net.csproj", "src\\Sentry.Maui\\Sentry.Maui.csproj", "src\\Sentry.NLog\\Sentry.NLog.csproj", + "src\\Sentry.OpenTelemetry\\Sentry.OpenTelemetry.csproj", "src\\Sentry.Profiling\\Sentry.Profiling.csproj", "src\\Sentry.Serilog\\Sentry.Serilog.csproj", "src\\Sentry\\Sentry.csproj", @@ -53,6 +55,7 @@ "test\\Sentry.Log4Net.Tests\\Sentry.Log4Net.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", "test\\Sentry.Profiling.Tests\\Sentry.Profiling.Tests.csproj", "test\\Sentry.Serilog.Tests\\Sentry.Serilog.Tests.csproj", "test\\Sentry.Testing.CrashableApp\\Sentry.Testing.CrashableApp.csproj", diff --git a/Sentry-CI-Build-Windows.slnf b/Sentry-CI-Build-Windows.slnf index 36b26e0cec..def81f59f6 100644 --- a/Sentry-CI-Build-Windows.slnf +++ b/Sentry-CI-Build-Windows.slnf @@ -21,11 +21,12 @@ "samples\\Sentry.Samples.Maui\\Sentry.Samples.Maui.csproj", "samples\\Sentry.Samples.ME.Logging\\Sentry.Samples.ME.Logging.csproj", "samples\\Sentry.Samples.NLog\\Sentry.Samples.NLog.csproj", + "samples\\Sentry.Samples.OpenTelemetry.AspNetCore\\Sentry.Samples.OpenTelemetry.AspNetCore.csproj", "samples\\Sentry.Samples.Serilog\\Sentry.Samples.Serilog.csproj", "src\\Sentry.Android.AssemblyReader\\Sentry.Android.AssemblyReader.csproj", - "src\\Sentry.AspNet\\Sentry.AspNet.csproj", "src\\Sentry.AspNetCore.Grpc\\Sentry.AspNetCore.Grpc.csproj", "src\\Sentry.AspNetCore\\Sentry.AspNetCore.csproj", + "src\\Sentry.AspNet\\Sentry.AspNet.csproj", "src\\Sentry.AzureFunctions.Worker\\Sentry.AzureFunctions.Worker.csproj", "src\\Sentry.Bindings.Android\\Sentry.Bindings.Android.csproj", "src\\Sentry.DiagnosticSource\\Sentry.DiagnosticSource.csproj", @@ -35,6 +36,7 @@ "src\\Sentry.Log4Net\\Sentry.Log4Net.csproj", "src\\Sentry.Maui\\Sentry.Maui.csproj", "src\\Sentry.NLog\\Sentry.NLog.csproj", + "src\\Sentry.OpenTelemetry\\Sentry.OpenTelemetry.csproj", "src\\Sentry.Profiling\\Sentry.Profiling.csproj", "src\\Sentry.Serilog\\Sentry.Serilog.csproj", "src\\Sentry\\Sentry.csproj", @@ -52,6 +54,7 @@ "test\\Sentry.Log4Net.Tests\\Sentry.Log4Net.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", "test\\Sentry.Profiling.Tests\\Sentry.Profiling.Tests.csproj", "test\\Sentry.Serilog.Tests\\Sentry.Serilog.Tests.csproj", "test\\Sentry.Testing.CrashableApp\\Sentry.Testing.CrashableApp.csproj", diff --git a/Sentry-CI-Build-macOS.slnf b/Sentry-CI-Build-macOS.slnf index d0f04ce844..ee07e03c3e 100644 --- a/Sentry-CI-Build-macOS.slnf +++ b/Sentry-CI-Build-macOS.slnf @@ -23,11 +23,12 @@ "samples\\Sentry.Samples.Maui\\Sentry.Samples.Maui.csproj", "samples\\Sentry.Samples.ME.Logging\\Sentry.Samples.ME.Logging.csproj", "samples\\Sentry.Samples.NLog\\Sentry.Samples.NLog.csproj", + "samples\\Sentry.Samples.OpenTelemetry.AspNetCore\\Sentry.Samples.OpenTelemetry.AspNetCore.csproj", "samples\\Sentry.Samples.Serilog\\Sentry.Samples.Serilog.csproj", "src\\Sentry.Android.AssemblyReader\\Sentry.Android.AssemblyReader.csproj", - "src\\Sentry.AspNet\\Sentry.AspNet.csproj", "src\\Sentry.AspNetCore.Grpc\\Sentry.AspNetCore.Grpc.csproj", "src\\Sentry.AspNetCore\\Sentry.AspNetCore.csproj", + "src\\Sentry.AspNet\\Sentry.AspNet.csproj", "src\\Sentry.AzureFunctions.Worker\\Sentry.AzureFunctions.Worker.csproj", "src\\Sentry.Bindings.Android\\Sentry.Bindings.Android.csproj", "src\\Sentry.Bindings.Cocoa\\Sentry.Bindings.Cocoa.csproj", @@ -38,6 +39,7 @@ "src\\Sentry.Log4Net\\Sentry.Log4Net.csproj", "src\\Sentry.Maui\\Sentry.Maui.csproj", "src\\Sentry.NLog\\Sentry.NLog.csproj", + "src\\Sentry.OpenTelemetry\\Sentry.OpenTelemetry.csproj", "src\\Sentry.Profiling\\Sentry.Profiling.csproj", "src\\Sentry.Serilog\\Sentry.Serilog.csproj", "src\\Sentry\\Sentry.csproj", @@ -55,6 +57,7 @@ "test\\Sentry.Log4Net.Tests\\Sentry.Log4Net.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", "test\\Sentry.Profiling.Tests\\Sentry.Profiling.Tests.csproj", "test\\Sentry.Serilog.Tests\\Sentry.Serilog.Tests.csproj", "test\\Sentry.Testing.CrashableApp\\Sentry.Testing.CrashableApp.csproj", diff --git a/Sentry-CI-CodeQL.slnf b/Sentry-CI-CodeQL.slnf index bb902fc307..52658c57be 100644 --- a/Sentry-CI-CodeQL.slnf +++ b/Sentry-CI-CodeQL.slnf @@ -14,6 +14,7 @@ "src\\Sentry.Log4Net\\Sentry.Log4Net.csproj", "src\\Sentry.Maui\\Sentry.Maui.csproj", "src\\Sentry.NLog\\Sentry.NLog.csproj", + "src\\Sentry.OpenTelemetry\\Sentry.OpenTelemetry.csproj", "src\\Sentry.Profiling\\Sentry.Profiling.csproj", "src\\Sentry.Serilog\\Sentry.Serilog.csproj", "src\\Sentry\\Sentry.csproj" diff --git a/Sentry-CI-Pack.slnf b/Sentry-CI-Pack.slnf index 7d692c9452..4a4ff2364d 100644 --- a/Sentry-CI-Pack.slnf +++ b/Sentry-CI-Pack.slnf @@ -16,6 +16,7 @@ "src\\Sentry.Log4Net\\Sentry.Log4Net.csproj", "src\\Sentry.Maui\\Sentry.Maui.csproj", "src\\Sentry.NLog\\Sentry.NLog.csproj", + "src\\Sentry.OpenTelemetry\\Sentry.OpenTelemetry.csproj", "src\\Sentry.Serilog\\Sentry.Serilog.csproj", "src\\Sentry\\Sentry.csproj" ] diff --git a/Sentry-CI-Test.slnf b/Sentry-CI-Test.slnf index edbe64616d..6efcf87d90 100644 --- a/Sentry-CI-Test.slnf +++ b/Sentry-CI-Test.slnf @@ -3,11 +3,11 @@ "path": "Sentry.Full.sln", "projects": [ "test\\Sentry.Android.AssemblyReader.Tests\\Sentry.Android.AssemblyReader.Tests.csproj", - "test\\Sentry.AzureFunctions.Worker.Tests\\Sentry.AzureFunctions.Worker.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", "test\\Sentry.AspNetCore.TestUtils\\Sentry.AspNetCore.TestUtils.csproj", + "test\\Sentry.AzureFunctions.Worker.Tests\\Sentry.AzureFunctions.Worker.Tests.csproj", "test\\Sentry.DiagnosticSource.IntegrationTests\\Sentry.DiagnosticSource.IntegrationTests.csproj", "test\\Sentry.DiagnosticSource.Tests\\Sentry.DiagnosticSource.Tests.csproj", "test\\Sentry.EntityFramework.Tests\\Sentry.EntityFramework.Tests.csproj", @@ -16,10 +16,11 @@ "test\\Sentry.Log4Net.Tests\\Sentry.Log4Net.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", "test\\Sentry.Serilog.Tests\\Sentry.Serilog.Tests.csproj", "test\\Sentry.Testing.CrashableApp\\Sentry.Testing.CrashableApp.csproj", "test\\Sentry.Testing\\Sentry.Testing.csproj", "test\\Sentry.Tests\\Sentry.Tests.csproj" ] } -} +} \ No newline at end of file diff --git a/Sentry.Full.sln b/Sentry.Full.sln index 366ebfe990..ac329df3d9 100644 --- a/Sentry.Full.sln +++ b/Sentry.Full.sln @@ -184,6 +184,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Samples.AzureFunctio EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.AzureFunctions.Worker.Tests", "test\Sentry.AzureFunctions.Worker.Tests\Sentry.AzureFunctions.Worker.Tests.csproj", "{CCABBDB9-CDEF-4DE6-9264-69797E8831DF}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.OpenTelemetry", "src\Sentry.OpenTelemetry\Sentry.OpenTelemetry.csproj", "{94C5E990-50EE-462F-B83F-B4357F549500}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.OpenTelemetry.Tests", "test\Sentry.OpenTelemetry.Tests\Sentry.OpenTelemetry.Tests.csproj", "{C51A781A-C568-4E1A-834C-8E4EFA3C5B54}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Samples.OpenTelemetry.AspNetCore", "samples\Sentry.Samples.OpenTelemetry.AspNetCore\Sentry.Samples.OpenTelemetry.AspNetCore.csproj", "{2502D3A0-7628-4768-A430-0854BEB1E7A5}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -451,6 +457,18 @@ Global {CCABBDB9-CDEF-4DE6-9264-69797E8831DF}.Debug|Any CPU.Build.0 = Debug|Any CPU {CCABBDB9-CDEF-4DE6-9264-69797E8831DF}.Release|Any CPU.ActiveCfg = Release|Any CPU {CCABBDB9-CDEF-4DE6-9264-69797E8831DF}.Release|Any CPU.Build.0 = Release|Any CPU + {94C5E990-50EE-462F-B83F-B4357F549500}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {94C5E990-50EE-462F-B83F-B4357F549500}.Debug|Any CPU.Build.0 = Debug|Any CPU + {94C5E990-50EE-462F-B83F-B4357F549500}.Release|Any CPU.ActiveCfg = Release|Any CPU + {94C5E990-50EE-462F-B83F-B4357F549500}.Release|Any CPU.Build.0 = Release|Any CPU + {C51A781A-C568-4E1A-834C-8E4EFA3C5B54}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C51A781A-C568-4E1A-834C-8E4EFA3C5B54}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C51A781A-C568-4E1A-834C-8E4EFA3C5B54}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C51A781A-C568-4E1A-834C-8E4EFA3C5B54}.Release|Any CPU.Build.0 = Release|Any CPU + {2502D3A0-7628-4768-A430-0854BEB1E7A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2502D3A0-7628-4768-A430-0854BEB1E7A5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2502D3A0-7628-4768-A430-0854BEB1E7A5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2502D3A0-7628-4768-A430-0854BEB1E7A5}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -523,6 +541,9 @@ Global {DDDF66BE-EC0E-4488-8A35-3D8BEB63F2DB} = {AF6AF4C7-8AA2-4D59-8064-2D79560904EB} {C38B2A7A-0B21-41BA-8AE4-5D0A2AB6B029} = {77454495-55EE-4B40-A089-71B9E8F82E89} {CCABBDB9-CDEF-4DE6-9264-69797E8831DF} = {83263231-1A2A-4733-B759-EEFF14E8C5D5} + {94C5E990-50EE-462F-B83F-B4357F549500} = {AF6AF4C7-8AA2-4D59-8064-2D79560904EB} + {C51A781A-C568-4E1A-834C-8E4EFA3C5B54} = {83263231-1A2A-4733-B759-EEFF14E8C5D5} + {2502D3A0-7628-4768-A430-0854BEB1E7A5} = {77454495-55EE-4B40-A089-71B9E8F82E89} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {0C652B1A-DF72-4EE5-A98B-194FE2C054F6} diff --git a/Sentry.sln b/Sentry.sln index 1f8262bba0..1a07e8d2b9 100644 --- a/Sentry.sln +++ b/Sentry.sln @@ -162,6 +162,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.AzureFunctions.Worke EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SingleFileTestApp", "test\SingleFileTestApp\SingleFileTestApp.csproj", "{08C99C2F-08D8-44A3-981F-768DC84DCEC7}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.OpenTelemetry", "src\Sentry.OpenTelemetry\Sentry.OpenTelemetry.csproj", "{D4B29F85-5029-462E-A67E-8B7183A0406D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.OpenTelemetry.Tests", "test\Sentry.OpenTelemetry.Tests\Sentry.OpenTelemetry.Tests.csproj", "{A58DE854-6576-4E07-98BF-03B9DCCDBF9A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Samples.OpenTelemetry.AspNetCore", "samples\Sentry.Samples.OpenTelemetry.AspNetCore\Sentry.Samples.OpenTelemetry.AspNetCore.csproj", "{6F791E40-49A8-4A67-81DB-6913E519310A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -376,10 +382,22 @@ Global {8639DB06-1F74-4890-8974-613305C1B6C5}.Debug|Any CPU.Build.0 = Debug|Any CPU {8639DB06-1F74-4890-8974-613305C1B6C5}.Release|Any CPU.ActiveCfg = Release|Any CPU {8639DB06-1F74-4890-8974-613305C1B6C5}.Release|Any CPU.Build.0 = Release|Any CPU + {D4B29F85-5029-462E-A67E-8B7183A0406D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D4B29F85-5029-462E-A67E-8B7183A0406D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D4B29F85-5029-462E-A67E-8B7183A0406D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D4B29F85-5029-462E-A67E-8B7183A0406D}.Release|Any CPU.Build.0 = Release|Any CPU + {A58DE854-6576-4E07-98BF-03B9DCCDBF9A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A58DE854-6576-4E07-98BF-03B9DCCDBF9A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A58DE854-6576-4E07-98BF-03B9DCCDBF9A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A58DE854-6576-4E07-98BF-03B9DCCDBF9A}.Release|Any CPU.Build.0 = Release|Any CPU {08C99C2F-08D8-44A3-981F-768DC84DCEC7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {08C99C2F-08D8-44A3-981F-768DC84DCEC7}.Debug|Any CPU.Build.0 = Debug|Any CPU {08C99C2F-08D8-44A3-981F-768DC84DCEC7}.Release|Any CPU.ActiveCfg = Release|Any CPU {08C99C2F-08D8-44A3-981F-768DC84DCEC7}.Release|Any CPU.Build.0 = Release|Any CPU + {6F791E40-49A8-4A67-81DB-6913E519310A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6F791E40-49A8-4A67-81DB-6913E519310A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6F791E40-49A8-4A67-81DB-6913E519310A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6F791E40-49A8-4A67-81DB-6913E519310A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -440,7 +458,10 @@ Global {203C4556-16DE-46CE-9FAF-C93D8B30D04A} = {AF6AF4C7-8AA2-4D59-8064-2D79560904EB} {243B75FF-1501-4DB7-B933-EE43D960EB90} = {77454495-55EE-4B40-A089-71B9E8F82E89} {8639DB06-1F74-4890-8974-613305C1B6C5} = {83263231-1A2A-4733-B759-EEFF14E8C5D5} + {D4B29F85-5029-462E-A67E-8B7183A0406D} = {AF6AF4C7-8AA2-4D59-8064-2D79560904EB} + {A58DE854-6576-4E07-98BF-03B9DCCDBF9A} = {83263231-1A2A-4733-B759-EEFF14E8C5D5} {08C99C2F-08D8-44A3-981F-768DC84DCEC7} = {83263231-1A2A-4733-B759-EEFF14E8C5D5} + {6F791E40-49A8-4A67-81DB-6913E519310A} = {77454495-55EE-4B40-A089-71B9E8F82E89} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {0C652B1A-DF72-4EE5-A98B-194FE2C054F6} diff --git a/SentryAspNetCore.slnf b/SentryAspNetCore.slnf index a4e25e52ea..d4206a4265 100644 --- a/SentryAspNetCore.slnf +++ b/SentryAspNetCore.slnf @@ -3,16 +3,17 @@ "path": "Sentry.sln", "projects": [ "samples\\Sentry.Samples.AspNetCore.Basic\\Sentry.Samples.AspNetCore.Basic.csproj", + "samples\\Sentry.Samples.AspNetCore.OpenTelemetry\\Sentry.Samples.AspNetCore.OpenTelemetry.csproj", "src\\Sentry.AspNetCore.Grpc\\Sentry.AspNetCore.Grpc.csproj", "src\\Sentry.AspNetCore\\Sentry.AspNetCore.csproj", + "src\\Sentry.DiagnosticSource\\Sentry.DiagnosticSource.csproj", "src\\Sentry.Extensions.Logging\\Sentry.Extensions.Logging.csproj", + "src\\Sentry.OpenTelemetry\\Sentry.OpenTelemetry.csproj", "src\\Sentry\\Sentry.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", "test\\Sentry.Extensions.Logging.Tests\\Sentry.Extensions.Logging.Tests.csproj", - "test\\Sentry.Google.Cloud.Functions.Tests\\Sentry.Google.Cloud.Functions.Tests.csproj", - "test\\Sentry.Serilog.Tests\\Sentry.Serilog.Tests.csproj", "test\\Sentry.Testing\\Sentry.Testing.csproj", "test\\Sentry.Tests\\Sentry.Tests.csproj" ] diff --git a/SentryNoSamples.slnf b/SentryNoSamples.slnf index 14af65b2a4..d3d38a1974 100644 --- a/SentryNoSamples.slnf +++ b/SentryNoSamples.slnf @@ -14,6 +14,7 @@ "src\\Sentry.Log4Net\\Sentry.Log4Net.csproj", "src\\Sentry.Maui\\Sentry.Maui.csproj", "src\\Sentry.NLog\\Sentry.NLog.csproj", + "src\\Sentry.OpenTelemetry\\Sentry.OpenTelemetry.csproj", "src\\Sentry.Serilog\\Sentry.Serilog.csproj", "src\\Sentry\\Sentry.csproj", "test\\Sentry.AspNet.Tests\\Sentry.AspNet.Tests.csproj", diff --git a/samples/Sentry.Samples.OpenTelemetry.AspNetCore/DiagnosticsConfig.cs b/samples/Sentry.Samples.OpenTelemetry.AspNetCore/DiagnosticsConfig.cs new file mode 100644 index 0000000000..ceff36ff4c --- /dev/null +++ b/samples/Sentry.Samples.OpenTelemetry.AspNetCore/DiagnosticsConfig.cs @@ -0,0 +1,9 @@ +using System.Diagnostics; + +namespace Sentry.Samples.OpenTelemetry.AspNetCore; + +public static class DiagnosticsConfig +{ + public const string ServiceName = "Sentry.Samples.OpenTelemetry.AspNetCore"; + public static ActivitySource ActivitySource { get; } = new(ServiceName); +} diff --git a/samples/Sentry.Samples.OpenTelemetry.AspNetCore/Program.cs b/samples/Sentry.Samples.OpenTelemetry.AspNetCore/Program.cs new file mode 100644 index 0000000000..a8462ea7f8 --- /dev/null +++ b/samples/Sentry.Samples.OpenTelemetry.AspNetCore/Program.cs @@ -0,0 +1,56 @@ +using System.ComponentModel.DataAnnotations; +using System.Diagnostics; +using OpenTelemetry.Resources; +using OpenTelemetry.Trace; +using Sentry; +using Sentry.OpenTelemetry; +using Sentry.Samples.OpenTelemetry.AspNetCore; + +var builder = WebApplication.CreateBuilder(args); + +// OpenTelemetry Configuration +// See https://opentelemetry.io/docs/instrumentation/net/getting-started/ +builder.Services.AddOpenTelemetry() + .WithTracing(tracerProviderBuilder => + tracerProviderBuilder + .AddSource(DiagnosticsConfig.ActivitySource.Name) + .ConfigureResource(resource => resource.AddService(DiagnosticsConfig.ServiceName)) + .AddAspNetCoreInstrumentation() + .AddHttpClientInstrumentation() + .AddSentry() + ); + +builder.WebHost.UseSentry(options => +{ + options.TracesSampleRate = 1.0; + options.UseOpenTelemetry(); + options.Debug = builder.Environment.IsDevelopment(); +}); + +var app = builder.Build(); + +var httpClient = new HttpClient(); +app.MapGet("/hello", async context => +{ + // Make an HTTP request to the /echo endpoint, to demonstrate that Baggage and TraceHeaders get propagated + // correctly... in a real world situation, we might have received a request to this endpoint from an upstream + // service that is instrumented with Sentry (passing in a SentryTraceHeader), and we might make an downstream + // request to another service that's also instrumented with Sentry. Having a single TraceId that gets propagated + // across all services by Sentry and OpenTelemetry ensures all of these events show as part of the same trace in + // the performance dashboard in Sentry. + var request = context.Request; + if (request.Query.TryGetValue("topping", out var topping)) + { + Activity.Current?.AddTag("topping", topping); + } + + var url = $"{request.Scheme}://{request.Host}{request.PathBase}/echo"; + var result = await httpClient.GetStringAsync(url); + await context.Response.WriteAsync(result); +}); + +app.MapGet("/echo", () => "Hi!"); + +app.MapGet("/throw", _ => throw new Exception("test")); + +app.Run(); diff --git a/samples/Sentry.Samples.OpenTelemetry.AspNetCore/Properties/launchSettings.json b/samples/Sentry.Samples.OpenTelemetry.AspNetCore/Properties/launchSettings.json new file mode 100644 index 0000000000..aed5f1f62b --- /dev/null +++ b/samples/Sentry.Samples.OpenTelemetry.AspNetCore/Properties/launchSettings.json @@ -0,0 +1,14 @@ +{ + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "hello", + "applicationUrl": "http://localhost:5092", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/samples/Sentry.Samples.OpenTelemetry.AspNetCore/Sentry.Samples.OpenTelemetry.AspNetCore.csproj b/samples/Sentry.Samples.OpenTelemetry.AspNetCore/Sentry.Samples.OpenTelemetry.AspNetCore.csproj new file mode 100644 index 0000000000..0c32d2ffce --- /dev/null +++ b/samples/Sentry.Samples.OpenTelemetry.AspNetCore/Sentry.Samples.OpenTelemetry.AspNetCore.csproj @@ -0,0 +1,20 @@ + + + + net7.0 + enable + enable + + + + + + + + + + + + + + diff --git a/samples/Sentry.Samples.OpenTelemetry.AspNetCore/appsettings.Development.json b/samples/Sentry.Samples.OpenTelemetry.AspNetCore/appsettings.Development.json new file mode 100644 index 0000000000..0c208ae918 --- /dev/null +++ b/samples/Sentry.Samples.OpenTelemetry.AspNetCore/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/samples/Sentry.Samples.OpenTelemetry.AspNetCore/appsettings.json b/samples/Sentry.Samples.OpenTelemetry.AspNetCore/appsettings.json new file mode 100644 index 0000000000..10f68b8c8b --- /dev/null +++ b/samples/Sentry.Samples.OpenTelemetry.AspNetCore/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/src/Sentry.AspNetCore/Extensions/HttpContextExtensions.cs b/src/Sentry.AspNetCore/Extensions/HttpContextExtensions.cs index a8b043844d..f2a7e6f1ba 100644 --- a/src/Sentry.AspNetCore/Extensions/HttpContextExtensions.cs +++ b/src/Sentry.AspNetCore/Extensions/HttpContextExtensions.cs @@ -1,5 +1,7 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; +using Microsoft.Extensions.Primitives; +using Sentry.Extensibility; #if !NETSTANDARD2_0 using Microsoft.AspNetCore.Http.Features; @@ -164,4 +166,49 @@ internal static bool RouteHasMvcParameters(string route) // e.g. "GET /pets/{id}" return $"{method} {route}"; } + + public static SentryTraceHeader? TryGetSentryTraceHeader(this HttpContext context, SentryOptions? options) + { + var value = context.Request.Headers.GetValueOrDefault(SentryTraceHeader.HttpHeaderName); + if (string.IsNullOrWhiteSpace(value)) + { + return null; + } + + options?.LogDebug("Received Sentry trace header '{0}'.", value); + + try + { + return SentryTraceHeader.Parse(value); + } + catch (Exception ex) + { + options?.LogError("Invalid Sentry trace header '{0}'.", ex, value); + return null; + } + } + + public static BaggageHeader? TryGetBaggageHeader(this HttpContext context, SentryOptions? options) + { + var value = context.Request.Headers.GetValueOrDefault(BaggageHeader.HttpHeaderName); + if (string.IsNullOrWhiteSpace(value)) + { + return null; + } + + // Note: If there are multiple baggage headers, they will be joined with comma delimiters, + // and can thus be treated as a single baggage header. + + options?.LogDebug("Received baggage header '{0}'.", value); + + try + { + return BaggageHeader.TryParse(value, onlySentry: true); + } + catch (Exception ex) + { + options?.LogError("Invalid baggage header '{0}'.", ex, value); + return null; + } + } } diff --git a/src/Sentry.AspNetCore/SentryTracingMiddleware.cs b/src/Sentry.AspNetCore/SentryTracingMiddleware.cs index 89ca707835..fc47284129 100644 --- a/src/Sentry.AspNetCore/SentryTracingMiddleware.cs +++ b/src/Sentry.AspNetCore/SentryTracingMiddleware.cs @@ -26,51 +26,6 @@ public SentryTracingMiddleware( _options = options.Value; } - private SentryTraceHeader? TryGetSentryTraceHeader(HttpContext context) - { - var value = context.Request.Headers.GetValueOrDefault(SentryTraceHeader.HttpHeaderName); - if (string.IsNullOrWhiteSpace(value)) - { - return null; - } - - _options.LogDebug("Received Sentry trace header '{0}'.", value); - - try - { - return SentryTraceHeader.Parse(value); - } - catch (Exception ex) - { - _options.LogError("Invalid Sentry trace header '{0}'.", ex, value); - return null; - } - } - - private BaggageHeader? TryGetBaggageHeader(HttpContext context) - { - var value = context.Request.Headers.GetValueOrDefault(BaggageHeader.HttpHeaderName); - if (string.IsNullOrWhiteSpace(value)) - { - return null; - } - - // Note: If there are multiple baggage headers, they will be joined with comma delimiters, - // and can thus be treated as a single baggage header. - - _options.LogDebug("Received baggage header '{0}'.", value); - - try - { - return BaggageHeader.TryParse(value, onlySentry: true); - } - catch (Exception ex) - { - _options.LogError("Invalid baggage header '{0}'.", ex, value); - return null; - } - } - private ITransaction? TryStartTransaction(HttpContext context) { if (context.Request.Method == HttpMethod.Options.Method) @@ -84,7 +39,7 @@ public SentryTracingMiddleware( var hub = _getHub(); // Attempt to start a transaction from the trace header if it exists - var traceHeader = TryGetSentryTraceHeader(context); + var traceHeader = context.TryGetSentryTraceHeader(_options); // It's important to try and set the transaction name to some value here so that it's available for use // in sampling. At a later stage, we will try to get the transaction name again, to account for the @@ -104,7 +59,7 @@ public SentryTracingMiddleware( }; // Set the Dynamic Sampling Context from the baggage header, if it exists. - var baggageHeader = TryGetBaggageHeader(context); + var baggageHeader = context.TryGetBaggageHeader(_options); var dynamicSamplingContext = baggageHeader?.CreateDynamicSamplingContext(); if (traceHeader is { } && baggageHeader is null) @@ -147,6 +102,17 @@ public async Task InvokeAsync(HttpContext context) return; } + if (_options.Instrumenter == Instrumenter.OpenTelemetry) + { + _options.LogInfo( + "When using OpenTelemetry instrumentation mode, the call to UseSentryTracing can be safely removed. " + + "ASP.NET Core should be instrumented by following the instructions at " + + "https://github.com/open-telemetry/opentelemetry-dotnet/blob/main/src/OpenTelemetry.Instrumentation.AspNetCore/README.md"); + + await _next(context).ConfigureAwait(false); + return; + } + if (_options.TransactionNameProvider is { } route) { context.Features.Set(route); diff --git a/src/Sentry.OpenTelemetry/OpenTelemetryExtensions.cs b/src/Sentry.OpenTelemetry/OpenTelemetryExtensions.cs new file mode 100644 index 0000000000..fecc538723 --- /dev/null +++ b/src/Sentry.OpenTelemetry/OpenTelemetryExtensions.cs @@ -0,0 +1,19 @@ +namespace Sentry.OpenTelemetry; + +internal static class OpenTelemetryExtensions +{ + public static SpanId AsSentrySpanId(this ActivitySpanId id) => SpanId.Parse(id.ToHexString()); + + public static ActivitySpanId AsActivitySpanId(this SpanId id) => ActivitySpanId.CreateFromString(id.ToString().AsSpan()); + + public static SentryId AsSentryId(this ActivityTraceId id) => SentryId.Parse(id.ToHexString()); + + public static ActivityTraceId AsActivityTraceId(this SentryId id) => ActivityTraceId.CreateFromString(id.ToString().AsSpan()); + + public static BaggageHeader AsBaggageHeader(this IEnumerable> baggage, bool useSentryPrefix = false) => + BaggageHeader.Create( + baggage.Where(member => member.Value != null) + .Select(kvp => (KeyValuePair)kvp!), + useSentryPrefix + ); +} diff --git a/src/Sentry.OpenTelemetry/OpenTelemetryTransactionProcessor.cs b/src/Sentry.OpenTelemetry/OpenTelemetryTransactionProcessor.cs new file mode 100644 index 0000000000..0e057f1d89 --- /dev/null +++ b/src/Sentry.OpenTelemetry/OpenTelemetryTransactionProcessor.cs @@ -0,0 +1,19 @@ +using Sentry.Extensibility; + +namespace Sentry.OpenTelemetry; + +internal class OpenTelemetryTransactionProcessor : ISentryTransactionProcessor +{ + public Transaction Process(Transaction transaction) + { + var activity = Activity.Current; + if (activity != null) + { + var trace = transaction.Contexts.Trace; + trace.TraceId = activity.TraceId.AsSentryId(); + trace.SpanId = activity.SpanId.AsSentrySpanId(); + } + + return transaction; + } +} diff --git a/src/Sentry.OpenTelemetry/Sentry.OpenTelemetry.csproj b/src/Sentry.OpenTelemetry/Sentry.OpenTelemetry.csproj new file mode 100644 index 0000000000..b80af97f3a --- /dev/null +++ b/src/Sentry.OpenTelemetry/Sentry.OpenTelemetry.csproj @@ -0,0 +1,20 @@ + + + + net6.0;net462;netstandard2.0;netstandard2.1 + enable + + + + + + + + + + + + + + + diff --git a/src/Sentry.OpenTelemetry/SentryOptionsExtensions.cs b/src/Sentry.OpenTelemetry/SentryOptionsExtensions.cs new file mode 100644 index 0000000000..93679a35b5 --- /dev/null +++ b/src/Sentry.OpenTelemetry/SentryOptionsExtensions.cs @@ -0,0 +1,19 @@ +namespace Sentry.OpenTelemetry; + +/// +/// OpenTelemetry Extensions for . +/// +public static class SentryOptionsExtensions +{ + /// + /// Enables OpenTelemetry instrumentation with Sentry + /// + /// + public static void UseOpenTelemetry(this SentryOptions options) + { + options.Instrumenter = Instrumenter.OpenTelemetry; + options.AddTransactionProcessor( + new OpenTelemetryTransactionProcessor() + ); + } +} diff --git a/src/Sentry.OpenTelemetry/SentryPropagator.cs b/src/Sentry.OpenTelemetry/SentryPropagator.cs new file mode 100644 index 0000000000..ab9eee8a35 --- /dev/null +++ b/src/Sentry.OpenTelemetry/SentryPropagator.cs @@ -0,0 +1,137 @@ +using Microsoft.Extensions.Primitives; +using OpenTelemetry; +using OpenTelemetry.Context.Propagation; +using Sentry.Extensibility; + +namespace Sentry.OpenTelemetry; + +/// +/// Sentry OpenTelemetry Propagator. +/// Injects and extracts both sentry-trace and baggage headers from carriers. +/// +public class SentryPropagator : BaggagePropagator +{ + private readonly IHub? _hub; + private IHub Hub => _hub ?? SentrySdk.CurrentHub; + private SentryOptions? Options => Hub.GetSentryOptions(); + + /// + /// + /// Creates a new SentryPropagator. + /// + /// + /// You should register the propagator with the OpenTelemetry SDK when initializing your application. + /// + /// + /// OpenTelemetry.Sdk.SetDefaultTextMapPropagator(new SentryPropagator()); + /// + /// + public SentryPropagator() : base() + { + } + + internal SentryPropagator(IHub hub) : this() + { + _hub = hub; + } + + /// + public override ISet Fields => new HashSet + { + SentryTraceHeader.HttpHeaderName, + BaggageHeader.HttpHeaderName + }; + + private static class OTelKeys + { + public const string SentryBaggageKey = "sentry.baggage"; + public const string SentryTraceKey = "sentry.trace"; + } + + /// + public override PropagationContext Extract(PropagationContext context, T carrier, Func> getter) + { + Options?.LogDebug("SentryPropagator.Extract"); + + var result = base.Extract(context, carrier, getter); + var baggage = result.Baggage; // The Otel .NET SDK takes care of baggage headers alread + + Options?.LogDebug("Baggage"); + foreach (var entry in baggage) + { + Options?.LogDebug(entry.ToString()); + } + + if (TryGetSentryTraceHeader(carrier, getter) is not {} sentryTraceHeader) + { + Options?.LogDebug("No SentryTraceHeader present in carrier"); + return result; + } + + Options?.LogDebug($"Extracted SentryTraceHeader from carrier: {sentryTraceHeader}"); + + var activityContext = new ActivityContext( + sentryTraceHeader.TraceId.AsActivityTraceId(), + sentryTraceHeader.SpanId.AsActivitySpanId(), + // NOTE: Our Java and JavaScript SDKs set sentryTraceHeader.IsSampled = true if any trace header is present. + sentryTraceHeader.IsSampled is true ? ActivityTraceFlags.Recorded : ActivityTraceFlags.None, + traceState:null, // See https://www.w3.org/TR/trace-context/#design-overview + isRemote: true + ); + return new PropagationContext(activityContext, baggage); + } + + /// + public override void Inject(PropagationContext context, T carrier, Action setter) + { + Options?.LogDebug("SentryPropagator.Inject"); + + // Don't inject if instrumentation is suppressed + if (Sdk.SuppressInstrumentation) + { + Options?.LogDebug("Not injecting Sentry tracing information. Instrumentation is suppressed."); + return; + } + + // Don't inject when the activity context is invalid. + if (!context.ActivityContext.IsValid()) + { + Options?.LogDebug("Not injecting Sentry tracing information for invalid activity context."); + return; + } + + // Don't inject if this is a request to the Sentry ingest endpoint. + if (carrier is HttpRequestMessage request && (Options?.IsSentryRequest(request.RequestUri) ?? false)) + { + return; + } + + // Reconstruct SentryTraceHeader from the OpenTelemetry activity/span context + // TODO: Check if this is correct. Although the TraceId will be retained, the SpanId may change. Is that how it's supposed to work? + var traceHeader = new SentryTraceHeader( + context.ActivityContext.TraceId.AsSentryId(), + context.ActivityContext.SpanId.AsSentrySpanId(), + context.ActivityContext.TraceFlags.HasFlag(ActivityTraceFlags.Recorded) + ); + + // Set the sentry trace header for downstream requests + Options?.LogDebug($"SentryTraceHeader: {traceHeader}"); + setter(carrier, SentryTraceHeader.HttpHeaderName, traceHeader.ToString()); + + base.Inject(context, carrier, setter); + } + + private static SentryTraceHeader? TryGetSentryTraceHeader(T carrier, Func> getter) + { + var headerValue = getter(carrier, SentryTraceHeader.HttpHeaderName); + var value = new StringValues(headerValue.ToArray()); + try + { + return SentryTraceHeader.Parse(value); + } + catch (Exception) + { + return null; + } + } +} diff --git a/src/Sentry.OpenTelemetry/SentrySpanProcessor.cs b/src/Sentry.OpenTelemetry/SentrySpanProcessor.cs new file mode 100644 index 0000000000..dbf2618402 --- /dev/null +++ b/src/Sentry.OpenTelemetry/SentrySpanProcessor.cs @@ -0,0 +1,306 @@ +using OpenTelemetry; +using Sentry.Extensibility; +using Sentry.Internal.Extensions; + +namespace Sentry.OpenTelemetry; + +// https://develop.sentry.dev/sdk/performance/opentelemetry + +/// +/// Sentry span processor for Open Telemetry. +/// +public class SentrySpanProcessor : BaseProcessor +{ + private readonly IHub _hub; + + // ReSharper disable once MemberCanBePrivate.Global - Used by tests + internal readonly ConcurrentDictionary _map = new(); + private readonly SentryOptions? _options; + private readonly Lazy> _resourceAttributes; + + /// + /// Constructs a . + /// + public SentrySpanProcessor(IHub hub) + { + _hub = hub; + _options = hub.GetSentryOptions(); + + if (_options is not { }) + { + throw new InvalidOperationException( + $"The Sentry SDK has not been initialised. To use Sentry with OpenTelemetry tracing you need to " + + "initialize the Sentry SDK."); + } + + if (_options.Instrumenter != Instrumenter.OpenTelemetry) + { + throw new InvalidOperationException( + "OpenTelemetry has not been configured on the Sentry SDK. To use OpenTelemetry tracing you need " + + "to initialize the Sentry SDK with options.UseOpenTelemetry()"); + } + + // Resource attributes are consistent between spans, but not available during construction. + // Thus, get a single instance lazily. + _resourceAttributes = new Lazy>(() => + ParentProvider?.GetResource().Attributes.ToDictionary() ?? new Dictionary(0)); + } + + /// + public override void OnStart(Activity data) + { + if (data.ParentSpanId != default && _map.TryGetValue(data.ParentSpanId, out var parentSpan)) + { + // We can find the parent span - start a child span. + var context = new SpanContext( + data.SpanId.AsSentrySpanId(), + data.ParentSpanId.AsSentrySpanId(), + data.TraceId.AsSentryId(), + data.OperationName, + data.DisplayName, + null, + null) + { + Instrumenter = Instrumenter.OpenTelemetry + }; + + var span = (SpanTracer)parentSpan.StartChild(context); + span.StartTimestamp = data.StartTimeUtc; + _map[data.SpanId] = span; + } + else + { + // If a parent exists at all, then copy its sampling decision. + bool? isSampled = data.HasRemoteParent ? data.Recorded : null; + + // No parent span found - start a new transaction + var transactionContext = new TransactionContext( + data.SpanId.AsSentrySpanId(), + data.ParentSpanId.AsSentrySpanId(), + data.TraceId.AsSentryId(), + data.DisplayName, + data.OperationName, + data.DisplayName, + null, + isSampled, + isSampled) + { + Instrumenter = Instrumenter.OpenTelemetry + }; + + var baggageHeader = data.Baggage.AsBaggageHeader(); + var dynamicSamplingContext = baggageHeader.CreateDynamicSamplingContext(); + var transaction = (TransactionTracer)_hub.StartTransaction( + transactionContext, new Dictionary(), dynamicSamplingContext + ); + transaction.StartTimestamp = data.StartTimeUtc; + _map[data.SpanId] = transaction; + } + } + + /// + public override void OnEnd(Activity data) + { + // Make a dictionary of the attributes (aka "tags") for faster lookup when used throughout the processor. + var attributes = data.TagObjects.ToDictionary(); + + if (attributes.TryGetTypedValue("http.url", out string? url) && (_options?.IsSentryRequest(url) ?? false)) + { + _options?.DiagnosticLogger?.LogDebug($"Ignoring Activity {data.SpanId} for Sentry request."); + if (_map.TryRemove(data.SpanId, out var removed) && (removed is SpanTracer spanTracerToRemove)) + { + spanTracerToRemove.IsSentryRequest = true; + } + return; + } + + if (!_map.TryGetValue(data.SpanId, out var span)) + { + _options?.DiagnosticLogger?.LogError($"Span not found for SpanId: {data.SpanId}. Did OnStart run? We might have a bug in the SDK."); + return; + } + + var (operation, description, source) = ParseOtelSpanDescription(data, attributes); + span.Operation = operation; + span.Description = description; + + if (span is TransactionTracer transaction) + { + transaction.Name = description; + transaction.NameSource = source; + + // Use the end timestamp from the activity data. + transaction.EndTimestamp = data.StartTimeUtc + data.Duration; + + // Transactions set otel attributes (and resource attributes) as context. + transaction.Contexts["otel"] = GetOtelContext(attributes); + } + else + { + // Use the end timestamp from the activity data. + ((SpanTracer)span).EndTimestamp = data.StartTimeUtc + data.Duration; + + // Spans set otel attributes in extras (passed to Sentry as "data" on the span). + // Resource attributes do not need to be set, as they would be identical as those set on the transaction. + span.SetExtras(attributes); + span.SetExtra("otel.kind", data.Kind); + } + + GenerateSentryErrorsFromOtelSpan(data, attributes); + + var status = GetSpanStatus(data.Status, attributes); + span.Finish(status); + + _map.TryRemove(data.SpanId, out _); + } + + internal static SpanStatus GetSpanStatus(ActivityStatusCode status, IDictionary attributes) => + status switch + { + ActivityStatusCode.Unset => SpanStatus.Ok, + ActivityStatusCode.Ok => SpanStatus.Ok, + ActivityStatusCode.Error => GetErrorSpanStatus(attributes), + _ => SpanStatus.UnknownError + }; + + private static SpanStatus GetErrorSpanStatus(IDictionary attributes) + { + if (attributes.TryGetTypedValue("http.status_code", out int httpCode)) + { + return SpanStatusConverter.FromHttpStatusCode(httpCode); + } + + if (attributes.TryGetTypedValue("rpc.grpc.status_code", out int grpcCode)) + { + return SpanStatusConverter.FromGrpcStatusCode(grpcCode); + } + + return SpanStatus.UnknownError; + } + + private static (string operation, string description, TransactionNameSource source) ParseOtelSpanDescription( + Activity activity, + IDictionary attributes) + { + // This function should loosely match the JavaScript implementation at: + // https://github.com/getsentry/sentry-javascript/blob/develop/packages/opentelemetry-node/src/utils/parse-otel-span-description.ts + // However, it should also follow the OpenTelemetry semantic conventions specification, as indicated. + + // HTTP span + // https://opentelemetry.io/docs/specs/otel/trace/semantic_conventions/http/ + if (attributes.TryGetTypedValue("http.method", out string httpMethod)) + { + if (activity.Kind == ActivityKind.Client) + { + // Per OpenTelemetry spec, client spans use only the method. + return ("http.client", httpMethod, TransactionNameSource.Custom); + } + + if (attributes.TryGetTypedValue("http.route", out string httpRoute)) + { + // A route exists. Use the method and route. + return ("http.server", $"{httpMethod} {httpRoute}", TransactionNameSource.Route); + } + + if (attributes.TryGetTypedValue("http.target", out string httpTarget)) + { + // A target exists. Use the method and target. If the target is "/" we can treat it like a route. + var source = httpTarget == "/" ? TransactionNameSource.Route : TransactionNameSource.Url; + return ("http.server", $"{httpMethod} {httpTarget}", source); + } + + // Some other type of HTTP server span. Pass it through with the original name. + return ("http.server", activity.DisplayName, TransactionNameSource.Custom); + } + + // DB span + // https://opentelemetry.io/docs/specs/otel/trace/semantic_conventions/database/ + if (attributes.ContainsKey("db.system")) + { + if (attributes.TryGetTypedValue("db.statement", out string dbStatement)) + { + // We have a database statement. Use it. + return ("db", dbStatement, TransactionNameSource.Task); + } + + // Some other type of DB span. Pass it through with the original name. + return ("db", activity.DisplayName, TransactionNameSource.Task); + } + + // RPC span + // https://opentelemetry.io/docs/specs/otel/trace/semantic_conventions/rpc/ + if (attributes.ContainsKey("rpc.service")) + { + return ("rpc", activity.DisplayName, TransactionNameSource.Route); + } + + // Messaging span + // https://opentelemetry.io/docs/specs/otel/trace/semantic_conventions/messaging/ + if (attributes.ContainsKey("messaging.system")) + { + return ("message", activity.DisplayName, TransactionNameSource.Route); + } + + // FaaS (Functions/Lambda) span + // https://opentelemetry.io/docs/specs/otel/trace/semantic_conventions/faas/ + if (attributes.TryGetTypedValue("faas.trigger", out string faasTrigger)) + { + return (faasTrigger, activity.DisplayName, TransactionNameSource.Route); + } + + // Default - pass through unmodified. + return (activity.OperationName, activity.DisplayName, TransactionNameSource.Custom); + } + + private Dictionary GetOtelContext(IDictionary attributes) + { + var otelContext = new Dictionary(); + if (attributes.Count > 0) + { + otelContext.Add("attributes", attributes); + } + + var resourceAttributes = _resourceAttributes.Value; + if (resourceAttributes.Count > 0) + { + otelContext.Add("resource", resourceAttributes); + } + + return otelContext; + } + + private void GenerateSentryErrorsFromOtelSpan(Activity activity, IDictionary spanAttributes) + { + // // https://develop.sentry.dev/sdk/performance/opentelemetry/#step-7-define-generatesentryerrorsfromotelspan + // // https://opentelemetry.io/docs/specs/otel/trace/semantic_conventions/exceptions/ + // + // foreach (var @event in activity.Events.Where(e => e.Name == "exception")) + // { + // // Note, this doesn't do anything yet because `exception` is not a valid attribute. + // // We cannot just use `exception.type`, `exception.message`, and `exception.stacktrace`. + // // See https://github.com/open-telemetry/opentelemetry-dotnet/issues/2439#issuecomment-1577314568 + // + // var eventAttributes = @event.Tags.ToDictionary(); + // if (!eventAttributes.TryGetTypedValue("exception", out Exception exception)) + // { + // continue; + // } + // + // // TODO: Validate that our `DuplicateEventDetectionEventProcessor` prevents this from doubling exceptions + // // that are also caught by other means, such as our AspNetCore middleware, etc. + // // (When options.RecordException = true is set on AddAspNetCoreInstrumentation...) + // // Also, in such cases - how will we get the otel scope and trace context on the other one? + // + // var sentryEvent = new SentryEvent(exception, @event.Timestamp); + // _hub.CaptureEvent(sentryEvent, scope => + // { + // scope.Contexts["otel"] = GetOtelContext(spanAttributes); + // + // var trace = scope.Contexts.Trace; + // trace.TraceId = activity.TraceId.AsSentryId(); + // trace.SpanId = activity.SpanId.AsSentrySpanId(); + // trace.ParentSpanId = activity.ParentSpanId.AsSentrySpanId(); + // }); + // } + } +} diff --git a/src/Sentry.OpenTelemetry/TracerProviderBuilderExtensions.cs b/src/Sentry.OpenTelemetry/TracerProviderBuilderExtensions.cs new file mode 100644 index 0000000000..34b2e4a3cc --- /dev/null +++ b/src/Sentry.OpenTelemetry/TracerProviderBuilderExtensions.cs @@ -0,0 +1,34 @@ +using OpenTelemetry; +using OpenTelemetry.Context.Propagation; +using OpenTelemetry.Trace; + +namespace Sentry.OpenTelemetry; + +/// +/// Contains extension methods for the class. +/// +public static class TracerProviderBuilderExtensions +{ + /// + /// Ensures OpenTelemetry trace information is sent to Sentry. + /// + /// . + /// + /// The default TextMapPropagator to be used by OpenTelemetry. + /// + /// If this parameter is not supplied, the will be used, which propagates the + /// baggage header as well as Sentry trace headers. + /// + /// + /// The is required for Sentry's OpenTelemetry integration to work but you + /// could wrap this in a if you needed other propagators as well. + /// + /// + /// The supplied for chaining. + public static TracerProviderBuilder AddSentry(this TracerProviderBuilder tracerProviderBuilder, TextMapPropagator? defaultTextMapPropagator = null) + { + defaultTextMapPropagator ??= new SentryPropagator(); + Sdk.SetDefaultTextMapPropagator(defaultTextMapPropagator); + return tracerProviderBuilder.AddProcessor(); + } +} diff --git a/src/Sentry/BaggageHeader.cs b/src/Sentry/BaggageHeader.cs index 77b2f9b6e7..4c2bf572c3 100644 --- a/src/Sentry/BaggageHeader.cs +++ b/src/Sentry/BaggageHeader.cs @@ -124,9 +124,9 @@ public static BaggageHeader Create( public static BaggageHeader Merge(IEnumerable baggageHeaders) => new(baggageHeaders.SelectMany(x => x.Members)); - private static bool IsValidKey(string key) + private static bool IsValidKey(string? key) { - if (key.Length == 0) + if (string.IsNullOrEmpty(key)) { return false; } diff --git a/src/Sentry/ISpan.cs b/src/Sentry/ISpan.cs index 249fa76992..38a31b7eb3 100644 --- a/src/Sentry/ISpan.cs +++ b/src/Sentry/ISpan.cs @@ -1,3 +1,5 @@ +using Sentry.Internal; + namespace Sentry; /// @@ -66,6 +68,19 @@ public static ISpan StartChild(this ISpan span, string operation, string? descri return child; } + internal static ISpan StartChild(this ISpan span, SpanContext context) + { + var transaction = span.GetTransaction() as TransactionTracer; + if (transaction?.StartChild(context.SpanId, span.SpanId, context.Operation, context.Instrumenter) + is not SpanTracer childSpan) + { + return NoOpSpan.Instance; + } + + childSpan.Description = context.Description; + return childSpan; + } + /// /// Gets the transaction that this span belongs to. /// diff --git a/src/Sentry/Instrumenter.cs b/src/Sentry/Instrumenter.cs new file mode 100644 index 0000000000..f169216255 --- /dev/null +++ b/src/Sentry/Instrumenter.cs @@ -0,0 +1,17 @@ +namespace Sentry; + +/// +/// Describes which approach is used to create spans. +/// +public enum Instrumenter +{ + /// + /// Spans are instrumented via the Sentry SDK. + /// + Sentry, + + /// + /// Spans are instrumented via OpenTelemetry. + /// + OpenTelemetry +} diff --git a/src/Sentry/Internal/Extensions/CollectionsExtensions.cs b/src/Sentry/Internal/Extensions/CollectionsExtensions.cs index 7973ea114e..0f7cb8fa6d 100644 --- a/src/Sentry/Internal/Extensions/CollectionsExtensions.cs +++ b/src/Sentry/Internal/Extensions/CollectionsExtensions.cs @@ -58,4 +58,17 @@ public static IReadOnlyDictionary AsReadOnly(this ID public static IEnumerable ExceptNulls(this IEnumerable source) => source.Where(x => x != null).Select(x => x!); + + public static bool TryGetTypedValue(this IDictionary source, string key, + [NotNullWhen(true)] out T value) + { + if (source.TryGetValue(key, out var obj) && obj is T typedValue) + { + value = typedValue; + return true; + } + + value = default!; + return false; + } } diff --git a/src/Sentry/Internal/Hub.cs b/src/Sentry/Internal/Hub.cs index 30f4b80409..65751e7522 100644 --- a/src/Sentry/Internal/Hub.cs +++ b/src/Sentry/Internal/Hub.cs @@ -41,6 +41,7 @@ internal Hub( options.LogFatal(msg); throw new InvalidOperationException(msg); } + options.LogDebug("Initializing Hub for Dsn: '{0}'.", options.Dsn); _options = options; @@ -116,6 +117,16 @@ internal ITransaction StartTransaction( IReadOnlyDictionary customSamplingContext, DynamicSamplingContext? dynamicSamplingContext) { + var instrumenter = (context as SpanContext)?.Instrumenter; + if (instrumenter != _options.Instrumenter) + { + _options.LogWarning( + $"Attempted to start a transaction via {instrumenter} instrumentation when the SDK is" + + $" configured for {_options.Instrumenter} instrumentation. The transaction will not be created."); + + return NoOpTransaction.Instance; + } + var transaction = new TransactionTracer(this, context); // If the hub is disabled, we will always sample out. In other words, starting a transaction diff --git a/src/Sentry/Internal/NoOpSpan.cs b/src/Sentry/Internal/NoOpSpan.cs new file mode 100644 index 0000000000..96b1cf9106 --- /dev/null +++ b/src/Sentry/Internal/NoOpSpan.cs @@ -0,0 +1,73 @@ +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 +{ + public static ISpan Instance { get; } = new NoOpSpan(); + + protected NoOpSpan() + { + } + + public SpanId SpanId => SpanId.Empty; + public SpanId? ParentSpanId => SpanId.Empty; + public SentryId TraceId => SentryId.Empty; + public bool? IsSampled => default; + public IReadOnlyDictionary Tags => ImmutableDictionary.Empty; + public IReadOnlyDictionary Extra => ImmutableDictionary.Empty; + public DateTimeOffset StartTimestamp => default; + public DateTimeOffset? EndTimestamp => default; + public bool IsFinished => default; + + public string Operation + { + get => string.Empty; + set { } + } + + public string? Description + { + get => default; + set { } + } + + public SpanStatus? Status + { + get => default; + set { } + } + + public ISpan StartChild(string operation) => this; + + public void Finish() + { + } + + public void Finish(SpanStatus status) + { + } + + public void Finish(Exception exception, SpanStatus status) + { + } + + public void Finish(Exception exception) + { + } + + public void SetTag(string key, string value) + { + } + + public void UnsetTag(string key) + { + } + + public void SetExtra(string key, object? value) + { + } + + public SentryTraceHeader GetTraceHeader() => SentryTraceHeader.Empty; +} diff --git a/src/Sentry/Internal/NoOpTransaction.cs b/src/Sentry/Internal/NoOpTransaction.cs new file mode 100644 index 0000000000..92535847d2 --- /dev/null +++ b/src/Sentry/Internal/NoOpTransaction.cs @@ -0,0 +1,85 @@ +namespace Sentry.Internal; + +/// +/// Transaction class to use when we can't return null but a request to create a transaction couldn't be completed. +/// +internal class NoOpTransaction : NoOpSpan, ITransaction +{ + public new static ITransaction Instance { get; } = new NoOpTransaction(); + + private NoOpTransaction() + { + } + + public SdkVersion Sdk => SdkVersion.Instance; + + public string Name + { + get => string.Empty; + set { } + } + + public bool? IsParentSampled + { + get => default; + set { } + } + + public SentryLevel? Level + { + get => default; + set { } + } + + public Request Request{ + get => new(); + set { } + } + + public Contexts Contexts{ + get => new(); + set { } + } + + public User User + { + get => new(); + set { } + } + + public string? Platform + { + get => default; + set { } + } + + public string? Release + { + get => default; + set { } + } + + public string? Environment + { + get => default; + set { } + } + + public string? TransactionName + { + get => default; + set { } + } + + public IReadOnlyList Fingerprint + { + get => ImmutableList.Empty; + set { } + } + + public IReadOnlyCollection Spans => ImmutableList.Empty; + public IReadOnlyCollection Breadcrumbs => ImmutableList.Empty; + + public ISpan? GetLastActiveSpan() => default; + public void AddBreadcrumb(Breadcrumb breadcrumb) { } +} diff --git a/src/Sentry/Sentry.csproj b/src/Sentry/Sentry.csproj index 8e32479f43..e4c1532943 100644 --- a/src/Sentry/Sentry.csproj +++ b/src/Sentry/Sentry.csproj @@ -118,11 +118,7 @@ - + @@ -166,6 +162,8 @@ + + diff --git a/src/Sentry/SentryOptions.cs b/src/Sentry/SentryOptions.cs index 07119b05d4..fa99c1644d 100644 --- a/src/Sentry/SentryOptions.cs +++ b/src/Sentry/SentryOptions.cs @@ -309,6 +309,22 @@ public float? SampleRate /// public string? Dsn { get; set; } + private readonly Lazy _sentryBaseUrl; + + internal bool IsSentryRequest(string? requestUri)=> + !string.IsNullOrEmpty(requestUri) && IsSentryRequest(new Uri(requestUri)); + + internal bool IsSentryRequest(Uri? requestUri) + { + if (string.IsNullOrEmpty(Dsn) || requestUri is null) + { + return false; + } + + var requestBaseUrl = requestUri.GetComponents(UriComponents.SchemeAndServer, UriFormat.Unescaped); + return string.Equals(requestBaseUrl, _sentryBaseUrl.Value, StringComparison.OrdinalIgnoreCase); + } + private Func? _beforeSend; internal Func? BeforeSendInternal => _beforeSend; @@ -899,6 +915,16 @@ public StackTraceMode StackTraceMode /// public Func? CrashedLastRun { get; set; } + /// + /// + /// Gets the used to create spans. + /// + /// + /// Defaults to + /// + /// + internal Instrumenter Instrumenter { get; set; } = Instrumenter.Sentry; + /// /// This property is no longer used. It will be removed in a future version. /// @@ -1097,5 +1123,10 @@ public SentryOptions() "Sentry.Samples" }; #endif + _sentryBaseUrl = new Lazy(() => + new Uri(Dsn ?? string.Empty).GetComponents( + UriComponents.SchemeAndServer, + UriFormat.Unescaped) + ); } } diff --git a/src/Sentry/SentrySdk.cs b/src/Sentry/SentrySdk.cs index 2215f33ccf..0ad66a5bed 100644 --- a/src/Sentry/SentrySdk.cs +++ b/src/Sentry/SentrySdk.cs @@ -18,7 +18,7 @@ public static partial class SentrySdk public static class SentrySdk #endif { - private static IHub CurrentHub = DisabledHub.Instance; + internal static IHub CurrentHub = DisabledHub.Instance; internal static SentryOptions? CurrentOptions => CurrentHub.GetSentryOptions(); diff --git a/src/Sentry/SentryTraceHeader.cs b/src/Sentry/SentryTraceHeader.cs index 7b009b29b8..212deb99bd 100644 --- a/src/Sentry/SentryTraceHeader.cs +++ b/src/Sentry/SentryTraceHeader.cs @@ -7,6 +7,8 @@ public class SentryTraceHeader { internal const string HttpHeaderName = "sentry-trace"; + internal static readonly SentryTraceHeader Empty = new(SentryId.Empty, SpanId.Empty, null); + /// /// Trace ID. /// diff --git a/src/Sentry/SpanContext.cs b/src/Sentry/SpanContext.cs index 8195fd9841..7369bcfa76 100644 --- a/src/Sentry/SpanContext.cs +++ b/src/Sentry/SpanContext.cs @@ -26,6 +26,11 @@ public class SpanContext : ISpanContext /// public bool? IsSampled { get; } + /// + /// Identifies which instrumentation is being used. + /// + public Instrumenter Instrumenter { get; internal set; } = Instrumenter.Sentry; + /// /// Initializes an instance of . /// diff --git a/src/Sentry/SpanStatus.cs b/src/Sentry/SpanStatus.cs index 08047555d3..a2b16b17e0 100644 --- a/src/Sentry/SpanStatus.cs +++ b/src/Sentry/SpanStatus.cs @@ -77,22 +77,45 @@ internal static class SpanStatusConverter public static SpanStatus FromHttpStatusCode(int code) => code switch { < 400 => SpanStatus.Ok, - 400 => SpanStatus.InvalidArgument, + + 400 => SpanStatus.FailedPrecondition, 401 => SpanStatus.Unauthenticated, 403 => SpanStatus.PermissionDenied, 404 => SpanStatus.NotFound, 409 => SpanStatus.AlreadyExists, 429 => SpanStatus.ResourceExhausted, 499 => SpanStatus.Cancelled, - < 500 => SpanStatus.InvalidArgument, + < 500 => SpanStatus.FailedPrecondition, + 500 => SpanStatus.InternalError, 501 => SpanStatus.Unimplemented, 503 => SpanStatus.Unavailable, 504 => SpanStatus.DeadlineExceeded, < 600 => SpanStatus.InternalError, + _ => SpanStatus.UnknownError }; - public static SpanStatus FromHttpStatusCode(HttpStatusCode code) => - FromHttpStatusCode((int)code); + public static SpanStatus FromHttpStatusCode(HttpStatusCode code) => FromHttpStatusCode((int)code); + + public static SpanStatus FromGrpcStatusCode(int code) => code switch + { + 1 => SpanStatus.Cancelled, + 2 => SpanStatus.UnknownError, + 3 => SpanStatus.InvalidArgument, + 4 => SpanStatus.DeadlineExceeded, + 5 => SpanStatus.NotFound, + 6 => SpanStatus.AlreadyExists, + 7 => SpanStatus.PermissionDenied, + 8 => SpanStatus.ResourceExhausted, + 9 => SpanStatus.FailedPrecondition, + 10 => SpanStatus.Aborted, + 11 => SpanStatus.OutOfRange, + 12 => SpanStatus.Unimplemented, + 13 => SpanStatus.InternalError, + 14 => SpanStatus.Unavailable, + 15 => SpanStatus.DataLoss, + 16 => SpanStatus.Unauthenticated, + _ => SpanStatus.UnknownError + }; } diff --git a/src/Sentry/SpanTracer.cs b/src/Sentry/SpanTracer.cs index 876cc6126a..1b560ae802 100644 --- a/src/Sentry/SpanTracer.cs +++ b/src/Sentry/SpanTracer.cs @@ -13,19 +13,19 @@ public class SpanTracer : ISpan internal TransactionTracer Transaction { get; } /// - public SpanId SpanId { get; } + public SpanId SpanId { get; internal set; } /// - public SpanId? ParentSpanId { get; internal set; } + public SpanId? ParentSpanId { get; } /// public SentryId TraceId { get; } /// - public DateTimeOffset StartTimestamp => _stopwatch.StartDateTimeOffset; + public DateTimeOffset StartTimestamp { get; internal set; } /// - public DateTimeOffset? EndTimestamp { get; private set; } + public DateTimeOffset? EndTimestamp { get; internal set; } /// public bool IsFinished => EndTimestamp is not null; @@ -39,6 +39,14 @@ public class SpanTracer : ISpan /// public SpanStatus? Status { get; set; } + /// + /// Used by the Sentry.OpenTelemetry.SentrySpanProcessor to mark a span as a Sentry request. Ideally we wouldn't + /// create these spans but since we can't avoid doing that, once we detect that it's a Sentry request we mark it + /// as such so that we can filter it when the transaction finishes and the TransactionTracer gets converted into + /// a Transaction. + /// + internal bool IsSentryRequest { get; set; } + /// public bool? IsSampled { get; internal set; } @@ -79,10 +87,28 @@ public SpanTracer( ParentSpanId = parentSpanId; TraceId = traceId; Operation = operation; + StartTimestamp = _stopwatch.StartDateTimeOffset; + } + + internal SpanTracer( + IHub hub, + TransactionTracer transaction, + SpanId spanId, + SpanId? parentSpanId, + SentryId traceId, + string operation) + { + _hub = hub; + Transaction = transaction; + SpanId = spanId; + ParentSpanId = parentSpanId; + TraceId = traceId; + Operation = operation; + StartTimestamp = _stopwatch.StartDateTimeOffset; } /// - public ISpan StartChild(string operation) => Transaction.StartChild(SpanId, operation); + public ISpan StartChild(string operation) => Transaction.StartChild(null, parentSpanId:SpanId, operation: operation); /// /// Used to mark a span as unfinished when it was previously marked as finished. This allows us to reuse spans for @@ -98,7 +124,7 @@ internal void Unfinish() public void Finish() { Status ??= SpanStatus.Ok; - EndTimestamp = _stopwatch.CurrentDateTimeOffset; + EndTimestamp ??= _stopwatch.CurrentDateTimeOffset; } /// diff --git a/src/Sentry/Transaction.cs b/src/Sentry/Transaction.cs index e4e823fe1c..4ca09f3cdb 100644 --- a/src/Sentry/Transaction.cs +++ b/src/Sentry/Transaction.cs @@ -259,7 +259,9 @@ public Transaction(ITransaction tracer) _breadcrumbs = tracer.Breadcrumbs.ToList(); _extra = tracer.Extra.ToDictionary(); _tags = tracer.Tags.ToDictionary(); - _spans = tracer.Spans.Select(s => new Span(s)).ToArray(); + _spans = tracer.Spans + .Where(s => s is not SpanTracer { IsSentryRequest: true }) // Filter sentry requests created by Sentry.OpenTelemetry.SentrySpanProcessor + .Select(s => new Span(s)).ToArray(); // Some items are not on the interface, but we only ever pass in a TransactionTracer anyway. if (tracer is TransactionTracer transactionTracer) diff --git a/src/Sentry/TransactionTracer.cs b/src/Sentry/TransactionTracer.cs index 513716cb85..3cf14d2a7e 100644 --- a/src/Sentry/TransactionTracer.cs +++ b/src/Sentry/TransactionTracer.cs @@ -1,3 +1,4 @@ +using Sentry.Extensibility; using Sentry.Internal; using Sentry.Protocol; @@ -10,6 +11,7 @@ public class TransactionTracer : ITransaction, IHasDistribution, IHasTransaction { private readonly IHub _hub; private readonly SentryStopwatch _stopwatch = SentryStopwatch.StartNew(); + private readonly Instrumenter _instrumenter = Instrumenter.Sentry; /// public SpanId SpanId @@ -54,7 +56,7 @@ public SentryId TraceId public string? Distribution { get; set; } /// - public DateTimeOffset StartTimestamp => _stopwatch.StartDateTimeOffset; + public DateTimeOffset StartTimestamp { get; internal set; } /// public DateTimeOffset? EndTimestamp { get; internal set; } @@ -87,7 +89,9 @@ internal set } } - /// + /// + /// The sample rate used for this transaction. + /// public double? SampleRate { get; internal set; } /// @@ -193,6 +197,7 @@ public TransactionTracer(IHub hub, string name, string operation, TransactionNam SpanId = SpanId.Create(); TraceId = SentryId.Create(); Operation = operation; + StartTimestamp = _stopwatch.StartDateTimeOffset; } /// @@ -210,59 +215,72 @@ public TransactionTracer(IHub hub, ITransactionContext context) Description = context.Description; Status = context.Status; IsSampled = context.IsSampled; + StartTimestamp = _stopwatch.StartDateTimeOffset; + + if (context is TransactionContext transactionContext) + { + _instrumenter = transactionContext.Instrumenter; + } } /// - public void AddBreadcrumb(Breadcrumb breadcrumb) => - _breadcrumbs.Add(breadcrumb); + public void AddBreadcrumb(Breadcrumb breadcrumb) => _breadcrumbs.Add(breadcrumb); /// - public void SetExtra(string key, object? value) => - _extra[key] = value; + public void SetExtra(string key, object? value) => _extra[key] = value; /// - public void SetTag(string key, string value) => - _tags[key] = value; + public void SetTag(string key, string value) => _tags[key] = value; /// - public void UnsetTag(string key) => - _tags.TryRemove(key, out _); + public void UnsetTag(string key) => _tags.TryRemove(key, out _); /// [EditorBrowsable(EditorBrowsableState.Never)] - public void SetMeasurement(string name, Measurement measurement) => - _measurements[name] = measurement; + public void SetMeasurement(string name, Measurement measurement) => _measurements[name] = measurement; - internal ISpan StartChild(SpanId parentSpanId, string operation) - { - // Limit spans to 1000 - var isOutOfLimit = _spans.Count >= 1000; + /// + public ISpan StartChild(string operation) => StartChild(spanId: null, parentSpanId: SpanId, operation); - var span = new SpanTracer(_hub, this, parentSpanId, TraceId, operation) + internal ISpan StartChild(SpanId? spanId, SpanId parentSpanId, string operation, + Instrumenter instrumenter = Instrumenter.Sentry) + { + if (instrumenter != _instrumenter) { - IsSampled = !isOutOfLimit - ? IsSampled - : false // sample out out-of-limit spans - }; + _hub.GetSentryOptions()?.LogWarning( + $"Attempted to create a span via {instrumenter} instrumentation to a span or transaction" + + $" originating from {_instrumenter} instrumentation. The span will not be created."); + return NoOpSpan.Instance; + } - if (!isOutOfLimit) + var span = new SpanTracer(_hub, this, parentSpanId, TraceId, operation); + if (spanId is { } id) { - _spans.Add(span); + span.SpanId = id; } + AddChildSpan(span); return span; } - /// - public ISpan StartChild(string operation) => - StartChild(SpanId, operation); + private void AddChildSpan(SpanTracer span) + { + // Limit spans to 1000 + var isOutOfLimit = _spans.Count >= 1000; + span.IsSampled = isOutOfLimit ? false : IsSampled; + + if (!isOutOfLimit) + { + _spans.Add(span); + } + } /// public void Finish() { TransactionProfiler?.Finish(); Status ??= SpanStatus.Ok; - EndTimestamp = _stopwatch.CurrentDateTimeOffset; + EndTimestamp ??= _stopwatch.CurrentDateTimeOffset; foreach (var span in _spans) { @@ -303,8 +321,5 @@ public void Finish(Exception exception) => Spans.OrderByDescending(x => x.StartTimestamp).FirstOrDefault(s => !s.IsFinished); /// - public SentryTraceHeader GetTraceHeader() => new( - TraceId, - SpanId, - IsSampled); + public SentryTraceHeader GetTraceHeader() => new(TraceId, SpanId, IsSampled); } diff --git a/test/Sentry.OpenTelemetry.Tests/ActivitySourceTests.cs b/test/Sentry.OpenTelemetry.Tests/ActivitySourceTests.cs new file mode 100644 index 0000000000..f2c20ba56f --- /dev/null +++ b/test/Sentry.OpenTelemetry.Tests/ActivitySourceTests.cs @@ -0,0 +1,26 @@ +namespace Sentry.OpenTelemetry.Tests; + +public abstract class ActivitySourceTests : IDisposable +{ + protected static readonly ActivitySource Tracer = new("SentrySpanProcessorTests", "1.0.0"); + private readonly ActivityListener _listener; + + protected ActivitySourceTests() + { + // Without a listener, activity source will not create activities + _listener = new ActivityListener + { + ActivityStarted = _ => { }, + ActivityStopped = _ => { }, + ShouldListenTo = _ => true, + SampleUsingParentId = (ref ActivityCreationOptions _) => ActivitySamplingResult.AllDataAndRecorded, + Sample = (ref ActivityCreationOptions _) => ActivitySamplingResult.AllDataAndRecorded + }; + ActivitySource.AddActivityListener(_listener); + } + + public void Dispose() + { + _listener?.Dispose(); + } +} diff --git a/test/Sentry.OpenTelemetry.Tests/OpenTelemetryExtensionsTests.cs b/test/Sentry.OpenTelemetry.Tests/OpenTelemetryExtensionsTests.cs new file mode 100644 index 0000000000..636a54f9fb --- /dev/null +++ b/test/Sentry.OpenTelemetry.Tests/OpenTelemetryExtensionsTests.cs @@ -0,0 +1,21 @@ +namespace Sentry.OpenTelemetry.Tests; + +public class OpenTelemetryExtensionsTests +{ + [Fact] + public void BaggageHeader_CreateWithValues_Filters_NullValue_Members() + { + var result = new Dictionary + { + ["a"] = "1", + ["b"] = null, + ["c"] = "3" + }.AsBaggageHeader(); + + result.Members.Should().Equal(new Dictionary + { + ["a"] = "1", + ["c"] = "3" + }); + } +} diff --git a/test/Sentry.OpenTelemetry.Tests/OpenTelemetryTransactionProcessorTests.cs b/test/Sentry.OpenTelemetry.Tests/OpenTelemetryTransactionProcessorTests.cs new file mode 100644 index 0000000000..f7287d49e9 --- /dev/null +++ b/test/Sentry.OpenTelemetry.Tests/OpenTelemetryTransactionProcessorTests.cs @@ -0,0 +1,37 @@ +namespace Sentry.OpenTelemetry.Tests; + +public class OpenTelemetryTransactionProcessorTests : ActivitySourceTests +{ + [Fact] + public void Process_WithActivity_SetsTraceAndSpanIds() + { + // Arrange + using var activity = Tracer.StartActivity("Parent"); + var transaction = new Transaction("name", "operation"); + var processor = new OpenTelemetryTransactionProcessor(); + + // Act + var processedTransaction = processor.Process(transaction); + + // Assert + processedTransaction.Contexts.Trace.TraceId.Should().Be(activity?.TraceId.AsSentryId()); + processedTransaction.Contexts.Trace.SpanId.Should().Be(activity?.SpanId.AsSentrySpanId()); + } + + [Fact] + public void Process_WithoutActivity_DoesNotModifyTransaction() + { + // Arrange + var transaction = new Transaction("name", "operation"); + var previousTraceId = transaction.Contexts.Trace.TraceId; + var previousSpanId = transaction.Contexts.Trace.SpanId; + var processor = new OpenTelemetryTransactionProcessor(); + + // Act + var processedTransaction = processor.Process(transaction); + + // Assert + Assert.Equal(previousTraceId, processedTransaction.Contexts.Trace.TraceId); + Assert.Equal(previousSpanId, processedTransaction.Contexts.Trace.SpanId); + } +} diff --git a/test/Sentry.OpenTelemetry.Tests/Sentry.OpenTelemetry.Tests.csproj b/test/Sentry.OpenTelemetry.Tests/Sentry.OpenTelemetry.Tests.csproj new file mode 100644 index 0000000000..19b6de9a65 --- /dev/null +++ b/test/Sentry.OpenTelemetry.Tests/Sentry.OpenTelemetry.Tests.csproj @@ -0,0 +1,19 @@ + + + + net7.0;net6.0;netcoreapp3.1;net48 + + + + + + + + + + + + + + + diff --git a/test/Sentry.OpenTelemetry.Tests/SentryPropagatorTests.cs b/test/Sentry.OpenTelemetry.Tests/SentryPropagatorTests.cs new file mode 100644 index 0000000000..3e3be52cd7 --- /dev/null +++ b/test/Sentry.OpenTelemetry.Tests/SentryPropagatorTests.cs @@ -0,0 +1,171 @@ +using Microsoft.Extensions.Primitives; +using OpenTelemetry; +using OpenTelemetry.Context.Propagation; + +namespace Sentry.OpenTelemetry.Tests; + +public class SentryPropagatorTests +{ + private static Baggage EmptyBaggage => new Baggage(); + + private static ActivityContext InvalidContext => default; + + private static ActivityContext ValidContext + { + get { + var sentryTraceHeader = new SentryTraceHeader( + SentryId.Parse("5bd5f6d346b442dd9177dce9302fd737"), + SpanId.Parse("b0d83d6cfec87606"), + true + ); + return new ActivityContext( + sentryTraceHeader.TraceId.AsActivityTraceId(), + sentryTraceHeader.SpanId.AsActivitySpanId(), + sentryTraceHeader.IsSampled is true ? ActivityTraceFlags.Recorded : ActivityTraceFlags.None, + null, + true + ); + } + } + + private static IEnumerable _getter(Dictionary request, string key) + => request.TryGetValue(key, out var value) ? new StringValues(value) : Enumerable.Empty(); + + private static void Setter(Dictionary carrier, string key, string value) => carrier[key] = value; + + + [Fact] + public void Inject_PropagationContext_To_Carrier() + { + // Arrange + var contextIn = new PropagationContext(ValidContext, EmptyBaggage); + var carrier = new Dictionary(); + var sut = new SentryPropagator(); + + // Act + sut.Inject(contextIn, carrier, (c, k, v) => c[k] = v); + + // Assert + carrier.Should().NotBeNull(); + using (new AssertionScope()) + { + carrier.Should().ContainKey("sentry-trace"); + + carrier["sentry-trace"].Should().Be("5bd5f6d346b442dd9177dce9302fd737-b0d83d6cfec87606-1"); + } + } + + [Fact] + public void Inject_PropagationContext_To_Baggage() + { + // Arrange + var baggageIn = Baggage.Create(new Dictionary() + { + { "foo", "bar" }, // simulate some non-sentry baggage... this shouldn't be altered + }); + var contextIn = new PropagationContext(ValidContext, baggageIn); + var carrier = new Dictionary(); + var sut = new SentryPropagator(); + + // Act + sut.Inject(contextIn, carrier, (c, k, v) => c[k] = v); + + // Assert + carrier.Should().NotBeNull(); + using (new AssertionScope()) + { + carrier.Should().ContainKey("baggage"); + var baggageDictionary = (BaggageHeader.TryParse(carrier["baggage"])?.Members is {} members) + ? members.ToDictionary() + : new Dictionary(); + baggageDictionary.Should().Equal(new Dictionary() + { + { "foo", "bar" }, + }); + } + } + + [Fact] + public void Inject_Invalid_Context_DoesNothing() + { + // Arrange + var contextIn = new PropagationContext(InvalidContext, EmptyBaggage); + var carrier = new Dictionary(); + var sut = new SentryPropagator(); + + var setter = Substitute.For, string, string>>(); + + // Act + sut.Inject(contextIn, carrier, setter); + + // Assert + setter.DidNotReceive(); + } + + [Fact] + public void Inject_SentryRequest_DoesNothing() + { + // Arrange + var contextIn = new PropagationContext(ValidContext, EmptyBaggage); + var carrier = new HttpRequestMessage(HttpMethod.Get, "https://123@o456.ingest.sentry.io/789/foo"); + + var options = new SentryOptions(){Dsn = "https://123@o456.ingest.sentry.io/789"}; + SentryClientExtensions.SentryOptionsForTestingOnly = options; + + var hub = Substitute.For(); + + var setter = Substitute.For>(); + + var sut = new SentryPropagator(hub); + + // Act + sut.Inject(contextIn, carrier, setter); + + // Assert + setter.DidNotReceive(); + } + + [Fact] + public void Extract_PropagationContext_From_Carrier() + { + // Arrange + var carrier = new Dictionary() + { + { "Accept", "*/*" }, + { "Connection", "keep-alive" }, + { "Host", "0.0.0.0" }, + { "User-Agent", "python-requests/2.31.0" }, + { "Accept-Encoding", "gzip, deflate" }, + { + "baggage", + "sentry-trace_id=5bd5f6d346b442dd9177dce9302fd737,sentry-environment=production,sentry-public_key=123,sentry-transaction=Pizza,sentry-sample_rate=1.0" + }, + { "sentry-trace", "5bd5f6d346b442dd9177dce9302fd737-b0d83d6cfec87606-1" } + }; + + var sut = new SentryPropagator(); + var contextIn = new PropagationContext(); + + // Act + var outContext = sut.Extract(contextIn, carrier, _getter); + + // Assert + outContext.Should().NotBeNull(); + outContext.ActivityContext.Should().NotBeNull(); + outContext.Baggage.Should().NotBeNull(); + using (new AssertionScope()) + { + $"{outContext.ActivityContext.TraceId}".Should().Be("5bd5f6d346b442dd9177dce9302fd737"); + $"{outContext.ActivityContext.SpanId}".Should().Be("b0d83d6cfec87606"); + outContext.ActivityContext.TraceFlags.Should().Be(ActivityTraceFlags.Recorded); + outContext.ActivityContext.TraceState.Should().BeNull(); + outContext.ActivityContext.IsRemote.Should().Be(true); + + outContext.Baggage.GetBaggage("sentry-trace_id").Should().Be("5bd5f6d346b442dd9177dce9302fd737"); + outContext.Baggage.GetBaggage("sentry-environment").Should().Be("production"); + outContext.Baggage.GetBaggage("sentry-public_key").Should().Be("123"); + outContext.Baggage.GetBaggage("sentry-transaction").Should().Be("Pizza"); + outContext.Baggage.GetBaggage("sentry-sample_rate").Should().Be("1.0"); + } + } +} diff --git a/test/Sentry.OpenTelemetry.Tests/SentrySpanProcessorTests.cs b/test/Sentry.OpenTelemetry.Tests/SentrySpanProcessorTests.cs new file mode 100644 index 0000000000..627f7644f5 --- /dev/null +++ b/test/Sentry.OpenTelemetry.Tests/SentrySpanProcessorTests.cs @@ -0,0 +1,310 @@ +namespace Sentry.OpenTelemetry.Tests; + +public class SentrySpanProcessorTests : ActivitySourceTests +{ + private class Fixture + { + public SentryOptions Options { get; } + + public ISentryClient Client { get; } + + public ISessionManager SessionManager { get; set; } + + public IInternalScopeManager ScopeManager { get; set; } + + public ISystemClock Clock { get; set; } + + public Fixture() + { + Options = new SentryOptions + { + Dsn = ValidDsn, + EnableTracing = true, + AutoSessionTracking = false + }; + + Client = Substitute.For(); + } + + public Hub Hub { get; private set; } + + public Hub GetHub() => Hub ??= new Hub(Options, Client, SessionManager, Clock, ScopeManager); + + public SentrySpanProcessor GetSut() + { + return new SentrySpanProcessor(GetHub()); + } + } + + private readonly Fixture _fixture = new(); + + [Fact] + public void Ctor_Instrumenter_OpenTelemetry_DoesNotThrowException() + { + // Arrange + _fixture.Options.Instrumenter = Instrumenter.OpenTelemetry; + + // Act + var sut = _fixture.GetSut(); + + // Assert + Assert.NotNull(sut); + } + + [Fact] + public void Ctor_Instrumenter_Not_OpenTelemetry_Throws() + { + // Arrange + _fixture.Options.Instrumenter = Instrumenter.Sentry; + + // Act & Assert + Assert.Throws(() => _fixture.GetSut()); + } + + [Fact] + public void GetSpanStatus() + { + using (new AssertionScope()) + { + var noAttributes = new Dictionary(); + + // Unset and OK -> OK + SentrySpanProcessor.GetSpanStatus(ActivityStatusCode.Unset, noAttributes).Should().Be(SpanStatus.Ok); + SentrySpanProcessor.GetSpanStatus(ActivityStatusCode.Ok, noAttributes).Should().Be(SpanStatus.Ok); + + // Error (no attributes) -> UnknownError + SentrySpanProcessor.GetSpanStatus(ActivityStatusCode.Error, noAttributes) + .Should().Be(SpanStatus.UnknownError); + + // Unknown status code -> UnknownError + SentrySpanProcessor.GetSpanStatus((ActivityStatusCode)42, noAttributes) + .Should().Be(SpanStatus.UnknownError); + + // We only test one http scenario, just to make sure the SpanStatusConverter is called for these headers. + // Tests for SpanStatusConverter ensure other http status codes would also work though + var notFoundAttributes = new Dictionary { ["http.status_code"] = 404 }; + SentrySpanProcessor.GetSpanStatus(ActivityStatusCode.Error, notFoundAttributes) + .Should().Be(SpanStatus.NotFound); + + // We only test one grpc scenario, just to make sure the SpanStatusConverter is called for these headers. + // Tests for SpanStatusConverter ensure other grpc status codes would also work though + var grpcAttributes = new Dictionary { ["rpc.grpc.status_code"] = 7 }; + SentrySpanProcessor.GetSpanStatus(ActivityStatusCode.Error, grpcAttributes) + .Should().Be(SpanStatus.PermissionDenied); + } + } + + [Fact] + public void OnStart_Transaction_With_DynamicSamplingContext() + { + // Arrange + _fixture.Options.Instrumenter = Instrumenter.OpenTelemetry; + var sut = _fixture.GetSut(); + + var expected = new Dictionary() + { + { "trace_id", SentryId.Create().ToString() }, + { "public_key", "d4d82fc1c2c4032a83f3a29aa3a3aff" }, + { "sample_rate", "0.5" }, + }; + var data = Tracer.StartActivity("test op")!; + data.AddBaggage($"{BaggageHeader.SentryKeyPrefix}trace_id", expected["trace_id"]); + data.AddBaggage($"{BaggageHeader.SentryKeyPrefix}public_key", expected["public_key"]); + data.AddBaggage($"{BaggageHeader.SentryKeyPrefix}sample_rate", expected["sample_rate"]); + + // Act + sut.OnStart(data!); + + // Assert + Assert.True(sut._map.TryGetValue(data.SpanId, out var span)); + if (span is not TransactionTracer transaction) + { + Assert.Fail("Span is not a transaction tracer"); + return; + } + if (transaction.DynamicSamplingContext is not {} actual) + { + Assert.Fail("Transaction does not have a dynamic sampling context"); + return; + } + using (new AssertionScope()) + { + actual.Items["trace_id"].Should().Be(expected["trace_id"]); + actual.Items["public_key"].Should().Be(expected["public_key"]); + actual.Items["sample_rate"].Should().Be(expected["sample_rate"]); + } + } + +#if NET5_0_OR_GREATER + /* + * Don't run on .NET Framework until we get a resolution to: + * https://github.com/open-telemetry/opentelemetry-dotnet/issues/4623 + * + * netcoreapp3.1 on macOS 12 fails for the same reason so we've just gone with NET5_0_OR_GREATER + */ + + + [Fact] + public void OnStart_WithParentSpanId_StartsChildSpan() + { + // Arrange + _fixture.Options.Instrumenter = Instrumenter.OpenTelemetry; + var sut = _fixture.GetSut(); + + using var parent = Tracer.StartActivity("Parent"); + sut.OnStart(parent); + + using var data = Tracer.StartActivity("TestActivity"); + + // Act + sut.OnStart(data!); + + // Assert + Assert.True(sut._map.TryGetValue(data.SpanId, out var span)); + using (new AssertionScope()) + { + span.Should().BeOfType(); + span.SpanId.Should().Be(data.SpanId.AsSentrySpanId()); + span.ParentSpanId.Should().Be(data.ParentSpanId.AsSentrySpanId()); + if (span is not SpanTracer spanTracer) + { + Assert.Fail("Span is not a span tracer"); + return; + } + using (new AssertionScope()) + { + spanTracer.SpanId.Should().Be(data.SpanId.AsSentrySpanId()); + spanTracer.ParentSpanId.Should().Be(data.ParentSpanId.AsSentrySpanId()); + spanTracer.TraceId.Should().Be(data.TraceId.AsSentryId()); + spanTracer.Operation.Should().Be(data.OperationName); + spanTracer.Description.Should().Be(data.DisplayName); + spanTracer.Status.Should().BeNull(); + spanTracer.StartTimestamp.Should().Be(data.StartTimeUtc); + } + } + } + + [Fact] + public void OnStart_WithoutParentSpanId_StartsNewTransaction() + { + // Arrange + _fixture.Options.Instrumenter = Instrumenter.OpenTelemetry; + var sut = _fixture.GetSut(); + + var data = Tracer.StartActivity("test op"); + + // Act + sut.OnStart(data!); + + // Assert + Assert.True(sut._map.TryGetValue(data.SpanId, out var span)); + if (span is not TransactionTracer transaction) + { + Assert.Fail("Span is not a transaction tracer"); + return; + } + using (new AssertionScope()) + { + transaction.SpanId.Should().Be(data.SpanId.AsSentrySpanId()); + transaction.ParentSpanId.Should().Be(new ActivitySpanId().AsSentrySpanId()); + transaction.TraceId.Should().Be(data.TraceId.AsSentryId()); + transaction.Name.Should().Be(data.DisplayName); + transaction.Operation.Should().Be(data.OperationName); + transaction.Description.Should().Be(data.DisplayName); + transaction.Status.Should().BeNull(); + transaction.StartTimestamp.Should().Be(data.StartTimeUtc); + } + } + + [Fact] + public void OnEnd_FinishesSpan() + { + // Arrange + _fixture.Options.Instrumenter = Instrumenter.OpenTelemetry; + var sut = _fixture.GetSut(); + + var parent = Tracer.StartActivity(name: "transaction")!; + sut.OnStart(parent); + + var tags = new Dictionary { + { "foo", "bar" } + }; + var data = Tracer.StartActivity(name: "test operation", kind: ActivityKind.Internal, parentContext: default, tags)!; + data.DisplayName = "test display name"; + sut.OnStart(data); + + sut._map.TryGetValue(data.SpanId, out var span); + + // Act + sut.OnEnd(data); + + // Assert + if (span is not SpanTracer spanTracer) + { + Assert.Fail("Span is not a span tracer"); + return; + } + + using (new AssertionScope()) + { + using (new AssertionScope()) + { + spanTracer.ParentSpanId.Should().Be(parent.SpanId.AsSentrySpanId()); + spanTracer.Operation.Should().Be(data.OperationName); + spanTracer.Description.Should().Be(data.DisplayName); + spanTracer.EndTimestamp.Should().NotBeNull(); + spanTracer.Extra["otel.kind"].Should().Be(data.Kind); + foreach (var keyValuePair in tags) + { + span.Extra[keyValuePair.Key].Should().Be(keyValuePair.Value); + } + spanTracer.Status.Should().Be(SpanStatus.Ok); + } + } + } + + [Fact] + public void OnEnd_FinishesTransaction() + { + // Arrange + _fixture.Options.Instrumenter = Instrumenter.OpenTelemetry; + var sut = _fixture.GetSut(); + + var tags = new Dictionary { + { "foo", "bar" } + }; + var data = Tracer.StartActivity(name: "test operation", kind: ActivityKind.Internal, parentContext: default, tags)!; + data.DisplayName = "test display name"; + sut.OnStart(data); + + sut._map.TryGetValue(data.SpanId, out var span); + + // Act + sut.OnEnd(data); + + // Assert + if (span is not TransactionTracer transaction) + { + Assert.Fail("Span is not a transaction tracer"); + return; + } + + using (new AssertionScope()) + { + using (new AssertionScope()) + { + transaction.ParentSpanId.Should().Be(new ActivitySpanId().AsSentrySpanId()); + transaction.Operation.Should().Be(data.OperationName); + transaction.Description.Should().Be(data.DisplayName); + transaction.Name.Should().Be(data.DisplayName); + transaction.NameSource.Should().Be(TransactionNameSource.Custom); + transaction.EndTimestamp.Should().NotBeNull(); + transaction.Contexts["otel"].Should().BeEquivalentTo(new Dictionary{ + { "attributes", tags } + }); + transaction.Status.Should().Be(SpanStatus.Ok); + } + } + } +#endif +} diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.Core3_1.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.Core3_1.verified.txt index 49f2c268f9..8f01bbf7d5 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.Core3_1.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.Core3_1.verified.txt @@ -319,6 +319,11 @@ namespace Sentry AllButFirst = 2, None = 3, } + public enum Instrumenter + { + Sentry = 0, + OpenTelemetry = 1, + } public static class MeasurementExtensions { public static void SetMeasurement(this Sentry.ITransactionData transaction, string name, double value, Sentry.MeasurementUnit unit = default) { } @@ -882,6 +887,7 @@ namespace Sentry { public SpanContext(Sentry.SpanId spanId, Sentry.SpanId? parentSpanId, Sentry.SentryId traceId, string operation, string? description, Sentry.SpanStatus? status, bool? isSampled) { } public string? Description { get; } + public Sentry.Instrumenter Instrumenter { get; } public bool? IsSampled { get; } public string Operation { get; } public Sentry.SpanId? ParentSpanId { get; } diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt index 953422b32d..6cc1c4e4a6 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt @@ -319,6 +319,11 @@ namespace Sentry AllButFirst = 2, None = 3, } + public enum Instrumenter + { + Sentry = 0, + OpenTelemetry = 1, + } public static class MeasurementExtensions { public static void SetMeasurement(this Sentry.ITransactionData transaction, string name, double value, Sentry.MeasurementUnit unit = default) { } @@ -883,6 +888,7 @@ namespace Sentry { public SpanContext(Sentry.SpanId spanId, Sentry.SpanId? parentSpanId, Sentry.SentryId traceId, string operation, string? description, Sentry.SpanStatus? status, bool? isSampled) { } public string? Description { get; } + public Sentry.Instrumenter Instrumenter { get; } public bool? IsSampled { get; } public string Operation { get; } public Sentry.SpanId? ParentSpanId { get; } diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt index 953422b32d..6cc1c4e4a6 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt @@ -319,6 +319,11 @@ namespace Sentry AllButFirst = 2, None = 3, } + public enum Instrumenter + { + Sentry = 0, + OpenTelemetry = 1, + } public static class MeasurementExtensions { public static void SetMeasurement(this Sentry.ITransactionData transaction, string name, double value, Sentry.MeasurementUnit unit = default) { } @@ -883,6 +888,7 @@ namespace Sentry { public SpanContext(Sentry.SpanId spanId, Sentry.SpanId? parentSpanId, Sentry.SentryId traceId, string operation, string? description, Sentry.SpanStatus? status, bool? isSampled) { } public string? Description { get; } + public Sentry.Instrumenter Instrumenter { get; } public bool? IsSampled { get; } public string Operation { get; } public Sentry.SpanId? ParentSpanId { get; } diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt index 50f87e8406..5374f5f004 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt @@ -318,6 +318,11 @@ namespace Sentry AllButFirst = 2, None = 3, } + public enum Instrumenter + { + Sentry = 0, + OpenTelemetry = 1, + } public static class MeasurementExtensions { public static void SetMeasurement(this Sentry.ITransactionData transaction, string name, double value, Sentry.MeasurementUnit unit = default) { } @@ -881,6 +886,7 @@ namespace Sentry { public SpanContext(Sentry.SpanId spanId, Sentry.SpanId? parentSpanId, Sentry.SentryId traceId, string operation, string? description, Sentry.SpanStatus? status, bool? isSampled) { } public string? Description { get; } + public Sentry.Instrumenter Instrumenter { get; } public bool? IsSampled { get; } public string Operation { get; } public Sentry.SpanId? ParentSpanId { get; } diff --git a/test/Sentry.Tests/HubTests.cs b/test/Sentry.Tests/HubTests.cs index 4546c0bf37..cee1932c65 100644 --- a/test/Sentry.Tests/HubTests.cs +++ b/test/Sentry.Tests/HubTests.cs @@ -594,6 +594,46 @@ public void StartTransaction_DisableTracing_SampledOut() transaction.IsSampled.Should().BeFalse(); } + [Fact] + public void StartTransaction_SameInstrumenter_SampledIn() + { + // Arrange + _fixture.Options.EnableTracing = true; + _fixture.Options.Instrumenter = Instrumenter.Sentry; // The default... making it explicit for this test though + var hub = _fixture.GetSut(); + + var transactionContext = new TransactionContext("name", "operation") + { + Instrumenter = _fixture.Options.Instrumenter + }; + + // Act + var transaction = hub.StartTransaction(transactionContext); + + // Assert + transaction.IsSampled.Should().BeTrue(); + } + + [Fact] + public void StartTransaction_DifferentInstrumenter_NoOp() + { + // Arrange + _fixture.Options.EnableTracing = true; + _fixture.Options.Instrumenter = Instrumenter.OpenTelemetry; + var hub = _fixture.GetSut(); + + var transactionContext = new TransactionContext("name", "operation") + { + Instrumenter = Instrumenter.Sentry // The default... making it explicit for this test though + }; + + // Act + var transaction = hub.StartTransaction(transactionContext); + + // Assert + transaction.Should().Be(NoOpTransaction.Instance); + } + [Fact] public void StartTransaction_EnableTracing_Sampler_SampledIn() { @@ -1132,7 +1172,7 @@ public void CaptureTransaction_HubEnabled(bool enabled) [Fact] public void CaptureTransaction_Client_Gets_Hint() { - // Arrange + // Arrange var hub = _fixture.GetSut(); // Act @@ -1146,7 +1186,7 @@ public void CaptureTransaction_Client_Gets_Hint() [Fact] public void CaptureTransaction_Client_Gets_ScopeAttachments() { - // Arrange + // Arrange var hub = _fixture.GetSut(); List attachments = new List { AttachmentHelper.FakeAttachment("foo"), diff --git a/test/Sentry.Tests/Protocol/TransactionTests.cs b/test/Sentry.Tests/Protocol/TransactionTests.cs index 55739dfd2f..6a523c8433 100644 --- a/test/Sentry.Tests/Protocol/TransactionTests.cs +++ b/test/Sentry.Tests/Protocol/TransactionTests.cs @@ -366,6 +366,28 @@ public void Finish_UnfinishedSpansGetsFinishedWithDeadlineStatus() Assert.Single(transaction.Spans.Where(span => span.Operation.EndsWith("finished") && span.Status == SpanStatus.Ok)); } + [Fact] + public void Finish_SentryRequestSpansGetIgnored() + { + // Arrange + var hub = Substitute.For(); + var transactionTracer = new TransactionTracer(hub, "my name", "my op"); + transactionTracer.StartChild("normalRequest").Finish(SpanStatus.Ok); + var sentryRequest = (SpanTracer)transactionTracer.StartChild("sentryRequest"); + sentryRequest.IsSentryRequest = true; + + Transaction transaction = null; + hub.CaptureTransaction(Arg.Do(t => transaction = t)); + + // Act + transactionTracer.Finish(); + + // Assert + transaction.Should().NotBeNull(); + transaction.Spans.Should().Contain(s => s.Operation == "normalRequest"); + transaction.Spans.Should().NotContain(s => s.Operation == "sentryRequest"); + } + [Fact] public void Finish_CapturesTransaction() { diff --git a/test/Sentry.Tests/SentryOptionsTests.cs b/test/Sentry.Tests/SentryOptionsTests.cs index c1971a45ba..ba17d07dfa 100644 --- a/test/Sentry.Tests/SentryOptionsTests.cs +++ b/test/Sentry.Tests/SentryOptionsTests.cs @@ -194,4 +194,50 @@ public void FailedRequestTargets_ByDefault_MatchesAnyUrl() var sut = new SentryOptions(); Assert.Contains(".*", sut.FailedRequestTargets); } + + [Fact] + public void IsSentryRequest_WithNullUri_ReturnsFalse() + { + var sut = new SentryOptions(); + + var actual = sut.IsSentryRequest((Uri)null); + + Assert.False(actual); + } + + [Fact] + public void IsSentryRequest_WithEmptyUri_ReturnsFalse() + { + var sut = new SentryOptions(); + + var actual = sut.IsSentryRequest(string.Empty); + + Assert.False(actual); + } + + [Fact] + public void IsSentryRequest_WithInvalidUri_ReturnsFalse() + { + var sut = new SentryOptions + { + Dsn = "https://foo.com" + }; + + var actual = sut.IsSentryRequest(new Uri("https://bar.com")); + + Assert.False(actual); + } + + [Fact] + public void IsSentryRequest_WithValidUri_ReturnsTrue() + { + var sut = new SentryOptions + { + Dsn = "https://123@456.ingest.sentry.io/789" + }; + + var actual = sut.IsSentryRequest(new Uri("https://456.ingest.sentry.io/api/789/envelope/")); + + Assert.True(actual); + } } From 594e32ba80e587d8270d58cd33115cae7732e5ee Mon Sep 17 00:00:00 2001 From: Stefan Jandl Date: Thu, 6 Jul 2023 10:09:56 +0200 Subject: [PATCH 094/142] chore: Use `latest` runners for CI (#2457) --- .github/actions/environment/action.yml | 15 ++++-------- .github/workflows/build.yml | 5 +--- .../SQLite/SentryDiagnosticListenerTests.cs | 24 +++++++++++++++---- 3 files changed, 25 insertions(+), 19 deletions(-) diff --git a/.github/actions/environment/action.yml b/.github/actions/environment/action.yml index ce57471e35..75b7b655ab 100644 --- a/.github/actions/environment/action.yml +++ b/.github/actions/environment/action.yml @@ -16,19 +16,12 @@ runs: distribution: 'temurin' java-version: '11' - # Android 33 is required for .NET Android targets, but is missing on windows-2019 images. - - name: Install Android 33 - if: runner.os == 'Windows' - shell: cmd - run: '%ANDROID_HOME%\cmdline-tools\latest\bin\sdkmanager "platforms;android-33"' - - # Note, the following is needed on the windows-2019 image only. - # All other versions of .NET we need are pre-installed on the GitHub Actions virtual images. - - name: Install .NET 6 SDK + # .NET 3.1 has been removed from all OS due to EOL + # https://github.com/actions/runner-images/issues/7667 + - name: Install .NET 3.1 SDK uses: actions/setup-dotnet@v3 - if: runner.os == 'Windows' with: - dotnet-version: 6.x.x + dotnet-version: 3.1.x - name: Install .NET 7 SDK uses: actions/setup-dotnet@v3 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1b3c03b2fe..e288bbbb26 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -15,10 +15,7 @@ jobs: strategy: fail-fast: false matrix: - # Using ubuntu-20.04 because ubuntu-latest is 22.04, which is fails Sentry.DiagnosticSource.Tests due to a SQLite on Mono issue. - # Using macos-12 because we need Xcode 13.3 or later to build Sentry.Samples.Maui. (macos-latest currently points at macos-11 which uses Xcode 13.2) - # Using windows-2019 because windows-latest is much slower and we don't need anything in particular from it. - os: [ubuntu-20.04, windows-2019, macos-12] + os: [ubuntu-latest, windows-latest, macos-latest] steps: - name: Cancel Previous Runs diff --git a/test/Sentry.DiagnosticSource.Tests/Integration/SQLite/SentryDiagnosticListenerTests.cs b/test/Sentry.DiagnosticSource.Tests/Integration/SQLite/SentryDiagnosticListenerTests.cs index 1a51c0ba22..2346795cdd 100644 --- a/test/Sentry.DiagnosticSource.Tests/Integration/SQLite/SentryDiagnosticListenerTests.cs +++ b/test/Sentry.DiagnosticSource.Tests/Integration/SQLite/SentryDiagnosticListenerTests.cs @@ -1,5 +1,6 @@ using Microsoft.EntityFrameworkCore; using Sentry.Internal.DiagnosticSource; +using Sentry.PlatformAbstractions; namespace Sentry.DiagnosticSource.Tests.Integration.SQLite; @@ -14,6 +15,13 @@ private class Fixture internal SentryScopeManager ScopeManager { get; } public Fixture() { + // We're skipping not just the tests but here too. + // Fixture creation breaks on Mono due to `Library e_sqlite3 not found`. + if (RuntimeInfo.GetRuntime().IsMono()) + { + return; + } + var options = new SentryOptions { TracesSampleRate = 1.0, @@ -54,9 +62,11 @@ public ITransaction StartTransaction(IHub hub, ITransactionContext context) private readonly Fixture _fixture = new(); - [Fact] + [SkippableFact] public void EfCoreIntegration_RunSynchronousQueryWithIssue_TransactionWithSpans() { + Skip.If(RuntimeInfo.GetRuntime().IsMono()); + // Arrange var hub = _fixture.Hub; var transaction = hub.StartTransaction("test", "test"); @@ -86,9 +96,11 @@ public void EfCoreIntegration_RunSynchronousQueryWithIssue_TransactionWithSpans( Assert.All(spans, span => Assert.True(span.IsFinished)); } - [Fact] + [SkippableFact] public void EfCoreIntegration_RunSynchronousQuery_TransactionWithSpans() { + Skip.If(RuntimeInfo.GetRuntime().IsMono()); + // Arrange var hub = _fixture.Hub; var transaction = hub.StartTransaction("test", "test"); @@ -108,9 +120,11 @@ public void EfCoreIntegration_RunSynchronousQuery_TransactionWithSpans() Assert.All(spans, span => Assert.True(span.IsFinished)); } - [Fact] + [SkippableFact] public async Task EfCoreIntegration_RunAsyncQuery_TransactionWithSpansWithOneCompiler() { + Skip.If(RuntimeInfo.GetRuntime().IsMono()); + // Arrange var context = _fixture.NewContext(); var commands = new List(); @@ -157,9 +171,11 @@ public async Task EfCoreIntegration_RunAsyncQuery_TransactionWithSpansWithOneCom }); } - [Fact] + [SkippableFact] public async Task EfCoreIntegration_RunAsyncQuery_TransactionWithSpans() { + Skip.If(RuntimeInfo.GetRuntime().IsMono()); + // Arrange var context = _fixture.NewContext(); var hub = _fixture.Hub; From 03baffcb48e8444dc58e2aa41195d929548c85d9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 6 Jul 2023 10:11:30 +0200 Subject: [PATCH 095/142] chore: update scripts/update-java.ps1 to 6.25.0 (#2458) --- CHANGELOG.md | 6 +++--- src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f54bc03e4..02a52e09dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,9 +22,9 @@ - Bump CLI from v2.18.1 to v2.19.4 ([#2428](https://github.com/getsentry/sentry-dotnet/pull/2428), [#2431](https://github.com/getsentry/sentry-dotnet/pull/2431), [#2451](https://github.com/getsentry/sentry-dotnet/pull/2451), [#2454](https://github.com/getsentry/sentry-dotnet/pull/2454)) - [changelog](https://github.com/getsentry/sentry-cli/blob/master/CHANGELOG.md#2194) - [diff](https://github.com/getsentry/sentry-cli/compare/2.18.1...2.19.4) -- Bump Java SDK from v6.22.0 to v6.24.0 ([#2429](https://github.com/getsentry/sentry-dotnet/pull/2429), [#2440](https://github.com/getsentry/sentry-dotnet/pull/2440)) - - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#6240) - - [diff](https://github.com/getsentry/sentry-java/compare/6.22.0...6.24.0) +- Bump Java SDK from v6.22.0 to v6.25.0 ([#2429](https://github.com/getsentry/sentry-dotnet/pull/2429), [#2440](https://github.com/getsentry/sentry-dotnet/pull/2440), [#2458](https://github.com/getsentry/sentry-dotnet/pull/2458)) + - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#6250) + - [diff](https://github.com/getsentry/sentry-java/compare/6.22.0...6.25.0) ## 3.33.1 diff --git a/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj b/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj index 924f673c5f..07e4c8a35d 100644 --- a/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj +++ b/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj @@ -3,7 +3,7 @@ net6.0-android $(NoWarn);BG8605;BG8606 - 6.24.0 + 6.25.0 $(BaseIntermediateOutputPath)sdks\Sentry\Android\$(SentryAndroidSdkVersion)\ .NET Bindings for the Sentry Android SDK From d06038de6b218b1f2b69305f542c10c139534349 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Fri, 7 Jul 2023 00:32:58 +1200 Subject: [PATCH 096/142] Chore: Update Target Frameworks for Sentry.OpenTelemetry (#2464) --- src/Sentry.OpenTelemetry/Sentry.OpenTelemetry.csproj | 2 +- src/Sentry.OpenTelemetry/SentrySpanProcessor.cs | 2 -- .../Sentry.OpenTelemetry.Tests.csproj | 2 +- .../SentrySpanProcessorTests.cs | 10 ---------- 4 files changed, 2 insertions(+), 14 deletions(-) diff --git a/src/Sentry.OpenTelemetry/Sentry.OpenTelemetry.csproj b/src/Sentry.OpenTelemetry/Sentry.OpenTelemetry.csproj index b80af97f3a..ba83051a5e 100644 --- a/src/Sentry.OpenTelemetry/Sentry.OpenTelemetry.csproj +++ b/src/Sentry.OpenTelemetry/Sentry.OpenTelemetry.csproj @@ -1,7 +1,7 @@ - net6.0;net462;netstandard2.0;netstandard2.1 + net6.0 enable diff --git a/src/Sentry.OpenTelemetry/SentrySpanProcessor.cs b/src/Sentry.OpenTelemetry/SentrySpanProcessor.cs index dbf2618402..0f7f1801b0 100644 --- a/src/Sentry.OpenTelemetry/SentrySpanProcessor.cs +++ b/src/Sentry.OpenTelemetry/SentrySpanProcessor.cs @@ -4,8 +4,6 @@ namespace Sentry.OpenTelemetry; -// https://develop.sentry.dev/sdk/performance/opentelemetry - /// /// Sentry span processor for Open Telemetry. /// diff --git a/test/Sentry.OpenTelemetry.Tests/Sentry.OpenTelemetry.Tests.csproj b/test/Sentry.OpenTelemetry.Tests/Sentry.OpenTelemetry.Tests.csproj index 19b6de9a65..187f373e59 100644 --- a/test/Sentry.OpenTelemetry.Tests/Sentry.OpenTelemetry.Tests.csproj +++ b/test/Sentry.OpenTelemetry.Tests/Sentry.OpenTelemetry.Tests.csproj @@ -1,7 +1,7 @@ - net7.0;net6.0;netcoreapp3.1;net48 + net7.0;net6.0 diff --git a/test/Sentry.OpenTelemetry.Tests/SentrySpanProcessorTests.cs b/test/Sentry.OpenTelemetry.Tests/SentrySpanProcessorTests.cs index 627f7644f5..e23e6a525b 100644 --- a/test/Sentry.OpenTelemetry.Tests/SentrySpanProcessorTests.cs +++ b/test/Sentry.OpenTelemetry.Tests/SentrySpanProcessorTests.cs @@ -135,15 +135,6 @@ public void OnStart_Transaction_With_DynamicSamplingContext() } } -#if NET5_0_OR_GREATER - /* - * Don't run on .NET Framework until we get a resolution to: - * https://github.com/open-telemetry/opentelemetry-dotnet/issues/4623 - * - * netcoreapp3.1 on macOS 12 fails for the same reason so we've just gone with NET5_0_OR_GREATER - */ - - [Fact] public void OnStart_WithParentSpanId_StartsChildSpan() { @@ -306,5 +297,4 @@ public void OnEnd_FinishesTransaction() } } } -#endif } From 3d2e1710ccc678a31a1b4de46119962e5fb6381a Mon Sep 17 00:00:00 2001 From: Stefan Jandl Date: Fri, 7 Jul 2023 09:52:39 +0200 Subject: [PATCH 097/142] pinned versions (#2465) --- .../Sentry.Samples.AspNetCore.Blazor.Wasm.csproj | 4 ++-- .../Sentry.Samples.AspNetCore.Grpc.csproj | 8 ++++---- .../Sentry.Samples.AspNetCore.Serilog.csproj | 6 +++--- .../Sentry.Samples.Aws.Lambda.AspNetCoreServer.csproj | 4 ++-- .../Sentry.Samples.EntityFramework.csproj | 6 +++--- .../Sentry.Samples.GenericHost.csproj | 2 +- .../Sentry.Samples.Google.Cloud.Functions.csproj | 2 +- .../Sentry.Samples.ME.Logging.csproj | 2 +- samples/Sentry.Samples.NLog/Sentry.Samples.NLog.csproj | 2 +- .../Sentry.Samples.Serilog/Sentry.Samples.Serilog.csproj | 4 ++-- 10 files changed, 20 insertions(+), 20 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 1493ae8ea1..da74516e54 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 @@ -5,8 +5,8 @@ - - + + diff --git a/samples/Sentry.Samples.AspNetCore.Grpc/Sentry.Samples.AspNetCore.Grpc.csproj b/samples/Sentry.Samples.AspNetCore.Grpc/Sentry.Samples.AspNetCore.Grpc.csproj index a2e953c9eb..bc3a6359d0 100644 --- a/samples/Sentry.Samples.AspNetCore.Grpc/Sentry.Samples.AspNetCore.Grpc.csproj +++ b/samples/Sentry.Samples.AspNetCore.Grpc/Sentry.Samples.AspNetCore.Grpc.csproj @@ -5,10 +5,10 @@ - - - - + + + + diff --git a/samples/Sentry.Samples.AspNetCore.Serilog/Sentry.Samples.AspNetCore.Serilog.csproj b/samples/Sentry.Samples.AspNetCore.Serilog/Sentry.Samples.AspNetCore.Serilog.csproj index a71c7130aa..b70feb536d 100644 --- a/samples/Sentry.Samples.AspNetCore.Serilog/Sentry.Samples.AspNetCore.Serilog.csproj +++ b/samples/Sentry.Samples.AspNetCore.Serilog/Sentry.Samples.AspNetCore.Serilog.csproj @@ -5,9 +5,9 @@ - - - + + + diff --git a/samples/Sentry.Samples.Aws.Lambda.AspNetCoreServer/Sentry.Samples.Aws.Lambda.AspNetCoreServer.csproj b/samples/Sentry.Samples.Aws.Lambda.AspNetCoreServer/Sentry.Samples.Aws.Lambda.AspNetCoreServer.csproj index cfc73ae22e..790270997e 100644 --- a/samples/Sentry.Samples.Aws.Lambda.AspNetCoreServer/Sentry.Samples.Aws.Lambda.AspNetCoreServer.csproj +++ b/samples/Sentry.Samples.Aws.Lambda.AspNetCoreServer/Sentry.Samples.Aws.Lambda.AspNetCoreServer.csproj @@ -5,8 +5,8 @@ Lambda - - + + diff --git a/samples/Sentry.Samples.EntityFramework/Sentry.Samples.EntityFramework.csproj b/samples/Sentry.Samples.EntityFramework/Sentry.Samples.EntityFramework.csproj index 345e5375f3..2fb9a07109 100644 --- a/samples/Sentry.Samples.EntityFramework/Sentry.Samples.EntityFramework.csproj +++ b/samples/Sentry.Samples.EntityFramework/Sentry.Samples.EntityFramework.csproj @@ -8,11 +8,11 @@ - - + + - + diff --git a/samples/Sentry.Samples.GenericHost/Sentry.Samples.GenericHost.csproj b/samples/Sentry.Samples.GenericHost/Sentry.Samples.GenericHost.csproj index cb8d8086e6..c9daed9dc5 100644 --- a/samples/Sentry.Samples.GenericHost/Sentry.Samples.GenericHost.csproj +++ b/samples/Sentry.Samples.GenericHost/Sentry.Samples.GenericHost.csproj @@ -13,7 +13,7 @@ - + diff --git a/samples/Sentry.Samples.Google.Cloud.Functions/Sentry.Samples.Google.Cloud.Functions.csproj b/samples/Sentry.Samples.Google.Cloud.Functions/Sentry.Samples.Google.Cloud.Functions.csproj index d2303fed9b..d0c9c3f682 100644 --- a/samples/Sentry.Samples.Google.Cloud.Functions/Sentry.Samples.Google.Cloud.Functions.csproj +++ b/samples/Sentry.Samples.Google.Cloud.Functions/Sentry.Samples.Google.Cloud.Functions.csproj @@ -6,7 +6,7 @@ - + diff --git a/samples/Sentry.Samples.ME.Logging/Sentry.Samples.ME.Logging.csproj b/samples/Sentry.Samples.ME.Logging/Sentry.Samples.ME.Logging.csproj index 799236afe2..8feade9e9d 100644 --- a/samples/Sentry.Samples.ME.Logging/Sentry.Samples.ME.Logging.csproj +++ b/samples/Sentry.Samples.ME.Logging/Sentry.Samples.ME.Logging.csproj @@ -10,6 +10,6 @@ - + diff --git a/samples/Sentry.Samples.NLog/Sentry.Samples.NLog.csproj b/samples/Sentry.Samples.NLog/Sentry.Samples.NLog.csproj index 44d55e8422..203f645468 100644 --- a/samples/Sentry.Samples.NLog/Sentry.Samples.NLog.csproj +++ b/samples/Sentry.Samples.NLog/Sentry.Samples.NLog.csproj @@ -6,7 +6,7 @@ - + true diff --git a/samples/Sentry.Samples.Serilog/Sentry.Samples.Serilog.csproj b/samples/Sentry.Samples.Serilog/Sentry.Samples.Serilog.csproj index 92c2257c57..b0e929ae26 100644 --- a/samples/Sentry.Samples.Serilog/Sentry.Samples.Serilog.csproj +++ b/samples/Sentry.Samples.Serilog/Sentry.Samples.Serilog.csproj @@ -7,8 +7,8 @@ - - + + From 5c048d42b17c28bcb9e1142026717dffeb3c4941 Mon Sep 17 00:00:00 2001 From: Stefan Jandl Date: Fri, 7 Jul 2023 14:20:52 +0200 Subject: [PATCH 098/142] pin everything (#2466) --- Directory.Build.props | 4 ++-- .../Sentry.Benchmarks.csproj | 4 ++-- .../Sentry.Samples.AspNetCore.Grpc.csproj | 2 +- test/Directory.Build.props | 24 +++++++++---------- .../TestUtils.DeviceTests.Runners.csproj | 6 ++--- .../DeviceTests/TestUtils.DeviceTests.csproj | 6 ++--- test/MauiTestUtils/Directory.Build.props | 4 ++-- .../Sentry.Log4Net.Tests.csproj | 2 +- .../Sentry.NLog.Tests.csproj | 2 +- 9 files changed, 27 insertions(+), 27 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index a4725a5b84..2ecab2089f 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -85,8 +85,8 @@ - - + + diff --git a/benchmarks/Sentry.Benchmarks/Sentry.Benchmarks.csproj b/benchmarks/Sentry.Benchmarks/Sentry.Benchmarks.csproj index e17e95ab59..0e41142686 100644 --- a/benchmarks/Sentry.Benchmarks/Sentry.Benchmarks.csproj +++ b/benchmarks/Sentry.Benchmarks/Sentry.Benchmarks.csproj @@ -7,8 +7,8 @@ - - + + diff --git a/samples/Sentry.Samples.AspNetCore.Grpc/Sentry.Samples.AspNetCore.Grpc.csproj b/samples/Sentry.Samples.AspNetCore.Grpc/Sentry.Samples.AspNetCore.Grpc.csproj index bc3a6359d0..22e4aedb9b 100644 --- a/samples/Sentry.Samples.AspNetCore.Grpc/Sentry.Samples.AspNetCore.Grpc.csproj +++ b/samples/Sentry.Samples.AspNetCore.Grpc/Sentry.Samples.AspNetCore.Grpc.csproj @@ -5,7 +5,7 @@ - + diff --git a/test/Directory.Build.props b/test/Directory.Build.props index 353cbe9e7c..0b3d74d32c 100644 --- a/test/Directory.Build.props +++ b/test/Directory.Build.props @@ -47,30 +47,30 @@ - - - - - - + + + + + + - - - + + + - + - - + + diff --git a/test/MauiTestUtils/DeviceTests.Runners/TestUtils.DeviceTests.Runners.csproj b/test/MauiTestUtils/DeviceTests.Runners/TestUtils.DeviceTests.Runners.csproj index 01cc2d33dd..4397e5a5e3 100644 --- a/test/MauiTestUtils/DeviceTests.Runners/TestUtils.DeviceTests.Runners.csproj +++ b/test/MauiTestUtils/DeviceTests.Runners/TestUtils.DeviceTests.Runners.csproj @@ -19,9 +19,9 @@ - - - + + + diff --git a/test/MauiTestUtils/DeviceTests/TestUtils.DeviceTests.csproj b/test/MauiTestUtils/DeviceTests/TestUtils.DeviceTests.csproj index a58689a8a3..2dd5fd8380 100644 --- a/test/MauiTestUtils/DeviceTests/TestUtils.DeviceTests.csproj +++ b/test/MauiTestUtils/DeviceTests/TestUtils.DeviceTests.csproj @@ -17,9 +17,9 @@ - - - + + + diff --git a/test/MauiTestUtils/Directory.Build.props b/test/MauiTestUtils/Directory.Build.props index e4271b88a6..dd80383b88 100644 --- a/test/MauiTestUtils/Directory.Build.props +++ b/test/MauiTestUtils/Directory.Build.props @@ -6,8 +6,8 @@ - - + + diff --git a/test/Sentry.Log4Net.Tests/Sentry.Log4Net.Tests.csproj b/test/Sentry.Log4Net.Tests/Sentry.Log4Net.Tests.csproj index fa3fbbb9ce..0d29e4dade 100644 --- a/test/Sentry.Log4Net.Tests/Sentry.Log4Net.Tests.csproj +++ b/test/Sentry.Log4Net.Tests/Sentry.Log4Net.Tests.csproj @@ -5,7 +5,7 @@ - + diff --git a/test/Sentry.NLog.Tests/Sentry.NLog.Tests.csproj b/test/Sentry.NLog.Tests/Sentry.NLog.Tests.csproj index 9eba4153f1..0702c21efe 100644 --- a/test/Sentry.NLog.Tests/Sentry.NLog.Tests.csproj +++ b/test/Sentry.NLog.Tests/Sentry.NLog.Tests.csproj @@ -5,7 +5,7 @@ - + From 695406d0b0e78b486447161dbab74b10c28b1161 Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Mon, 10 Jul 2023 00:58:24 -0700 Subject: [PATCH 099/142] chore: Using `latest` in vulnerabilities workflow (#2048) --- .github/workflows/vulnerabilities.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/vulnerabilities.yml b/.github/workflows/vulnerabilities.yml index 5e54c80fe7..43c3709a28 100644 --- a/.github/workflows/vulnerabilities.yml +++ b/.github/workflows/vulnerabilities.yml @@ -14,9 +14,7 @@ jobs: strategy: fail-fast: false matrix: - # Using macos-12 because we need Xcode 13.3 or later to build Sentry.Samples.Maui. (macos-latest currently points at macos-11 which uses Xcode 13.2) - # Using windows-2019 because windows-latest is much slower and we don't need anything in particular from it. - os: [ubuntu-latest, windows-2019, macos-12] + os: [ubuntu-latest, windows-latest, macos-latest] steps: - name: Checkout @@ -29,7 +27,7 @@ jobs: # We only need to restore to check for vulnerable packages - name: Restore .NET Dependencies - run: dotnet restore Sentry-CI-Build-${{ runner.os }}.slnf --nologo + run: dotnet restore --nologo # The dotnet list package command doesn't change its exit code on detection, so tee to a file and scan it # See https://github.com/NuGet/Home/issues/11315#issuecomment-1243055173 From 5a8a02437a39f92677bd1dc3c90d5a0332209e62 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Thu, 13 Jul 2023 11:04:46 +1200 Subject: [PATCH 100/142] Otel better init and additional target frameworks (#2467) Additional TFMs and improved initialisation for Sentry.OpenTelemetry --- Sentry.Full.sln | 47 +-- Sentry.sln | 18 +- .../App_Start/BundleConfig.cs | 30 ++ .../App_Start/FilterConfig.cs | 13 + .../App_Start/RouteConfig.cs | 23 ++ .../Controllers/HomeController.cs | 43 +++ .../Global.asax | 1 + .../Global.asax.cs | 70 +++++ .../Properties/AssemblyInfo.cs | 35 +++ .../README.md | 37 +++ ...Sentry.Samples.OpenTelemetry.AspNet.csproj | 277 ++++++++++++++++++ .../Telemetry.cs | 14 + .../Views/Home/About.cshtml | 9 + .../Views/Home/Index.cshtml | 12 + .../Views/Shared/Error.cshtml | 13 + .../Views/Shared/_Layout.cshtml | 35 +++ .../Views/Web.config | 43 +++ .../Views/_ViewStart.cshtml | 3 + .../Web.Debug.config | 30 ++ .../Web.Release.config | 31 ++ .../Web.config | 105 +++++++ .../favicon.ico | Bin 0 -> 32038 bytes .../packages.config | 44 +++ .../sentry-nuget.png | Bin 0 -> 6250 bytes .../Program.cs | 38 ++- .../README.md | 36 +++ .../{DiagnosticsConfig.cs => Telemetry.cs} | 2 +- .../SentryWebHostBuilderExtensions.cs | 1 - .../Sentry.OpenTelemetry.csproj | 29 +- .../SentryOptionsExtensions.cs | 40 ++- src/Sentry.OpenTelemetry/SentryPropagator.cs | 2 +- .../SentrySpanProcessor.cs | 11 +- src/Sentry/Sentry.csproj | 1 + .../ActivitySourceTests.cs | 29 +- .../OpenTelemetry/ATTRIBUTION.txt | 18 ++ .../OpenTelemetry/TestSampler.cs | 35 +++ .../Sentry.OpenTelemetry.Tests.csproj | 23 +- .../SentrySpanProcessorTests.cs | 4 + 38 files changed, 1121 insertions(+), 81 deletions(-) create mode 100644 samples/Sentry.Samples.OpenTelemetry.AspNet/App_Start/BundleConfig.cs create mode 100644 samples/Sentry.Samples.OpenTelemetry.AspNet/App_Start/FilterConfig.cs create mode 100644 samples/Sentry.Samples.OpenTelemetry.AspNet/App_Start/RouteConfig.cs create mode 100644 samples/Sentry.Samples.OpenTelemetry.AspNet/Controllers/HomeController.cs create mode 100644 samples/Sentry.Samples.OpenTelemetry.AspNet/Global.asax create mode 100644 samples/Sentry.Samples.OpenTelemetry.AspNet/Global.asax.cs create mode 100644 samples/Sentry.Samples.OpenTelemetry.AspNet/Properties/AssemblyInfo.cs create mode 100644 samples/Sentry.Samples.OpenTelemetry.AspNet/README.md create mode 100644 samples/Sentry.Samples.OpenTelemetry.AspNet/Sentry.Samples.OpenTelemetry.AspNet.csproj create mode 100644 samples/Sentry.Samples.OpenTelemetry.AspNet/Telemetry.cs create mode 100644 samples/Sentry.Samples.OpenTelemetry.AspNet/Views/Home/About.cshtml create mode 100644 samples/Sentry.Samples.OpenTelemetry.AspNet/Views/Home/Index.cshtml create mode 100644 samples/Sentry.Samples.OpenTelemetry.AspNet/Views/Shared/Error.cshtml create mode 100644 samples/Sentry.Samples.OpenTelemetry.AspNet/Views/Shared/_Layout.cshtml create mode 100644 samples/Sentry.Samples.OpenTelemetry.AspNet/Views/Web.config create mode 100644 samples/Sentry.Samples.OpenTelemetry.AspNet/Views/_ViewStart.cshtml create mode 100644 samples/Sentry.Samples.OpenTelemetry.AspNet/Web.Debug.config create mode 100644 samples/Sentry.Samples.OpenTelemetry.AspNet/Web.Release.config create mode 100644 samples/Sentry.Samples.OpenTelemetry.AspNet/Web.config create mode 100644 samples/Sentry.Samples.OpenTelemetry.AspNet/favicon.ico create mode 100644 samples/Sentry.Samples.OpenTelemetry.AspNet/packages.config create mode 100644 samples/Sentry.Samples.OpenTelemetry.AspNet/sentry-nuget.png create mode 100644 samples/Sentry.Samples.OpenTelemetry.AspNetCore/README.md rename samples/Sentry.Samples.OpenTelemetry.AspNetCore/{DiagnosticsConfig.cs => Telemetry.cs} (86%) create mode 100644 test/Sentry.OpenTelemetry.Tests/OpenTelemetry/ATTRIBUTION.txt create mode 100644 test/Sentry.OpenTelemetry.Tests/OpenTelemetry/TestSampler.cs diff --git a/Sentry.Full.sln b/Sentry.Full.sln index ac329df3d9..239ca14d76 100644 --- a/Sentry.Full.sln +++ b/Sentry.Full.sln @@ -28,9 +28,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".root", ".root", "{9D7E2F87 CONTRIBUTING.md = CONTRIBUTING.md Directory.Build.props = Directory.Build.props Directory.Build.targets = Directory.Build.targets - README.md = README.md global.json = global.json GlobalUsings.cs = GlobalUsings.cs + README.md = README.md EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{77454495-55EE-4B40-A089-71B9E8F82E89}" @@ -136,7 +136,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.DiagnosticSource.Tes EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.Samples.Android", "samples\Sentry.Samples.Android\Sentry.Samples.Android.csproj", "{5CB9167E-ED23-4A67-8D3A-B66B0C5196C8}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.DiagnosticSource.IntegrationTests", "test\Sentry.DiagnosticSource.IntegrationTests\Sentry.DiagnosticSource.IntegrationTests.csproj", "{F8120B9C-D4CA-43DA-B5E1-1CFBA7C36E3B}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.DiagnosticSource.IntegrationTests", "test\Sentry.DiagnosticSource.IntegrationTests\Sentry.DiagnosticSource.IntegrationTests.csproj", "{F8120B9C-D4CA-43DA-B5E1-1CFBA7C36E3B}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.Samples.Maui", "samples\Sentry.Samples.Maui\Sentry.Samples.Maui.csproj", "{EBCCABF9-F670-4C8D-AABC-4EB132961929}" EndProject @@ -154,23 +154,23 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestUtils.DeviceTests.Runne EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "MauiTestUtils", "MauiTestUtils", "{3945EBC3-DDF3-4EBB-9401-B3027E87F855}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Samples.Ios", "samples\Sentry.Samples.Ios\Sentry.Samples.Ios.csproj", "{C2876321-A612-4E66-AF33-D3928FA6366F}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.Samples.Ios", "samples\Sentry.Samples.Ios\Sentry.Samples.Ios.csproj", "{C2876321-A612-4E66-AF33-D3928FA6366F}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Testing.CrashableApp", "test\Sentry.Testing.CrashableApp\Sentry.Testing.CrashableApp.csproj", "{DA3CECBB-83BE-441A-852C-077809C48307}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.Testing.CrashableApp", "test\Sentry.Testing.CrashableApp\Sentry.Testing.CrashableApp.csproj", "{DA3CECBB-83BE-441A-852C-077809C48307}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Samples.MacCatalyst", "samples\Sentry.Samples.MacCatalyst\Sentry.Samples.MacCatalyst.csproj", "{3506539B-983C-44FD-BD95-1931562E7919}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.Samples.MacCatalyst", "samples\Sentry.Samples.MacCatalyst\Sentry.Samples.MacCatalyst.csproj", "{3506539B-983C-44FD-BD95-1931562E7919}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Samples.AspNetCore.Blazor.Server", "samples\Sentry.Samples.AspNetCore.Blazor.Server\Sentry.Samples.AspNetCore.Blazor.Server.csproj", "{70066C6C-0A18-4322-A02A-9A0DFE59C02B}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.Samples.AspNetCore.Blazor.Server", "samples\Sentry.Samples.AspNetCore.Blazor.Server\Sentry.Samples.AspNetCore.Blazor.Server.csproj", "{70066C6C-0A18-4322-A02A-9A0DFE59C02B}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Bindings.Android", "src\Sentry.Bindings.Android\Sentry.Bindings.Android.csproj", "{9A23DA57-3F0B-4646-A37A-71A4CE7C94CB}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.Bindings.Android", "src\Sentry.Bindings.Android\Sentry.Bindings.Android.csproj", "{9A23DA57-3F0B-4646-A37A-71A4CE7C94CB}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Bindings.Cocoa", "src\Sentry.Bindings.Cocoa\Sentry.Bindings.Cocoa.csproj", "{F533FD6D-1E10-4F06-BE3A-3B3C713A75A6}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.Bindings.Cocoa", "src\Sentry.Bindings.Cocoa\Sentry.Bindings.Cocoa.csproj", "{F533FD6D-1E10-4F06-BE3A-3B3C713A75A6}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Android.AssemblyReader.Tests", "test\Sentry.Android.AssemblyReader.Tests\Sentry.Android.AssemblyReader.Tests.csproj", "{5E12E053-22AF-4184-8581-3FCFD225617D}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.Android.AssemblyReader.Tests", "test\Sentry.Android.AssemblyReader.Tests\Sentry.Android.AssemblyReader.Tests.csproj", "{5E12E053-22AF-4184-8581-3FCFD225617D}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.AspNetCore.TestUtils", "test\Sentry.AspNetCore.TestUtils\Sentry.AspNetCore.TestUtils.csproj", "{C96CB65D-3D2D-404E-85C0-69A3FC03B48F}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.AspNetCore.TestUtils", "test\Sentry.AspNetCore.TestUtils\Sentry.AspNetCore.TestUtils.csproj", "{C96CB65D-3D2D-404E-85C0-69A3FC03B48F}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Android.AssemblyReader", "src\Sentry.Android.AssemblyReader\Sentry.Android.AssemblyReader.csproj", "{ED5E4F7E-8267-4F3C-BD2A-779AC9BF3D7C}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.Android.AssemblyReader", "src\Sentry.Android.AssemblyReader\Sentry.Android.AssemblyReader.csproj", "{ED5E4F7E-8267-4F3C-BD2A-779AC9BF3D7C}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.Profiling", "src\Sentry.Profiling\Sentry.Profiling.csproj", "{BD6CEF44-E05E-4C22-8D2F-0558A93DD2D6}" EndProject @@ -178,17 +178,19 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.Profiling.Tests", "t EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.Samples.Console.Profiling", "samples\Sentry.Samples.Console.Profiling\Sentry.Samples.Console.Profiling.csproj", "{0F84C0BB-FDD4-43A9-B594-923EB10C8E3F}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.AzureFunctions.Worker", "src\Sentry.AzureFunctions.Worker\Sentry.AzureFunctions.Worker.csproj", "{DDDF66BE-EC0E-4488-8A35-3D8BEB63F2DB}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.AzureFunctions.Worker", "src\Sentry.AzureFunctions.Worker\Sentry.AzureFunctions.Worker.csproj", "{DDDF66BE-EC0E-4488-8A35-3D8BEB63F2DB}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.Samples.AzureFunctions.Worker", "samples\Sentry.Samples.AzureFunctions.Worker\Sentry.Samples.AzureFunctions.Worker.csproj", "{C38B2A7A-0B21-41BA-8AE4-5D0A2AB6B029}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Samples.AzureFunctions.Worker", "samples\Sentry.Samples.AzureFunctions.Worker\Sentry.Samples.AzureFunctions.Worker.csproj", "{C38B2A7A-0B21-41BA-8AE4-5D0A2AB6B029}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.AzureFunctions.Worker.Tests", "test\Sentry.AzureFunctions.Worker.Tests\Sentry.AzureFunctions.Worker.Tests.csproj", "{CCABBDB9-CDEF-4DE6-9264-69797E8831DF}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.AzureFunctions.Worker.Tests", "test\Sentry.AzureFunctions.Worker.Tests\Sentry.AzureFunctions.Worker.Tests.csproj", "{CCABBDB9-CDEF-4DE6-9264-69797E8831DF}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.OpenTelemetry", "src\Sentry.OpenTelemetry\Sentry.OpenTelemetry.csproj", "{94C5E990-50EE-462F-B83F-B4357F549500}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.OpenTelemetry", "src\Sentry.OpenTelemetry\Sentry.OpenTelemetry.csproj", "{94C5E990-50EE-462F-B83F-B4357F549500}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.OpenTelemetry.Tests", "test\Sentry.OpenTelemetry.Tests\Sentry.OpenTelemetry.Tests.csproj", "{C51A781A-C568-4E1A-834C-8E4EFA3C5B54}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.OpenTelemetry.Tests", "test\Sentry.OpenTelemetry.Tests\Sentry.OpenTelemetry.Tests.csproj", "{C51A781A-C568-4E1A-834C-8E4EFA3C5B54}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.Samples.OpenTelemetry.AspNetCore", "samples\Sentry.Samples.OpenTelemetry.AspNetCore\Sentry.Samples.OpenTelemetry.AspNetCore.csproj", "{2502D3A0-7628-4768-A430-0854BEB1E7A5}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Samples.OpenTelemetry.AspNetCore", "samples\Sentry.Samples.OpenTelemetry.AspNetCore\Sentry.Samples.OpenTelemetry.AspNetCore.csproj", "{2502D3A0-7628-4768-A430-0854BEB1E7A5}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Samples.OpenTelemetry.AspNet", "samples\Sentry.Samples.OpenTelemetry.AspNet\Sentry.Samples.OpenTelemetry.AspNet.csproj", "{DDD39CD0-F323-460E-95EA-CFAC1606DDAC}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -400,15 +402,15 @@ Global {C2876321-A612-4E66-AF33-D3928FA6366F}.Release|Any CPU.ActiveCfg = Release|Any CPU {C2876321-A612-4E66-AF33-D3928FA6366F}.Release|Any CPU.Build.0 = Release|Any CPU {C2876321-A612-4E66-AF33-D3928FA6366F}.Release|Any CPU.Deploy.0 = Release|Any CPU - {DA3CECBB-83BE-441A-852C-077809C48307}.Release|Any CPU.ActiveCfg = Release|Any CPU - {DA3CECBB-83BE-441A-852C-077809C48307}.Release|Any CPU.Build.0 = Release|Any CPU {DA3CECBB-83BE-441A-852C-077809C48307}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DA3CECBB-83BE-441A-852C-077809C48307}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DA3CECBB-83BE-441A-852C-077809C48307}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DA3CECBB-83BE-441A-852C-077809C48307}.Release|Any CPU.Build.0 = Release|Any CPU {3506539B-983C-44FD-BD95-1931562E7919}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3506539B-983C-44FD-BD95-1931562E7919}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3506539B-983C-44FD-BD95-1931562E7919}.Debug|Any CPU.Deploy.0 = Debug|Any CPU {3506539B-983C-44FD-BD95-1931562E7919}.Release|Any CPU.ActiveCfg = Release|Any CPU {3506539B-983C-44FD-BD95-1931562E7919}.Release|Any CPU.Build.0 = Release|Any CPU - {3506539B-983C-44FD-BD95-1931562E7919}.Debug|Any CPU.Deploy.0 = Debug|Any CPU {70066C6C-0A18-4322-A02A-9A0DFE59C02B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {70066C6C-0A18-4322-A02A-9A0DFE59C02B}.Debug|Any CPU.Build.0 = Debug|Any CPU {70066C6C-0A18-4322-A02A-9A0DFE59C02B}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -469,6 +471,10 @@ Global {2502D3A0-7628-4768-A430-0854BEB1E7A5}.Debug|Any CPU.Build.0 = Debug|Any CPU {2502D3A0-7628-4768-A430-0854BEB1E7A5}.Release|Any CPU.ActiveCfg = Release|Any CPU {2502D3A0-7628-4768-A430-0854BEB1E7A5}.Release|Any CPU.Build.0 = Release|Any CPU + {DDD39CD0-F323-460E-95EA-CFAC1606DDAC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DDD39CD0-F323-460E-95EA-CFAC1606DDAC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DDD39CD0-F323-460E-95EA-CFAC1606DDAC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DDD39CD0-F323-460E-95EA-CFAC1606DDAC}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -544,6 +550,7 @@ Global {94C5E990-50EE-462F-B83F-B4357F549500} = {AF6AF4C7-8AA2-4D59-8064-2D79560904EB} {C51A781A-C568-4E1A-834C-8E4EFA3C5B54} = {83263231-1A2A-4733-B759-EEFF14E8C5D5} {2502D3A0-7628-4768-A430-0854BEB1E7A5} = {77454495-55EE-4B40-A089-71B9E8F82E89} + {DDD39CD0-F323-460E-95EA-CFAC1606DDAC} = {77454495-55EE-4B40-A089-71B9E8F82E89} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {0C652B1A-DF72-4EE5-A98B-194FE2C054F6} diff --git a/Sentry.sln b/Sentry.sln index 1a07e8d2b9..9e1441ea3e 100644 --- a/Sentry.sln +++ b/Sentry.sln @@ -160,13 +160,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.Samples.AzureFunctio EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.AzureFunctions.Worker.Tests", "test\Sentry.AzureFunctions.Worker.Tests\Sentry.AzureFunctions.Worker.Tests.csproj", "{8639DB06-1F74-4890-8974-613305C1B6C5}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SingleFileTestApp", "test\SingleFileTestApp\SingleFileTestApp.csproj", "{08C99C2F-08D8-44A3-981F-768DC84DCEC7}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SingleFileTestApp", "test\SingleFileTestApp\SingleFileTestApp.csproj", "{08C99C2F-08D8-44A3-981F-768DC84DCEC7}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.OpenTelemetry", "src\Sentry.OpenTelemetry\Sentry.OpenTelemetry.csproj", "{D4B29F85-5029-462E-A67E-8B7183A0406D}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.OpenTelemetry", "src\Sentry.OpenTelemetry\Sentry.OpenTelemetry.csproj", "{D4B29F85-5029-462E-A67E-8B7183A0406D}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.OpenTelemetry.Tests", "test\Sentry.OpenTelemetry.Tests\Sentry.OpenTelemetry.Tests.csproj", "{A58DE854-6576-4E07-98BF-03B9DCCDBF9A}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.OpenTelemetry.Tests", "test\Sentry.OpenTelemetry.Tests\Sentry.OpenTelemetry.Tests.csproj", "{A58DE854-6576-4E07-98BF-03B9DCCDBF9A}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Samples.OpenTelemetry.AspNetCore", "samples\Sentry.Samples.OpenTelemetry.AspNetCore\Sentry.Samples.OpenTelemetry.AspNetCore.csproj", "{6F791E40-49A8-4A67-81DB-6913E519310A}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.Samples.OpenTelemetry.AspNetCore", "samples\Sentry.Samples.OpenTelemetry.AspNetCore\Sentry.Samples.OpenTelemetry.AspNetCore.csproj", "{6F791E40-49A8-4A67-81DB-6913E519310A}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -382,6 +382,10 @@ Global {8639DB06-1F74-4890-8974-613305C1B6C5}.Debug|Any CPU.Build.0 = Debug|Any CPU {8639DB06-1F74-4890-8974-613305C1B6C5}.Release|Any CPU.ActiveCfg = Release|Any CPU {8639DB06-1F74-4890-8974-613305C1B6C5}.Release|Any CPU.Build.0 = Release|Any CPU + {08C99C2F-08D8-44A3-981F-768DC84DCEC7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {08C99C2F-08D8-44A3-981F-768DC84DCEC7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {08C99C2F-08D8-44A3-981F-768DC84DCEC7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {08C99C2F-08D8-44A3-981F-768DC84DCEC7}.Release|Any CPU.Build.0 = Release|Any CPU {D4B29F85-5029-462E-A67E-8B7183A0406D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D4B29F85-5029-462E-A67E-8B7183A0406D}.Debug|Any CPU.Build.0 = Debug|Any CPU {D4B29F85-5029-462E-A67E-8B7183A0406D}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -390,10 +394,6 @@ Global {A58DE854-6576-4E07-98BF-03B9DCCDBF9A}.Debug|Any CPU.Build.0 = Debug|Any CPU {A58DE854-6576-4E07-98BF-03B9DCCDBF9A}.Release|Any CPU.ActiveCfg = Release|Any CPU {A58DE854-6576-4E07-98BF-03B9DCCDBF9A}.Release|Any CPU.Build.0 = Release|Any CPU - {08C99C2F-08D8-44A3-981F-768DC84DCEC7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {08C99C2F-08D8-44A3-981F-768DC84DCEC7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {08C99C2F-08D8-44A3-981F-768DC84DCEC7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {08C99C2F-08D8-44A3-981F-768DC84DCEC7}.Release|Any CPU.Build.0 = Release|Any CPU {6F791E40-49A8-4A67-81DB-6913E519310A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6F791E40-49A8-4A67-81DB-6913E519310A}.Debug|Any CPU.Build.0 = Debug|Any CPU {6F791E40-49A8-4A67-81DB-6913E519310A}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -458,9 +458,9 @@ Global {203C4556-16DE-46CE-9FAF-C93D8B30D04A} = {AF6AF4C7-8AA2-4D59-8064-2D79560904EB} {243B75FF-1501-4DB7-B933-EE43D960EB90} = {77454495-55EE-4B40-A089-71B9E8F82E89} {8639DB06-1F74-4890-8974-613305C1B6C5} = {83263231-1A2A-4733-B759-EEFF14E8C5D5} + {08C99C2F-08D8-44A3-981F-768DC84DCEC7} = {83263231-1A2A-4733-B759-EEFF14E8C5D5} {D4B29F85-5029-462E-A67E-8B7183A0406D} = {AF6AF4C7-8AA2-4D59-8064-2D79560904EB} {A58DE854-6576-4E07-98BF-03B9DCCDBF9A} = {83263231-1A2A-4733-B759-EEFF14E8C5D5} - {08C99C2F-08D8-44A3-981F-768DC84DCEC7} = {83263231-1A2A-4733-B759-EEFF14E8C5D5} {6F791E40-49A8-4A67-81DB-6913E519310A} = {77454495-55EE-4B40-A089-71B9E8F82E89} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution diff --git a/samples/Sentry.Samples.OpenTelemetry.AspNet/App_Start/BundleConfig.cs b/samples/Sentry.Samples.OpenTelemetry.AspNet/App_Start/BundleConfig.cs new file mode 100644 index 0000000000..09092e9b03 --- /dev/null +++ b/samples/Sentry.Samples.OpenTelemetry.AspNet/App_Start/BundleConfig.cs @@ -0,0 +1,30 @@ +using System.Web; +using System.Web.Optimization; + +namespace Sentry.Samples.OpenTelemetry.AspNet +{ + public static class BundleConfig + { + // For more information on bundling, visit https://go.microsoft.com/fwlink/?LinkId=301862 + public static void RegisterBundles(BundleCollection bundles) + { + bundles.Add(new ScriptBundle("~/bundles/jquery").Include( + "~/Scripts/jquery-{version}.js")); + + bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include( + "~/Scripts/jquery.validate*")); + + // Use the development version of Modernizr to develop with and learn from. Then, when you're + // ready for production, use the build tool at https://modernizr.com to pick only the tests you need. + bundles.Add(new ScriptBundle("~/bundles/modernizr").Include( + "~/Scripts/modernizr-*")); + + bundles.Add(new Bundle("~/bundles/bootstrap").Include( + "~/Scripts/bootstrap.js")); + + bundles.Add(new StyleBundle("~/Content/css").Include( + "~/Content/bootstrap.css", + "~/Content/site.css")); + } + } +} diff --git a/samples/Sentry.Samples.OpenTelemetry.AspNet/App_Start/FilterConfig.cs b/samples/Sentry.Samples.OpenTelemetry.AspNet/App_Start/FilterConfig.cs new file mode 100644 index 0000000000..ec4afb4af2 --- /dev/null +++ b/samples/Sentry.Samples.OpenTelemetry.AspNet/App_Start/FilterConfig.cs @@ -0,0 +1,13 @@ +using System.Web; +using System.Web.Mvc; + +namespace Sentry.Samples.OpenTelemetry.AspNet +{ + public static class FilterConfig + { + public static void RegisterGlobalFilters(GlobalFilterCollection filters) + { + filters.Add(new HandleErrorAttribute()); + } + } +} diff --git a/samples/Sentry.Samples.OpenTelemetry.AspNet/App_Start/RouteConfig.cs b/samples/Sentry.Samples.OpenTelemetry.AspNet/App_Start/RouteConfig.cs new file mode 100644 index 0000000000..85946508fd --- /dev/null +++ b/samples/Sentry.Samples.OpenTelemetry.AspNet/App_Start/RouteConfig.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using System.Web.Mvc; +using System.Web.Routing; + +namespace Sentry.Samples.OpenTelemetry.AspNet +{ + public static class RouteConfig + { + public static void RegisterRoutes(RouteCollection routes) + { + routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); + + routes.MapRoute( + name: "Default", + url: "{controller}/{action}/{id}", + defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } + ); + } + } +} diff --git a/samples/Sentry.Samples.OpenTelemetry.AspNet/Controllers/HomeController.cs b/samples/Sentry.Samples.OpenTelemetry.AspNet/Controllers/HomeController.cs new file mode 100644 index 0000000000..0bd74d9fe0 --- /dev/null +++ b/samples/Sentry.Samples.OpenTelemetry.AspNet/Controllers/HomeController.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Threading; +using System.Web; +using System.Web.Mvc; +using Sentry; + +namespace Sentry.Samples.OpenTelemetry.AspNet.Controllers +{ + public class HomeController : Controller + { + public ActionResult Index() + { + SentrySdk.CaptureMessage("Hello Sentry!"); + return View(); + } + + public ActionResult About() + { + ViewBag.Message = "Sentry ASP.NET Sample."; + DoWork(); + return View(); + } + + private void DoWork() + { + // Instrument this using OpenTelemetry for distributed tracing and performance. + // Using Sentry's OpenTelemetry integration these traces will be sent to Sentry. + using (var activity = Telemetry.ActivitySource.StartActivity("DoWork")) + { + activity.AddTag("work", "100ms"); + Thread.Sleep(100); // Simulate some work + } + } + + public ActionResult Breakup() + { + throw new NotImplementedException(); // Simulate a bug... Sentry captures these too :-) + } + } +} \ No newline at end of file diff --git a/samples/Sentry.Samples.OpenTelemetry.AspNet/Global.asax b/samples/Sentry.Samples.OpenTelemetry.AspNet/Global.asax new file mode 100644 index 0000000000..0e746273da --- /dev/null +++ b/samples/Sentry.Samples.OpenTelemetry.AspNet/Global.asax @@ -0,0 +1 @@ +<%@ Application Codebehind="Global.asax.cs" Inherits="Sentry.Samples.OpenTelemetry.AspNet.MvcApplication" Language="C#" %> diff --git a/samples/Sentry.Samples.OpenTelemetry.AspNet/Global.asax.cs b/samples/Sentry.Samples.OpenTelemetry.AspNet/Global.asax.cs new file mode 100644 index 0000000000..86f49bf1f7 --- /dev/null +++ b/samples/Sentry.Samples.OpenTelemetry.AspNet/Global.asax.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using System.Web.Mvc; +using System.Web.Optimization; +using System.Web.Routing; +using OpenTelemetry.Resources; +using OpenTelemetry; +using OpenTelemetry.Trace; +using Sentry.AspNet; +using Sentry.Extensibility; +using Sentry.OpenTelemetry; +using Microsoft.Extensions.DependencyInjection; + +namespace Sentry.Samples.OpenTelemetry.AspNet +{ + public class MvcApplication : System.Web.HttpApplication + { + private IDisposable _sentry; + private TracerProvider _tracerProvider; + + protected void Application_Start() + { + var builder = Sdk.CreateTracerProviderBuilder() + .AddAspNetInstrumentation() + .AddSource(Telemetry.ServiceName) + .SetResourceBuilder( + ResourceBuilder.CreateDefault() + .AddService(serviceName: Telemetry.ServiceName, serviceVersion: "1.0.0") + ); + + // Initialize Sentry to capture AppDomain unhandled exceptions and more. + _sentry = SentrySdk.Init(o => + { + //o.Dsn = "...Your DSN..."; + o.Debug = true; + o.TracesSampleRate = 1.0; + o.AddAspNet(RequestSize.Always); + o.UseOpenTelemetry(builder); + }); + + _tracerProvider = builder.Build(); + + AreaRegistration.RegisterAllAreas(); + FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); + RouteConfig.RegisterRoutes(RouteTable.Routes); + BundleConfig.RegisterBundles(BundleTable.Bundles); + } + + // Global error catcher + protected void Application_Error() => Server.CaptureLastError(); + + protected void Application_BeginRequest() + { + Context.StartSentryTransaction(); + } + + protected void Application_EndRequest() + { + Context.FinishSentryTransaction(); + } + + protected void Application_End() + { + _tracerProvider?.Dispose(); + _sentry?.Dispose(); + } + } +} diff --git a/samples/Sentry.Samples.OpenTelemetry.AspNet/Properties/AssemblyInfo.cs b/samples/Sentry.Samples.OpenTelemetry.AspNet/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..870d18f15e --- /dev/null +++ b/samples/Sentry.Samples.OpenTelemetry.AspNet/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Sentry.Samples.OpenTelemetry.AspNet")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Sentry.Samples.OpenTelemetry.AspNet")] +[assembly: AssemblyCopyright("Copyright © 2023")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("f44017f9-e0d9-404b-a2d6-849e16b06605")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/samples/Sentry.Samples.OpenTelemetry.AspNet/README.md b/samples/Sentry.Samples.OpenTelemetry.AspNet/README.md new file mode 100644 index 0000000000..667fd5a47b --- /dev/null +++ b/samples/Sentry.Samples.OpenTelemetry.AspNet/README.md @@ -0,0 +1,37 @@ +# Overview + +This sample demonstrates how an ASP.NET application that is instrumented with the OpenTelemetry .NET SDK can be +configured to send trace information to Sentry, with the following initialization code in `Application_Start`: + +```csharp +var builder = Sdk.CreateTracerProviderBuilder() + .AddAspNetInstrumentation() + .AddSource(Telemetry.ServiceName) + .SetResourceBuilder( + ResourceBuilder.CreateDefault() + .AddService(serviceName: Telemetry.ServiceName, serviceVersion: "1.0.0") + ); + +// Initialize Sentry to capture AppDomain unhandled exceptions and more. +_sentry = SentrySdk.Init(o => +{ + //o.Dsn = "...Your DSN..."; + o.Debug = true; + o.TracesSampleRate = 1.0; + o.AddAspNet(RequestSize.Always); + o.UseOpenTelemetry(builder); +}); + +_tracerProvider = builder.Build(); +``` + +## Customizing propagation + +Sentry's OpenTelemetry integration sets the DefaultTextMapPropagator for OpenTelemetry to a `SentryPropagator`. This +propagator ensures that both the W3C baggage header and the sentry-trace header get propagated from upstream services +and/or to downstream services. + +If you need to further customize header propagation in your application (e.g. propagating other vendor specific headers) +then you can do so by creating a `CompositeTextMapPropagator` consisting of the custom propagator(s) you need plus the +`SentryPropagator`. You can supply this as a second (optional) parameter to the `UseOpenTelemetry` extension method +on `SentryOptions` above. diff --git a/samples/Sentry.Samples.OpenTelemetry.AspNet/Sentry.Samples.OpenTelemetry.AspNet.csproj b/samples/Sentry.Samples.OpenTelemetry.AspNet/Sentry.Samples.OpenTelemetry.AspNet.csproj new file mode 100644 index 0000000000..9474c84cf8 --- /dev/null +++ b/samples/Sentry.Samples.OpenTelemetry.AspNet/Sentry.Samples.OpenTelemetry.AspNet.csproj @@ -0,0 +1,277 @@ + + + + + + Debug + AnyCPU + + + 2.0 + {DDD39CD0-F323-460E-95EA-CFAC1606DDAC} + {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} + Library + Properties + Sentry.Samples.OpenTelemetry.AspNet + Sentry.Samples.OpenTelemetry.AspNet + v4.8 + false + true + + 44329 + + + + + + + + + true + full + false + bin\ + DEBUG;TRACE + prompt + 4 + + + true + pdbonly + true + bin\ + TRACE + prompt + 4 + + + + + ..\packages\Microsoft.Extensions.FileProviders.Abstractions.2.1.0\lib\netstandard2.0\Microsoft.Extensions.FileProviders.Abstractions.dll + + + ..\packages\Microsoft.Extensions.Hosting.Abstractions.2.1.0\lib\netstandard2.0\Microsoft.Extensions.Hosting.Abstractions.dll + + + ..\packages\OpenTelemetry.Extensions.Hosting.1.5.1\lib\netstandard2.0\OpenTelemetry.Extensions.Hosting.dll + + + ..\packages\OpenTelemetry.Instrumentation.AspNet.1.0.0-rc9.9\lib\net462\OpenTelemetry.Instrumentation.AspNet.dll + + + ..\packages\OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule.1.0.0-rc9.9\lib\net462\OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule.dll + + + + + + + + + + + + + + + + + + + + + + True + ..\packages\Microsoft.Web.Infrastructure.2.0.1\lib\net40\Microsoft.Web.Infrastructure.dll + + + + + + + True + ..\packages\Microsoft.AspNet.WebPages.3.2.9\lib\net45\System.Web.Helpers.dll + + + True + ..\packages\Microsoft.AspNet.Mvc.5.2.9\lib\net45\System.Web.Mvc.dll + + + ..\packages\Microsoft.AspNet.Web.Optimization.1.1.3\lib\net40\System.Web.Optimization.dll + + + True + ..\packages\Microsoft.AspNet.Razor.3.2.9\lib\net45\System.Web.Razor.dll + + + True + ..\packages\Microsoft.AspNet.WebPages.3.2.9\lib\net45\System.Web.WebPages.dll + + + True + ..\packages\Microsoft.AspNet.WebPages.3.2.9\lib\net45\System.Web.WebPages.Deployment.dll + + + True + ..\packages\Microsoft.AspNet.WebPages.3.2.9\lib\net45\System.Web.WebPages.Razor.dll + + + ..\packages\Newtonsoft.Json.12.0.2\lib\net45\Newtonsoft.Json.dll + + + True + ..\packages\WebGrease.1.6.0\lib\WebGrease.dll + + + True + ..\packages\Antlr.3.5.0.2\lib\Antlr3.Runtime.dll + + + + + ..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.2.0.1\lib\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll + + + + + + + + + Global.asax + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Web.config + + + Web.config + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {22aa261a-f8b4-41e2-95dd-82632eed0aa5} + Sentry.AspNet + + + {d4b29f85-5029-462e-a67e-8b7183a0406d} + Sentry.OpenTelemetry + + + {f2486cc8-fab7-4775-976f-c5a4cf97867f} + Sentry + + + + + 1.5.0 + + + + 10.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + + + + + + + + + True + True + 52915 + / + https://localhost:44329/ + False + False + + + False + + + + + \ No newline at end of file diff --git a/samples/Sentry.Samples.OpenTelemetry.AspNet/Telemetry.cs b/samples/Sentry.Samples.OpenTelemetry.AspNet/Telemetry.cs new file mode 100644 index 0000000000..9a1e24bfed --- /dev/null +++ b/samples/Sentry.Samples.OpenTelemetry.AspNet/Telemetry.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Web; + +namespace Sentry.Samples.OpenTelemetry.AspNet +{ + public static class Telemetry + { + public const string ServiceName = "Sentry.Samples.OpenTelemetry.AspNet"; + public static ActivitySource ActivitySource { get; } = new ActivitySource(ServiceName); + } +} \ No newline at end of file diff --git a/samples/Sentry.Samples.OpenTelemetry.AspNet/Views/Home/About.cshtml b/samples/Sentry.Samples.OpenTelemetry.AspNet/Views/Home/About.cshtml new file mode 100644 index 0000000000..5fa414e162 --- /dev/null +++ b/samples/Sentry.Samples.OpenTelemetry.AspNet/Views/Home/About.cshtml @@ -0,0 +1,9 @@ +@{ + ViewBag.Title = "About"; +} +
+

@ViewBag.Title.

+

@ViewBag.Message

+ +

Use this area to provide additional information.

+
\ No newline at end of file diff --git a/samples/Sentry.Samples.OpenTelemetry.AspNet/Views/Home/Index.cshtml b/samples/Sentry.Samples.OpenTelemetry.AspNet/Views/Home/Index.cshtml new file mode 100644 index 0000000000..04cb581beb --- /dev/null +++ b/samples/Sentry.Samples.OpenTelemetry.AspNet/Views/Home/Index.cshtml @@ -0,0 +1,12 @@ +@{ + ViewBag.Title = "Home Page"; +} + +
+
+

Sentry ASP.NET Sample

+
+ +
\ No newline at end of file diff --git a/samples/Sentry.Samples.OpenTelemetry.AspNet/Views/Shared/Error.cshtml b/samples/Sentry.Samples.OpenTelemetry.AspNet/Views/Shared/Error.cshtml new file mode 100644 index 0000000000..4c9a28a212 --- /dev/null +++ b/samples/Sentry.Samples.OpenTelemetry.AspNet/Views/Shared/Error.cshtml @@ -0,0 +1,13 @@ + + + + + Error + + +
+

Error.

+

An error occurred while processing your request.

+
+ + diff --git a/samples/Sentry.Samples.OpenTelemetry.AspNet/Views/Shared/_Layout.cshtml b/samples/Sentry.Samples.OpenTelemetry.AspNet/Views/Shared/_Layout.cshtml new file mode 100644 index 0000000000..6ebcb57fd8 --- /dev/null +++ b/samples/Sentry.Samples.OpenTelemetry.AspNet/Views/Shared/_Layout.cshtml @@ -0,0 +1,35 @@ + + + + + + @ViewBag.Title - My ASP.NET Application + + + + +
+ @RenderBody() +
+
+ + + + + + + diff --git a/samples/Sentry.Samples.OpenTelemetry.AspNet/Views/Web.config b/samples/Sentry.Samples.OpenTelemetry.AspNet/Views/Web.config new file mode 100644 index 0000000000..571862ce0e --- /dev/null +++ b/samples/Sentry.Samples.OpenTelemetry.AspNet/Views/Web.config @@ -0,0 +1,43 @@ + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/Sentry.Samples.OpenTelemetry.AspNet/Views/_ViewStart.cshtml b/samples/Sentry.Samples.OpenTelemetry.AspNet/Views/_ViewStart.cshtml new file mode 100644 index 0000000000..2de62418c0 --- /dev/null +++ b/samples/Sentry.Samples.OpenTelemetry.AspNet/Views/_ViewStart.cshtml @@ -0,0 +1,3 @@ +@{ + Layout = "~/Views/Shared/_Layout.cshtml"; +} diff --git a/samples/Sentry.Samples.OpenTelemetry.AspNet/Web.Debug.config b/samples/Sentry.Samples.OpenTelemetry.AspNet/Web.Debug.config new file mode 100644 index 0000000000..d7712aaf17 --- /dev/null +++ b/samples/Sentry.Samples.OpenTelemetry.AspNet/Web.Debug.config @@ -0,0 +1,30 @@ + + + + + + + + + + diff --git a/samples/Sentry.Samples.OpenTelemetry.AspNet/Web.Release.config b/samples/Sentry.Samples.OpenTelemetry.AspNet/Web.Release.config new file mode 100644 index 0000000000..28a4d5fcc3 --- /dev/null +++ b/samples/Sentry.Samples.OpenTelemetry.AspNet/Web.Release.config @@ -0,0 +1,31 @@ + + + + + + + + + + + diff --git a/samples/Sentry.Samples.OpenTelemetry.AspNet/Web.config b/samples/Sentry.Samples.OpenTelemetry.AspNet/Web.config new file mode 100644 index 0000000000..7bec6f8353 --- /dev/null +++ b/samples/Sentry.Samples.OpenTelemetry.AspNet/Web.config @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/Sentry.Samples.OpenTelemetry.AspNet/favicon.ico b/samples/Sentry.Samples.OpenTelemetry.AspNet/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..a3a799985c43bc7309d701b2cad129023377dc71 GIT binary patch literal 32038 zcmeHwX>eTEbtY7aYbrGrkNjgie?1jXjZ#zP%3n{}GObKv$BxI7Sl;Bwl5E+Qtj&t8 z*p|m4DO#HoJC-FyvNnp8NP<{Na0LMnTtO21(rBP}?EAiNjWgeO?z`{3ZoURUQlV2d zY1Pqv{m|X_oO91|?^z!6@@~od!@OH>&BN;>c@O+yUfy5w>LccTKJJ&`-k<%M^Zvi( z<$dKp=jCnNX5Qa+M_%6g|IEv~4R84q9|7E=|Ho(Wz3f-0wPjaRL;W*N^>q%^KGRr7 zxbjSORb_c&eO;oV_DZ7ua!sPH=0c+W;`vzJ#j~-x3uj};50#vqo*0w4!LUqs*UCh9 zvy2S%$#8$K4EOa&e@~aBS65_hc~Mpu=454VT2^KzWqEpBA=ME|O;1cn?8p<+{MKJf zbK#@1wzL44m$k(?85=Obido7=C|xWKe%66$z)NrzRwR>?hK?_bbwT z@Da?lBrBL}Zemo1@!9pYRau&!ld17h{f+UV0sY(R{ET$PBB|-=Nr@l-nY6w8HEAw* zRMIQU`24Jl_IFEPcS=_HdrOP5yf81z_?@M>83Vv65$QFr9nPg(wr`Ke8 zaY4ogdnMA*F7a4Q1_uXadTLUpCk;$ZPRRJ^sMOch;rlbvUGc1R9=u;dr9YANbQ<4Z z#P|Cp9BP$FXNPolgyr1XGt$^lFPF}rmBF5rj1Kh5%dforrP8W}_qJL$2qMBS-#%-|s#BPZBSETsn_EBYcr(W5dq( z@f%}C|iN7)YN`^)h7R?Cg}Do*w-!zwZb9=BMp%Wsh@nb22hA zA{`wa8Q;yz6S)zfo%sl08^GF`9csI9BlGnEy#0^Y3b);M+n<(}6jziM7nhe57a1rj zC@(2ISYBL^UtWChKzVWgf%4LW2Tqg_^7jMw`C$KvU+mcakFjV(BGAW9g%CzSyM;Df z143=mq0oxaK-H;o>F3~zJ<(3-j&?|QBn)WJfP#JR zRuA;`N?L83wQt78QIA$(Z)lGQY9r^SFal;LB^qi`8%8@y+mwcGsf~nv)bBy2S7z~9 z=;X@Gglk)^jpbNz?1;`!J3QUfAOp4U$Uxm5>92iT`mek#$>s`)M>;e4{#%HAAcb^8_Ax%ersk|}# z0bd;ZPu|2}18KtvmIo8`1@H~@2ejwo(5rFS`Z4&O{$$+ch2hC0=06Jh`@p+p8LZzY z&2M~8T6X^*X?yQ$3N5EzRv$(FtSxhW>>ABUyp!{484f8(%C1_y)3D%Qgfl_!sz`LTXOjR&L!zPA0qH_iNS!tY{!^2WfD%uT}P zI<~&?@&))5&hPPHVRl9);TPO>@UI2d!^ksb!$9T96V(F){puTsn(}qt_WXNw4VvHj zf;6A_XCvE`Z@}E-IOaG0rs>K>^=Sr&OgT_p;F@v0VCN0Y$r|Lw1?Wjt`AKK~RT*kJ z2>QPuVgLNcF+XKno;WBv$yj@d_WFJbl*#*V_Cwzo@%3n5%z4g21G*PVZ)wM5$A{klYozmGlB zT@u2+s}=f}25%IA!yNcXUr!!1)z(Nqbhojg0lv@7@0UlvUMT)*r;M$d0-t)Z?B1@qQk()o!4fqvfr_I0r7 zy1(NdkHEj#Yu{K>T#We#b#FD=c1XhS{hdTh9+8gy-vkcdkk*QS@y(xxEMb1w6z<^~ zYcETGfB#ibR#ql0EiD;PR$L&Vrh2uRv5t_$;NxC;>7_S5_OXxsi8udY3BUUdi55Sk zcyKM+PQ9YMA%D1kH1q48OFG(Gbl=FmV;yk8o>k%0$rJ8%-IYsHclnYuTskkaiCGkUlkMY~mx&K}XRlKIW;odWIeuKjtbc^8bBOTqK zjj(ot`_j?A6y_h%vxE9o*ntx#PGrnK7AljD_r58ylE*oy@{IY%+mA^!|2vW_`>`aC{#3`#3;D_$^S^cM zRcF+uTO2sICledvFgNMU@A%M)%8JbSLq{dD|2|2Sg8vvh_uV6*Q?F&rKaV{v_qz&y z`f;stIb?Cb2!Cg7CG91Bhu@D@RaIrq-+o+T2fwFu#|j>lD6ZS9-t^5cx>p|?flqUA z;Cgs#V)O#`Aw4$Kr)L5?|7f4izl!;n0jux}tEW$&&YBXz9o{+~HhoiYDJ`w5BVTl&ARya=M7zdy$FEe}iGBur8XE>rhLj&_yDk5D4n2GJZ07u7%zyAfNtOLn;)M?h*Py-Xtql5aJOtL4U8e|!t? z((sc6&OJXrPdVef^wZV&x=Z&~uA7^ix8rly^rEj?#d&~pQ{HN8Yq|fZ#*bXn-26P^ z5!)xRzYO9{u6vx5@q_{FE4#7BipS#{&J7*>y}lTyV94}dfE%Yk>@@pDe&F7J09(-0|wuI|$of-MRfK51#t@t2+U|*s=W; z!Y&t{dS%!4VEEi$efA!#<<7&04?kB}Soprd8*jYv;-Qj~h~4v>{XX~kjF+@Z7<t?^|i z#>_ag2i-CRAM8Ret^rZt*^K?`G|o>1o(mLkewxyA)38k93`<~4VFI?5VB!kBh%NNU zxb8K(^-MU1ImWQxG~nFB-Un;6n{lQz_FfsW9^H$Xcn{;+W^ZcG$0qLM#eNV=vGE@# z1~k&!h4@T|IiI<47@pS|i?Qcl=XZJL#$JKve;booMqDUYY{(xcdj6STDE=n?;fsS1 ze`h~Q{CT$K{+{t+#*I1=&&-UU8M&}AwAxD-rMa=e!{0gQXP@6azBq9(ji11uJF%@5 zCvV`#*?;ZguQ7o|nH%bm*s&jLej#@B35gy32ZAE0`Pz@#j6R&kN5w{O4~1rhDoU zEBdU)%Nl?8zi|DR((u|gg~r$aLYmGMyK%FO*qLvwxK5+cn*`;O`16c!&&XT{$j~5k zXb^fbh1GT-CI*Nj{-?r7HNg=e3E{6rxuluPXY z5Nm8ktc$o4-^SO0|Es_sp!A$8GVwOX+%)cH<;=u#R#nz;7QsHl;J@a{5NUAmAHq4D zIU5@jT!h?kUp|g~iN*!>jM6K!W5ar0v~fWrSHK@})@6Lh#h)C6F6@)&-+C3(zO! z8+kV|B7LctM3DpI*~EYo>vCj>_?x&H;>y0*vKwE0?vi$CLt zfSJB##P|M2dEUDBPKW=9cY-F;L;h3Fs4E2ERdN#NSL7ctAC z?-}_a{*L@GA7JHJudxtDVA{K5Yh*k(%#x4W7w+^ zcb-+ofbT5ieG+@QG2lx&7!MyE2JWDP@$k`M;0`*d+oQmJ2A^de!3c53HFcfW_Wtv< zKghQ;*FifmI}kE4dc@1y-u;@qs|V75Z^|Q0l0?teobTE8tGl@EB?k#q_wUjypJ*R zyEI=DJ^Z+d*&}B_xoWvs27LtH7972qqMxVFcX9}c&JbeNCXUZM0`nQIkf&C}&skSt z^9fw@b^Hb)!^hE2IJq~~GktG#ZWwWG<`@V&ckVR&r=JAO4YniJewVcG`HF;59}=bf zLyz0uxf6MhuSyH#-^!ZbHxYl^mmBVrx) zyrb8sQ*qBd_WXm9c~Of$&ZP$b^)<~0%nt#7y$1Jg$e}WCK>TeUB{P>|b1FAB?%K7>;XiOfd}JQ`|IP#Vf%kVy zXa4;XFZ+>n;F>uX&3|4zqWK2u3c<>q;tzjsb1;d{u;L$-hq3qe@82(ob<3qom#%`+ z;vzYAs7TIMl_O75BXu|r`Qhc4UT*vN$3Oo0kAC!{f2#HexDy|qUpgTF;k{o6|L>7l z=?`=*LXaow1o;oNNLXsGTrvC)$R&{m=94Tf+2iTT3Y_Or z-!;^0a{kyWtO4vksG_3cyc7HQ0~detf0+2+qxq(e1NS251N}w5iTSrM)`0p8rem!j zZ56hGD=pHI*B+dd)2B`%|9f0goozCSeXPw3 z+58k~sI02Yz#lOneJzYcG)EB0|F+ggC6D|B`6}d0khAK-gz7U3EGT|M_9$ZINqZjwf>P zJCZ=ogSoE`=yV5YXrcTQZx@Un(64*AlLiyxWnCJ9I<5Nc*eK6eV1Mk}ci0*NrJ=t| zCXuJG`#7GBbPceFtFEpl{(lTm`LX=B_!H+& z>$*Hf}}y zkt@nLXFG9%v**s{z&{H4e?aqp%&l#oU8lxUxk2o%K+?aAe6jLojA& z_|J0<-%u^<;NT*%4)n2-OdqfctSl6iCHE?W_Q2zpJken#_xUJlidzs249H=b#g z?}L4-Tnp6)t_5X?_$v)vz`s9@^BME2X@w<>sKZ3=B{%*B$T5Nj%6!-Hr;I!Scj`lH z&2dHFlOISwWJ&S2vf~@I4i~(0*T%OFiuX|eD*nd2utS4$1_JM?zmp>a#CsVy6Er^z zeNNZZDE?R3pM?>~e?H_N`C`hy%m4jb;6L#8=a7l>3eJS2LGgEUxsau-Yh9l~o7=Yh z2mYg3`m5*3Ik|lKQf~euzZlCWzaN&=vHuHtOwK!2@W6)hqq$Zm|7`Nmu%9^F6UH?+ z@2ii+=iJ;ZzhiUKu$QB()nKk3FooI>Jr_IjzY6=qxYy;&mvi7BlQ?t4kRjIhb|2q? zd^K~{-^cxjVSj?!Xs=Da5IHmFzRj!Kzh~b!?`P7c&T9s77VLYB?8_?F zauM^)p;qFG!9PHLfIsnt43UnmV?Wn?Ki7aXSosgq;f?MYUuSIYwOn(5vWhb{f%$pn z4ySN-z}_%7|B);A@PA5k*7kkdr4xZ@s{e9j+9w;*RFm;XPDQwx%~;8iBzSKTIGKO z{53ZZU*OLr@S5=k;?CM^i#zkxs3Sj%z0U`L%q`qM+tP zX$aL;*^g$7UyM2Go+_4A+f)IQcy^G$h2E zb?nT$XlgTEFJI8GN6NQf%-eVn9mPilRqUbT$pN-|;FEjq@Ao&TxpZg=mEgBHB zU@grU;&sfmqlO=6|G3sU;7t8rbK$?X0y_v9$^{X`m4jZ_BR|B|@?ZCLSPPEzz`w1n zP5nA;4(kQFKm%$enjkkBxM%Y}2si&d|62L)U(dCzCGn56HN+i#6|nV-TGIo0;W;`( zW-y=1KF4dp$$mC_|6}pbb>IHoKQeZajXQB>jVR?u`R>%l1o54?6NnS*arpVopdEF; zeC5J3*M0p`*8lif;!irrcjC?(uExejsi~>4wKYwstGY^N@KY}TujLx`S=Cu+T=!dx zKWlPm->I**E{A*q-Z^FFT5$G%7Ij0_*Mo4-y6~RmyTzUB&lfae(WZfO>um}mnsDXPEbau-!13!!xd!qh*{C)6&bz0j1I{>y$D-S)b*)JMCPk!=~KL&6Ngin0p6MCOxF2L_R9t8N!$2Wpced<#`y!F;w zKTi5V_kX&X09wAIJ#anfg9Dhn0s7(C6Nj3S-mVn(i|C6ZAVq0$hE)874co};g z^hR7pe4lU$P;*ggYc4o&UTQC%liCXooIfkI3TNaBV%t~FRr}yHu7kjQ2J*3;e%;iW zvDVCh8=G80KAeyhCuY2LjrC!Od1rvF7h}zszxGV)&!)6ChP5WAjv-zQAMNJIG!JHS zwl?pLxC-V5II#(hQ`l)ZAp&M0xd4%cxmco*MIk?{BD=BK`1vpc}D39|XlV z{c&0oGdDa~TL2FT4lh=~1NL5O-P~0?V2#ie`v^CnANfGUM!b4F=JkCwd7Q`c8Na2q zJGQQk^?6w}Vg9-{|2047((lAV84uN%sK!N2?V(!_1{{v6rdgZl56f0zDMQ+q)jKzzu^ztsVken;=DjAh6G`Cw`Q4G+BjS+n*=KI~^K{W=%t zbD-rN)O4|*Q~@<#@1Vx$E!0W9`B~IZeFn87sHMXD>$M%|Bh93rdGf1lKoX3K651t&nhsl= zXxG|%@8}Bbrlp_u#t*DZX<}_0Yb{A9*1Pd_)LtqNwy6xT4pZrOY{s?N4)pPwT(i#y zT%`lRi8U#Ken4fw>H+N`{f#FF?ZxFlLZg7z7#cr4X>id z{9kUD`d2=w_Zlb{^c`5IOxWCZ1k<0T1D1Z31IU0Q2edsZ1K0xv$pQVYq2KEp&#v#Z z?{m@Lin;*Str(C2sfF^L>{R3cjY`~#)m>Wm$Y|1fzeS0-$(Q^z@} zEO*vlb-^XK9>w&Ef^=Zzo-1AFSP#9zb~X5_+){$(eB4K z8gtW+nl{q+CTh+>v(gWrsP^DB*ge(~Q$AGxJ-eYc1isti%$%nM<_&Ev?%|??PK`$p z{f-PM{Ym8k<$$)(F9)tqzFJ?h&Dk@D?Dt{4CHKJWLs8$zy6+(R)pr@0ur)xY{=uXFFzH_> z-F^tN1y(2hG8V)GpDg%wW0Px_ep~nIjD~*HCSxDi0y`H!`V*~RHs^uQsb1*bK1qGpmd zB1m`Cjw0`nLBF2|umz+a#2X$c?Lj;M?Lj;MUp*d>7j~ayNAyj@SLpeH`)BgRH}byy zyQSat!;U{@O(<<2fp&oQkIy$z`_CQ-)O@RN;QD9T4y|wIJ^%U#(BF%=`i49}j!D-) zkOwPSJaG03SMkE~BzW}b_v>LA&y)EEYO6sbdnTX*$>UF|JhZ&^MSb4}Tgbne_4n+C zwI8U4i~PI>7a3{kVa8|))*%C0|K+bIbmV~a`|G#+`TU#g zXW;bWIcWsQi9c4X*RUDpIfyoPY)2bI-r9)xulm1CJDkQd6u+f)_N=w1ElgEBjprPF z3o?Ly0RVeY_{3~fPVckRMxe2lM8hj!B8F)JO z!`AP6>u>5Y&3o9t0QxBpNE=lJx#NyIbp1gD zzUYBIPYHIv9ngk-Zt~<)62^1Zs1LLYMh@_tP^I7EX-9)Ed0^@y{k65Gp0KRcTmMWw zU|+)qx{#q0SL+4q?Q`i0>COIIF8a0Cf&C`hbMj?LmG9K&iW-?PJt*u)38tTXAP>@R zZL6uH^!RYNq$p>PKz7f-zvg>OKXcZ8h!%Vo@{VUZp|+iUD_xb(N~G|6c#oQK^nHZU zKg#F6<)+`rf~k*Xjjye+syV{bwU2glMMMs-^ss4`bYaVroXzn`YQUd__UlZL_mLs z(vO}k!~(mi|L+(5&;>r<;|OHnbXBE78LruP;{yBxZ6y7K3)nMo-{6PCI7gQi6+rF_ zkPod!Z8n}q46ykrlQS|hVB(}(2Kf7BCZ>Vc;V>ccbk2~NGaf6wGQH@W9&?Zt3v(h*P4xDrN>ex7+jH*+Qg z%^jH$&+*!v{sQ!xkWN4+>|b}qGvEd6ANzgqoVy5Qfws}ef2QqF{iiR5{pT}PS&yjo z>lron#va-p=v;m>WB+XVz|o;UJFdjo5_!RRD|6W{4}A2a#bZv)gS_`b|KsSH)Sd_JIr%<%n06TX&t{&!H#{)?4W9hlJ`R1>FyugOh3=D_{einr zu(Wf`qTkvED+gEULO0I*Hs%f;&=`=X4;N8Ovf28x$A*11`dmfy2=$+PNqX>XcG`h% zJY&A6@&)*WT^rC(Caj}2+|X|6cICm5h0OK0cGB_!wEKFZJU)OQ+TZ1q2bTx9hxnq& z$9ee|f9|0M^)#E&Pr4)f?o&DMM4w>Ksb{hF(0|wh+5_{vPow{V%TFzU2za&gjttNi zIyR9qA56dX52Qbv2aY^g`U7R43-p`#sO1A=KS2aKgfR+Yu^bQ*i-qu z%0mP;Ap)B~zZgO9lG^`325gOf?iUHF{~7jyGC)3L(eL(SQ70VzR~wLN18tnx(Cz2~ zctBl1kI)wAe+cxWHw*NW-d;=pd+>+wd$a@GBju*wFvabSaPtHiT!o#QFC+wBVwYo3s=y;z1jM+M=Fj!FZM>UzpL-eZzOT( zhmZmEfWa=%KE#V3-ZK5#v!Hzd{zc^{ctF~- z>DT-U`}5!fk$aj24`#uGdB7r`>oX5tU|d*b|N3V1lXmv%MGrvE(dXG)^-J*LA>$LE z7kut4`zE)v{@Op|(|@i#c>tM!12FQh?}PfA0`Bp%=%*RiXVzLDXnXtE@4B)5uR}a> zbNU}q+712pIrM`k^odG8dKtG$zwHmQI^c}tfjx5?egx3!e%JRm_64e+>`Ra1IRfLb z1KQ`SxmH{cZfyVS5m(&`{V}Y4j6J{b17`h6KWqZ&hfc(oR zxM%w!$F(mKy05kY&lco3%zvLCxBW+t*rxO+i=qGMvobx0-<7`VUu)ka`){=ew+Ovt zg%52_{&UbkUA8aJPWsk)gYWV4`dnxI%s?7^fGpq{ZQuu=VH{-t7w~K%_E<8`zS;V- zKTho*>;UQQul^1GT^HCt@I-q?)&4!QDgBndn?3sNKYKCQFU4LGKJ$n@Je$&w9@E$X z^p@iJ(v&`1(tq~1zc>0Vow-KR&vm!GUzT?Eqgnc)leZ9p)-Z*C!zqb=-$XG0 z^!8RfuQs5s>Q~qcz92(a_Q+KH?C*vCTr~UdTiR`JGuNH8v(J|FTiSEcPrBpmHRtmd zI2Jng0J=bXK);YY^rM?jzn?~X-Pe`GbAy{D)Y6D&1GY-EBcy%Bq?bKh?A>DD9DD!p z?{q02wno2sraGUkZv5dx+J8)&K$)No43Zr(*S`FEdL!4C)}WE}vJd%{S6-3VUw>Wp z?Aasv`T0^%P$2vE?L+Qhj~qB~K%eW)xH(=b_jU}TLD&BP*Pc9hz@Z=e0nkpLkWl}> z_5J^i(9Z7$(XG9~I3sY)`OGZ#_L06+Dy4E>UstcP-rU@xJ$&rxvo!n1Ao`P~KLU-8 z{zDgN4-&A6N!kPSYbQ&7sLufi`YtE2uN$S?e&5n>Y4(q#|KP!cc1j)T^QrUXMPFaP z_SoYO8S8G}Z$?AL4`;pE?7J5K8yWqy23>cCT2{=-)+A$X^-I9=e!@J@A&-;Ufc)`H}c(VI&;0x zrrGv()5mjP%jXzS{^|29?bLNXS0bC%p!YXI!;O457rjCEEzMkGf~B3$T}dXBO23tP z+Ci>;5UoM?C@bU@f9G1^X3=ly&ZeFH<@|RnOG--A&)fd)AUgjw?%izq{p(KJ`EP0v z2mU)P!+3t@X14DA=E2RR-|p${GZ9ETX=d+kJRZL$nSa0daI@&oUUxnZg0xd_xu>Vz lzF#z5%kSKX?YLH3ll^(hI(_`L*t#Iva2Ede*Z;>H_ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/Sentry.Samples.OpenTelemetry.AspNet/sentry-nuget.png b/samples/Sentry.Samples.OpenTelemetry.AspNet/sentry-nuget.png new file mode 100644 index 0000000000000000000000000000000000000000..b0996b67bc1412d745fa19adb0109b3fe75fbae9 GIT binary patch literal 6250 zcmV-w7?tOVP){edntWWGl#+{wCtBlBStMnZt{s zn5hY(rVt6Sx5+zEMC{dzBi4rABlqj)%Q>IQ0L$$X?XuS57^oCy2LM_%uo!Su8De0#}b+iuC zXbqIhhx(W1XqH}6jXEQAJ{`b~ET$#i1bvV;!MX za1Lc9LyE64JVz|kioH%shJ2aT6v9G2TQa10noJXgvYD@=j<3!T_Fkc@m4^l`(LycP ze3k0}?V*v{UFG@z;dAt-QrKIlzC#{fVlw;F10~dlPw_+Mk%xM|R@$-((@8XugIOF+ zIm)9ROynQrWFv=_42jnm)?i^dr;|Y~s0SD4j*%C#OUaNp^cHIVWGZDP zLt^wJEc~7xC_pSHdzbAtt)RV`ta8JpQ zFrA9wX?mbY4Pq{aCO%g(#9#ebfWtC&MFnUq8!)^~w~`^g`Y{g8oYETk8X~f~js7SgELlt|M3`tj4Uc_Mm z{ZWy$D<5K*S~4VEU&ruOR772hp^49z49Ty7EXLs(%25&3o3{eO-;yC$<50)J#jodk zhrcC5M%wJ{c2sm_cml(c+~IG@ko$31!>*_mG?p5V@V8{h=V-#=Qq)TN4Tg>EQ!>O= zH|F3lpI)eyG=xYEt;3$>yy!qCJ~B}0<+@ufIC9(49GoWnVoenDp>Z>EI5 zB}0COLp28mq%L<-31s;<7X}=bA4@ZavrC5jNBdBP!%fIjWjsuhE0DJy#juPW5hX)- z2!|C64tUO8Y8Z<=^>H>~_$4Jn_`D5wCj~SxypH9`fM1{MF>GY?_HIab+dB1nz}flv zSRl(u$WuG99K(a#yCLV==<^ulslluu7jMx!=&!fc-|g9uomhs$G~}%t$;bBs7U-)O zUZwl?YshstRIyLMPx59gF;%dmX~J;s_G-w!Y>uz-{TYXP8cFc`&>+u*zuTuFkJu{s zK;)?}+O+N}Zgu@RG$0Myh~XF8qapUcDc^_>cbC$iU0F+lX9La}ufed9G25FVJ$M6$ z7wL*TH3`Fc2=N0PP7Nr(EWz;b_GZW=92yuOAMU=kC3We?dn8!M0OYA}U})y(?a7c` zSc$`*khgBYp_wnBEt-hKl>yyHbK=9_&ToiKA=a^He7HLuZB;q5NKnaM$Wx!C0mC`l ziy=4yWjuz%f`Fr?<7{YlJuv34ku)ymCPmz;KPBi_ZGu)qoeUYceQIFxgZPkz+_@L0<#BN+iPHH$4 zCG<(^Nbm^q)?FAX*m>JD>%W+gQ>;vt!*z4Ie<-Id76)-XkADX zIk;-;kf-X{zQ)@yEN7=}$B+SxVMcwKR~EnS2aIY9cjYX=YNc>r!vi2YG5ThE43(`3*UfO1$`xGf|}eJC`L5 zOw4s%>au{+^n47{JG&vr&`1DxGO(DIZ00EJbtyh#ca$y@rnP1G+uGHu_X%V!hoC5Z zC6^_uIS_j&#+?Djn@@&^za7|+pAx|a&W`>qsRPS<47W$|b&NxvGGQ9Pd7ageQEVWR zTOtdOXVVCl=jmnph@YcNe3yL%gFB}o4^RNlGcx{2(sCEJor}`~n#exDa6@M_`IIcHOWKQ){m)o1j3u+;Hs&M?V* zz^(Q-Fih`khI|8uT0Xfo|Gk4|{J9|_hwsi~U|G)IZq0r@Bja23oB2X#GGq`-aJbpD zX1$F+(-;sj3GFqoJj01Jkl_CK%Pcnh?cj#|9EVj5!6qm-(TFcEGCU&0s{qUO+(&{Y zjtr>(Y{D?9vlud(DjY7()55qAU*<(rx<1Ed4E6k!HL(Luu7#)x2kmdjRGWME%o|YN zhcAmb2nFgquqXuxKZG{tD2OM&o(+1&h z@h3C9g2SVz1s%j2_^~?JlAinxLzW7f;|@8x@iK--DC^jUoE`HB?ZDmMmK+v*kiQZ` zGmWu*jYrW0b_3|ZhU{e9Z?6kDimh%XOHQPbbRMNF_6HK)phw3w^9bJUA_@ZrV4w04i9Q(=s)IcQPL) zoi7I@oRt_RbreJPvt`xOQM>|INkz~r!}j1q(s+ly$WvEfSjjFO!;nX8{L=*$NmtM^ zF9da{Od_4{2i$6ZAH$6u!4Na;qMpN15%p_YW=hblk{3ziBlbX^IyFB0jW;Cp{QnO{ zzf|%ES|S_t>acOtk;dJ~Q(btj=;3dX2a&C1L@NxRdxDnuFeuFaEosyTlrImZuE^Ej zLI;tpz~P6j)HCr9TH$KPWud=Od0 zprz>mtl+TVzw|}YsSbE7j--(^9ta4{pTh7L%G%bD3vJjrEMUf8^Va`YXcD?Kv%0h<~8~z;_1dpeVH0lE~lE2{4z^i2A+IM>~7sE|$ zV2HiCy_x-s5ZBBNe3*VoI?qxb+lx4dx6JT{*h~OwI56Ij$l(UR4k!}7N`9UW`VH*M zTcmS}Z^&vx>5cq~#^3_3**xOI-{4EG@mC}+Af3A?D`-SSY@qRF3^(&=Yy^7%)dhyX z1Zq|>;9kgE=abHNyw6A0;4s?` z%UZ?I_*45;3~3!h?k;Rl=l@WRg<2cIJ{Pd+?AsD7i|8A-ew*0e7KeTt@Tp!e;Ltzh z^n6k9OCXI-p$-chsKw!$pbm>HhNw*Y5E^X;(bWy)Y6AKi?Yr8k=78cGvOf*sg>I{} zXu#0SM{X+6z~|$~CmG(Qr)Ti{cN~_|gA;L>8PL}_Ejs*FK#?}VB6>wP0cSBZvd;a( z1q=*mGpHwxuf|^JoPc;LF1Hb5*l1(Q3B@*KY;sk9z&E1b;_xFZRI>qxTamZ!Cym*Z zdqziVaJU!o32NN+);F-05S|zbU-f1Q4zEN{ySp6=>)o&3$gu&*SS|T!=Cg5iwp9$Y zhm>$R^3;wjiwb{*zwX1(%suQH)oC>oLzdMvxld#N=nocmQ{^}eCn1KiI(A;;Il*^= z!t;d4PV_VnBW7UA%xTvheE;wMzWlwumvpI&^I26Av$ z#4ORn*o+Vs?n106CXl@rGPRAU{DtIv#s@l4=~z@IJCK<~pqP?Cv}ohb9ir?bOu) z9RQ1h!r$~JsPzOB`T~JFF*MNN?*F}GpT&r|SpBKM;Rm_<{Hp?HNqil{2MmofWGdkV zTe34tG1SEs2uD**ex8iW`)B2=^-m4B*7HKZmH70zyTOEC49NMq)5e$>Nj#OBjf{x- zEvnp_%$bq?O#vm)&(Po<{^p<8^0Iv*9>tez(A$B^nT4Sqhf`zk{6&ny8DaCI6YOou zn7+n)yu)A5yni_Z5vQ{hKk7IhdFv4BQrE~Uo{|3`J}7w8%Gmt+SGG$$W-9*()_I1% zEmg+M(>08H@Z&?q#2-eQHInt-9{xyvDj6Cx^lLU{=0<+hJ6V6|8UFfAL^Xq!zRC)G zcs-yzxHp^Z8fhZIL*CQl(wG=iWL)p~cth-1(ta$!Fxl4-Gl;B_qwE6YulR6h{B=dB zb@Rnho>Bh_ETM(LAynesYU}eF(WT#UE?;!}&4O>}KRno$VWh{B#mo8~F^)Wk{KQ(C>&J zQhyd-u@3p3uvO!*Ecxk|(5K)*e)oCag>qP&;|$=Nwy&`~zT5KVT!tL)hPxlJTkPq0 zmT@+iuVJ6KpX3jczav@3huFy4)BbAu z^A2;LpdTYIJ&VHVX4)73i-o`=I6GDwI|xqtVU z<8ON0lOClTgk#wZW zwUs5BDL+PNvK&xWSIA|svdU-+gLH%*Qp>!guP8xZ)u@NIFhnCXPc8A0R;izI(?h#z zfm&j{Dm74B>8f4y?$&?7XdR?sDie~>COKTGCsZjPrt2yVN`A2MdQXVQ<8GVYNI3T* z4;@aH1XZjEzoa&+;HJ6RxdxSX<09UnkyIuV`HHdoP)5DApAyWFA6<2{!uhoNsAb;Q zI`@%oQi6Tu!;;js?ZYZL43RWgk86s?sEblLPQE-LhlA8Tqun)BTdC5U@}pcm6wYqi zPc5@fOI+{ElwhBN4cS#bthSr4^gA6R4|>a&S5&J6duVt@qtq?=HCD@ydM#8q86BpU z$?82NC{wwbwUs@UQOhh-eum1259QEL6ZBv5V!nJ??tadm8kNyVC0MLx`GKY=t8m82 zhj-=BO^w>hF6tk;;TQ(1OSmC}b)LMa)@|~o!9La_GdjUF#Ge_uRCNmF6JA^0A~|$T zJ|_dTqgrOI^0SM2du^N!WAsURutDc)p8Q!XhY4NOQwioOgq!rLPSIhInk3t2Ha{N?Io$Ds3c?Np9G0t67#!nyWwRZH4f<90r)R+o&*J{y((_ z%e2AG0vvW$%Y39`R2b{kl-$}f?g}2KMiqm4Ib<`cbEBY+L8Vr?u|T=I<+KG=s#QL^ zD5K)AgIqFtQV9-F$Dq+hds!2KwgGi1YoM39t2i7Yhi5WcU{a~9^(9lT8KOLz5AF*wQnCU2@H zBk6DMKhjSffsJy=CJpJMaML@1PRdAnMm5R$)+y=;U=!fRWS-bXZI3Ol z70!8XyjZJe{$t1@y`}`0Ymhn|*#FVqh7`_7O}6K9i~eH>x?c(Q(hX{DN_5xn?1O)Y z(3WueVO1!>8M;gzi~;JJ3`nzTU$O;%g6njY9Ny6rTPrkLZx_YSb-vn{j4i;IC4cZ6 z)xIg4yn-K`Rq>o@vIyT&pDj6ZIzm39xxH!GH2x=mNe@UW)44`Q9Z zttutR=t@1JJ=I>6r_{2l)E1N}lTxFtR~%#Yl)fdya(zeGonf9XmElX8qf69Z?Ljx& zyHl2o58Hz7>ZyFxs!rkTte@%yjhCT8-_lBLK_qoOIe3rD*b}v}rl#CBydAZHhOs{7 z-ehkSq2m#DGxUlUYrWcresZWu{y}3Kt&m>I$7WS~ zh9-kFN{8!M9jxBU$(5R-1g;@jU91hdTp1bq>-hguR;yK^m0GL!v_WO97*uxGfo`So z^<q+gW-BnB$>f3r&hFm5;s3Y`*IuZ*tORLnrOw~LUlRs#@Ylkt!VWGaHGqhM8 zg+HoF52!t<&||t!#bT}|=q#<4#co-KK{`ukYK+>O=X9LvwWH=}Z?zYv=>c`obWKp9 zyskU+fXOS<3^DZ9XLXW3uie#N%+YCDwx!4JK8G?#0I!Whhlrr+ARx9<6 zUe@0=&#sGbLoEF@T)SzA#^^K3s`V&SvmVp)dRLoe8K`}Atd10FT{2pw`}I#PlBJjS z(RiJ#t|H|dG-;+Pv{Fm8QZ@324yBSr+p46JAtggfDj8BTq@ tracerProviderBuilder - .AddSource(DiagnosticsConfig.ActivitySource.Name) - .ConfigureResource(resource => resource.AddService(DiagnosticsConfig.ServiceName)) + .AddSource(Telemetry.ActivitySource.Name) + .ConfigureResource(resource => resource.AddService(Telemetry.ServiceName)) .AddAspNetCoreInstrumentation() .AddHttpClientInstrumentation() - .AddSentry() - ); + .AddSentry() // <-- Configure OpenTelemetry to send traces to Sentry + ); builder.WebHost.UseSentry(options => { - options.TracesSampleRate = 1.0; - options.UseOpenTelemetry(); + //options.Dsn = "...Your DSN..."; options.Debug = builder.Environment.IsDevelopment(); + options.TracesSampleRate = 1.0; + options.UseOpenTelemetry(); // <-- Configure Sentry to use OpenTelemetry trace information }); var app = builder.Build(); @@ -32,25 +31,32 @@ var httpClient = new HttpClient(); app.MapGet("/hello", async context => { + var request = context.Request; + var name = LookupUser(request); + Activity.Current?.AddTag("name", name); + // Make an HTTP request to the /echo endpoint, to demonstrate that Baggage and TraceHeaders get propagated // correctly... in a real world situation, we might have received a request to this endpoint from an upstream // service that is instrumented with Sentry (passing in a SentryTraceHeader), and we might make an downstream // request to another service that's also instrumented with Sentry. Having a single TraceId that gets propagated // across all services by Sentry and OpenTelemetry ensures all of these events show as part of the same trace in // the performance dashboard in Sentry. - var request = context.Request; - if (request.Query.TryGetValue("topping", out var topping)) - { - Activity.Current?.AddTag("topping", topping); - } - - var url = $"{request.Scheme}://{request.Host}{request.PathBase}/echo"; + var url = $"{request.Scheme}://{request.Host}{request.PathBase}/echo/{name}"; var result = await httpClient.GetStringAsync(url); await context.Response.WriteAsync(result); }); -app.MapGet("/echo", () => "Hi!"); +app.MapGet("/echo/{name}", (string name) => $"Hi {name}!"); app.MapGet("/throw", _ => throw new Exception("test")); app.Run(); + +static string LookupUser(HttpRequest request) +{ + using var _ = Telemetry.ActivitySource.StartActivity(nameof(LookupUser)); + Thread.Sleep(100); // Simulate some work + return (request.Query.TryGetValue("name", out var name)) + ? name.ToString() + : "stranger"; +} diff --git a/samples/Sentry.Samples.OpenTelemetry.AspNetCore/README.md b/samples/Sentry.Samples.OpenTelemetry.AspNetCore/README.md new file mode 100644 index 0000000000..477ef72021 --- /dev/null +++ b/samples/Sentry.Samples.OpenTelemetry.AspNetCore/README.md @@ -0,0 +1,36 @@ +# Overview + +This sample demonstrates how an ASP.NET Core application that is instrumented with the OpenTelemetry .NET SDK can be +configured to send trace information to Sentry, with the following initialization code: + +```csharp +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddOpenTelemetry() + .WithTracing(tracerProviderBuilder => + tracerProviderBuilder + .AddSource(Telemetry.ActivitySource.Name) + .ConfigureResource(resource => resource.AddService(Telemetry.ServiceName)) + .AddAspNetCoreInstrumentation() + .AddHttpClientInstrumentation() + .AddSentry() // <-- Configure OpenTelemetry to send trace information to Sentry + ); + +builder.WebHost.UseSentry(options => +{ + options.Dsn = "...Your DSN..."; + options.Debug = builder.Environment.IsDevelopment(); + options.TracesSampleRate = 1.0; + options.UseOpenTelemetry(); // <-- Configure Sentry to use OpenTelemetry trace information +}); +``` + +## Customizing propagation + +Sentry's OpenTelemetry integration sets the DefaultTextMapPropagator for OpenTelemetry to a `SentryPropagator`. This +propagator ensures that both the W3C baggage header and the sentry-trace header get propagated from upstream services +and/or to downstream services. + +If you need to further customize header propagation in your application (e.g. propagating other vendor specific headers) +then you can do so by creating a `CompositeTextMapPropagator` consisting of the custom propagator(s) you need plus the +`SentryPropagator`. You can supply this as an optional parameter to the `AddSentry` method. diff --git a/samples/Sentry.Samples.OpenTelemetry.AspNetCore/DiagnosticsConfig.cs b/samples/Sentry.Samples.OpenTelemetry.AspNetCore/Telemetry.cs similarity index 86% rename from samples/Sentry.Samples.OpenTelemetry.AspNetCore/DiagnosticsConfig.cs rename to samples/Sentry.Samples.OpenTelemetry.AspNetCore/Telemetry.cs index ceff36ff4c..a5a30655b0 100644 --- a/samples/Sentry.Samples.OpenTelemetry.AspNetCore/DiagnosticsConfig.cs +++ b/samples/Sentry.Samples.OpenTelemetry.AspNetCore/Telemetry.cs @@ -2,7 +2,7 @@ namespace Sentry.Samples.OpenTelemetry.AspNetCore; -public static class DiagnosticsConfig +public static class Telemetry { public const string ServiceName = "Sentry.Samples.OpenTelemetry.AspNetCore"; public static ActivitySource ActivitySource { get; } = new(ServiceName); diff --git a/src/Sentry.AspNetCore/SentryWebHostBuilderExtensions.cs b/src/Sentry.AspNetCore/SentryWebHostBuilderExtensions.cs index 5cbe525276..9b92375b4c 100644 --- a/src/Sentry.AspNetCore/SentryWebHostBuilderExtensions.cs +++ b/src/Sentry.AspNetCore/SentryWebHostBuilderExtensions.cs @@ -95,7 +95,6 @@ public static IWebHostBuilder UseSentry( var sentryBuilder = logging.Services.AddSentry(); configureSentry?.Invoke(context, sentryBuilder); - }); _ = builder.ConfigureServices(c => _ = diff --git a/src/Sentry.OpenTelemetry/Sentry.OpenTelemetry.csproj b/src/Sentry.OpenTelemetry/Sentry.OpenTelemetry.csproj index ba83051a5e..9485d10d5e 100644 --- a/src/Sentry.OpenTelemetry/Sentry.OpenTelemetry.csproj +++ b/src/Sentry.OpenTelemetry/Sentry.OpenTelemetry.csproj @@ -1,20 +1,23 @@ - + - - net6.0 - enable - + + Official OpenTelemetry integration for Sentry - Open-source error tracking that helps developers monitor and fix crashes in real time. + $(PackageTags);OpenTelemetry + net6.0;netstandard2.1;netstandard2.0;net462 + enable + - - - - - - - + + + - + + + + + + diff --git a/src/Sentry.OpenTelemetry/SentryOptionsExtensions.cs b/src/Sentry.OpenTelemetry/SentryOptionsExtensions.cs index 93679a35b5..2035787a19 100644 --- a/src/Sentry.OpenTelemetry/SentryOptionsExtensions.cs +++ b/src/Sentry.OpenTelemetry/SentryOptionsExtensions.cs @@ -1,3 +1,6 @@ +using OpenTelemetry.Context.Propagation; +using OpenTelemetry.Trace; + namespace Sentry.OpenTelemetry; /// @@ -8,7 +11,42 @@ public static class SentryOptionsExtensions /// /// Enables OpenTelemetry instrumentation with Sentry /// - /// + /// instance + /// + /// + /// The default TextMapPropagator to be used by OpenTelemetry. + /// + /// If this parameter is not supplied, the will be used, which propagates the + /// baggage header as well as Sentry trace headers. + /// + /// + /// The is required for Sentry's OpenTelemetry integration to work but you + /// could wrap this in a if you needed other propagators as well. + /// + /// + public static void UseOpenTelemetry( + this SentryOptions options, + TracerProviderBuilder traceProviderBuilder, + TextMapPropagator? defaultTextMapPropagator = null + ) + { + options.Instrumenter = Instrumenter.OpenTelemetry; + options.AddTransactionProcessor( + new OpenTelemetryTransactionProcessor() + ); + + traceProviderBuilder.AddSentry(defaultTextMapPropagator); + } + + /// + /// Configures Sentry to use OpenTelemetry for distributed tracing. + /// + /// Note: if you are using this method to configure Sentry to work with OpenTelemetry you will also have to call + /// when building your + /// to ensure OpenTelemetry sends trace information to Sentry. + /// + /// + /// instance public static void UseOpenTelemetry(this SentryOptions options) { options.Instrumenter = Instrumenter.OpenTelemetry; diff --git a/src/Sentry.OpenTelemetry/SentryPropagator.cs b/src/Sentry.OpenTelemetry/SentryPropagator.cs index ab9eee8a35..f4ef891c96 100644 --- a/src/Sentry.OpenTelemetry/SentryPropagator.cs +++ b/src/Sentry.OpenTelemetry/SentryPropagator.cs @@ -124,9 +124,9 @@ public override void Inject(PropagationContext context, T carrier, Action(T carrier, Func> getter) { var headerValue = getter(carrier, SentryTraceHeader.HttpHeaderName); - var value = new StringValues(headerValue.ToArray()); try { + var value = new StringValues(headerValue.ToArray()); return SentryTraceHeader.Parse(value); } catch (Exception) diff --git a/src/Sentry.OpenTelemetry/SentrySpanProcessor.cs b/src/Sentry.OpenTelemetry/SentrySpanProcessor.cs index 0f7f1801b0..b31cf277d8 100644 --- a/src/Sentry.OpenTelemetry/SentrySpanProcessor.cs +++ b/src/Sentry.OpenTelemetry/SentrySpanProcessor.cs @@ -1,4 +1,4 @@ -using OpenTelemetry; +using OpenTelemetry; using Sentry.Extensibility; using Sentry.Internal.Extensions; @@ -16,6 +16,13 @@ public class SentrySpanProcessor : BaseProcessor private readonly SentryOptions? _options; private readonly Lazy> _resourceAttributes; + /// + /// Constructs a . + /// + public SentrySpanProcessor() : this(SentrySdk.CurrentHub) + { + } + /// /// Constructs a . /// @@ -27,7 +34,7 @@ public SentrySpanProcessor(IHub hub) if (_options is not { }) { throw new InvalidOperationException( - $"The Sentry SDK has not been initialised. To use Sentry with OpenTelemetry tracing you need to " + + "The Sentry SDK has not been initialised. To use Sentry with OpenTelemetry tracing you need to " + "initialize the Sentry SDK."); } diff --git a/src/Sentry/Sentry.csproj b/src/Sentry/Sentry.csproj index e4c1532943..b0ccee5a70 100644 --- a/src/Sentry/Sentry.csproj +++ b/src/Sentry/Sentry.csproj @@ -163,6 +163,7 @@ + diff --git a/test/Sentry.OpenTelemetry.Tests/ActivitySourceTests.cs b/test/Sentry.OpenTelemetry.Tests/ActivitySourceTests.cs index f2c20ba56f..c7bfdc9cc1 100644 --- a/test/Sentry.OpenTelemetry.Tests/ActivitySourceTests.cs +++ b/test/Sentry.OpenTelemetry.Tests/ActivitySourceTests.cs @@ -1,26 +1,29 @@ +using OpenTelemetry; +using OpenTelemetry.Trace; +using Sentry.OpenTelemetry.Tests.OpenTelemetry; + namespace Sentry.OpenTelemetry.Tests; public abstract class ActivitySourceTests : IDisposable { - protected static readonly ActivitySource Tracer = new("SentrySpanProcessorTests", "1.0.0"); - private readonly ActivityListener _listener; + protected readonly ActivitySource Tracer; + private readonly TracerProvider _traceProvider; protected ActivitySourceTests() { - // Without a listener, activity source will not create activities - _listener = new ActivityListener - { - ActivityStarted = _ => { }, - ActivityStopped = _ => { }, - ShouldListenTo = _ => true, - SampleUsingParentId = (ref ActivityCreationOptions _) => ActivitySamplingResult.AllDataAndRecorded, - Sample = (ref ActivityCreationOptions _) => ActivitySamplingResult.AllDataAndRecorded - }; - ActivitySource.AddActivityListener(_listener); + var activitySourceName = "SentrySpanProcessorTests"; + var testSampler = new TestSampler(); + Tracer = new ActivitySource(activitySourceName); + _traceProvider = Sdk.CreateTracerProviderBuilder() + .AddSource(activitySourceName) + .SetSampler(testSampler) + .Build(); } public void Dispose() { - _listener?.Dispose(); + _traceProvider?.Dispose(); + Tracer.Dispose(); + GC.SuppressFinalize(this); } } diff --git a/test/Sentry.OpenTelemetry.Tests/OpenTelemetry/ATTRIBUTION.txt b/test/Sentry.OpenTelemetry.Tests/OpenTelemetry/ATTRIBUTION.txt new file mode 100644 index 0000000000..327c24d113 --- /dev/null +++ b/test/Sentry.OpenTelemetry.Tests/OpenTelemetry/ATTRIBUTION.txt @@ -0,0 +1,18 @@ +Parts of the code in this subdirectory have been adapted from +https://github.com/open-telemetry/ + +The original license is as follows: + +Copyright The OpenTelemetry Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/test/Sentry.OpenTelemetry.Tests/OpenTelemetry/TestSampler.cs b/test/Sentry.OpenTelemetry.Tests/OpenTelemetry/TestSampler.cs new file mode 100644 index 0000000000..3e1c51ffb4 --- /dev/null +++ b/test/Sentry.OpenTelemetry.Tests/OpenTelemetry/TestSampler.cs @@ -0,0 +1,35 @@ +// +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using OpenTelemetry.Trace; + +namespace Sentry.OpenTelemetry.Tests.OpenTelemetry +{ + // This is a copy of OpenTelemetry.Tests.TestSampler: + // https://github.com/open-telemetry/opentelemetry-dotnet/blob/b23b1460e96efb5ecd78d1b36c2e00e84de7086b/test/OpenTelemetry.Tests/Shared/TestSampler.cs + internal class TestSampler : Sampler + { + public Func SamplingAction { get; set; } + + public SamplingParameters LatestSamplingParameters { get; private set; } + + public override SamplingResult ShouldSample(in SamplingParameters samplingParameters) + { + LatestSamplingParameters = samplingParameters; + return SamplingAction?.Invoke(samplingParameters) ?? new SamplingResult(SamplingDecision.RecordAndSample); + } + } +} diff --git a/test/Sentry.OpenTelemetry.Tests/Sentry.OpenTelemetry.Tests.csproj b/test/Sentry.OpenTelemetry.Tests/Sentry.OpenTelemetry.Tests.csproj index 187f373e59..9666d70068 100644 --- a/test/Sentry.OpenTelemetry.Tests/Sentry.OpenTelemetry.Tests.csproj +++ b/test/Sentry.OpenTelemetry.Tests/Sentry.OpenTelemetry.Tests.csproj @@ -1,17 +1,32 @@ - + - net7.0;net6.0 + net7.0;net6.0;netcoreapp3.1;net48 - - + + + + + + + + + + + + + + + diff --git a/test/Sentry.OpenTelemetry.Tests/SentrySpanProcessorTests.cs b/test/Sentry.OpenTelemetry.Tests/SentrySpanProcessorTests.cs index e23e6a525b..fb958d13c8 100644 --- a/test/Sentry.OpenTelemetry.Tests/SentrySpanProcessorTests.cs +++ b/test/Sentry.OpenTelemetry.Tests/SentrySpanProcessorTests.cs @@ -1,3 +1,7 @@ +using OpenTelemetry; +using OpenTelemetry.Trace; +using Sentry.PlatformAbstractions; + namespace Sentry.OpenTelemetry.Tests; public class SentrySpanProcessorTests : ActivitySourceTests From 0f2983aaafb58a6b2241df668e5a6c22e3ff46b6 Mon Sep 17 00:00:00 2001 From: getsentry-bot Date: Thu, 13 Jul 2023 01:51:51 +0000 Subject: [PATCH 101/142] release: 3.34.0 --- CHANGELOG.md | 2 +- Directory.Build.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 02a52e09dd..c0291c5926 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## Unreleased +## 3.34.0 ### Features diff --git a/Directory.Build.props b/Directory.Build.props index 2ecab2089f..943e5ab1f0 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,7 +1,7 @@ - 3.33.1 + 3.34.0 11 true $(MSBuildThisFileDirectory).assets\Sentry.snk From bc0e17eb47aea624288947bf3e648353e003e937 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Jul 2023 15:47:34 +1200 Subject: [PATCH 102/142] Bump jQuery in /samples/Sentry.Samples.OpenTelemetry.AspNet (#2473) Bumps jQuery from 3.4.1 to 3.5.0. --- updated-dependencies: - dependency-name: jQuery dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- samples/Sentry.Samples.OpenTelemetry.AspNet/packages.config | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/Sentry.Samples.OpenTelemetry.AspNet/packages.config b/samples/Sentry.Samples.OpenTelemetry.AspNet/packages.config index ac25a2f1f8..59567cad4d 100644 --- a/samples/Sentry.Samples.OpenTelemetry.AspNet/packages.config +++ b/samples/Sentry.Samples.OpenTelemetry.AspNet/packages.config @@ -1,8 +1,8 @@ - + - + From 8d80cd4ab1b9289255949932e87fceee7472ece9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Jul 2023 15:49:50 +1200 Subject: [PATCH 103/142] Bump Newtonsoft.Json in /samples/Sentry.Samples.OpenTelemetry.AspNet (#2475) Bumps [Newtonsoft.Json](https://github.com/JamesNK/Newtonsoft.Json) from 12.0.2 to 13.0.1. - [Release notes](https://github.com/JamesNK/Newtonsoft.Json/releases) - [Commits](https://github.com/JamesNK/Newtonsoft.Json/compare/12.0.2...13.0.1) --- updated-dependencies: - dependency-name: Newtonsoft.Json dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- samples/Sentry.Samples.OpenTelemetry.AspNet/packages.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/Sentry.Samples.OpenTelemetry.AspNet/packages.config b/samples/Sentry.Samples.OpenTelemetry.AspNet/packages.config index 59567cad4d..d6873788da 100644 --- a/samples/Sentry.Samples.OpenTelemetry.AspNet/packages.config +++ b/samples/Sentry.Samples.OpenTelemetry.AspNet/packages.config @@ -26,7 +26,7 @@ - + From 13087f6e052b39df88e2de64dcf43279a16078fa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Jul 2023 17:00:57 +1200 Subject: [PATCH 104/142] Bump jQuery.Validation in /samples/Sentry.Samples.OpenTelemetry.AspNet (#2474) Bumps jQuery.Validation from 1.17.0 to 1.19.4. --- updated-dependencies: - dependency-name: jQuery.Validation dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- samples/Sentry.Samples.OpenTelemetry.AspNet/packages.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/Sentry.Samples.OpenTelemetry.AspNet/packages.config b/samples/Sentry.Samples.OpenTelemetry.AspNet/packages.config index d6873788da..681ee2e9aa 100644 --- a/samples/Sentry.Samples.OpenTelemetry.AspNet/packages.config +++ b/samples/Sentry.Samples.OpenTelemetry.AspNet/packages.config @@ -3,7 +3,7 @@ - + From 7e2b81f401be05bfe866d243cdb27143b90a03fa Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 13 Jul 2023 17:54:50 +1200 Subject: [PATCH 105/142] chore: update scripts/update-java.ps1 to 6.25.1 (#2476) Co-authored-by: GitHub --- CHANGELOG.md | 6 +++--- src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 02a52e09dd..bec975dda6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,9 +22,9 @@ - Bump CLI from v2.18.1 to v2.19.4 ([#2428](https://github.com/getsentry/sentry-dotnet/pull/2428), [#2431](https://github.com/getsentry/sentry-dotnet/pull/2431), [#2451](https://github.com/getsentry/sentry-dotnet/pull/2451), [#2454](https://github.com/getsentry/sentry-dotnet/pull/2454)) - [changelog](https://github.com/getsentry/sentry-cli/blob/master/CHANGELOG.md#2194) - [diff](https://github.com/getsentry/sentry-cli/compare/2.18.1...2.19.4) -- Bump Java SDK from v6.22.0 to v6.25.0 ([#2429](https://github.com/getsentry/sentry-dotnet/pull/2429), [#2440](https://github.com/getsentry/sentry-dotnet/pull/2440), [#2458](https://github.com/getsentry/sentry-dotnet/pull/2458)) - - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#6250) - - [diff](https://github.com/getsentry/sentry-java/compare/6.22.0...6.25.0) +- Bump Java SDK from v6.22.0 to v6.25.1 ([#2429](https://github.com/getsentry/sentry-dotnet/pull/2429), [#2440](https://github.com/getsentry/sentry-dotnet/pull/2440), [#2458](https://github.com/getsentry/sentry-dotnet/pull/2458), [#2476](https://github.com/getsentry/sentry-dotnet/pull/2476)) + - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#6251) + - [diff](https://github.com/getsentry/sentry-java/compare/6.22.0...6.25.1) ## 3.33.1 diff --git a/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj b/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj index 07e4c8a35d..c6f4fe78cc 100644 --- a/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj +++ b/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj @@ -3,7 +3,7 @@ net6.0-android $(NoWarn);BG8605;BG8606 - 6.25.0 + 6.25.1 $(BaseIntermediateOutputPath)sdks\Sentry\Android\$(SentryAndroidSdkVersion)\ .NET Bindings for the Sentry Android SDK From 3cf62a9d0cfb28a9d5a5bb552da39d042f874ab6 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Thu, 13 Jul 2023 19:09:40 +1200 Subject: [PATCH 106/142] Remove missing files from project (#2477) --- ...Sentry.Samples.OpenTelemetry.AspNet.csproj | 60 +------------------ 1 file changed, 2 insertions(+), 58 deletions(-) diff --git a/samples/Sentry.Samples.OpenTelemetry.AspNet/Sentry.Samples.OpenTelemetry.AspNet.csproj b/samples/Sentry.Samples.OpenTelemetry.AspNet/Sentry.Samples.OpenTelemetry.AspNet.csproj index 9474c84cf8..e22edb2942 100644 --- a/samples/Sentry.Samples.OpenTelemetry.AspNet/Sentry.Samples.OpenTelemetry.AspNet.csproj +++ b/samples/Sentry.Samples.OpenTelemetry.AspNet/Sentry.Samples.OpenTelemetry.AspNet.csproj @@ -144,43 +144,9 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -198,34 +164,12 @@ + + - - - - - - - - - - - - - - - - - - - - - - - - From 6a10efebf7ef11593dfa01cf3fff94f7cf72fbf1 Mon Sep 17 00:00:00 2001 From: Stefan Jandl Date: Fri, 14 Jul 2023 10:09:00 +0200 Subject: [PATCH 107/142] chore: Added Otel console sample (#2478) --- Sentry.sln | 7 ++++ .../Program.cs | 2 +- .../README.md | 2 +- .../Program.cs | 31 ++++++++++++++++++ .../README.md | 32 +++++++++++++++++++ ...entry.Samples.OpenTelemetry.Console.csproj | 20 ++++++++++++ 6 files changed, 92 insertions(+), 2 deletions(-) create mode 100644 samples/Sentry.Samples.OpenTelemetry.Console/Program.cs create mode 100644 samples/Sentry.Samples.OpenTelemetry.Console/README.md create mode 100644 samples/Sentry.Samples.OpenTelemetry.Console/Sentry.Samples.OpenTelemetry.Console.csproj diff --git a/Sentry.sln b/Sentry.sln index 9e1441ea3e..7f13d1000a 100644 --- a/Sentry.sln +++ b/Sentry.sln @@ -168,6 +168,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.OpenTelemetry.Tests" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.Samples.OpenTelemetry.AspNetCore", "samples\Sentry.Samples.OpenTelemetry.AspNetCore\Sentry.Samples.OpenTelemetry.AspNetCore.csproj", "{6F791E40-49A8-4A67-81DB-6913E519310A}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Samples.OpenTelemetry.Console", "samples\Sentry.Samples.OpenTelemetry.Console\Sentry.Samples.OpenTelemetry.Console.csproj", "{D62E79F4-FC3C-4D75-ABFE-CDE76EF46DDE}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -398,6 +400,10 @@ Global {6F791E40-49A8-4A67-81DB-6913E519310A}.Debug|Any CPU.Build.0 = Debug|Any CPU {6F791E40-49A8-4A67-81DB-6913E519310A}.Release|Any CPU.ActiveCfg = Release|Any CPU {6F791E40-49A8-4A67-81DB-6913E519310A}.Release|Any CPU.Build.0 = Release|Any CPU + {D62E79F4-FC3C-4D75-ABFE-CDE76EF46DDE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D62E79F4-FC3C-4D75-ABFE-CDE76EF46DDE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D62E79F4-FC3C-4D75-ABFE-CDE76EF46DDE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D62E79F4-FC3C-4D75-ABFE-CDE76EF46DDE}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -462,6 +468,7 @@ Global {D4B29F85-5029-462E-A67E-8B7183A0406D} = {AF6AF4C7-8AA2-4D59-8064-2D79560904EB} {A58DE854-6576-4E07-98BF-03B9DCCDBF9A} = {83263231-1A2A-4733-B759-EEFF14E8C5D5} {6F791E40-49A8-4A67-81DB-6913E519310A} = {77454495-55EE-4B40-A089-71B9E8F82E89} + {D62E79F4-FC3C-4D75-ABFE-CDE76EF46DDE} = {77454495-55EE-4B40-A089-71B9E8F82E89} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {0C652B1A-DF72-4EE5-A98B-194FE2C054F6} diff --git a/samples/Sentry.Samples.OpenTelemetry.AspNetCore/Program.cs b/samples/Sentry.Samples.OpenTelemetry.AspNetCore/Program.cs index a9095ee6d2..9982a7e847 100644 --- a/samples/Sentry.Samples.OpenTelemetry.AspNetCore/Program.cs +++ b/samples/Sentry.Samples.OpenTelemetry.AspNetCore/Program.cs @@ -15,7 +15,7 @@ .ConfigureResource(resource => resource.AddService(Telemetry.ServiceName)) .AddAspNetCoreInstrumentation() .AddHttpClientInstrumentation() - .AddSentry() // <-- Configure OpenTelemetry to send traces to Sentry + .AddSentry() // <-- Configure OpenTelemetry to send traces to Sentry ); builder.WebHost.UseSentry(options => diff --git a/samples/Sentry.Samples.OpenTelemetry.AspNetCore/README.md b/samples/Sentry.Samples.OpenTelemetry.AspNetCore/README.md index 477ef72021..e97237ae1c 100644 --- a/samples/Sentry.Samples.OpenTelemetry.AspNetCore/README.md +++ b/samples/Sentry.Samples.OpenTelemetry.AspNetCore/README.md @@ -13,7 +13,7 @@ builder.Services.AddOpenTelemetry() .ConfigureResource(resource => resource.AddService(Telemetry.ServiceName)) .AddAspNetCoreInstrumentation() .AddHttpClientInstrumentation() - .AddSentry() // <-- Configure OpenTelemetry to send trace information to Sentry + .AddSentry() // <-- Configure OpenTelemetry to send trace information to Sentry ); builder.WebHost.UseSentry(options => diff --git a/samples/Sentry.Samples.OpenTelemetry.Console/Program.cs b/samples/Sentry.Samples.OpenTelemetry.Console/Program.cs new file mode 100644 index 0000000000..96e725171d --- /dev/null +++ b/samples/Sentry.Samples.OpenTelemetry.Console/Program.cs @@ -0,0 +1,31 @@ +/* + * This sample demonstrates how to initialize and enable Open Telemetry with Sentry + * in a console application. + * For using Open Telemetry and Sentry in ASP.NET, see Sentry.Samples.OpenTelemetry.AspNet. + * For using Open Telemetry and Sentry in ASP.NET Core, see Sentry.Samples.OpenTelemetry.AspNetCore. + */ + +using OpenTelemetry; +using OpenTelemetry.Resources; +using OpenTelemetry.Trace; +using Sentry; +using Sentry.OpenTelemetry; + +var serviceName = "Sentry.Samples.OpenTelemetry.Console"; +var serviceVersion = "1.0.0"; + +using var tracerProvider = Sdk.CreateTracerProviderBuilder() + .AddSource(serviceName) + .ConfigureResource(resource => + resource.AddService( + serviceName: serviceName, + serviceVersion: serviceVersion)) + .AddSentry() // <-- Configure OpenTelemetry to send traces to Sentry + .Build(); + +SentrySdk.Init(options => +{ + // options.Dsn = "... Your DSN ..."; + options.TracesSampleRate = 1.0; + options.UseOpenTelemetry(); // <-- Configure Sentry to use OpenTelemetry trace information +}); diff --git a/samples/Sentry.Samples.OpenTelemetry.Console/README.md b/samples/Sentry.Samples.OpenTelemetry.Console/README.md new file mode 100644 index 0000000000..554316fa6f --- /dev/null +++ b/samples/Sentry.Samples.OpenTelemetry.Console/README.md @@ -0,0 +1,32 @@ +# Overview + +This sample demonstrates how a console application that is instrumented with the OpenTelemetry .NET SDK can be +configured to send trace information to Sentry, with the following initialization code: + +```csharp +using var tracerProvider = Sdk.CreateTracerProviderBuilder() + .AddSource(serviceName) + .ConfigureResource(resource => + resource.AddService( + serviceName: serviceName, + serviceVersion: serviceVersion)) + .AddSentry() // <-- Configure OpenTelemetry to send traces to Sentry + .Build(); + +SentrySdk.Init(o => +{ + options.Dsn = "...Your DSN..."; + options.TracesSampleRate = 1.0; + o.UseOpenTelemetry(); // <-- Configure Sentry to use OpenTelemetry trace information +}); +``` + +## Customizing propagation + +Sentry's OpenTelemetry integration sets the DefaultTextMapPropagator for OpenTelemetry to a `SentryPropagator`. This +propagator ensures that both the W3C baggage header and the sentry-trace header get propagated from upstream services +and/or to downstream services. + +If you need to further customize header propagation in your application (e.g. propagating other vendor specific headers) +then you can do so by creating a `CompositeTextMapPropagator` consisting of the custom propagator(s) you need plus the +`SentryPropagator`. You can supply this as an optional parameter to the `AddSentry` method. diff --git a/samples/Sentry.Samples.OpenTelemetry.Console/Sentry.Samples.OpenTelemetry.Console.csproj b/samples/Sentry.Samples.OpenTelemetry.Console/Sentry.Samples.OpenTelemetry.Console.csproj new file mode 100644 index 0000000000..14a785f09b --- /dev/null +++ b/samples/Sentry.Samples.OpenTelemetry.Console/Sentry.Samples.OpenTelemetry.Console.csproj @@ -0,0 +1,20 @@ + + + + Exe + net6.0 + enable + enable + + + + + + + + + + + + + From 2bee53c4a2ae9bcc72a7f3f72f9675b064849cfb Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 14 Jul 2023 11:25:05 +0200 Subject: [PATCH 108/142] chore: update modules/sentry-cocoa to 8.9.0 (#2479) --- CHANGELOG.md | 8 ++++++++ modules/sentry-cocoa | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0bd320f938..2546225d46 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## Unreleased + +### Dependencies + +- Bump Cocoa SDK from v8.8.0 to v8.9.0 ([#2479](https://github.com/getsentry/sentry-dotnet/pull/2479)) + - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#890) + - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.8.0...8.9.0) + ## 3.34.0 ### Features diff --git a/modules/sentry-cocoa b/modules/sentry-cocoa index d277532e1c..88d40ec468 160000 --- a/modules/sentry-cocoa +++ b/modules/sentry-cocoa @@ -1 +1 @@ -Subproject commit d277532e1c8af813981ba01f591b15bbdd735615 +Subproject commit 88d40ec4688167b0ab8b3856eef9b3ae3076ba90 From 97d463cda1478e4190db1e7b9a6893d1d94cbfc0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 17 Jul 2023 09:51:17 +0200 Subject: [PATCH 109/142] chore: update modules/sentry-cocoa to 8.9.1 (#2483) --- CHANGELOG.md | 6 +++--- modules/sentry-cocoa | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2546225d46..a9e7b55674 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,9 +4,9 @@ ### Dependencies -- Bump Cocoa SDK from v8.8.0 to v8.9.0 ([#2479](https://github.com/getsentry/sentry-dotnet/pull/2479)) - - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#890) - - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.8.0...8.9.0) +- Bump Cocoa SDK from v8.8.0 to v8.9.1 ([#2479](https://github.com/getsentry/sentry-dotnet/pull/2479), [#2483](https://github.com/getsentry/sentry-dotnet/pull/2483)) + - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#891) + - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.8.0...8.9.1) ## 3.34.0 diff --git a/modules/sentry-cocoa b/modules/sentry-cocoa index 88d40ec468..e46936ed19 160000 --- a/modules/sentry-cocoa +++ b/modules/sentry-cocoa @@ -1 +1 @@ -Subproject commit 88d40ec4688167b0ab8b3856eef9b3ae3076ba90 +Subproject commit e46936ed191c0112cd3276e1c10c0bb7f865268e From 5f6074d189234291c97cd55ea83ab9a353b5be9a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 17 Jul 2023 09:53:37 +0200 Subject: [PATCH 110/142] chore(deps): update Java SDK to v6.25.2 (#2484) --- 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 a9e7b55674..884dcf14ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ ### Dependencies +- Bump Java SDK from v6.25.1 to v6.25.2 ([#2484](https://github.com/getsentry/sentry-dotnet/pull/2484)) + - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#6252) + - [diff](https://github.com/getsentry/sentry-java/compare/6.25.1...6.25.2) - Bump Cocoa SDK from v8.8.0 to v8.9.1 ([#2479](https://github.com/getsentry/sentry-dotnet/pull/2479), [#2483](https://github.com/getsentry/sentry-dotnet/pull/2483)) - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#891) - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.8.0...8.9.1) diff --git a/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj b/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj index c6f4fe78cc..6d09ea9a34 100644 --- a/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj +++ b/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj @@ -3,7 +3,7 @@ net6.0-android $(NoWarn);BG8605;BG8606 - 6.25.1 + 6.25.2 $(BaseIntermediateOutputPath)sdks\Sentry\Android\$(SentryAndroidSdkVersion)\ .NET Bindings for the Sentry Android SDK From 01a57471531345918aa86ded10f8787eea76cf6e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Jul 2023 13:05:03 +0200 Subject: [PATCH 111/142] Bump gradle/gradle-build-action from 2.5.1 to 2.6.0 (#2486) --- .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 4276e7d987..b6b8d985ef 100644 --- a/.github/workflows/device-tests-android.yml +++ b/.github/workflows/device-tests-android.yml @@ -84,7 +84,7 @@ jobs: run: dotnet tool install Microsoft.DotNet.XHarness.CLI --global --version "1.*-*" - name: Setup Gradle - uses: gradle/gradle-build-action@40b6781dcdec2762ad36556682ac74e31030cfe2 # pin@v2 + uses: gradle/gradle-build-action@bd5760595778326ba7f1441bcf7e88b49de61a25 # pin@v2 # Cached AVD setup per https://github.com/ReactiveCircus/android-emulator-runner/blob/main/README.md From f6aca1cf33664c8514326a8e09d843b181f076a0 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Mon, 17 Jul 2023 23:06:10 +1200 Subject: [PATCH 112/142] Removed Sentry.Samples.OpenTelemetry.AspNet from solutions (#2489) --- Sentry.Full.sln | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Sentry.Full.sln b/Sentry.Full.sln index 239ca14d76..c790a18611 100644 --- a/Sentry.Full.sln +++ b/Sentry.Full.sln @@ -190,7 +190,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.OpenTelemetry.Tests" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.Samples.OpenTelemetry.AspNetCore", "samples\Sentry.Samples.OpenTelemetry.AspNetCore\Sentry.Samples.OpenTelemetry.AspNetCore.csproj", "{2502D3A0-7628-4768-A430-0854BEB1E7A5}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Samples.OpenTelemetry.AspNet", "samples\Sentry.Samples.OpenTelemetry.AspNet\Sentry.Samples.OpenTelemetry.AspNet.csproj", "{DDD39CD0-F323-460E-95EA-CFAC1606DDAC}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.Samples.OpenTelemetry.Console", "samples\Sentry.Samples.OpenTelemetry.Console\Sentry.Samples.OpenTelemetry.Console.csproj", "{A9176F5B-0CA6-4479-BE64-A3C3CEA632D4}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -471,10 +471,10 @@ Global {2502D3A0-7628-4768-A430-0854BEB1E7A5}.Debug|Any CPU.Build.0 = Debug|Any CPU {2502D3A0-7628-4768-A430-0854BEB1E7A5}.Release|Any CPU.ActiveCfg = Release|Any CPU {2502D3A0-7628-4768-A430-0854BEB1E7A5}.Release|Any CPU.Build.0 = Release|Any CPU - {DDD39CD0-F323-460E-95EA-CFAC1606DDAC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {DDD39CD0-F323-460E-95EA-CFAC1606DDAC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {DDD39CD0-F323-460E-95EA-CFAC1606DDAC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {DDD39CD0-F323-460E-95EA-CFAC1606DDAC}.Release|Any CPU.Build.0 = Release|Any CPU + {A9176F5B-0CA6-4479-BE64-A3C3CEA632D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A9176F5B-0CA6-4479-BE64-A3C3CEA632D4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A9176F5B-0CA6-4479-BE64-A3C3CEA632D4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A9176F5B-0CA6-4479-BE64-A3C3CEA632D4}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -550,7 +550,7 @@ Global {94C5E990-50EE-462F-B83F-B4357F549500} = {AF6AF4C7-8AA2-4D59-8064-2D79560904EB} {C51A781A-C568-4E1A-834C-8E4EFA3C5B54} = {83263231-1A2A-4733-B759-EEFF14E8C5D5} {2502D3A0-7628-4768-A430-0854BEB1E7A5} = {77454495-55EE-4B40-A089-71B9E8F82E89} - {DDD39CD0-F323-460E-95EA-CFAC1606DDAC} = {77454495-55EE-4B40-A089-71B9E8F82E89} + {A9176F5B-0CA6-4479-BE64-A3C3CEA632D4} = {77454495-55EE-4B40-A089-71B9E8F82E89} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {0C652B1A-DF72-4EE5-A98B-194FE2C054F6} From d6d531cb7d3699f7c4f1f7225433928fbcdab00d Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Tue, 18 Jul 2023 10:07:03 +1200 Subject: [PATCH 113/142] Fixed baggage propagation when an exception is thrown from middleware (#2487) --- CHANGELOG.md | 4 +++ src/Sentry/Extensibility/HubAdapter.cs | 10 +++++++ src/Sentry/HubExtensions.cs | 11 ++++--- src/Sentry/Internal/Hub.cs | 9 ++++-- test/Sentry.Tests/HubTests.cs | 40 ++++++++++++++++++++++++++ 5 files changed, 68 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 884dcf14ae..ba719b61b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Fixes + +- Fixes baggage propagation when an exception is thrown from middleware ([#2487](https://github.com/getsentry/sentry-dotnet/pull/2487)) + ### Dependencies - Bump Java SDK from v6.25.1 to v6.25.2 ([#2484](https://github.com/getsentry/sentry-dotnet/pull/2484)) diff --git a/src/Sentry/Extensibility/HubAdapter.cs b/src/Sentry/Extensibility/HubAdapter.cs index 607b39aed6..bb1b5c8def 100644 --- a/src/Sentry/Extensibility/HubAdapter.cs +++ b/src/Sentry/Extensibility/HubAdapter.cs @@ -75,6 +75,16 @@ public ITransaction StartTransaction( IReadOnlyDictionary customSamplingContext) => SentrySdk.StartTransaction(context, customSamplingContext); + /// + /// Forwards the call to . + /// + [DebuggerStepThrough] + internal ITransaction StartTransaction( + ITransactionContext context, + IReadOnlyDictionary customSamplingContext, + DynamicSamplingContext? dynamicSamplingContext) + => SentrySdk.StartTransaction(context, customSamplingContext, dynamicSamplingContext); + /// /// Forwards the call to . /// diff --git a/src/Sentry/HubExtensions.cs b/src/Sentry/HubExtensions.cs index 0ca57e0b24..7364544d93 100644 --- a/src/Sentry/HubExtensions.cs +++ b/src/Sentry/HubExtensions.cs @@ -1,3 +1,4 @@ +using Sentry.Extensibility; using Sentry.Infrastructure; using Sentry.Internal; using Sentry.Internal.Extensions; @@ -227,10 +228,12 @@ internal static ITransaction StartTransaction( this IHub hub, ITransactionContext context, IReadOnlyDictionary customSamplingContext, - DynamicSamplingContext? dynamicSamplingContext) - => hub is Hub fullHub - ? fullHub.StartTransaction(context, customSamplingContext, dynamicSamplingContext) - : hub.StartTransaction(context, customSamplingContext); + DynamicSamplingContext? dynamicSamplingContext) => hub switch + { + Hub fullHub => fullHub.StartTransaction(context, customSamplingContext, dynamicSamplingContext), + HubAdapter adapter => adapter.StartTransaction(context, customSamplingContext, dynamicSamplingContext), + _ => hub.StartTransaction(context, customSamplingContext) + }; internal static ITransaction? GetTransaction(this IHub hub) { diff --git a/src/Sentry/Internal/Hub.cs b/src/Sentry/Internal/Hub.cs index 65751e7522..0fbbac748b 100644 --- a/src/Sentry/Internal/Hub.cs +++ b/src/Sentry/Internal/Hub.cs @@ -344,6 +344,11 @@ SentryId IHubEx.CaptureEventInternal(SentryEvent evt, Hint? hint, Scope? scope) evt.Contexts.Trace.SpanId = linkedSpan.SpanId; evt.Contexts.Trace.TraceId = linkedSpan.TraceId; evt.Contexts.Trace.ParentSpanId = linkedSpan.ParentSpanId; + // Try to read the DSC from the transaction associated with the linked span + if (linkedSpan?.GetTransaction() is TransactionTracer transactionTracer) + { + evt.DynamicSamplingContext = transactionTracer.DynamicSamplingContext; + } } var hasTerminalException = evt.HasTerminalException(); @@ -360,9 +365,9 @@ SentryId IHubEx.CaptureEventInternal(SentryEvent evt, Hint? hint, Scope? scope) actualScope.SessionUpdate = _sessionManager.ReportError(); } - // When a transaction is present, copy its DSC to the event. + // Try to get the DSC from the transaction, if we didn't get it already from the linked span var transaction = actualScope.Transaction as TransactionTracer; - evt.DynamicSamplingContext = transaction?.DynamicSamplingContext; + evt.DynamicSamplingContext ??= transaction?.DynamicSamplingContext; // Now capture the event with the Sentry client on the current scope. var sentryClient = currentScope.Value; diff --git a/test/Sentry.Tests/HubTests.cs b/test/Sentry.Tests/HubTests.cs index cee1932c65..07a64585be 100644 --- a/test/Sentry.Tests/HubTests.cs +++ b/test/Sentry.Tests/HubTests.cs @@ -154,6 +154,46 @@ public void CaptureException_ActiveSpanExistsOnScope_EventIsLinkedToSpan() Arg.Any()); } + [Fact] + public void CaptureException_TransactionFinished_Gets_DSC_From_LinkedSpan() + { + // Arrange + _fixture.Options.TracesSampleRate = 1.0; + var hub = _fixture.GetSut(); + var exception = new Exception("error"); + + var traceHeader = new SentryTraceHeader( + SentryId.Parse("75302ac48a024bde9a3b3734a82e36c8"), + SpanId.Parse("2000000000000000"), + true); + var transactionContext = new TransactionContext("foo", "bar", traceHeader); + + var dsc = BaggageHeader.Create(new List> + { + {"sentry-sample_rate", "1.0"}, + {"sentry-trace_id", "75302ac48a024bde9a3b3734a82e36c8"}, + {"sentry-public_key", "d4d82fc1c2c4032a83f3a29aa3a3aff"}, + {"sentry-replay_id","bfd31b89a59d41c99d96dc2baf840ecd"} + }).CreateDynamicSamplingContext(); + + var transaction = hub.StartTransaction( + transactionContext, + new Dictionary(), + dsc + ); + transaction.Finish(exception); + + // Act + hub.CaptureException(exception); + + // Assert + _fixture.Client.Received(1).CaptureEvent( + Arg.Is(evt => + evt.DynamicSamplingContext == dsc), + Arg.Any(), + Arg.Any()); + } + [Fact] public void CaptureException_ActiveSpanExistsOnScopeButIsSampledOut_EventIsNotLinkedToSpan() { From 73294560bbdfbf38a5b2f484e73d00832e575fa0 Mon Sep 17 00:00:00 2001 From: Sean Feldman Date: Tue, 18 Jul 2023 07:50:37 -0600 Subject: [PATCH 114/142] Fix Durable Functions preventing orchestrators from completing (#2491) * Keep synchronization context to ensure durable functions can execute where sync context is required by the orchestrator * Disable CA2007 error * Update CHANGELOG.md --- CHANGELOG.md | 1 + src/Sentry.AzureFunctions.Worker/.editorconfig | 5 +++++ .../SentryFunctionsWorkerMiddleware.cs | 6 +++--- 3 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 src/Sentry.AzureFunctions.Worker/.editorconfig diff --git a/CHANGELOG.md b/CHANGELOG.md index ba719b61b5..d969bcc8cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Fixes - Fixes baggage propagation when an exception is thrown from middleware ([#2487](https://github.com/getsentry/sentry-dotnet/pull/2487)) +- Fix Durable Functions preventing orchestrators from completing ([#2491](https://github.com/getsentry/sentry-dotnet/pull/2491)) ### Dependencies diff --git a/src/Sentry.AzureFunctions.Worker/.editorconfig b/src/Sentry.AzureFunctions.Worker/.editorconfig new file mode 100644 index 0000000000..bdf468a081 --- /dev/null +++ b/src/Sentry.AzureFunctions.Worker/.editorconfig @@ -0,0 +1,5 @@ +[*.cs] + +# Reason: Azure Functions worker doesn't set SynchronizationContext but Durable Functions does and required affinity. +# (https://github.com/Azure/azure-functions-dotnet-worker/issues/1520) +dotnet_diagnostic.CA2007.severity = none \ No newline at end of file diff --git a/src/Sentry.AzureFunctions.Worker/SentryFunctionsWorkerMiddleware.cs b/src/Sentry.AzureFunctions.Worker/SentryFunctionsWorkerMiddleware.cs index a22b639959..6827902394 100644 --- a/src/Sentry.AzureFunctions.Worker/SentryFunctionsWorkerMiddleware.cs +++ b/src/Sentry.AzureFunctions.Worker/SentryFunctionsWorkerMiddleware.cs @@ -15,7 +15,7 @@ public SentryFunctionsWorkerMiddleware(IHub hub) public async Task Invoke(FunctionContext context, FunctionExecutionDelegate next) { - var transactionName = await GetHttpTransactionNameAsync(context).ConfigureAwait(false) ?? context.FunctionDefinition.Name; + var transactionName = await GetHttpTransactionNameAsync(context) ?? context.FunctionDefinition.Name; var transaction = _hub.StartTransaction(transactionName, "function"); Exception? unhandledException = null; @@ -35,7 +35,7 @@ public async Task Invoke(FunctionContext context, FunctionExecutionDelegate next context.CancellationToken.ThrowIfCancellationRequested(); - await next(context).ConfigureAwait(false); + await next(context); } catch (Exception exception) { @@ -76,7 +76,7 @@ public async Task Invoke(FunctionContext context, FunctionExecutionDelegate next private static async Task GetHttpTransactionNameAsync(FunctionContext context) { // Get the HTTP request data - var requestData = await context.GetHttpRequestDataAsync().ConfigureAwait(false); + var requestData = await context.GetHttpRequestDataAsync(); if (requestData is null) { // not an HTTP trigger From 3d6b25320f325ee26cde90ec20e2fae1571a7614 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Wed, 19 Jul 2023 14:36:35 +1200 Subject: [PATCH 115/142] Re-enable HubTests.FlushOnDispose_SendsEnvelope (#2492) --- CHANGELOG.md | 1 + test/Sentry.Testing/RepeatAttribute.cs | 24 ++++++++++++++++++++++++ test/Sentry.Tests/HubTests.cs | 5 ----- 3 files changed, 25 insertions(+), 5 deletions(-) create mode 100644 test/Sentry.Testing/RepeatAttribute.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index d969bcc8cd..e44edffa99 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - Fixes baggage propagation when an exception is thrown from middleware ([#2487](https://github.com/getsentry/sentry-dotnet/pull/2487)) - Fix Durable Functions preventing orchestrators from completing ([#2491](https://github.com/getsentry/sentry-dotnet/pull/2491)) +- Re-enable HubTests.FlushOnDispose_SendsEnvelope ([#2492](https://github.com/getsentry/sentry-dotnet/pull/2492)) ### Dependencies diff --git a/test/Sentry.Testing/RepeatAttribute.cs b/test/Sentry.Testing/RepeatAttribute.cs new file mode 100644 index 0000000000..37443b0904 --- /dev/null +++ b/test/Sentry.Testing/RepeatAttribute.cs @@ -0,0 +1,24 @@ +namespace Sentry.Testing; + +public class RepeatAttribute : Xunit.Sdk.DataAttribute +{ + private readonly int _count; + private readonly object[] _data; + + public RepeatAttribute(int count, params object[] data) + { + if (count < 1) + { + throw new ArgumentOutOfRangeException(nameof(count), + "Repeat count must be greater than 0."); + } + + _count = count; + _data = data; + } + + public override IEnumerable GetData(MethodInfo testMethod) + { + return Enumerable.Repeat(_data, _count); + } +} diff --git a/test/Sentry.Tests/HubTests.cs b/test/Sentry.Tests/HubTests.cs index 07a64585be..f1999225ae 100644 --- a/test/Sentry.Tests/HubTests.cs +++ b/test/Sentry.Tests/HubTests.cs @@ -1290,12 +1290,7 @@ public void CaptureTransaction_EventProcessor_Gets_ScopeAttachments() hint.Attachments.Should().Contain(attachments); } -#if ANDROID && CI_BUILD - // TODO: Test is flaky in CI - [SkippableTheory(typeof(NSubstitute.Exceptions.ReceivedCallsException))] -#else [Theory] -#endif [InlineData(false)] [InlineData(true)] public async Task FlushOnDispose_SendsEnvelope(bool cachingEnabled) From 55d4f22e81861a2e0982cdf96a79c4135e8f03e1 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Thu, 20 Jul 2023 00:21:11 +1200 Subject: [PATCH 116/142] Add Sampling Decision to Trace Envelope Header (#2495) --- CHANGELOG.md | 6 ++- src/Sentry/DynamicSamplingContext.cs | 13 ++++++ src/Sentry/Internal/Hub.cs | 2 +- .../DynamicSamplingContextTests.cs | 41 ++++++++++++++++--- ...rocessorTests.WithTransaction.verified.txt | 3 +- ...sactionEndedAsCrashed.Core3_1.verified.txt | 2 + ...ctionEndedAsCrashed.DotNet6_0.verified.txt | 2 + ...ctionEndedAsCrashed.DotNet7_0.verified.txt | 2 + ...sactionEndedAsCrashed.Mono4_0.verified.txt | 2 + ...nsactionEndedAsCrashed.Net4_8.verified.txt | 2 + ...sactionProcessorTests.Discard.verified.txt | 3 +- ...nsactionProcessorTests.Simple.verified.txt | 4 +- 12 files changed, 72 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e44edffa99..0d31b6ef57 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,13 @@ ## Unreleased +### Features + +- Added Sampling Decision to Trace Envelope Header ([#2495](https://github.com/getsentry/sentry-dotnet/pull/2495)) + ### Fixes -- Fixes baggage propagation when an exception is thrown from middleware ([#2487](https://github.com/getsentry/sentry-dotnet/pull/2487)) +- Fixed baggage propagation when an exception is thrown from middleware ([#2487](https://github.com/getsentry/sentry-dotnet/pull/2487)) - Fix Durable Functions preventing orchestrators from completing ([#2491](https://github.com/getsentry/sentry-dotnet/pull/2491)) - Re-enable HubTests.FlushOnDispose_SendsEnvelope ([#2492](https://github.com/getsentry/sentry-dotnet/pull/2492)) diff --git a/src/Sentry/DynamicSamplingContext.cs b/src/Sentry/DynamicSamplingContext.cs index baeab3f916..40ffc2161e 100644 --- a/src/Sentry/DynamicSamplingContext.cs +++ b/src/Sentry/DynamicSamplingContext.cs @@ -22,6 +22,7 @@ internal class DynamicSamplingContext private DynamicSamplingContext( SentryId traceId, string publicKey, + bool? sampled, double sampleRate, string? release = null, string? environment = null, @@ -51,6 +52,11 @@ private DynamicSamplingContext( ["sample_rate"] = sampleRate.ToString(CultureInfo.InvariantCulture) }; + if (sampled.HasValue) + { + items.Add("sampled", sampled.Value ? "true" : "false"); + } + // Set optional values if (!string.IsNullOrWhiteSpace(release)) { @@ -95,6 +101,11 @@ private DynamicSamplingContext( return null; } + if (items.TryGetValue("sampled", out var sampledString) && !bool.TryParse(sampledString, out _)) + { + return null; + } + if (!items.TryGetValue("sample_rate", out var sampleRate) || !double.TryParse(sampleRate, NumberStyles.Float, CultureInfo.InvariantCulture, out var rate) || rate is < 0.0 or > 1.0) @@ -110,6 +121,7 @@ public static DynamicSamplingContext CreateFromTransaction(TransactionTracer tra // These should already be set on the transaction. var publicKey = Dsn.Parse(options.Dsn!).PublicKey; var traceId = transaction.TraceId; + var sampled = transaction.IsSampled; var sampleRate = transaction.SampleRate!.Value; var userSegment = transaction.User.Segment; var transactionName = transaction.NameSource.IsHighQuality() ? transaction.Name : null; @@ -121,6 +133,7 @@ public static DynamicSamplingContext CreateFromTransaction(TransactionTracer tra return new DynamicSamplingContext( traceId, publicKey, + sampled, sampleRate, release, environment, diff --git a/src/Sentry/Internal/Hub.cs b/src/Sentry/Internal/Hub.cs index 0fbbac748b..cce39ec678 100644 --- a/src/Sentry/Internal/Hub.cs +++ b/src/Sentry/Internal/Hub.cs @@ -172,7 +172,7 @@ internal ITransaction StartTransaction( } // Use the provided DSC, or create one based on this transaction. - // This must be done AFTER the sampling decision has been made. + // DSC creation must be done AFTER the sampling decision has been made. transaction.DynamicSamplingContext = dynamicSamplingContext ?? transaction.CreateDynamicSamplingContext(_options); diff --git a/test/Sentry.Tests/DynamicSamplingContextTests.cs b/test/Sentry.Tests/DynamicSamplingContextTests.cs index df14242d21..b2d55a093d 100644 --- a/test/Sentry.Tests/DynamicSamplingContextTests.cs +++ b/test/Sentry.Tests/DynamicSamplingContextTests.cs @@ -1,3 +1,5 @@ +using Xunit.Sdk; + namespace Sentry.Tests; public class DynamicSamplingContextTests @@ -142,6 +144,22 @@ public void CreateFromBaggage_SampleRate_TooHigh() Assert.Null(dsc); } + [Fact] + public void CreateFromBaggage_Sampled_MalFormed() + { + var baggage = BaggageHeader.Create(new List> + { + {"sentry-trace_id", "43365712692146d08ee11a729dfbcaca"}, + {"sentry-public_key", "d4d82fc1c2c4032a83f3a29aa3a3aff"}, + {"sentry-sample_rate", "1.0"}, + {"sentry-sampled", "foo"}, + }); + + var dsc = baggage.CreateDynamicSamplingContext(); + + Assert.Null(dsc); + } + [Fact] public void CreateFromBaggage_Valid_Minimum() { @@ -168,6 +186,7 @@ public void CreateFromBaggage_Valid_Complete() { {"sentry-trace_id", "43365712692146d08ee11a729dfbcaca"}, {"sentry-public_key", "d4d82fc1c2c4032a83f3a29aa3a3aff"}, + {"sentry-sampled", "true"}, {"sentry-sample_rate", "1.0"}, {"sentry-release", "test@1.0.0+abc"}, {"sentry-environment", "production"}, @@ -178,9 +197,10 @@ public void CreateFromBaggage_Valid_Complete() var dsc = baggage.CreateDynamicSamplingContext(); Assert.NotNull(dsc); - Assert.Equal(7, dsc.Items.Count); + Assert.Equal(baggage.Members.Count, dsc.Items.Count); Assert.Equal("43365712692146d08ee11a729dfbcaca", Assert.Contains("trace_id", dsc.Items)); 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("test@1.0.0+abc", Assert.Contains("release", dsc.Items)); Assert.Equal("production", Assert.Contains("environment", dsc.Items)); @@ -210,8 +230,11 @@ public void ToBaggageHeader() Assert.Equal(original.Members, result.Members); } - [Fact] - public void CreateFromTransaction() + [Theory] + [InlineData(false)] + [InlineData(true)] + [InlineData(null)] + public void CreateFromTransaction(bool? isSampled) { var options = new SentryOptions { @@ -230,7 +253,7 @@ public void CreateFromTransaction() { Name = "GET /person/{id}", NameSource = TransactionNameSource.Route, - IsSampled = true, + IsSampled = isSampled, SampleRate = 0.5, User = { @@ -241,9 +264,17 @@ public void CreateFromTransaction() var dsc = transaction.CreateDynamicSamplingContext(options); Assert.NotNull(dsc); - Assert.Equal(7, 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) + { + Assert.Equal(sampled ? "true" : "false", Assert.Contains("sampled", dsc.Items)); + } + else + { + Assert.DoesNotContain("sampled", dsc.Items); + } 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)); diff --git a/test/Sentry.Tests/EventProcessorTests.WithTransaction.verified.txt b/test/Sentry.Tests/EventProcessorTests.WithTransaction.verified.txt index e142c331c3..5a7390c2d0 100644 --- a/test/Sentry.Tests/EventProcessorTests.WithTransaction.verified.txt +++ b/test/Sentry.Tests/EventProcessorTests.WithTransaction.verified.txt @@ -1,4 +1,4 @@ -[ +[ { Header: { event_id: Guid_1, @@ -10,6 +10,7 @@ public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, release: release, sample_rate: 1, + sampled: true, trace_id: Guid_2, transaction: my transaction } diff --git a/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.Core3_1.verified.txt b/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.Core3_1.verified.txt index c572a61273..a67f115e35 100644 --- a/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.Core3_1.verified.txt +++ b/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.Core3_1.verified.txt @@ -32,6 +32,7 @@ public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, release: release, sample_rate: 1, + sampled: true, trace_id: Guid_3, transaction: my transaction } @@ -144,6 +145,7 @@ public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, release: release, sample_rate: 1, + sampled: true, trace_id: Guid_3, transaction: my transaction } 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 index 50892ef63a..b2a88282d9 100644 --- a/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.DotNet6_0.verified.txt +++ b/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.DotNet6_0.verified.txt @@ -32,6 +32,7 @@ public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, release: release, sample_rate: 1, + sampled: true, trace_id: Guid_3, transaction: my transaction } @@ -144,6 +145,7 @@ public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, release: release, sample_rate: 1, + sampled: true, trace_id: Guid_3, transaction: my transaction } 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 index 09055037ef..7e4c8954b0 100644 --- a/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.DotNet7_0.verified.txt +++ b/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.DotNet7_0.verified.txt @@ -32,6 +32,7 @@ public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, release: release, sample_rate: 1, + sampled: true, trace_id: Guid_3, transaction: my transaction } @@ -144,6 +145,7 @@ public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, release: release, sample_rate: 1, + sampled: true, trace_id: Guid_3, transaction: my transaction } diff --git a/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.Mono4_0.verified.txt b/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.Mono4_0.verified.txt index f879896db4..b4ed75b70a 100644 --- a/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.Mono4_0.verified.txt +++ b/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.Mono4_0.verified.txt @@ -32,6 +32,7 @@ public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, release: release, sample_rate: 1, + sampled: true, trace_id: Guid_3, transaction: my transaction } @@ -144,6 +145,7 @@ public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, release: release, sample_rate: 1, + sampled: true, trace_id: Guid_3, transaction: my transaction } 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 2b2d328805..04524791db 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 @@ -32,6 +32,7 @@ public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, release: release, sample_rate: 1, + sampled: true, trace_id: Guid_3, transaction: my transaction } @@ -144,6 +145,7 @@ public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, release: release, sample_rate: 1, + sampled: true, trace_id: Guid_3, transaction: my transaction } diff --git a/test/Sentry.Tests/TransactionProcessorTests.Discard.verified.txt b/test/Sentry.Tests/TransactionProcessorTests.Discard.verified.txt index bba787fc3f..1392e2d68c 100644 --- a/test/Sentry.Tests/TransactionProcessorTests.Discard.verified.txt +++ b/test/Sentry.Tests/TransactionProcessorTests.Discard.verified.txt @@ -1,4 +1,4 @@ -[ +[ { Header: { event_id: Guid_1, @@ -10,6 +10,7 @@ public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, release: release, sample_rate: 1, + sampled: true, trace_id: Guid_2, transaction: my transaction } diff --git a/test/Sentry.Tests/TransactionProcessorTests.Simple.verified.txt b/test/Sentry.Tests/TransactionProcessorTests.Simple.verified.txt index 5b2e472532..894eb09320 100644 --- a/test/Sentry.Tests/TransactionProcessorTests.Simple.verified.txt +++ b/test/Sentry.Tests/TransactionProcessorTests.Simple.verified.txt @@ -1,4 +1,4 @@ -[ +[ { Header: { event_id: Guid_1, @@ -10,6 +10,7 @@ public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, release: release, sample_rate: 1, + sampled: true, trace_id: Guid_2, transaction: my transaction } @@ -51,6 +52,7 @@ public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, release: release, sample_rate: 1, + sampled: true, trace_id: Guid_2, transaction: my transaction } From bbd1e66540800268b688ed96e2e875c2fd65b81c Mon Sep 17 00:00:00 2001 From: Sean Feldman Date: Thu, 20 Jul 2023 02:57:03 -0600 Subject: [PATCH 117/142] Support transaction finishing automatically with 'idle timeout' (#2452) --- CHANGELOG.md | 1 + CONTRIBUTING.md | 10 +++ .../SentrySpanProcessor.cs | 14 +++- src/Sentry/TransactionTracer.cs | 50 +++++++++++++- .../SentrySpanProcessorTests.cs | 69 ++++++++++++------- .../Sentry.Tests/Protocol/TransactionTests.cs | 69 +++++++++++++++++++ 6 files changed, 185 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d31b6ef57..90519d3178 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ - Added a MSBuild property `SentryUploadAndroidProguardMapping` to automatically upload the Proguard mapping file when targeting Android ([#2455](https://github.com/getsentry/sentry-dotnet/pull/2455)) - Symbolication for Single File Apps ([#2425](https://github.com/getsentry/sentry-dotnet/pull/2425)) - Add binding to `SwiftAsyncStacktraces` on iOS ([#2436](https://github.com/getsentry/sentry-dotnet/pull/2436)) +- Support transaction finishing automatically with 'idle timeout' ([#2452](https://github.com/getsentry/sentry-dotnet/pull/2452)) ### Fixes diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0ffe15b279..6e7096ff48 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -103,6 +103,16 @@ Below that, you'll add the heading 3 mentioned above. For example, if you're add There's a GitHub action check to verify if an entry was added. If the entry isn't a user-facing change, you can skip the verification with `#skip-changelog` written to the PR description. The bot writes a comment in the PR with a suggestion entry to the changelog based on the PR title. +## Naming tests + +Ideally we like tests to be named following the convention `Method_Context_Expectation`. + +[For example](https://github.com/getsentry/sentry-dotnet/blob/ebd70ffafd5f8bd5eb6bb9ee1a03cac77ae67b8d/test/Sentry.Tests/HubTests.cs#L43C1-L44C68): +```csharp + [Fact] + public void PushScope_BreadcrumbWithinScope_NotVisibleOutside() +``` + ## Verify tests Some tests use [Verify](https://github.com/VerifyTests/Verify) to check returned objects against snapshots that are part of the repo. diff --git a/src/Sentry.OpenTelemetry/SentrySpanProcessor.cs b/src/Sentry.OpenTelemetry/SentrySpanProcessor.cs index b31cf277d8..9a225da4a7 100644 --- a/src/Sentry.OpenTelemetry/SentrySpanProcessor.cs +++ b/src/Sentry.OpenTelemetry/SentrySpanProcessor.cs @@ -112,10 +112,20 @@ public override void OnEnd(Activity data) if (attributes.TryGetTypedValue("http.url", out string? url) && (_options?.IsSentryRequest(url) ?? false)) { _options?.DiagnosticLogger?.LogDebug($"Ignoring Activity {data.SpanId} for Sentry request."); - if (_map.TryRemove(data.SpanId, out var removed) && (removed is SpanTracer spanTracerToRemove)) + + if (_map.TryRemove(data.SpanId, out var removed)) { - spanTracerToRemove.IsSentryRequest = true; + if (removed is SpanTracer spanTracerToRemove) + { + spanTracerToRemove.IsSentryRequest = true; + } + + if (removed is TransactionTracer transactionTracer) + { + transactionTracer.IsSentryRequest = true; + } } + return; } diff --git a/src/Sentry/TransactionTracer.cs b/src/Sentry/TransactionTracer.cs index 3cf14d2a7e..ac4bc64d02 100644 --- a/src/Sentry/TransactionTracer.cs +++ b/src/Sentry/TransactionTracer.cs @@ -10,6 +10,8 @@ namespace Sentry; public class TransactionTracer : ITransaction, IHasDistribution, IHasTransactionNameSource, IHasMeasurements { private readonly IHub _hub; + private readonly Timer? _idleTimer; + private long _idleTimerStopped; private readonly SentryStopwatch _stopwatch = SentryStopwatch.StartNew(); private readonly Instrumenter _instrumenter = Instrumenter.Sentry; @@ -178,6 +180,15 @@ public IReadOnlyList Fingerprint internal ITransactionProfiler? TransactionProfiler { get; set; } + /// + /// Used by the Sentry.OpenTelemetry.SentrySpanProcessor to mark a transaction as a Sentry request. Ideally we wouldn't + /// create this transaction but since we can't avoid doing that, once we detect that it's a Sentry request we mark it + /// as such so that we can prevent finishing the transaction tracer when idle timeout elapses and the TransactionTracer gets converted into + /// a Transaction. + /// + internal bool IsSentryRequest { get; set; } + + // TODO: mark as internal in version 4 /// /// Initializes an instance of . /// @@ -186,6 +197,7 @@ public TransactionTracer(IHub hub, string name, string operation) { } + // TODO: mark as internal in version 4 /// /// Initializes an instance of . /// @@ -203,7 +215,14 @@ public TransactionTracer(IHub hub, string name, string operation, TransactionNam /// /// Initializes an instance of . /// - public TransactionTracer(IHub hub, ITransactionContext context) + public TransactionTracer(IHub hub, ITransactionContext context) : this(hub, context, null) + { + } + + /// + /// Initializes an instance of . + /// + internal TransactionTracer(IHub hub, ITransactionContext context, TimeSpan? idleTimeout = null) { _hub = hub; Name = context.Name; @@ -215,12 +234,25 @@ public TransactionTracer(IHub hub, ITransactionContext context) Description = context.Description; Status = context.Status; IsSampled = context.IsSampled; - StartTimestamp = _stopwatch.StartDateTimeOffset; - if (context is TransactionContext transactionContext) + if (context is TransactionContext transactionContext) { _instrumenter = transactionContext.Instrumenter; } + + // Set idle timer only if an idle timeout has been provided directly + if (idleTimeout.HasValue) + { + _idleTimer = new Timer(state => + { + if (state is not TransactionTracer transactionTracer) + { + return; + } + + transactionTracer.Finish(Status ?? SpanStatus.Ok); + }, this, idleTimeout.Value, Timeout.InfiniteTimeSpan); + } } /// @@ -278,6 +310,18 @@ private void AddChildSpan(SpanTracer span) /// public void Finish() { + if (Interlocked.Exchange(ref _idleTimerStopped, 1) == 0) + { + _idleTimer?.Change(Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan); + + _idleTimer?.Dispose(); + } + + if (IsSentryRequest) + { + return; + } + TransactionProfiler?.Finish(); Status ??= SpanStatus.Ok; EndTimestamp ??= _stopwatch.CurrentDateTimeOffset; diff --git a/test/Sentry.OpenTelemetry.Tests/SentrySpanProcessorTests.cs b/test/Sentry.OpenTelemetry.Tests/SentrySpanProcessorTests.cs index fb958d13c8..50af048c29 100644 --- a/test/Sentry.OpenTelemetry.Tests/SentrySpanProcessorTests.cs +++ b/test/Sentry.OpenTelemetry.Tests/SentrySpanProcessorTests.cs @@ -242,19 +242,17 @@ public void OnEnd_FinishesSpan() using (new AssertionScope()) { - using (new AssertionScope()) + spanTracer.ParentSpanId.Should().Be(parent.SpanId.AsSentrySpanId()); + spanTracer.Operation.Should().Be(data.OperationName); + spanTracer.Description.Should().Be(data.DisplayName); + spanTracer.EndTimestamp.Should().NotBeNull(); + spanTracer.Extra["otel.kind"].Should().Be(data.Kind); + foreach (var keyValuePair in tags) { - spanTracer.ParentSpanId.Should().Be(parent.SpanId.AsSentrySpanId()); - spanTracer.Operation.Should().Be(data.OperationName); - spanTracer.Description.Should().Be(data.DisplayName); - spanTracer.EndTimestamp.Should().NotBeNull(); - spanTracer.Extra["otel.kind"].Should().Be(data.Kind); - foreach (var keyValuePair in tags) - { - span.Extra[keyValuePair.Key].Should().Be(keyValuePair.Value); - } - spanTracer.Status.Should().Be(SpanStatus.Ok); + span.Extra[keyValuePair.Key].Should().Be(keyValuePair.Value); } + + spanTracer.Status.Should().Be(SpanStatus.Ok); } } @@ -286,19 +284,44 @@ public void OnEnd_FinishesTransaction() using (new AssertionScope()) { - using (new AssertionScope()) + transaction.ParentSpanId.Should().Be(new ActivitySpanId().AsSentrySpanId()); + transaction.Operation.Should().Be(data.OperationName); + transaction.Description.Should().Be(data.DisplayName); + transaction.Name.Should().Be(data.DisplayName); + transaction.NameSource.Should().Be(TransactionNameSource.Custom); + transaction.EndTimestamp.Should().NotBeNull(); + transaction.Contexts["otel"].Should().BeEquivalentTo(new Dictionary { - transaction.ParentSpanId.Should().Be(new ActivitySpanId().AsSentrySpanId()); - transaction.Operation.Should().Be(data.OperationName); - transaction.Description.Should().Be(data.DisplayName); - transaction.Name.Should().Be(data.DisplayName); - transaction.NameSource.Should().Be(TransactionNameSource.Custom); - transaction.EndTimestamp.Should().NotBeNull(); - transaction.Contexts["otel"].Should().BeEquivalentTo(new Dictionary{ - { "attributes", tags } - }); - transaction.Status.Should().Be(SpanStatus.Ok); - } + { "attributes", tags } + }); + transaction.Status.Should().Be(SpanStatus.Ok); + } + } + + [Fact] + public void OnEnd_IsSentryRequest_DoesNotFinishTransaction() + { + // Arrange + _fixture.Options.Instrumenter = Instrumenter.OpenTelemetry; + var sut = _fixture.GetSut(); + + var tags = new Dictionary { { "foo", "bar" }, { "http.url", _fixture.Options.Dsn } }; + var data = Tracer.StartActivity(name: "test operation", kind: ActivityKind.Internal, parentContext: default, tags)!; + data.DisplayName = "test display name"; + sut.OnStart(data); + + sut._map.TryGetValue(data.SpanId, out var span); + + // Act + sut.OnEnd(data); + + // Assert + if (span is not TransactionTracer transaction) + { + Assert.Fail("Span is not a transaction tracer"); + return; } + + transaction.IsSentryRequest.Should().BeTrue(); } } diff --git a/test/Sentry.Tests/Protocol/TransactionTests.cs b/test/Sentry.Tests/Protocol/TransactionTests.cs index 6a523c8433..c48ba870c0 100644 --- a/test/Sentry.Tests/Protocol/TransactionTests.cs +++ b/test/Sentry.Tests/Protocol/TransactionTests.cs @@ -388,6 +388,41 @@ public void Finish_SentryRequestSpansGetIgnored() transaction.Spans.Should().NotContain(s => s.Operation == "sentryRequest"); } + [Fact] + public async Task Finish_SentryRequestTransactionGetsIgnored() + { + // Arrange + var client = Substitute.For(); + var options = new SentryOptions + { + Dsn = ValidDsn, + }; + var hub = new Hub(options, client); + var context = new TransactionContext( + SpanId.Create(), + SpanId.Create(), + SentryId.Create(), + "my name", + "my operation", + "description", + SpanStatus.Ok, + null, + true, + TransactionNameSource.Component + ); + + var transaction = new TransactionTracer(hub, context, TimeSpan.FromMilliseconds(2)) + { + IsSentryRequest = true + }; + + // Act + await Task.Delay(TimeSpan.FromMilliseconds(5)); + + // Assert + transaction.IsFinished.Should().BeFalse(); + } + [Fact] public void Finish_CapturesTransaction() { @@ -526,4 +561,38 @@ public void ISpan_GetTransaction_FromSpan() // Assert Assert.Same(transaction, result); } + + [Fact] + public async Task NewTransactionTracer_IdleTimeoutProvided_AutomaticallyFinishes() + { + // Arrange + var client = Substitute.For(); + var options = new SentryOptions + { + Dsn = ValidDsn, + Debug = true + }; + var hub = new Hub(options, client); + var context = new TransactionContext( + SpanId.Create(), + SpanId.Create(), + SentryId.Create(), + "my name", + "my operation", + "description", + SpanStatus.Ok, + null, + true, + TransactionNameSource.Component + ); + + var transaction = new TransactionTracer(hub, context, TimeSpan.FromMilliseconds(2)); + + // Act + await Task.Delay(TimeSpan.FromSeconds(2)); + + // Assert + transaction.IsFinished.Should().BeTrue(); + } + } From 1fde75e5a347f3f05bf441ac26e9dbb2c82751fe Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 21 Jul 2023 11:39:03 +0200 Subject: [PATCH 118/142] chore: update modules/sentry-cocoa to 8.9.2 (#2500) --- CHANGELOG.md | 6 +++--- modules/sentry-cocoa | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 90519d3178..fc420696cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,9 +17,9 @@ - Bump Java SDK from v6.25.1 to v6.25.2 ([#2484](https://github.com/getsentry/sentry-dotnet/pull/2484)) - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#6252) - [diff](https://github.com/getsentry/sentry-java/compare/6.25.1...6.25.2) -- Bump Cocoa SDK from v8.8.0 to v8.9.1 ([#2479](https://github.com/getsentry/sentry-dotnet/pull/2479), [#2483](https://github.com/getsentry/sentry-dotnet/pull/2483)) - - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#891) - - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.8.0...8.9.1) +- Bump Cocoa SDK from v8.8.0 to v8.9.2 ([#2479](https://github.com/getsentry/sentry-dotnet/pull/2479), [#2483](https://github.com/getsentry/sentry-dotnet/pull/2483), [#2500](https://github.com/getsentry/sentry-dotnet/pull/2500)) + - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#892) + - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.8.0...8.9.2) ## 3.34.0 diff --git a/modules/sentry-cocoa b/modules/sentry-cocoa index e46936ed19..225c0296ba 160000 --- a/modules/sentry-cocoa +++ b/modules/sentry-cocoa @@ -1 +1 @@ -Subproject commit e46936ed191c0112cd3276e1c10c0bb7f865268e +Subproject commit 225c0296baecebe9219e3e85a99135169c167eb5 From 2f58db228c54133d10ef3f9eabf26b22af5b5cc5 Mon Sep 17 00:00:00 2001 From: IhateTrains Date: Mon, 24 Jul 2023 01:38:17 +0200 Subject: [PATCH 119/142] Fix typo in README (#2504) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 165364ac0f..7038eb9e30 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ Each NuGet package in the table above has its custom view of the docs. Click on Sentry has extensive documentation for its SDKs on [https://docs.sentry.io](https://docs.sentry.io/platforms/dotnet/). -Additionally, our [.NET API refererence docs](https://getsentry.github.io/sentry-dotnet/index.html) are generated and deployed on each merge to main. +Additionally, our [.NET API reference docs](https://getsentry.github.io/sentry-dotnet/index.html) are generated and deployed on each merge to main. ### Samples From a78a95c2e56c5bfc2da424910e1f6b7b7d7c9dc6 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Mon, 24 Jul 2023 20:59:26 +1200 Subject: [PATCH 120/142] Fixed SDK Failing to capture exceptions via Blazor WebAssembly (#2506) --- CHANGELOG.md | 1 + src/Sentry/Internal/DebugStackTrace.cs | 21 ++++++++++++++------- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fc420696cd..b889cbc3f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - Fixed baggage propagation when an exception is thrown from middleware ([#2487](https://github.com/getsentry/sentry-dotnet/pull/2487)) - Fix Durable Functions preventing orchestrators from completing ([#2491](https://github.com/getsentry/sentry-dotnet/pull/2491)) - Re-enable HubTests.FlushOnDispose_SendsEnvelope ([#2492](https://github.com/getsentry/sentry-dotnet/pull/2492)) +- Fixed the SDK failing to report issues via Blazor WebAssembly due to a `PlatformNotSupportedException` ([#2506](https://github.com/getsentry/sentry-dotnet/pull/2506)) ### Dependencies diff --git a/src/Sentry/Internal/DebugStackTrace.cs b/src/Sentry/Internal/DebugStackTrace.cs index 0e1517fe20..056bfe010a 100644 --- a/src/Sentry/Internal/DebugStackTrace.cs +++ b/src/Sentry/Internal/DebugStackTrace.cs @@ -440,16 +440,23 @@ private static void DemangleLambdaReturnType(SentryStackFrame frame) #if NET5_0_OR_GREATER && PLATFORM_NEUTRAL // Maybe we're dealing with a single file assembly // https://github.com/getsentry/sentry-dotnet/issues/2362 - if (SingleFileApp.MainModule.IsBundle()) + try { - if (SingleFileApp.MainModule?.GetDebugImage(module) is not { } embeddedDebugImage) + if (SingleFileApp.MainModule.IsBundle()) { - options.LogInfo("Skipping embedded debug image for module '{0}' because the Debug ID couldn't be determined", moduleName); - return null; - } + if (SingleFileApp.MainModule?.GetDebugImage(module) is not { } embeddedDebugImage) + { + options.LogInfo("Skipping embedded debug image for module '{0}' because the Debug ID couldn't be determined", moduleName); + return null; + } - options.LogDebug("Got embedded debug image for '{0}' having Debug ID: {1}", moduleName, embeddedDebugImage.DebugId); - return embeddedDebugImage; + options.LogDebug("Got embedded debug image for '{0}' having Debug ID: {1}", moduleName, embeddedDebugImage.DebugId); + return embeddedDebugImage; + } + } + catch (PlatformNotSupportedException) + { + // Thrown by Blazor WASM (and possibly other platforms). } #endif From 621212eabbd0198eb7c04c3b63fb5ba3fc082d82 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Jul 2023 12:34:50 +0200 Subject: [PATCH 121/142] Bump gradle/gradle-build-action from 2.6.0 to 2.6.1 (#2507) --- .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 b6b8d985ef..33c26df4ec 100644 --- a/.github/workflows/device-tests-android.yml +++ b/.github/workflows/device-tests-android.yml @@ -84,7 +84,7 @@ jobs: run: dotnet tool install Microsoft.DotNet.XHarness.CLI --global --version "1.*-*" - name: Setup Gradle - uses: gradle/gradle-build-action@bd5760595778326ba7f1441bcf7e88b49de61a25 # pin@v2 + uses: gradle/gradle-build-action@915a66c096a03101667f9df2e56c9efef558b165 # pin@v2 # Cached AVD setup per https://github.com/ReactiveCircus/android-emulator-runner/blob/main/README.md From 6a1f732a55e0642715758b0d2e15b18c72ef645d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 25 Jul 2023 10:39:50 +0200 Subject: [PATCH 122/142] chore: update modules/sentry-cocoa to 8.9.3 (#2510) --- CHANGELOG.md | 6 +++--- modules/sentry-cocoa | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b889cbc3f5..8cbee33212 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,9 +18,9 @@ - Bump Java SDK from v6.25.1 to v6.25.2 ([#2484](https://github.com/getsentry/sentry-dotnet/pull/2484)) - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#6252) - [diff](https://github.com/getsentry/sentry-java/compare/6.25.1...6.25.2) -- Bump Cocoa SDK from v8.8.0 to v8.9.2 ([#2479](https://github.com/getsentry/sentry-dotnet/pull/2479), [#2483](https://github.com/getsentry/sentry-dotnet/pull/2483), [#2500](https://github.com/getsentry/sentry-dotnet/pull/2500)) - - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#892) - - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.8.0...8.9.2) +- Bump Cocoa SDK from v8.8.0 to v8.9.3 ([#2479](https://github.com/getsentry/sentry-dotnet/pull/2479), [#2483](https://github.com/getsentry/sentry-dotnet/pull/2483), [#2500](https://github.com/getsentry/sentry-dotnet/pull/2500), [#2510](https://github.com/getsentry/sentry-dotnet/pull/2510)) + - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#893) + - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.8.0...8.9.3) ## 3.34.0 diff --git a/modules/sentry-cocoa b/modules/sentry-cocoa index 225c0296ba..259d8bc75a 160000 --- a/modules/sentry-cocoa +++ b/modules/sentry-cocoa @@ -1 +1 @@ -Subproject commit 225c0296baecebe9219e3e85a99135169c167eb5 +Subproject commit 259d8bc75aa4028416535d35840ff19fc7661292 From 42df89dea182d7d345165cd1cb2fe4bd8de50fc8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 25 Jul 2023 10:54:58 +0200 Subject: [PATCH 123/142] chore: update scripts/update-cli.ps1 to 2.20.0 (#2509) --- 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 8cbee33212..f3114640bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,9 @@ - Bump Java SDK from v6.25.1 to v6.25.2 ([#2484](https://github.com/getsentry/sentry-dotnet/pull/2484)) - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#6252) - [diff](https://github.com/getsentry/sentry-java/compare/6.25.1...6.25.2) +- Bump CLI from v2.19.4 to v2.20.0 ([#2509](https://github.com/getsentry/sentry-dotnet/pull/2509)) + - [changelog](https://github.com/getsentry/sentry-cli/blob/master/CHANGELOG.md#2200) + - [diff](https://github.com/getsentry/sentry-cli/compare/2.19.4...2.20.0) - Bump Cocoa SDK from v8.8.0 to v8.9.3 ([#2479](https://github.com/getsentry/sentry-dotnet/pull/2479), [#2483](https://github.com/getsentry/sentry-dotnet/pull/2483), [#2500](https://github.com/getsentry/sentry-dotnet/pull/2500), [#2510](https://github.com/getsentry/sentry-dotnet/pull/2510)) - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#893) - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.8.0...8.9.3) diff --git a/Directory.Build.props b/Directory.Build.props index 943e5ab1f0..183fa43376 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -100,7 +100,7 @@ - 2.19.4 + 2.20.0 $(MSBuildThisFileDirectory)tools\sentry-cli\$(SentryCLIVersion)\ diff --git a/src/Sentry/Sentry.csproj b/src/Sentry/Sentry.csproj index b0ccee5a70..de4cc752d6 100644 --- a/src/Sentry/Sentry.csproj +++ b/src/Sentry/Sentry.csproj @@ -96,25 +96,25 @@ + Include="sentry-cli-Darwin-arm64" FileHash="c726cf9092cb22b688c4c522f003a27527d9e6a35d6b37d442c2e0d0dc9b44c5" /> + Include="sentry-cli-Darwin-x86_64" FileHash="0c1ec6ca2b34975f4543d0c27141c3dd4a44799ee2ad164eac95f382ec8bd743" /> + Include="sentry-cli-Linux-aarch64" FileHash="1af69abb121554f32119c146efbab71cf861babb2cc8b824c1b75f586feed0a3" /> + Include="sentry-cli-Linux-i686" FileHash="42c75f02f3fba243ec47031fff3254a25986ec9f93cedbf729148ba3046fd133" /> + Include="sentry-cli-Linux-x86_64" FileHash="20f9463929fdc0719e19c6bdb7fb7e12fdb91c9fbfe51f0354401e09adc562cb" /> + Include="sentry-cli-Windows-i686.exe" FileHash="bf5b22d6d3aa8ac748837cedc5be47bb4f8105579ab30612a50746dd8ee2850d" /> + Include="sentry-cli-Windows-x86_64.exe" FileHash="299e9f3632bd8278384e60f7384279ccb394ca532515448f44e089a3fb119f1c" /> From 1608a84b78e822fb3ace57cb7926b8137d48bf94 Mon Sep 17 00:00:00 2001 From: Alexander Schuetz Date: Tue, 25 Jul 2023 15:05:38 +0200 Subject: [PATCH 124/142] Use 'HttpMessageHandler' instead of 'HttpClientHandler' to support NSUrlSessionHandler on iOS (#2503) --- CHANGELOG.md | 1 + .../Program.cs | 4 +- .../Program.cs | 2 +- .../Http/DefaultSentryHttpClientFactory.cs | 80 +++++++++++++++---- src/Sentry/SentryOptions.cs | 22 ++++- .../IntegrationTests.cs | 2 +- .../SamplingTransactionProfilerTests.cs | 2 +- .../ApiApprovalTests.Run.Core3_1.verified.txt | 2 + ...piApprovalTests.Run.DotNet6_0.verified.txt | 2 + ...piApprovalTests.Run.DotNet7_0.verified.txt | 2 + .../ApiApprovalTests.Run.Net4_8.verified.txt | 2 + test/Sentry.Tests/HubTests.cs | 2 +- .../DefaultSentryHttpClientFactoryTests.cs | 6 +- test/Sentry.Tests/SentryClientTests.cs | 2 +- 14 files changed, 106 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f3114640bc..e4fb6d0b56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - Fixed baggage propagation when an exception is thrown from middleware ([#2487](https://github.com/getsentry/sentry-dotnet/pull/2487)) - Fix Durable Functions preventing orchestrators from completing ([#2491](https://github.com/getsentry/sentry-dotnet/pull/2491)) - Re-enable HubTests.FlushOnDispose_SendsEnvelope ([#2492](https://github.com/getsentry/sentry-dotnet/pull/2492)) +- Introduced `HttpMessageHandler` in favor of the now deprecated `HttpClientHandler` on the options. This allows the SDK to support NSUrlSessionHandler on iOS ([#2503](https://github.com/getsentry/sentry-dotnet/pull/2503)) - Fixed the SDK failing to report issues via Blazor WebAssembly due to a `PlatformNotSupportedException` ([#2506](https://github.com/getsentry/sentry-dotnet/pull/2506)) ### Dependencies diff --git a/samples/Sentry.Samples.Console.Customized/Program.cs b/samples/Sentry.Samples.Console.Customized/Program.cs index ae347ef6f4..b1af6291fd 100644 --- a/samples/Sentry.Samples.Console.Customized/Program.cs +++ b/samples/Sentry.Samples.Console.Customized/Program.cs @@ -90,8 +90,8 @@ await SentrySdk.ConfigureScopeAsync(async scope => // Using a proxy: o.HttpProxy = null; //new WebProxy("https://localhost:3128"); - // Example customizing the HttpClientHandlers created - o.CreateHttpClientHandler = () => new HttpClientHandler + // Example customizing the HttpMessageHandlers created + o.CreateHttpMessageHandler = () => new HttpClientHandler { ServerCertificateCustomValidationCallback = (_, certificate, _, _) => !certificate.Archived diff --git a/samples/Sentry.Samples.Console.Profiling/Program.cs b/samples/Sentry.Samples.Console.Profiling/Program.cs index bf3fdf1dc1..5d3d1ee1c2 100644 --- a/samples/Sentry.Samples.Console.Profiling/Program.cs +++ b/samples/Sentry.Samples.Console.Profiling/Program.cs @@ -85,7 +85,7 @@ await SentrySdk.ConfigureScopeAsync(async scope => o.HttpProxy = null; //new WebProxy("https://localhost:3128"); // Example customizing the HttpClientHandlers created - o.CreateHttpClientHandler = () => new HttpClientHandler + o.CreateHttpMessageHandler = () => new HttpClientHandler { ServerCertificateCustomValidationCallback = (_, certificate, _, _) => !certificate.Archived diff --git a/src/Sentry/Internal/Http/DefaultSentryHttpClientFactory.cs b/src/Sentry/Internal/Http/DefaultSentryHttpClientFactory.cs index 51ac29f6fc..f8577e5bd7 100644 --- a/src/Sentry/Internal/Http/DefaultSentryHttpClientFactory.cs +++ b/src/Sentry/Internal/Http/DefaultSentryHttpClientFactory.cs @@ -20,25 +20,77 @@ public HttpClient Create(SentryOptions options) throw new ArgumentNullException(nameof(options)); } - var httpClientHandler = options.CreateHttpClientHandler?.Invoke() ?? new HttpClientHandler(); - if (options.HttpProxy != null) + HttpMessageHandler handler = options.CreateHttpMessageHandler?.Invoke() ?? new HttpClientHandler(); + if (handler is HttpClientHandler httpClientHandler) { - httpClientHandler.Proxy = options.HttpProxy; - options.LogInfo("Using Proxy: {0}", options.HttpProxy); - } + if (options.HttpProxy != null) + { + httpClientHandler.Proxy = options.HttpProxy; + options.LogInfo("Using Proxy: {0}", options.HttpProxy); + } - // If the platform supports automatic decompression - if (SupportsAutomaticDecompression(httpClientHandler)) - { - // if the SDK is configured to accept compressed data - httpClientHandler.AutomaticDecompression = options.DecompressionMethods; + // If the platform supports automatic decompression + if (SupportsAutomaticDecompression(httpClientHandler)) + { + // if the SDK is configured to accept compressed data + httpClientHandler.AutomaticDecompression = options.DecompressionMethods; + } + else + { + options.LogWarning("No response compression supported by HttpClientHandler."); + } } - else +#if IOS + if (handler is System.Net.Http.NSUrlSessionHandler nsUrlSessionHandler) { - options.LogDebug("No response compression supported by HttpClientHandler."); - } + if (options.HttpProxy != null) + { + bool supportsProxy = false; + if (nsUrlSessionHandler.SupportsProxy) + { + supportsProxy = true; + try + { + // Code analysis reports this as error, since it is marked as unsupported. + // Being aware of that this code is meant to support this feature as soon as + // supports it. +#pragma warning disable CA1416 + nsUrlSessionHandler.Proxy = options.HttpProxy; +#pragma warning restore CA1416 + options.LogInfo("Using Proxy: {0}", options.HttpProxy); + } + catch (PlatformNotSupportedException) + { + supportsProxy = false; + } + } + if (!supportsProxy) + { + options.LogWarning("No proxy supported by NSUrlSessionHandler."); + } + } - HttpMessageHandler handler = httpClientHandler; + // If the platform supports automatic decompression + bool compressionSupported = false; + try + { + if (nsUrlSessionHandler.SupportsAutomaticDecompression) + { + // if the SDK is configured to accept compressed data + nsUrlSessionHandler.AutomaticDecompression = options.DecompressionMethods; + compressionSupported = true; + } + } + catch (PlatformNotSupportedException) + { + compressionSupported = false; + } + if (!compressionSupported) + { + options.LogInfo("No response compression supported by NSUrlSessionHandler."); + } + } +#endif if (options.RequestBodyCompressionLevel != CompressionLevel.NoCompression) { diff --git a/src/Sentry/SentryOptions.cs b/src/Sentry/SentryOptions.cs index fa99c1644d..7dbc14e97b 100644 --- a/src/Sentry/SentryOptions.cs +++ b/src/Sentry/SentryOptions.cs @@ -540,10 +540,30 @@ public int MaxCacheItems /// public IWebProxy? HttpProxy { get; set; } + /// + /// private field to hold the , since a typecheck or cast won't work here. + /// + private Func? _createClientHandler = null; + /// /// Creates the inner most . + /// Deprecated in favor of . + /// + [Obsolete("Use CreateHttpMessageHandler instead")] + public Func? CreateHttpClientHandler + { + get => _createClientHandler; + set + { + CreateHttpMessageHandler = value; + _createClientHandler = value; + } + } + + /// + /// Creates the inner most . /// - public Func? CreateHttpClientHandler { get; set; } + public Func? CreateHttpMessageHandler { get; set; } /// /// A callback invoked when a is created. diff --git a/test/Sentry.Google.Cloud.Functions.Tests/IntegrationTests.cs b/test/Sentry.Google.Cloud.Functions.Tests/IntegrationTests.cs index f4411ee92d..a073e84e74 100644 --- a/test/Sentry.Google.Cloud.Functions.Tests/IntegrationTests.cs +++ b/test/Sentry.Google.Cloud.Functions.Tests/IntegrationTests.cs @@ -34,7 +34,7 @@ async Task VerifyAsync(HttpRequestMessage message) { // So we can assert on the payload without the need to Gzip decompress o.RequestBodyCompressionLevel = CompressionLevel.NoCompression; - o.CreateHttpClientHandler = () => new CallbackHttpClientHandler(VerifyAsync); + o.CreateHttpMessageHandler = () => new CallbackHttpClientHandler(VerifyAsync); }); services.AddFunctionTarget(); }) diff --git a/test/Sentry.Profiling.Tests/SamplingTransactionProfilerTests.cs b/test/Sentry.Profiling.Tests/SamplingTransactionProfilerTests.cs index 40d7e6e9cc..c6c47f0402 100644 --- a/test/Sentry.Profiling.Tests/SamplingTransactionProfilerTests.cs +++ b/test/Sentry.Profiling.Tests/SamplingTransactionProfilerTests.cs @@ -131,7 +131,7 @@ async Task VerifyAsync(HttpRequestMessage message) FileSystem = fileSystem, // So we don't need to deal with gzip'ed payload RequestBodyCompressionLevel = CompressionLevel.NoCompression, - CreateHttpClientHandler = () => new CallbackHttpClientHandler(VerifyAsync), + CreateHttpMessageHandler = () => new CallbackHttpClientHandler(VerifyAsync), // Not to send some session envelope AutoSessionTracking = false, Debug = true, diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.Core3_1.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.Core3_1.verified.txt index 8f01bbf7d5..7c4bb4c1d5 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.Core3_1.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.Core3_1.verified.txt @@ -603,7 +603,9 @@ namespace Sentry public bool CaptureFailedRequests { get; set; } public System.Action? ConfigureClient { get; set; } public System.Func? CrashedLastRun { get; set; } + [System.Obsolete("Use CreateHttpMessageHandler instead")] public System.Func? CreateHttpClientHandler { 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; } diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt index 6cc1c4e4a6..e2a943d0d3 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt @@ -604,7 +604,9 @@ namespace Sentry public bool CaptureFailedRequests { get; set; } public System.Action? ConfigureClient { get; set; } public System.Func? CrashedLastRun { get; set; } + [System.Obsolete("Use CreateHttpMessageHandler instead")] public System.Func? CreateHttpClientHandler { 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; } diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt index 6cc1c4e4a6..e2a943d0d3 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt @@ -604,7 +604,9 @@ namespace Sentry public bool CaptureFailedRequests { get; set; } public System.Action? ConfigureClient { get; set; } public System.Func? CrashedLastRun { get; set; } + [System.Obsolete("Use CreateHttpMessageHandler instead")] public System.Func? CreateHttpClientHandler { 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; } diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt index 5374f5f004..ac94a57bac 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt @@ -602,7 +602,9 @@ namespace Sentry public bool CaptureFailedRequests { get; set; } public System.Action? ConfigureClient { get; set; } public System.Func? CrashedLastRun { get; set; } + [System.Obsolete("Use CreateHttpMessageHandler instead")] public System.Func? CreateHttpClientHandler { 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; } diff --git a/test/Sentry.Tests/HubTests.cs b/test/Sentry.Tests/HubTests.cs index f1999225ae..8945b04b51 100644 --- a/test/Sentry.Tests/HubTests.cs +++ b/test/Sentry.Tests/HubTests.cs @@ -328,7 +328,7 @@ async Task VerifyAsync(HttpRequestMessage message) FileSystem = fileSystem, // So we don't need to deal with gzip'ed payload RequestBodyCompressionLevel = CompressionLevel.NoCompression, - CreateHttpClientHandler = () => new CallbackHttpClientHandler(VerifyAsync), + CreateHttpMessageHandler = () => new CallbackHttpClientHandler(VerifyAsync), // Not to send some session envelope AutoSessionTracking = false, Debug = true, diff --git a/test/Sentry.Tests/Internals/DefaultSentryHttpClientFactoryTests.cs b/test/Sentry.Tests/Internals/DefaultSentryHttpClientFactoryTests.cs index 1cf5b87c63..8e54d3b73a 100644 --- a/test/Sentry.Tests/Internals/DefaultSentryHttpClientFactoryTests.cs +++ b/test/Sentry.Tests/Internals/DefaultSentryHttpClientFactoryTests.cs @@ -98,7 +98,7 @@ public void Create_DefaultHeaders_AcceptJson() public void Create_ProvidedCreateHttpClientHandler_ReturnedHandlerUsed() { var handler = Substitute.For(); - _fixture.HttpOptions.CreateHttpClientHandler = () => handler; + _fixture.HttpOptions.CreateHttpMessageHandler = () => handler; var sut = Fixture.GetSut(); var client = sut.Create(_fixture.HttpOptions); @@ -109,7 +109,7 @@ public void Create_ProvidedCreateHttpClientHandler_ReturnedHandlerUsed() [Fact] public void Create_NullCreateHttpClientHandler_HttpClientHandlerUsed() { - _fixture.HttpOptions.CreateHttpClientHandler = null; + _fixture.HttpOptions.CreateHttpMessageHandler = null; var sut = Fixture.GetSut(); var client = sut.Create(_fixture.HttpOptions); @@ -120,7 +120,7 @@ public void Create_NullCreateHttpClientHandler_HttpClientHandlerUsed() [Fact] public void Create_NullReturnedCreateHttpClientHandler_HttpClientHandlerUsed() { - _fixture.HttpOptions.CreateHttpClientHandler = () => null; + _fixture.HttpOptions.CreateHttpMessageHandler = () => null; var sut = Fixture.GetSut(); var client = sut.Create(_fixture.HttpOptions); diff --git a/test/Sentry.Tests/SentryClientTests.cs b/test/Sentry.Tests/SentryClientTests.cs index fa9bd8aedc..abc5bb3123 100644 --- a/test/Sentry.Tests/SentryClientTests.cs +++ b/test/Sentry.Tests/SentryClientTests.cs @@ -1063,7 +1063,7 @@ public void Ctor_CreateHttpClientHandler_InvokedConfigureHandler() var invoked = false; _fixture.BackgroundWorker = null; _fixture.SentryOptions.Dsn = ValidDsn; - _fixture.SentryOptions.CreateHttpClientHandler = () => + _fixture.SentryOptions.CreateHttpMessageHandler = () => { invoked = true; return Substitute.For(); From 0714fd20df8991dfc47ab066a63dbfd495dc009b Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Wed, 26 Jul 2023 09:12:16 +1200 Subject: [PATCH 125/142] Align SDK with docs regarding session update for dropped events (#2496) --- CHANGELOG.md | 3 +- src/Sentry/Internal/Hub.cs | 23 +----- src/Sentry/SentryClient.cs | 54 ++++++++----- test/Sentry.Tests/HubTests.cs | 29 ++----- test/Sentry.Tests/HubTests.verify.cs | 5 +- test/Sentry.Tests/SentryClientTests.cs | 103 ++++++++++++++++++++++++- 6 files changed, 152 insertions(+), 65 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e4fb6d0b56..8f81a05212 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,8 +11,9 @@ - Fixed baggage propagation when an exception is thrown from middleware ([#2487](https://github.com/getsentry/sentry-dotnet/pull/2487)) - Fix Durable Functions preventing orchestrators from completing ([#2491](https://github.com/getsentry/sentry-dotnet/pull/2491)) - Re-enable HubTests.FlushOnDispose_SendsEnvelope ([#2492](https://github.com/getsentry/sentry-dotnet/pull/2492)) +- Fixed SDK not sending exceptions via Blazor WebAssembly due to a `PlatformNotSupportedException` ([#2506](https://github.com/getsentry/sentry-dotnet/pull/2506)) +- Align SDK with docs regarding session update for dropped events ([#2496](https://github.com/getsentry/sentry-dotnet/pull/2496)) - Introduced `HttpMessageHandler` in favor of the now deprecated `HttpClientHandler` on the options. This allows the SDK to support NSUrlSessionHandler on iOS ([#2503](https://github.com/getsentry/sentry-dotnet/pull/2503)) -- Fixed the SDK failing to report issues via Blazor WebAssembly due to a `PlatformNotSupportedException` ([#2506](https://github.com/getsentry/sentry-dotnet/pull/2506)) ### Dependencies diff --git a/src/Sentry/Internal/Hub.cs b/src/Sentry/Internal/Hub.cs index cce39ec678..68472df6e1 100644 --- a/src/Sentry/Internal/Hub.cs +++ b/src/Sentry/Internal/Hub.cs @@ -46,7 +46,7 @@ internal Hub( _options = options; _randomValuesFactory = randomValuesFactory ?? new SynchronizedRandomValuesFactory(); - _ownedClient = client ?? new SentryClient(options, _randomValuesFactory); + _ownedClient = client ?? new SentryClient(options, randomValuesFactory: _randomValuesFactory); _clock = clock ?? SystemClock.Clock; _sessionManager = sessionManager ?? new GlobalSessionManager(options); @@ -335,8 +335,8 @@ SentryId IHubEx.CaptureEventInternal(SentryEvent evt, Hint? hint, Scope? scope) { try { - var currentScope = ScopeManager.GetCurrent(); - var actualScope = scope ?? currentScope.Key; + ScopeManager.GetCurrent().Deconstruct(out var currentScope, out var sentryClient); + var actualScope = scope ?? currentScope; // Inject trace information from a linked span if (GetLinkedSpan(evt, actualScope) is { } linkedSpan) @@ -351,31 +351,16 @@ SentryId IHubEx.CaptureEventInternal(SentryEvent evt, Hint? hint, Scope? scope) } } - var hasTerminalException = evt.HasTerminalException(); - if (hasTerminalException) - { - // Event contains a terminal exception -> end session as crashed - _options.LogDebug("Ending session as Crashed, due to unhandled exception."); - actualScope.SessionUpdate = _sessionManager.EndSession(SessionEndStatus.Crashed); - } - else if (evt.HasException()) - { - // Event contains a non-terminal exception -> report error - // (this might return null if the session has already reported errors before) - actualScope.SessionUpdate = _sessionManager.ReportError(); - } - // Try to get the DSC from the transaction, if we didn't get it already from the linked span var transaction = actualScope.Transaction as TransactionTracer; evt.DynamicSamplingContext ??= transaction?.DynamicSamplingContext; // Now capture the event with the Sentry client on the current scope. - var sentryClient = currentScope.Value; var id = sentryClient.CaptureEvent(evt, hint, actualScope); actualScope.LastEventId = id; actualScope.SessionUpdate = null; - if (hasTerminalException) + if (evt.HasTerminalException()) { // Event contains a terminal exception -> finish any current transaction as aborted // Do this *after* the event was captured, so that the event is still linked to the transaction. diff --git a/src/Sentry/SentryClient.cs b/src/Sentry/SentryClient.cs index f48dac09f5..d0d52dfe7e 100644 --- a/src/Sentry/SentryClient.cs +++ b/src/Sentry/SentryClient.cs @@ -16,6 +16,7 @@ namespace Sentry; public class SentryClient : ISentryClient, IDisposable { private readonly SentryOptions _options; + private readonly ISessionManager _sessionManager; private readonly RandomValuesFactory _randomValuesFactory; internal IBackgroundWorker Worker { get; } @@ -32,22 +33,17 @@ public class SentryClient : ISentryClient, IDisposable /// /// The configuration for this client. public SentryClient(SentryOptions options) - : this(options, null, null) { } + : this(options, null, null, null) { } internal SentryClient( SentryOptions options, - RandomValuesFactory? randomValuesFactory) - : this(options, null, randomValuesFactory) - { - } - - internal SentryClient( - SentryOptions options, - IBackgroundWorker? worker, - RandomValuesFactory? randomValuesFactory = null) + IBackgroundWorker? worker = null, + RandomValuesFactory? randomValuesFactory = null, + ISessionManager? sessionManager = null) { _options = options ?? throw new ArgumentNullException(nameof(options)); _randomValuesFactory = randomValuesFactory ?? new SynchronizedRandomValuesFactory(); + _sessionManager = sessionManager ?? new GlobalSessionManager(options); options.SetupLogging(); // Only relevant if this client wasn't created as a result of calling Init @@ -211,16 +207,6 @@ public void CaptureSession(SessionUpdate sessionUpdate) // TODO: this method needs to be refactored, it's really hard to analyze nullability private SentryId DoSendEvent(SentryEvent @event, Hint? hint, Scope? scope) { - if (_options.SampleRate != null) - { - if (!_randomValuesFactory.NextBool(_options.SampleRate.Value)) - { - _options.ClientReportRecorder.RecordDiscardedEvent(DiscardReason.SampleRate, DataCategory.Error); - _options.LogDebug("Event sampled."); - return SentryId.Empty; - } - } - var filteredExceptions = ApplyExceptionFilters(@event.Exception); if (filteredExceptions?.Count > 0) { @@ -281,6 +267,34 @@ private SentryId DoSendEvent(SentryEvent @event, Hint? hint, Scope? scope) return SentryId.Empty; } + var hasTerminalException = processedEvent.HasTerminalException(); + if (hasTerminalException) + { + // Event contains a terminal exception -> end session as crashed + _options.LogDebug("Ending session as Crashed, due to unhandled exception."); + scope.SessionUpdate = _sessionManager.EndSession(SessionEndStatus.Crashed); + } + else if (processedEvent.HasException()) + { + // Event contains a non-terminal exception -> report error + // (this might return null if the session has already reported errors before) + scope.SessionUpdate = _sessionManager.ReportError(); + } + + if (_options.SampleRate != null) + { + if (!_randomValuesFactory.NextBool(_options.SampleRate.Value)) + { + _options.ClientReportRecorder.RecordDiscardedEvent(DiscardReason.SampleRate, DataCategory.Error); + _options.LogDebug("Event sampled."); + return SentryId.Empty; + } + } + else + { + _options.LogDebug("Event not sampled."); + } + if (!_options.SendDefaultPii) { processedEvent.Redact(); diff --git a/test/Sentry.Tests/HubTests.cs b/test/Sentry.Tests/HubTests.cs index 8945b04b51..56aef686a3 100644 --- a/test/Sentry.Tests/HubTests.cs +++ b/test/Sentry.Tests/HubTests.cs @@ -10,7 +10,7 @@ private class Fixture { public SentryOptions Options { get; } - public ISentryClient Client { get; } + public ISentryClient Client { get; set; } public ISessionManager SessionManager { get; set; } @@ -386,23 +386,6 @@ async Task VerifyAsync(HttpRequestMessage message) } #endif - [Fact] - public void CaptureEvent_SessionActive_ExceptionReportsError() - { - // Arrange - _fixture.Options.Release = "release"; - var hub = _fixture.GetSut(); - - hub.StartSession(); - - // Act - hub.CaptureEvent(new SentryEvent(new Exception())); - hub.EndSession(); - - // Assert - _fixture.Client.Received().CaptureSession(Arg.Is(s => s.ErrorCount == 1)); - } - [Fact] public void CaptureEvent_ActiveSession_UnhandledExceptionSessionEndedAsCrashed() { @@ -414,8 +397,9 @@ public void CaptureEvent_ActiveSession_UnhandledExceptionSessionEndedAsCrashed() Dsn = ValidDsn, Release = "release" }; - var client = new SentryClient(options, worker); - var hub = new Hub(options, client); + var sessionManager = new GlobalSessionManager(options); + var client = new SentryClient(options, worker, sessionManager: sessionManager); + var hub = new Hub(options, client, sessionManager); hub.StartSession(); @@ -477,8 +461,9 @@ public void AppDomainUnhandledExceptionIntegration_ActiveSession_UnhandledExcept Dsn = ValidDsn, Release = "release" }; - var client = new SentryClient(options, worker); - var hub = new Hub(options, client); + var sessionManager = new GlobalSessionManager(options); + var client = new SentryClient(options, worker, sessionManager: sessionManager); + var hub = new Hub(options, client, sessionManager); var integration = new AppDomainUnhandledExceptionIntegration(Substitute.For()); integration.Register(hub, options); diff --git a/test/Sentry.Tests/HubTests.verify.cs b/test/Sentry.Tests/HubTests.verify.cs index 9719f12521..34c782523c 100644 --- a/test/Sentry.Tests/HubTests.verify.cs +++ b/test/Sentry.Tests/HubTests.verify.cs @@ -15,8 +15,9 @@ public async Task CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEn Release = "release", TracesSampleRate = 1.0 }; - var client = new SentryClient(options, worker); - var hub = new Hub(options, client); + var sessionManager = new GlobalSessionManager(options); + var client = new SentryClient(options, worker, sessionManager: sessionManager); + var hub = new Hub(options, client, sessionManager); var transaction = hub.StartTransaction("my transaction", "my operation"); hub.ConfigureScope(scope => scope.Transaction = transaction); diff --git a/test/Sentry.Tests/SentryClientTests.cs b/test/Sentry.Tests/SentryClientTests.cs index abc5bb3123..eedec0fb0a 100644 --- a/test/Sentry.Tests/SentryClientTests.cs +++ b/test/Sentry.Tests/SentryClientTests.cs @@ -18,6 +18,7 @@ private class Fixture public IBackgroundWorker BackgroundWorker { get; set; } = Substitute.For(); public IClientReportRecorder ClientReportRecorder { get; } = Substitute.For(); + public ISessionManager SessionManager { get; } = Substitute.For(); public Fixture() { @@ -28,7 +29,7 @@ public Fixture() public SentryClient GetSut() { var randomValuesFactory = new IsolatedRandomValuesFactory(); - return new SentryClient(SentryOptions, BackgroundWorker, randomValuesFactory); + return new SentryClient(SentryOptions, BackgroundWorker, randomValuesFactory, SessionManager); } } @@ -612,6 +613,68 @@ public void CaptureEvent_WithSampleRate_AppropriateDistribution(float sampleRate }); } + [Fact] + public void CaptureEvent_Processing_Order() + { + // Arrange + var @event = new SentryEvent(new Exception()); + var processingOrder = new List(); + + var exceptionFilter = Substitute.For(); + exceptionFilter.Filter(Arg.Do(_ => + processingOrder.Add("exceptionFilter") + )).Returns(false); + _fixture.SentryOptions.ExceptionFilters.Add(exceptionFilter); + + var exceptionProcessor = Substitute.For(); + exceptionProcessor + .When(x => x.Process(Arg.Any(), Arg.Any())) + .Do(_ => processingOrder.Add("exceptionProcessor")); + var scope = new Scope(_fixture.SentryOptions); + scope.ExceptionProcessors.Add(exceptionProcessor); + + var eventProcessor = Substitute.For(); + eventProcessor.Process(default).ReturnsForAnyArgs(_ => + { + processingOrder.Add("eventProcessor"); + return @event; + }); + _fixture.SentryOptions.EventProcessors.Add((eventProcessor)); + + _fixture.SentryOptions.SetBeforeSend((e, _) => + { + processingOrder.Add("SetBeforeSend"); + return e; + }); + + _fixture.SessionManager.When(x => x.ReportError()) + .Do(_ => processingOrder.Add("UpdateSession")); + + var logger = Substitute.For(); + logger.IsEnabled(Arg.Any()).Returns(true); + logger.When(x => x.Log(Arg.Any(), Arg.Is("Event not sampled."))) + .Do(_ => processingOrder.Add("SampleRate")); + _fixture.SentryOptions.DiagnosticLogger = logger; + _fixture.SentryOptions.Debug = true; + + // Act + var client = _fixture.GetSut(); + client.CaptureEvent(@event, scope); + + // Assert + // See https://github.com/getsentry/sentry-dotnet/issues/1599 + var expectedOrder = new List() + { + "exceptionFilter", + "exceptionProcessor", + "eventProcessor", + "SetBeforeSend", + "UpdateSession", + "SampleRate" + }; + processingOrder.Should().Equal(expectedOrder); + } + [Fact] public void CaptureEvent_Release_SetFromOptions() { @@ -1121,4 +1184,42 @@ public void Ctor_WrapsCustomTransportWhenCachePathOnOptions() var cachingTransport = Assert.IsType(_fixture.SentryOptions.Transport); _ = Assert.IsType(cachingTransport.InnerTransport); } + + [Fact] + public void CaptureEvent_Exception_ReportsError() + { + // Arrange + var hub = _fixture.GetSut(); + + // Act + hub.CaptureEvent(new SentryEvent(new Exception())); + + // Assert + _fixture.SessionManager.Received(1).ReportError(); + } + + [Fact] + public void CaptureEvent_ActiveSession_UnhandledExceptionSessionEndedAsCrashed() + { + // Arrange + var client = _fixture.GetSut(); + + // Act + client.CaptureEvent(new SentryEvent() + { + SentryExceptions = new[] + { + new SentryException + { + Mechanism = new() + { + Handled = false + } + } + } + }); + + // Assert + _fixture.SessionManager.Received().EndSession(SessionEndStatus.Crashed); + } } From 30e93cbd848f66c0f4d089c45bdbb95d58f0f1c2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 27 Jul 2023 10:37:54 +0200 Subject: [PATCH 126/142] chore: update scripts/update-cli.ps1 to 2.20.1 (#2518) --- 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 8f81a05212..eccfcab46e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,9 +20,9 @@ - Bump Java SDK from v6.25.1 to v6.25.2 ([#2484](https://github.com/getsentry/sentry-dotnet/pull/2484)) - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#6252) - [diff](https://github.com/getsentry/sentry-java/compare/6.25.1...6.25.2) -- Bump CLI from v2.19.4 to v2.20.0 ([#2509](https://github.com/getsentry/sentry-dotnet/pull/2509)) - - [changelog](https://github.com/getsentry/sentry-cli/blob/master/CHANGELOG.md#2200) - - [diff](https://github.com/getsentry/sentry-cli/compare/2.19.4...2.20.0) +- Bump CLI from v2.19.4 to v2.20.1 ([#2509](https://github.com/getsentry/sentry-dotnet/pull/2509), [#2518](https://github.com/getsentry/sentry-dotnet/pull/2518)) + - [changelog](https://github.com/getsentry/sentry-cli/blob/master/CHANGELOG.md#2201) + - [diff](https://github.com/getsentry/sentry-cli/compare/2.19.4...2.20.1) - Bump Cocoa SDK from v8.8.0 to v8.9.3 ([#2479](https://github.com/getsentry/sentry-dotnet/pull/2479), [#2483](https://github.com/getsentry/sentry-dotnet/pull/2483), [#2500](https://github.com/getsentry/sentry-dotnet/pull/2500), [#2510](https://github.com/getsentry/sentry-dotnet/pull/2510)) - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#893) - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.8.0...8.9.3) diff --git a/Directory.Build.props b/Directory.Build.props index 183fa43376..33d7b04b5e 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -100,7 +100,7 @@ - 2.20.0 + 2.20.1 $(MSBuildThisFileDirectory)tools\sentry-cli\$(SentryCLIVersion)\ diff --git a/src/Sentry/Sentry.csproj b/src/Sentry/Sentry.csproj index de4cc752d6..b9d3b33835 100644 --- a/src/Sentry/Sentry.csproj +++ b/src/Sentry/Sentry.csproj @@ -96,25 +96,25 @@ + Include="sentry-cli-Darwin-arm64" FileHash="7ad93a9680d80597030f3fb7b5bd821025d9090cb09482c062f32ceb36abb62c" /> + Include="sentry-cli-Darwin-x86_64" FileHash="056f21ef424c05ea2ce93e10ee6b22e94d2d798aef8fc958a31c7753b8f03e95" /> + Include="sentry-cli-Linux-aarch64" FileHash="5b64a6df219c065cdb5c0502f03562461b6d01d3dcabc8f4994f68aab05710a4" /> + Include="sentry-cli-Linux-i686" FileHash="9e55f8e9c78971c93f0ff13301c3961108b7e07c9730b0eb6dac11da5b45c2c7" /> + Include="sentry-cli-Linux-x86_64" FileHash="3603956ecdfe03fa8914871fd3895e18cee2261fdef4f5070ab052d0ca094c3c" /> + Include="sentry-cli-Windows-i686.exe" FileHash="661b8155dc5c9ae6df78a1f8303ad7bcb0967b94ab8d17756a4e43b237dd21c7" /> + Include="sentry-cli-Windows-x86_64.exe" FileHash="03d06dac7664f634519144b5b2692d43c0530971a84408152561db8e2159b54d" /> From d4392e2e368d63fc47f9bb4f2953eb329ea5944d Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Fri, 28 Jul 2023 09:46:59 +1200 Subject: [PATCH 127/142] Using `Activity.RecordException` now correctly updates the error status of OpenTelemetry Spans (#2515) --- CHANGELOG.md | 1 + .../OpenTelemetry/ATTRIBUTION.txt | 18 +++ .../OpenTelemetry/SemanticConventions.cs | 115 ++++++++++++++++++ .../OpenTelemetry/SpanAttributeConstants.cs | 31 +++++ .../OpenTelemetry/StatusTags.cs | 10 ++ .../SentrySpanProcessor.cs | 112 ++++++++++------- .../SentrySpanProcessorTests.cs | 4 + 7 files changed, 250 insertions(+), 41 deletions(-) create mode 100644 src/Sentry.OpenTelemetry/OpenTelemetry/ATTRIBUTION.txt create mode 100644 src/Sentry.OpenTelemetry/OpenTelemetry/SemanticConventions.cs create mode 100644 src/Sentry.OpenTelemetry/OpenTelemetry/SpanAttributeConstants.cs create mode 100644 src/Sentry.OpenTelemetry/OpenTelemetry/StatusTags.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index eccfcab46e..bd5fec7af1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ - Fixed SDK not sending exceptions via Blazor WebAssembly due to a `PlatformNotSupportedException` ([#2506](https://github.com/getsentry/sentry-dotnet/pull/2506)) - Align SDK with docs regarding session update for dropped events ([#2496](https://github.com/getsentry/sentry-dotnet/pull/2496)) - Introduced `HttpMessageHandler` in favor of the now deprecated `HttpClientHandler` on the options. This allows the SDK to support NSUrlSessionHandler on iOS ([#2503](https://github.com/getsentry/sentry-dotnet/pull/2503)) +- Using `Activity.RecordException` now correctly updates the error status of OpenTelemetry Spans ([#2515](https://github.com/getsentry/sentry-dotnet/pull/2515)) ### Dependencies diff --git a/src/Sentry.OpenTelemetry/OpenTelemetry/ATTRIBUTION.txt b/src/Sentry.OpenTelemetry/OpenTelemetry/ATTRIBUTION.txt new file mode 100644 index 0000000000..01214feb1b --- /dev/null +++ b/src/Sentry.OpenTelemetry/OpenTelemetry/ATTRIBUTION.txt @@ -0,0 +1,18 @@ +Parts of the code in this subdirectory have been adapted from +https://github.com/open-telemetry/opentelemetry-dotnet/ + +The original license is as follows: + +Copyright The OpenTelemetry Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/src/Sentry.OpenTelemetry/OpenTelemetry/SemanticConventions.cs b/src/Sentry.OpenTelemetry/OpenTelemetry/SemanticConventions.cs new file mode 100644 index 0000000000..9248dd93d6 --- /dev/null +++ b/src/Sentry.OpenTelemetry/OpenTelemetry/SemanticConventions.cs @@ -0,0 +1,115 @@ +// Modified from: +// https://github.com/open-telemetry/opentelemetry-dotnet/blob/dacc532d51ca0f3775160b84fa6d7d9403a8ccde/src/Shared/SemanticConventions.cs + +// +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +// ReSharper disable once CheckNamespace +namespace Sentry.OpenTelemetry; + +/// +/// Constants for semantic attribute names outlined by the OpenTelemetry specifications. +/// and +/// . +/// +internal static class SemanticConventions +{ + // The set of constants matches the specification as of this commit. + // https://github.com/open-telemetry/opentelemetry-specification/tree/main/specification/trace/semantic_conventions + // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/exceptions.md + public const string AttributeNetTransport = "net.transport"; + public const string AttributeNetPeerIp = "net.peer.ip"; + public const string AttributeNetPeerPort = "net.peer.port"; + public const string AttributeNetPeerName = "net.peer.name"; + public const string AttributeNetHostIp = "net.host.ip"; + public const string AttributeNetHostPort = "net.host.port"; + public const string AttributeNetHostName = "net.host.name"; + + public const string AttributeEnduserId = "enduser.id"; + public const string AttributeEnduserRole = "enduser.role"; + public const string AttributeEnduserScope = "enduser.scope"; + + public const string AttributePeerService = "peer.service"; + + public const string AttributeHttpMethod = "http.method"; + public const string AttributeHttpUrl = "http.url"; + public const string AttributeHttpTarget = "http.target"; + public const string AttributeHttpHost = "http.host"; + public const string AttributeHttpScheme = "http.scheme"; + public const string AttributeHttpStatusCode = "http.status_code"; + public const string AttributeHttpStatusText = "http.status_text"; + public const string AttributeHttpFlavor = "http.flavor"; + public const string AttributeHttpServerName = "http.server_name"; + public const string AttributeHttpRoute = "http.route"; + public const string AttributeHttpClientIP = "http.client_ip"; + public const string AttributeHttpUserAgent = "http.user_agent"; + public const string AttributeHttpRequestContentLength = "http.request_content_length"; + public const string AttributeHttpRequestContentLengthUncompressed = "http.request_content_length_uncompressed"; + public const string AttributeHttpResponseContentLength = "http.response_content_length"; + public const string AttributeHttpResponseContentLengthUncompressed = "http.response_content_length_uncompressed"; + + public const string AttributeDbSystem = "db.system"; + public const string AttributeDbConnectionString = "db.connection_string"; + public const string AttributeDbUser = "db.user"; + public const string AttributeDbMsSqlInstanceName = "db.mssql.instance_name"; + public const string AttributeDbJdbcDriverClassName = "db.jdbc.driver_classname"; + public const string AttributeDbName = "db.name"; + public const string AttributeDbStatement = "db.statement"; + public const string AttributeDbOperation = "db.operation"; + public const string AttributeDbInstance = "db.instance"; + public const string AttributeDbUrl = "db.url"; + public const string AttributeDbCassandraKeyspace = "db.cassandra.keyspace"; + public const string AttributeDbHBaseNamespace = "db.hbase.namespace"; + public const string AttributeDbRedisDatabaseIndex = "db.redis.database_index"; + public const string AttributeDbMongoDbCollection = "db.mongodb.collection"; + + public const string AttributeRpcSystem = "rpc.system"; + public const string AttributeRpcService = "rpc.service"; + public const string AttributeRpcMethod = "rpc.method"; + public const string AttributeRpcGrpcStatusCode = "rpc.grpc.status_code"; + + public const string AttributeMessageType = "message.type"; + public const string AttributeMessageId = "message.id"; + public const string AttributeMessageCompressedSize = "message.compressed_size"; + public const string AttributeMessageUncompressedSize = "message.uncompressed_size"; + + public const string AttributeFaasTrigger = "faas.trigger"; + public const string AttributeFaasExecution = "faas.execution"; + public const string AttributeFaasDocumentCollection = "faas.document.collection"; + public const string AttributeFaasDocumentOperation = "faas.document.operation"; + public const string AttributeFaasDocumentTime = "faas.document.time"; + public const string AttributeFaasDocumentName = "faas.document.name"; + public const string AttributeFaasTime = "faas.time"; + public const string AttributeFaasCron = "faas.cron"; + + public const string AttributeMessagingSystem = "messaging.system"; + public const string AttributeMessagingDestination = "messaging.destination"; + public const string AttributeMessagingDestinationKind = "messaging.destination_kind"; + public const string AttributeMessagingTempDestination = "messaging.temp_destination"; + public const string AttributeMessagingProtocol = "messaging.protocol"; + public const string AttributeMessagingProtocolVersion = "messaging.protocol_version"; + public const string AttributeMessagingUrl = "messaging.url"; + public const string AttributeMessagingMessageId = "messaging.message_id"; + public const string AttributeMessagingConversationId = "messaging.conversation_id"; + public const string AttributeMessagingPayloadSize = "messaging.message_payload_size_bytes"; + public const string AttributeMessagingPayloadCompressedSize = "messaging.message_payload_compressed_size_bytes"; + public const string AttributeMessagingOperation = "messaging.operation"; + + public const string AttributeExceptionEventName = "exception"; + public const string AttributeExceptionType = "exception.type"; + public const string AttributeExceptionMessage = "exception.message"; + public const string AttributeExceptionStacktrace = "exception.stacktrace"; +} diff --git a/src/Sentry.OpenTelemetry/OpenTelemetry/SpanAttributeConstants.cs b/src/Sentry.OpenTelemetry/OpenTelemetry/SpanAttributeConstants.cs new file mode 100644 index 0000000000..4b9b99ce8e --- /dev/null +++ b/src/Sentry.OpenTelemetry/OpenTelemetry/SpanAttributeConstants.cs @@ -0,0 +1,31 @@ +// Modified from: +// https://github.com/open-telemetry/opentelemetry-dotnet/blob/dacc532d51ca0f3775160b84fa6d7d9403a8ccde/src/Shared/SpanAttributeConstants.cs + +// +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +// ReSharper disable once CheckNamespace +namespace Sentry.OpenTelemetry; + +/// +/// Defines well-known span attribute keys. +/// +internal static class SpanAttributeConstants +{ + public const string StatusCodeKey = "otel.status_code"; + public const string StatusDescriptionKey = "otel.status_description"; + public const string DatabaseStatementTypeKey = "db.statement_type"; +} diff --git a/src/Sentry.OpenTelemetry/OpenTelemetry/StatusTags.cs b/src/Sentry.OpenTelemetry/OpenTelemetry/StatusTags.cs new file mode 100644 index 0000000000..c69984b47f --- /dev/null +++ b/src/Sentry.OpenTelemetry/OpenTelemetry/StatusTags.cs @@ -0,0 +1,10 @@ +// ReSharper disable once CheckNamespace +namespace Sentry.OpenTelemetry; + +internal static class StatusTags +{ + // See https://github.com/open-telemetry/opentelemetry-dotnet/blob/dacc532d51ca0f3775160b84fa6d7d9403a8ccde/src/Shared/StatusHelper.cs#L26 + public const string UnsetStatusCodeTagValue = "UNSET"; + public const string OkStatusCodeTagValue = "OK"; + public const string ErrorStatusCodeTagValue = "ERROR"; +} diff --git a/src/Sentry.OpenTelemetry/SentrySpanProcessor.cs b/src/Sentry.OpenTelemetry/SentrySpanProcessor.cs index 9a225da4a7..11745c116b 100644 --- a/src/Sentry.OpenTelemetry/SentrySpanProcessor.cs +++ b/src/Sentry.OpenTelemetry/SentrySpanProcessor.cs @@ -1,4 +1,5 @@ using OpenTelemetry; +using OpenTelemetry.Trace; using Sentry.Extensibility; using Sentry.Internal.Extensions; @@ -169,14 +170,22 @@ public override void OnEnd(Activity data) _map.TryRemove(data.SpanId, out _); } - internal static SpanStatus GetSpanStatus(ActivityStatusCode status, IDictionary attributes) => - status switch + internal static SpanStatus GetSpanStatus(ActivityStatusCode status, IDictionary attributes) + { + // See https://github.com/open-telemetry/opentelemetry-dotnet/discussions/4703 + if (attributes.TryGetValue(SpanAttributeConstants.StatusCodeKey, out var statusCode) + && statusCode is StatusTags.ErrorStatusCodeTagValue + ) { + return GetErrorSpanStatus(attributes); + } + return status switch { ActivityStatusCode.Unset => SpanStatus.Ok, ActivityStatusCode.Ok => SpanStatus.Ok, ActivityStatusCode.Error => GetErrorSpanStatus(attributes), _ => SpanStatus.UnknownError }; + } private static SpanStatus GetErrorSpanStatus(IDictionary attributes) { @@ -203,7 +212,7 @@ private static (string operation, string description, TransactionNameSource sour // HTTP span // https://opentelemetry.io/docs/specs/otel/trace/semantic_conventions/http/ - if (attributes.TryGetTypedValue("http.method", out string httpMethod)) + if (attributes.TryGetTypedValue(SemanticConventions.AttributeHttpMethod, out string httpMethod)) { if (activity.Kind == ActivityKind.Client) { @@ -211,13 +220,13 @@ private static (string operation, string description, TransactionNameSource sour return ("http.client", httpMethod, TransactionNameSource.Custom); } - if (attributes.TryGetTypedValue("http.route", out string httpRoute)) + if (attributes.TryGetTypedValue(SemanticConventions.AttributeHttpRoute, out string httpRoute)) { // A route exists. Use the method and route. return ("http.server", $"{httpMethod} {httpRoute}", TransactionNameSource.Route); } - if (attributes.TryGetTypedValue("http.target", out string httpTarget)) + if (attributes.TryGetTypedValue(SemanticConventions.AttributeHttpTarget, out string httpTarget)) { // A target exists. Use the method and target. If the target is "/" we can treat it like a route. var source = httpTarget == "/" ? TransactionNameSource.Route : TransactionNameSource.Url; @@ -230,9 +239,9 @@ private static (string operation, string description, TransactionNameSource sour // DB span // https://opentelemetry.io/docs/specs/otel/trace/semantic_conventions/database/ - if (attributes.ContainsKey("db.system")) + if (attributes.ContainsKey(SemanticConventions.AttributeDbSystem)) { - if (attributes.TryGetTypedValue("db.statement", out string dbStatement)) + if (attributes.TryGetTypedValue(SemanticConventions.AttributeDbStatement, out string dbStatement)) { // We have a database statement. Use it. return ("db", dbStatement, TransactionNameSource.Task); @@ -244,21 +253,21 @@ private static (string operation, string description, TransactionNameSource sour // RPC span // https://opentelemetry.io/docs/specs/otel/trace/semantic_conventions/rpc/ - if (attributes.ContainsKey("rpc.service")) + if (attributes.ContainsKey(SemanticConventions.AttributeRpcService)) { return ("rpc", activity.DisplayName, TransactionNameSource.Route); } // Messaging span // https://opentelemetry.io/docs/specs/otel/trace/semantic_conventions/messaging/ - if (attributes.ContainsKey("messaging.system")) + if (attributes.ContainsKey(SemanticConventions.AttributeMessagingSystem)) { return ("message", activity.DisplayName, TransactionNameSource.Route); } // FaaS (Functions/Lambda) span // https://opentelemetry.io/docs/specs/otel/trace/semantic_conventions/faas/ - if (attributes.TryGetTypedValue("faas.trigger", out string faasTrigger)) + if (attributes.TryGetTypedValue(SemanticConventions.AttributeFaasTrigger, out string faasTrigger)) { return (faasTrigger, activity.DisplayName, TransactionNameSource.Route); } @@ -286,36 +295,57 @@ private static (string operation, string description, TransactionNameSource sour private void GenerateSentryErrorsFromOtelSpan(Activity activity, IDictionary spanAttributes) { - // // https://develop.sentry.dev/sdk/performance/opentelemetry/#step-7-define-generatesentryerrorsfromotelspan - // // https://opentelemetry.io/docs/specs/otel/trace/semantic_conventions/exceptions/ - // - // foreach (var @event in activity.Events.Where(e => e.Name == "exception")) - // { - // // Note, this doesn't do anything yet because `exception` is not a valid attribute. - // // We cannot just use `exception.type`, `exception.message`, and `exception.stacktrace`. - // // See https://github.com/open-telemetry/opentelemetry-dotnet/issues/2439#issuecomment-1577314568 - // - // var eventAttributes = @event.Tags.ToDictionary(); - // if (!eventAttributes.TryGetTypedValue("exception", out Exception exception)) - // { - // continue; - // } - // - // // TODO: Validate that our `DuplicateEventDetectionEventProcessor` prevents this from doubling exceptions - // // that are also caught by other means, such as our AspNetCore middleware, etc. - // // (When options.RecordException = true is set on AddAspNetCoreInstrumentation...) - // // Also, in such cases - how will we get the otel scope and trace context on the other one? - // - // var sentryEvent = new SentryEvent(exception, @event.Timestamp); - // _hub.CaptureEvent(sentryEvent, scope => - // { - // scope.Contexts["otel"] = GetOtelContext(spanAttributes); - // - // var trace = scope.Contexts.Trace; - // trace.TraceId = activity.TraceId.AsSentryId(); - // trace.SpanId = activity.SpanId.AsSentrySpanId(); - // trace.ParentSpanId = activity.ParentSpanId.AsSentrySpanId(); - // }); - // } + // https://develop.sentry.dev/sdk/performance/opentelemetry/#step-7-define-generatesentryerrorsfromotelspan + // https://opentelemetry.io/docs/specs/otel/trace/semantic_conventions/exceptions/ + foreach (var @event in activity.Events.Where(e => e.Name == SemanticConventions.AttributeExceptionEventName)) + { + var eventAttributes = @event.Tags.ToDictionary(); + // This would be where we would ideally implement full exception capture. That's not possible at the + // moment since the full exception isn't yet available via the OpenTelemetry API. + // See https://github.com/open-telemetry/opentelemetry-dotnet/issues/2439#issuecomment-1577314568 + // if (!eventAttributes.TryGetTypedValue("exception", out Exception exception)) + // { + // continue; + // } + + // At the moment, OTEL only gives us `exception.type`, `exception.message`, and `exception.stacktrace`... + // So the best we can do is a poor man's exception (no accurate symbolication or anything) + if (!eventAttributes.TryGetTypedValue(SemanticConventions.AttributeExceptionType, out string exceptionType)) + { + continue; + } + eventAttributes.TryGetTypedValue(SemanticConventions.AttributeExceptionMessage, out string message); + eventAttributes.TryGetTypedValue(SemanticConventions.AttributeExceptionStacktrace, out string stackTrace); + + Exception exception; + try + { + var type = Type.GetType(exceptionType)!; + exception = (Exception)Activator.CreateInstance(type, message)!; + exception.SetSentryMechanism("SentrySpanProcessor.ErrorSpan"); + } + catch + { + _options?.DiagnosticLogger?.LogError($"Failed to create poor man's exception for type : {exceptionType}"); + continue; + } + + // TODO: Validate that our `DuplicateEventDetectionEventProcessor` prevents this from doubling exceptions + // that are also caught by other means, such as our AspNetCore middleware, etc. + // (When options.RecordException = true is set on AddAspNetCoreInstrumentation...) + // Also, in such cases - how will we get the otel scope and trace context on the other one? + + var sentryEvent = new SentryEvent(exception, @event.Timestamp); + var otelContext = GetOtelContext(spanAttributes); + otelContext.Add("stack_trace", stackTrace); + sentryEvent.Contexts["otel"] = otelContext; + _hub.CaptureEvent(sentryEvent, scope => + { + var trace = scope.Contexts.Trace; + trace.SpanId = activity.SpanId.AsSentrySpanId(); + trace.ParentSpanId = activity.ParentSpanId.AsSentrySpanId(); + trace.TraceId = activity.TraceId.AsSentryId(); + }); + } } } diff --git a/test/Sentry.OpenTelemetry.Tests/SentrySpanProcessorTests.cs b/test/Sentry.OpenTelemetry.Tests/SentrySpanProcessorTests.cs index 50af048c29..fbf20899e6 100644 --- a/test/Sentry.OpenTelemetry.Tests/SentrySpanProcessorTests.cs +++ b/test/Sentry.OpenTelemetry.Tests/SentrySpanProcessorTests.cs @@ -95,6 +95,10 @@ public void GetSpanStatus() var grpcAttributes = new Dictionary { ["rpc.grpc.status_code"] = 7 }; SentrySpanProcessor.GetSpanStatus(ActivityStatusCode.Error, grpcAttributes) .Should().Be(SpanStatus.PermissionDenied); + + var errorAttributes = new Dictionary { [SpanAttributeConstants.StatusCodeKey] = StatusTags.ErrorStatusCodeTagValue }; + SentrySpanProcessor.GetSpanStatus(ActivityStatusCode.Ok, errorAttributes).Should().Be(SpanStatus.UnknownError); + SentrySpanProcessor.GetSpanStatus(ActivityStatusCode.Unset, errorAttributes).Should().Be(SpanStatus.UnknownError); } } From 0f58599de2842296bfedad27f354090d15179f53 Mon Sep 17 00:00:00 2001 From: IhateTrains Date: Mon, 31 Jul 2023 03:36:54 +0200 Subject: [PATCH 128/142] Add MinimumEventLevel to Sentry.Log4Net and convert events below it to breadcrumbs (#2505) --- CHANGELOG.md | 1 + samples/Sentry.Samples.Log4Net/Program.cs | 4 + samples/Sentry.Samples.Log4Net/app.config | 3 + src/Sentry.Log4Net/LevelMapping.cs | 14 +++ src/Sentry.Log4Net/SentryAppender.cs | 20 +++++ .../ApiApprovalTests.Run.Core3_1.verified.txt | 3 +- ...piApprovalTests.Run.DotNet6_0.verified.txt | 1 + ...piApprovalTests.Run.DotNet7_0.verified.txt | 1 + .../ApiApprovalTests.Run.Net4_8.verified.txt | 1 + .../SentryAppenderTests.cs | 88 +++++++++++++++++++ 10 files changed, 135 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bd5fec7af1..ae924e4b1e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Features - Added Sampling Decision to Trace Envelope Header ([#2495](https://github.com/getsentry/sentry-dotnet/pull/2495)) +- Add MinimumEventLevel to Sentry.Log4Net and convert events below it to breadcrumbs ([#2505](https://github.com/getsentry/sentry-dotnet/pull/2505)) ### Fixes diff --git a/samples/Sentry.Samples.Log4Net/Program.cs b/samples/Sentry.Samples.Log4Net/Program.cs index 20147a2454..c0f33f246f 100644 --- a/samples/Sentry.Samples.Log4Net/Program.cs +++ b/samples/Sentry.Samples.Log4Net/Program.cs @@ -26,6 +26,10 @@ private static void Main() // Does not result in an event in Sentry Log.Debug("Debug message which is not sent."); + // app.config sets MinimumEventLevel to ERROR, so every below that level is added as a breadcrumb. + Log.Info("Info message which is added as a breadcrumb."); + Log.Error("Error message which is sent as an event."); + try { DoWork(); diff --git a/samples/Sentry.Samples.Log4Net/app.config b/samples/Sentry.Samples.Log4Net/app.config index 96a7c59db2..70bc16f3c4 100644 --- a/samples/Sentry.Samples.Log4Net/app.config +++ b/samples/Sentry.Samples.Log4Net/app.config @@ -18,6 +18,9 @@ + + diff --git a/src/Sentry.Log4Net/LevelMapping.cs b/src/Sentry.Log4Net/LevelMapping.cs index d0cc3143df..f339d4d0bd 100644 --- a/src/Sentry.Log4Net/LevelMapping.cs +++ b/src/Sentry.Log4Net/LevelMapping.cs @@ -15,4 +15,18 @@ internal static class LevelMapping _ => null }; } + + public static BreadcrumbLevel? ToBreadcrumbLevel(this LoggingEvent loggingLevel) + { + return loggingLevel.Level switch + { + var l when l == Level.Fatal || l == Level.Emergency => BreadcrumbLevel.Critical, + var l when l == Level.Alert || l == Level.Critical || l == Level.Severe || l == Level.Error => BreadcrumbLevel.Error, + var l when l == Level.Warn => BreadcrumbLevel.Warning, + var l when l == Level.Notice || l == Level.Info => BreadcrumbLevel.Info, + var l when l == Level.Debug || l == Level.Verbose || l == Level.Trace || l == Level.Finer || l == Level.Finest || + l == Level.Fine || l == Level.All => BreadcrumbLevel.Debug, + _ => null + }; + } } diff --git a/src/Sentry.Log4Net/SentryAppender.cs b/src/Sentry.Log4Net/SentryAppender.cs index 29820a1caf..a8e954dfe8 100644 --- a/src/Sentry.Log4Net/SentryAppender.cs +++ b/src/Sentry.Log4Net/SentryAppender.cs @@ -32,6 +32,12 @@ internal static readonly SdkVersion NameAndVersion ///
public string? Environment { get; set; } + /// + /// Lowest level required for a log message to become an event. + /// Every level above threshold and below this level will become a breadcrumb. + /// + public Level? MinimumEventLevel { get; set; } + /// /// log4net SDK name. /// @@ -79,6 +85,20 @@ protected override void Append(LoggingEvent loggingEvent) } var exception = loggingEvent.ExceptionObject ?? loggingEvent.MessageObject as Exception; + + if (MinimumEventLevel is not null && loggingEvent.Level < MinimumEventLevel) + { + var message = !string.IsNullOrWhiteSpace(loggingEvent.RenderedMessage) ? loggingEvent.RenderedMessage : string.Empty; + var category = loggingEvent.LoggerName; + var level = loggingEvent.ToBreadcrumbLevel(); + IDictionary data = GetLoggingEventProperties(loggingEvent) + .Where(kvp => kvp.Value != null) + .ToDictionary(kvp => kvp.Key, kvp => kvp.Value!.ToString()); + + _hub.AddBreadcrumb(message, category, type: null, data, level ?? default); + return; + } + var evt = new SentryEvent(exception) { Logger = loggingEvent.LoggerName, diff --git a/test/Sentry.Log4Net.Tests/ApiApprovalTests.Run.Core3_1.verified.txt b/test/Sentry.Log4Net.Tests/ApiApprovalTests.Run.Core3_1.verified.txt index aedd58e120..117659776a 100644 --- a/test/Sentry.Log4Net.Tests/ApiApprovalTests.Run.Core3_1.verified.txt +++ b/test/Sentry.Log4Net.Tests/ApiApprovalTests.Run.Core3_1.verified.txt @@ -1,4 +1,4 @@ -[assembly: System.CLSCompliant(true)] +[assembly: System.CLSCompliant(true)] namespace Sentry.Log4Net { public class SentryAppender : log4net.Appender.AppenderSkeleton @@ -6,6 +6,7 @@ namespace Sentry.Log4Net 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() { } diff --git a/test/Sentry.Log4Net.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt b/test/Sentry.Log4Net.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt index 4fee33a751..117659776a 100644 --- a/test/Sentry.Log4Net.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt +++ b/test/Sentry.Log4Net.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt @@ -6,6 +6,7 @@ namespace Sentry.Log4Net 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() { } diff --git a/test/Sentry.Log4Net.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt b/test/Sentry.Log4Net.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt index 4fee33a751..117659776a 100644 --- a/test/Sentry.Log4Net.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt +++ b/test/Sentry.Log4Net.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt @@ -6,6 +6,7 @@ namespace Sentry.Log4Net 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() { } diff --git a/test/Sentry.Log4Net.Tests/ApiApprovalTests.Run.Net4_8.verified.txt b/test/Sentry.Log4Net.Tests/ApiApprovalTests.Run.Net4_8.verified.txt index 4fee33a751..117659776a 100644 --- a/test/Sentry.Log4Net.Tests/ApiApprovalTests.Run.Net4_8.verified.txt +++ b/test/Sentry.Log4Net.Tests/ApiApprovalTests.Run.Net4_8.verified.txt @@ -6,6 +6,7 @@ namespace Sentry.Log4Net 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() { } diff --git a/test/Sentry.Log4Net.Tests/SentryAppenderTests.cs b/test/Sentry.Log4Net.Tests/SentryAppenderTests.cs index b329a0d61a..b4e5b58a89 100644 --- a/test/Sentry.Log4Net.Tests/SentryAppenderTests.cs +++ b/test/Sentry.Log4Net.Tests/SentryAppenderTests.cs @@ -9,10 +9,14 @@ private class Fixture public IDisposable SdkDisposeHandle { get; set; } = Substitute.For(); public Func InitAction { get; set; } public IHub Hub { get; set; } = Substitute.For(); + public Func HubAccessor { get; set; } + public Scope Scope { get; } = new(new SentryOptions()); public string Dsn { get; set; } = "dsn"; public Fixture() { + HubAccessor = () => Hub; + Hub.ConfigureScope(Arg.Invoke(Scope)); InitAction = s => { DsnReceivedOnInit = s; @@ -125,6 +129,20 @@ public void Append_NullEvent_NoOp() sut.DoAppend(null as LoggingEvent); } + [Fact] + public void Append_BelowThreshold_DoesNotSendEvent() + { + var sut = _fixture.GetSut(); + sut.Threshold = Level.Warn; + var evt = new LoggingEvent(new LoggingEventData + { + Level = Level.Info + }); + + sut.DoAppend(evt); + _fixture.Hub.DidNotReceiveWithAnyArgs().CaptureEvent(Arg.Any()); + } + [Fact] public void Append_ByDefault_DoesNotSetUser() { @@ -291,6 +309,76 @@ public void Append_ConfiguredEnvironment() .CaptureEvent(Arg.Is(e => e.Environment == expected)); } + [Fact] + public void MinimumEventLevel_DefaultsToNull() + { + var appender = new SentryAppender(); + Assert.Null(appender.MinimumEventLevel); + } + + [Fact] + public void DoAppend_BelowMinimumEventLevel_AddsBreadcrumb() + { + var sut = _fixture.GetSut(); + sut.Threshold = Level.Debug; + sut.MinimumEventLevel = Level.Error; + + const string expectedBreadcrumbMsg = "log4net breadcrumb"; + var warnEvt = new LoggingEvent(new LoggingEventData + { + Level = Level.Warn, + Message = expectedBreadcrumbMsg + }); + sut.DoAppend(warnEvt); + + var breadcrumb = _fixture.Scope.Breadcrumbs.First(); + Assert.Equal(expectedBreadcrumbMsg, breadcrumb.Message); + } + + [Fact] + public void DoAppend_NullMinimumEventLevel_AddsEvent() + { + var sut = _fixture.GetSut(); + sut.Threshold = Level.Debug; + sut.MinimumEventLevel = null; + + const string expectedMessage = "log4net message"; + var warnEvt = new LoggingEvent(new LoggingEventData + { + Level = Level.Warn, + Message = expectedMessage + }); + sut.DoAppend(warnEvt); + + // No breadcrumb is added. + Assert.Empty(_fixture.Scope.Breadcrumbs); + // Event is sent instead. + _ = _fixture.Hub.Received(1) + .CaptureEvent(Arg.Is(e => e.Message.Message == expectedMessage)); + } + + [Fact] + public void DoAppend_AboveMinimumEventLevel_AddsEvent() + { + var sut = _fixture.GetSut(); + sut.Threshold = Level.Debug; + sut.MinimumEventLevel = Level.Warn; + + const string expectedMessage = "log4net message"; + var warnEvt = new LoggingEvent(new LoggingEventData + { + Level = Level.Error, + Message = expectedMessage + }); + sut.DoAppend(warnEvt); + + // No breadcrumb is added. + Assert.Empty(_fixture.Scope.Breadcrumbs); + // Event is sent instead. + _ = _fixture.Hub.Received(1) + .CaptureEvent(Arg.Is(e => e.Message.Message == expectedMessage)); + } + [Fact] public void Close_DisposesSdk() { From e9756bca801291e6f5b24f6b162f7eb1b8c8f995 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 31 Jul 2023 13:56:49 +0200 Subject: [PATCH 129/142] Bump gradle/gradle-build-action from 2.6.1 to 2.7.0 (#2524) --- .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 33c26df4ec..31b1202c2d 100644 --- a/.github/workflows/device-tests-android.yml +++ b/.github/workflows/device-tests-android.yml @@ -84,7 +84,7 @@ jobs: run: dotnet tool install Microsoft.DotNet.XHarness.CLI --global --version "1.*-*" - name: Setup Gradle - uses: gradle/gradle-build-action@915a66c096a03101667f9df2e56c9efef558b165 # pin@v2 + uses: gradle/gradle-build-action@a4cf152f482c7ca97ef56ead29bf08bcd953284c # pin@v2 # Cached AVD setup per https://github.com/ReactiveCircus/android-emulator-runner/blob/main/README.md From 64319d05189ce8f3dc72006cea4d9b19420ca5d6 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Tue, 1 Aug 2023 23:09:05 +1200 Subject: [PATCH 130/142] Fixed Transaction name not reporting correctly when using UseExceptionHandler (#2511) --- CHANGELOG.md | 1 + .../ExceptionHandlerFeatureProcessor.cs | 85 +++++++++ .../Extensions/HttpContextExtensions.cs | 139 +------------- src/Sentry.AspNetCore/RouteUtils.cs | 113 ++++++++++++ src/Sentry.AspNetCore/SentryMiddleware.cs | 12 +- .../Extensions/HttpContextExtensionsTests.cs | 143 --------------- .../IntegrationsTests.cs | 136 ++++++++++++++ .../RouteUtilsTests.cs | 173 ++++++++++++++++++ 8 files changed, 529 insertions(+), 273 deletions(-) create mode 100644 src/Sentry.AspNetCore/ExceptionHandlerFeatureProcessor.cs create mode 100644 src/Sentry.AspNetCore/RouteUtils.cs create mode 100644 test/Sentry.AspNetCore.Tests/RouteUtilsTests.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index ae924e4b1e..9bfce440dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ - Align SDK with docs regarding session update for dropped events ([#2496](https://github.com/getsentry/sentry-dotnet/pull/2496)) - Introduced `HttpMessageHandler` in favor of the now deprecated `HttpClientHandler` on the options. This allows the SDK to support NSUrlSessionHandler on iOS ([#2503](https://github.com/getsentry/sentry-dotnet/pull/2503)) - Using `Activity.RecordException` now correctly updates the error status of OpenTelemetry Spans ([#2515](https://github.com/getsentry/sentry-dotnet/pull/2515)) +- Fixed Transaction name not reporting correctly when using UseExceptionHandler ([#2511](https://github.com/getsentry/sentry-dotnet/pull/2511)) ### Dependencies diff --git a/src/Sentry.AspNetCore/ExceptionHandlerFeatureProcessor.cs b/src/Sentry.AspNetCore/ExceptionHandlerFeatureProcessor.cs new file mode 100644 index 0000000000..526a2d417d --- /dev/null +++ b/src/Sentry.AspNetCore/ExceptionHandlerFeatureProcessor.cs @@ -0,0 +1,85 @@ +using Microsoft.AspNetCore.Diagnostics; +using Microsoft.AspNetCore.Routing; +using Sentry.Extensibility; + +namespace Sentry.AspNetCore; + +#if NET6_0_OR_GREATER +internal class ExceptionHandlerFeatureProcessor : ISentryEventExceptionProcessor +{ + private readonly string _originalMethod; + private readonly IExceptionHandlerFeature _exceptionHandlerFeature; + + public ExceptionHandlerFeatureProcessor(string originalMethod, IExceptionHandlerFeature exceptionHandlerFeature) + { + _originalMethod = originalMethod; + _exceptionHandlerFeature = exceptionHandlerFeature; + } + + public void Process(Exception exception, SentryEvent sentryEvent) + { + // When exceptions get caught by the UseExceptionHandler feature we reset the TransactionName and Tags + // to reflect the route values of the original route (not the global error handling route) + ApplyTransactionName(sentryEvent, _originalMethod); + ApplyRouteTags(sentryEvent); + } + + internal void ApplyRouteTags(SentryEvent evt) + { + var endpoint = _exceptionHandlerFeature.Endpoint as RouteEndpoint; + + var actionName = endpoint?.DisplayName; + if (actionName is not null) + { + evt.SetTag("ActionName", actionName); + } + + if (_exceptionHandlerFeature.RouteValues is {} routeValues) + { + if (routeValues.TryGetValue("controller", out var controller)) + { + evt.SetTag("route.controller", $"{controller}"); + } + if (routeValues.TryGetValue("action", out var action)) + { + evt.SetTag("route.action", $"{action}"); + } + } + } + + internal void ApplyTransactionName(SentryEvent evt, + string method) + { + // If no route template details are available, fall back to the Path + var route = TryGetRouteTemplate() ?? _exceptionHandlerFeature.Path; + if (!string.IsNullOrWhiteSpace(route)) + { + evt.TransactionName = $"{method} {route}"; // e.g. "GET /pets/{id}" + } + } + + internal string? TryGetRouteTemplate() + { + // Requires .UseRouting()/.UseEndpoints() + var endpoint = _exceptionHandlerFeature.Endpoint as RouteEndpoint; + var routePattern = endpoint?.RoutePattern.RawText; + if (string.IsNullOrWhiteSpace(routePattern)) + { + return null; + } + + var routeValues = _exceptionHandlerFeature.RouteValues; + if (routeValues is null) + { + return null; + } + + if (RouteUtils.NewRouteFormat(routePattern, routeValues) is { } formattedRoute) + { + return formattedRoute; + } + + return RouteUtils.LegacyRouteFormat(routeValues); + } +} +#endif diff --git a/src/Sentry.AspNetCore/Extensions/HttpContextExtensions.cs b/src/Sentry.AspNetCore/Extensions/HttpContextExtensions.cs index f2a7e6f1ba..8f51a8576a 100644 --- a/src/Sentry.AspNetCore/Extensions/HttpContextExtensions.cs +++ b/src/Sentry.AspNetCore/Extensions/HttpContextExtensions.cs @@ -1,6 +1,5 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; -using Microsoft.Extensions.Primitives; using Sentry.Extensibility; #if !NETSTANDARD2_0 @@ -11,147 +10,29 @@ namespace Sentry.AspNetCore.Extensions; internal static class HttpContextExtensions { - public static string? TryGetRouteTemplate(this HttpContext context) + internal static string? TryGetRouteTemplate(this HttpContext context) { #if !NETSTANDARD2_0 // endpoint routing is only supported after ASP.NET Core 3.0 // Requires .UseRouting()/.UseEndpoints() var endpoint = context.Features.Get()?.Endpoint as RouteEndpoint; var routePattern = endpoint?.RoutePattern.RawText; - if (NewRouteFormat(routePattern, context) is { } formattedRoute) + // GetRouteData can return null on netstandard2 (despite annotations claiming otherwise) + if (RouteUtils.NewRouteFormat(routePattern, context.GetRouteData()?.Values, context.Request.PathBase) + is { } formattedRoute) { return formattedRoute; } #endif - return LegacyRouteFormat(context); - } - - public static string? TryGetCustomTransactionName(this HttpContext context) => - context.Features.Get()?.Invoke(context); - - // Internal for testing. - internal static string? NewRouteFormat(string? routePattern, HttpContext context) - { - if (string.IsNullOrWhiteSpace(routePattern)) - { - return null; - } - - var builder = new StringBuilder(); - if (context.Request.PathBase.HasValue) - { - builder.Append(context.Request.PathBase.Value?.TrimStart('/')) - .Append('/'); - } - - // Skip route pattern if it resembles to a MVC route or null e.g. - // {controller=Home}/{action=Index}/{id?} - if (RouteHasMvcParameters(routePattern)) - { - builder.Append(ReplaceMvcParameters(routePattern, context)); - } - else - { - if (builder is { Length: > 1 } && builder[^1].Equals('/') && routePattern[0] == '/') - { - builder.Length--; - } - - builder.Append(routePattern); - } - - return builder.ToString(); - } - - // Internal for testing. - internal static string? LegacyRouteFormat(HttpContext context) - { // Fallback for legacy .UseMvc(). - // Uses context.Features.Get() under the hood and CAN be null, - // despite the annotations claiming otherwise. - var routeData = context.GetRouteData(); - - // GetRouteData can return null on netstandard2 - if (routeData == null) - { - return null; - } - - var values = routeData.Values; - - if (values["action"] is not string action) - { - // If the handler doesn't use routing (i.e. it checks `context.Request.Path` directly), - // then there is no way for us to extract anything that resembles a route template. - return null; - } - - var builder = new StringBuilder(); - if (context.Request.PathBase.HasValue) - { - builder.Append(context.Request.PathBase.Value?.TrimStart('/')) - .Append('.'); - } - - if (values["area"] is string area) - { - builder.Append(area) - .Append('.'); - } - - if (values["controller"] is string controller) - { - builder.Append(controller) - .Append('.'); - } - - builder.Append(action); - - return builder.ToString(); + // Note: GetRouteData can return null on netstandard2 + return (context.GetRouteData() is { } routeData) + ? RouteUtils.LegacyRouteFormat(routeData.Values, context.Request.PathBase) + : null; } - // Internal for testing. - internal static string ReplaceMvcParameters(string route, HttpContext context) - { - var routeData = context.GetRouteData(); - - // GetRouteData can return null on netstandard2 - if (routeData == null) - { - return route; - } - - var values = routeData.Values; - - if (values["controller"] is string controller) - { - route = Regex.Replace(route, "{controller=[^}]+}", controller); - } - - if (values["action"] is string action) - { - route = Regex.Replace(route, "{action=[^}]+}", action); - } - - if (values["area"] is string area) - { - route = Regex.Replace(route, "{area=[^}]+}", area); - } - - if (values["version"] is string version) - { - route = Regex.Replace(route, "{version:[^}]+}", version); - } - - return route; - } - - // Internal for testing. - internal static bool RouteHasMvcParameters(string route) - => route.Contains("{controller=") || - route.Contains("{action=") || - route.Contains("{version:") || - route.Contains("{area="); + internal static string? TryGetCustomTransactionName(this HttpContext context) => + context.Features.Get()?.Invoke(context); public static string? TryGetTransactionName(this HttpContext context) { diff --git a/src/Sentry.AspNetCore/RouteUtils.cs b/src/Sentry.AspNetCore/RouteUtils.cs new file mode 100644 index 0000000000..90bd231448 --- /dev/null +++ b/src/Sentry.AspNetCore/RouteUtils.cs @@ -0,0 +1,113 @@ +using Microsoft.AspNetCore.Routing; + +namespace Sentry.AspNetCore; + +internal static class RouteUtils +{ + // Internal for testing. + internal static string? NewRouteFormat(string? routePattern, RouteValueDictionary? values, string? pathBase = null) + { + if (string.IsNullOrWhiteSpace(routePattern)) + { + return null; + } + + var builder = new StringBuilder(); + if (!string.IsNullOrWhiteSpace(pathBase)) + { + builder.Append(pathBase.TrimStart('/')) + .Append('/'); + } + + // Skip route pattern if it resembles to a MVC route or null e.g. + // {controller=Home}/{action=Index}/{id?} + if (RouteHasMvcParameters(routePattern)) + { + builder.Append(ReplaceMvcParameters(routePattern, values)); + } + else + { + if (builder is { Length: > 1 } && builder[^1].Equals('/') && routePattern[0] == '/') + { + builder.Length--; + } + + builder.Append(routePattern); + } + + return builder.ToString(); + } + + // Internal for testing. + internal static string? LegacyRouteFormat(RouteValueDictionary values, string? pathBase = null) + { + if (values["action"] is not string action) + { + // If the handler doesn't use routing (i.e. it checks `context.Request.Path` directly), + // then there is no way for us to extract anything that resembles a route template. + return null; + } + + var builder = new StringBuilder(); + if (!string.IsNullOrWhiteSpace(pathBase)) + { + builder.Append(pathBase.TrimStart('/')) + .Append('.'); + } + + if (values["area"] is string area) + { + builder.Append(area) + .Append('.'); + } + + if (values["controller"] is string controller) + { + builder.Append(controller) + .Append('.'); + } + + builder.Append(action); + + return builder.ToString(); + } + + // Internal for testing. + internal static string ReplaceMvcParameters(string route, RouteValueDictionary? values) + { + // GetRouteData can return null on netstandard2 + if (values == null) + { + return route; + } + + if (values["controller"] is string controller) + { + route = Regex.Replace(route, "{controller=[^}]+}", controller); + } + + if (values["action"] is string action) + { + route = Regex.Replace(route, "{action=[^}]+}", action); + } + + if (values["area"] is string area) + { + route = Regex.Replace(route, "{area=[^}]+}", area); + } + + if (values["version"] is string version) + { + route = Regex.Replace(route, "{version:[^}]+}", version); + } + + return route; + } + + // Internal for testing. + internal static bool RouteHasMvcParameters(string route) + => route.Contains("{controller=") || + route.Contains("{action=") || + route.Contains("{version:") || + route.Contains("{area="); +} diff --git a/src/Sentry.AspNetCore/SentryMiddleware.cs b/src/Sentry.AspNetCore/SentryMiddleware.cs index f7dcf0ee10..6dd13a8980 100644 --- a/src/Sentry.AspNetCore/SentryMiddleware.cs +++ b/src/Sentry.AspNetCore/SentryMiddleware.cs @@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; +using Sentry.AspNetCore.Extensions; using Sentry.Extensibility; using Sentry.Reflection; @@ -116,6 +117,7 @@ public async Task InvokeAsync(HttpContext context, RequestDelegate next) try { + var originalMethod = context.Request.Method; await next(context).ConfigureAwait(false); // When an exception was handled by other component (i.e: UseExceptionHandler feature). @@ -126,6 +128,14 @@ public async Task InvokeAsync(HttpContext context, RequestDelegate next) "This exception was caught by an ASP.NET Core custom error handler. " + "The web server likely returned a customized error page as a result of this exception."; +#if NET6_0_OR_GREATER + hub.ConfigureScope(scope => + { + scope.ExceptionProcessors.Add( + new ExceptionHandlerFeatureProcessor(originalMethod, exceptionFeature) + ); + }); +#endif CaptureException(exceptionFeature.Error, eventId, "IExceptionHandlerFeature", description); } @@ -151,7 +161,7 @@ public async Task InvokeAsync(HttpContext context, RequestDelegate next) } // Some environments disables the application after sending a request, - // making the OnCompleted flush to not work. + // preventing OnCompleted flush from working. Task FlushBeforeCompleted() => hub.FlushAsync(_options.FlushTimeout); void CaptureException(Exception e, SentryId evtId, string mechanism, string description) diff --git a/test/Sentry.AspNetCore.Tests/Extensions/HttpContextExtensionsTests.cs b/test/Sentry.AspNetCore.Tests/Extensions/HttpContextExtensionsTests.cs index b87b9ecf37..311420ca9f 100644 --- a/test/Sentry.AspNetCore.Tests/Extensions/HttpContextExtensionsTests.cs +++ b/test/Sentry.AspNetCore.Tests/Extensions/HttpContextExtensionsTests.cs @@ -51,149 +51,6 @@ private static void AddRouteValuesIfNotNull(RouteValueDictionary route, string k } } - private static string LegacyFormat(string controller, string action, string area) - => !string.IsNullOrWhiteSpace(area) ? $"{area}.{controller}.{action}" : $"{controller}.{action}"; - - [Theory] - [InlineData("{area=MyArea}/{controller=Home}/{action=Index}/{id?}", "theArea/house/about/{id?}", "house", "about", "theArea")] - [InlineData("{area=MyArea}/{controller=Home}/{action=Index}/{id?}", "{area=MyArea}/house/about/{id?}", "house", "about", null)] - [InlineData("{area=}/{controller=}/{action=}/{id?}", "{area=}/{controller=}/{action=}/{id?}", "house", "about", "theArea")] - [InlineData("{controller=Home}/{action=Index}/{id?}", "house/about/{id?}", "house", "about", null)] - [InlineData("{controller=Home}/{action=Index}", "house/about", "house", "about", null)] - [InlineData("{controller=Home}/{id?}", "house/{id?}", "house", "about", null)] - [InlineData("{action=Index}/{id?}", "about/{id?}", null, "about", null)] - [InlineData("not/mvc/", "not/mvc/", "house", "about", "area")] - [InlineData("not/mvc/{controller}/{action}/{area}", "not/mvc/{controller}/{action}/{area}", "house", "about", "area")] - public void ReplaceMcvParameters_ParsedParameters(string routeInput, string assertOutput, string controller, string action, string area) - { - // Arrange - var httpContext = Fixture.GetMvcSut(area, controller, action); - // Act - var filteredRoute = HttpContextExtensions.ReplaceMvcParameters(routeInput, httpContext); - - // Assert - Assert.Equal(assertOutput, filteredRoute); - } - - [Theory] - [InlineData("{area=MyArea}/{controller=Home}/{action=Index}/{id?}")] - [InlineData("{controller=Home}/{action=Index}/{id?}")] - [InlineData("{controller=Home}/{id?}")] - [InlineData("abc/{controller=}/")] - [InlineData("{action=Index}/{id?}")] - [InlineData("{area=Index}/{id?}")] - [InlineData("v{version:apiVersion}/Target")] - public void RouteHasMvcParameters_RouteWithMvcParameters_True(string route) - { - // Assert - Assert.True(HttpContextExtensions.RouteHasMvcParameters(route)); - } - - [Theory] - [InlineData("test/{area}")] - [InlineData("test/{action}")] - [InlineData("test/{controller}")] - [InlineData("area/test")] - [InlineData("/area/test")] - [InlineData("/")] - [InlineData("")] - public void RouteHasMvcParameters_RouteWithoutMvcParameters_False(string route) - { - // Assert - Assert.False(HttpContextExtensions.RouteHasMvcParameters(route)); - } - - [Theory] - [InlineData("{area=MyArea}/{controller=Home}/{action=Index}/{id?}", "myPath/theArea/house/about/{id?}", "house", "about", "theArea")] - [InlineData("{area=MyArea}/{controller=Home}/{action=Index}/{id?}", "myPath/{area=MyArea}/house/about/{id?}", "house", "about", null)] - [InlineData("{area=}/{controller=}/{action=}/{id?}", "myPath/{area=}/{controller=}/{action=}/{id?}", "house", "about", "theArea")] - [InlineData("{controller=Home}/{action=Index}/{id?}", "myPath/house/about/{id?}", "house", "about", null)] - [InlineData("{controller=Home}/{action=Index}", "myPath/house/about", "house", "about", null)] - [InlineData("{controller=Home}/{id?}", "myPath/house/{id?}", "house", "about", null)] - [InlineData("{action=Index}/{id?}", "myPath/about/{id?}", null, "about", null)] - public void NewRouteFormat_MvcRouteWithPathBase_ParsedParameters(string routeInput, string expectedOutput, string controller, string action, string area) - { - // Arrange - var httpContext = Fixture.GetMvcSut(area, controller, action, pathBase: "/myPath"); - - // Act - var filteredRoute = HttpContextExtensions.NewRouteFormat(routeInput, httpContext); - - // Assert - Assert.Equal(expectedOutput, filteredRoute); - } - - [Theory] - [InlineData("{area=MyArea}/{controller=Home}/{action=Index}/{id?}", "theArea/house/about/{id?}", "house", "about", "theArea", null)] - [InlineData("{area=MyArea}/{controller=Home}/{action=Index}/{id?}", "{area=MyArea}/house/about/{id?}", "house", "about", null, null)] - [InlineData("{area=}/{controller=}/{action=}/{id?}", "{area=}/{controller=}/{action=}/{id?}", "house", "about", "theArea", null)] - [InlineData("{controller=Home}/{action=Index}/{id?}", "house/about/{id?}", "house", "about", null, null)] - [InlineData("{controller=Home}/{action=Index}", "house/about", "house", "about", null, null)] - [InlineData("{controller=Home}/{id?}", "house/{id?}", "house", "about", null, null)] - [InlineData("{action=Index}/{id?}", "about/{id?}", null, "about", null, null)] - [InlineData("v{version:apiVersion}/Target", "v1.1/Target", null, "about", null, "1.1")] - public void NewRouteFormat_MvcRouteWithoutPathBase_ParsedParameters(string routeInput, string expectedOutput, string controller, string action, string area, string version) - { - // Arrange - var httpContext = Fixture.GetMvcSut(area, controller, action, null, version); - - // Act - var filteredRoute = HttpContextExtensions.NewRouteFormat(routeInput, httpContext); - - // Assert - Assert.Equal(expectedOutput, filteredRoute); - } - - [Theory] - [InlineData("myPath/some/Path", "/myPath", "some/Path")] - [InlineData("some/Path", null, "some/Path")] - [InlineData("api/health", "/api", "/health")] - [InlineData(null, null, "")] - [InlineData(null, null, null)] - public void NewRouteFormat_WithPathBase_MatchesExpectedRoute(string expectedRoute, string pathBase, string rawRoute) - { - // Arrange - var httpContext = Fixture.GetSut(pathBase); - - // Act - var filteredRoute = HttpContextExtensions.NewRouteFormat(rawRoute, httpContext); - - // Assert - Assert.Equal(expectedRoute, filteredRoute); - } - - [Theory] - [InlineData("myPath.myArea.myController.myAction", "/myPath", "myController", "myAction", "myArea")] - [InlineData("myArea.myController.myAction", null, "myController", "myAction", "myArea")] - [InlineData("myController.myAction", null, "myController", "myAction", null)] - [InlineData(null, null, null, null, null)] - public void LegacyRouteFormat_WithPathBase_MatchesExpectedRoute(string expectedRoute, string pathBase, string controller, string action, string area) - { - // Arrange - var httpContext = Fixture.GetMvcSut(area, controller, action, pathBase); - - // Act - var filteredRoute = HttpContextExtensions.LegacyRouteFormat(httpContext); - - // Assert - Assert.Equal(expectedRoute, filteredRoute); - } - - [Theory] - [InlineData("myController", "myAction", "myArea")] - [InlineData("myController", "myAction", null)] - public void LegacyRouteFormat_ValidRoutes_MatchPreviousImplementationResult(string controller, string action, string area) - { - // Arrange - var httpContext = Fixture.GetMvcSut(area, controller, action); - - // Act - var filteredRoute = HttpContextExtensions.LegacyRouteFormat(httpContext); - - // Assert - Assert.Equal(LegacyFormat(controller, action, area), filteredRoute); - } - [Fact] public void TryGetRouteTemplate_NoRoute_NullOutput() { diff --git a/test/Sentry.AspNetCore.Tests/IntegrationsTests.cs b/test/Sentry.AspNetCore.Tests/IntegrationsTests.cs index c889e65b8b..ae38dc31c8 100644 --- a/test/Sentry.AspNetCore.Tests/IntegrationsTests.cs +++ b/test/Sentry.AspNetCore.Tests/IntegrationsTests.cs @@ -1,3 +1,9 @@ +#if NET6_0_OR_GREATER +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.TestHost; +#endif using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Sentry.AspNetCore.TestUtils; @@ -7,6 +13,136 @@ namespace Sentry.AspNetCore.Tests; [Collection(nameof(SentrySdkCollection))] public partial class IntegrationsTests : AspNetSentrySdkTestFixture { +#if NET6_0_OR_GREATER + [Fact] + public async Task CaptureException_UseExceptionHandler_SetTransactionNameFromInitialRequest() + { + // Arrange + SentryEvent exceptionEvent = null; + var exceptionProcessor = Substitute.For(); + exceptionProcessor.Process(Arg.Any(), Arg.Do( + evt => exceptionEvent = evt + )); + Configure = o => + { + o.ExceptionProcessors.Add(exceptionProcessor); + }; + + const string throwPath = "/throw"; + const string errorPath = "/error"; + Handlers = new[] + { + new RequestHandler + { + Path = throwPath, + Handler = _ => throw new Exception("test error") + }, + new RequestHandler + { + Path = errorPath, + Response = "error" + } + }; + ConfigureApp = app => + { + app.UseExceptionHandler(errorPath); + }; + Build(); + + // Act + _ = await HttpClient.GetAsync(throwPath); + + // Assert + exceptionEvent.Should().NotBeNull(); + exceptionEvent.TransactionName.Should().Be("GET /throw"); + } + + [Fact] + public async Task CaptureException_UseExceptionHandler_SetRouteDataFromInitialRequest() + { + // Arrange + SentryEvent exceptionEvent = null; + var exceptionProcessor = Substitute.For(); + exceptionProcessor.Process(Arg.Any(), Arg.Do( + evt => exceptionEvent = evt + )); + Configure = o => + { + o.ExceptionProcessors.Add(exceptionProcessor); + }; + + const string throwPath = "/test/throw"; + const string errorPath = "/test/error"; + ConfigureServices = services => + { + services.AddRouting(); + var controllers = services.AddControllers(); + controllers.UseSpecificControllers(typeof(TestController)); + }; + ConfigureApp = app => + { + app.UseExceptionHandler(errorPath); + app.UseRouting(); + app.UseEndpoints(routeBuilder => routeBuilder.MapControllers()); + }; + + //Build(); + var builder = new WebHostBuilder(); + + _ = builder.ConfigureServices(s => + { + var lastException = new LastExceptionFilter(); + _ = s.AddSingleton(lastException); + _ = s.AddSingleton(lastException); + + ConfigureServices?.Invoke(s); + }); + _ = builder.Configure(app => + { + ConfigureApp?.Invoke(app); + }); + + ConfigureWebHost?.Invoke(builder); + ConfigureBuilder(builder); + + TestServer = new TestServer(builder); + HttpClient = TestServer.CreateClient(); + + // Act + _ = await HttpClient.GetAsync(throwPath); + + // Assert + exceptionEvent.Should().NotBeNull(); + using (new AssertionScope()) + { + exceptionEvent.Tags.Should().Contain(kvp => + kvp.Key == "ActionName" && + kvp.Value == "Sentry.AspNetCore.Tests.IntegrationsTests+TestController.Throw (Sentry.AspNetCore.Tests)" + ); + exceptionEvent.Tags.Should().Contain(kvp => + kvp.Key == "route.controller" && + kvp.Value == "Test" + ); + exceptionEvent.Tags.Should().Contain(kvp => + kvp.Key == "route.action" && + kvp.Value == "Throw" + ); + } + } + + public class TestController : Controller + { + [HttpGet("[controller]/[action]")] + public IActionResult Error() => Content("Error"); + + [HttpGet("[controller]/[action]")] + public IActionResult Throw() + { + throw new Exception("This is an example exception"); + } + } +#endif + [Fact] public async Task UnhandledException_AvailableThroughLastExceptionFilter() { diff --git a/test/Sentry.AspNetCore.Tests/RouteUtilsTests.cs b/test/Sentry.AspNetCore.Tests/RouteUtilsTests.cs new file mode 100644 index 0000000000..0d64c8df64 --- /dev/null +++ b/test/Sentry.AspNetCore.Tests/RouteUtilsTests.cs @@ -0,0 +1,173 @@ +using Microsoft.AspNetCore.Routing; + +namespace Sentry.AspNetCore.Tests; + +public class RouteUtilsTests +{ + private static RouteValueDictionary GetRouteValues( + string area = null, + string controller = null, + string action = null, + string version = null) + { + var values = new RouteValueDictionary(); + + AddRouteValuesIfNotNull(values, "controller", controller); + AddRouteValuesIfNotNull(values, "action", action); + AddRouteValuesIfNotNull(values, "area", area); + AddRouteValuesIfNotNull(values, "version", version); + return values; + } + + private static void AddRouteValuesIfNotNull(RouteValueDictionary route, string key, string value) + { + if (value is not null) + { + route.Add(key, value); + } + } + + [Theory] + [InlineData("{area=MyArea}/{controller=Home}/{action=Index}/{id?}", "myPath/theArea/house/about/{id?}", "house", "about", "theArea")] + [InlineData("{area=MyArea}/{controller=Home}/{action=Index}/{id?}", "myPath/{area=MyArea}/house/about/{id?}", "house", "about", null)] + [InlineData("{area=}/{controller=}/{action=}/{id?}", "myPath/{area=}/{controller=}/{action=}/{id?}", "house", "about", "theArea")] + [InlineData("{controller=Home}/{action=Index}/{id?}", "myPath/house/about/{id?}", "house", "about", null)] + [InlineData("{controller=Home}/{action=Index}", "myPath/house/about", "house", "about", null)] + [InlineData("{controller=Home}/{id?}", "myPath/house/{id?}", "house", "about", null)] + [InlineData("{action=Index}/{id?}", "myPath/about/{id?}", null, "about", null)] + public void NewRouteFormat_MvcRouteWithPathBase_ParsedParameters(string routeInput, string expectedOutput, string controller, string action, string area) + { + // Arrange + var values = GetRouteValues(area, controller, action); + + // Act + var filteredRoute = RouteUtils.NewRouteFormat(routeInput, values, "/myPath"); + + // Assert + Assert.Equal(expectedOutput, filteredRoute); + } + + [Theory] + [InlineData("{area=MyArea}/{controller=Home}/{action=Index}/{id?}", "theArea/house/about/{id?}", "house", "about", "theArea", null)] + [InlineData("{area=MyArea}/{controller=Home}/{action=Index}/{id?}", "{area=MyArea}/house/about/{id?}", "house", "about", null, null)] + [InlineData("{area=}/{controller=}/{action=}/{id?}", "{area=}/{controller=}/{action=}/{id?}", "house", "about", "theArea", null)] + [InlineData("{controller=Home}/{action=Index}/{id?}", "house/about/{id?}", "house", "about", null, null)] + [InlineData("{controller=Home}/{action=Index}", "house/about", "house", "about", null, null)] + [InlineData("{controller=Home}/{id?}", "house/{id?}", "house", "about", null, null)] + [InlineData("{action=Index}/{id?}", "about/{id?}", null, "about", null, null)] + [InlineData("v{version:apiVersion}/Target", "v1.1/Target", null, "about", null, "1.1")] + public void NewRouteFormat_MvcRouteWithoutPathBase_ParsedParameters(string routeInput, string expectedOutput, string controller, string action, string area, string version) + { + // Arrange + var values = GetRouteValues(area, controller, action, version); + + // Act + var filteredRoute = RouteUtils.NewRouteFormat(routeInput, values); + + // Assert + Assert.Equal(expectedOutput, filteredRoute); + } + + [Theory] + [InlineData("myPath/some/Path", "/myPath", "some/Path")] + [InlineData("some/Path", null, "some/Path")] + [InlineData("api/health", "/api", "/health")] + [InlineData(null, null, "")] + [InlineData(null, null, null)] + public void NewRouteFormat_WithPathBase_MatchesExpectedRoute(string expectedRoute, string pathBase, string rawRoute) + { + // Arrange + var values = GetRouteValues(); + + // Act + var filteredRoute = RouteUtils.NewRouteFormat(rawRoute, values, pathBase); + + // Assert + Assert.Equal(expectedRoute, filteredRoute); + } + + private static string LegacyFormat(string controller, string action, string area) + => !string.IsNullOrWhiteSpace(area) ? $"{area}.{controller}.{action}" : $"{controller}.{action}"; + + [Theory] + [InlineData("myPath.myArea.myController.myAction", "/myPath", "myController", "myAction", "myArea")] + [InlineData("myArea.myController.myAction", null, "myController", "myAction", "myArea")] + [InlineData("myController.myAction", null, "myController", "myAction", null)] + [InlineData(null, null, null, null, null)] + public void LegacyRouteFormat_WithPathBase_MatchesExpectedRoute(string expectedRoute, string pathBase, string controller, string action, string area) + { + // Arrange + var values = GetRouteValues(area, controller, action); + + // Act + var filteredRoute = RouteUtils.LegacyRouteFormat(values, pathBase); + + // Assert + Assert.Equal(expectedRoute, filteredRoute); + } + + [Theory] + [InlineData("myController", "myAction", "myArea")] + [InlineData("myController", "myAction", null)] + public void LegacyRouteFormat_ValidRoutes_MatchPreviousImplementationResult(string controller, string action, string area) + { + // Arrange + var values = GetRouteValues(area, controller, action); + + // Act + var filteredRoute = RouteUtils.LegacyRouteFormat(values); + + // Assert + Assert.Equal(LegacyFormat(controller, action, area), filteredRoute); + } + + [Theory] + [InlineData("{area=MyArea}/{controller=Home}/{action=Index}/{id?}", "theArea/house/about/{id?}", "house", "about", "theArea")] + [InlineData("{area=MyArea}/{controller=Home}/{action=Index}/{id?}", "{area=MyArea}/house/about/{id?}", "house", "about", null)] + [InlineData("{area=}/{controller=}/{action=}/{id?}", "{area=}/{controller=}/{action=}/{id?}", "house", "about", "theArea")] + [InlineData("{controller=Home}/{action=Index}/{id?}", "house/about/{id?}", "house", "about", null)] + [InlineData("{controller=Home}/{action=Index}", "house/about", "house", "about", null)] + [InlineData("{controller=Home}/{id?}", "house/{id?}", "house", "about", null)] + [InlineData("{action=Index}/{id?}", "about/{id?}", null, "about", null)] + [InlineData("not/mvc/", "not/mvc/", "house", "about", "area")] + [InlineData("not/mvc/{controller}/{action}/{area}", "not/mvc/{controller}/{action}/{area}", "house", "about", "area")] + public void ReplaceMcvParameters_ParsedParameters(string routeInput, string assertOutput, string controller, string action, string area) + { + // Arrange + var values = GetRouteValues(area, controller, action); + + // Act + var filteredRoute = RouteUtils.ReplaceMvcParameters(routeInput, values); + + // Assert + Assert.Equal(assertOutput, filteredRoute); + } + + [Theory] + [InlineData("{area=MyArea}/{controller=Home}/{action=Index}/{id?}")] + [InlineData("{controller=Home}/{action=Index}/{id?}")] + [InlineData("{controller=Home}/{id?}")] + [InlineData("abc/{controller=}/")] + [InlineData("{action=Index}/{id?}")] + [InlineData("{area=Index}/{id?}")] + [InlineData("v{version:apiVersion}/Target")] + public void RouteHasMvcParameters_RouteWithMvcParameters_True(string route) + { + // Assert + Assert.True(RouteUtils.RouteHasMvcParameters(route)); + } + + [Theory] + [InlineData("test/{area}")] + [InlineData("test/{action}")] + [InlineData("test/{controller}")] + [InlineData("area/test")] + [InlineData("/area/test")] + [InlineData("/")] + [InlineData("")] + public void RouteHasMvcParameters_RouteWithoutMvcParameters_False(string route) + { + // Assert + Assert.False(RouteUtils.RouteHasMvcParameters(route)); + } +} From 592dfd8c77f8bd6f770b2f009f86191c2305db43 Mon Sep 17 00:00:00 2001 From: Markus Hintersteiner Date: Tue, 1 Aug 2023 16:04:51 +0200 Subject: [PATCH 131/142] fix(android): Fix internal Android SDK API changes break build (#2513) --- CHANGELOG.md | 6 +++--- .../Sentry.Bindings.Android.csproj | 2 +- src/Sentry.Bindings.Android/Transforms/Metadata.xml | 10 ++++++++++ .../Android/Extensions/SentryEventExtensions.cs | 3 ++- 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9bfce440dc..937055254b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,9 +20,9 @@ ### Dependencies -- Bump Java SDK from v6.25.1 to v6.25.2 ([#2484](https://github.com/getsentry/sentry-dotnet/pull/2484)) - - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#6252) - - [diff](https://github.com/getsentry/sentry-java/compare/6.25.1...6.25.2) +- Bump Java SDK from v6.25.1 to v6.26.0 ([#2484](https://github.com/getsentry/sentry-dotnet/pull/2484), [#2498](https://github.com/getsentry/sentry-dotnet/pull/2498)) + - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#6260) + - [diff](https://github.com/getsentry/sentry-java/compare/6.25.1...6.26.0) - Bump CLI from v2.19.4 to v2.20.1 ([#2509](https://github.com/getsentry/sentry-dotnet/pull/2509), [#2518](https://github.com/getsentry/sentry-dotnet/pull/2518)) - [changelog](https://github.com/getsentry/sentry-cli/blob/master/CHANGELOG.md#2201) - [diff](https://github.com/getsentry/sentry-cli/compare/2.19.4...2.20.1) diff --git a/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj b/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj index 6d09ea9a34..dfc8ca2492 100644 --- a/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj +++ b/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj @@ -3,7 +3,7 @@ net6.0-android $(NoWarn);BG8605;BG8606 - 6.25.2 + 6.26.0 $(BaseIntermediateOutputPath)sdks\Sentry\Android\$(SentryAndroidSdkVersion)\ .NET Bindings for the Sentry Android SDK diff --git a/src/Sentry.Bindings.Android/Transforms/Metadata.xml b/src/Sentry.Bindings.Android/Transforms/Metadata.xml index 60747904e6..67432fad95 100644 --- a/src/Sentry.Bindings.Android/Transforms/Metadata.xml +++ b/src/Sentry.Bindings.Android/Transforms/Metadata.xml @@ -88,6 +88,16 @@ + + + Sentry.JavaSdk.JsonObjectWriter + $(NoWarn);BG8605;BG8606 - 6.26.0 + 6.27.0 $(BaseIntermediateOutputPath)sdks\Sentry\Android\$(SentryAndroidSdkVersion)\ .NET Bindings for the Sentry Android SDK From 165e0966c43a4695f0cab49c161107e873678432 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 3 Aug 2023 09:13:58 +0200 Subject: [PATCH 134/142] chore: update scripts/update-cli.ps1 to 2.20.3 (#2527) --- 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 2e4628526a..7b76a6e921 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,9 +24,9 @@ - Bump Java SDK from v6.25.1 to v6.27.0 ([#2484](https://github.com/getsentry/sentry-dotnet/pull/2484), [#2498](https://github.com/getsentry/sentry-dotnet/pull/2498), [#2517](https://github.com/getsentry/sentry-dotnet/pull/2517)) - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#6270) - [diff](https://github.com/getsentry/sentry-java/compare/6.25.1...6.27.0) -- Bump CLI from v2.19.4 to v2.20.1 ([#2509](https://github.com/getsentry/sentry-dotnet/pull/2509), [#2518](https://github.com/getsentry/sentry-dotnet/pull/2518)) - - [changelog](https://github.com/getsentry/sentry-cli/blob/master/CHANGELOG.md#2201) - - [diff](https://github.com/getsentry/sentry-cli/compare/2.19.4...2.20.1) +- Bump CLI from v2.19.4 to v2.20.3 ([#2509](https://github.com/getsentry/sentry-dotnet/pull/2509), [#2518](https://github.com/getsentry/sentry-dotnet/pull/2518), [#2527](https://github.com/getsentry/sentry-dotnet/pull/2527)) + - [changelog](https://github.com/getsentry/sentry-cli/blob/master/CHANGELOG.md#2203) + - [diff](https://github.com/getsentry/sentry-cli/compare/2.19.4...2.20.3) - Bump Cocoa SDK from v8.8.0 to v8.9.3 ([#2479](https://github.com/getsentry/sentry-dotnet/pull/2479), [#2483](https://github.com/getsentry/sentry-dotnet/pull/2483), [#2500](https://github.com/getsentry/sentry-dotnet/pull/2500), [#2510](https://github.com/getsentry/sentry-dotnet/pull/2510)) - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#893) - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.8.0...8.9.3) diff --git a/Directory.Build.props b/Directory.Build.props index 33d7b04b5e..e0886e7a75 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -100,7 +100,7 @@ - 2.20.1 + 2.20.3 $(MSBuildThisFileDirectory)tools\sentry-cli\$(SentryCLIVersion)\ diff --git a/src/Sentry/Sentry.csproj b/src/Sentry/Sentry.csproj index b9d3b33835..2d850d14fd 100644 --- a/src/Sentry/Sentry.csproj +++ b/src/Sentry/Sentry.csproj @@ -96,25 +96,25 @@ + Include="sentry-cli-Darwin-arm64" FileHash="9b39ca8f6b633f971753c99b2742cfc4f29d84357e04e6ddf97ff436259f1d41" /> + Include="sentry-cli-Darwin-x86_64" FileHash="a260e81796edf73e54320c8d6bf1839bfe9be5a8781ccce7882b262200955bd2" /> + Include="sentry-cli-Linux-aarch64" FileHash="fae5777deee53329b8c427fb9eda86bd4f01412b73d0d672b9485e6f95be0247" /> + Include="sentry-cli-Linux-i686" FileHash="b9e36903099d050892a977f5490a5a92dc43907b39331e291cec49270e47d56a" /> + Include="sentry-cli-Linux-x86_64" FileHash="f2d4eb5a5d338278b7c38d5fa16a33026602827c6fac5cdb7b35b909c91de001" /> + Include="sentry-cli-Windows-i686.exe" FileHash="1f874311502f39d2a0a5d192b785a66d5bb8fd0cbca75b992aff86acfad7a916" /> + Include="sentry-cli-Windows-x86_64.exe" FileHash="8762fb82bc12e3a9642013ef91a495d459d533ff6d1a0112baaf776c7540aef1" /> From 55ca61eb90b7a269b950d95500502e154f16787d Mon Sep 17 00:00:00 2001 From: Stefan Jandl Date: Thu, 3 Aug 2023 14:41:16 +0200 Subject: [PATCH 135/142] feat: Tracing without performance (#2493) --- CHANGELOG.md | 1 + src/Sentry.AspNet/HttpContextExtensions.cs | 28 +++++- src/Sentry.AspNetCore/SentryMiddleware.cs | 13 +++ .../SentryTracingMiddleware.cs | 30 +++--- src/Sentry/BaggageHeader.cs | 12 +-- src/Sentry/DynamicSamplingContext.cs | 30 +++++- src/Sentry/Extensibility/DisabledHub.cs | 18 ++++ src/Sentry/Extensibility/HubAdapter.cs | 18 ++++ src/Sentry/IHub.cs | 19 +++- src/Sentry/Internal/Hub.cs | 92 ++++++++++++++---- src/Sentry/Scope.cs | 11 ++- src/Sentry/SentryHttpMessageHandler.cs | 6 +- src/Sentry/SentryOptions.cs | 13 ++- src/Sentry/SentryPropagationContext.cs | 58 +++++++++++ src/Sentry/SentrySdk.cs | 23 ++++- src/Sentry/SpanContext.cs | 2 +- src/Sentry/TransactionContext.cs | 4 +- .../ApiApprovalTests.Run.Net4_8.verified.txt | 1 + .../SentryMiddlewareTests.cs | 68 +++++++++++++ ...rTests.LoggingAsync.DotNet6_0.verified.txt | 8 +- ...rTests.LoggingAsync.DotNet7_0.verified.txt | 8 +- .../IntegrationTests.verify.cs | 3 +- ...gingInsideTheContextOfLogging.verified.txt | 26 ++++- .../IntegrationTests.Simple.verified.txt | 12 ++- .../IntegrationTests.verify.cs | 6 +- ...gingInsideTheContextOfLogging.verified.txt | 12 ++- ...tegrationTests.Simple.Core3_1.verified.txt | 14 ++- ...grationTests.Simple.DotNet6_0.verified.txt | 14 ++- ...grationTests.Simple.DotNet7_0.verified.txt | 14 ++- ...tegrationTests.Simple.Mono4_0.verified.txt | 12 ++- ...ntegrationTests.Simple.Net4_8.verified.txt | 14 ++- .../IntegrationTests.verify.cs | 2 + ...gingInsideTheContextOfLogging.verified.txt | 12 ++- ...tegrationTests.Simple.Core3_1.verified.txt | 56 +++++++++-- ...grationTests.Simple.DotNet6_0.verified.txt | 56 +++++++++-- ...grationTests.Simple.DotNet7_0.verified.txt | 56 +++++++++-- ...tegrationTests.Simple.Mono4_0.verified.txt | 54 +++++++++-- ...ntegrationTests.Simple.Net4_8.verified.txt | 56 +++++++++-- .../IntegrationTests.verify.cs | 2 + .../ApiApprovalTests.Run.Core3_1.verified.txt | 18 +++- ...piApprovalTests.Run.DotNet6_0.verified.txt | 18 +++- ...piApprovalTests.Run.DotNet7_0.verified.txt | 18 +++- .../ApiApprovalTests.Run.Net4_8.verified.txt | 18 +++- .../DynamicSamplingContextTests.cs | 16 ++++ .../EventProcessorTests.Simple.verified.txt | 13 ++- test/Sentry.Tests/HubTests.cs | 95 ++++++++++++++++++- test/Sentry.Tests/ScopeTests.cs | 24 +++++ test/Sentry.Tests/SentryOptionsTests.cs | 29 ++++++ .../SentryPropagationContextTests.cs | 38 ++++++++ 49 files changed, 1034 insertions(+), 137 deletions(-) create mode 100644 src/Sentry/SentryPropagationContext.cs create mode 100644 test/Sentry.Tests/SentryPropagationContextTests.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b76a6e921..44d645ed6e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Features +- Distributed tracing now works independently of the performance feature. This allows you to connect errors to other Sentry instrumented applications ([#2493](https://github.com/getsentry/sentry-dotnet/pull/2493)) - Added Sampling Decision to Trace Envelope Header ([#2495](https://github.com/getsentry/sentry-dotnet/pull/2495)) - Add MinimumEventLevel to Sentry.Log4Net and convert events below it to breadcrumbs ([#2505](https://github.com/getsentry/sentry-dotnet/pull/2505)) diff --git a/src/Sentry.AspNet/HttpContextExtensions.cs b/src/Sentry.AspNet/HttpContextExtensions.cs index 754cac419d..6d0466caa1 100644 --- a/src/Sentry.AspNet/HttpContextExtensions.cs +++ b/src/Sentry.AspNet/HttpContextExtensions.cs @@ -55,6 +55,25 @@ public static class HttpContextExtensions } } + /// + /// Starts or continues a Sentry trace. + /// + public static void StartOrContinueTrace(this HttpContext httpContext) + { + var options = SentrySdk.CurrentOptions; + + var traceHeader = TryGetSentryTraceHeader(httpContext, options); + var baggageHeader = TryGetBaggageHeader(httpContext, options); + + var method = httpContext.Request.HttpMethod; + var path = httpContext.Request.Path; + + var transactionName = $"{method} {path}"; + const string operation = "http.server"; + + SentrySdk.ContinueTrace(traceHeader, baggageHeader, transactionName, operation); + } + /// /// Starts a new Sentry transaction that encompasses the currently executing HTTP request. /// @@ -65,13 +84,13 @@ public static ITransaction StartSentryTransaction(this HttpContext httpContext) var options = SentrySdk.CurrentOptions; var traceHeader = TryGetSentryTraceHeader(httpContext, options); + var baggageHeader = TryGetBaggageHeader(httpContext, options); var transactionName = $"{method} {path}"; const string transactionOperation = "http.server"; - var transactionContext = traceHeader is not null - ? new TransactionContext(transactionName, transactionOperation, traceHeader, TransactionNameSource.Url) - : new TransactionContext(transactionName, transactionOperation, TransactionNameSource.Url); + var transactionContext = SentrySdk.ContinueTrace(traceHeader, baggageHeader, transactionName, transactionOperation); + transactionContext.NameSource = TransactionNameSource.Url; var customSamplingContext = new Dictionary(3, StringComparer.Ordinal) { @@ -81,10 +100,9 @@ public static ITransaction StartSentryTransaction(this HttpContext httpContext) }; // Set the Dynamic Sampling Context from the baggage header, if it exists. - var baggageHeader = TryGetBaggageHeader(httpContext, options); var dynamicSamplingContext = baggageHeader?.CreateDynamicSamplingContext(); - if (traceHeader is { } && baggageHeader is null) + if (traceHeader is not null && baggageHeader is null) { // We received a sentry-trace header without a baggage header, which indicates the request // originated from an older SDK that doesn't support dynamic sampling. diff --git a/src/Sentry.AspNetCore/SentryMiddleware.cs b/src/Sentry.AspNetCore/SentryMiddleware.cs index 6dd13a8980..7ace286888 100644 --- a/src/Sentry.AspNetCore/SentryMiddleware.cs +++ b/src/Sentry.AspNetCore/SentryMiddleware.cs @@ -18,6 +18,10 @@ namespace Sentry.AspNetCore; ///
internal class SentryMiddleware : IMiddleware { + internal static readonly object TraceHeaderItemKey = new(); + internal static readonly object BaggageHeaderItemKey = new(); + internal static readonly object TransactionContextItemKey = new(); + private readonly Func _getHub; private readonly SentryAspNetCoreOptions _options; private readonly IHostingEnvironment _hostingEnvironment; @@ -91,6 +95,15 @@ public async Task InvokeAsync(HttpContext context, RequestDelegate next) context.Response.OnCompleted(() => hub.FlushAsync(_options.FlushTimeout)); } + var traceHeader = context.TryGetSentryTraceHeader(_options); + var baggageHeader = context.TryGetBaggageHeader(_options); + var transactionContext = hub.ContinueTrace(traceHeader, baggageHeader); + + // Adding the headers and the TransactionContext to the context to be picked up by the Sentry tracing middleware + context.Items.Add(TraceHeaderItemKey, traceHeader); + context.Items.Add(BaggageHeaderItemKey, baggageHeader); + context.Items.Add(TransactionContextItemKey, transactionContext); + hub.ConfigureScope(scope => { // At the point lots of stuff from the request are not yet filled diff --git a/src/Sentry.AspNetCore/SentryTracingMiddleware.cs b/src/Sentry.AspNetCore/SentryTracingMiddleware.cs index fc47284129..f909fd3ff6 100644 --- a/src/Sentry.AspNetCore/SentryTracingMiddleware.cs +++ b/src/Sentry.AspNetCore/SentryTracingMiddleware.cs @@ -36,19 +36,19 @@ public SentryTracingMiddleware( try { - var hub = _getHub(); - - // Attempt to start a transaction from the trace header if it exists - var traceHeader = context.TryGetSentryTraceHeader(_options); + // The trace gets continued in the SentryMiddleware. + context.Items.TryGetValue(SentryMiddleware.TransactionContextItemKey, out var transactionContextObject); + if (transactionContextObject is not TransactionContext transactionContext) + { + throw new KeyNotFoundException($"Failed to retrieve the '{nameof(SentryMiddleware.TransactionContextItemKey)}' from the HttpContext items."); + } // It's important to try and set the transaction name to some value here so that it's available for use // in sampling. At a later stage, we will try to get the transaction name again, to account for the // other middlewares that may have ran after ours. - var transactionName = context.TryGetTransactionName() ?? string.Empty; - - var transactionContext = traceHeader is not null - ? new TransactionContext(transactionName, OperationName, traceHeader, TransactionNameSource.Route) - : new TransactionContext(transactionName, OperationName, TransactionNameSource.Route); + transactionContext.Name = context.TryGetTransactionName() ?? string.Empty; + transactionContext.Operation = OperationName; + transactionContext.NameSource = TransactionNameSource.Route; var customSamplingContext = new Dictionary(4, StringComparer.Ordinal) { @@ -58,11 +58,14 @@ public SentryTracingMiddleware( [SamplingExtensions.KeyForHttpContext] = context, }; - // Set the Dynamic Sampling Context from the baggage header, if it exists. - var baggageHeader = context.TryGetBaggageHeader(_options); + var traceHeader = context.Items.TryGetValue(SentryMiddleware.TraceHeaderItemKey, out var traceHeaderObject) + ? 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 { } && baggageHeader is null) + if (traceHeader is not null && baggageHeader is null) { // We received a sentry-trace header without a baggage header, which indicates the request // originated from an older SDK that doesn't support dynamic sampling. @@ -73,7 +76,7 @@ public SentryTracingMiddleware( dynamicSamplingContext = DynamicSamplingContext.Empty; } - var transaction = hub.StartTransaction(transactionContext, customSamplingContext, dynamicSamplingContext); + var transaction = _getHub().StartTransaction(transactionContext, customSamplingContext, dynamicSamplingContext); _options.LogInfo( "Started transaction with span ID '{0}' and trace ID '{1}'.", @@ -95,7 +98,6 @@ public SentryTracingMiddleware( public async Task InvokeAsync(HttpContext context) { var hub = _getHub(); - if (!hub.IsEnabled) { await _next(context).ConfigureAwait(false); diff --git a/src/Sentry/BaggageHeader.cs b/src/Sentry/BaggageHeader.cs index 4c2bf572c3..83fe9f0a84 100644 --- a/src/Sentry/BaggageHeader.cs +++ b/src/Sentry/BaggageHeader.cs @@ -8,7 +8,7 @@ namespace Sentry; ///
/// /// -internal class BaggageHeader +public class BaggageHeader { internal const string HttpHeaderName = "baggage"; internal const string SentryKeyPrefix = "sentry-"; @@ -19,14 +19,14 @@ internal class BaggageHeader // "Uniqueness of keys between multiple list-members in a baggage-string is not guaranteed." // "The order of duplicate entries SHOULD be preserved when mutating the list." - public IReadOnlyList> Members { get; } + internal IReadOnlyList> Members { get; } private BaggageHeader(IEnumerable> members) => Members = members.ToList(); // 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. - public IReadOnlyDictionary GetSentryMembers() => + internal IReadOnlyDictionary GetSentryMembers() => Members .Where(kvp => kvp.Key.StartsWith(SentryKeyPrefix)) .GroupBy(kvp => kvp.Key, kvp => kvp.Value) @@ -59,7 +59,7 @@ public override string ToString() /// /// An object representing the members baggage header, or null if there are no members parsed. /// - public static BaggageHeader? TryParse(string baggage, bool onlySentry = false) + internal static BaggageHeader? TryParse(string baggage, bool onlySentry = false) { // Example from W3C baggage spec: // "key1=value1;property1;property2, key2 = value2, key3=value3; propertyKey=propertyValue" @@ -107,7 +107,7 @@ public override string ToString() return members.Count == 0 ? null : new BaggageHeader(members); } - public static BaggageHeader Create( + internal static BaggageHeader Create( IEnumerable> items, bool useSentryPrefix = false) { @@ -121,7 +121,7 @@ public static BaggageHeader Create( return new BaggageHeader(members); } - public static BaggageHeader Merge(IEnumerable baggageHeaders) => + internal static BaggageHeader Merge(IEnumerable baggageHeaders) => new(baggageHeaders.SelectMany(x => x.Members)); private static bool IsValidKey(string? key) diff --git a/src/Sentry/DynamicSamplingContext.cs b/src/Sentry/DynamicSamplingContext.cs index 40ffc2161e..c163de23c7 100644 --- a/src/Sentry/DynamicSamplingContext.cs +++ b/src/Sentry/DynamicSamplingContext.cs @@ -23,7 +23,7 @@ private DynamicSamplingContext( SentryId traceId, string publicKey, bool? sampled, - double sampleRate, + double? sampleRate = null, string? release = null, string? environment = null, string? userSegment = null, @@ -49,15 +49,19 @@ private DynamicSamplingContext( { ["trace_id"] = traceId.ToString(), ["public_key"] = publicKey, - ["sample_rate"] = sampleRate.ToString(CultureInfo.InvariantCulture) }; + // Set optional values if (sampled.HasValue) { items.Add("sampled", sampled.Value ? "true" : "false"); } - // Set optional values + if (sampleRate is not null) + { + items.Add("sample_rate", sampleRate.Value.ToString(CultureInfo.InvariantCulture)); + } + if (!string.IsNullOrWhiteSpace(release)) { items.Add("release", release); @@ -119,7 +123,7 @@ private DynamicSamplingContext( public static DynamicSamplingContext CreateFromTransaction(TransactionTracer transaction, SentryOptions options) { // These should already be set on the transaction. - var publicKey = Dsn.Parse(options.Dsn!).PublicKey; + var publicKey = options.ParsedDsn.PublicKey; var traceId = transaction.TraceId; var sampled = transaction.IsSampled; var sampleRate = transaction.SampleRate!.Value; @@ -140,6 +144,21 @@ public static DynamicSamplingContext CreateFromTransaction(TransactionTracer tra userSegment, transactionName); } + + public static DynamicSamplingContext CreateFromPropagationContext(SentryPropagationContext propagationContext, SentryOptions options) + { + var traceId = propagationContext.TraceId; + var publicKey = options.ParsedDsn.PublicKey; + var release = options.SettingLocator.GetRelease(); + var environment = options.SettingLocator.GetEnvironment(); + + return new DynamicSamplingContext( + traceId, + publicKey, + null, + release: release, + environment:environment); + } } internal static class DynamicSamplingContextExtensions @@ -149,4 +168,7 @@ internal static class DynamicSamplingContextExtensions public static DynamicSamplingContext CreateDynamicSamplingContext(this TransactionTracer transaction, SentryOptions options) => DynamicSamplingContext.CreateFromTransaction(transaction, options); + + public static DynamicSamplingContext CreateDynamicSamplingContext(this SentryPropagationContext propagationContext, SentryOptions options) + => DynamicSamplingContext.CreateFromPropagationContext(propagationContext, options); } diff --git a/src/Sentry/Extensibility/DisabledHub.cs b/src/Sentry/Extensibility/DisabledHub.cs index 109e5ef248..6ea3e66da2 100644 --- a/src/Sentry/Extensibility/DisabledHub.cs +++ b/src/Sentry/Extensibility/DisabledHub.cs @@ -74,6 +74,24 @@ public void BindException(Exception exception, ISpan span) ///
public SentryTraceHeader? GetTraceHeader() => null; + /// + /// Returns null. + /// + public BaggageHeader? GetBaggage() => null; + + /// + /// Returns null. + /// + public TransactionContext ContinueTrace( + SentryTraceHeader? traceHeader, + BaggageHeader? baggageHeader, + string? name = null, + string? operation = null) + { + // Transactions from DisabledHub are always sampled out + return new TransactionContext( name ?? string.Empty, operation ?? string.Empty, false); + } + /// /// No-Op. /// diff --git a/src/Sentry/Extensibility/HubAdapter.cs b/src/Sentry/Extensibility/HubAdapter.cs index bb1b5c8def..6989cd1ac9 100644 --- a/src/Sentry/Extensibility/HubAdapter.cs +++ b/src/Sentry/Extensibility/HubAdapter.cs @@ -106,6 +106,24 @@ public void BindException(Exception exception, ISpan span) => public SentryTraceHeader? GetTraceHeader() => SentrySdk.GetTraceHeader(); + /// + /// Forwards the call to . + /// + [DebuggerStepThrough] + public BaggageHeader? GetBaggage() + => SentrySdk.GetBaggage(); + + /// + /// Forwards the call to . + /// + [DebuggerStepThrough] + public TransactionContext ContinueTrace( + SentryTraceHeader? traceHeader, + BaggageHeader? baggageHeader, + string? name = null, + string? operation = null) + => SentrySdk.ContinueTrace(traceHeader, baggageHeader, name, operation); + /// /// Forwards the call to . /// diff --git a/src/Sentry/IHub.cs b/src/Sentry/IHub.cs index 894b01621a..570e60b499 100644 --- a/src/Sentry/IHub.cs +++ b/src/Sentry/IHub.cs @@ -40,10 +40,27 @@ ITransaction StartTransaction( ISpan? GetSpan(); /// - /// Gets the Sentry trace header for the last active span. + /// Gets the Sentry trace header that allows tracing across services /// SentryTraceHeader? GetTraceHeader(); + /// + /// Gets the Sentry baggage header that allows tracing across services + /// + BaggageHeader? GetBaggage(); + + /// + /// Continues a trace based on HTTP header values. + /// + /// + /// If no "sentry-trace" header is provided a random trace ID and span ID is created. + /// + TransactionContext ContinueTrace( + SentryTraceHeader? traceHeader, + BaggageHeader? baggageHeader, + string? name = null, + string? operation = null); + /// /// Starts a new session. /// diff --git a/src/Sentry/Internal/Hub.cs b/src/Sentry/Internal/Hub.cs index 68472df6e1..aec1a01567 100644 --- a/src/Sentry/Internal/Hub.cs +++ b/src/Sentry/Internal/Hub.cs @@ -195,7 +195,47 @@ public void BindException(Exception exception, ISpan span) public ISpan? GetSpan() => ScopeManager.GetCurrent().Key.Span; - public SentryTraceHeader? GetTraceHeader() => GetSpan()?.GetTraceHeader(); + public SentryTraceHeader GetTraceHeader() + { + if (GetSpan()?.GetTraceHeader() is { } traceHeader) + { + return traceHeader; + } + + // With either tracing disabled or no active span on the current scope we fall back to the propagation context + var propagationContext = ScopeManager.GetCurrent().Key.PropagationContext; + // In either case, we must not append a sampling decision. + return new SentryTraceHeader(propagationContext.TraceId, propagationContext.SpanId, null); + } + + public BaggageHeader GetBaggage() + { + if (GetSpan() is TransactionTracer { DynamicSamplingContext: { IsEmpty: false } dsc } ) + { + return dsc.ToBaggageHeader(); + } + + var propagationContext = ScopeManager.GetCurrent().Key.PropagationContext; + propagationContext.DynamicSamplingContext ??= propagationContext.CreateDynamicSamplingContext(_options); + return propagationContext.DynamicSamplingContext.ToBaggageHeader(); + } + + public TransactionContext ContinueTrace( + SentryTraceHeader? traceHeader, + BaggageHeader? baggageHeader, + string? name = null, + string? operation = null) + { + var propagationContext = SentryPropagationContext.CreateFromHeaders(_options.DiagnosticLogger, traceHeader, baggageHeader); + ConfigureScope(scope => scope.PropagationContext = propagationContext); + + // If we have to create a new SentryTraceHeader we don't make a sampling decision + traceHeader ??= new SentryTraceHeader(propagationContext.TraceId, propagationContext.SpanId, null); + return new TransactionContext( + name ?? string.Empty, + operation ?? string.Empty, + traceHeader); + } public void StartSession() { @@ -283,7 +323,7 @@ private void EndSession(DateTimeOffset timestamp, SessionEndStatus status) public void EndSession(SessionEndStatus status = SessionEndStatus.Exited) => EndSession(_clock.GetUtcNow(), status); - private ISpan? GetLinkedSpan(SentryEvent evt, Scope scope) + private ISpan? GetLinkedSpan(SentryEvent evt) { // Find the span which is bound to the same exception if (evt.Exception is { } exception && @@ -292,13 +332,29 @@ public void EndSession(SessionEndStatus status = SessionEndStatus.Exited) => return spanBoundToException; } - // Otherwise just get the currently active span on the scope (unless it's sampled out) - if (scope.Span is { IsSampled: not false } span) + return null; + } + + private void ApplyTraceContextToEvent(SentryEvent evt, ISpan span) + { + evt.Contexts.Trace.SpanId = span.SpanId; + evt.Contexts.Trace.TraceId = span.TraceId; + evt.Contexts.Trace.ParentSpanId = span.ParentSpanId; + + if (span.GetTransaction() is TransactionTracer transactionTracer) { - return span; + evt.DynamicSamplingContext = transactionTracer.DynamicSamplingContext; } + } - return null; + private void ApplyTraceContextToEvent(SentryEvent evt, SentryPropagationContext propagationContext) + { + evt.Contexts.Trace.TraceId = propagationContext.TraceId; + evt.Contexts.Trace.SpanId = propagationContext.SpanId; + evt.Contexts.Trace.ParentSpanId = propagationContext.ParentSpanId; + + propagationContext.DynamicSamplingContext ??= propagationContext.CreateDynamicSamplingContext(_options); + evt.DynamicSamplingContext = propagationContext.DynamicSamplingContext; } public SentryId CaptureEvent(SentryEvent evt, Action configureScope) => @@ -338,22 +394,20 @@ SentryId IHubEx.CaptureEventInternal(SentryEvent evt, Hint? hint, Scope? scope) ScopeManager.GetCurrent().Deconstruct(out var currentScope, out var sentryClient); var actualScope = scope ?? currentScope; - // Inject trace information from a linked span - if (GetLinkedSpan(evt, actualScope) is { } linkedSpan) + // We get the span linked to the event or fall back to the current span + var span = GetLinkedSpan(evt) ?? actualScope.Span; + if (span is not null) { - evt.Contexts.Trace.SpanId = linkedSpan.SpanId; - evt.Contexts.Trace.TraceId = linkedSpan.TraceId; - evt.Contexts.Trace.ParentSpanId = linkedSpan.ParentSpanId; - // Try to read the DSC from the transaction associated with the linked span - if (linkedSpan?.GetTransaction() is TransactionTracer transactionTracer) + if (span.IsSampled is not false) { - evt.DynamicSamplingContext = transactionTracer.DynamicSamplingContext; + ApplyTraceContextToEvent(evt, span); } } - - // Try to get the DSC from the transaction, if we didn't get it already from the linked span - var transaction = actualScope.Transaction as TransactionTracer; - evt.DynamicSamplingContext ??= transaction?.DynamicSamplingContext; + else + { + // If there is no span on the scope (and not just no sampled one), fall back to the propagation context + ApplyTraceContextToEvent(evt, actualScope.PropagationContext); + } // Now capture the event with the Sentry client on the current scope. var id = sentryClient.CaptureEvent(evt, hint, actualScope); @@ -365,7 +419,7 @@ SentryId IHubEx.CaptureEventInternal(SentryEvent evt, Hint? hint, Scope? scope) // Event contains a terminal exception -> finish any current transaction as aborted // Do this *after* the event was captured, so that the event is still linked to the transaction. _options.LogDebug("Ending transaction as Aborted, due to unhandled exception."); - transaction?.Finish(SpanStatus.Aborted); + actualScope.Transaction?.Finish(SpanStatus.Aborted); } return id; diff --git a/src/Sentry/Scope.cs b/src/Sentry/Scope.cs index e950d5eff8..94c52db9ae 100644 --- a/src/Sentry/Scope.cs +++ b/src/Sentry/Scope.cs @@ -191,6 +191,8 @@ public ITransaction? Transaction set => _transaction = value; } + internal SentryPropagationContext PropagationContext { get; set; } + internal SessionUpdate? SessionUpdate { get; set; } /// @@ -233,8 +235,14 @@ public ITransaction? Transaction /// Creates a scope with the specified options. ///
public Scope(SentryOptions? options) + : this(options, null) + { + } + + internal Scope(SentryOptions? options, SentryPropagationContext? propagationContext) { Options = options ?? new SentryOptions(); + PropagationContext = propagationContext ?? new SentryPropagationContext(); } // For testing. Should explicitly require SentryOptions. @@ -346,6 +354,7 @@ public void Clear() _extra.Clear(); _tags.Clear(); ClearAttachments(); + PropagationContext = new(); } /// @@ -476,7 +485,7 @@ public void Apply(Scope other) /// public Scope Clone() { - var clone = new Scope(Options) + var clone = new Scope(Options, PropagationContext) { OnEvaluating = OnEvaluating }; diff --git a/src/Sentry/SentryHttpMessageHandler.cs b/src/Sentry/SentryHttpMessageHandler.cs index 78ef415f52..7540d5866a 100644 --- a/src/Sentry/SentryHttpMessageHandler.cs +++ b/src/Sentry/SentryHttpMessageHandler.cs @@ -156,14 +156,12 @@ private void AddSentryTraceHeader(HttpRequestMessage request) private void AddBaggageHeader(HttpRequestMessage request) { - var transaction = _hub.GetSpan(); - if (transaction is not TransactionTracer {DynamicSamplingContext: {IsEmpty: false} dsc}) + var baggage = _hub.GetBaggage(); + if (baggage is null) { return; } - var baggage = dsc.ToBaggageHeader(); - if (request.Headers.TryGetValues(BaggageHeader.HttpHeaderName, out var baggageHeaders)) { var headers = baggageHeaders.ToList(); diff --git a/src/Sentry/SentryOptions.cs b/src/Sentry/SentryOptions.cs index 7dbc14e97b..8c2e2fb170 100644 --- a/src/Sentry/SentryOptions.cs +++ b/src/Sentry/SentryOptions.cs @@ -304,10 +304,21 @@ public float? SampleRate /// public string? Environment { get; set; } + private string? _dsn; /// /// The Data Source Name of a given project in Sentry. /// - public string? Dsn { get; set; } + public string? Dsn { + get => _dsn; + set + { + _dsn = value; + _parsedDsn = null; + } + } + + internal Dsn? _parsedDsn; + internal Dsn ParsedDsn => _parsedDsn ??= Sentry.Dsn.Parse(Dsn!); private readonly Lazy _sentryBaseUrl; diff --git a/src/Sentry/SentryPropagationContext.cs b/src/Sentry/SentryPropagationContext.cs new file mode 100644 index 0000000000..ac6a8803a0 --- /dev/null +++ b/src/Sentry/SentryPropagationContext.cs @@ -0,0 +1,58 @@ +using Sentry.Extensibility; + +namespace Sentry; + +internal class SentryPropagationContext +{ + public SentryId TraceId { get; } + public SpanId SpanId { get; } + public SpanId? ParentSpanId { get; } + + private DynamicSamplingContext? _dynamicSamplingContext; + public DynamicSamplingContext? DynamicSamplingContext + { + get => _dynamicSamplingContext; + set + { + if (_dynamicSamplingContext is null) + { + _dynamicSamplingContext = value; + } + else + { + throw new Exception("Attempted to set the DynamicSamplingContext but the context exists already."); + } + } + } + + internal SentryPropagationContext( + SentryId traceId, + SpanId parentSpanId, + DynamicSamplingContext? dynamicSamplingContext = null) + { + TraceId = traceId; + SpanId = SpanId.Create(); + ParentSpanId = parentSpanId; + DynamicSamplingContext = dynamicSamplingContext; + } + + public SentryPropagationContext() + { + TraceId = SentryId.Create(); + SpanId = SpanId.Create(); + } + + public static SentryPropagationContext CreateFromHeaders(IDiagnosticLogger? logger, SentryTraceHeader? traceHeader, BaggageHeader? baggageHeader) + { + logger?.LogDebug("Creating a propagation context from headers."); + + if (traceHeader == null) + { + logger?.LogInfo("Sentry trace header is null. Creating new Sentry Propagation Context."); + return new SentryPropagationContext(); + } + + var dynamicSamplingContext = baggageHeader?.CreateDynamicSamplingContext(); + return new SentryPropagationContext(traceHeader.TraceId, traceHeader.SpanId, dynamicSamplingContext); + } +} diff --git a/src/Sentry/SentrySdk.cs b/src/Sentry/SentrySdk.cs index 0ad66a5bed..723a220241 100644 --- a/src/Sentry/SentrySdk.cs +++ b/src/Sentry/SentrySdk.cs @@ -611,12 +611,33 @@ public static void BindException(Exception exception, ISpan span) => CurrentHub.GetSpan(); /// - /// Gets the Sentry trace header. + /// Gets the Sentry trace header of the parent that allows tracing across services /// [DebuggerStepThrough] public static SentryTraceHeader? GetTraceHeader() => CurrentHub.GetTraceHeader(); + /// + /// Gets the Sentry "baggage" header that allows tracing across services + /// + [DebuggerStepThrough] + public static BaggageHeader? GetBaggage() + => CurrentHub.GetBaggage(); + + /// + /// Continues a trace based on HTTP header values. + /// + /// + /// If no "sentry-trace" header is provided a random trace ID and span ID is created. + /// + [DebuggerStepThrough] + public static TransactionContext ContinueTrace( + SentryTraceHeader? traceHeader, + BaggageHeader? baggageHeader, + string? name = null, + string? operation = null) + => CurrentHub.ContinueTrace(traceHeader, baggageHeader, name, operation); + /// [DebuggerStepThrough] public static void StartSession() diff --git a/src/Sentry/SpanContext.cs b/src/Sentry/SpanContext.cs index 7369bcfa76..f55bc1521a 100644 --- a/src/Sentry/SpanContext.cs +++ b/src/Sentry/SpanContext.cs @@ -15,7 +15,7 @@ public class SpanContext : ISpanContext public SentryId TraceId { get; } /// - public string Operation { get; } + public string Operation { get; set; } /// public string? Description { get; } diff --git a/src/Sentry/TransactionContext.cs b/src/Sentry/TransactionContext.cs index fdc03c0e13..e3aee07680 100644 --- a/src/Sentry/TransactionContext.cs +++ b/src/Sentry/TransactionContext.cs @@ -6,10 +6,10 @@ namespace Sentry; public class TransactionContext : SpanContext, ITransactionContext, IHasTransactionNameSource { /// - public string Name { get; } + public string Name { get; set; } /// - public TransactionNameSource NameSource { get; } + public TransactionNameSource NameSource { get; set; } /// /// Whether the parent transaction of this transaction has been sampled. diff --git a/test/Sentry.AspNet.Tests/ApiApprovalTests.Run.Net4_8.verified.txt b/test/Sentry.AspNet.Tests/ApiApprovalTests.Run.Net4_8.verified.txt index c4c5a2d4b3..7980235cdf 100644 --- a/test/Sentry.AspNet.Tests/ApiApprovalTests.Run.Net4_8.verified.txt +++ b/test/Sentry.AspNet.Tests/ApiApprovalTests.Run.Net4_8.verified.txt @@ -3,6 +3,7 @@ public static class HttpContextExtensions { public static void FinishSentryTransaction(this System.Web.HttpContext httpContext) { } + public static void StartOrContinueTrace(this System.Web.HttpContext httpContext) { } public static Sentry.ITransaction StartSentryTransaction(this System.Web.HttpContext httpContext) { } } public static class SentryAspNetOptionsExtensions diff --git a/test/Sentry.AspNetCore.Tests/SentryMiddlewareTests.cs b/test/Sentry.AspNetCore.Tests/SentryMiddlewareTests.cs index 60c12d5833..ac3152aadf 100644 --- a/test/Sentry.AspNetCore.Tests/SentryMiddlewareTests.cs +++ b/test/Sentry.AspNetCore.Tests/SentryMiddlewareTests.cs @@ -2,6 +2,7 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; using Microsoft.Extensions.Logging; +using Sentry.AspNetCore.Extensions; #if NETCOREAPP3_1_OR_GREATER using IHostingEnvironment = Microsoft.AspNetCore.Hosting.IWebHostEnvironment; @@ -658,6 +659,73 @@ public async Task InvokeAsync_SetsEventIdOnEvent() _ = _fixture.Hub.Received().CaptureEvent(Arg.Is(e => e.EventId.Equals(eventId))); } + [Fact] + public async Task InvokeAsync_RequestContainsSentryHeaders_ContinuesTrace() + { + SentryTraceHeader capturedTraceHeader = null; + BaggageHeader capturedBaggageHeader = null; + _fixture.Hub.When(hub => hub.ContinueTrace(Arg.Any(), Arg.Any())) + .Do(callInfo => + { + capturedTraceHeader = callInfo.Arg(); + capturedBaggageHeader = callInfo.Arg(); + }); + + var sut = _fixture.GetSut(); + var request = Substitute.For(); + var fakeHeaders = new HeaderDictionary + { + { "Sentry-Trace", "4b4d2878507b43d3af7dd8c4ab7a96d9-3cc6fd1337d243de"}, + { "Baggage", "sentry-trace_id=4b4d2878507b43d3af7dd8c4ab7a96d9, sentry-public_key=eb18e953812b41c3aeb042e666fd3b5c"}, + }; + _ = request.Headers.Returns(fakeHeaders); + _ = _fixture.HttpContext.Request.Returns(request); + _ = request.HttpContext.Returns(_fixture.HttpContext); + + await sut.InvokeAsync(_fixture.HttpContext, _fixture.RequestDelegate); + + _fixture.Hub.Received().ContinueTrace(Arg.Any(),Arg.Any()); + + Assert.NotNull(capturedTraceHeader); + Assert.Equal("4b4d2878507b43d3af7dd8c4ab7a96d9", 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() + { + var sut = _fixture.GetSut(); + var request = Substitute.For(); + var fakeHeaders = new HeaderDictionary + { + { "Sentry-Trace", "4b4d2878507b43d3af7dd8c4ab7a96d9-3cc6fd1337d243de"}, + { "Baggage", "sentry-trace_id=4b4d2878507b43d3af7dd8c4ab7a96d9, sentry-public_key=eb18e953812b41c3aeb042e666fd3b5c"}, + }; + var contextItems = new Dictionary(); + _fixture.HttpContext.Items.When(items => items.Add(Arg.Any(), Arg.Any())) + .Do(info => + { + contextItems.Add(info.Args()[0], info.Args()[1]); + }); + _ = request.Headers.Returns(fakeHeaders); + _ = _fixture.HttpContext.Request.Returns(request); + _ = request.HttpContext.Returns(_fixture.HttpContext); + + await sut.InvokeAsync(_fixture.HttpContext, _fixture.RequestDelegate); + + _fixture.HttpContext.Items.Received(3); + + var traceHeader = contextItems[SentryMiddleware.TraceHeaderItemKey] as SentryTraceHeader; + Assert.NotNull(traceHeader); + Assert.Equal("4b4d2878507b43d3af7dd8c4ab7a96d9", 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()); + var transactionContext = contextItems[SentryMiddleware.BaggageHeaderItemKey] as BaggageHeader; + Assert.NotNull(transactionContext); + } + [Fact] public void PopulateScope_AddEventProcessors() { diff --git a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.LoggingAsync.DotNet6_0.verified.txt b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.LoggingAsync.DotNet6_0.verified.txt index 976514b4d6..19c3feaf15 100644 --- a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.LoggingAsync.DotNet6_0.verified.txt +++ b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.LoggingAsync.DotNet6_0.verified.txt @@ -16,7 +16,11 @@ WHERE @@ROWCOUNT = 1 AND [Id] = scope_identity(); Platform: csharp, Level: error, Request: {}, - Contexts: {}, + Contexts: { + trace: { + Operation: + } + }, User: {}, Breadcrumbs: [ { @@ -102,4 +106,4 @@ WHERE @@ROWCOUNT = 1 AND [Id] = scope_identity(); 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 index 976514b4d6..19c3feaf15 100644 --- a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.LoggingAsync.DotNet7_0.verified.txt +++ b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.LoggingAsync.DotNet7_0.verified.txt @@ -16,7 +16,11 @@ WHERE @@ROWCOUNT = 1 AND [Id] = scope_identity(); Platform: csharp, Level: error, Request: {}, - Contexts: {}, + Contexts: { + trace: { + Operation: + } + }, User: {}, Breadcrumbs: [ { @@ -102,4 +106,4 @@ WHERE @@ROWCOUNT = 1 AND [Id] = scope_identity(); IsFinished: true } } -] +] \ No newline at end of file diff --git a/test/Sentry.EntityFramework.Tests/IntegrationTests.verify.cs b/test/Sentry.EntityFramework.Tests/IntegrationTests.verify.cs index b01b35a1a5..0073167fc2 100644 --- a/test/Sentry.EntityFramework.Tests/IntegrationTests.verify.cs +++ b/test/Sentry.EntityFramework.Tests/IntegrationTests.verify.cs @@ -20,7 +20,8 @@ public async Task Simple() TracesSampleRate = 1, Transport = transport, Dsn = ValidDsn, - DiagnosticLevel = SentryLevel.Debug + DiagnosticLevel = SentryLevel.Debug, + Release = "test-release" }; options.AddEntityFramework(); diff --git a/test/Sentry.Log4Net.Tests/IntegrationTests.LoggingInsideTheContextOfLogging.verified.txt b/test/Sentry.Log4Net.Tests/IntegrationTests.LoggingInsideTheContextOfLogging.verified.txt index 47e68b9b82..4bfe6a72fe 100644 --- a/test/Sentry.Log4Net.Tests/IntegrationTests.LoggingInsideTheContextOfLogging.verified.txt +++ b/test/Sentry.Log4Net.Tests/IntegrationTests.LoggingInsideTheContextOfLogging.verified.txt @@ -5,6 +5,12 @@ event_id: Guid_1, sdk: { name: sentry.dotnet + }, + trace: { + environment: production, + public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, + release: test-release, + trace_id: Guid_2 } }, Items: [ @@ -21,7 +27,11 @@ Platform: csharp, Level: error, Request: {}, - Contexts: {}, + Contexts: { + trace: { + Operation: + } + }, User: {}, Environment: production } @@ -31,9 +41,15 @@ }, { Header: { - event_id: Guid_2, + event_id: Guid_3, sdk: { name: sentry.dotnet + }, + trace: { + environment: production, + public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, + release: test-release, + trace_id: Guid_2 } }, Items: [ @@ -50,7 +66,11 @@ Platform: csharp, Level: error, Request: {}, - Contexts: {}, + Contexts: { + trace: { + Operation: + } + }, User: {}, Environment: production } diff --git a/test/Sentry.Log4Net.Tests/IntegrationTests.Simple.verified.txt b/test/Sentry.Log4Net.Tests/IntegrationTests.Simple.verified.txt index 0f2dd62274..3efadb9332 100644 --- a/test/Sentry.Log4Net.Tests/IntegrationTests.Simple.verified.txt +++ b/test/Sentry.Log4Net.Tests/IntegrationTests.Simple.verified.txt @@ -4,6 +4,12 @@ event_id: Guid_1, sdk: { name: sentry.dotnet + }, + trace: { + environment: production, + public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, + release: test-release, + trace_id: Guid_2 } }, Items: [ @@ -20,7 +26,11 @@ Platform: csharp, Level: error, Request: {}, - Contexts: {}, + Contexts: { + trace: { + Operation: + } + }, User: {}, Environment: production } diff --git a/test/Sentry.Log4Net.Tests/IntegrationTests.verify.cs b/test/Sentry.Log4Net.Tests/IntegrationTests.verify.cs index 5ff4c59677..f6c08b4991 100644 --- a/test/Sentry.Log4Net.Tests/IntegrationTests.verify.cs +++ b/test/Sentry.Log4Net.Tests/IntegrationTests.verify.cs @@ -13,7 +13,8 @@ public async Task Simple() Transport = transport, DiagnosticLogger = diagnosticLogger, Dsn = ValidDsn, - AttachStacktrace = false + AttachStacktrace = false, + Release = "test-release" }; var hub = SentrySdk.InitHub(options); @@ -47,7 +48,8 @@ public async Task LoggingInsideTheContextOfLogging() DiagnosticLevel = SentryLevel.Debug, Transport = transport, Dsn = ValidDsn, - AttachStacktrace = false + AttachStacktrace = false, + Release = "test-release" }; var hub = SentrySdk.InitHub(options); diff --git a/test/Sentry.NLog.Tests/IntegrationTests.LoggingInsideTheContextOfLogging.verified.txt b/test/Sentry.NLog.Tests/IntegrationTests.LoggingInsideTheContextOfLogging.verified.txt index 9b2faf7f41..271399a591 100644 --- a/test/Sentry.NLog.Tests/IntegrationTests.LoggingInsideTheContextOfLogging.verified.txt +++ b/test/Sentry.NLog.Tests/IntegrationTests.LoggingInsideTheContextOfLogging.verified.txt @@ -11,6 +11,12 @@ event_id: Guid_1, sdk: { name: sentry.dotnet + }, + trace: { + environment: production, + public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, + release: test-release, + trace_id: Guid_2 } }, Items: [ @@ -28,7 +34,11 @@ Platform: csharp, Level: error, Request: {}, - Contexts: {}, + Contexts: { + trace: { + Operation: + } + }, User: {}, Environment: production } diff --git a/test/Sentry.NLog.Tests/IntegrationTests.Simple.Core3_1.verified.txt b/test/Sentry.NLog.Tests/IntegrationTests.Simple.Core3_1.verified.txt index 8a61fb4a81..8592621b82 100644 --- a/test/Sentry.NLog.Tests/IntegrationTests.Simple.Core3_1.verified.txt +++ b/test/Sentry.NLog.Tests/IntegrationTests.Simple.Core3_1.verified.txt @@ -4,6 +4,12 @@ event_id: Guid_1, sdk: { name: sentry.dotnet + }, + trace: { + environment: production, + public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, + release: test-release, + trace_id: Guid_2 } }, Items: [ @@ -34,7 +40,7 @@ FileName: IntegrationTests.verify.cs, Function: Task IntegrationTests.Simple(), Module: null, - LineNumber: 47, + LineNumber: 48, ColumnNumber: 17, AbsolutePath: {ProjectDirectory}IntegrationTests.verify.cs, ContextLine: null, @@ -72,7 +78,11 @@ ], Level: error, Request: {}, - Contexts: {}, + Contexts: { + trace: { + Operation: + } + }, User: { Id: myId, Username: , diff --git a/test/Sentry.NLog.Tests/IntegrationTests.Simple.DotNet6_0.verified.txt b/test/Sentry.NLog.Tests/IntegrationTests.Simple.DotNet6_0.verified.txt index 8a61fb4a81..8592621b82 100644 --- a/test/Sentry.NLog.Tests/IntegrationTests.Simple.DotNet6_0.verified.txt +++ b/test/Sentry.NLog.Tests/IntegrationTests.Simple.DotNet6_0.verified.txt @@ -4,6 +4,12 @@ event_id: Guid_1, sdk: { name: sentry.dotnet + }, + trace: { + environment: production, + public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, + release: test-release, + trace_id: Guid_2 } }, Items: [ @@ -34,7 +40,7 @@ FileName: IntegrationTests.verify.cs, Function: Task IntegrationTests.Simple(), Module: null, - LineNumber: 47, + LineNumber: 48, ColumnNumber: 17, AbsolutePath: {ProjectDirectory}IntegrationTests.verify.cs, ContextLine: null, @@ -72,7 +78,11 @@ ], Level: error, Request: {}, - Contexts: {}, + Contexts: { + trace: { + Operation: + } + }, User: { Id: myId, Username: , diff --git a/test/Sentry.NLog.Tests/IntegrationTests.Simple.DotNet7_0.verified.txt b/test/Sentry.NLog.Tests/IntegrationTests.Simple.DotNet7_0.verified.txt index 8a61fb4a81..8592621b82 100644 --- a/test/Sentry.NLog.Tests/IntegrationTests.Simple.DotNet7_0.verified.txt +++ b/test/Sentry.NLog.Tests/IntegrationTests.Simple.DotNet7_0.verified.txt @@ -4,6 +4,12 @@ event_id: Guid_1, sdk: { name: sentry.dotnet + }, + trace: { + environment: production, + public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, + release: test-release, + trace_id: Guid_2 } }, Items: [ @@ -34,7 +40,7 @@ FileName: IntegrationTests.verify.cs, Function: Task IntegrationTests.Simple(), Module: null, - LineNumber: 47, + LineNumber: 48, ColumnNumber: 17, AbsolutePath: {ProjectDirectory}IntegrationTests.verify.cs, ContextLine: null, @@ -72,7 +78,11 @@ ], Level: error, Request: {}, - Contexts: {}, + Contexts: { + trace: { + Operation: + } + }, User: { Id: myId, Username: , 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 ef9f70ec01..5b4398486b 100644 --- a/test/Sentry.NLog.Tests/IntegrationTests.Simple.Mono4_0.verified.txt +++ b/test/Sentry.NLog.Tests/IntegrationTests.Simple.Mono4_0.verified.txt @@ -4,6 +4,12 @@ event_id: Guid_1, sdk: { name: sentry.dotnet + }, + trace: { + environment: production, + public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, + release: test-release, + trace_id: Guid_2 } }, Items: [ @@ -72,7 +78,11 @@ ], Level: error, Request: {}, - Contexts: {}, + Contexts: { + trace: { + Operation: + } + }, User: { Id: myId, Username: , 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 8a61fb4a81..8592621b82 100644 --- a/test/Sentry.NLog.Tests/IntegrationTests.Simple.Net4_8.verified.txt +++ b/test/Sentry.NLog.Tests/IntegrationTests.Simple.Net4_8.verified.txt @@ -4,6 +4,12 @@ event_id: Guid_1, sdk: { name: sentry.dotnet + }, + trace: { + environment: production, + public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, + release: test-release, + trace_id: Guid_2 } }, Items: [ @@ -34,7 +40,7 @@ FileName: IntegrationTests.verify.cs, Function: Task IntegrationTests.Simple(), Module: null, - LineNumber: 47, + LineNumber: 48, ColumnNumber: 17, AbsolutePath: {ProjectDirectory}IntegrationTests.verify.cs, ContextLine: null, @@ -72,7 +78,11 @@ ], Level: error, Request: {}, - Contexts: {}, + Contexts: { + trace: { + Operation: + } + }, User: { Id: myId, Username: , diff --git a/test/Sentry.NLog.Tests/IntegrationTests.verify.cs b/test/Sentry.NLog.Tests/IntegrationTests.verify.cs index 1fb57eb86f..13cc47c8c4 100644 --- a/test/Sentry.NLog.Tests/IntegrationTests.verify.cs +++ b/test/Sentry.NLog.Tests/IntegrationTests.verify.cs @@ -20,6 +20,7 @@ public Task Simple() options.IncludeEventDataOnBreadcrumbs = true; options.MinimumBreadcrumbLevel = LogLevel.Debug; options.Dsn = ValidDsn; + options.Release = "test-release"; options.User = new SentryNLogUser { Id = "${scopeproperty:item=id}", @@ -76,6 +77,7 @@ public Task LoggingInsideTheContextOfLogging() options.Transport = transport; options.Dsn = ValidDsn; options.AttachStacktrace = false; + options.Release = "test-release"; }); LogManager.Configuration = configuration; diff --git a/test/Sentry.Serilog.Tests/IntegrationTests.LoggingInsideTheContextOfLogging.verified.txt b/test/Sentry.Serilog.Tests/IntegrationTests.LoggingInsideTheContextOfLogging.verified.txt index c8801dacd1..1a4cd367c2 100644 --- a/test/Sentry.Serilog.Tests/IntegrationTests.LoggingInsideTheContextOfLogging.verified.txt +++ b/test/Sentry.Serilog.Tests/IntegrationTests.LoggingInsideTheContextOfLogging.verified.txt @@ -11,6 +11,12 @@ event_id: Guid_1, sdk: { name: sentry.dotnet + }, + trace: { + environment: production, + public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, + release: test-release, + trace_id: Guid_2 } }, Items: [ @@ -27,7 +33,11 @@ Platform: csharp, Level: error, Request: {}, - Contexts: {}, + Contexts: { + trace: { + Operation: + } + }, User: {}, Environment: production } diff --git a/test/Sentry.Serilog.Tests/IntegrationTests.Simple.Core3_1.verified.txt b/test/Sentry.Serilog.Tests/IntegrationTests.Simple.Core3_1.verified.txt index ebf1fb5c3e..e7b68e6710 100644 --- a/test/Sentry.Serilog.Tests/IntegrationTests.Simple.Core3_1.verified.txt +++ b/test/Sentry.Serilog.Tests/IntegrationTests.Simple.Core3_1.verified.txt @@ -4,6 +4,12 @@ event_id: Guid_1, sdk: { name: sentry.dotnet + }, + trace: { + environment: production, + public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, + release: test-release, + trace_id: Guid_2 } }, Items: [ @@ -21,7 +27,11 @@ ServerName: TheMachineName, Level: debug, Request: {}, - Contexts: {}, + Contexts: { + trace: { + Operation: + } + }, User: { Username: TheUserName, IpAddress: {{auto}} @@ -38,9 +48,15 @@ }, { Header: { - event_id: Guid_2, + event_id: Guid_3, sdk: { name: sentry.dotnet + }, + trace: { + environment: production, + public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, + release: test-release, + trace_id: Guid_2 } }, Items: [ @@ -58,7 +74,11 @@ ServerName: TheMachineName, Level: debug, Request: {}, - Contexts: {}, + Contexts: { + trace: { + Operation: + } + }, User: { Username: TheUserName, IpAddress: {{auto}} @@ -81,9 +101,15 @@ }, { Header: { - event_id: Guid_3, + event_id: Guid_4, sdk: { name: sentry.dotnet + }, + trace: { + environment: production, + public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, + release: test-release, + trace_id: Guid_2 } }, Items: [ @@ -101,7 +127,11 @@ ServerName: TheMachineName, Level: error, Request: {}, - Contexts: {}, + Contexts: { + trace: { + Operation: + } + }, User: { Username: TheUserName, IpAddress: {{auto}} @@ -128,9 +158,15 @@ }, { Header: { - event_id: Guid_4, + event_id: Guid_5, sdk: { name: sentry.dotnet + }, + trace: { + environment: production, + public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, + release: test-release, + trace_id: Guid_2 } }, Items: [ @@ -164,7 +200,7 @@ FileName: IntegrationTests.verify.cs, Function: Task IntegrationTests.Simple(), Module: null, - LineNumber: 46, + LineNumber: 47, ColumnNumber: 17, AbsolutePath: {ProjectDirectory}IntegrationTests.verify.cs, ContextLine: null, @@ -205,7 +241,11 @@ ], Level: fatal, Request: {}, - Contexts: {}, + Contexts: { + trace: { + Operation: + } + }, User: { Username: TheUserName, IpAddress: {{auto}} diff --git a/test/Sentry.Serilog.Tests/IntegrationTests.Simple.DotNet6_0.verified.txt b/test/Sentry.Serilog.Tests/IntegrationTests.Simple.DotNet6_0.verified.txt index ebf1fb5c3e..e7b68e6710 100644 --- a/test/Sentry.Serilog.Tests/IntegrationTests.Simple.DotNet6_0.verified.txt +++ b/test/Sentry.Serilog.Tests/IntegrationTests.Simple.DotNet6_0.verified.txt @@ -4,6 +4,12 @@ event_id: Guid_1, sdk: { name: sentry.dotnet + }, + trace: { + environment: production, + public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, + release: test-release, + trace_id: Guid_2 } }, Items: [ @@ -21,7 +27,11 @@ ServerName: TheMachineName, Level: debug, Request: {}, - Contexts: {}, + Contexts: { + trace: { + Operation: + } + }, User: { Username: TheUserName, IpAddress: {{auto}} @@ -38,9 +48,15 @@ }, { Header: { - event_id: Guid_2, + event_id: Guid_3, sdk: { name: sentry.dotnet + }, + trace: { + environment: production, + public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, + release: test-release, + trace_id: Guid_2 } }, Items: [ @@ -58,7 +74,11 @@ ServerName: TheMachineName, Level: debug, Request: {}, - Contexts: {}, + Contexts: { + trace: { + Operation: + } + }, User: { Username: TheUserName, IpAddress: {{auto}} @@ -81,9 +101,15 @@ }, { Header: { - event_id: Guid_3, + event_id: Guid_4, sdk: { name: sentry.dotnet + }, + trace: { + environment: production, + public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, + release: test-release, + trace_id: Guid_2 } }, Items: [ @@ -101,7 +127,11 @@ ServerName: TheMachineName, Level: error, Request: {}, - Contexts: {}, + Contexts: { + trace: { + Operation: + } + }, User: { Username: TheUserName, IpAddress: {{auto}} @@ -128,9 +158,15 @@ }, { Header: { - event_id: Guid_4, + event_id: Guid_5, sdk: { name: sentry.dotnet + }, + trace: { + environment: production, + public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, + release: test-release, + trace_id: Guid_2 } }, Items: [ @@ -164,7 +200,7 @@ FileName: IntegrationTests.verify.cs, Function: Task IntegrationTests.Simple(), Module: null, - LineNumber: 46, + LineNumber: 47, ColumnNumber: 17, AbsolutePath: {ProjectDirectory}IntegrationTests.verify.cs, ContextLine: null, @@ -205,7 +241,11 @@ ], Level: fatal, Request: {}, - Contexts: {}, + Contexts: { + trace: { + Operation: + } + }, User: { Username: TheUserName, IpAddress: {{auto}} diff --git a/test/Sentry.Serilog.Tests/IntegrationTests.Simple.DotNet7_0.verified.txt b/test/Sentry.Serilog.Tests/IntegrationTests.Simple.DotNet7_0.verified.txt index ebf1fb5c3e..e7b68e6710 100644 --- a/test/Sentry.Serilog.Tests/IntegrationTests.Simple.DotNet7_0.verified.txt +++ b/test/Sentry.Serilog.Tests/IntegrationTests.Simple.DotNet7_0.verified.txt @@ -4,6 +4,12 @@ event_id: Guid_1, sdk: { name: sentry.dotnet + }, + trace: { + environment: production, + public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, + release: test-release, + trace_id: Guid_2 } }, Items: [ @@ -21,7 +27,11 @@ ServerName: TheMachineName, Level: debug, Request: {}, - Contexts: {}, + Contexts: { + trace: { + Operation: + } + }, User: { Username: TheUserName, IpAddress: {{auto}} @@ -38,9 +48,15 @@ }, { Header: { - event_id: Guid_2, + event_id: Guid_3, sdk: { name: sentry.dotnet + }, + trace: { + environment: production, + public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, + release: test-release, + trace_id: Guid_2 } }, Items: [ @@ -58,7 +74,11 @@ ServerName: TheMachineName, Level: debug, Request: {}, - Contexts: {}, + Contexts: { + trace: { + Operation: + } + }, User: { Username: TheUserName, IpAddress: {{auto}} @@ -81,9 +101,15 @@ }, { Header: { - event_id: Guid_3, + event_id: Guid_4, sdk: { name: sentry.dotnet + }, + trace: { + environment: production, + public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, + release: test-release, + trace_id: Guid_2 } }, Items: [ @@ -101,7 +127,11 @@ ServerName: TheMachineName, Level: error, Request: {}, - Contexts: {}, + Contexts: { + trace: { + Operation: + } + }, User: { Username: TheUserName, IpAddress: {{auto}} @@ -128,9 +158,15 @@ }, { Header: { - event_id: Guid_4, + event_id: Guid_5, sdk: { name: sentry.dotnet + }, + trace: { + environment: production, + public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, + release: test-release, + trace_id: Guid_2 } }, Items: [ @@ -164,7 +200,7 @@ FileName: IntegrationTests.verify.cs, Function: Task IntegrationTests.Simple(), Module: null, - LineNumber: 46, + LineNumber: 47, ColumnNumber: 17, AbsolutePath: {ProjectDirectory}IntegrationTests.verify.cs, ContextLine: null, @@ -205,7 +241,11 @@ ], Level: fatal, Request: {}, - Contexts: {}, + Contexts: { + trace: { + Operation: + } + }, User: { Username: TheUserName, IpAddress: {{auto}} diff --git a/test/Sentry.Serilog.Tests/IntegrationTests.Simple.Mono4_0.verified.txt b/test/Sentry.Serilog.Tests/IntegrationTests.Simple.Mono4_0.verified.txt index 92a4deb728..df00db54ed 100644 --- a/test/Sentry.Serilog.Tests/IntegrationTests.Simple.Mono4_0.verified.txt +++ b/test/Sentry.Serilog.Tests/IntegrationTests.Simple.Mono4_0.verified.txt @@ -4,6 +4,12 @@ event_id: Guid_1, sdk: { name: sentry.dotnet + }, + trace: { + environment: production, + public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, + release: test-release, + trace_id: Guid_2 } }, Items: [ @@ -21,7 +27,11 @@ ServerName: TheMachineName, Level: debug, Request: {}, - Contexts: {}, + Contexts: { + trace: { + Operation: + } + }, User: { Username: TheUserName, IpAddress: {{auto}} @@ -38,9 +48,15 @@ }, { Header: { - event_id: Guid_2, + event_id: Guid_3, sdk: { name: sentry.dotnet + }, + trace: { + environment: production, + public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, + release: test-release, + trace_id: Guid_2 } }, Items: [ @@ -58,7 +74,11 @@ ServerName: TheMachineName, Level: debug, Request: {}, - Contexts: {}, + Contexts: { + trace: { + Operation: + } + }, User: { Username: TheUserName, IpAddress: {{auto}} @@ -81,9 +101,15 @@ }, { Header: { - event_id: Guid_3, + event_id: Guid_4, sdk: { name: sentry.dotnet + }, + trace: { + environment: production, + public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, + release: test-release, + trace_id: Guid_2 } }, Items: [ @@ -101,7 +127,11 @@ ServerName: TheMachineName, Level: error, Request: {}, - Contexts: {}, + Contexts: { + trace: { + Operation: + } + }, User: { Username: TheUserName, IpAddress: {{auto}} @@ -128,9 +158,15 @@ }, { Header: { - event_id: Guid_4, + event_id: Guid_5, sdk: { name: sentry.dotnet + }, + trace: { + environment: production, + public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, + release: test-release, + trace_id: Guid_2 } }, Items: [ @@ -205,7 +241,11 @@ ], Level: fatal, Request: {}, - Contexts: {}, + Contexts: { + trace: { + Operation: + } + }, User: { Username: TheUserName, IpAddress: {{auto}} diff --git a/test/Sentry.Serilog.Tests/IntegrationTests.Simple.Net4_8.verified.txt b/test/Sentry.Serilog.Tests/IntegrationTests.Simple.Net4_8.verified.txt index ebf1fb5c3e..e7b68e6710 100644 --- a/test/Sentry.Serilog.Tests/IntegrationTests.Simple.Net4_8.verified.txt +++ b/test/Sentry.Serilog.Tests/IntegrationTests.Simple.Net4_8.verified.txt @@ -4,6 +4,12 @@ event_id: Guid_1, sdk: { name: sentry.dotnet + }, + trace: { + environment: production, + public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, + release: test-release, + trace_id: Guid_2 } }, Items: [ @@ -21,7 +27,11 @@ ServerName: TheMachineName, Level: debug, Request: {}, - Contexts: {}, + Contexts: { + trace: { + Operation: + } + }, User: { Username: TheUserName, IpAddress: {{auto}} @@ -38,9 +48,15 @@ }, { Header: { - event_id: Guid_2, + event_id: Guid_3, sdk: { name: sentry.dotnet + }, + trace: { + environment: production, + public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, + release: test-release, + trace_id: Guid_2 } }, Items: [ @@ -58,7 +74,11 @@ ServerName: TheMachineName, Level: debug, Request: {}, - Contexts: {}, + Contexts: { + trace: { + Operation: + } + }, User: { Username: TheUserName, IpAddress: {{auto}} @@ -81,9 +101,15 @@ }, { Header: { - event_id: Guid_3, + event_id: Guid_4, sdk: { name: sentry.dotnet + }, + trace: { + environment: production, + public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, + release: test-release, + trace_id: Guid_2 } }, Items: [ @@ -101,7 +127,11 @@ ServerName: TheMachineName, Level: error, Request: {}, - Contexts: {}, + Contexts: { + trace: { + Operation: + } + }, User: { Username: TheUserName, IpAddress: {{auto}} @@ -128,9 +158,15 @@ }, { Header: { - event_id: Guid_4, + event_id: Guid_5, sdk: { name: sentry.dotnet + }, + trace: { + environment: production, + public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, + release: test-release, + trace_id: Guid_2 } }, Items: [ @@ -164,7 +200,7 @@ FileName: IntegrationTests.verify.cs, Function: Task IntegrationTests.Simple(), Module: null, - LineNumber: 46, + LineNumber: 47, ColumnNumber: 17, AbsolutePath: {ProjectDirectory}IntegrationTests.verify.cs, ContextLine: null, @@ -205,7 +241,11 @@ ], Level: fatal, Request: {}, - Contexts: {}, + Contexts: { + trace: { + Operation: + } + }, User: { Username: TheUserName, IpAddress: {{auto}} diff --git a/test/Sentry.Serilog.Tests/IntegrationTests.verify.cs b/test/Sentry.Serilog.Tests/IntegrationTests.verify.cs index 7f9c8a45c6..f63f852fd2 100644 --- a/test/Sentry.Serilog.Tests/IntegrationTests.verify.cs +++ b/test/Sentry.Serilog.Tests/IntegrationTests.verify.cs @@ -23,6 +23,7 @@ public Task Simple() _.SendDefaultPii = true; _.TextFormatter = new MessageTemplateTextFormatter("[{MyTaskId}] {Message}"); _.AttachStacktrace = false; + _.Release = "test-release"; }); Log.Logger = configuration.CreateLogger(); @@ -76,6 +77,7 @@ public Task LoggingInsideTheContextOfLogging() _.Dsn = ValidDsn; _.Debug = true; _.AttachStacktrace = false; + _.Release = "test-release"; }); Log.Logger = configuration.CreateLogger(); diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.Core3_1.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.Core3_1.verified.txt index 7c4bb4c1d5..c11c70c6c4 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.Core3_1.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.Core3_1.verified.txt @@ -19,6 +19,10 @@ namespace Sentry UnrealLogs = 4, ViewHierarchy = 5, } + public class BaggageHeader + { + public override string ToString() { } + } [System.Diagnostics.DebuggerDisplay("Message: {Message}, Type: {Type}")] public sealed class Breadcrumb : Sentry.IJsonSerializable { @@ -225,7 +229,9 @@ namespace Sentry Sentry.SentryId LastEventId { get; } void BindException(System.Exception exception, Sentry.ISpan span); Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, System.Action configureScope); + Sentry.TransactionContext ContinueTrace(Sentry.SentryTraceHeader? traceHeader, Sentry.BaggageHeader? baggageHeader, string? name = null, string? operation = null); void EndSession(Sentry.SessionEndStatus status = 0); + Sentry.BaggageHeader? GetBaggage(); Sentry.ISpan? GetSpan(); Sentry.SentryTraceHeader? GetTraceHeader(); void PauseSession(); @@ -734,11 +740,13 @@ namespace Sentry 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 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() { } @@ -891,7 +899,7 @@ namespace Sentry public string? Description { get; } public Sentry.Instrumenter Instrumenter { get; } public bool? IsSampled { get; } - public string Operation { get; } + public string Operation { get; set; } public Sentry.SpanId? ParentSpanId { get; } public Sentry.SpanId SpanId { get; } public Sentry.SpanStatus? Status { get; } @@ -1050,8 +1058,8 @@ namespace Sentry public TransactionContext(Sentry.SpanId spanId, Sentry.SpanId? parentSpanId, Sentry.SentryId traceId, string name, string operation, string? description, Sentry.SpanStatus? status, bool? isSampled, bool? isParentSampled) { } public TransactionContext(Sentry.SpanId spanId, Sentry.SpanId? parentSpanId, Sentry.SentryId traceId, string name, string operation, string? description, Sentry.SpanStatus? status, bool? isSampled, bool? isParentSampled, Sentry.TransactionNameSource nameSource) { } public bool? IsParentSampled { get; } - public string Name { get; } - public Sentry.TransactionNameSource NameSource { get; } + public string Name { get; set; } + public Sentry.TransactionNameSource NameSource { get; set; } } public enum TransactionNameSource { @@ -1208,9 +1216,11 @@ namespace Sentry.Extensibility 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 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() { } @@ -1247,8 +1257,10 @@ namespace Sentry.Extensibility 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 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() { } diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt index e2a943d0d3..8b7b362331 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt @@ -19,6 +19,10 @@ namespace Sentry UnrealLogs = 4, ViewHierarchy = 5, } + public class BaggageHeader + { + public override string ToString() { } + } [System.Diagnostics.DebuggerDisplay("Message: {Message}, Type: {Type}")] public sealed class Breadcrumb : Sentry.IJsonSerializable { @@ -225,7 +229,9 @@ namespace Sentry Sentry.SentryId LastEventId { get; } void BindException(System.Exception exception, Sentry.ISpan span); Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, System.Action configureScope); + Sentry.TransactionContext ContinueTrace(Sentry.SentryTraceHeader? traceHeader, Sentry.BaggageHeader? baggageHeader, string? name = null, string? operation = null); void EndSession(Sentry.SessionEndStatus status = 0); + Sentry.BaggageHeader? GetBaggage(); Sentry.ISpan? GetSpan(); Sentry.SentryTraceHeader? GetTraceHeader(); void PauseSession(); @@ -735,11 +741,13 @@ namespace Sentry 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 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() { } @@ -892,7 +900,7 @@ namespace Sentry public string? Description { get; } public Sentry.Instrumenter Instrumenter { get; } public bool? IsSampled { get; } - public string Operation { get; } + public string Operation { get; set; } public Sentry.SpanId? ParentSpanId { get; } public Sentry.SpanId SpanId { get; } public Sentry.SpanStatus? Status { get; } @@ -1051,8 +1059,8 @@ namespace Sentry public TransactionContext(Sentry.SpanId spanId, Sentry.SpanId? parentSpanId, Sentry.SentryId traceId, string name, string operation, string? description, Sentry.SpanStatus? status, bool? isSampled, bool? isParentSampled) { } public TransactionContext(Sentry.SpanId spanId, Sentry.SpanId? parentSpanId, Sentry.SentryId traceId, string name, string operation, string? description, Sentry.SpanStatus? status, bool? isSampled, bool? isParentSampled, Sentry.TransactionNameSource nameSource) { } public bool? IsParentSampled { get; } - public string Name { get; } - public Sentry.TransactionNameSource NameSource { get; } + public string Name { get; set; } + public Sentry.TransactionNameSource NameSource { get; set; } } public enum TransactionNameSource { @@ -1209,9 +1217,11 @@ namespace Sentry.Extensibility 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 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() { } @@ -1248,8 +1258,10 @@ namespace Sentry.Extensibility 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 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() { } diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt index e2a943d0d3..8b7b362331 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt @@ -19,6 +19,10 @@ namespace Sentry UnrealLogs = 4, ViewHierarchy = 5, } + public class BaggageHeader + { + public override string ToString() { } + } [System.Diagnostics.DebuggerDisplay("Message: {Message}, Type: {Type}")] public sealed class Breadcrumb : Sentry.IJsonSerializable { @@ -225,7 +229,9 @@ namespace Sentry Sentry.SentryId LastEventId { get; } void BindException(System.Exception exception, Sentry.ISpan span); Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, System.Action configureScope); + Sentry.TransactionContext ContinueTrace(Sentry.SentryTraceHeader? traceHeader, Sentry.BaggageHeader? baggageHeader, string? name = null, string? operation = null); void EndSession(Sentry.SessionEndStatus status = 0); + Sentry.BaggageHeader? GetBaggage(); Sentry.ISpan? GetSpan(); Sentry.SentryTraceHeader? GetTraceHeader(); void PauseSession(); @@ -735,11 +741,13 @@ namespace Sentry 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 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() { } @@ -892,7 +900,7 @@ namespace Sentry public string? Description { get; } public Sentry.Instrumenter Instrumenter { get; } public bool? IsSampled { get; } - public string Operation { get; } + public string Operation { get; set; } public Sentry.SpanId? ParentSpanId { get; } public Sentry.SpanId SpanId { get; } public Sentry.SpanStatus? Status { get; } @@ -1051,8 +1059,8 @@ namespace Sentry public TransactionContext(Sentry.SpanId spanId, Sentry.SpanId? parentSpanId, Sentry.SentryId traceId, string name, string operation, string? description, Sentry.SpanStatus? status, bool? isSampled, bool? isParentSampled) { } public TransactionContext(Sentry.SpanId spanId, Sentry.SpanId? parentSpanId, Sentry.SentryId traceId, string name, string operation, string? description, Sentry.SpanStatus? status, bool? isSampled, bool? isParentSampled, Sentry.TransactionNameSource nameSource) { } public bool? IsParentSampled { get; } - public string Name { get; } - public Sentry.TransactionNameSource NameSource { get; } + public string Name { get; set; } + public Sentry.TransactionNameSource NameSource { get; set; } } public enum TransactionNameSource { @@ -1209,9 +1217,11 @@ namespace Sentry.Extensibility 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 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() { } @@ -1248,8 +1258,10 @@ namespace Sentry.Extensibility 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 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() { } diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt index ac94a57bac..05f94c169b 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt @@ -19,6 +19,10 @@ namespace Sentry UnrealLogs = 4, ViewHierarchy = 5, } + public class BaggageHeader + { + public override string ToString() { } + } [System.Diagnostics.DebuggerDisplay("Message: {Message}, Type: {Type}")] public sealed class Breadcrumb : Sentry.IJsonSerializable { @@ -224,7 +228,9 @@ namespace Sentry Sentry.SentryId LastEventId { get; } void BindException(System.Exception exception, Sentry.ISpan span); Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, System.Action configureScope); + Sentry.TransactionContext ContinueTrace(Sentry.SentryTraceHeader? traceHeader, Sentry.BaggageHeader? baggageHeader, string? name = null, string? operation = null); void EndSession(Sentry.SessionEndStatus status = 0); + Sentry.BaggageHeader? GetBaggage(); Sentry.ISpan? GetSpan(); Sentry.SentryTraceHeader? GetTraceHeader(); void PauseSession(); @@ -733,11 +739,13 @@ namespace Sentry 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 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() { } @@ -890,7 +898,7 @@ namespace Sentry public string? Description { get; } public Sentry.Instrumenter Instrumenter { get; } public bool? IsSampled { get; } - public string Operation { get; } + public string Operation { get; set; } public Sentry.SpanId? ParentSpanId { get; } public Sentry.SpanId SpanId { get; } public Sentry.SpanStatus? Status { get; } @@ -1049,8 +1057,8 @@ namespace Sentry public TransactionContext(Sentry.SpanId spanId, Sentry.SpanId? parentSpanId, Sentry.SentryId traceId, string name, string operation, string? description, Sentry.SpanStatus? status, bool? isSampled, bool? isParentSampled) { } public TransactionContext(Sentry.SpanId spanId, Sentry.SpanId? parentSpanId, Sentry.SentryId traceId, string name, string operation, string? description, Sentry.SpanStatus? status, bool? isSampled, bool? isParentSampled, Sentry.TransactionNameSource nameSource) { } public bool? IsParentSampled { get; } - public string Name { get; } - public Sentry.TransactionNameSource NameSource { get; } + public string Name { get; set; } + public Sentry.TransactionNameSource NameSource { get; set; } } public enum TransactionNameSource { @@ -1207,9 +1215,11 @@ namespace Sentry.Extensibility 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 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() { } @@ -1246,8 +1256,10 @@ namespace Sentry.Extensibility 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 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() { } diff --git a/test/Sentry.Tests/DynamicSamplingContextTests.cs b/test/Sentry.Tests/DynamicSamplingContextTests.cs index b2d55a093d..e1ffe9230c 100644 --- a/test/Sentry.Tests/DynamicSamplingContextTests.cs +++ b/test/Sentry.Tests/DynamicSamplingContextTests.cs @@ -281,4 +281,20 @@ public void CreateFromTransaction(bool? isSampled) Assert.Equal("Group A", Assert.Contains("user_segment", dsc.Items)); Assert.Equal("GET /person/{id}", Assert.Contains("transaction", dsc.Items)); } + + [Fact] + public void CreateFromPropagationContext_Valid_Complete() + { + 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); + + 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)); + } } diff --git a/test/Sentry.Tests/EventProcessorTests.Simple.verified.txt b/test/Sentry.Tests/EventProcessorTests.Simple.verified.txt index 79df0cca81..2109bcb4f8 100644 --- a/test/Sentry.Tests/EventProcessorTests.Simple.verified.txt +++ b/test/Sentry.Tests/EventProcessorTests.Simple.verified.txt @@ -1,9 +1,15 @@ -[ +[ { Header: { event_id: Guid_1, sdk: { name: sentry.dotnet + }, + trace: { + environment: production, + public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, + release: release, + trace_id: Guid_2 } }, Items: [ @@ -20,7 +26,10 @@ Level: info, Request: {}, Contexts: { - key: value + key: value, + trace: { + Operation: + } }, User: {}, Environment: production diff --git a/test/Sentry.Tests/HubTests.cs b/test/Sentry.Tests/HubTests.cs index 56aef686a3..900c415ed5 100644 --- a/test/Sentry.Tests/HubTests.cs +++ b/test/Sentry.Tests/HubTests.cs @@ -218,11 +218,12 @@ public void CaptureException_ActiveSpanExistsOnScopeButIsSampledOut_EventIsNotLi } [Fact] - public void CaptureException_NoActiveSpanAndNoSpanBoundToSameException_EventIsNotLinkedToSpan() + public void CaptureException_NoActiveSpanAndNoSpanBoundToSameException_EventContainsPropagationContext() { // Arrange _fixture.Options.TracesSampleRate = 1.0; var hub = _fixture.GetSut(); + var scope = hub.ScopeManager.GetCurrent().Key; // Act hub.CaptureException(new Exception("error")); @@ -230,8 +231,8 @@ public void CaptureException_NoActiveSpanAndNoSpanBoundToSameException_EventIsNo // Assert _fixture.Client.Received(1).CaptureEvent( Arg.Is(evt => - evt.Contexts.Trace.TraceId == default && - evt.Contexts.Trace.SpanId == default), + evt.Contexts.Trace.TraceId == scope.PropagationContext.TraceId && + evt.Contexts.Trace.SpanId == scope.PropagationContext.SpanId), Arg.Any(), Arg.Any()); } @@ -849,6 +850,94 @@ public void GetTraceHeader_ReturnsHeaderForActiveSpan() }); } + [Fact] + public void GetTraceHeader_NoSpanActive_ReturnsHeaderFromPropagationContext() + { + // Arrange + var hub = _fixture.GetSut(); + var propagationContext = new SentryPropagationContext( + SentryId.Parse("75302ac48a024bde9a3b3734a82e36c8"), + SpanId.Parse("2000000000000000")); + hub.ConfigureScope(scope => scope.PropagationContext = propagationContext); + + // Act + var header = hub.GetTraceHeader(); + + // Assert + header.Should().NotBeNull(); + header.SpanId.Should().Be(propagationContext.SpanId); + header.TraceId.Should().Be(propagationContext.TraceId); + header.IsSampled.Should().BeNull(); + } + + [Fact] + public void GetBaggage_SpanActive_ReturnsBaggageFromSpan() + { + // Arrange + var hub = _fixture.GetSut(); + var transaction = hub.StartTransaction("test-name", "_"); + + // Act + hub.WithScope(scope => + { + scope.Transaction = transaction; + + var baggage = hub.GetBaggage(); + + // Assert + baggage.Should().NotBeNull(); + Assert.Contains("test-name", baggage!.ToString()); + }); + } + + [Fact] + public void GetBaggage_NoSpanActive_ReturnsBaggageFromPropagationContext() + { + // Arrange + var hub = _fixture.GetSut(); + var propagationContext = new SentryPropagationContext( + SentryId.Parse("43365712692146d08ee11a729dfbcaca"), SpanId.Parse("1000000000000000")); + hub.ConfigureScope(scope => scope.PropagationContext = propagationContext); + + // Act + var baggage = hub.GetBaggage(); + + // Assert + baggage.Should().NotBeNull(); + Assert.Contains("43365712692146d08ee11a729dfbcaca", baggage!.ToString()); + } + + [Fact] + public void ContinueTrace_SetsPropagationContextAndReturnsTransactionContext() + { + // Arrange + var hub = _fixture.GetSut(); + var propagationContext = new SentryPropagationContext( + SentryId.Parse("43365712692146d08ee11a729dfbcaca"), SpanId.Parse("1000000000000000")); + hub.ConfigureScope(scope => scope.PropagationContext = propagationContext); + var traceHeader = new SentryTraceHeader(SentryId.Parse("5bd5f6d346b442dd9177dce9302fd737"), + SpanId.Parse("2000000000000000"), null); + var baggageHeader = BaggageHeader.Create(new List> + { + {"sentry-public_key", "49d0f7386ad645858ae85020e393bef3"} + }); + + hub.ConfigureScope(scope => scope.PropagationContext.TraceId.Should().Be("43365712692146d08ee11a729dfbcaca")); // Sanity check + + // Act + var transactionContext = hub.ContinueTrace(traceHeader, baggageHeader, "test-name"); + + // Assert + hub.ConfigureScope(scope => + { + scope.PropagationContext.TraceId.Should().Be(SentryId.Parse("5bd5f6d346b442dd9177dce9302fd737")); + scope.PropagationContext.ParentSpanId.Should().Be(SpanId.Parse("2000000000000000")); + }); + + transactionContext.TraceId.Should().Be(SentryId.Parse("5bd5f6d346b442dd9177dce9302fd737")); + transactionContext.ParentSpanId.Should().Be(SpanId.Parse("2000000000000000")); + } + [Fact] public void CaptureTransaction_AfterTransactionFinishes_ResetsTransactionOnScope() { diff --git a/test/Sentry.Tests/ScopeTests.cs b/test/Sentry.Tests/ScopeTests.cs index 44052c4c92..a55efc3ecf 100644 --- a/test/Sentry.Tests/ScopeTests.cs +++ b/test/Sentry.Tests/ScopeTests.cs @@ -89,6 +89,18 @@ public void Clone_NewScope_IncludesOptions() Assert.Same(options, clone.Options); } + [Fact] + public void Clone_NewScope_IncludesPropagationContext() + { + var options = new SentryOptions(); + var propagationContext = new SentryPropagationContext(); + var sut = new Scope(options, propagationContext); + + var clone = sut.Clone(); + + Assert.Same(propagationContext, clone.PropagationContext); + } + [Fact] public void Clone_CopiesFields() { @@ -309,6 +321,18 @@ public void Clear_SetsPropertiesToDefaultValues() } } + [Fact] + public void Clear_ResetsPropagationContext() + { + var options = new SentryOptions(); + var propagationContext = new SentryPropagationContext(); + var sut = new Scope(options, propagationContext); + + sut.Clear(); + + Assert.NotSame(propagationContext, sut.PropagationContext); + } + [Fact] public void ClearAttachments_HasAttachments_EmptyList() { diff --git a/test/Sentry.Tests/SentryOptionsTests.cs b/test/Sentry.Tests/SentryOptionsTests.cs index ba17d07dfa..5e29918860 100644 --- a/test/Sentry.Tests/SentryOptionsTests.cs +++ b/test/Sentry.Tests/SentryOptionsTests.cs @@ -240,4 +240,33 @@ public void IsSentryRequest_WithValidUri_ReturnsTrue() Assert.True(actual); } + + [Fact] + public void ParseDsn_ReturnsParsedDsn() + { + var sut = new SentryOptions + { + Dsn = "https://123@456.ingest.sentry.io/789" + }; + var expected = Dsn.Parse(sut.Dsn); + + var actual = sut.ParsedDsn; + + Assert.Equal(expected.Source, actual.Source); + } + + [Fact] + public void ParseDsn_DsnIsSetAgain_Resets() + { + var sut = new SentryOptions + { + Dsn = "https://123@456.ingest.sentry.io/789" + }; + + _ = sut.ParsedDsn; + Assert.NotNull(sut._parsedDsn); // Sanity check + sut.Dsn = "some-other-dsn"; + + Assert.Null(sut._parsedDsn); + } } diff --git a/test/Sentry.Tests/SentryPropagationContextTests.cs b/test/Sentry.Tests/SentryPropagationContextTests.cs new file mode 100644 index 0000000000..1ba3383e1a --- /dev/null +++ b/test/Sentry.Tests/SentryPropagationContextTests.cs @@ -0,0 +1,38 @@ +namespace Sentry.Tests; + +public class SentryPropagationContextTests +{ + [Fact] + public void CreateFromHeaders_TraceHeaderNullButBaggageExists_CreatesPropagationContextWithoutDynamicSamplingContext() + { + var baggageHeader = BaggageHeader.Create(new List> + { + { "sentry-sample_rate", "1.0" }, + { "sentry-trace_id", "75302ac48a024bde9a3b3734a82e36c8" }, + { "sentry-public_key", "d4d82fc1c2c4032a83f3a29aa3a3aff" }, + { "sentry-replay_id", "bfd31b89a59d41c99d96dc2baf840ecd" } + }); + + var propagationContext = SentryPropagationContext.CreateFromHeaders(null, null, baggageHeader); + + Assert.Null(propagationContext.DynamicSamplingContext); + } + + [Fact] + public void CreateFromHeaders_BaggageHeaderNotNull_CreatesPropagationContextWithDynamicSamplingContext() + { + var traceHeader = new SentryTraceHeader(SentryId.Create(), SpanId.Create(), null); + var baggageHeader = BaggageHeader.Create(new List> + { + { "sentry-sample_rate", "1.0" }, + { "sentry-trace_id", "75302ac48a024bde9a3b3734a82e36c8" }, + { "sentry-public_key", "d4d82fc1c2c4032a83f3a29aa3a3aff" }, + { "sentry-replay_id", "bfd31b89a59d41c99d96dc2baf840ecd" } + }); + + var propagationContext = SentryPropagationContext.CreateFromHeaders(null, traceHeader, baggageHeader); + + Assert.NotNull(propagationContext.DynamicSamplingContext); + Assert.Equal(4, propagationContext.DynamicSamplingContext.Items.Count); + } +} From 6df138761d4b0ec33a6688039a9cebddb33c5efd Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 4 Aug 2023 10:41:33 +0200 Subject: [PATCH 136/142] chore: update modules/sentry-cocoa to 8.9.4 (#2531) --- CHANGELOG.md | 6 +++--- modules/sentry-cocoa | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 44d645ed6e..e546bcf103 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,9 +28,9 @@ - Bump CLI from v2.19.4 to v2.20.3 ([#2509](https://github.com/getsentry/sentry-dotnet/pull/2509), [#2518](https://github.com/getsentry/sentry-dotnet/pull/2518), [#2527](https://github.com/getsentry/sentry-dotnet/pull/2527)) - [changelog](https://github.com/getsentry/sentry-cli/blob/master/CHANGELOG.md#2203) - [diff](https://github.com/getsentry/sentry-cli/compare/2.19.4...2.20.3) -- Bump Cocoa SDK from v8.8.0 to v8.9.3 ([#2479](https://github.com/getsentry/sentry-dotnet/pull/2479), [#2483](https://github.com/getsentry/sentry-dotnet/pull/2483), [#2500](https://github.com/getsentry/sentry-dotnet/pull/2500), [#2510](https://github.com/getsentry/sentry-dotnet/pull/2510)) - - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#893) - - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.8.0...8.9.3) +- Bump Cocoa SDK from v8.8.0 to v8.9.4 ([#2479](https://github.com/getsentry/sentry-dotnet/pull/2479), [#2483](https://github.com/getsentry/sentry-dotnet/pull/2483), [#2500](https://github.com/getsentry/sentry-dotnet/pull/2500), [#2510](https://github.com/getsentry/sentry-dotnet/pull/2510), [#2531](https://github.com/getsentry/sentry-dotnet/pull/2531)) + - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#894) + - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.8.0...8.9.4) ## 3.34.0 diff --git a/modules/sentry-cocoa b/modules/sentry-cocoa index 259d8bc75a..04bee4ad86 160000 --- a/modules/sentry-cocoa +++ b/modules/sentry-cocoa @@ -1 +1 @@ -Subproject commit 259d8bc75aa4028416535d35840ff19fc7661292 +Subproject commit 04bee4ad86d74d4cb4d7101ff826d6e355301ba9 From 815f6cb066a707b2af273ceba56e456f9073b498 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 7 Aug 2023 11:14:01 +1200 Subject: [PATCH 137/142] chore: update scripts/update-cli.ps1 to 2.20.4 (#2530) --- 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 e546bcf103..899d04bacc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,9 +25,9 @@ - Bump Java SDK from v6.25.1 to v6.27.0 ([#2484](https://github.com/getsentry/sentry-dotnet/pull/2484), [#2498](https://github.com/getsentry/sentry-dotnet/pull/2498), [#2517](https://github.com/getsentry/sentry-dotnet/pull/2517)) - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#6270) - [diff](https://github.com/getsentry/sentry-java/compare/6.25.1...6.27.0) -- Bump CLI from v2.19.4 to v2.20.3 ([#2509](https://github.com/getsentry/sentry-dotnet/pull/2509), [#2518](https://github.com/getsentry/sentry-dotnet/pull/2518), [#2527](https://github.com/getsentry/sentry-dotnet/pull/2527)) - - [changelog](https://github.com/getsentry/sentry-cli/blob/master/CHANGELOG.md#2203) - - [diff](https://github.com/getsentry/sentry-cli/compare/2.19.4...2.20.3) +- Bump CLI from v2.19.4 to v2.20.4 ([#2509](https://github.com/getsentry/sentry-dotnet/pull/2509), [#2518](https://github.com/getsentry/sentry-dotnet/pull/2518), [#2527](https://github.com/getsentry/sentry-dotnet/pull/2527), [#2530](https://github.com/getsentry/sentry-dotnet/pull/2530)) + - [changelog](https://github.com/getsentry/sentry-cli/blob/master/CHANGELOG.md#2204) + - [diff](https://github.com/getsentry/sentry-cli/compare/2.19.4...2.20.4) - Bump Cocoa SDK from v8.8.0 to v8.9.4 ([#2479](https://github.com/getsentry/sentry-dotnet/pull/2479), [#2483](https://github.com/getsentry/sentry-dotnet/pull/2483), [#2500](https://github.com/getsentry/sentry-dotnet/pull/2500), [#2510](https://github.com/getsentry/sentry-dotnet/pull/2510), [#2531](https://github.com/getsentry/sentry-dotnet/pull/2531)) - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#894) - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.8.0...8.9.4) diff --git a/Directory.Build.props b/Directory.Build.props index e0886e7a75..c261e583f5 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -100,7 +100,7 @@ - 2.20.3 + 2.20.4 $(MSBuildThisFileDirectory)tools\sentry-cli\$(SentryCLIVersion)\ diff --git a/src/Sentry/Sentry.csproj b/src/Sentry/Sentry.csproj index 2d850d14fd..cf0c003448 100644 --- a/src/Sentry/Sentry.csproj +++ b/src/Sentry/Sentry.csproj @@ -96,25 +96,25 @@ + Include="sentry-cli-Darwin-arm64" FileHash="25f1ff796b349a133cd3c28ac12afd89e6cd849863a66994e6958a93880cdc26" /> + Include="sentry-cli-Darwin-x86_64" FileHash="04f4b832cba392ff275c67c26315ac218450fd0dbd654b0bf94a8db31575db9b" /> + Include="sentry-cli-Linux-aarch64" FileHash="0c67bc6edd8d3c205980a5082c549099f8d84eed0516028dcdcddbdf1d20d9a7" /> + Include="sentry-cli-Linux-i686" FileHash="24bf81e4ff3ed71af756d1a6ade905f9a885a91bfccf7160c458651798caa491" /> + Include="sentry-cli-Linux-x86_64" FileHash="dde8e494a4178143417a2a208559493ad7954be2a6aecf2195c21d36468b53a1" /> + Include="sentry-cli-Windows-i686.exe" FileHash="5f10392c11d3a0e6ca98a8e85a0741f5b1e798d5b58ee09a923bd5fd2b70f449" /> + Include="sentry-cli-Windows-x86_64.exe" FileHash="4e7dfde7b0d54a755755f2b3133ec1c11b5f058883025849b113fa5ef7a95df5" /> From a5e5bb2460a5ef22c3d2a057e0c6c868577ecf2b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 7 Aug 2023 14:59:04 +0200 Subject: [PATCH 138/142] chore: update scripts/update-java.ps1 to 6.28.0 (#2533) --- CHANGELOG.md | 6 +++--- src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 899d04bacc..e39582b489 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,9 +22,9 @@ ### Dependencies -- Bump Java SDK from v6.25.1 to v6.27.0 ([#2484](https://github.com/getsentry/sentry-dotnet/pull/2484), [#2498](https://github.com/getsentry/sentry-dotnet/pull/2498), [#2517](https://github.com/getsentry/sentry-dotnet/pull/2517)) - - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#6270) - - [diff](https://github.com/getsentry/sentry-java/compare/6.25.1...6.27.0) +- Bump Java SDK from v6.25.1 to v6.28.0 ([#2484](https://github.com/getsentry/sentry-dotnet/pull/2484), [#2498](https://github.com/getsentry/sentry-dotnet/pull/2498), [#2517](https://github.com/getsentry/sentry-dotnet/pull/2517), [#2533](https://github.com/getsentry/sentry-dotnet/pull/2533)) + - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#6280) + - [diff](https://github.com/getsentry/sentry-java/compare/6.25.1...6.28.0) - Bump CLI from v2.19.4 to v2.20.4 ([#2509](https://github.com/getsentry/sentry-dotnet/pull/2509), [#2518](https://github.com/getsentry/sentry-dotnet/pull/2518), [#2527](https://github.com/getsentry/sentry-dotnet/pull/2527), [#2530](https://github.com/getsentry/sentry-dotnet/pull/2530)) - [changelog](https://github.com/getsentry/sentry-cli/blob/master/CHANGELOG.md#2204) - [diff](https://github.com/getsentry/sentry-cli/compare/2.19.4...2.20.4) diff --git a/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj b/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj index 75fd1e1ed0..876f3672f7 100644 --- a/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj +++ b/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj @@ -3,7 +3,7 @@ net6.0-android $(NoWarn);BG8605;BG8606 - 6.27.0 + 6.28.0 $(BaseIntermediateOutputPath)sdks\Sentry\Android\$(SentryAndroidSdkVersion)\ .NET Bindings for the Sentry Android SDK From 4ab03a68da03f857b781ca32a72565a3c06e4356 Mon Sep 17 00:00:00 2001 From: getsentry-bot Date: Tue, 8 Aug 2023 16:07:32 +0000 Subject: [PATCH 139/142] release: 3.35.0 --- CHANGELOG.md | 2 +- Directory.Build.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e39582b489..b2b32b8672 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## Unreleased +## 3.35.0 ### Features diff --git a/Directory.Build.props b/Directory.Build.props index c261e583f5..bfcf76c398 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,7 +1,7 @@ - 3.34.0 + 3.35.0 11 true $(MSBuildThisFileDirectory).assets\Sentry.snk From d584fea1738278396b733b4ef52a675f829666d7 Mon Sep 17 00:00:00 2001 From: Stefan Jandl Date: Fri, 11 Aug 2023 01:31:41 +0200 Subject: [PATCH 140/142] Internal: Renamed `IsTracingEnabled` to `IsPerformanceMonitoringEnabled` in SentryOptions (#2528) --- .../SentryDiagnosticListenerIntegration.cs | 2 +- .../DbInterceptionIntegration.cs | 2 +- src/Sentry/Platforms/Android/SentrySdk.cs | 2 +- src/Sentry/Platforms/iOS/SentrySdk.cs | 2 +- src/Sentry/SentryOptions.cs | 4 +- test/Sentry.Tests/SentryOptionsTests.cs | 40 +++++++++---------- 6 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/SentryDiagnosticListenerIntegration.cs b/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/SentryDiagnosticListenerIntegration.cs index 0c36ae501f..e6328e3835 100644 --- a/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/SentryDiagnosticListenerIntegration.cs +++ b/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/SentryDiagnosticListenerIntegration.cs @@ -7,7 +7,7 @@ internal class SentryDiagnosticListenerIntegration : ISdkIntegration { public void Register(IHub hub, SentryOptions options) { - if (!options.IsTracingEnabled) + if (!options.IsPerformanceMonitoringEnabled) { options.Log(SentryLevel.Info, "DiagnosticSource Integration is disabled because tracing is disabled."); options.DisableDiagnosticSourceIntegration(); diff --git a/src/Sentry.EntityFramework/DbInterceptionIntegration.cs b/src/Sentry.EntityFramework/DbInterceptionIntegration.cs index c9a676f433..bf561ef039 100644 --- a/src/Sentry.EntityFramework/DbInterceptionIntegration.cs +++ b/src/Sentry.EntityFramework/DbInterceptionIntegration.cs @@ -9,7 +9,7 @@ internal class DbInterceptionIntegration : ISdkIntegration public void Register(IHub hub, SentryOptions options) { - if (!options.IsTracingEnabled) + if (!options.IsPerformanceMonitoringEnabled) { options.DiagnosticLogger?.LogInfo(SampleRateDisabledMessage); } diff --git a/src/Sentry/Platforms/Android/SentrySdk.cs b/src/Sentry/Platforms/Android/SentrySdk.cs index 09019f418a..942facc124 100644 --- a/src/Sentry/Platforms/Android/SentrySdk.cs +++ b/src/Sentry/Platforms/Android/SentrySdk.cs @@ -115,7 +115,7 @@ private static void InitSentryAndroidSdk(SentryOptions options) } // These options we have behind feature flags - if (options is {IsTracingEnabled: true, Android.EnableAndroidSdkTracing: true}) + if (options is {IsPerformanceMonitoringEnabled: true, Android.EnableAndroidSdkTracing: true}) { o.EnableTracing = (JavaBoolean?)options.EnableTracing; o.TracesSampleRate = (JavaDouble?)options.TracesSampleRate; diff --git a/src/Sentry/Platforms/iOS/SentrySdk.cs b/src/Sentry/Platforms/iOS/SentrySdk.cs index 7a79d04d5c..729bdb37f6 100644 --- a/src/Sentry/Platforms/iOS/SentrySdk.cs +++ b/src/Sentry/Platforms/iOS/SentrySdk.cs @@ -69,7 +69,7 @@ private static void InitSentryCocoaSdk(SentryOptions options) } // These options we have behind feature flags - if (options is {IsTracingEnabled: true, iOS.EnableCocoaSdkTracing: true}) + if (options is {IsPerformanceMonitoringEnabled: true, iOS.EnableCocoaSdkTracing: true}) { if (options.EnableTracing != null) { diff --git a/src/Sentry/SentryOptions.cs b/src/Sentry/SentryOptions.cs index 8c2e2fb170..5adcf76661 100644 --- a/src/Sentry/SentryOptions.cs +++ b/src/Sentry/SentryOptions.cs @@ -716,10 +716,10 @@ public IList FailedRequestTargets { public Dictionary DefaultTags => _defaultTags ??= new Dictionary(); /// - /// Indicates whether tracing is enabled, via any combination of + /// Indicates whether the performance feature is enabled, via any combination of /// , , or . /// - internal bool IsTracingEnabled => EnableTracing switch + internal bool IsPerformanceMonitoringEnabled => EnableTracing switch { false => false, null => TracesSampler is not null || TracesSampleRate is > 0.0, diff --git a/test/Sentry.Tests/SentryOptionsTests.cs b/test/Sentry.Tests/SentryOptionsTests.cs index 5e29918860..9edea7e0e4 100644 --- a/test/Sentry.Tests/SentryOptionsTests.cs +++ b/test/Sentry.Tests/SentryOptionsTests.cs @@ -52,58 +52,58 @@ public void TracesSampler_Default_Null() } [Fact] - public void IsTracingEnabled_Default_False() + public void IsPerformanceMonitoringEnabled_Default_False() { var sut = new SentryOptions(); - Assert.False(sut.IsTracingEnabled); + Assert.False(sut.IsPerformanceMonitoringEnabled); } [Fact] - public void IsTracingEnabled_EnableTracing_True() + public void IsPerformanceMonitoringEnabled_EnableTracing_True() { var sut = new SentryOptions { EnableTracing = true }; - Assert.True(sut.IsTracingEnabled); + Assert.True(sut.IsPerformanceMonitoringEnabled); } [Fact] - public void IsTracingEnabled_EnableTracing_False() + public void IsPerformanceMonitoringEnabled_EnableTracing_False() { var sut = new SentryOptions { EnableTracing = false }; - Assert.False(sut.IsTracingEnabled); + Assert.False(sut.IsPerformanceMonitoringEnabled); } [Fact] - public void IsTracingEnabled_TracesSampleRate_Zero() + public void IsPerformanceMonitoringEnabled_TracesSampleRate_Zero() { var sut = new SentryOptions { TracesSampleRate = 0.0 }; - Assert.False(sut.IsTracingEnabled); + Assert.False(sut.IsPerformanceMonitoringEnabled); } [Fact] - public void IsTracingEnabled_TracesSampleRate_GreaterThanZero() + public void IsPerformanceMonitoringEnabled_TracesSampleRate_GreaterThanZero() { var sut = new SentryOptions { TracesSampleRate = double.Epsilon }; - Assert.True(sut.IsTracingEnabled); + Assert.True(sut.IsPerformanceMonitoringEnabled); } [Fact] - public void IsTracingEnabled_TracesSampleRate_LessThanZero() + public void IsPerformanceMonitoringEnabled_TracesSampleRate_LessThanZero() { var sut = new SentryOptions(); Assert.Throws(() => @@ -111,7 +111,7 @@ public void IsTracingEnabled_TracesSampleRate_LessThanZero() } [Fact] - public void IsTracingEnabled_TracesSampleRate_GreaterThanOne() + public void IsPerformanceMonitoringEnabled_TracesSampleRate_GreaterThanOne() { var sut = new SentryOptions(); Assert.Throws(() => @@ -119,18 +119,18 @@ public void IsTracingEnabled_TracesSampleRate_GreaterThanOne() } [Fact] - public void IsTracingEnabled_TracesSampler_Provided() + public void IsPerformanceMonitoringEnabled_TracesSampler_Provided() { var sut = new SentryOptions { TracesSampler = _ => null }; - Assert.True(sut.IsTracingEnabled); + Assert.True(sut.IsPerformanceMonitoringEnabled); } [Fact] - public void IsTracingEnabled_EnableTracing_True_TracesSampleRate_Zero() + 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. @@ -141,11 +141,11 @@ public void IsTracingEnabled_EnableTracing_True_TracesSampleRate_Zero() TracesSampleRate = 0.0 }; - Assert.False(sut.IsTracingEnabled); + Assert.False(sut.IsPerformanceMonitoringEnabled); } [Fact] - public void IsTracingEnabled_EnableTracing_False_TracesSampleRate_One() + public void IsPerformanceMonitoringEnabled_EnableTracing_False_TracesSampleRate_One() { // Edge Case: // Tracing disabled should be treated as disabled regardless of sample rate set. @@ -156,11 +156,11 @@ public void IsTracingEnabled_EnableTracing_False_TracesSampleRate_One() TracesSampleRate = 1.0 }; - Assert.False(sut.IsTracingEnabled); + Assert.False(sut.IsPerformanceMonitoringEnabled); } [Fact] - public void IsTracingEnabled_EnableTracing_False_TracesSampler_Provided() + public void IsPerformanceMonitoringEnabled_EnableTracing_False_TracesSampler_Provided() { // Edge Case: // Tracing disabled should be treated as disabled regardless of sampler function set. @@ -171,7 +171,7 @@ public void IsTracingEnabled_EnableTracing_False_TracesSampler_Provided() TracesSampler = _ => null }; - Assert.False(sut.IsTracingEnabled); + Assert.False(sut.IsPerformanceMonitoringEnabled); } [Fact] From d4dda091cd4bc957d2b5f582fd3b3e83f9b157ab Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 11 Aug 2023 10:22:43 +0200 Subject: [PATCH 141/142] chore: update scripts/update-cli.ps1 to 2.20.5 (#2539) --- 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 b2b32b8672..0d63f7d71f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## Unreleased + +### Dependencies + +- Bump CLI from v2.20.4 to v2.20.5 ([#2539](https://github.com/getsentry/sentry-dotnet/pull/2539)) + - [changelog](https://github.com/getsentry/sentry-cli/blob/master/CHANGELOG.md#2205) + - [diff](https://github.com/getsentry/sentry-cli/compare/2.20.4...2.20.5) + ## 3.35.0 ### Features diff --git a/Directory.Build.props b/Directory.Build.props index bfcf76c398..6ed40028ef 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -100,7 +100,7 @@ - 2.20.4 + 2.20.5 $(MSBuildThisFileDirectory)tools\sentry-cli\$(SentryCLIVersion)\ diff --git a/src/Sentry/Sentry.csproj b/src/Sentry/Sentry.csproj index cf0c003448..db2b56f2e1 100644 --- a/src/Sentry/Sentry.csproj +++ b/src/Sentry/Sentry.csproj @@ -96,25 +96,25 @@ + Include="sentry-cli-Darwin-arm64" FileHash="51910fee9a12f062e72eb4c3cfd52e57537a398da2634b50f83d1fcb2666b159" /> + Include="sentry-cli-Darwin-x86_64" FileHash="01bfff67c5b5f1981d885a9800ccef124596c657a4f0e5901b11e8e0cd2f327e" /> + Include="sentry-cli-Linux-aarch64" FileHash="d111f436e48893dbf19e8894d48a5001a2d702fcec98af8dc0d45c7ee8d885cf" /> + Include="sentry-cli-Linux-i686" FileHash="21a2f454006e48222ec7c92ea9be2bee19b2013ebcb78e83eb9d062f3ec87f60" /> + Include="sentry-cli-Linux-x86_64" FileHash="7f949b842cc5c48e6fe291469f972df915c68db53d0ff0b51cf8d30dd90cd719" /> + Include="sentry-cli-Windows-i686.exe" FileHash="3ccd585433a3dba3fb014b151ab59aae816de1445a7395e7aff8b831e0c2e11a" /> + Include="sentry-cli-Windows-x86_64.exe" FileHash="c06c4e22b8edc6d3c1e4cc11fd6cd515f980069b4c736b0ec04fd5cfc1689c3c" /> From d915f986c8921966210f1940e1fb0e87fb92925c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 15 Aug 2023 17:13:30 +1200 Subject: [PATCH 142/142] chore: update modules/sentry-cocoa to 8.9.5 (#2542) --- CHANGELOG.md | 3 +++ modules/sentry-cocoa | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d63f7d71f..f38e225f79 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ - Bump CLI from v2.20.4 to v2.20.5 ([#2539](https://github.com/getsentry/sentry-dotnet/pull/2539)) - [changelog](https://github.com/getsentry/sentry-cli/blob/master/CHANGELOG.md#2205) - [diff](https://github.com/getsentry/sentry-cli/compare/2.20.4...2.20.5) +- Bump Cocoa SDK from v8.9.4 to v8.9.5 ([#2542](https://github.com/getsentry/sentry-dotnet/pull/2542)) + - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#895) + - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.9.4...8.9.5) ## 3.35.0 diff --git a/modules/sentry-cocoa b/modules/sentry-cocoa index 04bee4ad86..6760ad0289 160000 --- a/modules/sentry-cocoa +++ b/modules/sentry-cocoa @@ -1 +1 @@ -Subproject commit 04bee4ad86d74d4cb4d7101ff826d6e355301ba9 +Subproject commit 6760ad028983d16d7b9bf13e6857b4befa271f17