-
-
Notifications
You must be signed in to change notification settings - Fork 226
feat(logs): add Buffering and Batching #4310
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 8 commits
Commits
Show all changes
32 commits
Select commit
Hold shift + click to select a range
d24d165
feat: add Batch Processor for Logs
Flash0ver aad0599
test: Batch Processor for Logs
Flash0ver 76fcc1b
docs: Batch Processor for Logs
Flash0ver 2ad33f6
test: fix unavailable API on TargetFramework=net48
Flash0ver 38e1c04
test: run all Logs tests on full framework
Flash0ver f7a43b8
ref: remove usage of System.Threading.Lock
Flash0ver e6b0b74
ref: rename members for clarity
Flash0ver a84b78f
Merge branch 'feat/logs' into feat/logs-buffering
Flash0ver 53c90ea
ref: delete Timer-Abstraction and change to System.Threading.Timer
Flash0ver 6580632
ref: delete .ctor only called from tests
Flash0ver 6e2ee9b
ref: switch Buffer-Processor to be lock-free but discarding
Flash0ver 0774709
test: fix BatchBuffer and Tests
Flash0ver d9ae794
fix: flushing buffer on Timeout
Flash0ver 7e1f5ea
feat: add Backpressure-ClientReport
Flash0ver 365a2fb
ref: make BatchProcessor more resilient
Flash0ver c478391
Format code
getsentry-bot 211beea
Merge branch 'feat/logs' into feat/logs-buffering
Flash0ver c699c2d
test: fix on .NET Framework
Flash0ver b21b537
fix: BatchBuffer flushed on Shutdown/Dispose
Flash0ver e8850db
ref: minimize locking
Flash0ver 57f9ccc
Merge branch 'feat/logs' into feat/logs-buffering
Flash0ver c63bc53
ref: rename BatchProcessor to StructuredLogBatchProcessor
Flash0ver f28cc6d
ref: rename BatchBuffer to StructuredLogBatchBuffer
Flash0ver 79ce02e
ref: remove internal options
Flash0ver 0702796
test: ref
Flash0ver 4e5f097
perf: update Benchmark result
Flash0ver 1276725
ref: make SentryStructuredLogger. Flush abstract
Flash0ver 3816fab
ref: guard an invariant of the Flush-Scope
Flash0ver 28b6654
ref: remove unused values
Flash0ver 1eef330
docs: improve comments
Flash0ver d72ca5c
perf: update Benchmark after signature change
Flash0ver 49fefc1
ref: discard logs gracefully when Hub is (being) disposed
Flash0ver File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,80 @@ | ||
| namespace Sentry.Internal; | ||
|
|
||
| /// <summary> | ||
| /// A slim wrapper over an <see cref="System.Array"/>, | ||
| /// intended for buffering. | ||
| /// </summary> | ||
| internal sealed class BatchBuffer<T> | ||
| { | ||
| private readonly T[] _array; | ||
| private int _count; | ||
|
|
||
| public BatchBuffer(int capacity) | ||
| { | ||
| ThrowIfNegativeOrZero(capacity, nameof(capacity)); | ||
|
|
||
| _array = new T[capacity]; | ||
| _count = 0; | ||
| } | ||
|
|
||
| internal int Count => _count; | ||
| internal int Capacity => _array.Length; | ||
| internal bool IsEmpty => _count == 0 && _array.Length != 0; | ||
| internal bool IsFull => _count == _array.Length; | ||
|
|
||
| internal bool TryAdd(T item) | ||
| { | ||
| if (_count < _array.Length) | ||
| { | ||
| _array[_count] = item; | ||
| _count++; | ||
| return true; | ||
| } | ||
|
|
||
| return false; | ||
| } | ||
|
|
||
| internal T[] ToArray() | ||
| { | ||
| if (_count == 0) | ||
| { | ||
| return Array.Empty<T>(); | ||
| } | ||
|
|
||
| var array = new T[_count]; | ||
| Array.Copy(_array, array, _count); | ||
| return array; | ||
| } | ||
|
|
||
| internal void Clear() | ||
| { | ||
| if (_count == 0) | ||
| { | ||
| return; | ||
| } | ||
|
|
||
| var count = _count; | ||
| _count = 0; | ||
| Array.Clear(_array, 0, count); | ||
Flash0ver marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| internal T[] ToArrayAndClear() | ||
| { | ||
| var array = ToArray(); | ||
| Clear(); | ||
| return array; | ||
| } | ||
|
|
||
| private static void ThrowIfNegativeOrZero(int capacity, string paramName) | ||
| { | ||
| if (capacity <= 0) | ||
| { | ||
| ThrowNegativeOrZero(capacity, paramName); | ||
| } | ||
| } | ||
|
|
||
| private static void ThrowNegativeOrZero(int capacity, string paramName) | ||
| { | ||
| throw new ArgumentOutOfRangeException(paramName, capacity, "Argument must neither be negative nor zero."); | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,93 @@ | ||
| using System.Timers; | ||
| using Sentry.Protocol; | ||
| using Sentry.Protocol.Envelopes; | ||
|
|
||
| namespace Sentry.Internal; | ||
|
|
||
| /// <summary> | ||
| /// The Sentry Batch Processor. | ||
| /// This implementation is not complete yet. | ||
| /// Also, the specification is still work in progress. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// Sentry Specification: <see href="https://develop.sentry.dev/sdk/telemetry/spans/batch-processor/"/>. | ||
| /// OpenTelemetry spec: <see href="https://github.com/open-telemetry/opentelemetry-collector/blob/main/processor/batchprocessor/README.md"/>. | ||
| /// </remarks> | ||
| internal sealed class BatchProcessor : IDisposable | ||
jamescrosswell marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| { | ||
| private readonly IHub _hub; | ||
| private readonly BatchProcessorTimer _timer; | ||
| private readonly BatchBuffer<SentryLog> _logs; | ||
jamescrosswell marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| private readonly object _lock; | ||
|
|
||
| private DateTime _lastFlush = DateTime.MinValue; | ||
|
|
||
| public BatchProcessor(IHub hub, int batchCount, TimeSpan batchInterval) | ||
| : this(hub, batchCount, new TimersBatchProcessorTimer(batchInterval)) | ||
| { | ||
| } | ||
|
|
||
| public BatchProcessor(IHub hub, int batchCount, BatchProcessorTimer timer) | ||
| { | ||
| _hub = hub; | ||
|
|
||
| _timer = timer; | ||
| _timer.Elapsed += OnIntervalElapsed; | ||
Flash0ver marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| _logs = new BatchBuffer<SentryLog>(batchCount); | ||
| _lock = new object(); | ||
| } | ||
|
|
||
| internal void Enqueue(SentryLog log) | ||
| { | ||
| lock (_lock) | ||
| { | ||
| EnqueueCore(log); | ||
| } | ||
| } | ||
|
|
||
| private void EnqueueCore(SentryLog log) | ||
Flash0ver marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| { | ||
| var isFirstLog = _logs.IsEmpty; | ||
| var added = _logs.TryAdd(log); | ||
| Debug.Assert(added, $"Since we currently have no lock-free scenario, it's unexpected to exceed the {nameof(BatchBuffer<SentryLog>)}'s capacity."); | ||
Flash0ver marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| if (isFirstLog && !_logs.IsFull) | ||
| { | ||
| _timer.Enabled = true; | ||
| } | ||
| else if (_logs.IsFull) | ||
| { | ||
| _timer.Enabled = false; | ||
| Flush(); | ||
Flash0ver marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
| } | ||
|
|
||
| private void Flush() | ||
| { | ||
| _lastFlush = DateTime.UtcNow; | ||
|
|
||
| var logs = _logs.ToArrayAndClear(); | ||
| _ = _hub.CaptureEnvelope(Envelope.FromLog(new StructuredLog(logs))); | ||
| } | ||
|
|
||
| private void OnIntervalElapsed(object? sender, ElapsedEventArgs e) | ||
| { | ||
| _timer.Enabled = false; | ||
|
|
||
| lock (_lock) | ||
| { | ||
| if (!_logs.IsEmpty && e.SignalTime > _lastFlush) | ||
| { | ||
| Flush(); | ||
| } | ||
Flash0ver marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
| } | ||
|
|
||
| public void Dispose() | ||
| { | ||
| _timer.Enabled = false; | ||
| _timer.Elapsed -= OnIntervalElapsed; | ||
| _timer.Dispose(); | ||
| } | ||
| } | ||
Flash0ver marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| using System.Timers; | ||
|
|
||
| namespace Sentry.Internal; | ||
|
|
||
| internal abstract class BatchProcessorTimer : IDisposable | ||
Flash0ver marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| { | ||
| protected BatchProcessorTimer() | ||
| { | ||
| } | ||
|
|
||
| public abstract bool Enabled { get; set; } | ||
|
|
||
| public abstract event EventHandler<ElapsedEventArgs> Elapsed; | ||
|
|
||
| public void Dispose() | ||
Flash0ver marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| { | ||
| Dispose(true); | ||
| GC.SuppressFinalize(this); | ||
| } | ||
|
|
||
| protected virtual void Dispose(bool disposing) | ||
| { | ||
| } | ||
Flash0ver marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| internal sealed class TimersBatchProcessorTimer : BatchProcessorTimer | ||
| { | ||
| private readonly System.Timers.Timer _timer; | ||
|
|
||
| public TimersBatchProcessorTimer(TimeSpan interval) | ||
| { | ||
| _timer = new System.Timers.Timer(interval.TotalMilliseconds) | ||
| { | ||
| AutoReset = false, | ||
| Enabled = false, | ||
| }; | ||
| _timer.Elapsed += OnElapsed; | ||
| } | ||
|
|
||
| public override bool Enabled | ||
Flash0ver marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| { | ||
| get => _timer.Enabled; | ||
| set => _timer.Enabled = value; | ||
| } | ||
|
|
||
| public override event EventHandler<ElapsedEventArgs>? Elapsed; | ||
Flash0ver marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| private void OnElapsed(object? sender, ElapsedEventArgs e) | ||
| { | ||
| Elapsed?.Invoke(sender, e); | ||
Flash0ver marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| protected override void Dispose(bool disposing) | ||
| { | ||
| if (disposing) | ||
| { | ||
| _timer.Elapsed -= OnElapsed; | ||
| _timer.Dispose(); | ||
| } | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
jamescrosswell marked this conversation as resolved.
Show resolved
Hide resolved
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| using Sentry.Extensibility; | ||
|
|
||
| namespace Sentry.Protocol; | ||
|
|
||
| /// <summary> | ||
| /// Represents the Sentry Log protocol. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// Sentry Docs: <see href="https://docs.sentry.io/product/explore/logs/"/>. | ||
| /// Sentry Developer Documentation: <see href="https://develop.sentry.dev/sdk/telemetry/logs/"/>. | ||
| /// </remarks> | ||
| internal sealed class StructuredLog : ISentryJsonSerializable | ||
| { | ||
| private readonly SentryLog[] _items; | ||
|
|
||
| public StructuredLog(SentryLog log) | ||
Flash0ver marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| { | ||
| _items = [log]; | ||
Flash0ver marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| public StructuredLog(SentryLog[] logs) | ||
| { | ||
| _items = logs; | ||
| } | ||
|
|
||
| public int Length => _items.Length; | ||
| public ReadOnlySpan<SentryLog> Items => _items; | ||
|
|
||
| public void WriteTo(Utf8JsonWriter writer, IDiagnosticLogger? logger) | ||
| { | ||
| writer.WriteStartObject(); | ||
| writer.WriteStartArray("items"); | ||
|
|
||
| foreach (var log in _items) | ||
| { | ||
| log.WriteTo(writer, logger); | ||
| } | ||
|
|
||
| writer.WriteEndArray(); | ||
| writer.WriteEndObject(); | ||
| } | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.