diff --git a/CHANGELOG.md b/CHANGELOG.md
index 11f2237078..2f57a6c237 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,13 @@
## Unreleased
+### Features
+
+- Add _experimental_ support for [Sentry Structured Logging](https://docs.sentry.io/product/explore/logs/) ([#4308](https://github.com/getsentry/sentry-dotnet/pull/4308))
+ - Structured-Logger API ([#4158](https://github.com/getsentry/sentry-dotnet/pull/4158))
+ - Buffering and Batching ([#4310](https://github.com/getsentry/sentry-dotnet/pull/4310))
+ - Integrations for `Sentry.Extensions.Logging`, `Sentry.AspNetCore` and `Sentry.Maui` ([#4193](https://github.com/getsentry/sentry-dotnet/pull/4193))
+
### Fixes
- Update `sample_rate` of _Dynamic Sampling Context (DSC)_ when making sampling decisions ([#4374](https://github.com/getsentry/sentry-dotnet/pull/4374))
diff --git a/benchmarks/Sentry.Benchmarks/BenchmarkDotNet.Artifacts/results/Sentry.Benchmarks.Extensions.Logging.SentryStructuredLoggerBenchmarks-report-github.md b/benchmarks/Sentry.Benchmarks/BenchmarkDotNet.Artifacts/results/Sentry.Benchmarks.Extensions.Logging.SentryStructuredLoggerBenchmarks-report-github.md
new file mode 100644
index 0000000000..172b688e55
--- /dev/null
+++ b/benchmarks/Sentry.Benchmarks/BenchmarkDotNet.Artifacts/results/Sentry.Benchmarks.Extensions.Logging.SentryStructuredLoggerBenchmarks-report-github.md
@@ -0,0 +1,13 @@
+```
+
+BenchmarkDotNet v0.13.12, macOS 15.5 (24F74) [Darwin 24.5.0]
+Apple M3 Pro, 1 CPU, 12 logical and 12 physical cores
+.NET SDK 9.0.301
+ [Host] : .NET 8.0.14 (8.0.1425.11118), Arm64 RyuJIT AdvSIMD
+ DefaultJob : .NET 8.0.14 (8.0.1425.11118), Arm64 RyuJIT AdvSIMD
+
+
+```
+| Method | Mean | Error | StdDev | Gen0 | Allocated |
+|------- |---------:|--------:|--------:|-------:|----------:|
+| Log | 288.4 ns | 1.28 ns | 1.20 ns | 0.1163 | 976 B |
diff --git a/benchmarks/Sentry.Benchmarks/BenchmarkDotNet.Artifacts/results/Sentry.Benchmarks.StructuredLogBatchProcessorBenchmarks-report-github.md b/benchmarks/Sentry.Benchmarks/BenchmarkDotNet.Artifacts/results/Sentry.Benchmarks.StructuredLogBatchProcessorBenchmarks-report-github.md
new file mode 100644
index 0000000000..befa791365
--- /dev/null
+++ b/benchmarks/Sentry.Benchmarks/BenchmarkDotNet.Artifacts/results/Sentry.Benchmarks.StructuredLogBatchProcessorBenchmarks-report-github.md
@@ -0,0 +1,18 @@
+```
+
+BenchmarkDotNet v0.13.12, macOS 15.5 (24F74) [Darwin 24.5.0]
+Apple M3 Pro, 1 CPU, 12 logical and 12 physical cores
+.NET SDK 9.0.301
+ [Host] : .NET 8.0.14 (8.0.1425.11118), Arm64 RyuJIT AdvSIMD
+ DefaultJob : .NET 8.0.14 (8.0.1425.11118), Arm64 RyuJIT AdvSIMD
+
+
+```
+| Method | BatchCount | OperationsPerInvoke | Mean | Error | StdDev | Gen0 | Allocated |
+|---------------- |----------- |-------------------- |------------:|---------:|---------:|-------:|----------:|
+| **EnqueueAndFlush** | **10** | **100** | **1,774.5 ns** | **7.57 ns** | **6.71 ns** | **0.6104** | **5 KB** |
+| **EnqueueAndFlush** | **10** | **200** | **3,468.5 ns** | **11.16 ns** | **10.44 ns** | **1.2207** | **10 KB** |
+| **EnqueueAndFlush** | **10** | **1000** | **17,259.7 ns** | **51.92 ns** | **46.02 ns** | **6.1035** | **50 KB** |
+| **EnqueueAndFlush** | **100** | **100** | **857.5 ns** | **4.21 ns** | **3.73 ns** | **0.1469** | **1.2 KB** |
+| **EnqueueAndFlush** | **100** | **200** | **1,681.4 ns** | **1.74 ns** | **1.63 ns** | **0.2937** | **2.41 KB** |
+| **EnqueueAndFlush** | **100** | **1000** | **8,302.2 ns** | **12.00 ns** | **10.64 ns** | **1.4648** | **12.03 KB** |
diff --git a/benchmarks/Sentry.Benchmarks/Extensions.Logging/SentryStructuredLoggerBenchmarks.cs b/benchmarks/Sentry.Benchmarks/Extensions.Logging/SentryStructuredLoggerBenchmarks.cs
new file mode 100644
index 0000000000..3e18747a65
--- /dev/null
+++ b/benchmarks/Sentry.Benchmarks/Extensions.Logging/SentryStructuredLoggerBenchmarks.cs
@@ -0,0 +1,90 @@
+#nullable enable
+
+using BenchmarkDotNet.Attributes;
+using Microsoft.Extensions.Logging;
+using Sentry.Extensibility;
+using Sentry.Extensions.Logging;
+using Sentry.Internal;
+using Sentry.Testing;
+
+namespace Sentry.Benchmarks.Extensions.Logging;
+
+public class SentryStructuredLoggerBenchmarks
+{
+ private Hub _hub = null!;
+ private Sentry.Extensions.Logging.SentryStructuredLogger _logger = null!;
+ private LogRecord _logRecord = null!;
+ private SentryLog? _lastLog;
+
+ [GlobalSetup]
+ public void Setup()
+ {
+ SentryLoggingOptions options = new()
+ {
+ Dsn = DsnSamples.ValidDsn,
+ Experimental =
+ {
+ EnableLogs = true,
+ },
+ ExperimentalLogging =
+ {
+ MinimumLogLevel = LogLevel.Information,
+ }
+ };
+ options.Experimental.SetBeforeSendLog((SentryLog log) =>
+ {
+ _lastLog = log;
+ return null;
+ });
+
+ MockClock clock = new(new DateTimeOffset(2025, 04, 22, 14, 51, 00, 789, TimeSpan.FromHours(2)));
+ SdkVersion sdk = new()
+ {
+ Name = "SDK Name",
+ Version = "SDK Version",
+ };
+
+ _hub = new Hub(options, DisabledHub.Instance);
+ _logger = new Sentry.Extensions.Logging.SentryStructuredLogger("CategoryName", options, _hub, clock, sdk);
+ _logRecord = new LogRecord(LogLevel.Information, new EventId(2025, "EventName"), new InvalidOperationException("exception-message"), "Number={Number}, Text={Text}", 2018, "message");
+ }
+
+ [Benchmark]
+ public void Log()
+ {
+ _logger.Log(_logRecord.LogLevel, _logRecord.EventId, _logRecord.Exception, _logRecord.Message, _logRecord.Args);
+ }
+
+ [GlobalCleanup]
+ public void Cleanup()
+ {
+ _hub.Dispose();
+
+ if (_lastLog is null)
+ {
+ throw new InvalidOperationException("Last Log is null");
+ }
+ if (_lastLog.Message != "Number=2018, Text=message")
+ {
+ throw new InvalidOperationException($"Last Log with Message: '{_lastLog.Message}'");
+ }
+ }
+
+ private sealed class LogRecord
+ {
+ public LogRecord(LogLevel logLevel, EventId eventId, Exception? exception, string? message, params object?[] args)
+ {
+ LogLevel = logLevel;
+ EventId = eventId;
+ Exception = exception;
+ Message = message;
+ Args = args;
+ }
+
+ public LogLevel LogLevel { get; }
+ public EventId EventId { get; }
+ public Exception? Exception { get; }
+ public string? Message { get; }
+ public object?[] Args { get; }
+ }
+}
diff --git a/benchmarks/Sentry.Benchmarks/Sentry.Benchmarks.csproj b/benchmarks/Sentry.Benchmarks/Sentry.Benchmarks.csproj
index bdb8ed918a..48231469f2 100644
--- a/benchmarks/Sentry.Benchmarks/Sentry.Benchmarks.csproj
+++ b/benchmarks/Sentry.Benchmarks/Sentry.Benchmarks.csproj
@@ -14,6 +14,7 @@
+
diff --git a/benchmarks/Sentry.Benchmarks/StructuredLogBatchProcessorBenchmarks.cs b/benchmarks/Sentry.Benchmarks/StructuredLogBatchProcessorBenchmarks.cs
new file mode 100644
index 0000000000..336d726926
--- /dev/null
+++ b/benchmarks/Sentry.Benchmarks/StructuredLogBatchProcessorBenchmarks.cs
@@ -0,0 +1,68 @@
+using BenchmarkDotNet.Attributes;
+using NSubstitute;
+using Sentry.Extensibility;
+using Sentry.Internal;
+
+namespace Sentry.Benchmarks;
+
+public class StructuredLogBatchProcessorBenchmarks
+{
+ private Hub _hub;
+ private StructuredLogBatchProcessor _batchProcessor;
+ private SentryLog _log;
+
+ [Params(10, 100)]
+ public int BatchCount { get; set; }
+
+ [Params(100, 200, 1_000)]
+ public int OperationsPerInvoke { get; set; }
+
+ [GlobalSetup]
+ public void Setup()
+ {
+ SentryOptions options = new()
+ {
+ Dsn = DsnSamples.ValidDsn,
+ Experimental =
+ {
+ EnableLogs = true,
+ },
+ };
+
+ var batchInterval = Timeout.InfiniteTimeSpan;
+
+ var clientReportRecorder = Substitute.For();
+ clientReportRecorder
+ .When(static recorder => recorder.RecordDiscardedEvent(Arg.Any(), Arg.Any(), Arg.Any()))
+ .Throw();
+
+ var diagnosticLogger = Substitute.For();
+ diagnosticLogger
+ .When(static logger => logger.IsEnabled(Arg.Any()))
+ .Throw();
+ diagnosticLogger
+ .When(static logger => logger.Log(Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any