Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
d24d165
feat: add Batch Processor for Logs
Flash0ver Jun 26, 2025
aad0599
test: Batch Processor for Logs
Flash0ver Jun 26, 2025
76fcc1b
docs: Batch Processor for Logs
Flash0ver Jun 26, 2025
2ad33f6
test: fix unavailable API on TargetFramework=net48
Flash0ver Jun 27, 2025
38e1c04
test: run all Logs tests on full framework
Flash0ver Jun 27, 2025
f7a43b8
ref: remove usage of System.Threading.Lock
Flash0ver Jun 27, 2025
e6b0b74
ref: rename members for clarity
Flash0ver Jun 30, 2025
a84b78f
Merge branch 'feat/logs' into feat/logs-buffering
Flash0ver Jun 30, 2025
53c90ea
ref: delete Timer-Abstraction and change to System.Threading.Timer
Flash0ver Jul 1, 2025
6580632
ref: delete .ctor only called from tests
Flash0ver Jul 1, 2025
6e2ee9b
ref: switch Buffer-Processor to be lock-free but discarding
Flash0ver Jul 2, 2025
0774709
test: fix BatchBuffer and Tests
Flash0ver Jul 3, 2025
d9ae794
fix: flushing buffer on Timeout
Flash0ver Jul 8, 2025
7e1f5ea
feat: add Backpressure-ClientReport
Flash0ver Jul 10, 2025
365a2fb
ref: make BatchProcessor more resilient
Flash0ver Jul 11, 2025
c478391
Format code
getsentry-bot Jul 11, 2025
211beea
Merge branch 'feat/logs' into feat/logs-buffering
Flash0ver Jul 11, 2025
c699c2d
test: fix on .NET Framework
Flash0ver Jul 11, 2025
b21b537
fix: BatchBuffer flushed on Shutdown/Dispose
Flash0ver Jul 14, 2025
e8850db
ref: minimize locking
Flash0ver Jul 22, 2025
57f9ccc
Merge branch 'feat/logs' into feat/logs-buffering
Flash0ver Jul 22, 2025
c63bc53
ref: rename BatchProcessor to StructuredLogBatchProcessor
Flash0ver Jul 22, 2025
f28cc6d
ref: rename BatchBuffer to StructuredLogBatchBuffer
Flash0ver Jul 22, 2025
79ce02e
ref: remove internal options
Flash0ver Jul 23, 2025
0702796
test: ref
Flash0ver Jul 23, 2025
4e5f097
perf: update Benchmark result
Flash0ver Jul 23, 2025
1276725
ref: make SentryStructuredLogger. Flush abstract
Flash0ver Jul 24, 2025
3816fab
ref: guard an invariant of the Flush-Scope
Flash0ver Jul 24, 2025
28b6654
ref: remove unused values
Flash0ver Jul 24, 2025
1eef330
docs: improve comments
Flash0ver Jul 24, 2025
d72ca5c
perf: update Benchmark after signature change
Flash0ver Jul 25, 2025
49fefc1
ref: discard logs gracefully when Hub is (being) disposed
Flash0ver Jul 28, 2025
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
Prev Previous commit
Next Next commit
ref: remove internal options
  • Loading branch information
Flash0ver committed Jul 23, 2025
commit 79ce02e6988d8b13773d39dc7e2affb862f00318
20 changes: 2 additions & 18 deletions src/Sentry/Internal/DefaultSentryStructuredLogger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,31 +11,15 @@ internal sealed class DefaultSentryStructuredLogger : SentryStructuredLogger

private readonly StructuredLogBatchProcessor _batchProcessor;

internal DefaultSentryStructuredLogger(IHub hub, SentryOptions options, ISystemClock clock)
internal DefaultSentryStructuredLogger(IHub hub, SentryOptions options, ISystemClock clock, int batchCount, TimeSpan batchInterval)
{
Debug.Assert(options is { Experimental.EnableLogs: true });

_hub = hub;
_options = options;
_clock = clock;

_batchProcessor = new StructuredLogBatchProcessor(hub, ClampBatchCount(options.Experimental.InternalBatchSize), ClampBatchInterval(options.Experimental.InternalBatchTimeout), clock, _options.ClientReportRecorder, _options.DiagnosticLogger);
}

private static int ClampBatchCount(int batchCount)
{
return batchCount <= 0
? 1
: batchCount > 1_000_000
? 1_000_000
: batchCount;
}

private static TimeSpan ClampBatchInterval(TimeSpan batchInterval)
{
return batchInterval.TotalMilliseconds is <= 0 or > int.MaxValue
? TimeSpan.FromMilliseconds(int.MaxValue)
: batchInterval;
_batchProcessor = new StructuredLogBatchProcessor(hub, batchCount, batchInterval, clock, _options.ClientReportRecorder, _options.DiagnosticLogger);
}

/// <inheritdoc />
Expand Down
2 changes: 1 addition & 1 deletion src/Sentry/Internal/StructuredLogBatchProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ private void CaptureLogs(StructuredLogBatchBuffer buffer)
}
}

