Dreamine.Logging 1.0.2
Dreamine.Logging 프로젝트의 API와 구성 요소를 제공합니다.
로딩중...
검색중...
일치하는것 없음
AsyncQueueSink.cs
이 파일의 문서화 페이지로 가기
1using System;
2using System.Collections.Generic;
3using System.Threading;
4using System.Threading.Channels;
5using System.Threading.Tasks;
8
10{
27 public sealed class AsyncQueueSink : IDreamineLogSink, IAsyncDisposable, IDisposable
28 {
37 private readonly IDreamineLogSink _inner;
46 private readonly Channel<DreamineLogEntry> _channel;
55 private readonly CancellationTokenSource _cts = new();
64 private readonly Task _workerTask;
73 private readonly int _drainBatchSize;
82 private long _droppedCount;
91 private int _disposed;
92
101 public long DroppedCount => Interlocked.Read(ref _droppedCount);
102
152 IDreamineLogSink inner,
153 int capacity = 8192,
154 int drainBatchSize = 256)
155 {
156 _inner = inner ?? throw new ArgumentNullException(nameof(inner));
157
158 if (capacity <= 0)
159 {
160 throw new ArgumentOutOfRangeException(nameof(capacity));
161 }
162
163 if (drainBatchSize <= 0)
164 {
165 throw new ArgumentOutOfRangeException(nameof(drainBatchSize));
166 }
167
168 _drainBatchSize = drainBatchSize;
169
170 var options = new BoundedChannelOptions(capacity)
171 {
172 FullMode = BoundedChannelFullMode.DropOldest,
173 SingleReader = true,
174 SingleWriter = false,
175 AllowSynchronousContinuations = false
176 };
177
178 _channel = Channel.CreateBounded<DreamineLogEntry>(options);
179
180 _workerTask = Task.Factory.StartNew(
181 RunAsync,
182 CancellationToken.None,
183 TaskCreationOptions.LongRunning,
184 TaskScheduler.Default).Unwrap();
185 }
186
219 public void Write(DreamineLogEntry entry)
220 {
221 ArgumentNullException.ThrowIfNull(entry);
222
223 if (Volatile.Read(ref _disposed) != 0)
224 {
225 return;
226 }
227
228 // BoundedChannelFullMode.DropOldest guarantees TryWrite always succeeds
229 // unless the channel is completed.
230 if (!_channel.Writer.TryWrite(entry))
231 {
232 Interlocked.Increment(ref _droppedCount);
233 }
234 }
235
252 private async Task RunAsync()
253 {
254 var reader = _channel.Reader;
255 var batch = new List<DreamineLogEntry>(_drainBatchSize);
256
257 try
258 {
259 while (await reader.WaitToReadAsync(_cts.Token).ConfigureAwait(false))
260 {
261 batch.Clear();
262
263 while (batch.Count < _drainBatchSize && reader.TryRead(out var entry))
264 {
265 batch.Add(entry);
266 }
267
268 foreach (var item in batch)
269 {
270 try
271 {
272 _inner.Write(item);
273 }
274 catch
275 {
276 // Logging failure must not terminate the application.
277 }
278 }
279 }
280 }
281 catch (OperationCanceledException)
282 {
283 // Expected on shutdown.
284 }
285 catch
286 {
287 // Defensive: never let the worker crash silently kill the channel.
288 }
289 }
290
315 public async Task ShutdownAsync(TimeSpan timeout)
316 {
317 if (Interlocked.Exchange(ref _disposed, 1) != 0)
318 {
319 return;
320 }
321
322 _channel.Writer.TryComplete();
323
324 using var timeoutCts = new CancellationTokenSource(timeout);
325 try
326 {
327 await _workerTask.WaitAsync(timeoutCts.Token).ConfigureAwait(false);
328 }
329 catch (OperationCanceledException)
330 {
331 _cts.Cancel();
332 try
333 {
334 await _workerTask.ConfigureAwait(false);
335 }
336 catch
337 {
338 // Suppress shutdown errors.
339 }
340 }
341
342 // Best-effort dispose on the inner sink so file handles are closed.
343 if (_inner is IAsyncDisposable asyncDisposable)
344 {
345 try
346 {
347 await asyncDisposable.DisposeAsync().ConfigureAwait(false);
348 }
349 catch
350 {
351 // Suppress on shutdown.
352 }
353 }
354 else if (_inner is IDisposable disposable)
355 {
356 try
357 {
358 disposable.Dispose();
359 }
360 catch
361 {
362 // Suppress on shutdown.
363 }
364 }
365
366 _cts.Dispose();
367 }
368
385 public async ValueTask DisposeAsync()
386 {
387 await ShutdownAsync(TimeSpan.FromSeconds(2)).ConfigureAwait(false);
388 }
389
398 public void Dispose()
399 {
400 try
401 {
402 ShutdownAsync(TimeSpan.FromSeconds(2)).GetAwaiter().GetResult();
403 }
404 catch
405 {
406 // Suppress on dispose.
407 }
408 }
409 }
410}
readonly Channel< DreamineLogEntry > _channel
readonly CancellationTokenSource _cts
AsyncQueueSink(IDreamineLogSink inner, int capacity=8192, int drainBatchSize=256)
readonly IDreamineLogSink _inner
async Task ShutdownAsync(TimeSpan timeout)
void Write(DreamineLogEntry entry)