diff --git a/.github/actions/environment/action.yml b/.github/actions/environment/action.yml
index 9e451a282e..a646c021f8 100644
--- a/.github/actions/environment/action.yml
+++ b/.github/actions/environment/action.yml
@@ -24,6 +24,12 @@ runs:
shell: bash
run: sudo chmod 666 /var/run/docker.sock
+ # Install old deprecated libssl1 for .NET 5.0 on Linux
+ - name: Install libssl1 for NET 5.0 on Linux
+ if: ${{ runner.os == 'Linux' }}
+ shell: bash
+ run: sudo ./scripts/install-libssl1.sh
+
- name: Install Linux ARM 32-bit dependencies
if: ${{ matrix.rid == 'linux-arm' }}
shell: bash
@@ -56,7 +62,7 @@ runs:
uses: android-actions/setup-android@07976c6290703d34c16d382cb36445f98bb43b1f # v3.2.0
with:
# Exclude "tools" because the emulator is not needed (nor available for Windows/Linux ARM64)
- packages: platform-tools platforms;android-34 platforms;android-35 build-tools;36.0.0
+ packages: platform-tools platforms;android-35 platforms;android-36 build-tools;36.0.0
log-accepted-android-sdk-licenses: false
# Java 11 is needed by .NET Android
@@ -90,6 +96,15 @@ runs:
dotnet-version: |
8.0.x
9.0.304
+ 10.0.100-rc.1.25451.107
+ global-json-file: global.json
+
+ # .NET 5.0 does not support ARM64 on macOS
+ - name: Install .NET 5.0 SDK
+ if: ${{ runner.os != 'macOS' || runner.arch != 'ARM64' }}
+ uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4.3.1
+ with:
+ dotnet-version: 5.0.x
- name: Install .NET Workloads
shell: bash
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 1bb59a1678..435e61fd07 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -64,15 +64,11 @@ jobs:
key: sentry-native-${{ matrix.rid }}-${{ hashFiles('scripts/build-sentry-native.ps1') }}-${{ hashFiles('.git/modules/modules/sentry-native/HEAD') }}
enableCrossOsArchive: true
- - name: Remove unused applications
- if: ${{ !matrix.container }}
- uses: ./.github/actions/freediskspace
-
- run: scripts/build-sentry-native.ps1
if: steps.cache.outputs.cache-hit != 'true'
shell: pwsh
- build:
+ build-sentry:
needs: build-sentry-native
name: .NET (${{ matrix.rid }})
runs-on: ${{ matrix.os }}
@@ -254,7 +250,8 @@ jobs:
uses: actions/checkout@v5
with:
# We only check out what is absolutely necessary to reduce a chance of local files impacting
- # integration tests, e.g. Directory.Build.props, nuget.config, ...
+ # integration tests (nuget.config etc.)... But we need the root Directory.Build.props calculate
+ # the package version
sparse-checkout: |
integration-test
.github
@@ -267,11 +264,23 @@ jobs:
path: src
- name: Integration test
+ if: ${{ (matrix.rid != 'linux-musl-x64') && (matrix.rid != 'linux-musl-arm64') }}
+ uses: getsentry/github-workflows/sentry-cli/integration-test/@a5e409bd5bad4c295201cdcfe862b17c50b29ab7 # v2.14.1
+ with:
+ path: integration-test
+
+ # For the linux-musl runtimes we have to pin the ContainerBaseImage for preview or RC builds, since
+ # these don't conform to the normal naming conventions and don't get resolved automatically. We do
+ # by passing it as parameter to aot.Tests.ps1 via an environment variable
+ - name: Integration test (musl)
+ if: ${{ (matrix.rid == 'linux-musl-x64') || (matrix.rid == 'linux-musl-arm64') }}
uses: getsentry/github-workflows/sentry-cli/integration-test/@v2
+ env:
+ ContainerBaseImage: 'mcr.microsoft.com/dotnet/nightly/runtime-deps:10.0-preview-alpine3.22'
with:
path: integration-test
- msbuild:
+ ms-build:
needs: build-sentry-native
name: MSBuild
runs-on: windows-latest
@@ -301,7 +310,12 @@ jobs:
- name: Run MSBuild
id: msbuild
- run: msbuild Sentry-CI-Build-Windows.slnf -t:Restore,Build -p:Configuration=Release --nologo -v:minimal -flp:logfile=msbuild.log -p:CopyLocalLockFileAssemblies=true -bl:msbuild.binlog
+ run: msbuild Sentry-CI-Build-Windows.slnf -t:Restore,Build,Pack -p:Configuration=Release --nologo -v:minimal -flp:logfile=msbuild.log -p:CopyLocalLockFileAssemblies=true -bl:msbuild.binlog
+
+ - name: Test MSBuild
+ uses: getsentry/github-workflows/sentry-cli/integration-test/@a5e409bd5bad4c295201cdcfe862b17c50b29ab7 # v2.14.1
+ with:
+ path: integration-test/msbuild.Tests.ps1
- name: Upload logs
if: ${{ always() }}
@@ -316,7 +330,7 @@ jobs:
# Unsupported Native AOT runtimes should have SentryNative auto-disabled
# to avoid native library loading errors on startup.
unsupported-aot:
- needs: build
+ needs: build-sentry
name: Unsupported AOT (${{ matrix.rid }})
runs-on: ${{ matrix.os }}
@@ -348,7 +362,7 @@ jobs:
path: src
- name: Test AOT
- uses: getsentry/github-workflows/sentry-cli/integration-test/@v2
+ uses: getsentry/github-workflows/sentry-cli/integration-test/@a5e409bd5bad4c295201cdcfe862b17c50b29ab7 # v2.14.1
env:
RuntimeIdentifier: ${{ matrix.rid }}
with:
@@ -384,7 +398,7 @@ jobs:
uses: ./.github/actions/buildnative
- name: Publish Test app (macOS)
- run: dotnet publish test/Sentry.TrimTest/Sentry.TrimTest.csproj -c Release -r osx-arm64
+ run: dotnet publish test/Sentry.TrimTest/Sentry.TrimTest.csproj -c Release -r osx-arm64 -p:NO_MOBILE=true
- name: Publish Test app (Android)
run: dotnet publish test/Sentry.MauiTrimTest/Sentry.MauiTrimTest.csproj -c Release -f net9.0-android35.0 -r android-arm64
diff --git a/.github/workflows/device-tests-android.yml b/.github/workflows/device-tests-android.yml
index b97936f3e2..b234d39df2 100644
--- a/.github/workflows/device-tests-android.yml
+++ b/.github/workflows/device-tests-android.yml
@@ -8,6 +8,7 @@ on:
pull_request:
paths-ignore:
- "**.md"
+ workflow_dispatch:
jobs:
build:
@@ -64,6 +65,12 @@ jobs:
env:
DOTNET_CLI_TELEMETRY_OPTOUT: 1
DOTNET_NOLOGO: 1
+ # We don't need the Google APIs, but the default images are not available for 32+
+ ANDROID_EMULATOR_TARGET: google_apis
+ ANDROID_EMULATOR_RAM_SIZE: 2048M
+ ANDROID_EMULATOR_ARCH: x86_64
+ ANDROID_EMULATOR_DISK_SIZE: 4096M
+ ANDROID_EMULATOR_OPTIONS: -no-snapshot-save -no-window -accel on -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
steps:
# See https://github.blog/changelog/2023-02-23-hardware-accelerated-android-virtualization-on-actions-windows-and-linux-larger-hosted-runners/
- name: Enable KVM group perms
@@ -87,17 +94,33 @@ jobs:
# Cached AVD setup per https://github.com/ReactiveCircus/android-emulator-runner/blob/main/README.md
- name: Run Tests
+ id: first-run
+ continue-on-error: true
timeout-minutes: 40
uses: reactivecircus/android-emulator-runner@1dcd0090116d15e7c562f8db72807de5e036a4ed # Tag: v2.34.0
with:
api-level: ${{ matrix.api-level }}
- # We don't need the Google APIs, but the default images are not available for 32+
- target: google_apis
+ target: ${{ env.ANDROID_EMULATOR_TARGET }}
force-avd-creation: false
- ram-size: 2048M
- arch: x86_64
- disk-size: 4096M
- emulator-options: -no-snapshot-save -no-window -accel on -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
+ ram-size: ${{ env.ANDROID_EMULATOR_RAM_SIZE }}
+ arch: ${{ env.ANDROID_EMULATOR_ARCH }}
+ disk-size: ${{ env.ANDROID_EMULATOR_DISK_SIZE }}
+ emulator-options: ${{ env.ANDROID_EMULATOR_OPTIONS }}
+ disable-animations: false
+ script: pwsh scripts/device-test.ps1 android -Run -Tfm ${{ matrix.tfm }}
+
+ - name: Retry Tests (if previous failed to run)
+ if: steps.first-run.outcome == 'failure'
+ timeout-minutes: 40
+ uses: reactivecircus/android-emulator-runner@1dcd0090116d15e7c562f8db72807de5e036a4ed # Tag: v2.34.0
+ with:
+ api-level: ${{ matrix.api-level }}
+ target: ${{ env.ANDROID_EMULATOR_TARGET }}
+ force-avd-creation: false
+ ram-size: ${{ env.ANDROID_EMULATOR_RAM_SIZE }}
+ arch: ${{ env.ANDROID_EMULATOR_ARCH }}
+ disk-size: ${{ env.ANDROID_EMULATOR_DISK_SIZE }}
+ emulator-options: ${{ env.ANDROID_EMULATOR_OPTIONS }}
disable-animations: false
script: pwsh scripts/device-test.ps1 android -Run -Tfm ${{ matrix.tfm }}
diff --git a/.github/workflows/device-tests-ios.yml b/.github/workflows/device-tests-ios.yml
index c56226bdb6..0e118745cc 100644
--- a/.github/workflows/device-tests-ios.yml
+++ b/.github/workflows/device-tests-ios.yml
@@ -8,6 +8,7 @@ on:
pull_request:
paths-ignore:
- "**.md"
+ workflow_dispatch:
jobs:
ios-tests:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 141222b598..0e39c2c1a3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,43 @@
# Changelog
+## 6.0.0-preview.1
+
+### Features
+
+- Added `EnableBackpressureHandling` option for Automatic backpressure handling. When enabled this automatically reduces the sample rate when the SDK detects events being dropped. ([#4452](https://github.com/getsentry/sentry-dotnet/pull/4452))
+- Add (experimental) _Structured Logs_ integration for `Serilog` ([#4462](https://github.com/getsentry/sentry-dotnet/pull/4462))
+
+### Fixes
+
+- Upload linked PDBs to fix non-IL-stripped symbolication for iOS ([#4527](https://github.com/getsentry/sentry-dotnet/pull/4527))
+- In MAUI Android apps, generate and inject UUID to APK and upload ProGuard mapping to Sentry with the UUID ([#4532](https://github.com/getsentry/sentry-dotnet/pull/4532))
+- Fixed WASM0001 warning when building Blazor WebAssembly projects ([#4519](https://github.com/getsentry/sentry-dotnet/pull/4519))
+
+### Dependencies
+
+- Bump Cocoa SDK from v8.56.0 to v8.56.2 ([#4555](https://github.com/getsentry/sentry-dotnet/pull/4555), [#4572](https://github.com/getsentry/sentry-dotnet/pull/4572))
+ - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#8562)
+ - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.56.0...8.56.2)
+- Bump Native SDK from v0.11.0 to v0.11.1 ([#4557](https://github.com/getsentry/sentry-dotnet/pull/4557))
+ - [changelog](https://github.com/getsentry/sentry-native/blob/master/CHANGELOG.md#0111)
+ - [diff](https://github.com/getsentry/sentry-native/compare/0.11.0...0.11.1)
+- Bump CLI from v2.54.0 to v2.55.0 ([#4556](https://github.com/getsentry/sentry-dotnet/pull/4556))
+ - [changelog](https://github.com/getsentry/sentry-cli/blob/master/CHANGELOG.md#2550)
+ - [diff](https://github.com/getsentry/sentry-cli/compare/2.54.0...2.55.0)
+
+### Dependencies
+
+- Bump Java SDK from v8.21.1 to v8.22.0 ([#4552](https://github.com/getsentry/sentry-dotnet/pull/4552))
+ - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#8220)
+ - [diff](https://github.com/getsentry/sentry-java/compare/8.21.1...8.22.0)
+
+## Version 6.0.0
+
+### BREAKING CHANGES
+
+- This release adds support for .NET 10 and drops support for net8.0-android, net8.0-ios, net8.0-maccatalyst and net8.0-windows10.0.19041.0 ([#4461](https://github.com/getsentry/sentry-dotnet/pull/4461))
+- Added support for v3 of the Android AssemblyStore format that is used in .NET 10 and dropped support for v1 that was used in .NET 8 ([#4583](https://github.com/getsentry/sentry-dotnet/pull/4583))
+
## 5.15.1
### Fixes
diff --git a/Directory.Build.props b/Directory.Build.props
index a74738d5cd..eac7530416 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -1,7 +1,8 @@
- 5.15.1
+ 6.0.0-preview.1
+ prerelease
13
true
true
@@ -20,11 +21,27 @@
$(NoWarn);NU1902;NU1903
+
+ $(NoWarn);XCODE_26_0_PREVIEW
+
NU1902;NU1903
+
+
+ net10.0
+ net9.0
+ net8.0
+ net8.0;net9.0;net10.0
+ net10.0-android36.0
+ net9.0-android35.0
+ net9.0-ios26
+ net9.0-ios18.0
+ net9.0-maccatalyst26
+ net9.0-maccatalyst18.0
+ net9.0-windows10.0.19041.0
-
+
dev
@@ -86,7 +103,7 @@
- 2.54.0
+ 2.55.0
$(MSBuildThisFileDirectory)tools\sentry-cli\$(SentryCLIVersion)\
diff --git a/Sentry.sln b/Sentry.sln
index 3730377a46..e8b04afa38 100644
--- a/Sentry.sln
+++ b/Sentry.sln
@@ -209,6 +209,74 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.SourceGenerators.Tes
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Maui.CommunityToolkit.Mvvm.Tests", "test\Sentry.Maui.CommunityToolkit.Mvvm.Tests\Sentry.Maui.CommunityToolkit.Mvvm.Tests.csproj", "{ADC91A84-6054-42EC-8241-0D717E4C7194}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "scripts", "scripts", "{A8A97D6A-02C0-4808-9D62-DFFAB324A323}"
+ ProjectSection(SolutionItems) = preProject
+ scripts\update-cli.ps1 = scripts\update-cli.ps1
+ scripts\bump-version.sh = scripts\bump-version.sh
+ scripts\device-test.ps1 = scripts\device-test.ps1
+ scripts\dirty-check.ps1 = scripts\dirty-check.ps1
+ scripts\update-java.ps1 = scripts\update-java.ps1
+ scripts\bump-version.ps1 = scripts\bump-version.ps1
+ scripts\parse-xunit2-xml.ps1 = scripts\parse-xunit2-xml.ps1
+ scripts\build-sentry-cocoa.sh = scripts\build-sentry-cocoa.sh
+ scripts\update-project-xml.ps1 = scripts\update-project-xml.ps1
+ scripts\build-sentry-native.ps1 = scripts\build-sentry-native.ps1
+ scripts\ios-simulator-utils.ps1 = scripts\ios-simulator-utils.ps1
+ scripts\commit-formatted-code.sh = scripts\commit-formatted-code.sh
+ scripts\accept-verifier-changes.ps1 = scripts\accept-verifier-changes.ps1
+ scripts\generate-cocoa-bindings.ps1 = scripts\generate-cocoa-bindings.ps1
+ scripts\generate-solution-filters.ps1 = scripts\generate-solution-filters.ps1
+ scripts\generate-solution-filters-config.yaml = scripts\generate-solution-filters-config.yaml
+ EndProjectSection
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{EC6ADE8A-E557-4848-8F03-519039830B5F}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{BFF081D8-7CC0-4069-99F5-5CA0D70B56AB}"
+ ProjectSection(SolutionItems) = preProject
+ .github\workflows\build.yml = .github\workflows\build.yml
+ .github\workflows\alpine.yml = .github\workflows\alpine.yml
+ .github\workflows\danger.yml = .github\workflows\danger.yml
+ .github\workflows\release.yml = .github\workflows\release.yml
+ .github\workflows\format-code.yml = .github\workflows\format-code.yml
+ .github\workflows\update-deps.yml = .github\workflows\update-deps.yml
+ .github\workflows\codeql-analysis.yml = .github\workflows\codeql-analysis.yml
+ .github\workflows\vulnerabilities.yml = .github\workflows\vulnerabilities.yml
+ .github\workflows\device-tests-ios.yml = .github\workflows\device-tests-ios.yml
+ .github\workflows\device-tests-android.yml = .github\workflows\device-tests-android.yml
+ EndProjectSection
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "actions", "actions", "{5D50D425-244F-4B79-B9F5-21D26DD52DC1}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "environment", "environment", "{39216438-F347-427C-AB70-48DB1BA6E299}"
+ ProjectSection(SolutionItems) = preProject
+ .github\actions\environment\action.yml = .github\actions\environment\action.yml
+ EndProjectSection
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "buildnative", "buildnative", "{A384A71C-A46F-49DB-B7FB-5DEEFC5E6CA3}"
+ ProjectSection(SolutionItems) = preProject
+ .github\actions\buildnative\action.yml = .github\actions\buildnative\action.yml
+ EndProjectSection
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "freediskspace", "freediskspace", "{E34AA22F-B42E-4D4C-B96E-426AEBC2F367}"
+ ProjectSection(SolutionItems) = preProject
+ .github\actions\freediskspace\action.yml = .github\actions\freediskspace\action.yml
+ EndProjectSection
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "install-zstd", "install-zstd", "{94A2DCA5-F298-41FB-913A-476668EF5786}"
+ ProjectSection(SolutionItems) = preProject
+ .github\actions\install-zstd\action.yml = .github\actions\install-zstd\action.yml
+ EndProjectSection
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "integration-test", "integration-test", "{94CCDBEF-5867-4C24-A305-0C2AE738AF42}"
+ ProjectSection(SolutionItems) = preProject
+ integration-test\common.ps1 = integration-test\common.ps1
+ integration-test\aot.Tests.ps1 = integration-test\aot.Tests.ps1
+ integration-test\cli.Tests.ps1 = integration-test\cli.Tests.ps1
+ integration-test\runtime.Tests.ps1 = integration-test\runtime.Tests.ps1
+ EndProjectSection
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "net4-console", "net4-console", "{33793113-C7B5-434D-B5C1-6CA1A9587842}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -1343,5 +1411,12 @@ Global
{C3CDF61C-3E28-441C-A9CE-011C89D11719} = {230B9384-90FD-4551-A5DE-1A5C197F25B6}
{3A76FF7D-2F32-4EA5-8999-2FFE3C7CB893} = {6987A1CC-608E-4868-A02C-09D30C8B7B2D}
{ADC91A84-6054-42EC-8241-0D717E4C7194} = {6987A1CC-608E-4868-A02C-09D30C8B7B2D}
+ {BFF081D8-7CC0-4069-99F5-5CA0D70B56AB} = {EC6ADE8A-E557-4848-8F03-519039830B5F}
+ {5D50D425-244F-4B79-B9F5-21D26DD52DC1} = {EC6ADE8A-E557-4848-8F03-519039830B5F}
+ {39216438-F347-427C-AB70-48DB1BA6E299} = {5D50D425-244F-4B79-B9F5-21D26DD52DC1}
+ {A384A71C-A46F-49DB-B7FB-5DEEFC5E6CA3} = {5D50D425-244F-4B79-B9F5-21D26DD52DC1}
+ {E34AA22F-B42E-4D4C-B96E-426AEBC2F367} = {5D50D425-244F-4B79-B9F5-21D26DD52DC1}
+ {94A2DCA5-F298-41FB-913A-476668EF5786} = {5D50D425-244F-4B79-B9F5-21D26DD52DC1}
+ {33793113-C7B5-434D-B5C1-6CA1A9587842} = {94CCDBEF-5867-4C24-A305-0C2AE738AF42}
EndGlobalSection
EndGlobal
diff --git a/SentryNoMobile.slnf b/SentryNoMobile.slnf
index 1592f77697..bfc8311ad7 100644
--- a/SentryNoMobile.slnf
+++ b/SentryNoMobile.slnf
@@ -24,7 +24,6 @@
"samples\\Sentry.Samples.GraphQL.Server\\Sentry.Samples.GraphQL.Server.csproj",
"samples\\Sentry.Samples.Hangfire\\Sentry.Samples.Hangfire.csproj",
"samples\\Sentry.Samples.Log4Net\\Sentry.Samples.Log4Net.csproj",
- "samples\\Sentry.Samples.MacCatalyst\\Sentry.Samples.MacCatalyst.csproj",
"samples\\Sentry.Samples.MacOS\\Sentry.Samples.MacOS.csproj",
"samples\\Sentry.Samples.ME.Logging\\Sentry.Samples.ME.Logging.csproj",
"samples\\Sentry.Samples.NLog\\Sentry.Samples.NLog.csproj",
diff --git a/benchmarks/Sentry.Benchmarks/BackgroundWorkerFlushBenchmarks.cs b/benchmarks/Sentry.Benchmarks/BackgroundWorkerFlushBenchmarks.cs
index 3f31663e09..7d165930a8 100644
--- a/benchmarks/Sentry.Benchmarks/BackgroundWorkerFlushBenchmarks.cs
+++ b/benchmarks/Sentry.Benchmarks/BackgroundWorkerFlushBenchmarks.cs
@@ -20,7 +20,7 @@ public Task SendEnvelopeAsync(Envelope envelope, CancellationToken cancellationT
[IterationSetup]
public void IterationSetup()
{
- _backgroundWorker = new BackgroundWorker(new FakeTransport(), new SentryOptions { MaxQueueItems = 1000 });
+ _backgroundWorker = new BackgroundWorker(new FakeTransport(), new SentryOptions { MaxQueueItems = 1000 }, null);
_event = new SentryEvent();
_envelope = Envelope.FromEvent(_event);
diff --git a/benchmarks/Sentry.Benchmarks/Sentry.Benchmarks.csproj b/benchmarks/Sentry.Benchmarks/Sentry.Benchmarks.csproj
index 48231469f2..5a28b1186f 100644
--- a/benchmarks/Sentry.Benchmarks/Sentry.Benchmarks.csproj
+++ b/benchmarks/Sentry.Benchmarks/Sentry.Benchmarks.csproj
@@ -2,7 +2,7 @@
Exe
- net8.0
+ $(PreviousTfm)
false
diff --git a/global.json b/global.json
index 9cd89532c5..9920ba14bd 100644
--- a/global.json
+++ b/global.json
@@ -1,7 +1,7 @@
{
"sdk": {
- "version": "9.0.304",
- "workloadVersion": "9.0.304",
+ "version": "10.0.100-rc.1.25451.107",
+ "workloadVersion": "10.0.100-rc.1.25458.2",
"rollForward": "disable",
"allowPrerelease": false
}
diff --git a/integration-test/aot.Tests.ps1 b/integration-test/aot.Tests.ps1
index 14849c332f..269e8d14ae 100644
--- a/integration-test/aot.Tests.ps1
+++ b/integration-test/aot.Tests.ps1
@@ -51,19 +51,21 @@ Console.WriteLine("Hello, Sentry!");
It 'Aot' {
$rid = $env:RuntimeIdentifier
- if ($rid)
- {
- dotnet publish -c Release -r $rid | Write-Host
+ $baseImage = $env:ContainerBaseImage
+ $publishArgs = @('-c', 'Release')
+ if ($rid) {
+ Write-Host "Environment RuntimeIdentifier: $rid"
+ $publishArgs += @('-r', $rid)
}
- else
- {
- dotnet publish -c Release | Write-Host
+ if ($baseImage) {
+ Write-Host "Using ContainerBaseImage: $baseImage"
+ $publishArgs += "-p:ContainerBaseImage=$baseImage"
}
+ dotnet publish @publishArgs | Write-Host
$LASTEXITCODE | Should -Be 0
$tfm = (Get-ChildItem -Path "bin/Release" -Directory | Select-Object -First 1).Name
- if (-not $rid)
- {
+ if (-not $rid) {
$rid = (Get-ChildItem -Path "bin/Release/$tfm" -Directory | Select-Object -First 1).Name
}
& "bin/Release/$tfm/$rid/publish/hello-sentry" | Write-Host
diff --git a/integration-test/cli.Tests.ps1 b/integration-test/cli.Tests.ps1
index 1a39cf541a..ff7b8a7eee 100644
--- a/integration-test/cli.Tests.ps1
+++ b/integration-test/cli.Tests.ps1
@@ -4,7 +4,7 @@ $ErrorActionPreference = 'Stop'
. $PSScriptRoot/common.ps1
Describe 'Console apps () - normal build' -ForEach @(
- @{ framework = "net8.0" }
+ foreach ($fw in $currentFrameworks) { @{ framework = $fw } }
) {
BeforeAll {
DotnetNew 'console' 'console-app' $framework
@@ -40,7 +40,7 @@ Describe 'Console apps () - normal build' -ForEach @(
}
Describe 'Console apps () - native AOT publish' -ForEach @(
- @{ framework = "net8.0" }
+ foreach ($fw in $currentFrameworks) { @{ framework = $fw } }
) {
BeforeAll {
DotnetNew 'console' 'console-app' $framework
@@ -92,10 +92,6 @@ Describe 'Console apps () - native AOT publish' -ForEach @(
$result = RunDotnetWithSentryCLI 'publish' 'console-app' $False $True $framework
$result.ScriptOutput | Should -AnyElementMatch "Preparing upload to Sentry for project 'console-app'"
$sourceBundle = 'console-app.src.zip'
- if ($IsMacOS)
- {
- $sourceBundle = 'console-app.src.zip'
- }
$result.UploadedDebugFiles() | Sort-Object -Unique | Should -Be @($sourceBundle)
}
@@ -105,8 +101,8 @@ Describe 'Console apps () - native AOT publish' -ForEach @(
}
}
-Describe 'MAUI' -ForEach @(
- @{ framework = "net8.0" }
+Describe 'MAUI ()' -ForEach @(
+ @{ framework = $previousFramework }
) -Skip:($env:NO_MOBILE -eq "true") {
BeforeAll {
RegisterLocalPackage 'Sentry.Android.AssemblyReader'
@@ -119,8 +115,8 @@ Describe 'MAUI' -ForEach @(
}
$name = 'maui-app'
- $androidTpv = '34.0'
- $iosTpv = '17.0'
+ $androidTpv = GetAndroidTpv $framework
+ $iosTpv = GetIosTpv $framework
DotnetNew 'maui' $name $framework
@@ -148,6 +144,7 @@ Describe 'MAUI' -ForEach @(
It "uploads symbols and sources for an Android build" {
$result = RunDotnetWithSentryCLI 'build' 'maui-app' $True $True "$framework-android$androidTpv"
+ Write-Host "UploadedDebugFiles: $($result.UploadedDebugFiles() | Out-String)"
$result.UploadedDebugFiles() | Sort-Object -Unique | Should -Be @(
'libsentry-android.so',
'libsentry.so',
@@ -156,11 +153,12 @@ Describe 'MAUI' -ForEach @(
'maui-app.pdb'
)
$result.ScriptOutput | Should -AnyElementMatch 'Uploaded a total of 1 new mapping files'
- $result.ScriptOutput | Should -AnyElementMatch 'Found 25 debug information files \(1 with embedded sources\)'
+ $result.ScriptOutput | Should -AnyElementMatch "Found 23 debug information files \(1 with embedded sources\)"
}
It "uploads symbols and sources for an iOS build" -Skip:(!$IsMacOS) {
$result = RunDotnetWithSentryCLI 'build' 'maui-app' $True $True "$framework-ios$iosTpv"
+ Write-Host "UploadedDebugFiles: $($result.UploadedDebugFiles() | Out-String)"
$result.UploadedDebugFiles() | Sort-Object -Unique | Should -Be @(
'libmono-component-debugger.dylib',
'libmono-component-diagnostics_tracing.dylib',
@@ -176,9 +174,14 @@ Describe 'MAUI' -ForEach @(
'libxamarin-dotnet.dylib',
'maui-app',
'maui-app.pdb',
+ 'Microsoft.iOS.pdb',
+ 'Microsoft.Maui.Controls.pdb',
+ 'Microsoft.Maui.Controls.Xaml.pdb',
+ 'Microsoft.Maui.Essentials.pdb',
+ 'Microsoft.Maui.Graphics.pdb',
+ 'Microsoft.Maui.pdb',
'Sentry'
)
- $nonZeroNumberRegex = '[1-9][0-9]*';
- $result.ScriptOutput | Should -AnyElementMatch "Found $nonZeroNumberRegex debug information files \($nonZeroNumberRegex with embedded sources\)"
+ $result.ScriptOutput | Should -AnyElementMatch "Found 77 debug information files \(8 with embedded sources\)"
}
}
diff --git a/integration-test/common.ps1 b/integration-test/common.ps1
index b7215fc87e..312825360e 100644
--- a/integration-test/common.ps1
+++ b/integration-test/common.ps1
@@ -1,5 +1,9 @@
-# So that this works in VS Code testing integration. Otherwise the script is run within its directory.
+$global:longTermFramework = 'net8.0'
+$global:previousFramework = 'net9.0'
+$global:latestFramework = 'net10.0'
+$global:currentFrameworks = @($longTermFramework, $previousFramework, $latestFramework)
+# So that this works in VS Code testing integration. Otherwise the script is run within its directory.
# In CI, the module is loaded automatically
if (!(Test-Path env:CI ))
{
@@ -58,9 +62,74 @@ BeforeAll {
Push-Location $PSScriptRoot
$env:SENTRY_LOG_LEVEL = 'debug';
+ function GetAndroidTpv($framework)
+ {
+ switch ($framework) {
+ 'net9.0' { return '35.0' } # matches PreviousAndroidTfm (net9.0-android35.0)
+ 'net10.0' { return '36.0' } # matches LatestAndroidTfm (net10.0-android36.0)
+ default { throw "Unsupported framework '$framework' for Android target platform version." }
+ }
+ }
+
+ function GetIosTpv($framework)
+ {
+ switch ($framework) {
+ 'net9.0' { return '18.0' } # matches PreviousIosTfm / PreviousMacCatalystTfm
+ 'net10.0' { return '26' } # aligns with ios26 / maccatalyst26
+ default { throw "Unsupported framework '$framework' for iOS target platform version." }
+ }
+ }
+
function GetSentryPackageVersion()
{
- (Select-Xml -Path "$PSScriptRoot/../Directory.Build.props" -XPath "/Project/PropertyGroup/VersionPrefix").Node.InnerText
+ # Read version directly from Directory.Build.props
+ $propsFile = Join-Path $PSScriptRoot '..\Directory.Build.props'
+
+ if (-not (Test-Path $propsFile)) {
+ throw "Directory.Build.props not found at $propsFile"
+ }
+
+ # Parse the props file using PowerShell XML parsing
+ Write-Host "Parsing props file as XML..."
+ [xml]$propsXml = Get-Content $propsFile
+
+ # Look for VersionPrefix and VersionSuffix in PropertyGroup elements
+ $versionPrefix = ""
+ $versionSuffix = ""
+
+ foreach ($propGroup in $propsXml.Project.PropertyGroup) {
+ if ($propGroup.PSObject.Properties["VersionPrefix"]) {
+ $versionPrefix = $propGroup.VersionPrefix
+ Write-Host "Found VersionPrefix: '$versionPrefix'"
+ }
+
+ # For VersionSuffix, we need to be careful about conditions
+ # Only use VersionSuffix if it's not in a conditional PropertyGroup
+ # or if it's explicitly set (not the 'dev' fallback for non-Release)
+ if ($propGroup.PSObject.Properties["VersionSuffix"]) {
+ $condition = $null
+ if ($propGroup.PSObject.Properties["Condition"]) {
+ $condition = $propGroup.Condition
+ Write-Host "Ignoring VersionSuffix: '$($propGroup.VersionSuffix)' with condition: '$condition'"
+ # Skip conditional VersionSuffix as we're building in Release mode
+ }
+ else {
+ # No condition - this is the explicit VersionSuffix we want
+ $versionSuffix = $propGroup.VersionSuffix
+ Write-Host "Found VersionSuffix: '$versionSuffix'"
+ }
+ }
+ }
+
+ if (-not $versionPrefix) {
+ throw "Could not find VersionPrefix in $propsFile"
+ }
+
+ # Combine prefix and suffix
+ $fullVersion = if ($versionSuffix) { "$versionPrefix-$versionSuffix" } else { $versionPrefix }
+ Write-Host "Full Version: '$fullVersion'"
+
+ return $fullVersion
}
function RegisterLocalPackage([string] $name)
@@ -142,7 +211,7 @@ BeforeAll {
}
elseif ($action -eq "publish")
{
- $result.ScriptOutput | Should -AnyElementMatch "$((Get-Item $project).Basename) -> .*$project/bin/Release/$TargetFramework/.*/publish"
+ $result.ScriptOutput | Should -AnyElementMatch "$((Get-Item $project).Basename) -> .*$project/bin/Release/$TargetFramework/.*publish.*"
}
$result.ScriptOutput | Should -Not -AnyElementMatch "Preparing upload to Sentry for project 'Sentry'"
$result.HasErrors() | Should -BeFalse
@@ -154,7 +223,7 @@ BeforeAll {
Push-Location $projectPath
try
{
- dotnet restore | ForEach-Object { Write-Host $_ }
+ dotnet restore /p:CheckEolTargetFramework=false | ForEach-Object { Write-Host $_ }
if ($LASTEXITCODE -ne 0)
{
throw "Failed to restore the test app project."
@@ -197,16 +266,13 @@ BeforeAll {
if ($type -eq 'console')
{
AddPackageReference $name 'Sentry'
- if (!$IsMacOS -or $framework -eq 'net8.0')
- {
- @"
+ @"
true
"@ | Out-File $name/Directory.Build.props
- }
}
}
}
diff --git a/integration-test/msbuild.Tests.ps1 b/integration-test/msbuild.Tests.ps1
new file mode 100644
index 0000000000..95288d7213
--- /dev/null
+++ b/integration-test/msbuild.Tests.ps1
@@ -0,0 +1,101 @@
+# This file contains test cases for https://pester.dev/
+Set-StrictMode -Version Latest
+$ErrorActionPreference = 'Stop'
+. $PSScriptRoot/common.ps1
+
+$IsARM64 = "Arm64".Equals([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture.ToString())
+
+# NOTE: These .NET versions are used to build a test app that consumes the Sentry
+# .NET SDK, and are not tied to the .NET version used to build the SDK itself.
+Describe 'MSBuild app' {
+ BeforeDiscovery {
+ $frameworks = @()
+
+ # .NET 5.0 does not support ARM64 on macOS
+ if (-not $IsMacOS -or -not $IsARM64)
+ {
+ $frameworks += @{
+ framework = 'net5.0'
+ sdk = '5.0.400'
+ # NuGet 5 does not support packageSourceMapping
+ config = "$PSScriptRoot\nuget5.config"
+ }
+ }
+
+ $frameworks += @(
+ @{ framework = 'net8.0'; sdk = '8.0.400' },
+ @{ framework = 'net9.0'; sdk = '9.0.300' }
+ )
+ }
+
+ Context '()' -ForEach $frameworks {
+ BeforeEach {
+ Write-Host "::group::Create msbuild-app"
+ dotnet new console --no-restore --output msbuild-app --framework $framework | ForEach-Object { Write-Host $_ }
+ $LASTEXITCODE | Should -Be 0
+ AddPackageReference msbuild-app Sentry
+ Push-Location msbuild-app
+ @'
+using System.Runtime.InteropServices;
+using Sentry;
+
+SentrySdk.Init(options =>
+{
+ options.Dsn = args[0];
+ options.Debug = true;
+});
+
+SentrySdk.CaptureMessage($"Hello from MSBuild app");
+'@ | Out-File Program.cs
+ Write-Host "::endgroup::"
+
+ Write-Host "::group::Setup .NET SDK"
+ if (Test-Path variable:sdk)
+ {
+ # Pin to a specific SDK version to use MSBuild from that version
+ @"
+{
+ "sdk": {
+ "version": "$sdk",
+ "rollForward": "latestFeature"
+ }
+}
+"@ | Out-File global.json
+ }
+ Write-Host "Using .NET SDK: $(dotnet --version)"
+ Write-Host "Using MSBuild version: $(dotnet msbuild -version)"
+ Write-Host "::endgroup::"
+ }
+
+ AfterEach {
+ Pop-Location
+ Remove-Item msbuild-app -Recurse -Force -ErrorAction SilentlyContinue
+ }
+
+ It 'builds without warnings and is able to capture a message' {
+ Write-Host "::group::Restore packages"
+ if (!(Test-Path variable:config))
+ {
+ $config = "$PSScriptRoot/nuget.config"
+ }
+ dotnet restore msbuild-app.csproj --configfile $config -p:CheckEolTargetFramework=false | ForEach-Object { Write-Host $_ }
+ $LASTEXITCODE | Should -Be 0
+ Write-Host "::endgroup::"
+
+ # TODO: pass -p:TreatWarningsAsErrors=true after #4554 is fixed
+ dotnet msbuild msbuild-app.csproj -t:Build -p:Configuration=Release -p:TreatWarningsAsErrors=false | ForEach-Object { Write-Host $_ }
+ $LASTEXITCODE | Should -Be 0
+
+ Write-Host "::group::Run msbuild-app"
+ $result = Invoke-SentryServer {
+ param([string]$url)
+ $dsn = $url.Replace('http://', 'http://key@') + '/0'
+ dotnet msbuild msbuild-app.csproj -t:Run -p:Configuration=Release -p:RunArguments=$dsn | ForEach-Object { Write-Host $_ }
+ $LASTEXITCODE | Should -Be 0
+ }
+ $result.HasErrors() | Should -BeFalse
+ $result.Envelopes() | Should -AnyElementMatch "`"message`":`"Hello from MSBuild app`""
+ Write-Host "::endgroup::"
+ }
+ }
+}
diff --git a/integration-test/nuget5.config b/integration-test/nuget5.config
new file mode 100644
index 0000000000..822e3e5985
--- /dev/null
+++ b/integration-test/nuget5.config
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
diff --git a/integration-test/runtime.Tests.ps1 b/integration-test/runtime.Tests.ps1
index 158890edc3..f6f98407c6 100644
--- a/integration-test/runtime.Tests.ps1
+++ b/integration-test/runtime.Tests.ps1
@@ -4,7 +4,7 @@ $ErrorActionPreference = 'Stop'
. $PSScriptRoot/common.ps1
Describe 'Console app NativeAOT ()' -ForEach @(
- @{ framework = 'net8.0' }
+ foreach ($fw in $currentFrameworks) { @{ framework = $fw } }
) {
BeforeAll {
$path = './console-app'
@@ -188,7 +188,7 @@ Describe 'Console app regression (missing System.Reflection.Metadata)' {
dotnet remove ./net4-console/console-app.csproj package Sentry
}
- It 'Ensure System.Reflection.Metadata is not missing' {
+ It 'Ensure System.Reflection.Metadata is not missing' -Skip:(!$IsWindows) {
$path = './net4-console'
Remove-Item -Recurse -Force -Path @("$path/bin", "$path/obj") -ErrorAction SilentlyContinue
AddPackageReference $path 'Sentry'
diff --git a/modules/Ben.Demystifier b/modules/Ben.Demystifier
index 0a6b30f6ac..7c9eeea3ed 160000
--- a/modules/Ben.Demystifier
+++ b/modules/Ben.Demystifier
@@ -1 +1 @@
-Subproject commit 0a6b30f6ac6892ff45b9dcd5b07d710e5228256e
+Subproject commit 7c9eeea3ed2416f2aa0491da640fa84337f2a7a2
diff --git a/modules/sentry-cocoa.properties b/modules/sentry-cocoa.properties
index d4dde9863e..1b37e7bb3c 100644
--- a/modules/sentry-cocoa.properties
+++ b/modules/sentry-cocoa.properties
@@ -1,2 +1,2 @@
-version = 8.56.0
+version = 8.56.2
repo = https://github.com/getsentry/sentry-cocoa
diff --git a/modules/sentry-native b/modules/sentry-native
index 3bd091313a..075b3bfee1 160000
--- a/modules/sentry-native
+++ b/modules/sentry-native
@@ -1 +1 @@
-Subproject commit 3bd091313ae97be90be62696a2babe591a988eb8
+Subproject commit 075b3bfee1dbb85fa10d50df631286196943a3e0
diff --git a/samples/Sentry.Samples.Android/Sentry.Samples.Android.csproj b/samples/Sentry.Samples.Android/Sentry.Samples.Android.csproj
index ace5748bae..a9eacd63be 100644
--- a/samples/Sentry.Samples.Android/Sentry.Samples.Android.csproj
+++ b/samples/Sentry.Samples.Android/Sentry.Samples.Android.csproj
@@ -1,6 +1,6 @@
- net8.0-android
+ net9.0-android35.0
21
Exe
enable
diff --git a/samples/Sentry.Samples.AspNetCore.Basic/Sentry.Samples.AspNetCore.Basic.csproj b/samples/Sentry.Samples.AspNetCore.Basic/Sentry.Samples.AspNetCore.Basic.csproj
index eb93b9fefb..6b83d73fc1 100644
--- a/samples/Sentry.Samples.AspNetCore.Basic/Sentry.Samples.AspNetCore.Basic.csproj
+++ b/samples/Sentry.Samples.AspNetCore.Basic/Sentry.Samples.AspNetCore.Basic.csproj
@@ -1,7 +1,7 @@
- net8.0
+ net10.0
diff --git a/samples/Sentry.Samples.AspNetCore.Blazor.Server/Sentry.Samples.AspNetCore.Blazor.Server.csproj b/samples/Sentry.Samples.AspNetCore.Blazor.Server/Sentry.Samples.AspNetCore.Blazor.Server.csproj
index 04fa5f4380..e173fff774 100644
--- a/samples/Sentry.Samples.AspNetCore.Blazor.Server/Sentry.Samples.AspNetCore.Blazor.Server.csproj
+++ b/samples/Sentry.Samples.AspNetCore.Blazor.Server/Sentry.Samples.AspNetCore.Blazor.Server.csproj
@@ -1,7 +1,7 @@
- net8.0
+ net10.0
enable
enable
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 c64bb41a0a..22d6f53b09 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
@@ -1,7 +1,7 @@
- net8.0
+ net9.0
true
@@ -15,13 +15,8 @@
-
-
+
+
-
-
-
-
-
diff --git a/samples/Sentry.Samples.AspNetCore.Grpc/Program.cs b/samples/Sentry.Samples.AspNetCore.Grpc/Program.cs
index cdd26f9eb0..12d4313821 100644
--- a/samples/Sentry.Samples.AspNetCore.Grpc/Program.cs
+++ b/samples/Sentry.Samples.AspNetCore.Grpc/Program.cs
@@ -1,69 +1,73 @@
using System.Net;
-using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Sentry.AspNetCore;
using Sentry.AspNetCore.Grpc;
+using Sentry.Samples.AspNetCore.Grpc;
-namespace Sentry.Samples.AspNetCore.Grpc;
+var builder = WebApplication.CreateBuilder(args);
-public static class Program
-{
- public static void Main(string[] args)
+builder.WebHost
+ .UseShutdownTimeout(TimeSpan.FromSeconds(10))
+ .ConfigureKestrel(options =>
{
- BuildWebHost(args).Run();
- }
-
- public static IWebHost BuildWebHost(string[] args) =>
- WebHost.CreateDefaultBuilder(args)
- .UseShutdownTimeout(TimeSpan.FromSeconds(10))
- .ConfigureKestrel(options =>
- {
- // Setup a HTTP/2 endpoint without TLS due to macOS limitation.
- // https://docs.microsoft.com/en-us/aspnet/core/grpc/troubleshoot?view=aspnetcore-5.0#unable-to-start-aspnet-core-grpc-app-on-macos
- options.ListenLocalhost(5000, o => o.Protocols =
- HttpProtocols.Http2);
- })
- .UseStartup()
-
- // Example integration with advanced configuration scenarios:
- .UseSentry(builder =>
- {
- builder.AddGrpc();
- builder.AddSentryOptions(options =>
- {
+ // Setup a HTTP/2 endpoint without TLS due to macOS limitation.
+ // https://docs.microsoft.com/en-us/aspnet/core/grpc/troubleshoot?view=aspnetcore-5.0#unable-to-start-aspnet-core-grpc-app-on-macos
+ options.ListenLocalhost(5000, o => o.Protocols = HttpProtocols.Http2);
+ })
+ .UseSentry(sentryBuilder =>
+ {
+ sentryBuilder.AddGrpc();
+ sentryBuilder.AddSentryOptions(options =>
+ {
#if !SENTRY_DSN_DEFINED_IN_ENV
- // A DSN is required. You can set here in code, via the SENTRY_DSN environment variable or in your
- // appsettings.json file.
- // See https://docs.sentry.io/platforms/dotnet/guides/aspnetcore/#configure
- options.Dsn = SamplesShared.Dsn;
+ // A DSN is required. You can set here in code, via the SENTRY_DSN environment variable or in your
+ // appsettings.json file.
+ // See https://docs.sentry.io/platforms/dotnet/guides/aspnetcore/#configure
+ options.Dsn = SamplesShared.Dsn;
#endif
- // The parameter 'options' here has values populated through the configuration system.
- // That includes 'appsettings.json', environment variables and anything else
- // defined on the ConfigurationBuilder.
- // See: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-2.1&tabs=basicconfiguration
- // Tracks the release which sent the event and enables more features: https://docs.sentry.io/learn/releases/
- // If not explicitly set here, the SDK attempts to read it from: AssemblyInformationalVersionAttribute and AssemblyVersion
- // TeamCity: %build.vcs.number%, VSTS: BUILD_SOURCEVERSION, Travis-CI: TRAVIS_COMMIT, AppVeyor: APPVEYOR_REPO_COMMIT, CircleCI: CIRCLE_SHA1
- options.Release =
- "e386dfd"; // Could also be any format, such as: 2.0, or however version of your app is
+ // The parameter 'options' here has values populated through the configuration system.
+ // That includes 'appsettings.json', environment variables and anything else
+ // defined on the ConfigurationBuilder.
+ // Tracks the release which sent the event and enables more features: https://docs.sentry.io/learn/releases/
+ // If not explicitly set here, the SDK attempts to read it from: AssemblyInformationalVersionAttribute and AssemblyVersion
+ options.Release = "e386dfd";
+
+ // Enable performance monitoring
+ options.TracesSampleRate = 1.0;
+
+ options.MaxBreadcrumbs = 200;
- options.TracesSampleRate = 1.0;
+ // Set a proxy for outgoing HTTP connections
+ options.HttpProxy = null; // new WebProxy("https://localhost:3128");
- options.MaxBreadcrumbs = 200;
+ // Example: Disabling support to compressed responses:
+ options.DecompressionMethods = DecompressionMethods.None;
- // Set a proxy for outgoing HTTP connections
- options.HttpProxy = null; // new WebProxy("https://localhost:3128");
+ options.MaxQueueItems = 100;
+ options.ShutdownTimeout = TimeSpan.FromSeconds(5);
- // Example: Disabling support to compressed responses:
- options.DecompressionMethods = DecompressionMethods.None;
+ // Configures the root scope
+ options.ConfigureScope(s => s.SetTag("Always sent", "this tag"));
+ });
+ });
- options.MaxQueueItems = 100;
- options.ShutdownTimeout = TimeSpan.FromSeconds(5);
+// Services
+builder.Services.AddGrpcReflection();
+builder.Services.AddGrpc();
- // Configures the root scope
- options.ConfigureScope(s => s.SetTag("Always sent", "this tag"));
- });
- })
- .Build();
+var app = builder.Build();
+
+if (app.Environment.IsDevelopment())
+{
+ app.UseDeveloperExceptionPage();
+}
+
+// Map gRPC endpoints
+app.MapGrpcService();
+if (app.Environment.IsDevelopment())
+{
+ app.MapGrpcReflectionService();
}
+
+app.Run();
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 89dc8c6a3e..dfc6c6520d 100644
--- a/samples/Sentry.Samples.AspNetCore.Grpc/Sentry.Samples.AspNetCore.Grpc.csproj
+++ b/samples/Sentry.Samples.AspNetCore.Grpc/Sentry.Samples.AspNetCore.Grpc.csproj
@@ -1,7 +1,7 @@
- net8.0
+ net10.0
diff --git a/samples/Sentry.Samples.AspNetCore.Grpc/Startup.cs b/samples/Sentry.Samples.AspNetCore.Grpc/Startup.cs
deleted file mode 100644
index 5bc4580274..0000000000
--- a/samples/Sentry.Samples.AspNetCore.Grpc/Startup.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-namespace Sentry.Samples.AspNetCore.Grpc;
-
-public class Startup
-{
- // This method gets called by the runtime. Use this method to add services to the container.
- // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
- public void ConfigureServices(IServiceCollection services)
- {
- services.AddGrpcReflection();
- services.AddGrpc();
- }
-
- // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
- public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
- {
- if (env.IsDevelopment())
- {
- app.UseDeveloperExceptionPage();
- }
-
- app.UseRouting();
-
- app.UseEndpoints(endpoints =>
- {
- endpoints.MapGrpcService();
- if (env.IsDevelopment())
- {
- endpoints.MapGrpcReflectionService();
- }
- });
- }
-}
diff --git a/samples/Sentry.Samples.AspNetCore.Mvc/Sentry.Samples.AspNetCore.Mvc.csproj b/samples/Sentry.Samples.AspNetCore.Mvc/Sentry.Samples.AspNetCore.Mvc.csproj
index d3bbf0aa34..d6eb7609a7 100644
--- a/samples/Sentry.Samples.AspNetCore.Mvc/Sentry.Samples.AspNetCore.Mvc.csproj
+++ b/samples/Sentry.Samples.AspNetCore.Mvc/Sentry.Samples.AspNetCore.Mvc.csproj
@@ -1,7 +1,7 @@
- net8.0
+ net10.0
enable
enable
Samples.AspNetCore.Mvc
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 a56d6004e2..5c3cf4c562 100644
--- a/samples/Sentry.Samples.AspNetCore.Serilog/Sentry.Samples.AspNetCore.Serilog.csproj
+++ b/samples/Sentry.Samples.AspNetCore.Serilog/Sentry.Samples.AspNetCore.Serilog.csproj
@@ -1,7 +1,7 @@
- net8.0
+ net10.0
diff --git a/samples/Sentry.Samples.AspNetCore.WebAPI.Profiling/Sentry.Samples.AspNetCore.WebAPI.Profiling.csproj b/samples/Sentry.Samples.AspNetCore.WebAPI.Profiling/Sentry.Samples.AspNetCore.WebAPI.Profiling.csproj
index 6ca934c5fc..68bf35efdd 100644
--- a/samples/Sentry.Samples.AspNetCore.WebAPI.Profiling/Sentry.Samples.AspNetCore.WebAPI.Profiling.csproj
+++ b/samples/Sentry.Samples.AspNetCore.WebAPI.Profiling/Sentry.Samples.AspNetCore.WebAPI.Profiling.csproj
@@ -1,7 +1,7 @@
- net8.0
+ net10.0
enable
enable
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 8d2dd8b221..c9150b1b76 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
@@ -1,6 +1,6 @@
- net8.0
+ net10.0
true
Lambda
diff --git a/samples/Sentry.Samples.Azure.Functions.Worker/Sentry.Samples.Azure.Functions.Worker.csproj b/samples/Sentry.Samples.Azure.Functions.Worker/Sentry.Samples.Azure.Functions.Worker.csproj
index 3145f92db6..1ee89930e3 100644
--- a/samples/Sentry.Samples.Azure.Functions.Worker/Sentry.Samples.Azure.Functions.Worker.csproj
+++ b/samples/Sentry.Samples.Azure.Functions.Worker/Sentry.Samples.Azure.Functions.Worker.csproj
@@ -1,6 +1,6 @@
- net8.0
+ net9.0
v4
Exe
diff --git a/samples/Sentry.Samples.Console.Basic/Sentry.Samples.Console.Basic.csproj b/samples/Sentry.Samples.Console.Basic/Sentry.Samples.Console.Basic.csproj
index bf9efb93d8..44f961dfae 100644
--- a/samples/Sentry.Samples.Console.Basic/Sentry.Samples.Console.Basic.csproj
+++ b/samples/Sentry.Samples.Console.Basic/Sentry.Samples.Console.Basic.csproj
@@ -4,9 +4,9 @@
Exe
enable
enable
- net9.0;net8.0;net462
+ net9.0;net10.0;net462
-
+
true
true
diff --git a/samples/Sentry.Samples.Console.Customized/Sentry.Samples.Console.Customized.csproj b/samples/Sentry.Samples.Console.Customized/Sentry.Samples.Console.Customized.csproj
index ec23ce7173..26a1861513 100644
--- a/samples/Sentry.Samples.Console.Customized/Sentry.Samples.Console.Customized.csproj
+++ b/samples/Sentry.Samples.Console.Customized/Sentry.Samples.Console.Customized.csproj
@@ -2,7 +2,7 @@
Exe
- net8.0
+ net10.0
diff --git a/samples/Sentry.Samples.Console.HeapDump/Sentry.Samples.Console.HeapDump.csproj b/samples/Sentry.Samples.Console.HeapDump/Sentry.Samples.Console.HeapDump.csproj
index cfcc72e664..b88b507419 100644
--- a/samples/Sentry.Samples.Console.HeapDump/Sentry.Samples.Console.HeapDump.csproj
+++ b/samples/Sentry.Samples.Console.HeapDump/Sentry.Samples.Console.HeapDump.csproj
@@ -2,7 +2,7 @@
Exe
- net8.0
+ net10.0
enable
enable
true
@@ -51,5 +51,5 @@
-
+
diff --git a/samples/Sentry.Samples.Console.Native/Sentry.Samples.Console.Native.csproj b/samples/Sentry.Samples.Console.Native/Sentry.Samples.Console.Native.csproj
index e4fd5c44cc..190dd6ac5c 100644
--- a/samples/Sentry.Samples.Console.Native/Sentry.Samples.Console.Native.csproj
+++ b/samples/Sentry.Samples.Console.Native/Sentry.Samples.Console.Native.csproj
@@ -4,7 +4,7 @@
Exe
enable
enable
- net8.0
+ net10.0
true
diff --git a/samples/Sentry.Samples.Console.Profiling/Sentry.Samples.Console.Profiling.csproj b/samples/Sentry.Samples.Console.Profiling/Sentry.Samples.Console.Profiling.csproj
index 82e57fc84e..6f715d10c3 100644
--- a/samples/Sentry.Samples.Console.Profiling/Sentry.Samples.Console.Profiling.csproj
+++ b/samples/Sentry.Samples.Console.Profiling/Sentry.Samples.Console.Profiling.csproj
@@ -2,7 +2,7 @@
Exe
- net8.0
+ net10.0
diff --git a/samples/Sentry.Samples.EntityFramework/Sentry.Samples.EntityFramework.csproj b/samples/Sentry.Samples.EntityFramework/Sentry.Samples.EntityFramework.csproj
index 2e27079e5d..0408fce517 100644
--- a/samples/Sentry.Samples.EntityFramework/Sentry.Samples.EntityFramework.csproj
+++ b/samples/Sentry.Samples.EntityFramework/Sentry.Samples.EntityFramework.csproj
@@ -2,7 +2,7 @@
Exe
- net8.0
+ net10.0
diff --git a/samples/Sentry.Samples.GenericHost/Sentry.Samples.GenericHost.csproj b/samples/Sentry.Samples.GenericHost/Sentry.Samples.GenericHost.csproj
index 78283ed704..d2b5e11219 100644
--- a/samples/Sentry.Samples.GenericHost/Sentry.Samples.GenericHost.csproj
+++ b/samples/Sentry.Samples.GenericHost/Sentry.Samples.GenericHost.csproj
@@ -2,7 +2,7 @@
Exe
- net8.0
+ net9.0
@@ -20,8 +20,6 @@
-
-
-
+
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 c212fcf871..4d6ec1bb59 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
@@ -2,7 +2,7 @@
Exe
- net8.0
+ net10.0
diff --git a/samples/Sentry.Samples.GraphQL.Client.Http/Sentry.Samples.GraphQL.Client.Http.csproj b/samples/Sentry.Samples.GraphQL.Client.Http/Sentry.Samples.GraphQL.Client.Http.csproj
index 183f1fef77..14fee315b4 100644
--- a/samples/Sentry.Samples.GraphQL.Client.Http/Sentry.Samples.GraphQL.Client.Http.csproj
+++ b/samples/Sentry.Samples.GraphQL.Client.Http/Sentry.Samples.GraphQL.Client.Http.csproj
@@ -2,7 +2,7 @@
Exe
- net8.0
+ net10.0
enable
enable
diff --git a/samples/Sentry.Samples.GraphQL.Server/Sentry.Samples.GraphQL.Server.csproj b/samples/Sentry.Samples.GraphQL.Server/Sentry.Samples.GraphQL.Server.csproj
index 2078f97579..c7ed799d41 100644
--- a/samples/Sentry.Samples.GraphQL.Server/Sentry.Samples.GraphQL.Server.csproj
+++ b/samples/Sentry.Samples.GraphQL.Server/Sentry.Samples.GraphQL.Server.csproj
@@ -1,7 +1,7 @@
- net8.0
+ net10.0
enable
enable
diff --git a/samples/Sentry.Samples.Hangfire/Sentry.Samples.Hangfire.csproj b/samples/Sentry.Samples.Hangfire/Sentry.Samples.Hangfire.csproj
index ac7347715a..804f68bb63 100644
--- a/samples/Sentry.Samples.Hangfire/Sentry.Samples.Hangfire.csproj
+++ b/samples/Sentry.Samples.Hangfire/Sentry.Samples.Hangfire.csproj
@@ -1,7 +1,7 @@
- net8.0
+ net10.0
enable
enable
diff --git a/samples/Sentry.Samples.Ios/Sentry.Samples.Ios.csproj b/samples/Sentry.Samples.Ios/Sentry.Samples.Ios.csproj
index e1cd4e8127..55ff9e06b4 100644
--- a/samples/Sentry.Samples.Ios/Sentry.Samples.Ios.csproj
+++ b/samples/Sentry.Samples.Ios/Sentry.Samples.Ios.csproj
@@ -1,7 +1,7 @@
- net9.0-ios
+ net9.0-ios18.0
18
Exe
enable
diff --git a/samples/Sentry.Samples.Log4Net/Sentry.Samples.Log4Net.csproj b/samples/Sentry.Samples.Log4Net/Sentry.Samples.Log4Net.csproj
index 43c4215f48..7f0c0b8178 100644
--- a/samples/Sentry.Samples.Log4Net/Sentry.Samples.Log4Net.csproj
+++ b/samples/Sentry.Samples.Log4Net/Sentry.Samples.Log4Net.csproj
@@ -2,7 +2,7 @@
Exe
- net8.0
+ net10.0
3.5.234
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 0d60cbe639..dfe4a8f252 100644
--- a/samples/Sentry.Samples.ME.Logging/Sentry.Samples.ME.Logging.csproj
+++ b/samples/Sentry.Samples.ME.Logging/Sentry.Samples.ME.Logging.csproj
@@ -2,11 +2,11 @@
Exe
- net8.0
+ net9.0
-
+
diff --git a/samples/Sentry.Samples.MacCatalyst/AppDelegate.cs b/samples/Sentry.Samples.MacCatalyst/AppDelegate.cs
index 044ae0c169..1be71dc6d4 100644
--- a/samples/Sentry.Samples.MacCatalyst/AppDelegate.cs
+++ b/samples/Sentry.Samples.MacCatalyst/AppDelegate.cs
@@ -11,7 +11,7 @@ public override UIWindow? Window
set;
}
- public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
+ public override bool FinishedLaunching(UIApplication application, NSDictionary? launchOptions)
{
// Init the Sentry SDK
SentrySdk.Init(options =>
diff --git a/samples/Sentry.Samples.MacCatalyst/Sentry.Samples.MacCatalyst.csproj b/samples/Sentry.Samples.MacCatalyst/Sentry.Samples.MacCatalyst.csproj
index deacfb2b37..0ba0946ebf 100644
--- a/samples/Sentry.Samples.MacCatalyst/Sentry.Samples.MacCatalyst.csproj
+++ b/samples/Sentry.Samples.MacCatalyst/Sentry.Samples.MacCatalyst.csproj
@@ -1,11 +1,11 @@
- net8.0-maccatalyst17.0
+ net9.0-maccatalyst18.0
Exe
enable
true
- 14.2
+ 15.0
true
diff --git a/samples/Sentry.Samples.MacOS/Sentry.Samples.MacOS.csproj b/samples/Sentry.Samples.MacOS/Sentry.Samples.MacOS.csproj
index 6228d98e39..c2cced507d 100644
--- a/samples/Sentry.Samples.MacOS/Sentry.Samples.MacOS.csproj
+++ b/samples/Sentry.Samples.MacOS/Sentry.Samples.MacOS.csproj
@@ -1,11 +1,11 @@
- net8.0-macos
+ net9.0-macos
Exe
enable
true
- 10.15
+ 12.0
true
osx-arm64
diff --git a/samples/Sentry.Samples.Maui/Sentry.Samples.Maui.csproj b/samples/Sentry.Samples.Maui/Sentry.Samples.Maui.csproj
index 77c4e079ee..add0969475 100644
--- a/samples/Sentry.Samples.Maui/Sentry.Samples.Maui.csproj
+++ b/samples/Sentry.Samples.Maui/Sentry.Samples.Maui.csproj
@@ -6,7 +6,7 @@
On Mac, we'll also build for iOS and MacCatalyst.
On Windows, we'll also build for Windows 10.
-->
- $(TargetFrameworks);net9.0-android35.0
+ $(TargetFrameworks);net9.0-android
$(TargetFrameworks);net9.0-windows10.0.19041.0;net9.0-ios18.0;net9.0-maccatalyst18.0
$(TargetFrameworks);net9.0-ios18.0;net9.0-maccatalyst18.0
Exe
diff --git a/samples/Sentry.Samples.NLog/Sentry.Samples.NLog.csproj b/samples/Sentry.Samples.NLog/Sentry.Samples.NLog.csproj
index 9b7e809ed0..a25df53d21 100644
--- a/samples/Sentry.Samples.NLog/Sentry.Samples.NLog.csproj
+++ b/samples/Sentry.Samples.NLog/Sentry.Samples.NLog.csproj
@@ -2,7 +2,7 @@
Exe
- net8.0
+ net10.0
diff --git a/samples/Sentry.Samples.OpenTelemetry.AspNetCore/Sentry.Samples.OpenTelemetry.AspNetCore.csproj b/samples/Sentry.Samples.OpenTelemetry.AspNetCore/Sentry.Samples.OpenTelemetry.AspNetCore.csproj
index 7159dfcb7e..a07956f015 100644
--- a/samples/Sentry.Samples.OpenTelemetry.AspNetCore/Sentry.Samples.OpenTelemetry.AspNetCore.csproj
+++ b/samples/Sentry.Samples.OpenTelemetry.AspNetCore/Sentry.Samples.OpenTelemetry.AspNetCore.csproj
@@ -1,7 +1,7 @@
- net8.0
+ net10.0
enable
enable
diff --git a/samples/Sentry.Samples.OpenTelemetry.Console/Sentry.Samples.OpenTelemetry.Console.csproj b/samples/Sentry.Samples.OpenTelemetry.Console/Sentry.Samples.OpenTelemetry.Console.csproj
index 8b5c0e9654..0aed012a4f 100644
--- a/samples/Sentry.Samples.OpenTelemetry.Console/Sentry.Samples.OpenTelemetry.Console.csproj
+++ b/samples/Sentry.Samples.OpenTelemetry.Console/Sentry.Samples.OpenTelemetry.Console.csproj
@@ -2,7 +2,7 @@
Exe
- net8.0
+ net10.0
enable
enable
diff --git a/samples/Sentry.Samples.Serilog/Program.cs b/samples/Sentry.Samples.Serilog/Program.cs
index 59ed09548d..c1822a3714 100644
--- a/samples/Sentry.Samples.Serilog/Program.cs
+++ b/samples/Sentry.Samples.Serilog/Program.cs
@@ -25,6 +25,8 @@ private static void Main()
// Error and higher is sent as event (default is Error)
options.MinimumEventLevel = LogEventLevel.Error;
options.AttachStacktrace = true;
+ // send structured logs to Sentry
+ options.Experimental.EnableLogs = true;
// send PII like the username of the user logged in to the device
options.SendDefaultPii = true;
// Optional Serilog text formatter used to format LogEvent to string. If TextFormatter is set, FormatProvider is ignored.
diff --git a/samples/Sentry.Samples.Serilog/Sentry.Samples.Serilog.csproj b/samples/Sentry.Samples.Serilog/Sentry.Samples.Serilog.csproj
index f9df0dc20f..ab1add9334 100644
--- a/samples/Sentry.Samples.Serilog/Sentry.Samples.Serilog.csproj
+++ b/samples/Sentry.Samples.Serilog/Sentry.Samples.Serilog.csproj
@@ -2,7 +2,7 @@
Exe
- net8.0
+ net10.0
3.5.234
diff --git a/scripts/device-test.ps1 b/scripts/device-test.ps1
index 7822b81582..f9d5c30868 100644
--- a/scripts/device-test.ps1
+++ b/scripts/device-test.ps1
@@ -86,8 +86,8 @@ try
{
if (!(Get-Command xharness -ErrorAction SilentlyContinue))
{
- Push-Location ($CI ? $env:RUNNER_TEMP : $IsWindows ? $env:TMP : $IsMacos ? $env:TMPDIR : '/temp')
- dotnet tool install Microsoft.DotNet.XHarness.CLI --global --version '10.0.0-prerelease.25412.1' `
+ Push-Location ($CI ? $env:RUNNER_TEMP : $IsWindows ? $env:TMP : $IsMacos ? $env:TMPDIR : '/tmp')
+ dotnet tool install Microsoft.DotNet.XHarness.CLI --global --version '10.0.0-prerelease.25466.1' `
--add-source https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-eng/nuget/v3/index.json
Pop-Location
}
diff --git a/scripts/generate-solution-filters-config.yaml b/scripts/generate-solution-filters-config.yaml
index 45c95d8f4b..251aca7e7d 100644
--- a/scripts/generate-solution-filters-config.yaml
+++ b/scripts/generate-solution-filters-config.yaml
@@ -32,6 +32,11 @@ groupConfigs:
trimTests:
- "**/Sentry.TrimTest.csproj"
- "**/Sentry.MauiTrimTest.csproj"
+ mobileOnly:
+ - "**/*Android*.csproj"
+ - "**/*Ios*.csproj"
+ - "**/*MacCatalyst*.csproj"
+ - "**/*Maui*.csproj"
filterConfigs:
@@ -61,6 +66,7 @@ filterConfigs:
- "windowsOnly"
- "artefacts"
- "trimTests"
+ - "mobileOnly"
patterns:
- "**/*Android*.csproj"
- "**/*DeviceTests*.csproj"
@@ -240,6 +246,7 @@ filterConfigs:
groups:
- "artefacts"
- "trimTests"
+ - "mobileOnly"
patterns:
- "**/*Bindings*.csproj"
- "**/*Android*.csproj"
diff --git a/scripts/install-libssl1.sh b/scripts/install-libssl1.sh
new file mode 100755
index 0000000000..7097e1cd6b
--- /dev/null
+++ b/scripts/install-libssl1.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+set -euo pipefail
+
+# Install old deprecated libssl 1.x for .NET 5.0 on Linux to avoid:
+# Error: 'No usable version of libssl was found'
+
+if apk --version >/dev/null 2>&1; then
+ # Alpine Linux: openssl1.1-compat from the community repo
+ apk add --repository=https://dl-cdn.alpinelinux.org/alpine/v3.18/community openssl1.1-compat
+elif dpkg --version >/dev/null 2>&1; then
+ # Ubuntu: libssl1 from focal-security
+ # https://github.com/actions/runner-images/blob/d43555be6577f2ac4e4f78bf683c520687891e1b/images/ubuntu/scripts/build/install-sqlpackage.sh#L11-L21
+ if [ "$(dpkg --print-architecture)" = "arm64" ]; then
+ echo "deb http://ports.ubuntu.com/ubuntu-ports focal-security main" | tee /etc/apt/sources.list.d/focal-security.list
+ else
+ echo "deb http://security.ubuntu.com/ubuntu focal-security main" | tee /etc/apt/sources.list.d/focal-security.list
+ fi
+ apt-get update
+ apt-get install -y --no-install-recommends libssl1.1
+ rm /etc/apt/sources.list.d/focal-security.list
+ apt-get update
+fi
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index a63860aea1..e3e1feb526 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -36,7 +36,7 @@
-
+
true
diff --git a/src/Sentry.Android.AssemblyReader/AndroidAssemblyReaderFactory.cs b/src/Sentry.Android.AssemblyReader/AndroidAssemblyReaderFactory.cs
index e111a46f71..2e4b8d03ab 100644
--- a/src/Sentry.Android.AssemblyReader/AndroidAssemblyReaderFactory.cs
+++ b/src/Sentry.Android.AssemblyReader/AndroidAssemblyReaderFactory.cs
@@ -1,4 +1,3 @@
-using Sentry.Android.AssemblyReader.V1;
using Sentry.Android.AssemblyReader.V2;
namespace Sentry.Android.AssemblyReader;
@@ -19,28 +18,13 @@ public static IAndroidAssemblyReader Open(string apkPath, IList supporte
{
logger?.Invoke(DebugLoggerLevel.Debug, "Opening APK: {0}", apkPath);
-#if NET9_0
- logger?.Invoke(DebugLoggerLevel.Debug, "Reading files using V2 APK layout.");
- if (AndroidAssemblyStoreReaderV2.TryReadStore(apkPath, supportedAbis, logger, out var readerV2))
+ if (AndroidAssemblyStoreReader.TryReadStore(apkPath, supportedAbis, logger, out var readerV2))
{
- logger?.Invoke(DebugLoggerLevel.Debug, "APK uses AssemblyStore V2");
+ logger?.Invoke(DebugLoggerLevel.Debug, "APK uses AssemblyStore");
return readerV2;
}
logger?.Invoke(DebugLoggerLevel.Debug, "APK doesn't use AssemblyStore");
- return new AndroidAssemblyDirectoryReaderV2(apkPath, supportedAbis, logger);
-#else
- logger?.Invoke(DebugLoggerLevel.Debug, "Reading files using V1 APK layout.");
-
- var zipArchive = ZipFile.OpenRead(apkPath);
- if (zipArchive.GetEntry("assemblies/assemblies.manifest") is not null)
- {
- logger?.Invoke(DebugLoggerLevel.Debug, "APK uses AssemblyStore V1");
- return new AndroidAssemblyStoreReaderV1(zipArchive, supportedAbis, logger);
- }
-
- logger?.Invoke(DebugLoggerLevel.Debug, "APK doesn't use AssemblyStore");
- return new AndroidAssemblyDirectoryReaderV1(zipArchive, supportedAbis, logger);
-#endif
+ return new AndroidAssemblyDirectoryReader(apkPath, supportedAbis, logger);
}
}
diff --git a/src/Sentry.Android.AssemblyReader/Sentry.Android.AssemblyReader.csproj b/src/Sentry.Android.AssemblyReader/Sentry.Android.AssemblyReader.csproj
index 2c6dcde33e..fa94fec130 100644
--- a/src/Sentry.Android.AssemblyReader/Sentry.Android.AssemblyReader.csproj
+++ b/src/Sentry.Android.AssemblyReader/Sentry.Android.AssemblyReader.csproj
@@ -1,7 +1,7 @@
- net8.0;net9.0
+ $(PreviousTfm);$(LatestTfm)
.NET assembly reader for Android
diff --git a/src/Sentry.Android.AssemblyReader/V1/ATTRIBUTION.txt b/src/Sentry.Android.AssemblyReader/V1/ATTRIBUTION.txt
deleted file mode 100644
index 16c3f15ed7..0000000000
--- a/src/Sentry.Android.AssemblyReader/V1/ATTRIBUTION.txt
+++ /dev/null
@@ -1,31 +0,0 @@
-Parts of the code in this subdirectory have been adapted from
-https://github.com/xamarin/xamarin-android/blob/c92702619f5fabcff0ed88e09160baf9edd70f41/tools/assembly-store-reader/AssemblyStoreExplorer.cs
-
-The original license is as follows:
-
-
-Xamarin.Android SDK
-
-The MIT License (MIT)
-
-Copyright (c) .NET Foundation Contributors
-
-All rights reserved.
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/src/Sentry.Android.AssemblyReader/V1/AndroidAssemblyDirectoryReaderV1.cs b/src/Sentry.Android.AssemblyReader/V1/AndroidAssemblyDirectoryReaderV1.cs
deleted file mode 100644
index 7b5e1c1a7e..0000000000
--- a/src/Sentry.Android.AssemblyReader/V1/AndroidAssemblyDirectoryReaderV1.cs
+++ /dev/null
@@ -1,53 +0,0 @@
-namespace Sentry.Android.AssemblyReader.V1;
-
-// The "Old" app type - where each DLL is placed in the 'assemblies' directory as an individual file.
-internal sealed class AndroidAssemblyDirectoryReaderV1 : AndroidAssemblyReader, IAndroidAssemblyReader
-{
- public AndroidAssemblyDirectoryReaderV1(ZipArchive zip, IList supportedAbis, DebugLogger? logger)
- : base(zip, supportedAbis, logger) { }
-
- public PEReader? TryReadAssembly(string name)
- {
- if (File.Exists(name))
- {
- // The assembly is already extracted to the file system. Just read it.
- var stream = File.OpenRead(name);
- return new PEReader(stream);
- }
-
- var zipEntry = FindAssembly(name);
- if (zipEntry is null)
- {
- Logger?.Invoke(DebugLoggerLevel.Debug, "Couldn't find assembly {0} in the APK", name);
- return null;
- }
-
- Logger?.Invoke(DebugLoggerLevel.Debug, "Resolved assembly {0} in the APK at {1}", name, zipEntry.FullName);
-
- // We need a seekable stream for the PEReader (or even to check whether the DLL is compressed), so make a copy.
- var memStream = zipEntry.Extract();
- return ArchiveUtils.CreatePEReader(name, memStream, Logger);
- }
-
- private ZipArchiveEntry? FindAssembly(string name)
- {
- var zipEntry = ZipArchive.GetEntry($"assemblies/{name}");
-
- if (zipEntry is null)
- {
- foreach (var abi in SupportedAbis)
- {
- if (abi.Length > 0)
- {
- zipEntry = ZipArchive.GetEntry($"assemblies/{abi}/{name}");
- if (zipEntry is not null)
- {
- break;
- }
- }
- }
- }
-
- return zipEntry;
- }
-}
diff --git a/src/Sentry.Android.AssemblyReader/V1/AndroidAssemblyStoreReaderV1.cs b/src/Sentry.Android.AssemblyReader/V1/AndroidAssemblyStoreReaderV1.cs
deleted file mode 100644
index 60c947b23e..0000000000
--- a/src/Sentry.Android.AssemblyReader/V1/AndroidAssemblyStoreReaderV1.cs
+++ /dev/null
@@ -1,510 +0,0 @@
-namespace Sentry.Android.AssemblyReader.V1;
-
-// See https://devblogs.microsoft.com/dotnet/performance-improvements-in-dotnet-maui/#single-file-assembly-stores
-internal sealed class AndroidAssemblyStoreReaderV1 : AndroidAssemblyReader, IAndroidAssemblyReader
-{
- private readonly AssemblyStoreExplorer _explorer;
-
- public AndroidAssemblyStoreReaderV1(ZipArchive zip, IList supportedAbis, DebugLogger? logger)
- : base(zip, supportedAbis, logger)
- {
- _explorer = new(zip, supportedAbis, logger);
- }
-
- public PEReader? TryReadAssembly(string name)
- {
- var assembly = TryFindAssembly(name);
- if (assembly is null)
- {
- Logger?.Invoke(DebugLoggerLevel.Debug, "Couldn't find assembly {0} in the APK AssemblyStore", name);
- return null;
- }
-
- Logger?.Invoke(DebugLoggerLevel.Debug, "Resolved assembly {0} in the APK {1} AssemblyStore", name, assembly.Store.Arch);
-
- var stream = assembly.GetImage();
- if (stream is null)
- {
- Logger?.Invoke(DebugLoggerLevel.Debug, "Couldn't access assembly {0} image stream", name);
- return null;
- }
-
- return ArchiveUtils.CreatePEReader(name, stream, Logger);
- }
-
- private AssemblyStoreAssembly? TryFindAssembly(string name)
- {
- if (_explorer.AssembliesByName.TryGetValue(name, out var assembly))
- {
- return assembly;
- }
-
- if (name.EndsWith(".dll", ignoreCase: true, CultureInfo.InvariantCulture) ||
- name.EndsWith(".exe", ignoreCase: true, CultureInfo.InvariantCulture))
- {
- if (_explorer.AssembliesByName.TryGetValue(name.Substring(0, name.Length - 4), out assembly))
- {
- return assembly;
- }
- }
-
- return null;
- }
-
- // Adapted from https://github.com/xamarin/xamarin-android/blob/c92702619f5fabcff0ed88e09160baf9edd70f41/tools/assembly-store-reader/AssemblyStoreExplorer.cs
- // With the original code licensed under MIT License (https://github.com/xamarin/xamarin-android/blob/2bd13c4a00ae78db34663a4b9c7a4c5bfb20c344/LICENSE).
- private class AssemblyStoreExplorer
- {
- private AssemblyStoreReader? _indexStore;
- private readonly AssemblyStoreManifestReader _manifest;
- private readonly DebugLogger? _logger;
-
- public IDictionary AssembliesByName { get; } =
- new SortedDictionary(StringComparer.OrdinalIgnoreCase);
-
- public IDictionary AssembliesByHash32 { get; } =
- new Dictionary();
-
- public IDictionary AssembliesByHash64 { get; } =
- new Dictionary();
-
- public List Assemblies { get; } = new();
-
- public IDictionary> Stores { get; } =
- new SortedDictionary>();
-
- public AssemblyStoreExplorer(ZipArchive zip, IList supportedAbis, DebugLogger? logger)
- {
- _logger = logger;
- _manifest = new AssemblyStoreManifestReader(zip.GetEntry("assemblies/assemblies.manifest")!.Open());
-
- TryAddStore(zip, null);
- foreach (var abi in supportedAbis)
- {
- if (!string.IsNullOrEmpty(abi))
- {
- TryAddStore(zip, abi);
- }
- }
-
- zip.Dispose();
- ProcessStores();
- }
-
- private void ProcessStores()
- {
- if (Stores.Count == 0 || _indexStore == null)
- {
- return;
- }
-
- ProcessIndex(_indexStore.GlobalIndex32, "32", (he, assembly) =>
- {
- assembly.Hash32 = (uint)he.Hash;
- assembly.RuntimeIndex = he.MappingIndex;
-
- if (_manifest.EntriesByHash32.TryGetValue(assembly.Hash32, out var me))
- {
- assembly.Name = me.Name;
- }
-
- if (!AssembliesByHash32.ContainsKey(assembly.Hash32))
- {
- AssembliesByHash32.Add(assembly.Hash32, assembly);
- }
- });
-
- ProcessIndex(_indexStore.GlobalIndex64, "64", (he, assembly) =>
- {
- assembly.Hash64 = he.Hash;
- if (assembly.RuntimeIndex != he.MappingIndex)
- {
- _logger?.Invoke(DebugLoggerLevel.Debug,
- $"assembly with hashes 0x{assembly.Hash32} and 0x{assembly.Hash64} has a different 32-bit runtime index ({assembly.RuntimeIndex}) than the 64-bit runtime index({he.MappingIndex})");
- }
-
- if (_manifest.EntriesByHash64.TryGetValue(assembly.Hash64, out var me))
- {
- if (string.IsNullOrEmpty(assembly.Name))
- {
- _logger?.Invoke(DebugLoggerLevel.Debug,
- $"32-bit hash 0x{assembly.Hash32:x} did not match any assembly name in the manifest");
- assembly.Name = me.Name;
- if (string.IsNullOrEmpty(assembly.Name))
- {
- _logger?.Invoke(DebugLoggerLevel.Debug,
- $"64-bit hash 0x{assembly.Hash64:x} did not match any assembly name in the manifest");
- }
- }
- else if (!string.Equals(assembly.Name, me.Name, StringComparison.Ordinal))
- {
- _logger?.Invoke(DebugLoggerLevel.Debug,
- $"32-bit hash 0x{assembly.Hash32:x} maps to assembly name '{assembly.Name}', however 64-bit hash 0x{assembly.Hash64:x} for the same entry matches assembly name '{me.Name}'");
- }
- }
-
- if (!AssembliesByHash64.ContainsKey(assembly.Hash64))
- {
- AssembliesByHash64.Add(assembly.Hash64, assembly);
- }
- });
-
- foreach (var kvp in Stores)
- {
- var list = kvp.Value;
- if (list.Count < 2)
- {
- continue;
- }
-
- var template = list[0];
- for (var i = 1; i < list.Count; i++)
- {
- var other = list[i];
- if (!template.HasIdenticalContent(other))
- {
- throw new Exception(
- $"Store ID {template.StoreId} for architecture {other.Arch} is not identical to other stores with the same ID");
- }
- }
- }
-
- void ProcessIndex(List index, string bitness,
- Action assemblyHandler)
- {
- foreach (var he in index)
- {
- if (!Stores.TryGetValue(he.StoreId, out var storeList))
- {
- _logger?.Invoke(DebugLoggerLevel.Debug, $"store with id {he.StoreId} not part of the set");
- continue;
- }
-
- foreach (var store in storeList)
- {
- if (he.LocalStoreIndex >= (uint)store.Assemblies.Count)
- {
- _logger?.Invoke(DebugLoggerLevel.Debug,
- $"{bitness}-bit index entry with hash 0x{he.Hash:x} has invalid store {store.StoreId} index {he.LocalStoreIndex} (maximum allowed is {store.Assemblies.Count})");
- continue;
- }
-
- var assembly = store.Assemblies[(int)he.LocalStoreIndex];
- assemblyHandler(he, assembly);
-
- if (!AssembliesByName.ContainsKey(assembly.Name))
- {
- AssembliesByName.Add(assembly.Name, assembly);
- }
- }
- }
- }
- }
-
- private void TryAddStore(ZipArchive archive, string? abi)
- {
- var infix = abi is null ? "" : $".{abi}";
- if (archive.GetEntry($"assemblies/assemblies{infix}.blob") is { } zipEntry)
- {
- var memStream = new MemoryStream((int)zipEntry.Length);
- using (var zipStream = zipEntry.Open())
- {
- zipStream.CopyTo(memStream);
- memStream.Position = 0;
- }
-
- AddStore(new AssemblyStoreReader(memStream, abi));
- }
- }
-
- private void AddStore(AssemblyStoreReader reader)
- {
- if (reader.HasGlobalIndex)
- {
- _indexStore = reader;
- }
-
- if (!Stores.TryGetValue(reader.StoreId, out var storeList))
- {
- storeList = new List();
- Stores.Add(reader.StoreId, storeList);
- }
-
- storeList.Add(reader);
-
- Assemblies.AddRange(reader.Assemblies);
- }
- }
-
- // Adapted from https://github.com/xamarin/xamarin-android/blob/c92702619f5fabcff0ed88e09160baf9edd70f41/tools/assembly-store-reader/AssemblyStoreManifestReader.cs
- // With the original code licensed under MIT License (https://github.com/xamarin/xamarin-android/blob/2bd13c4a00ae78db34663a4b9c7a4c5bfb20c344/LICENSE).
- private class AssemblyStoreManifestReader
- {
- public List Entries { get; } = new();
- public Dictionary EntriesByHash32 { get; } = new();
- public Dictionary EntriesByHash64 { get; } = new();
-
- public AssemblyStoreManifestReader(Stream manifest)
- {
- using var sr = new StreamReader(manifest, Encoding.UTF8, detectEncodingFromByteOrderMarks: false);
- ReadManifest(sr);
- }
-
- private void ReadManifest(StreamReader reader)
- {
- // First line is ignored, it contains headers
- reader.ReadLine();
-
- // Each subsequent line consists of fields separated with any number of spaces (for the pleasure of a human being reading the manifest)
- while (!reader.EndOfStream)
- {
- var fields = reader.ReadLine()?.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
- if (fields == null)
- {
- continue;
- }
-
- var entry = new AssemblyStoreManifestEntry(fields);
- Entries.Add(entry);
- if (entry.Hash32 != 0)
- {
- EntriesByHash32.Add(entry.Hash32, entry);
- }
-
- if (entry.Hash64 != 0)
- {
- EntriesByHash64.Add(entry.Hash64, entry);
- }
- }
- }
- }
-
- // Adapted from https://github.com/xamarin/xamarin-android/blob/c92702619f5fabcff0ed88e09160baf9edd70f41/tools/assembly-store-reader/AssemblyStoreManifestEntry.cs
- // With the original code licensed under MIT License (https://github.com/xamarin/xamarin-android/blob/2bd13c4a00ae78db34663a4b9c7a4c5bfb20c344/LICENSE).
- private class AssemblyStoreManifestEntry
- {
- // Fields are:
- // Hash 32 | Hash 64 | Store ID | Store idx | Name
- private const int NumberOfFields = 5;
- private const int Hash32FieldIndex = 0;
- private const int Hash64FieldIndex = 1;
- private const int StoreIdFieldIndex = 2;
- private const int StoreIndexFieldIndex = 3;
- private const int NameFieldIndex = 4;
-
- public uint Hash32 { get; }
- public ulong Hash64 { get; }
- public uint StoreId { get; }
- public uint IndexInStore { get; }
- public string Name { get; }
-
- public AssemblyStoreManifestEntry(string[] fields)
- {
- if (fields.Length != NumberOfFields)
- {
- throw new ArgumentOutOfRangeException(nameof(fields), "Invalid number of fields");
- }
-
- Hash32 = GetUInt32(fields[Hash32FieldIndex]);
- Hash64 = GetUInt64(fields[Hash64FieldIndex]);
- StoreId = GetUInt32(fields[StoreIdFieldIndex]);
- IndexInStore = GetUInt32(fields[StoreIndexFieldIndex]);
- Name = fields[NameFieldIndex].Trim();
- }
-
- private static uint GetUInt32(string value)
- {
- if (uint.TryParse(PrepHexValue(value), NumberStyles.HexNumber, null, out var hash))
- {
- return hash;
- }
-
- return 0;
- }
-
- private static ulong GetUInt64(string value)
- {
- if (ulong.TryParse(PrepHexValue(value), NumberStyles.HexNumber, null, out var hash))
- {
- return hash;
- }
-
- return 0;
- }
-
- private static string PrepHexValue(string value)
- {
- if (value.StartsWith("0x", StringComparison.Ordinal))
- {
- return value.Substring(2);
- }
-
- return value;
- }
- }
-
- // Adapted from https://github.com/xamarin/xamarin-android/blob/c92702619f5fabcff0ed88e09160baf9edd70f41/tools/assembly-store-reader/AssemblyStoreHashEntry.cs
- // With the original code licensed under MIT License (https://github.com/xamarin/xamarin-android/blob/2bd13c4a00ae78db34663a4b9c7a4c5bfb20c344/LICENSE).
- private class AssemblyStoreHashEntry
- {
- public bool Is32Bit { get; }
- public ulong Hash { get; }
- public uint MappingIndex { get; }
- public uint LocalStoreIndex { get; }
- public uint StoreId { get; }
-
- internal AssemblyStoreHashEntry(BinaryReader reader, bool is32Bit)
- {
- Is32Bit = is32Bit;
- Hash = reader.ReadUInt64();
- MappingIndex = reader.ReadUInt32();
- LocalStoreIndex = reader.ReadUInt32();
- StoreId = reader.ReadUInt32();
- }
- }
-
- // Adapted from https://github.com/xamarin/xamarin-android/blob/c92702619f5fabcff0ed88e09160baf9edd70f41/tools/assembly-store-reader/AssemblyStoreReader.cs
- // With the original code licensed under MIT License (https://github.com/xamarin/xamarin-android/blob/2bd13c4a00ae78db34663a4b9c7a4c5bfb20c344/LICENSE).
- private class AssemblyStoreReader
- {
- // These two constants must be identical to the native ones in src/monodroid/jni/xamarin-app.hh
- private const uint ASSEMBLY_STORE_MAGIC = 0x41424158; // 'XABA', little-endian
- private const uint ASSEMBLY_STORE_FORMAT_VERSION = 1; // The highest format version this reader understands
-
- private readonly MemoryStream _storeData;
-
- public uint Version { get; private set; }
- public uint LocalEntryCount { get; private set; }
- public uint GlobalEntryCount { get; private set; }
- public uint StoreId { get; private set; }
- public List Assemblies { get; }
- public List GlobalIndex32 { get; } = new();
- public List GlobalIndex64 { get; } = new();
- public string Arch { get; }
-
- public bool HasGlobalIndex => StoreId == 0;
-
- public AssemblyStoreReader(MemoryStream store, string? arch = null)
- {
- Arch = arch ?? string.Empty;
- _storeData = store;
- using var reader = new BinaryReader(store, Encoding.UTF8, leaveOpen: true);
- ReadHeader(reader);
-
- Assemblies = new List();
- ReadLocalEntries(reader, Assemblies);
- if (HasGlobalIndex)
- {
- ReadGlobalIndex(reader, GlobalIndex32, GlobalIndex64);
- }
- }
-
- internal MemoryStream? GetAssemblyImageSlice(AssemblyStoreAssembly assembly) =>
- GetDataSlice(assembly.DataOffset, assembly.DataSize);
-
- internal MemoryStream? GetAssemblyDebugDataSlice(AssemblyStoreAssembly assembly) =>
- assembly.DebugDataOffset == 0 ? null : GetDataSlice(assembly.DebugDataOffset, assembly.DebugDataSize);
-
- internal MemoryStream? GetAssemblyConfigSlice(AssemblyStoreAssembly assembly) =>
- assembly.ConfigDataOffset == 0 ? null : GetDataSlice(assembly.ConfigDataOffset, assembly.ConfigDataSize);
-
- private MemoryStream? GetDataSlice(uint offset, uint size) =>
- size == 0 ? null : new ArchiveUtils.MemorySlice(_storeData, (int)offset, (int)size);
-
- public bool HasIdenticalContent(AssemblyStoreReader other)
- {
- return
- other.Version == Version &&
- other.LocalEntryCount == LocalEntryCount &&
- other.GlobalEntryCount == GlobalEntryCount &&
- other.StoreId == StoreId &&
- other.Assemblies.Count == Assemblies.Count &&
- other.GlobalIndex32.Count == GlobalIndex32.Count &&
- other.GlobalIndex64.Count == GlobalIndex64.Count;
- }
-
- private void ReadHeader(BinaryReader reader)
- {
- if (reader.ReadUInt32() != ASSEMBLY_STORE_MAGIC)
- {
- throw new InvalidOperationException("Invalid header magic number");
- }
-
- Version = reader.ReadUInt32();
- if (Version == 0)
- {
- throw new InvalidOperationException("Invalid version number: 0");
- }
-
- if (Version > ASSEMBLY_STORE_FORMAT_VERSION)
- {
- throw new InvalidOperationException(
- $"Store format version {Version} is higher than the one understood by this reader, {ASSEMBLY_STORE_FORMAT_VERSION}");
- }
-
- LocalEntryCount = reader.ReadUInt32();
- GlobalEntryCount = reader.ReadUInt32();
- StoreId = reader.ReadUInt32();
- }
-
- private void ReadLocalEntries(BinaryReader reader, List assemblies)
- {
- for (uint i = 0; i < LocalEntryCount; i++)
- {
- assemblies.Add(new AssemblyStoreAssembly(reader, this));
- }
- }
-
- private void ReadGlobalIndex(BinaryReader reader, List index32,
- List index64)
- {
- ReadIndex(true, index32);
- ReadIndex(false, index64);
-
- void ReadIndex(bool is32Bit, List index)
- {
- for (uint i = 0; i < GlobalEntryCount; i++)
- {
- index.Add(new AssemblyStoreHashEntry(reader, is32Bit));
- }
- }
- }
- }
-
- // Adapted from https://github.com/xamarin/xamarin-android/blob/c92702619f5fabcff0ed88e09160baf9edd70f41/tools/assembly-store-reader/AssemblyStoreAssembly.cs
- // With the original code licensed under MIT License (https://github.com/xamarin/xamarin-android/blob/2bd13c4a00ae78db34663a4b9c7a4c5bfb20c344/LICENSE).
- private class AssemblyStoreAssembly
- {
- public uint DataOffset { get; }
- public uint DataSize { get; }
- public uint DebugDataOffset { get; }
- public uint DebugDataSize { get; }
- public uint ConfigDataOffset { get; }
- public uint ConfigDataSize { get; }
-
- public uint Hash32 { get; set; }
- public ulong Hash64 { get; set; }
- public string Name { get; set; } = string.Empty;
- public uint RuntimeIndex { get; set; }
-
- public AssemblyStoreReader Store { get; }
-
- internal AssemblyStoreAssembly(BinaryReader reader, AssemblyStoreReader store)
- {
- Store = store;
-
- DataOffset = reader.ReadUInt32();
- DataSize = reader.ReadUInt32();
- DebugDataOffset = reader.ReadUInt32();
- DebugDataSize = reader.ReadUInt32();
- ConfigDataOffset = reader.ReadUInt32();
- ConfigDataSize = reader.ReadUInt32();
- }
-
- public MemoryStream? GetImage() => Store.GetAssemblyImageSlice(this);
-
- public MemoryStream? GetDebugData() => Store.GetAssemblyDebugDataSlice(this);
-
- public MemoryStream? GetAssemblyConfig() => Store.GetAssemblyConfigSlice(this);
- }
-}
diff --git a/src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyDirectoryReaderV2.cs b/src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyDirectoryReader.cs
similarity index 98%
rename from src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyDirectoryReaderV2.cs
rename to src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyDirectoryReader.cs
index b250640d1f..ca2b96d886 100644
--- a/src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyDirectoryReaderV2.cs
+++ b/src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyDirectoryReader.cs
@@ -1,13 +1,13 @@
namespace Sentry.Android.AssemblyReader.V2;
// The "Old" app type - where each DLL is placed in the 'assemblies' directory as an individual file.
-internal sealed class AndroidAssemblyDirectoryReaderV2 : IAndroidAssemblyReader
+internal sealed class AndroidAssemblyDirectoryReader : IAndroidAssemblyReader
{
private DebugLogger? Logger { get; }
private HashSet SupportedArchitectures { get; } = new();
private readonly ArchiveAssemblyHelper _archiveAssemblyHelper;
- public AndroidAssemblyDirectoryReaderV2(string apkPath, IList supportedAbis, DebugLogger? logger)
+ public AndroidAssemblyDirectoryReader(string apkPath, IList supportedAbis, DebugLogger? logger)
{
Logger = logger;
foreach (var abi in supportedAbis)
diff --git a/src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyStoreReaderV2.cs b/src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyStoreReader.cs
similarity index 94%
rename from src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyStoreReaderV2.cs
rename to src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyStoreReader.cs
index 2840c7ec67..0e9c6586f4 100644
--- a/src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyStoreReaderV2.cs
+++ b/src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyStoreReader.cs
@@ -1,17 +1,17 @@
namespace Sentry.Android.AssemblyReader.V2;
-internal class AndroidAssemblyStoreReaderV2 : IAndroidAssemblyReader
+internal class AndroidAssemblyStoreReader : IAndroidAssemblyReader
{
private readonly IList _explorers;
private readonly DebugLogger? _logger;
- private AndroidAssemblyStoreReaderV2(IList explorers, DebugLogger? logger)
+ private AndroidAssemblyStoreReader(IList explorers, DebugLogger? logger)
{
_explorers = explorers;
_logger = logger;
}
- public static bool TryReadStore(string inputFile, IList supportedAbis, DebugLogger? logger, [NotNullWhen(true)] out AndroidAssemblyStoreReaderV2? reader)
+ public static bool TryReadStore(string inputFile, IList supportedAbis, DebugLogger? logger, [NotNullWhen(true)] out AndroidAssemblyStoreReader? reader)
{
List supportedExplorers = [];
@@ -67,7 +67,7 @@ public static bool TryReadStore(string inputFile, IList supportedAbis, D
return false;
}
- reader = new AndroidAssemblyStoreReaderV2(supportedExplorers, logger);
+ reader = new AndroidAssemblyStoreReader(supportedExplorers, logger);
return true;
}
diff --git a/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreExplorer.cs b/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreExplorer.cs
index 0ab9c04e2e..5854d3c070 100644
--- a/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreExplorer.cs
+++ b/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreExplorer.cs
@@ -79,28 +79,19 @@ public static (IList? explorers, string? errorMessage) Op
}
private static (IList? explorers, string? errorMessage) OpenAab(FileInfo fi, DebugLogger? logger)
- => OpenCommon(fi, [StoreReaderV2.AabPaths, StoreReader_V1.AabPaths], logger);
+ => OpenCommon(fi, StoreReader.AabPaths, logger);
private static (IList? explorers, string? errorMessage) OpenAabBase(FileInfo fi, DebugLogger? logger)
- => OpenCommon(fi, [StoreReaderV2.AabBasePaths, StoreReader_V1.AabBasePaths], logger);
+ => OpenCommon(fi, StoreReader.AabBasePaths, logger);
private static (IList? explorers, string? errorMessage) OpenApk(FileInfo fi, DebugLogger? logger)
- => OpenCommon(fi, [StoreReaderV2.ApkPaths, StoreReader_V1.ApkPaths], logger);
+ => OpenCommon(fi, StoreReader.ApkPaths, logger);
- private static (IList? explorers, string? errorMessage) OpenCommon(FileInfo fi, List> pathLists, DebugLogger? logger)
+ private static (IList? explorers, string? errorMessage) OpenCommon(FileInfo fi, IList paths, DebugLogger? logger)
{
using var zip = ZipFile.OpenRead(fi.FullName);
-
- foreach (var paths in pathLists)
- {
- var (explorers, errorMessage, pathsFound) = TryLoad(fi, zip, paths, logger);
- if (pathsFound)
- {
- return (explorers, errorMessage);
- }
- }
-
- return (null, "Unable to find any blob entries");
+ var (explorers, errorMessage, pathsFound) = TryLoad(fi, zip, paths, logger);
+ return pathsFound ? (explorers, errorMessage) : (null, "Unable to find any blob entries");
}
private static (IList? explorers, string? errorMessage, bool pathsFound) TryLoad(FileInfo fi, ZipArchive zip, IList paths, DebugLogger? logger)
diff --git a/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreItem.cs b/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreItem.cs
index a132f51ead..2ecde5fd48 100644
--- a/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreItem.cs
+++ b/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreItem.cs
@@ -1,5 +1,7 @@
/*
* Adapted from https://github.com/dotnet/android/blob/86260ed36dfe1a90c8ed6a2bb1cd0607d637f403/tools/assembly-store-reader-mk2/AssemblyStore/AssemblyStoreItem.cs
+ * Updated from https://github.com/dotnet/android/blob/64018e13e53cec7246e54866b520d3284de344e0/tools/assembly-store-reader-mk2/AssemblyStore/AssemblyStoreItem.cs
+ * - Adding support for AssemblyStore v3 format that shipped in .NET 10 (https://github.com/dotnet/android/pull/10249)
* Original code licensed under the MIT License (https://github.com/dotnet/android/blob/5ebcb1dd1503648391e3c0548200495f634d90c6/LICENSE.TXT)
*/
@@ -17,11 +19,13 @@ internal abstract class AssemblyStoreItem
public uint ConfigOffset { get; protected set; }
public uint ConfigSize { get; protected set; }
public AndroidTargetArch TargetArch { get; protected set; }
+ public bool Ignore { get; }
- protected AssemblyStoreItem(string name, bool is64Bit, List hashes)
+ protected AssemblyStoreItem(string name, bool is64Bit, List hashes, bool ignore)
{
Name = name;
Hashes = hashes.AsReadOnly();
Is64Bit = is64Bit;
+ Ignore = ignore;
}
}
diff --git a/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreReader.cs b/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreReader.cs
index 834cb2c36d..a3201754e1 100644
--- a/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreReader.cs
+++ b/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreReader.cs
@@ -32,23 +32,7 @@ protected AssemblyStoreReader(Stream store, string path, DebugLogger? logger)
public static AssemblyStoreReader? Create(Stream store, string path, DebugLogger? logger)
{
- var reader = MakeReaderReady(new StoreReader_V1(store, path, logger));
- if (reader != null)
- {
- return reader;
- }
-
- reader = MakeReaderReady(new StoreReaderV2(store, path, logger));
- if (reader != null)
- {
- return reader;
- }
-
- return null;
- }
-
- private static AssemblyStoreReader? MakeReaderReady(AssemblyStoreReader reader)
- {
+ var reader = new StoreReader(store, path, logger);
if (!reader.IsSupported())
{
return null;
diff --git a/src/Sentry.Android.AssemblyReader/V2/StoreReaderV2.Classes.cs b/src/Sentry.Android.AssemblyReader/V2/StoreReader.Classes.cs
similarity index 80%
rename from src/Sentry.Android.AssemblyReader/V2/StoreReaderV2.Classes.cs
rename to src/Sentry.Android.AssemblyReader/V2/StoreReader.Classes.cs
index 85aa91ba89..5ed713b6be 100644
--- a/src/Sentry.Android.AssemblyReader/V2/StoreReaderV2.Classes.cs
+++ b/src/Sentry.Android.AssemblyReader/V2/StoreReader.Classes.cs
@@ -1,11 +1,13 @@
/*
* Adapted from https://github.com/dotnet/android/blob/5ebcb1dd1503648391e3c0548200495f634d90c6/tools/assembly-store-reader-mk2/AssemblyStore/StoreReader_V2.Classes.cs
+ * Updated from https://github.com/dotnet/android/blob/64018e13e53cec7246e54866b520d3284de344e0/tools/assembly-store-reader-mk2/AssemblyStore/StoreReader_V2.Classes.cs
+ * - Adding support for AssemblyStore v3 format that shipped in .NET 10 (https://github.com/dotnet/android/pull/10249)
* Original code licensed under the MIT License (https://github.com/dotnet/android/blob/5ebcb1dd1503648391e3c0548200495f634d90c6/LICENSE.TXT)
*/
namespace Sentry.Android.AssemblyReader.V2;
-internal partial class StoreReaderV2
+internal partial class StoreReader
{
private sealed class Header
{
@@ -33,11 +35,13 @@ private sealed class IndexEntry
{
public readonly ulong name_hash;
public readonly uint descriptor_index;
+ public readonly bool ignore;
- public IndexEntry(ulong name_hash, uint descriptor_index)
+ public IndexEntry(ulong name_hash, uint descriptor_index, bool ignore)
{
this.name_hash = name_hash;
this.descriptor_index = descriptor_index;
+ this.ignore = ignore;
}
}
@@ -57,8 +61,9 @@ private sealed class EntryDescriptor
private sealed class StoreItemV2 : AssemblyStoreItem
{
- public StoreItemV2(AndroidTargetArch targetArch, string name, bool is64Bit, List indexEntries, EntryDescriptor descriptor)
- : base(name, is64Bit, IndexToHashes(indexEntries))
+ public StoreItemV2(AndroidTargetArch targetArch, string name, bool is64Bit, List indexEntries,
+ EntryDescriptor descriptor, bool ignore)
+ : base(name, is64Bit, IndexToHashes(indexEntries), ignore)
{
DataOffset = descriptor.data_offset;
DataSize = descriptor.data_size;
@@ -86,11 +91,13 @@ private sealed class TemporaryItem
public readonly string Name;
public readonly List IndexEntries = new List();
public readonly EntryDescriptor Descriptor;
+ public readonly bool Ignored;
- public TemporaryItem(string name, EntryDescriptor descriptor)
+ public TemporaryItem(string name, EntryDescriptor descriptor, bool ignored)
{
Name = name;
Descriptor = descriptor;
+ Ignored = ignored;
}
}
}
diff --git a/src/Sentry.Android.AssemblyReader/V2/StoreReaderV2.cs b/src/Sentry.Android.AssemblyReader/V2/StoreReader.cs
similarity index 90%
rename from src/Sentry.Android.AssemblyReader/V2/StoreReaderV2.cs
rename to src/Sentry.Android.AssemblyReader/V2/StoreReader.cs
index 97bac95694..5220bf7dc7 100644
--- a/src/Sentry.Android.AssemblyReader/V2/StoreReaderV2.cs
+++ b/src/Sentry.Android.AssemblyReader/V2/StoreReader.cs
@@ -1,15 +1,22 @@
/*
* Adapted from https://github.com/dotnet/android/blob/5ebcb1dd1503648391e3c0548200495f634d90c6/tools/assembly-store-reader-mk2/AssemblyStore/StoreReader_V2.cs
+ * Updated from https://github.com/dotnet/android/blob/64018e13e53cec7246e54866b520d3284de344e0/tools/assembly-store-reader-mk2/AssemblyStore/StoreReader_V2.cs
+ * - Adding support for AssemblyStore v3 format that shipped in .NET 10 (https://github.com/dotnet/android/pull/10249)
* Original code licensed under the MIT License (https://github.com/dotnet/android/blob/5ebcb1dd1503648391e3c0548200495f634d90c6/LICENSE.TXT)
*/
namespace Sentry.Android.AssemblyReader.V2;
-internal partial class StoreReaderV2 : AssemblyStoreReader
+internal partial class StoreReader : AssemblyStoreReader
{
// Bit 31 is set for 64-bit platforms, cleared for the 32-bit ones
+#if NET9_0
private const uint ASSEMBLY_STORE_FORMAT_VERSION_64BIT = 0x80000002; // Must match the ASSEMBLY_STORE_FORMAT_VERSION native constant
private const uint ASSEMBLY_STORE_FORMAT_VERSION_32BIT = 0x00000002;
+#else
+ private const uint ASSEMBLY_STORE_FORMAT_VERSION_64BIT = 0x80000003; // Must match the ASSEMBLY_STORE_FORMAT_VERSION native constant
+ private const uint ASSEMBLY_STORE_FORMAT_VERSION_32BIT = 0x00000003;
+#endif
private const uint ASSEMBLY_STORE_FORMAT_VERSION_MASK = 0xF0000000;
private const uint ASSEMBLY_STORE_ABI_AARCH64 = 0x00010000;
private const uint ASSEMBLY_STORE_ABI_ARM = 0x00020000;
@@ -28,7 +35,7 @@ internal partial class StoreReaderV2 : AssemblyStoreReader
private Header? header;
private ulong elfOffset = 0;
- static StoreReaderV2()
+ static StoreReader()
{
var paths = new List {
GetArchPath (AndroidTargetArch.Arm64),
@@ -69,7 +76,7 @@ string GetArchPath(AndroidTargetArch arch, string? root = null)
}
}
- public StoreReaderV2(Stream store, string path, DebugLogger? logger)
+ public StoreReader(Stream store, string path, DebugLogger? logger)
: base(store, path, logger)
{
supportedVersions = new HashSet {
@@ -178,7 +185,12 @@ protected override void Prepare()
}
uint descriptor_index = reader.ReadUInt32();
- index.Add(new IndexEntry(name_hash, descriptor_index));
+#if NET10_0_OR_GREATER
+ bool ignore = reader.ReadByte () != 0;
+#else
+ bool ignore = false;
+#endif
+ index.Add(new IndexEntry(name_hash, descriptor_index, ignore));
}
var descriptors = new List();
@@ -218,7 +230,7 @@ protected override void Prepare()
{
if (!tempItems.TryGetValue(ie.descriptor_index, out TemporaryItem? item))
{
- item = new TemporaryItem(names[(int)ie.descriptor_index], descriptors[(int)ie.descriptor_index]);
+ item = new TemporaryItem(names[(int)ie.descriptor_index], descriptors[(int)ie.descriptor_index], ie.ignore);
tempItems.Add(ie.descriptor_index, item);
}
item.IndexEntries.Add(ie);
@@ -233,7 +245,7 @@ protected override void Prepare()
foreach (var kvp in tempItems)
{
TemporaryItem ti = kvp.Value;
- var item = new StoreItemV2(TargetArch, ti.Name, Is64Bit, ti.IndexEntries, ti.Descriptor);
+ var item = new StoreItemV2(TargetArch, ti.Name, Is64Bit, ti.IndexEntries, ti.Descriptor, ti.Ignored);
storeItems.Add(item);
}
Assemblies = storeItems.AsReadOnly();
diff --git a/src/Sentry.Android.AssemblyReader/V2/StoreReaderV1.cs b/src/Sentry.Android.AssemblyReader/V2/StoreReaderV1.cs
deleted file mode 100644
index c1a3fe3c2d..0000000000
--- a/src/Sentry.Android.AssemblyReader/V2/StoreReaderV1.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Adapted from https://github.com/dotnet/android/blob/5ebcb1dd1503648391e3c0548200495f634d90c6/tools/assembly-store-reader-mk2/AssemblyStore/StoreReader_V1.cs
- * Original code licensed under the MIT License (https://github.com/dotnet/android/blob/5ebcb1dd1503648391e3c0548200495f634d90c6/LICENSE.TXT)
- */
-
-namespace Sentry.Android.AssemblyReader.V2;
-
-internal class StoreReader_V1 : AssemblyStoreReader
-{
- public override string Description => "Assembly store v1";
- public override bool NeedsExtensionInName => false;
-
- public static IList ApkPaths { get; }
- public static IList AabPaths { get; }
- public static IList AabBasePaths { get; }
-
- static StoreReader_V1()
- {
- ApkPaths = new List().AsReadOnly();
- AabPaths = new List().AsReadOnly();
- AabBasePaths = new List().AsReadOnly();
- }
-
- public StoreReader_V1(Stream store, string path, DebugLogger? logger)
- : base(store, path, logger)
- { }
-
- protected override bool IsSupported()
- {
- return false;
- }
-
- protected override void Prepare()
- {
- }
-
- protected override ulong GetStoreStartDataOffset() => 0;
-}
diff --git a/src/Sentry.AspNetCore.Blazor.WebAssembly/Sentry.AspNetCore.Blazor.WebAssembly.csproj b/src/Sentry.AspNetCore.Blazor.WebAssembly/Sentry.AspNetCore.Blazor.WebAssembly.csproj
index 4ff5701fa0..61d6dd2229 100644
--- a/src/Sentry.AspNetCore.Blazor.WebAssembly/Sentry.AspNetCore.Blazor.WebAssembly.csproj
+++ b/src/Sentry.AspNetCore.Blazor.WebAssembly/Sentry.AspNetCore.Blazor.WebAssembly.csproj
@@ -1,7 +1,7 @@
- net9.0;net8.0
+ $(CurrentTfms)
Sentry.AspNetCore.Blazor.WebAssembly
@@ -10,11 +10,16 @@
+
+
+
+
-
-
+
+
+
diff --git a/src/Sentry.AspNetCore.Grpc/Sentry.AspNetCore.Grpc.csproj b/src/Sentry.AspNetCore.Grpc/Sentry.AspNetCore.Grpc.csproj
index cafddf4b7e..3ed88a9201 100644
--- a/src/Sentry.AspNetCore.Grpc/Sentry.AspNetCore.Grpc.csproj
+++ b/src/Sentry.AspNetCore.Grpc/Sentry.AspNetCore.Grpc.csproj
@@ -1,7 +1,7 @@
- net8.0
+ $(CurrentTfms)
$(PackageTags);AspNetCore;gRPC
Official ASP.NET Core gRPC integration for Sentry - Open-source error tracking that helps developers monitor and fix crashes in real time.
diff --git a/src/Sentry.AspNetCore/Sentry.AspNetCore.csproj b/src/Sentry.AspNetCore/Sentry.AspNetCore.csproj
index d75cabef9f..a44b2a9962 100644
--- a/src/Sentry.AspNetCore/Sentry.AspNetCore.csproj
+++ b/src/Sentry.AspNetCore/Sentry.AspNetCore.csproj
@@ -1,7 +1,7 @@
- net9.0;net8.0
+ $(CurrentTfms)
$(PackageTags);AspNetCore;MVC
Official ASP.NET Core integration for Sentry - Open-source error tracking that helps developers monitor and fix crashes in real time.
@@ -11,18 +11,15 @@
true
+
+ $(InterceptorsNamespaces);Microsoft.Extensions.Configuration.Binder.SourceGeneration
+
+
-
-
-
-
-
-
-
diff --git a/src/Sentry.Azure.Functions.Worker/Sentry.Azure.Functions.Worker.csproj b/src/Sentry.Azure.Functions.Worker/Sentry.Azure.Functions.Worker.csproj
index 7d04ce1a0b..c3329b290f 100644
--- a/src/Sentry.Azure.Functions.Worker/Sentry.Azure.Functions.Worker.csproj
+++ b/src/Sentry.Azure.Functions.Worker/Sentry.Azure.Functions.Worker.csproj
@@ -1,7 +1,7 @@
- net8.0;netstandard2.0
+ $(CurrentTfms);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.
diff --git a/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj b/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj
index 0180326bd2..e99fcd08fb 100644
--- a/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj
+++ b/src/Sentry.Bindings.Android/Sentry.Bindings.Android.csproj
@@ -1,7 +1,7 @@
- net8.0-android34.0;net9.0-android35.0
- 8.21.1
+ $(LatestAndroidTfm);$(PreviousAndroidTfm)
+ 8.22.0
$(BaseIntermediateOutputPath)sdks\$(TargetFramework)\Sentry\Android\$(SentryAndroidSdkVersion)\
@@ -43,16 +43,18 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
+
+
@@ -95,37 +86,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
+
diff --git a/src/Sentry.Bindings.Android/Transforms/Metadata.xml b/src/Sentry.Bindings.Android/Transforms/Metadata.xml
index fb6b6558da..35f9c0be20 100644
--- a/src/Sentry.Bindings.Android/Transforms/Metadata.xml
+++ b/src/Sentry.Bindings.Android/Transforms/Metadata.xml
@@ -156,4 +156,7 @@
+
+
+
diff --git a/src/Sentry.Bindings.Cocoa/ApiDefinitions.cs b/src/Sentry.Bindings.Cocoa/ApiDefinitions.cs
index f33b1c6815..2c076ddf1a 100644
--- a/src/Sentry.Bindings.Cocoa/ApiDefinitions.cs
+++ b/src/Sentry.Bindings.Cocoa/ApiDefinitions.cs
@@ -416,9 +416,9 @@ interface SentryEnvelopeItemHeader : SentrySerializable
[Export ("initWithType:length:filenname:contentType:")]
NativeHandle Constructor (string type, nuint length, string filename, string contentType);
- // -(instancetype _Nonnull)initWithType:(NSString * _Nonnull)type length:(NSUInteger)length contentType:(NSString * _Nonnull)contentType itemCount:(NSNumber * _Nonnull)itemCount;
+ // -(instancetype _Nonnull)initWithType:(NSString * _Nonnull)type length:(NSUInteger)length contentType:(NSString * _Nullable)contentType itemCount:(NSNumber * _Nonnull)itemCount;
[Export ("initWithType:length:contentType:itemCount:")]
- NativeHandle Constructor (string type, nuint length, string contentType, NSNumber itemCount);
+ NativeHandle Constructor (string type, nuint length, [NullAllowed] string contentType, NSNumber itemCount);
// @property (readonly, copy, nonatomic) NSString * _Nonnull type;
[Export ("type")]
diff --git a/src/Sentry.Bindings.Cocoa/Sentry.Bindings.Cocoa.csproj b/src/Sentry.Bindings.Cocoa/Sentry.Bindings.Cocoa.csproj
index 98e6fde2d2..10b3899977 100644
--- a/src/Sentry.Bindings.Cocoa/Sentry.Bindings.Cocoa.csproj
+++ b/src/Sentry.Bindings.Cocoa/Sentry.Bindings.Cocoa.csproj
@@ -1,9 +1,9 @@
- net8.0-ios17.0;net8.0-maccatalyst17.0
- net8.0-ios17.0
- net8.0-maccatalyst17.0
+ $(PreviousIosTfm);$(PreviousMacCatalystTfm)
+ $(PreviousIosTfm)
+ $(PreviousMacCatalystTfm)
true
true
.NET Bindings for the Sentry Cocoa SDK
diff --git a/src/Sentry.Bindings.Cocoa/SwiftApiDefinitions.cs b/src/Sentry.Bindings.Cocoa/SwiftApiDefinitions.cs
index 391360da15..1bf7b8ded4 100644
--- a/src/Sentry.Bindings.Cocoa/SwiftApiDefinitions.cs
+++ b/src/Sentry.Bindings.Cocoa/SwiftApiDefinitions.cs
@@ -329,9 +329,6 @@ interface SentryRRWebEvent : SentrySerializable
[Export ("initWithType:timestamp:data:")]
[DesignatedInitializer]
NativeHandle Constructor (SentryRRWebEventType type, NSDate timestamp, [NullAllowed] NSDictionary data);
- // -(NSDictionary * _Nonnull)serialize __attribute__((warn_unused_result("")));
- [Export ("serialize")]
- new NSDictionary Serialize();
}
// @interface SentrySDK : NSObject
diff --git a/src/Sentry.Extensions.Logging/Sentry.Extensions.Logging.csproj b/src/Sentry.Extensions.Logging/Sentry.Extensions.Logging.csproj
index 4d52cd091c..4201207da5 100644
--- a/src/Sentry.Extensions.Logging/Sentry.Extensions.Logging.csproj
+++ b/src/Sentry.Extensions.Logging/Sentry.Extensions.Logging.csproj
@@ -1,7 +1,7 @@
- net9.0;net8.0;netstandard2.0
+ $(CurrentTfms);netstandard2.0
$(PackageTags);Logging;Microsoft.Extensions.Logging
Official Microsoft.Extensions.Logging integration for Sentry - Open-source error tracking that helps developers monitor and fix crashes in real time.
@@ -32,6 +32,12 @@
+
+
+
+
+
+
diff --git a/src/Sentry.Google.Cloud.Functions/Sentry.Google.Cloud.Functions.csproj b/src/Sentry.Google.Cloud.Functions/Sentry.Google.Cloud.Functions.csproj
index 6c455dc928..581bd88f3a 100644
--- a/src/Sentry.Google.Cloud.Functions/Sentry.Google.Cloud.Functions.csproj
+++ b/src/Sentry.Google.Cloud.Functions/Sentry.Google.Cloud.Functions.csproj
@@ -1,7 +1,7 @@
- net9.0;net8.0
+ $(CurrentTfms)
$(PackageTags);GCP;Google Cloud Functions
Official Google Cloud Functions integration for Sentry - Open-source error tracking that helps developers monitor and fix crashes in real time.
@@ -16,11 +16,4 @@
-
-
-
-
-
-
-
diff --git a/src/Sentry.Hangfire/Sentry.Hangfire.csproj b/src/Sentry.Hangfire/Sentry.Hangfire.csproj
index 941dd8c94d..4e2f001c85 100644
--- a/src/Sentry.Hangfire/Sentry.Hangfire.csproj
+++ b/src/Sentry.Hangfire/Sentry.Hangfire.csproj
@@ -3,7 +3,7 @@
Official Hangfire integration for Sentry - Open-source error tracking that helps developers monitor and fix crashes in real time.
$(PackageTags);Hangfire
- net9.0;net8.0;net462
+ $(CurrentTfms);net462
enable
false
diff --git a/src/Sentry.Maui/Internal/MauiEventsBinder.cs b/src/Sentry.Maui/Internal/MauiEventsBinder.cs
index 4af503036b..78e619bed9 100644
--- a/src/Sentry.Maui/Internal/MauiEventsBinder.cs
+++ b/src/Sentry.Maui/Internal/MauiEventsBinder.cs
@@ -298,9 +298,9 @@ internal void HandlePageEvents(Page page, bool bind = true)
// https://github.com/dotnet/docs-maui/issues/583
page.NavigatedTo -= OnPageOnNavigatedTo;
- // Layout changed event
- // https://docs.microsoft.com/dotnet/api/xamarin.forms.ilayout.layoutchanged
- page.LayoutChanged -= OnPageOnLayoutChanged;
+ // Size changed event
+ // https://learn.microsoft.com/dotnet/api/microsoft.maui.controls.visualelement.sizechanged
+ page.SizeChanged -= OnPageOnSizeChanged;
if (bind)
{
@@ -313,9 +313,9 @@ internal void HandlePageEvents(Page page, bool bind = true)
// https://github.com/dotnet/docs-maui/issues/583
page.NavigatedTo += OnPageOnNavigatedTo;
- // Layout changed event
- // https://docs.microsoft.com/dotnet/api/xamarin.forms.ilayout.layoutchanged
- page.LayoutChanged += OnPageOnLayoutChanged;
+ // Size changed event
+ // https://learn.microsoft.com/dotnet/api/microsoft.maui.controls.visualelement.sizechanged
+ page.SizeChanged += OnPageOnSizeChanged;
}
}
@@ -446,6 +446,6 @@ private void OnPageOnDisappearing(object? sender, EventArgs _) =>
private void OnPageOnNavigatedTo(object? sender, NavigatedToEventArgs e) =>
_hub.AddBreadcrumbForEvent(_options, sender, nameof(Page.NavigatedTo), NavigationType, NavigationCategory, data => data.AddElementInfo(_options, e.GetPreviousPage(), "PreviousPage"));
- private void OnPageOnLayoutChanged(object? sender, EventArgs _) =>
- _hub.AddBreadcrumbForEvent(_options, sender, nameof(Page.LayoutChanged), SystemType, RenderingCategory);
+ private void OnPageOnSizeChanged(object? sender, EventArgs _) =>
+ _hub.AddBreadcrumbForEvent(_options, sender, nameof(Page.SizeChanged), SystemType, RenderingCategory);
}
diff --git a/src/Sentry.Maui/Sentry.Maui.csproj b/src/Sentry.Maui/Sentry.Maui.csproj
index 38c14e4788..cdaed0c240 100644
--- a/src/Sentry.Maui/Sentry.Maui.csproj
+++ b/src/Sentry.Maui/Sentry.Maui.csproj
@@ -3,14 +3,14 @@
Official MAUI integration for Sentry - Open-source error tracking that helps developers monitor and fix crashes in real time.
- net9.0;net8.0
- $(TargetFrameworks);net8.0-android34.0;net9.0-android35.0
- $(TargetFrameworks);net8.0-ios17.0;net9.0-ios18.0
- $(TargetFrameworks);net8.0-maccatalyst17.0;net9.0-maccatalyst18.0
- $(TargetFrameworks);net8.0-windows10.0.19041.0
+ $(LatestTfm);$(PreviousTfm)
+ $(TargetFrameworks);$(LatestAndroidTfm);$(PreviousAndroidTfm)
+ $(TargetFrameworks);$(LatestIosTfm);$(PreviousIosTfm)
+ $(TargetFrameworks);$(LatestMacCatalystTfm);$(PreviousMacCatalystTfm)
+ $(TargetFrameworks);$(PreviousWindowsTfm)
- net9.0;net8.0
+ $(CurrentTfms)
$(PackageTags);Profiling;Diagnostic
Performance profiling support for Sentry - Open-source error tracking that helps developers monitor and fix crashes in real time.
diff --git a/src/Sentry.Serilog/LogLevelExtensions.cs b/src/Sentry.Serilog/LogLevelExtensions.cs
index 03a16ea216..07960179b2 100644
--- a/src/Sentry.Serilog/LogLevelExtensions.cs
+++ b/src/Sentry.Serilog/LogLevelExtensions.cs
@@ -42,4 +42,18 @@ public static BreadcrumbLevel ToBreadcrumbLevel(this LogEventLevel level)
_ => (BreadcrumbLevel)level
};
}
+
+ public static SentryLogLevel ToSentryLogLevel(this LogEventLevel level)
+ {
+ return level switch
+ {
+ LogEventLevel.Verbose => SentryLogLevel.Trace,
+ LogEventLevel.Debug => SentryLogLevel.Debug,
+ LogEventLevel.Information => SentryLogLevel.Info,
+ LogEventLevel.Warning => SentryLogLevel.Warning,
+ LogEventLevel.Error => SentryLogLevel.Error,
+ LogEventLevel.Fatal => SentryLogLevel.Fatal,
+ _ => (SentryLogLevel)level,
+ };
+ }
}
diff --git a/src/Sentry.Serilog/Sentry.Serilog.csproj b/src/Sentry.Serilog/Sentry.Serilog.csproj
index eca849fcd6..60c27e77f7 100644
--- a/src/Sentry.Serilog/Sentry.Serilog.csproj
+++ b/src/Sentry.Serilog/Sentry.Serilog.csproj
@@ -1,7 +1,7 @@
- net9.0;net8.0;netstandard2.1;netstandard2.0;net462
+ $(CurrentTfms);netstandard2.1;netstandard2.0;net462
$(PackageTags);Logging;Serilog
Official Serilog integration for Sentry - Open-source error tracking that helps developers monitor and fix crashes in real time.
true
diff --git a/src/Sentry.Serilog/SentrySink.Structured.cs b/src/Sentry.Serilog/SentrySink.Structured.cs
new file mode 100644
index 0000000000..6584afb934
--- /dev/null
+++ b/src/Sentry.Serilog/SentrySink.Structured.cs
@@ -0,0 +1,126 @@
+using Sentry.Internal.Extensions;
+using Serilog.Parsing;
+
+namespace Sentry.Serilog;
+
+internal sealed partial class SentrySink
+{
+ private static void CaptureStructuredLog(IHub hub, SentryOptions options, LogEvent logEvent, string formatted, string? template)
+ {
+ GetTraceIdAndSpanId(hub, out var traceId, out var spanId);
+ GetStructuredLoggingParametersAndAttributes(logEvent, out var parameters, out var attributes);
+
+ SentryLog log = new(logEvent.Timestamp, traceId, logEvent.Level.ToSentryLogLevel(), formatted)
+ {
+ Template = template,
+ Parameters = parameters,
+ ParentSpanId = spanId,
+ };
+
+ log.SetDefaultAttributes(options, Sdk);
+
+ foreach (var attribute in attributes)
+ {
+ log.SetAttribute(attribute.Key, attribute.Value);
+ }
+
+ hub.Logger.CaptureLog(log);
+ }
+
+ private static void GetTraceIdAndSpanId(IHub hub, out SentryId traceId, out SpanId? spanId)
+ {
+ var span = hub.GetSpan();
+ if (span is not null)
+ {
+ traceId = span.TraceId;
+ spanId = span.SpanId;
+ return;
+ }
+
+ var scope = hub.GetScope();
+ if (scope is not null)
+ {
+ traceId = scope.PropagationContext.TraceId;
+ spanId = scope.PropagationContext.SpanId;
+ return;
+ }
+
+ traceId = SentryId.Empty;
+ spanId = null;
+ }
+
+ private static void GetStructuredLoggingParametersAndAttributes(LogEvent logEvent, out ImmutableArray> parameters, out List> attributes)
+ {
+ var propertyNames = new HashSet();
+ foreach (var token in logEvent.MessageTemplate.Tokens)
+ {
+ if (token is PropertyToken property)
+ {
+ propertyNames.Add(property.PropertyName);
+ }
+ }
+
+ var @params = ImmutableArray.CreateBuilder>();
+ attributes = new List>();
+
+ foreach (var property in logEvent.Properties)
+ {
+ if (propertyNames.Contains(property.Key))
+ {
+ foreach (var parameter in GetLogEventProperties(property))
+ {
+ @params.Add(parameter);
+ }
+ }
+ else
+ {
+ foreach (var attribute in GetLogEventProperties(property))
+ {
+ attributes.Add(new KeyValuePair($"property.{attribute.Key}", attribute.Value));
+ }
+ }
+ }
+
+ parameters = @params.DrainToImmutable();
+ return;
+
+ static IEnumerable> GetLogEventProperties(KeyValuePair property)
+ {
+ if (property.Value is ScalarValue scalarValue)
+ {
+ if (scalarValue.Value is not null)
+ {
+ yield return new KeyValuePair(property.Key, scalarValue.Value);
+ }
+ }
+ else if (property.Value is SequenceValue sequenceValue)
+ {
+ if (sequenceValue.Elements.Count != 0)
+ {
+ yield return new KeyValuePair(property.Key, sequenceValue.ToString());
+ }
+ }
+ else if (property.Value is DictionaryValue dictionaryValue)
+ {
+ if (dictionaryValue.Elements.Count != 0)
+ {
+ yield return new KeyValuePair(property.Key, dictionaryValue.ToString());
+ }
+ }
+ else if (property.Value is StructureValue structureValue)
+ {
+ foreach (var prop in structureValue.Properties)
+ {
+ if (LogEventProperty.IsValidName(prop.Name))
+ {
+ yield return new KeyValuePair($"{property.Key}.{prop.Name}", prop.Value.ToString());
+ }
+ }
+ }
+ else if (!property.Value.IsNull())
+ {
+ yield return new KeyValuePair(property.Key, property.Value);
+ }
+ }
+ }
+}
diff --git a/src/Sentry.Serilog/SentrySink.cs b/src/Sentry.Serilog/SentrySink.cs
index b2a6671c67..369d52a673 100644
--- a/src/Sentry.Serilog/SentrySink.cs
+++ b/src/Sentry.Serilog/SentrySink.cs
@@ -5,7 +5,7 @@ namespace Sentry.Serilog;
///
///
///
-internal sealed class SentrySink : ILogEventSink, IDisposable
+internal sealed partial class SentrySink : ILogEventSink, IDisposable
{
private readonly IDisposable? _sdkDisposable;
private readonly SentrySerilogOptions _options;
@@ -13,6 +13,12 @@ internal sealed class SentrySink : ILogEventSink, IDisposable
internal static readonly SdkVersion NameAndVersion
= typeof(SentrySink).Assembly.GetNameAndVersion();
+ private static readonly SdkVersion Sdk = new()
+ {
+ Name = SdkName,
+ Version = NameAndVersion.Version,
+ };
+
///
/// Serilog SDK name.
///
@@ -50,6 +56,11 @@ internal SentrySink(
public void Emit(LogEvent logEvent)
{
+ if (!IsEnabled(logEvent))
+ {
+ return;
+ }
+
if (isReentrant.Value)
{
_options.DiagnosticLogger?.LogError($"Reentrant log event detected. Logging when inside the scope of another log event can cause a StackOverflowException. LogEventInfo.Message: {logEvent.MessageTemplate.Text}");
@@ -67,6 +78,15 @@ public void Emit(LogEvent logEvent)
}
}
+ private bool IsEnabled(LogEvent logEvent)
+ {
+ var options = _hubAccessor().GetSentryOptions();
+
+ return logEvent.Level >= _options.MinimumEventLevel
+ || logEvent.Level >= _options.MinimumBreadcrumbLevel
+ || options?.Experimental.EnableLogs is true;
+ }
+
private void InnerEmit(LogEvent logEvent)
{
if (logEvent.TryGetSourceContext(out var context))
@@ -77,8 +97,7 @@ private void InnerEmit(LogEvent logEvent)
}
}
- var hub = _hubAccessor();
- if (hub is null || !hub.IsEnabled)
+ if (_hubAccessor() is not { IsEnabled: true } hub)
{
return;
}
@@ -122,30 +141,37 @@ private void InnerEmit(LogEvent logEvent)
}
}
- if (logEvent.Level < _options.MinimumBreadcrumbLevel)
+ if (logEvent.Level >= _options.MinimumBreadcrumbLevel)
{
- return;
+ Dictionary? data = null;
+ if (exception != null && !string.IsNullOrWhiteSpace(formatted))
+ {
+ // Exception.Message won't be used as Breadcrumb message
+ // Avoid losing it by adding as data:
+ data = new Dictionary
+ {
+ { "exception_message", exception.Message }
+ };
+ }
+
+ hub.AddBreadcrumb(
+ _clock,
+ string.IsNullOrWhiteSpace(formatted)
+ ? exception?.Message ?? ""
+ : formatted,
+ context,
+ data: data,
+ level: logEvent.Level.ToBreadcrumbLevel());
}
- Dictionary? data = null;
- if (exception != null && !string.IsNullOrWhiteSpace(formatted))
+ // Read the options from the Hub, rather than the Sink's Serilog-Options, because 'EnableLogs' is declared in the base 'SentryOptions', rather than the derived 'SentrySerilogOptions'.
+ // In cases where Sentry's Serilog-Sink is added without a DSN (i.e., without initializing the SDK) and the SDK is initialized differently (e.g., through ASP.NET Core),
+ // then the 'EnableLogs' option of this Sink's Serilog-Options is default, but the Hub's Sentry-Options have the actual user-defined value configured.
+ var options = hub.GetSentryOptions();
+ if (options?.Experimental.EnableLogs is true)
{
- // Exception.Message won't be used as Breadcrumb message
- // Avoid losing it by adding as data:
- data = new Dictionary
- {
- {"exception_message", exception.Message}
- };
+ CaptureStructuredLog(hub, options, logEvent, formatted, template);
}
-
- hub.AddBreadcrumb(
- _clock,
- string.IsNullOrWhiteSpace(formatted)
- ? exception?.Message ?? ""
- : formatted,
- context,
- data: data,
- level: logEvent.Level.ToBreadcrumbLevel());
}
private static bool IsSentryContext(string context) =>
diff --git a/src/Sentry.Serilog/SentrySinkExtensions.cs b/src/Sentry.Serilog/SentrySinkExtensions.cs
index 924cec9d84..e300ae1697 100644
--- a/src/Sentry.Serilog/SentrySinkExtensions.cs
+++ b/src/Sentry.Serilog/SentrySinkExtensions.cs
@@ -13,8 +13,8 @@ public static class SentrySinkExtensions
///
/// The logger configuration .
/// The Sentry DSN (required).
- /// Minimum log level to send an event.
/// Minimum log level to record a breadcrumb.
+ /// Minimum log level to send an event.
/// The Serilog format provider.
/// The Serilog text formatter.
/// Whether to include default Personal Identifiable information.
@@ -35,6 +35,7 @@ public static class SentrySinkExtensions
/// What mode to use for reporting referenced assemblies in each event sent to sentry. Defaults to
/// What modes to use for event automatic de-duplication.
/// Default tags to add to all events.
+ /// Whether to send structured logs.
///
/// This sample shows how each item may be set from within a configuration file:
///
@@ -50,7 +51,7 @@ public static class SentrySinkExtensions
/// "dsn": "https://MY-DSN@sentry.io",
/// "minimumBreadcrumbLevel": "Verbose",
/// "minimumEventLevel": "Error",
- /// "outputTemplate": "{Timestamp:o} [{Level:u3}] ({Application}/{MachineName}/{ThreadId}) {Message}{NewLine}{Exception}"///
+ /// "outputTemplate": "{Timestamp:o} [{Level:u3}] ({Application}/{MachineName}/{ThreadId}) {Message}{NewLine}{Exception}",
/// "sendDefaultPii": false,
/// "isEnvironmentUser": false,
/// "serverName": "MyServerName",
@@ -71,7 +72,8 @@ public static class SentrySinkExtensions
/// "defaultTags": {
/// "key-1", "value-1",
/// "key-2", "value-2"
- /// }
+ /// },
+ /// "experimentalEnableLogs": true
/// }
/// }
/// ]
@@ -103,7 +105,8 @@ public static LoggerConfiguration Sentry(
SentryLevel? diagnosticLevel = null,
ReportAssembliesMode? reportAssembliesMode = null,
DeduplicateMode? deduplicateMode = null,
- Dictionary? defaultTags = null)
+ Dictionary? defaultTags = null,
+ bool? experimentalEnableLogs = null)
{
return loggerConfiguration.Sentry(o => ConfigureSentrySerilogOptions(o,
dsn,
@@ -128,7 +131,8 @@ public static LoggerConfiguration Sentry(
diagnosticLevel,
reportAssembliesMode,
deduplicateMode,
- defaultTags));
+ defaultTags,
+ experimentalEnableLogs));
}
///
@@ -157,7 +161,7 @@ public static LoggerConfiguration Sentry(
/// "Args": {
/// "minimumEventLevel": "Error",
/// "minimumBreadcrumbLevel": "Verbose",
- /// "outputTemplate": "{Timestamp:o} [{Level:u3}] ({Application}/{MachineName}/{ThreadId}) {Message}{NewLine}{Exception}"///
+ /// "outputTemplate": "{Timestamp:o} [{Level:u3}] ({Application}/{MachineName}/{ThreadId}) {Message}{NewLine}{Exception}"
/// }
/// }
/// ]
@@ -205,7 +209,8 @@ internal static void ConfigureSentrySerilogOptions(
SentryLevel? diagnosticLevel = null,
ReportAssembliesMode? reportAssembliesMode = null,
DeduplicateMode? deduplicateMode = null,
- Dictionary? defaultTags = null)
+ Dictionary? defaultTags = null,
+ bool? experimentalEnableLogs = null)
{
if (dsn is not null)
{
@@ -317,6 +322,11 @@ internal static void ConfigureSentrySerilogOptions(
sentrySerilogOptions.DeduplicateMode = deduplicateMode.Value;
}
+ if (experimentalEnableLogs.HasValue)
+ {
+ sentrySerilogOptions.Experimental.EnableLogs = experimentalEnableLogs.Value;
+ }
+
// Serilog-specific items
sentrySerilogOptions.InitializeSdk = dsn is not null; // Inferred from the Sentry overload that is used
if (defaultTags?.Count > 0)
@@ -354,7 +364,6 @@ public static LoggerConfiguration Sentry(
sdkDisposable = SentrySdk.Init(options);
}
- var minimumOverall = (LogEventLevel)Math.Min((int)options.MinimumBreadcrumbLevel, (int)options.MinimumEventLevel);
- return loggerConfiguration.Sink(new SentrySink(options, sdkDisposable), minimumOverall);
+ return loggerConfiguration.Sink(new SentrySink(options, sdkDisposable));
}
}
diff --git a/src/Sentry/AttributeReader.cs b/src/Sentry/AttributeReader.cs
index c90de13c89..213ac99e13 100644
--- a/src/Sentry/AttributeReader.cs
+++ b/src/Sentry/AttributeReader.cs
@@ -2,6 +2,25 @@ namespace Sentry;
internal static class AttributeReader
{
- public static string? TryGetProjectDirectory(Assembly assembly) =>
- assembly.GetCustomAttributes().FirstOrDefault(x => x.Key == "Sentry.ProjectDirectory")?.Value;
+ public static string? TryGetProjectDirectory(Assembly assembly)
+ {
+ // Use metadata (CustomAttributeData) to avoid hard-referencing AssemblyMetadataAttribute,
+ // which may be trimmed. This safely returns null if the attribute instances were removed.
+ // The constructor is: `AssemblyMetadataAttribute(string key, string? value);`
+ foreach (var cad in assembly.GetCustomAttributesData())
+ {
+ var at = cad.AttributeType;
+ if (at is { Namespace: "System.Reflection", Name: "AssemblyMetadataAttribute" }
+ && cad.ConstructorArguments.Count == 2
+ && cad.ConstructorArguments[0].ArgumentType == typeof(string)
+ && string.Equals(cad.ConstructorArguments[0].Value as string, "Sentry.ProjectDirectory", StringComparison.Ordinal)
+ && cad.ConstructorArguments[1].ArgumentType == typeof(string)
+ )
+ {
+ return cad.ConstructorArguments[1].Value as string;
+ }
+ }
+
+ return null;
+ }
}
diff --git a/src/Sentry/BindableSentryOptions.cs b/src/Sentry/BindableSentryOptions.cs
index 9ca3847e1e..f9077de9b4 100644
--- a/src/Sentry/BindableSentryOptions.cs
+++ b/src/Sentry/BindableSentryOptions.cs
@@ -9,6 +9,7 @@ internal partial class BindableSentryOptions
{
public bool? IsGlobalModeEnabled { get; set; }
public bool? EnableScopeSync { get; set; }
+ public bool? EnableBackpressureHandling { get; set; }
public List? TagFilters { get; set; }
public bool? SendDefaultPii { get; set; }
public bool? IsEnvironmentUser { get; set; }
@@ -64,6 +65,7 @@ public void ApplyTo(SentryOptions options)
{
options.IsGlobalModeEnabled = IsGlobalModeEnabled ?? options.IsGlobalModeEnabled;
options.EnableScopeSync = EnableScopeSync ?? options.EnableScopeSync;
+ options.EnableBackpressureHandling = EnableBackpressureHandling ?? options.EnableBackpressureHandling;
options.TagFilters = TagFilters?.Select(s => new StringOrRegex(s)).ToList() ?? options.TagFilters;
options.SendDefaultPii = SendDefaultPii ?? options.SendDefaultPii;
options.IsEnvironmentUser = IsEnvironmentUser ?? options.IsEnvironmentUser;
diff --git a/src/Sentry/Http/HttpTransportBase.cs b/src/Sentry/Http/HttpTransportBase.cs
index 846e889923..681818e824 100644
--- a/src/Sentry/Http/HttpTransportBase.cs
+++ b/src/Sentry/Http/HttpTransportBase.cs
@@ -16,6 +16,7 @@ public abstract class HttpTransportBase
internal const string DefaultErrorMessage = "No message";
private readonly SentryOptions _options;
+ private readonly BackpressureMonitor? _backpressureMonitor;
private readonly ISystemClock _clock;
private readonly Func _getEnvironmentVariable;
@@ -24,7 +25,7 @@ public abstract class HttpTransportBase
// Using string instead of SentryId here so that we can use Interlocked.Exchange(...).
private string? _lastDiscardedSessionInitId;
- private string _typeName;
+ private readonly string _typeName;
///
/// Constructor for this class.
@@ -33,8 +34,8 @@ public abstract class HttpTransportBase
/// An optional method used to read environment variables.
/// An optional system clock - used for testing.
protected HttpTransportBase(SentryOptions options,
- Func? getEnvironmentVariable = default,
- ISystemClock? clock = default)
+ Func? getEnvironmentVariable = null,
+ ISystemClock? clock = null)
{
_options = options;
_clock = clock ?? SystemClock.Clock;
@@ -42,6 +43,21 @@ protected HttpTransportBase(SentryOptions options,
_typeName = GetType().Name;
}
+ ///
+ /// Constructor for this class.
+ ///
+ /// The Sentry options.
+ /// The Sentry options.
+ /// An optional method used to read environment variables.
+ /// An optional system clock - used for testing.
+ internal HttpTransportBase(SentryOptions options, BackpressureMonitor? backpressureMonitor,
+ Func? getEnvironmentVariable = null,
+ ISystemClock? clock = null)
+ : this(options, getEnvironmentVariable, clock)
+ {
+ _backpressureMonitor = backpressureMonitor;
+ }
+
// Keep track of rate limits and their expiry dates.
// Internal for testing.
internal ConcurrentDictionary CategoryLimitResets { get; } = new();
@@ -256,6 +272,7 @@ private void ExtractRateLimits(HttpHeaders responseHeaders)
}
var now = _clock.GetUtcNow();
+ _backpressureMonitor?.RecordRateLimitHit(now);
// Join to a string to handle both single-header and multi-header cases
var rateLimitsEncoded = string.Join(",", rateLimitHeaderValues);
diff --git a/src/Sentry/Internal/BackgroundWorker.cs b/src/Sentry/Internal/BackgroundWorker.cs
index d747922d5a..99c4ce2d78 100644
--- a/src/Sentry/Internal/BackgroundWorker.cs
+++ b/src/Sentry/Internal/BackgroundWorker.cs
@@ -9,6 +9,7 @@ internal class BackgroundWorker : IBackgroundWorker, IDisposable
{
private readonly ITransport _transport;
private readonly SentryOptions _options;
+ private readonly BackpressureMonitor? _backpressureMonitor;
private readonly ConcurrentQueueLite _queue;
private readonly int _maxItems;
private readonly CancellationTokenSource _shutdownSource;
@@ -26,11 +27,13 @@ internal class BackgroundWorker : IBackgroundWorker, IDisposable
public BackgroundWorker(
ITransport transport,
SentryOptions options,
+ BackpressureMonitor? backpressureMonitor,
CancellationTokenSource? shutdownSource = null,
ConcurrentQueueLite? queue = null)
{
_transport = transport;
_options = options;
+ _backpressureMonitor = backpressureMonitor;
_queue = queue ?? new ConcurrentQueueLite();
_maxItems = options.MaxQueueItems;
_shutdownSource = shutdownSource ?? new CancellationTokenSource();
@@ -66,6 +69,7 @@ public bool EnqueueEnvelope(Envelope envelope, bool process)
var eventId = envelope.TryGetEventId(_options.DiagnosticLogger);
if (Interlocked.Increment(ref _currentItems) > _maxItems)
{
+ _backpressureMonitor?.RecordQueueOverflow();
Interlocked.Decrement(ref _currentItems);
_options.ClientReportRecorder.RecordDiscardedEvents(DiscardReason.QueueOverflow, envelope);
_options.LogInfo("Discarding envelope {0} because the queue is full.", eventId);
diff --git a/src/Sentry/Internal/BackpressureMonitor.cs b/src/Sentry/Internal/BackpressureMonitor.cs
new file mode 100644
index 0000000000..c45b38264d
--- /dev/null
+++ b/src/Sentry/Internal/BackpressureMonitor.cs
@@ -0,0 +1,166 @@
+using Sentry.Extensibility;
+using Sentry.Infrastructure;
+
+namespace Sentry.Internal;
+
+///
+///
+/// Monitors system health and calculates a DownsampleFactor that can be applied to events and transactions when the
+/// system is under load.
+///
+///
+/// The health checks used by the monitor are:
+///
+///
+/// - if any events have been dropped due to queue being full in the last 2 seconds
+/// - if any new rate limits have been applied since the last check
+///
+/// This check is performed every 10 seconds. With each negative health check we halve tracesSampleRate up to 10 times, meaning the original tracesSampleRate is multiplied by 1, 1/2, 1/4, ... up to 1/1024 (~ 0.001%). Any positive health check resets to the original tracesSampleRate set in SentryOptions.
+///
+/// Backpressure Management
+internal class BackpressureMonitor : IDisposable
+{
+ internal const int MaxDownsamples = 10;
+ private const int CheckIntervalInSeconds = 10;
+ private const int RecentThresholdInSeconds = 2;
+
+ private readonly IDiagnosticLogger? _logger;
+ private readonly ISystemClock _clock;
+ private long _lastQueueOverflow = DateTimeOffset.MinValue.Ticks;
+ private long _lastRateLimitEvent = DateTimeOffset.MinValue.Ticks;
+ private volatile int _downsampleLevel = 0;
+
+ private static readonly long RecencyThresholdTicks = TimeSpan.FromSeconds(RecentThresholdInSeconds).Ticks;
+ private static readonly long CheckIntervalTicks = TimeSpan.FromSeconds(CheckIntervalInSeconds).Ticks;
+
+ private readonly CancellationTokenSource _cts = new();
+
+ private readonly Task _workerTask;
+ internal int DownsampleLevel => _downsampleLevel;
+ internal long LastQueueOverflowTicks => Interlocked.Read(ref _lastQueueOverflow);
+ internal long LastRateLimitEventTicks => Interlocked.Read(ref _lastRateLimitEvent);
+
+ public BackpressureMonitor(IDiagnosticLogger? logger, ISystemClock? clock = null, bool enablePeriodicHealthCheck = true)
+ {
+ _logger = logger;
+ _clock = clock ?? SystemClock.Clock;
+
+ if (enablePeriodicHealthCheck)
+ {
+ _logger?.LogDebug("Starting BackpressureMonitor.");
+ _workerTask = Task.Run(() => DoWorkAsync(_cts.Token));
+ }
+ else
+ {
+ _workerTask = Task.CompletedTask;
+ }
+ }
+
+ ///
+ /// For testing purposes only. Sets the downsample level directly.
+ ///
+ internal void SetDownsampleLevel(int level)
+ {
+ Interlocked.Exchange(ref _downsampleLevel, level);
+ }
+
+ internal void IncrementDownsampleLevel()
+ {
+ var oldValue = _downsampleLevel;
+ if (oldValue < MaxDownsamples)
+ {
+ var newValue = oldValue + 1;
+ if (Interlocked.CompareExchange(ref _downsampleLevel, newValue, oldValue) == oldValue)
+ {
+ _logger?.LogDebug("System is under pressure, increasing downsample level to {0}.", newValue);
+ }
+ }
+ }
+
+ ///
+ /// A multiplier that can be applied to the SampleRate or TracesSampleRate to reduce the amount of data sent to
+ /// Sentry when the system is under pressure.
+ ///
+ public double DownsampleFactor
+ {
+ get
+ {
+ var level = _downsampleLevel;
+ return 1d / (1 << level); // 1 / (2^level) = 1, 1/2, 1/4, 1/8, ...
+ }
+ }
+
+ public void RecordRateLimitHit(DateTimeOffset when) => Interlocked.Exchange(ref _lastRateLimitEvent, when.Ticks);
+
+ public void RecordQueueOverflow() => Interlocked.Exchange(ref _lastQueueOverflow, _clock.GetUtcNow().Ticks);
+
+ private async Task DoWorkAsync(CancellationToken cancellationToken)
+ {
+ try
+ {
+ while (!cancellationToken.IsCancellationRequested)
+ {
+ DoHealthCheck();
+
+ await Task.Delay(TimeSpan.FromSeconds(CheckIntervalInSeconds), cancellationToken).ConfigureAwait(false);
+ }
+ }
+ catch (OperationCanceledException)
+ {
+ // Task was cancelled, exit gracefully
+ }
+ }
+
+ internal void DoHealthCheck()
+ {
+ if (IsHealthy)
+ {
+ var previous = Interlocked.Exchange(ref _downsampleLevel, 0);
+ if (previous > 0)
+ {
+ _logger?.LogDebug("System is healthy, resetting downsample level.");
+ }
+ }
+ else
+ {
+ IncrementDownsampleLevel();
+ }
+ }
+
+ ///
+ /// Checks for any recent queue overflows or any rate limit events since the last check.
+ ///
+ ///
+ internal bool IsHealthy
+ {
+ get
+ {
+ var nowTicks = _clock.GetUtcNow().Ticks;
+ var recentOverflowCutoff = nowTicks - RecencyThresholdTicks;
+ var rateLimitCutoff = nowTicks - CheckIntervalTicks;
+ return LastQueueOverflowTicks < recentOverflowCutoff && LastRateLimitEventTicks < rateLimitCutoff;
+ }
+ }
+
+ public void Dispose()
+ {
+ try
+ {
+ _cts.Cancel();
+ _workerTask.Wait();
+ }
+ catch (AggregateException ex) when (ex.InnerException is OperationCanceledException)
+ {
+ // Ignore cancellation
+ }
+ catch (Exception ex)
+ {
+ // Log rather than throw
+ _logger?.LogWarning(ex, "Error in BackpressureMonitor.Dispose");
+ }
+ finally
+ {
+ _cts.Dispose();
+ }
+ }
+}
diff --git a/src/Sentry/Internal/BackpressureMonitorExtensions.cs b/src/Sentry/Internal/BackpressureMonitorExtensions.cs
new file mode 100644
index 0000000000..cddbe22023
--- /dev/null
+++ b/src/Sentry/Internal/BackpressureMonitorExtensions.cs
@@ -0,0 +1,6 @@
+namespace Sentry.Internal;
+
+internal static class BackpressureMonitorExtensions
+{
+ internal static double GetDownsampleFactor(this BackpressureMonitor? monitor) => monitor?.DownsampleFactor ?? 1.0;
+}
diff --git a/src/Sentry/Internal/Http/HttpTransport.cs b/src/Sentry/Internal/Http/HttpTransport.cs
index 7f8b7ce91d..a245dc4e14 100644
--- a/src/Sentry/Internal/Http/HttpTransport.cs
+++ b/src/Sentry/Internal/Http/HttpTransport.cs
@@ -15,10 +15,10 @@ public HttpTransport(SentryOptions options, HttpClient httpClient)
_httpClient = httpClient;
}
- internal HttpTransport(SentryOptions options, HttpClient httpClient,
+ internal HttpTransport(SentryOptions options, HttpClient httpClient, BackpressureMonitor? backpressureMonitor,
Func? getEnvironmentVariable = default,
ISystemClock? clock = default)
- : base(options, getEnvironmentVariable, clock)
+ : base(options, backpressureMonitor, getEnvironmentVariable, clock)
{
_httpClient = httpClient;
}
diff --git a/src/Sentry/Internal/Http/LazyHttpTransport.cs b/src/Sentry/Internal/Http/LazyHttpTransport.cs
index 51f6b71be7..f2475c9471 100644
--- a/src/Sentry/Internal/Http/LazyHttpTransport.cs
+++ b/src/Sentry/Internal/Http/LazyHttpTransport.cs
@@ -7,9 +7,9 @@ internal class LazyHttpTransport : ITransport
{
private readonly Lazy _httpTransport;
- public LazyHttpTransport(SentryOptions options)
+ public LazyHttpTransport(SentryOptions options, BackpressureMonitor? backpressureMonitor)
{
- _httpTransport = new Lazy(() => new HttpTransport(options, options.GetHttpClient()));
+ _httpTransport = new Lazy(() => new HttpTransport(options, options.GetHttpClient(), backpressureMonitor));
}
public Task SendEnvelopeAsync(Envelope envelope, CancellationToken cancellationToken = default)
diff --git a/src/Sentry/Internal/Hub.cs b/src/Sentry/Internal/Hub.cs
index ec92169cd3..5170a64233 100644
--- a/src/Sentry/Internal/Hub.cs
+++ b/src/Sentry/Internal/Hub.cs
@@ -14,9 +14,11 @@ internal class Hub : IHub, IDisposable
private readonly ISystemClock _clock;
private readonly ISessionManager _sessionManager;
private readonly SentryOptions _options;
+ private readonly ISampleRandHelper _sampleRandHelper;
private readonly RandomValuesFactory _randomValuesFactory;
private readonly IReplaySession _replaySession;
private readonly List _integrationsToCleanup = new();
+ private readonly BackpressureMonitor? _backpressureMonitor;
#if MEMORY_DUMP_SUPPORTED
private readonly MemoryMonitor? _memoryMonitor;
@@ -44,7 +46,9 @@ internal Hub(
ISystemClock? clock = null,
IInternalScopeManager? scopeManager = null,
RandomValuesFactory? randomValuesFactory = null,
- IReplaySession? replaySession = null)
+ IReplaySession? replaySession = null,
+ ISampleRandHelper? sampleRandHelper = null,
+ BackpressureMonitor? backpressureMonitor = null)
{
if (string.IsNullOrWhiteSpace(options.Dsn))
{
@@ -59,8 +63,13 @@ internal Hub(
_randomValuesFactory = randomValuesFactory ?? new SynchronizedRandomValuesFactory();
_sessionManager = sessionManager ?? new GlobalSessionManager(options);
_clock = clock ?? SystemClock.Clock;
- client ??= new SentryClient(options, randomValuesFactory: _randomValuesFactory, sessionManager: _sessionManager);
+ if (_options.EnableBackpressureHandling)
+ {
+ _backpressureMonitor = backpressureMonitor ?? new BackpressureMonitor(_options.DiagnosticLogger, clock);
+ }
+ client ??= new SentryClient(options, randomValuesFactory: _randomValuesFactory, sessionManager: _sessionManager, backpressureMonitor: _backpressureMonitor);
_replaySession = replaySession ?? ReplaySession.Instance;
+ _sampleRandHelper = sampleRandHelper ?? new SampleRandHelperAdapter();
ScopeManager = scopeManager ?? new SentryScopeManager(options, client);
if (!options.IsGlobalModeEnabled)
@@ -175,9 +184,10 @@ internal ITransactionTracer StartTransaction(
bool? isSampled = null;
double? sampleRate = null;
+ DiscardReason? discardReason = null;
var sampleRand = dynamicSamplingContext?.Items.TryGetValue("sample_rand", out var dscSampleRand) ?? false
? double.Parse(dscSampleRand, NumberStyles.Float, CultureInfo.InvariantCulture)
- : SampleRandHelper.GenerateSampleRand(context.TraceId.ToString());
+ : _sampleRandHelper.GenerateSampleRand(context.TraceId.ToString());
// TracesSampler runs regardless of whether a decision has already been made, as it can be used to override it.
if (_options.TracesSampler is { } tracesSampler)
@@ -189,8 +199,14 @@ internal ITransactionTracer StartTransaction(
if (tracesSampler(samplingContext) is { } samplerSampleRate)
{
// The TracesSampler trumps all other sampling decisions (even the trace header)
- sampleRate = samplerSampleRate;
- isSampled = SampleRandHelper.IsSampled(sampleRand, samplerSampleRate);
+ sampleRate = samplerSampleRate * _backpressureMonitor.GetDownsampleFactor();
+ isSampled = SampleRandHelper.IsSampled(sampleRand, sampleRate.Value);
+ if (isSampled is false)
+ {
+ // If sampling out is only a result of the downsampling then we specify the reason as backpressure
+ // management... otherwise the event would have been sampled out anyway, so it's just regular sampling.
+ discardReason = sampleRand < samplerSampleRate ? DiscardReason.Backpressure : DiscardReason.SampleRate;
+ }
// Ensure the actual sampleRate is set on the provided DSC (if any) when the TracesSampler reached a sampling decision
dynamicSamplingContext?.SetSampleRate(samplerSampleRate);
@@ -201,8 +217,15 @@ internal ITransactionTracer StartTransaction(
// finally fallback to Random sampling if the decision has been made by no other means
if (isSampled == null)
{
- sampleRate = _options.TracesSampleRate ?? 0.0;
+ var optionsSampleRate = _options.TracesSampleRate ?? 0.0;
+ sampleRate = optionsSampleRate * _backpressureMonitor.GetDownsampleFactor();
isSampled = context.IsSampled ?? SampleRandHelper.IsSampled(sampleRand, sampleRate.Value);
+ if (isSampled is false)
+ {
+ // If sampling out is only a result of the downsampling then we specify the reason as backpressure
+ // management... otherwise the event would have been sampled out anyway, so it's just regular sampling.
+ discardReason = sampleRand < optionsSampleRate ? DiscardReason.Backpressure : DiscardReason.SampleRate;
+ }
if (context.IsSampled is null && _options.TracesSampleRate is not null)
{
@@ -220,6 +243,7 @@ internal ITransactionTracer StartTransaction(
{
SampleRate = sampleRate,
SampleRand = sampleRand,
+ DiscardReason = discardReason,
DynamicSamplingContext = dynamicSamplingContext // Default to the provided DSC
};
// If no DSC was provided, create one based on this transaction.
@@ -845,6 +869,8 @@ public void Dispose()
}
//Don't dispose of ScopeManager since we want dangling transactions to still be able to access tags.
+ _backpressureMonitor?.Dispose();
+
#if __IOS__
// TODO
#elif ANDROID
diff --git a/src/Sentry/Internal/SampleRandHelper.cs b/src/Sentry/Internal/SampleRandHelper.cs
index 5e420c70f8..4a8d1c028f 100644
--- a/src/Sentry/Internal/SampleRandHelper.cs
+++ b/src/Sentry/Internal/SampleRandHelper.cs
@@ -11,5 +11,16 @@ internal static double GenerateSampleRand(string traceId)
<= 0 => false,
_ => sampleRand < rate
};
+}
+[DebuggerStepThrough]
+internal class SampleRandHelperAdapter : ISampleRandHelper
+{
+ [DebuggerStepThrough]
+ public double GenerateSampleRand(string traceId) => SampleRandHelper.GenerateSampleRand(traceId);
+}
+
+internal interface ISampleRandHelper
+{
+ public double GenerateSampleRand(string traceId);
}
diff --git a/src/Sentry/Internal/SdkComposer.cs b/src/Sentry/Internal/SdkComposer.cs
index dab9a08986..d7263f8765 100644
--- a/src/Sentry/Internal/SdkComposer.cs
+++ b/src/Sentry/Internal/SdkComposer.cs
@@ -8,14 +8,16 @@ namespace Sentry.Internal;
internal class SdkComposer
{
private readonly SentryOptions _options;
+ private readonly BackpressureMonitor? _backpressureMonitor;
- public SdkComposer(SentryOptions options)
+ public SdkComposer(SentryOptions options, BackpressureMonitor? backpressureMonitor)
{
_options = options ?? throw new ArgumentNullException(nameof(options));
if (options.Dsn is null)
{
throw new ArgumentException("No DSN defined in the SentryOptions");
}
+ _backpressureMonitor = backpressureMonitor;
}
private ITransport CreateTransport()
@@ -23,7 +25,7 @@ private ITransport CreateTransport()
_options.LogDebug("Creating transport.");
// Start from either the transport given on options, or create a new HTTP transport.
- var transport = _options.Transport ?? new LazyHttpTransport(_options);
+ var transport = _options.Transport ?? new LazyHttpTransport(_options, _backpressureMonitor);
// When a cache directory path is given, wrap the transport in a caching transport.
if (!string.IsNullOrWhiteSpace(_options.CacheDirectoryPath))
@@ -87,6 +89,6 @@ public IBackgroundWorker CreateBackgroundWorker()
var transport = CreateTransport();
- return new BackgroundWorker(transport, _options);
+ return new BackgroundWorker(transport, _options, _backpressureMonitor);
}
}
diff --git a/src/Sentry/Internal/UnsampledTransaction.cs b/src/Sentry/Internal/UnsampledTransaction.cs
index a14698f7f3..8f55b7d808 100644
--- a/src/Sentry/Internal/UnsampledTransaction.cs
+++ b/src/Sentry/Internal/UnsampledTransaction.cs
@@ -42,6 +42,8 @@ public UnsampledTransaction(IHub hub, ITransactionContext context)
public double? SampleRand { get; set; }
+ public DiscardReason? DiscardReason { get; set; }
+
public override string Name
{
get => _context.Name;
@@ -72,8 +74,9 @@ public override void Finish()
// Record the discarded events
var spanCount = Spans.Count + 1; // 1 for each span + 1 for the transaction itself
- _options?.ClientReportRecorder.RecordDiscardedEvent(DiscardReason.SampleRate, DataCategory.Transaction);
- _options?.ClientReportRecorder.RecordDiscardedEvent(DiscardReason.SampleRate, DataCategory.Span, spanCount);
+ var discardReason = DiscardReason ?? Internal.DiscardReason.SampleRate;
+ _options?.ClientReportRecorder.RecordDiscardedEvent(discardReason, DataCategory.Transaction);
+ _options?.ClientReportRecorder.RecordDiscardedEvent(discardReason, DataCategory.Span, spanCount);
_options?.LogDebug("Finished unsampled transaction");
}
diff --git a/src/Sentry/Platforms/Native/CFunctions.cs b/src/Sentry/Platforms/Native/CFunctions.cs
index 582112fc4c..5faa149720 100644
--- a/src/Sentry/Platforms/Native/CFunctions.cs
+++ b/src/Sentry/Platforms/Native/CFunctions.cs
@@ -322,14 +322,25 @@ private static Dictionary LoadDebugImagesOnce(IDiagnosticLogge
[DllImport("sentry-native")]
internal static extern void sentry_value_decref(sentry_value_t value);
- // native union sentry_value_u/t
- [StructLayout(LayoutKind.Explicit)]
+ // Mirrors the native `sentry_value_t` union (uint64_t or double).
+ // Implemented with a single ulong backing field and BitConverter
+ // to reinterpret values, since explicit unions cause issues with
+ // Blazor WASM interop generators.
internal struct sentry_value_t
{
- [FieldOffset(0)]
- internal ulong _bits;
- [FieldOffset(0)]
- internal double _double;
+ private ulong _bits;
+
+ internal ulong Bits
+ {
+ readonly get => _bits;
+ set => _bits = value;
+ }
+
+ internal double Double
+ {
+ readonly get => BitConverter.UInt64BitsToDouble(_bits);
+ set => _bits = BitConverter.DoubleToUInt64Bits(value);
+ }
}
[DllImport("sentry-native")]
diff --git a/src/Sentry/Sentry.csproj b/src/Sentry/Sentry.csproj
index c560abff9b..850b3840d8 100644
--- a/src/Sentry/Sentry.csproj
+++ b/src/Sentry/Sentry.csproj
@@ -8,12 +8,12 @@
- net9.0;net8.0;netstandard2.1;netstandard2.0;net462
- $(TargetFrameworks);net9.0-android35.0;net8.0-android34.0
- $(TargetFrameworks);net9.0-ios18.0;net8.0-ios17.0
- $(TargetFrameworks);net9.0-maccatalyst18.0;net8.0-maccatalyst17.0
+ $(CurrentTfms);netstandard2.1;netstandard2.0;net462
+ $(TargetFrameworks);$(LatestAndroidTfm);$(PreviousAndroidTfm)
+ $(TargetFrameworks);$(LatestIosTfm);$(PreviousIosTfm)
+ $(TargetFrameworks);$(LatestMacCatalystTfm);$(PreviousMacCatalystTfm)
-
+
@@ -25,8 +25,8 @@
true
-
-
+
+
@@ -113,13 +113,13 @@
<_OSArchitecture>$([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture)
-
-
-
-
-
-
-
+
+
+
+
+
+
+
@@ -191,10 +191,11 @@
-
+
-
diff --git a/src/Sentry/SentryClient.cs b/src/Sentry/SentryClient.cs
index 5e7ae5cfd1..80ede995dd 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 BackpressureMonitor? _backpressureMonitor;
private readonly ISessionManager _sessionManager;
private readonly RandomValuesFactory _randomValuesFactory;
private readonly Enricher _enricher;
@@ -41,9 +42,11 @@ internal SentryClient(
SentryOptions options,
IBackgroundWorker? worker = null,
RandomValuesFactory? randomValuesFactory = null,
- ISessionManager? sessionManager = null)
+ ISessionManager? sessionManager = null,
+ BackpressureMonitor? backpressureMonitor = null)
{
_options = options ?? throw new ArgumentNullException(nameof(options));
+ _backpressureMonitor = backpressureMonitor;
_randomValuesFactory = randomValuesFactory ?? new SynchronizedRandomValuesFactory();
_sessionManager = sessionManager ?? new GlobalSessionManager(options);
_enricher = new Enricher(options);
@@ -52,7 +55,7 @@ internal SentryClient(
if (worker == null)
{
- var composer = new SdkComposer(options);
+ var composer = new SdkComposer(options, backpressureMonitor);
Worker = composer.CreateBackgroundWorker();
}
else
@@ -307,7 +310,6 @@ public SentryId CaptureCheckIn(
/// A task to await for the flush operation.
public Task FlushAsync(TimeSpan timeout) => Worker.FlushAsync(timeout);
- // TODO: this method needs to be refactored, it's really hard to analyze nullability
private SentryId DoSendEvent(SentryEvent @event, SentryHint? hint, Scope? scope)
{
var filteredExceptions = ApplyExceptionFilters(@event.Exception);
@@ -375,16 +377,22 @@ private SentryId DoSendEvent(SentryEvent @event, SentryHint? hint, Scope? scope)
if (_options.SampleRate != null)
{
- if (!_randomValuesFactory.NextBool(_options.SampleRate.Value))
+ var sampleRate = _options.SampleRate.Value;
+ var downsampledRate = sampleRate * _backpressureMonitor.GetDownsampleFactor();
+ var sampleRand = _randomValuesFactory.NextDouble();
+ if (sampleRand >= downsampledRate)
{
- _options.ClientReportRecorder.RecordDiscardedEvent(DiscardReason.SampleRate, DataCategory.Error);
- _options.LogDebug("Event sampled.");
+ // If sampling out is only a result of the downsampling then we specify the reason as backpressure
+ // management... otherwise the event would have been sampled out anyway, so it's just regular sampling.
+ var reason = sampleRand < sampleRate ? DiscardReason.Backpressure : DiscardReason.SampleRate;
+ _options.ClientReportRecorder.RecordDiscardedEvent(reason, DataCategory.Error);
+ _options.LogDebug("Event sampled out.");
return SentryId.Empty;
}
}
else
{
- _options.LogDebug("Event not sampled.");
+ _options.LogDebug("Event sampled in.");
}
if (!_options.SendDefaultPii)
diff --git a/src/Sentry/SentryLog.cs b/src/Sentry/SentryLog.cs
index b506b9da6c..7e58fec173 100644
--- a/src/Sentry/SentryLog.cs
+++ b/src/Sentry/SentryLog.cs
@@ -9,6 +9,7 @@ namespace Sentry;
/// This API is experimental and it may change in the future.
///
[Experimental(DiagnosticId.ExperimentalFeature)]
+[DebuggerDisplay(@"SentryLog \{ Level = {Level}, Message = '{Message}' \}")]
public sealed class SentryLog
{
private readonly Dictionary _attributes;
diff --git a/src/Sentry/SentryOptions.cs b/src/Sentry/SentryOptions.cs
index df4e2937e3..ace651ec46 100644
--- a/src/Sentry/SentryOptions.cs
+++ b/src/Sentry/SentryOptions.cs
@@ -94,6 +94,12 @@ public bool IsGlobalModeEnabled
///
public bool EnableScopeSync { get; set; }
+ ///
+ /// Enables or disables automatic backpressure handling. When enabled, the SDK will monitor system health and
+ /// reduce the sampling rate of events and transactions when the system is under load.
+ ///
+ public bool EnableBackpressureHandling { get; set; } = false;
+
///
/// This holds a reference to the current transport, when one is active.
/// If set manually before initialization, the provided transport will be used instead of the default transport.
diff --git a/src/Sentry/SentrySdk.cs b/src/Sentry/SentrySdk.cs
index eab7b9838f..b4bb1fbeea 100644
--- a/src/Sentry/SentrySdk.cs
+++ b/src/Sentry/SentrySdk.cs
@@ -316,7 +316,13 @@ public static class Experimental
public static IDisposable PushScope() => CurrentHub.PushScope();
///
+ ///
/// Binds the client to the current scope.
+ ///
+ ///
+ /// This might be used to bind a client with a different DSN or configuration (e.g. so that a particular thread or
+ /// part of the application sends events to a different Sentry project).
+ ///
///
/// The client.
[DebuggerStepThrough]
diff --git a/src/Sentry/buildTransitive/Sentry.targets b/src/Sentry/buildTransitive/Sentry.targets
index 653cc294c0..c33ed0efbf 100644
--- a/src/Sentry/buildTransitive/Sentry.targets
+++ b/src/Sentry/buildTransitive/Sentry.targets
@@ -18,6 +18,7 @@
Sentry.Attributes$(MSBuildProjectExtension.Replace('proj', ''))
+ $([System.Guid]::NewGuid())
true
@@ -125,11 +126,14 @@
$(SentrySetCommitReleaseOptions) --org $(SentryOrg)
$(SentrySetCommitReleaseOptions) --project $(SentryProject)
+ <_SentryCLIProGuardOptions Condition="'$(SentryProGuardUUID)' != ''">$(_SentryCLIProGuardOptions) --uuid "$(SentryProGuardUUID)"
+ <_SentryCLIProGuardOptions Condition="'$(_SentryCLIProGuardOptions.Trim())' != ''">$(_SentryCLIProGuardOptions.Trim())
+
$(SentryCLIUploadOptions) --org $(SentryOrg)
$(SentryCLIUploadOptions) --project $(SentryProject)
$(SentryCLIBaseCommand) debug-files upload
$(SentryCLIDebugFilesUploadCommand) $(SentryCLIUploadOptions.Trim())
- $(SentryCLIBaseCommand) upload-proguard
+ $(SentryCLIBaseCommand) upload-proguard $(_SentryCLIProGuardOptions)
$(SentryCLIProGuardMappingUploadCommand) $(SentryCLIUploadOptions.Trim())
@@ -217,7 +221,7 @@
$(SentryCLIUploadDirectory)
- $(SentryCLIUploadItems) $(IntermediateOutputPath)linked/$(AssemblyName).pdb
+ $(SentryCLIUploadItems) $(IntermediateOutputPath)linked/*.pdb
$(SentryCLIUploadItems) @(AndroidNativeSymbolFilesExceptDll -> '%(Identity)', ' ')
@@ -267,9 +271,22 @@
-
-
+
+
+
+
+ <_Parameter1>io.sentry.proguard-uuid
+ $(SentryProGuardUUID)
+
+
+
+
+
+
diff --git a/test/AndroidTestApp/AndroidTestApp.csproj b/test/AndroidTestApp/AndroidTestApp.csproj
index 3d8a1981e1..cefce5290f 100644
--- a/test/AndroidTestApp/AndroidTestApp.csproj
+++ b/test/AndroidTestApp/AndroidTestApp.csproj
@@ -1,7 +1,8 @@
- net8.0-android;net9.0-android
+ net10.0-android;net9.0-android
false
+ false
21
Exe
enable
@@ -9,5 +10,6 @@
com.companyname.AndroidTestApp
1
1.0
+ android-x64
diff --git a/test/Directory.Build.props b/test/Directory.Build.props
index 20b6c7ac79..301d759ba4 100644
--- a/test/Directory.Build.props
+++ b/test/Directory.Build.props
@@ -11,6 +11,8 @@
$(NoWarn);SYSLIB0005;SYSLIB0012
$(NoWarn);IDE1006
+
+ $(NoWarn);ASPDEPR004;ASPDEPR008
false
@@ -72,7 +74,7 @@
-
+
diff --git a/test/Sentry.Analyzers.Tests/Sentry.Analyzers.Tests.csproj b/test/Sentry.Analyzers.Tests/Sentry.Analyzers.Tests.csproj
index 9750861210..c580d53e9d 100644
--- a/test/Sentry.Analyzers.Tests/Sentry.Analyzers.Tests.csproj
+++ b/test/Sentry.Analyzers.Tests/Sentry.Analyzers.Tests.csproj
@@ -1,7 +1,7 @@
- net8.0
+ $(PreviousTfm)
enable
false
diff --git a/test/Sentry.Android.AssemblyReader.Tests/AndroidAssemblyReaderTests.cs b/test/Sentry.Android.AssemblyReader.Tests/AndroidAssemblyReaderTests.cs
index 24344f2edd..4675f4e02e 100644
--- a/test/Sentry.Android.AssemblyReader.Tests/AndroidAssemblyReaderTests.cs
+++ b/test/Sentry.Android.AssemblyReader.Tests/AndroidAssemblyReaderTests.cs
@@ -1,4 +1,3 @@
-using Sentry.Android.AssemblyReader.V1;
using Sentry.Android.AssemblyReader.V2;
namespace Sentry.Android.AssemblyReader.Tests;
@@ -7,10 +6,10 @@ public class AndroidAssemblyReaderTests
{
private readonly ITestOutputHelper _output;
-#if NET9_0
+#if NET10_0
+ private static string TargetFramework => "net10.0";
+#elif NET9_0
private static string TargetFramework => "net9.0";
-#elif NET8_0
- private static string TargetFramework => "net8.0";
#else
// Adding a new TFM to the project? Include it above
#error "Target Framework not yet supported for AndroidAssemblyReader"
@@ -52,11 +51,11 @@ public void CreatesCorrectStoreReader()
using var sut = GetSut(isAot: false, isAssemblyStore: true, isCompressed: true);
switch (TargetFramework)
{
- case "net9.0":
- Assert.IsType(sut);
+ case "net10.0":
+ Assert.IsType(sut);
break;
- case "net8.0":
- Assert.IsType(sut);
+ case "net9.0":
+ Assert.IsType(sut);
break;
default:
throw new NotSupportedException($"Unsupported target framework: {TargetFramework}");
@@ -72,11 +71,11 @@ public void CreatesCorrectArchiveReader()
using var sut = GetSut(isAot: false, isAssemblyStore: false, isCompressed: true);
switch (TargetFramework)
{
- case "net9.0":
- Assert.IsType(sut);
+ case "net10.0":
+ Assert.IsType(sut);
break;
- case "net8.0":
- Assert.IsType(sut);
+ case "net9.0":
+ Assert.IsType(sut);
break;
default:
throw new NotSupportedException($"Unsupported target framework: {TargetFramework}");
@@ -93,11 +92,7 @@ public void ReturnsNullIfAssemblyDoesntExist(bool isAssemblyStore)
}
public static IEnumerable
@@ -57,7 +56,8 @@
..\AndroidTestApp\bin\$(TargetFramework)\$(_ConfigString)\com.companyname.AndroidTestApp-Signed.apk
TestAPKs\$(TargetFramework)-$(_ConfigString).apk
-
+
+
diff --git a/test/Sentry.AspNetCore.Grpc.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt b/test/Sentry.AspNetCore.Grpc.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt
new file mode 100644
index 0000000000..60439aee0e
--- /dev/null
+++ b/test/Sentry.AspNetCore.Grpc.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt
@@ -0,0 +1,50 @@
+namespace Sentry.AspNetCore.Grpc
+{
+ public class DefaultProtobufRequestPayloadExtractor : Sentry.AspNetCore.Grpc.IProtobufRequestPayloadExtractor
+ {
+ public DefaultProtobufRequestPayloadExtractor() { }
+ public Google.Protobuf.IMessage ExtractPayload(Sentry.AspNetCore.Grpc.IProtobufRequest request)
+ where TRequest : class, Google.Protobuf.IMessage { }
+ }
+ public interface IProtobufRequestPayloadExtractor
+ {
+ Google.Protobuf.IMessage? ExtractPayload(Sentry.AspNetCore.Grpc.IProtobufRequest request)
+ where TRequest : class, Google.Protobuf.IMessage;
+ }
+ public interface IProtobufRequest
+ {
+ long? ContentLength { get; }
+ TRequest Request { get; }
+ }
+ public class ProtobufRequestExtractionDispatcher : Sentry.AspNetCore.Grpc.IProtobufRequestPayloadExtractor
+ {
+ public ProtobufRequestExtractionDispatcher(System.Collections.Generic.IEnumerable extractors, Sentry.SentryOptions options, System.Func sizeSwitch) { }
+ public Google.Protobuf.IMessage? ExtractPayload(Sentry.AspNetCore.Grpc.IProtobufRequest request)
+ where TRequest : class, Google.Protobuf.IMessage { }
+ }
+ public static class ScopeExtensions
+ {
+ public static void Populate(this Sentry.Scope scope, Grpc.Core.ServerCallContext context, TRequest? request, Sentry.AspNetCore.SentryAspNetCoreOptions options)
+ where TRequest : class { }
+ }
+ public static class SentryBuilderExtensions
+ {
+ public static Sentry.AspNetCore.ISentryBuilder AddGrpc(this Sentry.AspNetCore.ISentryBuilder builder) { }
+ }
+ public class SentryGrpcInterceptor : Grpc.Core.Interceptors.Interceptor
+ {
+ public SentryGrpcInterceptor(System.Func hubAccessor, Microsoft.Extensions.Options.IOptions options) { }
+ public override System.Threading.Tasks.Task ClientStreamingServerHandler(Grpc.Core.IAsyncStreamReader requestStream, Grpc.Core.ServerCallContext context, Grpc.Core.ClientStreamingServerMethod continuation)
+ where TRequest : class
+ where TResponse : class { }
+ public override System.Threading.Tasks.Task DuplexStreamingServerHandler(Grpc.Core.IAsyncStreamReader requestStream, Grpc.Core.IServerStreamWriter responseStream, Grpc.Core.ServerCallContext context, Grpc.Core.DuplexStreamingServerMethod continuation)
+ where TRequest : class
+ where TResponse : class { }
+ public override System.Threading.Tasks.Task ServerStreamingServerHandler(TRequest request, Grpc.Core.IServerStreamWriter responseStream, Grpc.Core.ServerCallContext context, Grpc.Core.ServerStreamingServerMethod continuation)
+ where TRequest : class
+ where TResponse : class { }
+ public override System.Threading.Tasks.Task UnaryServerHandler(TRequest request, Grpc.Core.ServerCallContext context, Grpc.Core.UnaryServerMethod continuation)
+ where TRequest : class
+ where TResponse : class { }
+ }
+}
\ No newline at end of file
diff --git a/test/Sentry.AspNetCore.Grpc.Tests/Sentry.AspNetCore.Grpc.Tests.csproj b/test/Sentry.AspNetCore.Grpc.Tests/Sentry.AspNetCore.Grpc.Tests.csproj
index 24227ac339..57dd94d734 100644
--- a/test/Sentry.AspNetCore.Grpc.Tests/Sentry.AspNetCore.Grpc.Tests.csproj
+++ b/test/Sentry.AspNetCore.Grpc.Tests/Sentry.AspNetCore.Grpc.Tests.csproj
@@ -1,13 +1,18 @@
- net9.0;net8.0
+ $(CurrentTfms)
-
-
-
+
+
+
+
+
+
+
+
diff --git a/test/Sentry.AspNetCore.TestUtils/Sentry.AspNetCore.TestUtils.csproj b/test/Sentry.AspNetCore.TestUtils/Sentry.AspNetCore.TestUtils.csproj
index c60ea32096..e93320ccdf 100644
--- a/test/Sentry.AspNetCore.TestUtils/Sentry.AspNetCore.TestUtils.csproj
+++ b/test/Sentry.AspNetCore.TestUtils/Sentry.AspNetCore.TestUtils.csproj
@@ -1,7 +1,7 @@
- net9.0;net8.0
+ $(CurrentTfms)
$(TargetFrameworks);net48
false
@@ -54,7 +54,14 @@
-
+
+
+
+
+
+
+
+
diff --git a/test/Sentry.AspNetCore.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt b/test/Sentry.AspNetCore.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt
new file mode 100644
index 0000000000..5f9eab70e9
--- /dev/null
+++ b/test/Sentry.AspNetCore.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt
@@ -0,0 +1,83 @@
+namespace Microsoft.AspNetCore.Builder
+{
+ public static class SentryTracingMiddlewareExtensions
+ {
+ public static Microsoft.AspNetCore.Builder.IApplicationBuilder UseSentryTracing(this Microsoft.AspNetCore.Builder.IApplicationBuilder builder) { }
+ }
+}
+namespace Microsoft.AspNetCore.Hosting
+{
+ public static class SentryWebHostBuilderExtensions
+ {
+ public static void AddSentryTunneling(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, params string[] hostnames) { }
+ public static Microsoft.AspNetCore.Hosting.IWebHostBuilder UseSentry(this Microsoft.AspNetCore.Hosting.IWebHostBuilder builder) { }
+ public static Microsoft.AspNetCore.Hosting.IWebHostBuilder UseSentry(this Microsoft.AspNetCore.Hosting.IWebHostBuilder builder, System.Action? configureSentry) { }
+ public static Microsoft.AspNetCore.Hosting.IWebHostBuilder UseSentry(this Microsoft.AspNetCore.Hosting.IWebHostBuilder builder, System.Action? configureOptions) { }
+ public static Microsoft.AspNetCore.Hosting.IWebHostBuilder UseSentry(this Microsoft.AspNetCore.Hosting.IWebHostBuilder builder, System.Action? configureSentry) { }
+ public static Microsoft.AspNetCore.Hosting.IWebHostBuilder UseSentry(this Microsoft.AspNetCore.Hosting.IWebHostBuilder builder, System.Action? configureOptions) { }
+ public static Microsoft.AspNetCore.Hosting.IWebHostBuilder UseSentry(this Microsoft.AspNetCore.Hosting.IWebHostBuilder builder, string dsn) { }
+ public static void UseSentryTunneling(this Microsoft.AspNetCore.Builder.IApplicationBuilder builder, string path = "/tunnel") { }
+ }
+}
+namespace Microsoft.Extensions.DependencyInjection
+{
+ public static class ServiceCollectionExtensions
+ {
+ public static Sentry.AspNetCore.ISentryBuilder AddSentry(this Microsoft.Extensions.DependencyInjection.IServiceCollection services) { }
+ }
+}
+namespace Sentry.AspNetCore
+{
+ public interface ISentryBuilder
+ {
+ Microsoft.Extensions.DependencyInjection.IServiceCollection Services { get; }
+ }
+ public static class SamplingExtensions
+ {
+ public static Microsoft.AspNetCore.Http.HttpContext? TryGetHttpContext(this Sentry.TransactionSamplingContext samplingContext) { }
+ public static string? TryGetHttpMethod(this Sentry.TransactionSamplingContext samplingContext) { }
+ public static string? TryGetHttpPath(this Sentry.TransactionSamplingContext samplingContext) { }
+ public static string? TryGetHttpRoute(this Sentry.TransactionSamplingContext samplingContext) { }
+ }
+ public static class ScopeExtensions
+ {
+ public static void Populate(this Sentry.Scope scope, System.Diagnostics.Activity activity) { }
+ public static void Populate(this Sentry.Scope scope, Microsoft.AspNetCore.Http.HttpContext context, Sentry.AspNetCore.SentryAspNetCoreOptions options) { }
+ }
+ [Microsoft.Extensions.Logging.ProviderAlias("Sentry")]
+ public class SentryAspNetCoreLoggerProvider : Sentry.Extensions.Logging.SentryLoggerProvider
+ {
+ public SentryAspNetCoreLoggerProvider(Microsoft.Extensions.Options.IOptions options, Sentry.IHub hub) { }
+ }
+ public class SentryAspNetCoreOptions : Sentry.Extensions.Logging.SentryLoggingOptions
+ {
+ public SentryAspNetCoreOptions() { }
+ public bool AdjustStandardEnvironmentNameCasing { get; set; }
+ public bool AutoRegisterTracing { get; set; }
+ public bool CaptureBlockingCalls { get; set; }
+ public bool FlushOnCompletedRequest { get; set; }
+ public bool IncludeActivityData { get; set; }
+ public Sentry.Extensibility.RequestSize MaxRequestBodySize { get; set; }
+ public Sentry.AspNetCore.TransactionNameProvider? TransactionNameProvider { get; set; }
+ }
+ public class SentryAspNetCoreOptionsSetup : Microsoft.Extensions.Options.IConfigureOptions
+ {
+ public SentryAspNetCoreOptionsSetup(Microsoft.Extensions.Logging.Configuration.ILoggerProviderConfiguration providerConfiguration) { }
+ public void Configure(Sentry.AspNetCore.SentryAspNetCoreOptions options) { }
+ }
+ public static class SentryBuilderExtensions
+ {
+ public static Sentry.AspNetCore.ISentryBuilder AddSentryOptions(this Sentry.AspNetCore.ISentryBuilder builder, System.Action? configureOptions) { }
+ }
+ public class SentryStartupFilter : Microsoft.AspNetCore.Hosting.IStartupFilter
+ {
+ public SentryStartupFilter() { }
+ public System.Action Configure(System.Action next) { }
+ }
+ public class SentryTunnelMiddleware : Microsoft.AspNetCore.Http.IMiddleware
+ {
+ public SentryTunnelMiddleware(string[] allowedHosts) { }
+ public System.Threading.Tasks.Task InvokeAsync(Microsoft.AspNetCore.Http.HttpContext context, Microsoft.AspNetCore.Http.RequestDelegate next) { }
+ }
+ public delegate string? TransactionNameProvider(Microsoft.AspNetCore.Http.HttpContext context);
+}
\ No newline at end of file
diff --git a/test/Sentry.AspNetCore.Tests/Sentry.AspNetCore.Tests.csproj b/test/Sentry.AspNetCore.Tests/Sentry.AspNetCore.Tests.csproj
index 0761e91aa6..77c4d3735e 100644
--- a/test/Sentry.AspNetCore.Tests/Sentry.AspNetCore.Tests.csproj
+++ b/test/Sentry.AspNetCore.Tests/Sentry.AspNetCore.Tests.csproj
@@ -1,7 +1,7 @@
- net9.0;net8.0
+ $(CurrentTfms)
diff --git a/test/Sentry.AspNetCore.Tests/WebIntegrationTests.PreFlightIgnoresTransaction.DotNet.verified.txt b/test/Sentry.AspNetCore.Tests/WebIntegrationTests.PreFlightIgnoresTransaction.DotNet.verified.txt
deleted file mode 100644
index 5f282702bb..0000000000
--- a/test/Sentry.AspNetCore.Tests/WebIntegrationTests.PreFlightIgnoresTransaction.DotNet.verified.txt
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/test/Sentry.AspNetCore.Tests/WebIntegrationTests.PreFlightIgnoresTransaction.DotNet10_0.verified.txt b/test/Sentry.AspNetCore.Tests/WebIntegrationTests.PreFlightIgnoresTransaction.DotNet10_0.verified.txt
new file mode 100644
index 0000000000..679694a306
--- /dev/null
+++ b/test/Sentry.AspNetCore.Tests/WebIntegrationTests.PreFlightIgnoresTransaction.DotNet10_0.verified.txt
@@ -0,0 +1,14 @@
+{
+ result: {
+ Status: 204 No Content,
+ Headers: {
+ Access-Control-Allow-Headers: origin,
+ Access-Control-Allow-Methods: GET,
+ Access-Control-Allow-Origin: *
+ },
+ Content: {
+ Headers: {}
+ }
+ },
+ Payloads: []
+}
\ No newline at end of file
diff --git a/test/Sentry.AspNetCore.Tests/WebIntegrationTests.Versioning.DotNet.DotNet8_0.verified.txt b/test/Sentry.AspNetCore.Tests/WebIntegrationTests.Versioning.DotNet.DotNet8_0.verified.txt
deleted file mode 100644
index 9abeb1309b..0000000000
--- a/test/Sentry.AspNetCore.Tests/WebIntegrationTests.Versioning.DotNet.DotNet8_0.verified.txt
+++ /dev/null
@@ -1,87 +0,0 @@
-{
- result: Hello world,
- Payloads: [
- {
- Source: {
- Name: GET v1.1/Target,
- NameSource: Route,
- Platform: csharp,
- Operation: http.server,
- Description: ,
- Status: Ok,
- IsSampled: true,
- SampleRate: 1.0,
- Request: {
- Method: GET,
- QueryString:
- },
- Contexts: {
- trace: {
- Operation: http.server,
- Description: ,
- Status: Ok,
- IsSampled: true
- }
- },
- User: {},
- Environment: production,
- Breadcrumbs: [
- {
- Message: Request starting HTTP/1.1 GET http://localhost/v1.1/Target - - -,
- Data: {
- eventId: 1
- },
- Category: Microsoft.AspNetCore.Hosting.Diagnostics
- },
- {
- Message: Executing endpoint 'Sentry.AspNetCore.Tests.WebIntegrationTests+VersionController.Method (Sentry.AspNetCore.Tests)',
- Data: {
- eventId: ExecutingEndpoint
- },
- Category: Microsoft.AspNetCore.Routing.EndpointMiddleware
- },
- {
- Message: Route matched with {action = "Method", controller = "Version"}. Executing controller action with signature System.String Method() on controller Sentry.AspNetCore.Tests.WebIntegrationTests+VersionController (Sentry.AspNetCore.Tests).,
- Data: {
- eventId: ControllerActionExecuting
- },
- Category: Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker
- },
- {
- Message: Executing ObjectResult, writing value of type 'System.String'.,
- Data: {
- eventId: ObjectResultExecuting
- },
- Category: Microsoft.AspNetCore.Mvc.Infrastructure.ObjectResultExecutor
- },
- {
- Message: Executed action Sentry.AspNetCore.Tests.WebIntegrationTests+VersionController.Method,
- Data: {
- eventId: ActionExecuted
- },
- Category: Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker
- },
- {
- Message: Executed endpoint 'Sentry.AspNetCore.Tests.WebIntegrationTests+VersionController.Method (Sentry.AspNetCore.Tests)',
- Data: {
- eventId: ExecutedEndpoint
- },
- Category: Microsoft.AspNetCore.Routing.EndpointMiddleware
- }
- ],
- Extra: {
- http.request.method: GET,
- http.response.status_code: 200
- },
- Tags: {
- ActionId: Guid_1,
- ActionName: Sentry.AspNetCore.Tests.WebIntegrationTests+VersionController.Method (Sentry.AspNetCore.Tests),
- route.action: Method,
- route.controller: Version,
- route.version: 1.1
- },
- IsFinished: true
- }
- }
- ]
-}
\ No newline at end of file
diff --git a/test/Sentry.AspNetCore.Tests/WebIntegrationTests.Versioning.verified.txt b/test/Sentry.AspNetCore.Tests/WebIntegrationTests.Versioning.DotNet10_0.verified.txt
similarity index 94%
rename from test/Sentry.AspNetCore.Tests/WebIntegrationTests.Versioning.verified.txt
rename to test/Sentry.AspNetCore.Tests/WebIntegrationTests.Versioning.DotNet10_0.verified.txt
index b132b2f346..019cc1fb2d 100644
--- a/test/Sentry.AspNetCore.Tests/WebIntegrationTests.Versioning.verified.txt
+++ b/test/Sentry.AspNetCore.Tests/WebIntegrationTests.Versioning.DotNet10_0.verified.txt
@@ -3,6 +3,7 @@
Payloads: [
{
Source: {
+ Origin: auto.http.aspnetcore,
Name: GET /v1.1/Target,
NameSource: Route,
Platform: csharp,
@@ -18,16 +19,19 @@
Contexts: {
trace: {
Operation: http.server,
+ Origin: auto.http.aspnetcore,
Description: ,
Status: Ok,
IsSampled: true
}
},
- User: {},
+ User: {
+ Id: Guid_1
+ },
Environment: production,
Breadcrumbs: [
{
- Message: Request starting HTTP/1.1 GET http://localhost/v1.1/Target - -,
+ Message: Request starting HTTP/1.1 GET http://localhost/v1.1/Target - - -,
Data: {
eventId: 1
},
@@ -69,12 +73,8 @@
Category: Microsoft.AspNetCore.Routing.EndpointMiddleware
}
],
- Extra: {
- http.request.method: GET,
- http.response.status_code: 200
- },
Tags: {
- ActionId: Guid_1,
+ ActionId: Guid_2,
ActionName: Sentry.AspNetCore.Tests.WebIntegrationTests+VersionController.Method (Sentry.AspNetCore.Tests),
route.action: Method,
route.controller: Version,
diff --git a/test/Sentry.Azure.Functions.Worker.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt b/test/Sentry.Azure.Functions.Worker.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt
new file mode 100644
index 0000000000..f6ee752df8
--- /dev/null
+++ b/test/Sentry.Azure.Functions.Worker.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt
@@ -0,0 +1,15 @@
+namespace Sentry.Azure.Functions.Worker
+{
+ public class SentryAzureFunctionsOptions : Sentry.Extensions.Logging.SentryLoggingOptions
+ {
+ public SentryAzureFunctionsOptions() { }
+ }
+ public static class SentryFunctionsWorkerApplicationBuilderExtensions
+ {
+ public static Microsoft.Azure.Functions.Worker.IFunctionsWorkerApplicationBuilder UseSentry(this Microsoft.Azure.Functions.Worker.IFunctionsWorkerApplicationBuilder builder, Microsoft.Extensions.Hosting.HostBuilderContext context) { }
+ public static Microsoft.Azure.Functions.Worker.IFunctionsWorkerApplicationBuilder UseSentry(this Microsoft.Azure.Functions.Worker.IFunctionsWorkerApplicationBuilder builder, System.Action? optionsConfiguration) { }
+ public static Microsoft.Azure.Functions.Worker.IFunctionsWorkerApplicationBuilder UseSentry(this Microsoft.Azure.Functions.Worker.IFunctionsWorkerApplicationBuilder builder, Microsoft.Extensions.Configuration.IConfiguration configuration, System.Action? optionsConfiguration) { }
+ public static Microsoft.Azure.Functions.Worker.IFunctionsWorkerApplicationBuilder UseSentry(this Microsoft.Azure.Functions.Worker.IFunctionsWorkerApplicationBuilder builder, Microsoft.Extensions.Hosting.HostBuilderContext context, System.Action? optionsConfiguration) { }
+ public static Microsoft.Azure.Functions.Worker.IFunctionsWorkerApplicationBuilder UseSentry(this Microsoft.Azure.Functions.Worker.IFunctionsWorkerApplicationBuilder builder, Microsoft.Extensions.Hosting.HostBuilderContext context, string dsn) { }
+ }
+}
\ No newline at end of file
diff --git a/test/Sentry.Azure.Functions.Worker.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt b/test/Sentry.Azure.Functions.Worker.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt
new file mode 100644
index 0000000000..f6ee752df8
--- /dev/null
+++ b/test/Sentry.Azure.Functions.Worker.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt
@@ -0,0 +1,15 @@
+namespace Sentry.Azure.Functions.Worker
+{
+ public class SentryAzureFunctionsOptions : Sentry.Extensions.Logging.SentryLoggingOptions
+ {
+ public SentryAzureFunctionsOptions() { }
+ }
+ public static class SentryFunctionsWorkerApplicationBuilderExtensions
+ {
+ public static Microsoft.Azure.Functions.Worker.IFunctionsWorkerApplicationBuilder UseSentry(this Microsoft.Azure.Functions.Worker.IFunctionsWorkerApplicationBuilder builder, Microsoft.Extensions.Hosting.HostBuilderContext context) { }
+ public static Microsoft.Azure.Functions.Worker.IFunctionsWorkerApplicationBuilder UseSentry(this Microsoft.Azure.Functions.Worker.IFunctionsWorkerApplicationBuilder builder, System.Action? optionsConfiguration) { }
+ public static Microsoft.Azure.Functions.Worker.IFunctionsWorkerApplicationBuilder UseSentry(this Microsoft.Azure.Functions.Worker.IFunctionsWorkerApplicationBuilder builder, Microsoft.Extensions.Configuration.IConfiguration configuration, System.Action? optionsConfiguration) { }
+ public static Microsoft.Azure.Functions.Worker.IFunctionsWorkerApplicationBuilder UseSentry(this Microsoft.Azure.Functions.Worker.IFunctionsWorkerApplicationBuilder builder, Microsoft.Extensions.Hosting.HostBuilderContext context, System.Action? optionsConfiguration) { }
+ public static Microsoft.Azure.Functions.Worker.IFunctionsWorkerApplicationBuilder UseSentry(this Microsoft.Azure.Functions.Worker.IFunctionsWorkerApplicationBuilder builder, Microsoft.Extensions.Hosting.HostBuilderContext context, string dsn) { }
+ }
+}
\ No newline at end of file
diff --git a/test/Sentry.Azure.Functions.Worker.Tests/Sentry.Azure.Functions.Worker.Tests.csproj b/test/Sentry.Azure.Functions.Worker.Tests/Sentry.Azure.Functions.Worker.Tests.csproj
index eee27b2b21..7e2f3e1603 100644
--- a/test/Sentry.Azure.Functions.Worker.Tests/Sentry.Azure.Functions.Worker.Tests.csproj
+++ b/test/Sentry.Azure.Functions.Worker.Tests/Sentry.Azure.Functions.Worker.Tests.csproj
@@ -1,7 +1,7 @@
- net8.0
+ $(CurrentTfms)
$(TargetFrameworks);net48
diff --git a/test/Sentry.DiagnosticSource.IntegrationTests/LocalDbFixture.cs b/test/Sentry.DiagnosticSource.IntegrationTests/LocalDbFixture.cs
index 0e1a01e200..8afd8df752 100644
--- a/test/Sentry.DiagnosticSource.IntegrationTests/LocalDbFixture.cs
+++ b/test/Sentry.DiagnosticSource.IntegrationTests/LocalDbFixture.cs
@@ -15,6 +15,8 @@ public sealed class LocalDbFixture : IDisposable
"SqlListenerTests8";
#elif NET9_0
"SqlListenerTests9";
+#elif NET10_0
+ "SqlListenerTests10";
#else
#error Needs a version specific name to prevent the tests from tripping over one another when running in parallel
#endif
diff --git a/test/Sentry.DiagnosticSource.IntegrationTests/Sentry.DiagnosticSource.IntegrationTests.csproj b/test/Sentry.DiagnosticSource.IntegrationTests/Sentry.DiagnosticSource.IntegrationTests.csproj
index 504be0b959..62d67cadbb 100644
--- a/test/Sentry.DiagnosticSource.IntegrationTests/Sentry.DiagnosticSource.IntegrationTests.csproj
+++ b/test/Sentry.DiagnosticSource.IntegrationTests/Sentry.DiagnosticSource.IntegrationTests.csproj
@@ -1,11 +1,18 @@
- net9.0;net8.0
+ $(CurrentTfms)
$(TargetFrameworks);net48
+
+
+
+
+
+
+
@@ -29,8 +36,6 @@
-
-
diff --git a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.LoggingAsync.DotNet10_0.verified.txt b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.LoggingAsync.DotNet10_0.verified.txt
new file mode 100644
index 0000000000..348b185b6f
--- /dev/null
+++ b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.LoggingAsync.DotNet10_0.verified.txt
@@ -0,0 +1,63 @@
+[
+ {
+ Source: {
+ Name: my transaction,
+ Platform: csharp,
+ Operation: my operation,
+ Description: ,
+ Status: Ok,
+ IsSampled: true,
+ SampleRate: 1.0,
+ Request: {},
+ Contexts: {
+ trace: {
+ Operation: my operation,
+ Description: ,
+ Status: Ok,
+ IsSampled: true
+ }
+ },
+ User: {
+ Id: Guid_1
+ },
+ Spans: [
+ {
+ IsFinished: true,
+ Operation: db.connection,
+ Description: SqlListenerTests.verify_LoggingAsync,
+ Status: Ok,
+ IsSampled: true,
+ Data: {
+ bytes_sent : 376,
+ db.connection_id: Guid_2,
+ db.name: SqlListenerTests.verify_LoggingAsync,
+ db.operation_id: Guid_3,
+ db.server: (LocalDb)\SqlListenerTests,
+ db.system: sql,
+ rows_sent: 0
+ }
+ },
+ {
+ IsFinished: true,
+ Operation: db.query,
+ Description:
+SET IMPLICIT_TRANSACTIONS OFF;
+SET NOCOUNT ON;
+INSERT INTO [TestEntities] ([Property])
+OUTPUT INSERTED.[Id]
+VALUES (@p0);
+,
+ Status: Ok,
+ IsSampled: true,
+ Data: {
+ db.connection_id: Guid_2,
+ db.name: SqlListenerTests.verify_LoggingAsync,
+ db.operation_id: Guid_4,
+ db.system: sql
+ }
+ }
+ ],
+ IsFinished: true
+ }
+ }
+]
\ No newline at end of file
diff --git a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.DotNet10_0.verified.txt b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.DotNet10_0.verified.txt
new file mode 100644
index 0000000000..05803effbe
--- /dev/null
+++ b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.RecordsEfAsync.DotNet10_0.verified.txt
@@ -0,0 +1,131 @@
+[
+ {
+ Source: {
+ Exception: {
+ $type: Exception,
+ Type: Exception,
+ Message: my exception
+ },
+ Platform: csharp,
+ SentryExceptions: [
+ {
+ Type: System.Exception,
+ Value: my exception,
+ Mechanism: {
+ Type: generic,
+ Synthetic: false,
+ IsExceptionGroup: false,
+ Data: {
+ HResult: 0x80131500
+ }
+ }
+ }
+ ],
+ Level: error,
+ TransactionName: my transaction,
+ Request: {},
+ Contexts: {
+ trace: {
+ Operation:
+ }
+ },
+ User: {
+ Id: Guid_1
+ }
+ }
+ },
+ {
+ Source: {
+ Name: my transaction,
+ Platform: csharp,
+ Operation: my operation,
+ Description: ,
+ Status: Ok,
+ IsSampled: true,
+ SampleRate: 1.0,
+ Request: {},
+ Contexts: {
+ trace: {
+ Operation: my operation,
+ Description: ,
+ Status: Ok,
+ IsSampled: true
+ }
+ },
+ User: {
+ Id: Guid_1
+ },
+ Breadcrumbs: [
+ {
+ Message: my exception,
+ Category: Exception,
+ Level: critical
+ }
+ ],
+ Spans: [
+ {
+ IsFinished: true,
+ Operation: db.connection,
+ Description: SqlListenerTests.verify_RecordsEfAsync,
+ Status: Ok,
+ IsSampled: true,
+ Data: {
+ bytes_received: 225,
+ bytes_sent : 570,
+ db.connection_id: Guid_2,
+ db.name: SqlListenerTests.verify_RecordsEfAsync,
+ db.operation_id: Guid_3,
+ db.server: (LocalDb)\SqlListenerTests,
+ db.system: sql,
+ rows_sent: 1
+ }
+ },
+ {
+ IsFinished: true,
+ Operation: db.query,
+ Description:
+SET IMPLICIT_TRANSACTIONS OFF;
+SET NOCOUNT ON;
+INSERT INTO [TestEntities] ([Property])
+OUTPUT INSERTED.[Id]
+VALUES (@p0);
+,
+ Status: Ok,
+ IsSampled: true,
+ Data: {
+ db.connection_id: Guid_2,
+ db.name: SqlListenerTests.verify_RecordsEfAsync,
+ db.operation_id: Guid_4,
+ db.system: sql
+ }
+ },
+ {
+ IsFinished: true,
+ Operation: db.query.compile,
+ Description: 'DbSet()',
+ Status: Ok,
+ IsSampled: true,
+ Data: {
+ db.system: mssql
+ }
+ },
+ {
+ IsFinished: true,
+ Operation: db.query,
+ Description:
+SELECT [t].[Id], [t].[Property]
+FROM [TestEntities] AS [t],
+ Status: Ok,
+ IsSampled: true,
+ Data: {
+ db.connection_id: Guid_2,
+ db.name: SqlListenerTests.verify_RecordsEfAsync,
+ db.operation_id: Guid_5,
+ db.system: sql
+ }
+ }
+ ],
+ IsFinished: true
+ }
+ }
+]
\ No newline at end of file
diff --git a/test/Sentry.DiagnosticSource.Tests/Sentry.DiagnosticSource.Tests.csproj b/test/Sentry.DiagnosticSource.Tests/Sentry.DiagnosticSource.Tests.csproj
index cf4e670998..93ad42e99b 100644
--- a/test/Sentry.DiagnosticSource.Tests/Sentry.DiagnosticSource.Tests.csproj
+++ b/test/Sentry.DiagnosticSource.Tests/Sentry.DiagnosticSource.Tests.csproj
@@ -1,11 +1,17 @@
- net9.0;net8.0
+ $(CurrentTfms)
$(TargetFrameworks);net48
+
+
+
+
+
+
@@ -16,8 +22,6 @@
-
-
diff --git a/test/Sentry.EntityFramework.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt b/test/Sentry.EntityFramework.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt
new file mode 100644
index 0000000000..53cafe92cc
--- /dev/null
+++ b/test/Sentry.EntityFramework.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt
@@ -0,0 +1,39 @@
+namespace Sentry.EntityFramework.ErrorProcessors
+{
+ public class DbConcurrencyExceptionProcessor : Sentry.Extensibility.SentryEventExceptionProcessor
+ {
+ public DbConcurrencyExceptionProcessor() { }
+ protected override void ProcessException(System.Data.DBConcurrencyException exception, Sentry.SentryEvent sentryEvent) { }
+ }
+ public class DbEntityValidationExceptionProcessor : Sentry.Extensibility.SentryEventExceptionProcessor
+ {
+ public DbEntityValidationExceptionProcessor() { }
+ protected override void ProcessException(System.Data.Entity.Validation.DbEntityValidationException exception, Sentry.SentryEvent sentryEvent) { }
+ }
+}
+namespace Sentry.EntityFramework
+{
+ public interface IQueryLogger
+ {
+ void Log(string text, Sentry.BreadcrumbLevel level = -1);
+ }
+ public class SentryCommandInterceptor : System.Data.Entity.Infrastructure.Interception.IDbCommandInterceptor, System.Data.Entity.Infrastructure.Interception.IDbInterceptor
+ {
+ public SentryCommandInterceptor(Sentry.EntityFramework.IQueryLogger queryLogger) { }
+ public virtual void Log(System.Data.Common.DbCommand command, System.Data.Entity.Infrastructure.Interception.DbCommandInterceptionContext interceptionContext) { }
+ public void NonQueryExecuted(System.Data.Common.DbCommand command, System.Data.Entity.Infrastructure.Interception.DbCommandInterceptionContext interceptionContext) { }
+ public void NonQueryExecuting(System.Data.Common.DbCommand command, System.Data.Entity.Infrastructure.Interception.DbCommandInterceptionContext interceptionContext) { }
+ public void ReaderExecuted(System.Data.Common.DbCommand command, System.Data.Entity.Infrastructure.Interception.DbCommandInterceptionContext interceptionContext) { }
+ public void ReaderExecuting(System.Data.Common.DbCommand command, System.Data.Entity.Infrastructure.Interception.DbCommandInterceptionContext interceptionContext) { }
+ public void ScalarExecuted(System.Data.Common.DbCommand command, System.Data.Entity.Infrastructure.Interception.DbCommandInterceptionContext