if (logs is not null)
if (logs is not null && logs.Length != 0)
{
_ = _hub.CaptureEnvelope(Envelope.FromLog(new StructuredLog(logs)));
}
Expand Down
17 changes: 0 additions & 17 deletions src/Sentry/SentryOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1897,22 +1897,5 @@ public void SetBeforeSendLog(Func<SentryLog, SentryLog?> beforeSendLog)
{
_beforeSendLog = beforeSendLog;
}

/// <summary>
/// This API will be removed in the future.
/// </summary>
/// <remarks>
/// Threshold of items in the buffer when sending all items, regardless of <see cref="InternalBatchTimeout"/>.
/// </remarks>
public int InternalBatchSize { get; set; } = 100;

/// <summary>
/// This API will be removed in the future.
/// </summary>
/// <remarks>
/// Time after which all items in the buffer are sent, regardless of <see cref="InternalBatchSize"/>.
/// <para>Must not exceed 30 seconds.</para>
/// </remarks>
public TimeSpan InternalBatchTimeout { get; set; } = TimeSpan.FromSeconds(5);
}
}
5 changes: 4 additions & 1 deletion src/Sentry/SentryStructuredLogger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,12 @@ namespace Sentry;
public abstract class SentryStructuredLogger : IDisposable
{
internal static SentryStructuredLogger Create(IHub hub, SentryOptions options, ISystemClock clock)
=> Create(hub, options, clock, 100, TimeSpan.FromSeconds(5));

internal static SentryStructuredLogger Create(IHub hub, SentryOptions options, ISystemClock clock, int batchCount, TimeSpan batchInterval)
{
return options.Experimental.EnableLogs
? new DefaultSentryStructuredLogger(hub, options, clock)
? new DefaultSentryStructuredLogger(hub, options, clock, batchCount, batchInterval)
: DisabledSentryStructuredLogger.Instance;
}

Expand Down
2 changes: 0 additions & 2 deletions test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt
Original file line number Diff line number Diff line change
Expand Up @@ -833,8 +833,6 @@ namespace Sentry
public sealed class SentryExperimentalOptions
{
public bool EnableLogs { get; set; }
public int InternalBatchSize { get; set; }
public System.TimeSpan InternalBatchTimeout { get; set; }
public void SetBeforeSendLog(System.Func<Sentry.SentryLog, Sentry.SentryLog?> beforeSendLog) { }
}
}
Expand Down
2 changes: 0 additions & 2 deletions test/Sentry.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt
Original file line number Diff line number Diff line change
Expand Up @@ -833,8 +833,6 @@ namespace Sentry
public sealed class SentryExperimentalOptions
{
public bool EnableLogs { get; set; }
public int InternalBatchSize { get; set; }
public System.TimeSpan InternalBatchTimeout { get; set; }
public void SetBeforeSendLog(System.Func<Sentry.SentryLog, Sentry.SentryLog?> beforeSendLog) { }
}
}
Expand Down
2 changes: 0 additions & 2 deletions test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt
Original file line number Diff line number Diff line change
Expand Up @@ -795,8 +795,6 @@ namespace Sentry
public sealed class SentryExperimentalOptions
{
public bool EnableLogs { get; set; }
public int InternalBatchSize { get; set; }
public System.TimeSpan InternalBatchTimeout { get; set; }
public void SetBeforeSendLog(System.Func<Sentry.SentryLog, Sentry.SentryLog?> beforeSendLog) { }
}
}
Expand Down
25 changes: 23 additions & 2 deletions test/Sentry.Tests/HubTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1429,6 +1429,7 @@ public void Logger_IsDisabled_DoesNotCaptureLog()

// Act
hub.Logger.LogWarning("Message");
hub.Logger.Flush();

// Assert
_fixture.Client.Received(0).CaptureEnvelope(
Expand All @@ -1439,16 +1440,16 @@ public void Logger_IsDisabled_DoesNotCaptureLog()
hub.Logger.Should().BeOfType<DisabledSentryStructuredLogger>();
}

