From 3475a7b988a84aa00a9b39f2b14683e5f8addb7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EB=82=98=EB=9E=98?= Date: Fri, 12 Sep 2025 17:47:24 +0900 Subject: [PATCH] =?UTF-8?q?[=EB=B3=80=EA=B2=BD]=20AptabaseClient.cs=20-=20?= =?UTF-8?q?=EC=A2=85=EB=A3=8C=20=EC=A7=81=EC=A0=84=EC=97=90=20=EB=B3=B4?= =?UTF-8?q?=EB=82=B4=EB=8A=94=20Event=20=EB=88=84=EB=9D=BD=20=EB=B0=A9?= =?UTF-8?q?=EC=A7=80=ED=95=98=EA=B8=B0=20=EC=9C=84=ED=95=9C=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=88=98=EC=A0=95=20=20=20:=20=ED=81=90=EC=97=90?= =?UTF-8?q?=20=EC=9D=B4=EB=B2=A4=ED=8A=B8=EB=A5=BC=20=EC=B6=94=EA=B0=80?= =?UTF-8?q?=ED=95=98=EB=A9=B4=20+1,=20=ED=81=90=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EC=9D=BD=EC=96=B4=EC=99=80=EC=84=9C=20Event=EB=A5=BC=20Send?= =?UTF-8?q?=ED=95=98=EB=A9=B4=20-1=ED=95=98=EB=8A=94=20=ED=95=84=EB=93=9C?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80=20=20=20:=20=ED=95=84=EB=93=9C=EA=B0=80?= =?UTF-8?q?=200=EC=9D=B4=EB=90=98=EA=B8=B0=EB=A5=BC=20=EA=B8=B0=EB=8B=A4?= =?UTF-8?q?=EB=A6=AC=EB=8A=94=20=ED=95=A8=EC=88=98=20=EC=B6=94=EA=B0=80(?= =?UTF-8?q?=EB=8C=80=EA=B8=B0=20=ED=95=9C=EA=B3=84=20=EC=8B=9C=EA=B0=84=20?= =?UTF-8?q?=EA=B8=B0=EB=B3=B8=EA=B0=92=201500ms)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [변경] AptabaseClientBase.cs - EventData에 ipAddredd 할당하는 코드 제거 : 매번 API 호출하면서 속도 지연의 원인이 됨 - aptabase-debug.log 파일 생성하는 코드 제거 : 파일 관리가 안되고 있으므로 제거 --- src/AptabaseClient.cs | 58 +++++++++++++++++++++++++++++++++------ src/AptabaseClientBase.cs | 8 +++--- 2 files changed, 53 insertions(+), 13 deletions(-) diff --git a/src/AptabaseClient.cs b/src/AptabaseClient.cs index 48eef33..a50513d 100644 --- a/src/AptabaseClient.cs +++ b/src/AptabaseClient.cs @@ -1,6 +1,7 @@ using Microsoft.Extensions.Logging; using System.Threading.Channels; using System; +using System.Diagnostics; namespace Aptabase.WPF; @@ -10,6 +11,10 @@ public class AptabaseClient : IAptabaseClient private Task? _processingTask; private AptabaseClientBase? _client; private CancellationTokenSource? _cts; + private ILogger? _logger; + private int _queueItemCount = 0; + + public bool IsRunning => _processingTask != null && !_processingTask.IsCompleted; public AptabaseClient(string appKey, AptabaseOptions? options, ILogger? logger) { @@ -20,30 +25,44 @@ public class AptabaseClient : IAptabaseClient { _client = new AptabaseClientBase(appKey, options, logger); _channel = Channel.CreateUnbounded(); - _processingTask = Task.Run(ProcessEventsAsync); _cts = new CancellationTokenSource(); + _processingTask = Task.Run(() => ProcessEventsAsync(_cts.Token)); + _logger = logger; } - public Task TrackEvent(string eventName, Dictionary? props = null) + public async Task TrackEvent(string eventName, Dictionary? props = null) { - _channel?.Writer.TryWrite(new EventData(eventName, props)); - - return Task.CompletedTask; + try + { + if (_channel != null) + { + await _channel.Writer.WriteAsync(new EventData(eventName, props)); + Interlocked.Increment(ref _queueItemCount); + Debug.WriteLine($"Queued event {eventName} to Aptabase {_queueItemCount}"); + } + } + catch (Exception ex) + { + _logger?.LogError(ex, "Error queueing event {EventName} to Aptabase", eventName); + Console.WriteLine(ex.ToString()); + } } - private async Task ProcessEventsAsync() + private async Task ProcessEventsAsync(CancellationToken cancellationToken) { try { if (_channel is null || _cts is null || _client is null) { + Interlocked.CompareExchange(ref _queueItemCount, 0, 0); return; } - while (await _channel.Reader.WaitToReadAsync()) + while (await _channel.Reader.WaitToReadAsync(cancellationToken)) { if (_cts.IsCancellationRequested) { + Interlocked.CompareExchange(ref _queueItemCount, 0, 0); break; } @@ -51,23 +70,30 @@ public class AptabaseClient : IAptabaseClient { if (_cts.IsCancellationRequested) { + Interlocked.CompareExchange(ref _queueItemCount, 0, 0); break; } try { await _client.TrackEvent(eventData); + Interlocked.Decrement(ref _queueItemCount); + Debug.WriteLine($"Sent event {eventData.EventName} to Aptabase {eventData.Timestamp} / {_queueItemCount}"); } catch (Exception ex) { + _logger?.LogError(ex, "Error sending event {EventName} to Aptabase", eventData.EventName); Console.WriteLine(ex.ToString()); + Debug.WriteLine($"Error sending event {eventData.EventName} to Aptabase: {ex.Message}"); } } } } - catch (ChannelClosedException) + catch (ChannelClosedException ex) { - // ignore + Interlocked.CompareExchange(ref _queueItemCount, 0, 0); + _logger?.LogError(ex, "Channel closed unexpectedly"); + Debug.WriteLine($"Channel closed unexpectedly: {ex.Message}"); } } @@ -94,4 +120,18 @@ public class AptabaseClient : IAptabaseClient GC.SuppressFinalize(this); } + + public async Task StopAsync(int timeoutMs = 1500) + { + _channel?.Writer.Complete(); + + var sw = Stopwatch.StartNew(); + while (Volatile.Read(ref _queueItemCount) > 0 && sw.ElapsedMilliseconds < timeoutMs) + { + await Task.Delay(100); // 큐가 비워질 때까지 대기 + } + + if (_processingTask is { IsCompleted: false }) + await Task.WhenAny(_processingTask, Task.Delay(timeoutMs)); // 드레인 대기 (타임아웃 허용) + } } diff --git a/src/AptabaseClientBase.cs b/src/AptabaseClientBase.cs index ce4380b..0659682 100644 --- a/src/AptabaseClientBase.cs +++ b/src/AptabaseClientBase.cs @@ -73,13 +73,13 @@ internal class AptabaseClientBase : IAsyncDisposable if (eventData.Props is null) eventData.Props = new Dictionary(); - eventData.Props["ipAddress"] = await GetPublicIpAddress(); + //eventData.Props["ipAddress"] = await GetPublicIpAddress(); 느려서 제외. 필요하면 다른 방법 사용 요망 var body = JsonContent.Create(eventData); var response = await _http.PostAsync("/api/v0/event", body); - var logPath = "aptabase-debug.log"; + //var logPath = "aptabase-debug.log"; //파일 관리 안되므로 제거. 필요하면 다른 방법 사용 요망 if (!response.IsSuccessStatusCode) { @@ -94,11 +94,11 @@ internal class AptabaseClientBase : IAsyncDisposable var responseBody = await response.Content.ReadAsStringAsync(); _logger?.LogError("Failed to perform TrackEvent due to {StatusCode} and response body {Body}", response.StatusCode, responseBody); - File.AppendAllText(logPath, $"Failed to perform TrackEvent due to {response.StatusCode} and response body {responseBody}\n"); + //File.AppendAllText(logPath, $"{eventData.EventName} Failed to perform TrackEvent due to {response.StatusCode} and response body {responseBody}\n"); } else { var responseBody = await response.Content.ReadAsStringAsync(); - File.AppendAllText(logPath, $"TrackEvent successful with response body {responseBody}\n"); + //File.AppendAllText(logPath, $"{eventData.EventName} TrackEvent successful with response body {responseBody}\n"); } }