180 lines
7.1 KiB
C#
180 lines
7.1 KiB
C#
#nullable enable
|
|
#if !UNITY_WEBGL || UNITY_EDITOR
|
|
using log4net.Appender;
|
|
using log4net.Core;
|
|
using SQLite4Unity3d;
|
|
using System.Threading;
|
|
using UVC.Extention;
|
|
|
|
namespace UVC.Log
|
|
{
|
|
/// <summary>
|
|
/// SQLite 데이터베이스에 로그를 저장하는 log4net Appender 구현체입니다.
|
|
/// 로그 메시지를 SQLite 데이터베이스 테이블에 저장하고 오래된 로그를 자동으로 정리합니다.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// 이 클래스는 log4net의 AppenderSkeleton을 상속받아 로그 레코드를 SQLite 데이터베이스에 저장합니다.
|
|
/// 한 달 이상 지난 로그 레코드는 자동으로 삭제되며, 데이터베이스 락 상황에서의 재시도 메커니즘을 포함하고 있습니다.
|
|
/// </remarks>
|
|
/// <example>
|
|
/// log4net 구성 파일(App.config 또는 log4net.config)에서 다음과 같이 설정할 수 있습니다:
|
|
/// <code>
|
|
/// <appender name="SQLiteAppender" type="UVC.Log.SQLiteAppender, AssemblyName">
|
|
/// <connectionString value="data.db" />
|
|
/// <param name="Threshold" value="DEBUG" />
|
|
/// </appender>
|
|
/// </code>
|
|
///
|
|
/// 코드에서 직접 설정하는 방법:
|
|
/// <code>
|
|
/// var sqliteAppender = new SQLiteAppender();
|
|
/// sqliteAppender.ConnectionString = "data.db";
|
|
/// sqliteAppender.Threshold = log4net.Core.Level.Debug;
|
|
///
|
|
/// log4net.Config.BasicConfigurator.Configure(sqliteAppender);
|
|
/// </code>
|
|
/// </example>
|
|
public class SQLiteAppender : AppenderSkeleton
|
|
{
|
|
/// <summary>
|
|
/// SQLite 데이터베이스 파일의 경로를 지정하거나 가져옵니다.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// 절대 경로 또는 상대 경로로 설정할 수 있으며, 상대 경로는 애플리케이션의 실행 디렉터리를 기준으로 합니다.
|
|
/// </remarks>
|
|
/// <example>
|
|
/// <code>
|
|
/// var sqliteAppender = new SQLiteAppender();
|
|
/// // 절대 경로 사용
|
|
/// sqliteAppender.ConnectionString = "C:\\Logs\\application.db";
|
|
/// // 또는 상대 경로 사용
|
|
/// sqliteAppender.ConnectionString = "Logs\\application.db";
|
|
/// </code>
|
|
/// </example>
|
|
public string ConnectionString { get; set; }
|
|
|
|
/// <summary>
|
|
/// 한 달 이상 된 로그 데이터 삭제 여부를 추적합니다.
|
|
/// </summary>
|
|
private bool doDeleted = false;
|
|
|
|
/// <summary>
|
|
/// SQLite 데이터베이스 연결을 저장합니다.
|
|
/// </summary>
|
|
private SQLiteConnection? db;
|
|
|
|
/// <summary>
|
|
/// 데이터베이스 작업에 대한 동기화 잠금 객체입니다.
|
|
/// </summary>
|
|
private static readonly object _dbLock = new object();
|
|
|
|
/// <summary>
|
|
/// 로깅 이벤트를 SQLite 데이터베이스에 기록합니다.
|
|
/// </summary>
|
|
/// <param name="loggingEvent">기록할 로깅 이벤트</param>
|
|
/// <remarks>
|
|
/// 이 메서드는 log4net 프레임워크에 의해 새로운 로그 메시지가 발생할 때마다 호출됩니다.
|
|
/// 빈 메시지는 무시하고, 로그 항목을 데이터베이스에 저장합니다.
|
|
/// 데이터베이스 연결이 없는 경우 새로 생성하고, 필요시 오래된 로그를 삭제합니다.
|
|
/// </remarks>
|
|
protected override void Append(LoggingEvent loggingEvent)
|
|
{
|
|
// 빈 메시지 무시
|
|
if (loggingEvent.RenderedMessage.Trim().IsNullOrEmpty()) return;
|
|
|
|
// 로그 항목 생성
|
|
var logEntry = new LogEntry
|
|
{
|
|
Date = loggingEvent.TimeStamp.ToString("yyyy-MM-ddTHH:mm:ss.fffZ"),
|
|
Thread = loggingEvent.ThreadName,
|
|
Level = loggingEvent.Level.ToString(),
|
|
Logger = loggingEvent.LoggerName,
|
|
Message = loggingEvent.RenderedMessage,
|
|
Exception = loggingEvent.GetExceptionString()
|
|
};
|
|
|
|
// 데이터베이스 연결이 없으면 새로 생성
|
|
if (db == null)
|
|
{
|
|
var dbPath = ConnectionString;// System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ConnectionString);
|
|
db = new SQLiteConnection(dbPath);
|
|
db.CreateTable<LogEntry>();
|
|
}
|
|
|
|
// 한 달 이상 된 로그 데이터 삭제 (최초 1회만 실행)
|
|
if (doDeleted == false)
|
|
{
|
|
//한달 지난 로그 삭제
|
|
db.Execute("DELETE FROM LogEntry WHERE Date < datetime('now', '-1 month', 'localtime');");
|
|
doDeleted = true;
|
|
}
|
|
|
|
// 스레드 동기화를 통해 데이터베이스 작업 수행
|
|
lock (_dbLock)
|
|
{
|
|
// 데이터베이스 락 시 재시도 메커니즘
|
|
int retryCount = 100;
|
|
while (retryCount > 0)
|
|
{
|
|
try
|
|
{
|
|
db.Insert(logEntry);
|
|
break;
|
|
}
|
|
catch (SQLiteException ex) when (ex.Message.Contains("database is locked"))
|
|
{
|
|
retryCount--;
|
|
if (retryCount == 0) break; // 최대 재시도 횟수 초과
|
|
Thread.Sleep(100); // 재시도 전에 100ms 대기
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// SQLite 데이터베이스에 저장되는 로그 항목 구조를 정의합니다.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// 이 클래스는 로그 테이블의 스키마를 정의하며, SQLite4Unity3d의 ORM 기능을 사용합니다.
|
|
/// </remarks>
|
|
public class LogEntry
|
|
{
|
|
/// <summary>
|
|
/// 로그 항목의 고유 식별자입니다. 자동 증가되는 기본 키로 사용됩니다.
|
|
/// </summary>
|
|
[PrimaryKey, AutoIncrement]
|
|
public int Id { get; set; }
|
|
|
|
/// <summary>
|
|
/// 로그가 생성된 날짜와 시간을 ISO 8601 형식으로 저장합니다.
|
|
/// </summary>
|
|
public string Date { get; set; }
|
|
|
|
/// <summary>
|
|
/// 로그를 생성한 스레드의 이름을 저장합니다.
|
|
/// </summary>
|
|
public string Thread { get; set; }
|
|
|
|
/// <summary>
|
|
/// 로그 레벨을 저장합니다 (DEBUG, INFO, WARN, ERROR, FATAL 등).
|
|
/// </summary>
|
|
public string Level { get; set; }
|
|
|
|
/// <summary>
|
|
/// 로그를 생성한 로거의 이름을 저장합니다.
|
|
/// </summary>
|
|
public string Logger { get; set; }
|
|
|
|
/// <summary>
|
|
/// 로그 메시지 내용을 저장합니다.
|
|
/// </summary>
|
|
public string Message { get; set; }
|
|
|
|
/// <summary>
|
|
/// 예외 정보를 저장합니다. 예외가 없는 경우 빈 문자열이 될 수 있습니다.
|
|
/// </summary>
|
|
public string Exception { get; set; }
|
|
}
|
|
}
|
|
}
|
|
#endif |