[Fact(Skip = "Remove InternalBatchSize")]
[Fact]
public void Logger_IsEnabled_DoesCaptureLog()
{
// Arrange
_fixture.Options.Experimental.EnableLogs = true;
_fixture.Options.Experimental.InternalBatchSize = 1;
var hub = _fixture.GetSut();

// Act
hub.Logger.LogWarning("Message");
hub.Logger.Flush();

// Assert
_fixture.Client.Received(1).CaptureEnvelope(
Expand Down Expand Up @@ -1487,6 +1488,26 @@ public void Logger_DisableAfterCreate_HasNoEffect()
hub.Logger.Should().BeOfType<DefaultSentryStructuredLogger>();
}

[Fact]
public void Logger_Dispose_DoesCaptureLog()
{
// Arrange
_fixture.Options.Experimental.EnableLogs = true;
var hub = _fixture.GetSut();
hub.Logger.LogWarning("Message");

// Act
hub.Dispose();

// Assert
_fixture.Client.Received(1).CaptureEnvelope(
Arg.Is<Envelope>(envelope =>
envelope.Items.Single(item => item.Header["type"].Equals("log")).Payload.GetType().IsAssignableFrom(typeof(JsonSerializable))
)
);
hub.Logger.Should().BeOfType<DefaultSentryStructuredLogger>();
}

[Fact]
public void Dispose_IsEnabled_SetToFalse()
{
Expand Down
20 changes: 12 additions & 8 deletions test/Sentry.Tests/SentryStructuredLoggerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ public Fixture()
DiagnosticLogger = DiagnosticLogger,
};
Clock = new MockClock(new DateTimeOffset(2025, 04, 22, 14, 51, 00, TimeSpan.Zero));
BatchSize = 2;
BatchTimeout = Timeout.InfiniteTimeSpan;
TraceId = SentryId.Create();
ParentSpanId = SpanId.Create();

Expand All @@ -30,6 +32,8 @@ public Fixture()
public IHub Hub { get; }
public SentryOptions Options { get; }
public ISystemClock Clock { get; }
public int BatchSize { get; set; }
public TimeSpan BatchTimeout { get; set; }
public SentryId TraceId { get; private set; }
public SpanId? ParentSpanId { get; private set; }

Expand All @@ -40,7 +44,7 @@ public void WithoutTraceHeader()
ParentSpanId = SpanId.Empty;
}

public SentryStructuredLogger GetSut() => SentryStructuredLogger.Create(Hub, Options, Clock);
public SentryStructuredLogger GetSut() => SentryStructuredLogger.Create(Hub, Options, Clock, BatchSize, BatchTimeout);
}

private readonly Fixture _fixture;
Expand Down Expand Up @@ -74,7 +78,7 @@ public void Create_Disabled_CachedDisabledInstance()
instance.Should().BeSameAs(other);
}

[Theory(Skip = "Remove InternalBatchSize")]
[Theory]
[InlineData(SentryLogLevel.Trace)]
[InlineData(SentryLogLevel.Debug)]
[InlineData(SentryLogLevel.Info)]
Expand All @@ -84,13 +88,13 @@ public void Create_Disabled_CachedDisabledInstance()
public void Log_Enabled_CapturesEnvelope(SentryLogLevel level)
{
_fixture.Options.Experimental.EnableLogs = true;
_fixture.Options.Experimental.InternalBatchSize = 1;
var logger = _fixture.GetSut();

Envelope envelope = null!;
_fixture.Hub.CaptureEnvelope(Arg.Do<Envelope>(arg => envelope = arg));

logger.Log(level, "Template string with arguments: {0}, {1}, {2}, {3}", ["string", true, 1, 2.2], ConfigureLog);
logger.Flush();

_fixture.Hub.Received(1).CaptureEnvelope(Arg.Any<Envelope>());
envelope.AssertEnvelope(_fixture, level);
Expand All @@ -113,31 +117,30 @@ public void Log_Disabled_DoesNotCaptureEnvelope(SentryLogLevel level)
_fixture.Hub.Received(0).CaptureEnvelope(Arg.Any<Envelope>());
}

[Fact(Skip = "Remove InternalBatchSize")]
[Fact]
public void Log_WithoutTraceHeader_CapturesEnvelope()
{
_fixture.WithoutTraceHeader();
_fixture.Options.Experimental.EnableLogs = true;
_fixture.Options.Experimental.InternalBatchSize = 1;
var logger = _fixture.GetSut();

Envelope envelope = null!;
_fixture.Hub.CaptureEnvelope(Arg.Do<Envelope>(arg => envelope = arg));

logger.LogTrace("Template string with arguments: {0}, {1}, {2}, {3}", ["string", true, 1, 2.2], ConfigureLog);
logger.Flush();

_fixture.Hub.Received(1).CaptureEnvelope(Arg.Any<Envelope>());
envelope.AssertEnvelope(_fixture, SentryLogLevel.Trace);
}

[Fact(Skip = "Remove InternalBatchSize")]
[Fact]
public void Log_WithBeforeSendLog_InvokesCallback()
{
var invocations = 0;
SentryLog configuredLog = null!;

_fixture.Options.Experimental.EnableLogs = true;
_fixture.Options.Experimental.InternalBatchSize = 1;
_fixture.Options.Experimental.SetBeforeSendLog((SentryLog log) =>
{
invocations++;
Expand All @@ -147,6 +150,7 @@ public void Log_WithBeforeSendLog_InvokesCallback()
var logger = _fixture.GetSut();

logger.LogTrace("Template string with arguments: {0}, {1}, {2}, {3}", ["string", true, 1, 2.2], ConfigureLog);
logger.Flush();

_fixture.Hub.Received(1).CaptureEnvelope(Arg.Any<Envelope>());
invocations.Should().Be(1);
Expand Down Expand Up @@ -221,7 +225,7 @@ public void Log_InvalidBeforeSendLog_DoesNotCaptureEnvelope()
entry.Args.Should().BeEmpty();
}

[Fact(Skip = "May no longer be required after feedback.")]
[Fact]
public void Dispose_Log_Throws()
{
_fixture.Options.Experimental.EnableLogs = true;
Expand Down
Loading