Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
# Changelog

## Unreleased
## 6.1.0-alpha.1

### Features

- Extended `SentryThread` by `Main` to allow indication whether the thread is considered the current main thread ([#4807](https://github.com/getsentry/sentry-dotnet/pull/4807))
- Add _experimental_ support for [Sentry trace-connected Metrics](https://docs.sentry.io/product/explore/metrics/) ([#4834](https://github.com/getsentry/sentry-dotnet/pull/4834))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • 🚫 The changelog entry seems to be part of an already released section ## 6.1.0.
    Consider moving the entry to the ## Unreleased section, please.


### Dependencies

Expand Down
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project>

<PropertyGroup>
<VersionPrefix>6.0.0</VersionPrefix>
<VersionPrefix>6.1.0-alpha.1</VersionPrefix>
<LangVersion>13</LangVersion>
<AccelerateBuildsInVisualStudio>true</AccelerateBuildsInVisualStudio>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
using BenchmarkDotNet.Attributes;
using Sentry.Extensibility;
using Sentry.Internal;
using Sentry.Protocol;

namespace Sentry.Benchmarks;

public class StructuredLogBatchProcessorBenchmarks
/// <summary>
/// <see cref="BatchProcessor{TItem}"/> (formerly "Sentry.Internal.StructuredLogBatchProcessor") was originally developed as Batch Processor for Logs only.
/// When adding support for Trace-connected Metrics, which are quite similar to Logs, it has been made generic to support both.
/// For comparability of results, we still benchmark with <see cref="SentryLog"/>, rather than <see cref="SentryMetric"/>.
/// </summary>
public class BatchProcessorBenchmarks
{
private Hub _hub;
private StructuredLogBatchProcessor _batchProcessor;
private BatchProcessor<SentryLog> _batchProcessor;
private SentryLog _log;

[Params(10, 100)]
Expand All @@ -29,7 +35,7 @@ public void Setup()
var clientReportRecorder = new NullClientReportRecorder();

_hub = new Hub(options, DisabledHub.Instance);
_batchProcessor = new StructuredLogBatchProcessor(_hub, BatchCount, batchInterval, clientReportRecorder, null);
_batchProcessor = new BatchProcessor<SentryLog>(_hub, BatchCount, batchInterval, StructuredLog.Capture, clientReportRecorder, null);
_log = new SentryLog(DateTimeOffset.Now, SentryId.Empty, SentryLogLevel.Trace, "message");
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
```

BenchmarkDotNet v0.13.12, macOS 26.1 (25B78) [Darwin 25.1.0]
Apple M3 Pro, 1 CPU, 12 logical and 12 physical cores
.NET SDK 10.0.100
[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 | Median | Gen0 | Allocated |
|------------------------- |----------- |-------------------- |-------------:|------------:|-------------:|-------------:|-------:|----------:|
| **EnqueueAndFlush** | **10** | **100** | **1,896.9 ns** | **9.94 ns** | **8.81 ns** | **1,894.2 ns** | **0.6104** | **5 KB** |
| EnqueueAndFlush_Parallel | 10 | 100 | 16,520.9 ns | 327.78 ns | 746.51 ns | 16,350.4 ns | 1.1292 | 9.29 KB |
| **EnqueueAndFlush** | **10** | **200** | **4,085.5 ns** | **80.03 ns** | **74.86 ns** | **4,087.1 ns** | **1.2207** | **10 KB** |
| EnqueueAndFlush_Parallel | 10 | 200 | 39,371.8 ns | 776.85 ns | 1,360.59 ns | 38,725.0 ns | 1.6479 | 13.6 KB |
| **EnqueueAndFlush** | **10** | **1000** | **18,829.3 ns** | **182.18 ns** | **142.24 ns** | **18,836.4 ns** | **6.1035** | **50 KB** |
| EnqueueAndFlush_Parallel | 10 | 1000 | 151,934.1 ns | 2,631.83 ns | 3,232.12 ns | 151,495.9 ns | 3.6621 | 31.31 KB |
| **EnqueueAndFlush** | **100** | **100** | **864.9 ns** | **2.16 ns** | **1.68 ns** | **865.0 ns** | **0.1469** | **1.2 KB** |
| EnqueueAndFlush_Parallel | 100 | 100 | 7,414.9 ns | 74.86 ns | 70.02 ns | 7,405.9 ns | 0.5722 | 4.61 KB |
| **EnqueueAndFlush** | **100** | **200** | **1,836.9 ns** | **15.28 ns** | **12.76 ns** | **1,834.9 ns** | **0.2937** | **2.41 KB** |
| EnqueueAndFlush_Parallel | 100 | 200 | 37,119.5 ns | 726.04 ns | 1,252.39 ns | 36,968.9 ns | 0.8545 | 7.27 KB |
| **EnqueueAndFlush** | **100** | **1000** | **8,567.2 ns** | **84.25 ns** | **74.68 ns** | **8,547.4 ns** | **1.4648** | **12.03 KB** |
| EnqueueAndFlush_Parallel | 100 | 1000 | 255,284.5 ns | 5,095.08 ns | 12,593.77 ns | 258,313.9 ns | 1.9531 | 19.02 KB |

This file was deleted.

17 changes: 16 additions & 1 deletion src/Sentry/BindableSentryOptions.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
namespace Sentry;

/// <summary>
/// Contains representations of the subset of properties in SentryOptions that can be set from ConfigurationBindings.
/// Contains representations of the subset of properties in <see cref="SentryOptions"/> that can be set from ConfigurationBindings.
/// Note that all of these properties are nullable, so that if they are not present in configuration, the values from
/// the type being bound to will be preserved.
/// </summary>
Expand Down Expand Up @@ -56,6 +56,8 @@ internal partial class BindableSentryOptions
public bool? EnableSpotlight { get; set; }
public string? SpotlightUrl { get; set; }

public ExperimentalSentryOptions? Experimental { get; set; }

public void ApplyTo(SentryOptions options)
{
options.IsGlobalModeEnabled = IsGlobalModeEnabled ?? options.IsGlobalModeEnabled;
Expand Down Expand Up @@ -106,11 +108,24 @@ public void ApplyTo(SentryOptions options)
options.EnableSpotlight = EnableSpotlight ?? options.EnableSpotlight;
options.SpotlightUrl = SpotlightUrl ?? options.SpotlightUrl;

if (Experimental is { } experimental)
{
options.Experimental.EnableMetrics = experimental.EnableMetrics ?? options.Experimental.EnableMetrics;
}

#if ANDROID
Android.ApplyTo(options.Android);
Native.ApplyTo(options.Native);
#elif __IOS__
Native.ApplyTo(options.Native);
#endif
}

/// <summary>
/// Bindable Options for <see cref="SentryOptions.ExperimentalSentryOptions"/>.
/// </summary>
internal class ExperimentalSentryOptions
{
public bool? EnableMetrics { get; set; }
}
}
7 changes: 6 additions & 1 deletion src/Sentry/Extensibility/DisabledHub.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using Sentry.Internal;
using Sentry.Protocol.Envelopes;
using Sentry.Protocol.Metrics;

namespace Sentry.Extensibility;

Expand Down Expand Up @@ -267,4 +266,10 @@ public void Dispose()
/// Disabled Logger.
/// </summary>
public SentryStructuredLogger Logger => DisabledSentryStructuredLogger.Instance;

/// <summary>
/// Disabled Metrics.
/// </summary>
[Experimental("SENTRYTRACECONNECTEDMETRICS")]
public SentryTraceMetrics Metrics => DisabledSentryTraceMetrics.Instance;
}
7 changes: 6 additions & 1 deletion src/Sentry/Extensibility/HubAdapter.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using Sentry.Infrastructure;
using Sentry.Protocol.Envelopes;
using Sentry.Protocol.Metrics;

namespace Sentry.Extensibility;

Expand Down Expand Up @@ -37,6 +36,12 @@ private HubAdapter() { }
/// </summary>
public SentryStructuredLogger Logger { [DebuggerStepThrough] get => SentrySdk.Logger; }

/// <summary>
/// Forwards the call to <see cref="SentrySdk"/>.
/// </summary>
[Experimental("SENTRYTRACECONNECTEDMETRICS")]
public SentryTraceMetrics Metrics { [DebuggerStepThrough] get => SentrySdk.Experimental.Metrics; }

/// <summary>
/// Forwards the call to <see cref="SentrySdk"/>.
/// </summary>
Expand Down
13 changes: 13 additions & 0 deletions src/Sentry/IHub.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,19 @@ public interface IHub : ISentryClient, ISentryScopeManager
/// </remarks>
public SentryStructuredLogger Logger { get; }

/// <summary>
/// Generates and sends metrics to Sentry.
/// </summary>
/// <remarks>
/// Available options:
/// <list type="bullet">
/// <item><see cref="Sentry.SentryOptions.ExperimentalSentryOptions.EnableMetrics"/></item>
/// <item><see cref="Sentry.SentryOptions.ExperimentalSentryOptions.SetBeforeSendMetric{T}(System.Func{SentryMetric{T}, SentryMetric{T}})"/></item>
/// </list>
/// </remarks>
[Experimental("SENTRYTRACECONNECTEDMETRICS")]
public SentryTraceMetrics Metrics { get; }

/// <summary>
/// Starts a transaction.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
namespace Sentry.Internal;

/// <summary>
/// A wrapper over an <see cref="System.Array"/>, intended for reusable buffering.
/// A wrapper over an <see cref="System.Array"/>, intended for reusable buffering for <see cref="BatchProcessor{TItem}"/>.
/// </summary>
/// <remarks>
/// Must be attempted to flush via <see cref="TryEnterFlushScope"/> when either the <see cref="Capacity"/> is reached,
Expand All @@ -12,15 +12,15 @@ namespace Sentry.Internal;
/// allowing multiple threads for <see cref="Add"/> or exclusive access for <see cref="FlushScope.Flush"/>.
/// </remarks>
[DebuggerDisplay("Name = {Name}, Capacity = {Capacity}, Additions = {_additions}, AddCount = {AddCount}, IsDisposed = {_disposed}")]
internal sealed class StructuredLogBatchBuffer : IDisposable
internal sealed class BatchBuffer<TItem> : IDisposable
{
private readonly SentryLog[] _array;
private readonly TItem[] _array;
private int _additions;
private readonly ScopedCountdownLock _addLock;

private readonly Timer _timer;
private readonly TimeSpan _timeout;
private readonly Action<StructuredLogBatchBuffer> _timeoutExceededAction;
private readonly Action<BatchBuffer<TItem>> _timeoutExceededAction;

private volatile bool _disposed;

Expand All @@ -31,12 +31,12 @@ internal sealed class StructuredLogBatchBuffer : IDisposable
/// <param name="timeout">When the timeout exceeds after an item has been added and the <paramref name="capacity"/> not yet been exceeded, <paramref name="timeoutExceededAction"/> is invoked.</param>
/// <param name="timeoutExceededAction">The operation to execute when the <paramref name="timeout"/> exceeds if the buffer is neither empty nor full.</param>
/// <param name="name">Name of the new buffer.</param>
public StructuredLogBatchBuffer(int capacity, TimeSpan timeout, Action<StructuredLogBatchBuffer> timeoutExceededAction, string? name = null)
public BatchBuffer(int capacity, TimeSpan timeout, Action<BatchBuffer<TItem>> timeoutExceededAction, string? name = null)
{
ThrowIfLessThanTwo(capacity, nameof(capacity));
ThrowIfNegativeOrZero(timeout, nameof(timeout));

_array = new SentryLog[capacity];
_array = new TItem[capacity];
_additions = 0;
_addLock = new ScopedCountdownLock();

Expand Down Expand Up @@ -72,18 +72,18 @@ public StructuredLogBatchBuffer(int capacity, TimeSpan timeout, Action<Structure
/// Is thread-safe.
/// </summary>
/// <param name="item">Element attempted to be added.</param>
/// <returns>An <see cref="StructuredLogBatchBufferAddStatus"/> describing the result of the thread-safe operation.</returns>
internal StructuredLogBatchBufferAddStatus Add(SentryLog item)
/// <returns>An <see cref="BatchBufferAddStatus"/> describing the result of the thread-safe operation.</returns>
internal BatchBufferAddStatus Add(TItem item)
{
if (_disposed)
{
return StructuredLogBatchBufferAddStatus.IgnoredIsDisposed;
return BatchBufferAddStatus.IgnoredIsDisposed;
}

using var scope = _addLock.TryEnterCounterScope();
if (!scope.IsEntered)
{
return StructuredLogBatchBufferAddStatus.IgnoredIsFlushing;
return BatchBufferAddStatus.IgnoredIsFlushing;
}

var count = Interlocked.Increment(ref _additions);
Expand All @@ -92,24 +92,24 @@ internal StructuredLogBatchBufferAddStatus Add(SentryLog item)
{
EnableTimer();
_array[count - 1] = item;
return StructuredLogBatchBufferAddStatus.AddedFirst;
return BatchBufferAddStatus.AddedFirst;
}

if (count < _array.Length)
{
_array[count - 1] = item;
return StructuredLogBatchBufferAddStatus.Added;
return BatchBufferAddStatus.Added;
}

if (count == _array.Length)
{
DisableTimer();
_array[count - 1] = item;
return StructuredLogBatchBufferAddStatus.AddedLast;
return BatchBufferAddStatus.AddedLast;
}

Debug.Assert(count > _array.Length);
return StructuredLogBatchBufferAddStatus.IgnoredCapacityExceeded;
return BatchBufferAddStatus.IgnoredCapacityExceeded;
}

/// <summary>
Expand Down Expand Up @@ -157,7 +157,7 @@ internal void OnIntervalElapsed(object? state)
/// Returns a new Array consisting of the elements successfully added.
/// </summary>
/// <returns>An Array with Length of successful additions.</returns>
private SentryLog[] ToArrayAndClear()
private TItem[] ToArrayAndClear()
{
var additions = _additions;
var length = _array.Length;
Expand All @@ -173,7 +173,7 @@ private SentryLog[] ToArrayAndClear()
/// </summary>
/// <param name="length">The Length of the buffer a new Array is created from.</param>
/// <returns>An Array with Length of <paramref name="length"/>.</returns>
private SentryLog[] ToArrayAndClear(int length)
private TItem[] ToArrayAndClear(int length)
{
Debug.Assert(_addLock.IsSet);

Expand All @@ -182,14 +182,14 @@ private SentryLog[] ToArrayAndClear(int length)
return array;
}

private SentryLog[] ToArray(int length)
private TItem[] ToArray(int length)
{
if (length == 0)
{
return Array.Empty<SentryLog>();
return Array.Empty<TItem>();
}

var array = new SentryLog[length];
var array = new TItem[length];
Array.Copy(_array, array, length);
return array;
}
Expand Down Expand Up @@ -253,17 +253,17 @@ static void ThrowNegativeOrZero(TimeSpan value, string paramName)
/// A scope than ensures only a single <see cref="Flush"/> operation is in progress,
/// and blocks the calling thread until all <see cref="Add"/> operations have finished.
/// When <see cref="IsEntered"/> is <see langword="true"/>, no more <see cref="Add"/> can be started,
/// which will then return <see cref="StructuredLogBatchBufferAddStatus.IgnoredIsFlushing"/> immediately.
/// which will then return <see cref="BatchBufferAddStatus.IgnoredIsFlushing"/> immediately.
/// </summary>
/// <remarks>
/// Only <see cref="Flush"/> when scope <see cref="IsEntered"/>.
/// </remarks>
internal ref struct FlushScope : IDisposable
{
private StructuredLogBatchBuffer? _lockObj;
private BatchBuffer<TItem>? _lockObj;
private ScopedCountdownLock.LockScope _scope;

internal FlushScope(StructuredLogBatchBuffer lockObj, ScopedCountdownLock.LockScope scope)
internal FlushScope(BatchBuffer<TItem> lockObj, ScopedCountdownLock.LockScope scope)
{
Debug.Assert(scope.IsEntered);
_lockObj = lockObj;
Expand All @@ -272,7 +272,7 @@ internal FlushScope(StructuredLogBatchBuffer lockObj, ScopedCountdownLock.LockSc

internal bool IsEntered => _scope.IsEntered;

internal SentryLog[] Flush()
internal TItem[] Flush()
{
var lockObj = _lockObj;
if (lockObj is not null)
Expand Down Expand Up @@ -300,7 +300,7 @@ public void Dispose()
}
}

internal enum StructuredLogBatchBufferAddStatus : byte
internal enum BatchBufferAddStatus : byte
{
AddedFirst,
Added,
Expand Down
Loading
Loading