301 lines
12 KiB
C#
301 lines
12 KiB
C#
#nullable enable
|
|
|
|
using Cysharp.Threading.Tasks;
|
|
using SQLite4Unity3d;
|
|
using System;
|
|
using System.IO;
|
|
using System.Threading.Tasks;
|
|
using UnityEngine;
|
|
|
|
|
|
namespace UVC.Log
|
|
{
|
|
/// <summary>
|
|
/// ServerLog는 HTTP 요청과 MQTT 메시지를 기록하는 기능을 제공합니다.
|
|
/// </summary>
|
|
public class ServerLog
|
|
{
|
|
|
|
private static bool doDeletedHttp = false;
|
|
private static bool doDeletedMqtt = false;
|
|
|
|
private static SQLiteConnection? db;
|
|
|
|
private static readonly object _dbLock = new object();
|
|
|
|
static ServerLog()
|
|
{
|
|
try
|
|
{
|
|
#if UNITY_WEBGL && !UNITY_EDITOR
|
|
// WebGL: 백그라운드(ThreadPool) 사용 불가 → 동기 초기화
|
|
EnsureDatabaseConnectionWebGL();
|
|
CleanupOldLogsWebGL();
|
|
#else
|
|
CheckDatabaseConnectionAsync().ContinueWith((_) =>
|
|
{
|
|
lock (_dbLock)
|
|
{
|
|
if (doDeletedHttp == false)
|
|
{
|
|
//한달 지난 로그 삭제
|
|
db?.Execute("DELETE FROM HttpLogEntry WHERE RequestDate < datetime('now', '-1 month', 'localtime');");
|
|
doDeletedHttp = true;
|
|
}
|
|
if (doDeletedMqtt == false)
|
|
{
|
|
//한달 지난 로그 삭제
|
|
db?.Execute("DELETE FROM MqttLogEntry WHERE Date < datetime('now', '-1 month', 'localtime');");
|
|
doDeletedMqtt = true;
|
|
}
|
|
}
|
|
});
|
|
#endif
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.LogException(ex);
|
|
throw;
|
|
}
|
|
}
|
|
|
|
#if UNITY_WEBGL && !UNITY_EDITOR
|
|
/// <summary>
|
|
/// WebGL 환경에서 데이터베이스 연결을 동기적으로 초기화합니다.
|
|
/// </summary>
|
|
private static void EnsureDatabaseConnectionWebGL()
|
|
{
|
|
if (db != null) return;
|
|
|
|
string appDataPath = Application.persistentDataPath;
|
|
DirectoryInfo di = new DirectoryInfo(Path.Combine(appDataPath, "unityLogs"));
|
|
if (!di.Exists) di.Create();
|
|
|
|
// WebGL Player 환경 구분
|
|
string dbPath = Application.platform == RuntimePlatform.WebGLPlayer
|
|
? Path.Combine(di.FullName, "serverWebGLLog.db")
|
|
: Path.Combine(di.FullName, "serverEditorLog.db");
|
|
|
|
db = new SQLiteConnection(dbPath);
|
|
try
|
|
{
|
|
db.CreateTable<HttpLogEntry>();
|
|
db.CreateTable<MqttLogEntry>();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.LogException(ex);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// WebGL에서 오래된 로그를 동기적으로 정리합니다.
|
|
/// </summary>
|
|
private static void CleanupOldLogsWebGL()
|
|
{
|
|
lock (_dbLock)
|
|
{
|
|
if (!doDeletedHttp)
|
|
{
|
|
db?.Execute("DELETE FROM HttpLogEntry WHERE RequestDate < datetime('now', '-1 month', 'localtime');");
|
|
doDeletedHttp = true;
|
|
}
|
|
if (!doDeletedMqtt)
|
|
{
|
|
db?.Execute("DELETE FROM MqttLogEntry WHERE Date < datetime('now', '-1 month', 'localtime');");
|
|
doDeletedMqtt = true;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/// <summary>
|
|
/// HTTP 요청을 로깅하여 데이터베이스에 로그 항목을 생성하고 저장합니다.
|
|
/// </summary>
|
|
/// <remarks>이 메소드는 스레드 안전하며 로그 항목이 동기화된 방식으로 데이터베이스에 저장되도록 보장합니다.
|
|
/// 호출자는 유효한 입력 값을 제공할 책임이 있습니다.</remarks>
|
|
/// <param name="url">HTTP 요청의 URL입니다.</param>
|
|
/// <param name="method">요청에 사용된 HTTP 메소드(예: GET, POST)입니다.</param>
|
|
/// <param name="headerJson">JSON 형식의 HTTP 요청 헤더입니다.</param>
|
|
/// <param name="bodyString">HTTP 요청의 본문을 문자열로 표현한 것입니다.</param>
|
|
/// <param name="date">HTTP 요청의 날짜와 시간을 문자열로 표현한 것입니다.</param>
|
|
/// <returns>로깅된 HTTP 요청을 나타내는 <see cref="HttpLogEntry"/> 객체입니다. 반환된 객체는
|
|
/// 요청 URL, 메소드, 헤더, 본문, 날짜와 같은 세부 정보를 포함합니다.</returns>
|
|
public static HttpLogEntry LogHttpRequest(string url, string method, string headerJson, string bodyString, string date)
|
|
{
|
|
#if UNITY_WEBGL && !UNITY_EDITOR
|
|
// WebGL: 초기화 보장
|
|
if (db == null) EnsureDatabaseConnectionWebGL();
|
|
#endif
|
|
var logEntry = new HttpLogEntry
|
|
{
|
|
RequestURL = url,
|
|
RequestMethod = method,
|
|
RequestHeader = headerJson,
|
|
RequestBody = bodyString,
|
|
RequestDate = date,
|
|
};
|
|
lock (_dbLock)
|
|
{
|
|
try
|
|
{
|
|
db?.Insert(logEntry);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.LogException(ex);
|
|
}
|
|
return logEntry; // 반환값으로 ID를 반환
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// HTTP 응답의 세부 정보를 데이터베이스에 기록합니다.
|
|
/// </summary>
|
|
/// <remarks>이 메소드는 스레드 안전하며 로깅 작업이 동기화되도록 보장합니다.</remarks>
|
|
/// <param name="httpLogEntry">기록할 응답의 세부 정보가 포함된 HTTP 로그 항목입니다. 이 매개변수는 <see
|
|
/// langword="null"/>일 수 없습니다.</param>
|
|
public static void LogHttpResponse(HttpLogEntry httpLogEntry)
|
|
{
|
|
#if UNITY_WEBGL && !UNITY_EDITOR
|
|
if (db == null) EnsureDatabaseConnectionWebGL();
|
|
#endif
|
|
|
|
lock (_dbLock)
|
|
{
|
|
try
|
|
{
|
|
db?.Update(httpLogEntry);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.LogException(ex);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// MQTT 메시지를 데이터베이스에 기록하고 생성된 로그 항목을 반환합니다.
|
|
/// </summary>
|
|
/// <remarks>이 메서드는 MQTT 메시지에 대한 새로운 로그 항목을 생성하여 데이터베이스에 저장합니다. 이 메서드는 스레드 안전하며 데이터베이스 작업이 동기화되도록 보장합니다.</remarks>
|
|
/// <param name="url">MQTT 브로커의 URL입니다.</param>
|
|
/// <param name="port">MQTT 브로커에 연결하는 데 사용된 포트입니다.</param>
|
|
/// <param name="topic">MQTT 메시지의 토픽입니다.</param>
|
|
/// <param name="payload">MQTT 메시지의 페이로드입니다.</param>
|
|
/// <param name="date">메시지가 수신된 날짜와 시간(문자열 형식)입니다.</param>
|
|
/// <param name="exception">처리 중 오류가 발생한 경우의 예외 메시지(옵션). <see langword="null"/>일 수 있습니다.</param>
|
|
/// <returns>기록된 MQTT 메시지를 나타내는 <see cref="MqttLogEntry"/> 객체입니다.</returns>
|
|
public static MqttLogEntry LogMqtt(string url, string port, string topic, string payload, string date, string? exception = null)
|
|
{
|
|
#if UNITY_WEBGL && !UNITY_EDITOR
|
|
if (db == null) EnsureDatabaseConnectionWebGL();
|
|
#endif
|
|
var logEntry = new MqttLogEntry
|
|
{
|
|
URL = url,
|
|
Port = port,
|
|
Topic = topic,
|
|
Payload = payload,
|
|
Date = date,
|
|
Exception = exception
|
|
};
|
|
lock (_dbLock)
|
|
{
|
|
try
|
|
{
|
|
db?.Insert(logEntry);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.LogException(ex);
|
|
}
|
|
return logEntry;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
protected static async Task CheckDatabaseConnectionAsync()
|
|
{
|
|
#if UNITY_WEBGL && !UNITY_EDITOR
|
|
// WebGL: 비동기 + 스레드 전환 제거, 동기 루틴 사용
|
|
EnsureDatabaseConnectionWebGL();
|
|
return;
|
|
#else
|
|
if (db == null)
|
|
{
|
|
string dbPath = string.Empty;
|
|
string appDataPath = string.Empty;
|
|
bool isMainThread = PlayerLoopHelper.IsMainThread;
|
|
if (!isMainThread) await UniTask.SwitchToMainThread();
|
|
appDataPath = Application.persistentDataPath;
|
|
if (!isMainThread) await UniTask.SwitchToThreadPool();
|
|
DirectoryInfo di = new DirectoryInfo(Path.Combine(appDataPath, "unityLogs"));
|
|
if (!di.Exists) di.Create();
|
|
// unity runtime 일때
|
|
if (Application.platform == RuntimePlatform.WindowsPlayer)
|
|
{
|
|
dbPath = Path.Combine(di.FullName, @"serverRuntimeLog.db");
|
|
}
|
|
else
|
|
{
|
|
// unity editor 일때
|
|
dbPath = Path.Combine(di.FullName, @"serverEditorLog.db");
|
|
}
|
|
db = new SQLiteConnection(dbPath);
|
|
try
|
|
{
|
|
db.CreateTable<HttpLogEntry>();
|
|
db.CreateTable<MqttLogEntry>();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.LogException(ex);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// HTTP 요청 및 관련 응답을 위한 로그 항목을 나타냅니다.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// 이 클래스는 HTTP 요청의 URL, 메소드, 헤더, 본문과 응답 데이터를 (가능한 경우) 포함한 세부 정보를 저장하는 데 사용됩니다.
|
|
/// 또한 요청 또는 응답 처리 중 발생한 예외 사항도 기록합니다.
|
|
/// </remarks>
|
|
public class HttpLogEntry
|
|
{
|
|
[PrimaryKey, AutoIncrement]
|
|
public int Id { get; set; }
|
|
public string RequestURL { get; set; }
|
|
public string RequestMethod { get; set; }
|
|
public string RequestHeader { get; set; }
|
|
public string RequestBody { get; set; }
|
|
public string RequestDate { get; set; }
|
|
public string? ResponseData { get; set; }
|
|
public string? ResponseDate { get; set; }
|
|
public string? Exception { get; set; }
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// MQTT 메시지에 대한 로그 항목을 나타냅니다. 연결, 토픽, 페이로드 및 모든 관련 예외에 대한 세부 정보를 포함합니다.
|
|
/// </summary>
|
|
/// <remarks>이 클래스는 일반적으로 로깅 또는 디버깅 목적으로 MQTT 메시지에 대한 정보를 저장하고 검색하는 데 사용됩니다.
|
|
/// 각 로그 항목에는 URL, 포트, 토픽, 페이로드 및 메시지가 기록된 날짜와 같은 세부 정보가 포함됩니다.
|
|
/// MQTT 작업 중 예외가 발생한 경우 <see cref="Exception"/> 속성에도 기록될 수 있습니다.</remarks>
|
|
public class MqttLogEntry
|
|
{
|
|
[PrimaryKey, AutoIncrement]
|
|
public int Id { get; set; }
|
|
public string URL { get; set; }
|
|
public string Port { get; set; }
|
|
public string Topic { get; set; }
|
|
public string Payload { get; set; }
|
|
public string Date { get; set; }
|
|
public string? Exception { get; set; }
|
|
}
|
|
}
|