간접 호출 기반 로깅 시스템 설계

간접 호출 기반 로깅 시스템 설계

성능이 중요한 애플리케이션에서는 인터페이스 기반 로깅의 오버헤드를 줄이면서도 다형성을 유지하는 것이 중요합니다. 이 글에서는 Dictionary를 활용한 간접 호출 기반 로깅 시스템을 설계하고, 이를 통해 성능과 유연성을 동시에 확보하는 방법을 설명합니다.

로깅 시스템 초기화

간접 호출 기반 로깅 시스템에서는 각 로그 레벨에 대해 Dictionary<LogLevel, Action<string>>를 사용하여 로깅 메서드를 간접 호출하는 방식을 구현합니다. 이 방식은 인터페이스 기반 로깅 시스템과 다르게 메서드 호출을 사전 설정된 대리자를 통해 처리하여 성능을 최적화합니다.

public class Log
{
    private static readonly Lazy<Log> instance = new Lazy<Log>(() => new Log());
    public static Log Instance => instance.Value;
    private Dictionary<LogLevel, Action<string>> _loggers = new Dictionary<LogLevel, Action<string>>();
    private LogType _type = LogType.File;
    private LogLevel _level = LogLevel.Debug;
    private string _path = $@"C:\Logs\cLib.log";
    public LogType Type
    {
        get => _type;
        set
        {
            if (_type != value)
            {
                _type = value;
                Build().Initialize();
            }
        }
    }
    public LogLevel Level
    {
        get => _level;
        set
        {
            if (_level != value)
            {
                _level = value;
                Initialize();
            }
        }
    }
    public string Path
    {
        get => _path;
        set
        {
            if (_path != value)
            {
                _path = value;
                Initialize();
            }
        }
    }
    public Log SetType(LogType type)
    {
        Type = type;
        return this;
    }
    public Log SetLevel(LogLevel level)
    {
        Level = level;
        return this;
    }
    public Log SetPath(string path)
    {
        Path = path;
        return this;
    }
    public Log Build()
    {
        var loggerFactory = _type switch
        {
            LogType.Zero => new ZeroLoggerFactory(),
            LogType.File => new SerilogLoggerFactory(),
            _ => throw new ArgumentException("Invalid LogType")
        };
        InitializeLogger(loggerFactory.CreateLogger(_path, _level));
        return this;
    }
    private void InitializeLogger(ILogger logger)
    {
        _loggers = new Dictionary<LogLevel, Action<string>>
        {
            { LogLevel.Trace, logger.Trace },
            { LogLevel.Debug, logger.Debug },
            { LogLevel.Info, logger.Info },
            { LogLevel.Warn, logger.Info }, // Warn과 Info가 동일한 로그로 처리되는 경우
            { LogLevel.Error, logger.Error }
        };
    }
    public static void LogMessage(LogLevel level, string msg)
    {
        if (Instance._loggers.TryGetValue(level, out var logAction))
        {
            logAction.Invoke(msg);
        }
    }
    public static void Trace(string msg) => LogMessage(LogLevel.Trace, msg);
    public static void Debug(string msg) => LogMessage(LogLevel.Debug, msg);
    public static void Info(string msg) => LogMessage(LogLevel.Info, msg);
    public static void Error(string msg) => LogMessage(LogLevel.Error, msg);
}