[변경] AptabaseClient.cs

- 종료 직전에 보내는 Event 누락 방지하기 위한 코드 수정
  : 큐에 이벤트를 추가하면 +1, 큐에서 읽어와서 Event를 Send하면 -1하는 필드 추가
  : 필드가 0이되기를 기다리는 함수 추가(대기 한계 시간 기본값 1500ms)

[변경] AptabaseClientBase.cs
- EventData에 ipAddredd 할당하는 코드 제거
 : 매번 API 호출하면서 속도 지연의 원인이 됨
- aptabase-debug.log 파일 생성하는 코드 제거
 : 파일 관리가 안되고 있으므로 제거
This commit is contained in:
정나래
2025-09-12 17:47:24 +09:00
parent be3d38d066
commit 3475a7b988
2 changed files with 53 additions and 13 deletions

View File

@@ -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<EventData>();
_processingTask = Task.Run(ProcessEventsAsync);
_cts = new CancellationTokenSource();
_processingTask = Task.Run(() => ProcessEventsAsync(_cts.Token));
_logger = logger;
}
public Task TrackEvent(string eventName, Dictionary<string, object>? props = null)
public async Task TrackEvent(string eventName, Dictionary<string, object>? 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)); // 드레인 대기 (타임아웃 허용)
}
}

View File

@@ -73,13 +73,13 @@ internal class AptabaseClientBase : IAsyncDisposable
if (eventData.Props is null)
eventData.Props = new Dictionary<string, object>();
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");
}
}