[변경] AptabaseClient.cs
- 종료 직전에 보내는 Event 누락 방지하기 위한 코드 수정 : 큐에 이벤트를 추가하면 +1, 큐에서 읽어와서 Event를 Send하면 -1하는 필드 추가 : 필드가 0이되기를 기다리는 함수 추가(대기 한계 시간 기본값 1500ms) [변경] AptabaseClientBase.cs - EventData에 ipAddredd 할당하는 코드 제거 : 매번 API 호출하면서 속도 지연의 원인이 됨 - aptabase-debug.log 파일 생성하는 코드 제거 : 파일 관리가 안되고 있으므로 제거
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using System.Threading.Channels;
|
using System.Threading.Channels;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
namespace Aptabase.WPF;
|
namespace Aptabase.WPF;
|
||||||
|
|
||||||
@@ -10,6 +11,10 @@ public class AptabaseClient : IAptabaseClient
|
|||||||
private Task? _processingTask;
|
private Task? _processingTask;
|
||||||
private AptabaseClientBase? _client;
|
private AptabaseClientBase? _client;
|
||||||
private CancellationTokenSource? _cts;
|
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)
|
public AptabaseClient(string appKey, AptabaseOptions? options, ILogger? logger)
|
||||||
{
|
{
|
||||||
@@ -20,30 +25,44 @@ public class AptabaseClient : IAptabaseClient
|
|||||||
{
|
{
|
||||||
_client = new AptabaseClientBase(appKey, options, logger);
|
_client = new AptabaseClientBase(appKey, options, logger);
|
||||||
_channel = Channel.CreateUnbounded<EventData>();
|
_channel = Channel.CreateUnbounded<EventData>();
|
||||||
_processingTask = Task.Run(ProcessEventsAsync);
|
|
||||||
_cts = new CancellationTokenSource();
|
_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));
|
try
|
||||||
|
{
|
||||||
return Task.CompletedTask;
|
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
|
try
|
||||||
{
|
{
|
||||||
if (_channel is null || _cts is null || _client is null)
|
if (_channel is null || _cts is null || _client is null)
|
||||||
{
|
{
|
||||||
|
Interlocked.CompareExchange(ref _queueItemCount, 0, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (await _channel.Reader.WaitToReadAsync())
|
while (await _channel.Reader.WaitToReadAsync(cancellationToken))
|
||||||
{
|
{
|
||||||
if (_cts.IsCancellationRequested)
|
if (_cts.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
|
Interlocked.CompareExchange(ref _queueItemCount, 0, 0);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,23 +70,30 @@ public class AptabaseClient : IAptabaseClient
|
|||||||
{
|
{
|
||||||
if (_cts.IsCancellationRequested)
|
if (_cts.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
|
Interlocked.CompareExchange(ref _queueItemCount, 0, 0);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await _client.TrackEvent(eventData);
|
await _client.TrackEvent(eventData);
|
||||||
|
Interlocked.Decrement(ref _queueItemCount);
|
||||||
|
Debug.WriteLine($"Sent event {eventData.EventName} to Aptabase {eventData.Timestamp} / {_queueItemCount}");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
_logger?.LogError(ex, "Error sending event {EventName} to Aptabase", eventData.EventName);
|
||||||
Console.WriteLine(ex.ToString());
|
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);
|
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)); // 드레인 대기 (타임아웃 허용)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,13 +73,13 @@ internal class AptabaseClientBase : IAsyncDisposable
|
|||||||
if (eventData.Props is null)
|
if (eventData.Props is null)
|
||||||
eventData.Props = new Dictionary<string, object>();
|
eventData.Props = new Dictionary<string, object>();
|
||||||
|
|
||||||
eventData.Props["ipAddress"] = await GetPublicIpAddress();
|
//eventData.Props["ipAddress"] = await GetPublicIpAddress(); 느려서 제외. 필요하면 다른 방법 사용 요망
|
||||||
|
|
||||||
var body = JsonContent.Create(eventData);
|
var body = JsonContent.Create(eventData);
|
||||||
|
|
||||||
var response = await _http.PostAsync("/api/v0/event", body);
|
var response = await _http.PostAsync("/api/v0/event", body);
|
||||||
|
|
||||||
var logPath = "aptabase-debug.log";
|
//var logPath = "aptabase-debug.log"; //파일 관리 안되므로 제거. 필요하면 다른 방법 사용 요망
|
||||||
|
|
||||||
if (!response.IsSuccessStatusCode)
|
if (!response.IsSuccessStatusCode)
|
||||||
{
|
{
|
||||||
@@ -94,11 +94,11 @@ internal class AptabaseClientBase : IAsyncDisposable
|
|||||||
var responseBody = await response.Content.ReadAsStringAsync();
|
var responseBody = await response.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
_logger?.LogError("Failed to perform TrackEvent due to {StatusCode} and response body {Body}", response.StatusCode, responseBody);
|
_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
|
else
|
||||||
{ var responseBody = await response.Content.ReadAsStringAsync();
|
{ 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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user