초기 커밋.
This commit is contained in:
178
Assets/Scripts/UVC/Log/SQLiteAppender.cs
Normal file
178
Assets/Scripts/UVC/Log/SQLiteAppender.cs
Normal file
@@ -0,0 +1,178 @@
|
||||
using log4net.Appender;
|
||||
using log4net.Core;
|
||||
using SQLite4Unity3d;
|
||||
using System;
|
||||
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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user