programing tip

Log4Net Wrapper 클래스는 어떻게 생겼습니까?

itbloger 2020. 12. 31. 08:00
반응형

Log4Net Wrapper 클래스는 어떻게 생겼습니까?


저는 .net (c #)에 대한 로깅 프레임 워크를 찾고 있었고 여기 stackoverflow에 대한 몇 가지 질문 / 답변 스레드를 읽은 후 log4net을 사용하기로 결정했습니다. 나는 사람들이 log4net에 래퍼 클래스를 사용한다고 반복해서 언급하는 것을 보았고 그것이 어떻게 생겼는지 궁금합니다.

내 코드를 여러 프로젝트 (데이터 액세스 / 비즈니스 / 웹 서비스 / ..)로 분할했습니다. log4net 래퍼 클래스는 어떻게 생겼습니까? 모든 프로젝트에 래퍼 클래스를 포함해야합니까? 모두 함께 별도의 프로젝트로 빌드해야합니까?

래퍼는 싱글 톤 클래스 여야합니까?


기본적으로 인터페이스를 만든 다음 Log4net의 클래스와 메서드를 직접 래핑하는 해당 인터페이스의 구체적인 구현을 만듭니다. 추가 로깅 시스템은 해당 시스템의 다른 클래스와 메서드를 래핑하는보다 구체적인 클래스를 만들어 래핑 할 수 있습니다. 마지막으로 팩토리를 사용하여 구성 설정 또는 코드 변경 줄에 따라 래퍼 인스턴스를 만듭니다. (참고 : StructureMap 과 같은 Inversion of Control 컨테이너를 사용하면 더 유연하고 복잡하게 만들 수 있습니다 .)

public interface ILogger
{
    void Debug(object message);
    bool IsDebugEnabled { get; }

    // continue for all methods like Error, Fatal ...
}

public class Log4NetWrapper : ILogger
{
    private readonly log4net.ILog _logger;

    public Log4NetWrapper(Type type)
    {
        _logger = log4net.LogManager.GetLogger(type);
    }

    public void Debug(object message)
    {
        _logger.Debug(message);
    }

    public bool IsDebugEnabled
    {
        get { return _logger.IsDebugEnabled; }
    }

    // complete ILogger interface implementation
}

public static class LogManager
{
    public static ILogger GetLogger(Type type)
    {
        // if configuration file says log4net...
        return new Log4NetWrapper(type);
        // if it says Joe's Logger...
        // return new JoesLoggerWrapper(type);
    }
}

클래스에서이 코드를 사용하는 예 (정적 읽기 전용 필드로 선언 됨) :

private static readonly ILogger _logger =
    LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

다음을 사용하여 약간 더 성능 친화적 인 효과를 얻을 수 있습니다.

private static readonly ILogger _logger = 
    LogManager.GetLogger(typeof(YourTypeName));

앞의 예는 유지 관리가 더 용이 한 것으로 간주됩니다.

Log4Net이 호출 유형에 대해 기록하기 때문에 모든 로깅을 처리하기 위해 Singleton을 생성하고 싶지는 않습니다. 로그 파일에서 모든 메시지를보고하는 단일 유형을 보는 것보다 각 유형이 자체 로거를 사용하도록하는 것이 훨씬 깨끗하고 유용합니다.

구현은 상당히 재사용 가능해야하기 때문에 (조직의 다른 프로젝트) 자체 어셈블리로 만들거나 이상적으로는 자신의 개인 / 조직의 프레임 워크 / 유틸리티 어셈블리에 포함 할 수 있습니다. 각 비즈니스 / 데이터 / UI 어셈블리에서 별도로 클래스를 다시 선언하지 마십시오. 이는 유지 관리가 불가능합니다.


위의 cfeduke의 답변 과 같은 것으로 가정하면 다음과 같이 과부하를 추가 할 수도 있습니다 LogManager.

public static ILogger GetLogger()
{
    var stack = new StackTrace();
    var frame = stack.GetFrame(1);
    return new Log4NetWrapper(frame.GetMethod().DeclaringType);
}

그런 식으로 코드에서 이제 다음을 사용할 수 있습니다.

private static readonly ILogger _logger = LogManager.GetLogger();

다음 중 하나 대신 :

private static readonly ILogger _logger =
    LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static readonly ILogger _logger = 
    LogManager.GetLogger(typeof(YourTypeName));

이는 첫 번째 대안 (예 :를 사용하는 대안)과 실질적으로 동일 MethodBase.GetCurrentMethod().DeclaringType하며 조금 더 간단합니다.


log4net에 대한 래퍼 작성에서 어떤 이점을 계획하고 있습니까? 래퍼를 작성하기 전에 먼저 log4net 클래스에 익숙해지는 것이 좋습니다. cfeduke는 래퍼를 작성하는 방법에 대한 그의 대답에 옳지 만, 그의 예제에 실제 기능을 추가 할 필요가 없다면 래퍼는 로깅 프로세스를 늦추고 미래의 유지 관리자에게 복잡성을 추가하는 데 성공할 것입니다. .Net에서 사용 가능한 리팩토링 도구가 이러한 변경을 매우 쉽게 만들 때 특히 그렇습니다.


log4net 종속성을 단일 프로젝트로 성공적으로 격리했습니다. 동일한 작업을 수행하려는 경우 내 래퍼 클래스는 다음과 같습니다.

using System;

namespace Framework.Logging
{
    public class Logger
    {
        private readonly log4net.ILog _log;

        public Logger()
        {
            _log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
        }

        public Logger(string name)
        {
            _log = log4net.LogManager.GetLogger(name);
        }

        public Logger(Type type)
        {
            _log = log4net.LogManager.GetLogger(type);
        }

        public void Debug(object message, Exception ex = null)
        {
            if (_log.IsDebugEnabled)
            {
                if (ex == null)
                {
                    _log.Debug(message);
                }
                else
                {
                    _log.Debug(message, ex);
                }
            }
        }

        public void Info(object message, Exception ex = null)
        {
            if (_log.IsInfoEnabled)
            {
                if (ex == null)
                {
                    _log.Info(message);
                }
                else
                {
                    _log.Info(message, ex);
                }
            }
        }

        public void Warn(object message, Exception ex = null)
        {
            if (_log.IsWarnEnabled)
            {
                if (ex == null)
                {
                    _log.Warn(message);
                }
                else
                {
                    _log.Warn(message, ex);
                }
            }
        }

        public void Error(object message, Exception ex = null)
        {
            if (_log.IsErrorEnabled)
            {
                if (ex == null)
                {
                    _log.Error(message);
                }
                else
                {
                    _log.Error(message, ex);
                }
            }
        }

        public void Fatal(object message, Exception ex = null)
        {
            if (_log.IsFatalEnabled)
            {
                if (ex == null)
                {
                    _log.Fatal(message);
                }
                else
                {
                    _log.Fatal(message, ex);
                }
            }
        }
    }
}

And dont forget to add this in the AssemblyInfo.cs of the interfacing project (took me a good few hours to find this)

[assembly: log4net.Config.XmlConfigurator(Watch = true, ConfigFile = "log4net.config")]

And put your log4net configuration xml in log4net.config file, set it as Content, Copy Always


There are frameworks like the Prism Library for WPF that promote the usage of a facade for the logging framework of your choice.

This is an example that uses log4net:

using System;
using log4net;
using log4net.Core;
using Prism.Logging;

public class Log4NetLoggerFacade : ILoggerFacade
{
    private static readonly ILog Log4NetLog = LogManager.GetLogger(typeof (Log4NetLoggerFacade));

    public void Log(string message, Category category, Priority priority)
    {
        switch (category)
        {
            case Category.Debug:
                Log4NetLog.Logger.Log(typeof(Log4NetLoggerFacade), Level.Debug, message, null);
                break;
            case Category.Exception:
                Log4NetLog.Logger.Log(typeof(Log4NetLoggerFacade), Level.Error, message, null);
                break;
            case Category.Info:
                Log4NetLog.Logger.Log(typeof(Log4NetLoggerFacade), Level.Info, message, null);
                break;
            case Category.Warn:
                Log4NetLog.Logger.Log(typeof(Log4NetLoggerFacade), Level.Warn, message, null);
                break;
            default:
                throw new ArgumentOutOfRangeException(nameof(category), category, null);
        }
    }
}

Note that by specifying the callerStackBoundaryDeclaringType you can still get the class name of the caller issuing the logging request. All you need to do is to include %C %M in your conversion pattern:

<layout type="log4net.Layout.PatternLayout">
    <conversionPattern value="%date [%thread] %-5level %C.%M - %message%newline" />
</layout>

However, as the documentation warns, generating the caller class information is slow, therefore it must be used wisely.


My understanding is that a wrapper class for log4net would be a static class which takes care of initializing the logging object from app.config/web.config or by code (e.g. integration with NUnit).


a possible use for a log4net wrapper could be a class that gets the calling class and method via reflection to get an idea of where your logging entry happened. at least i use this frequently.


Alconja, I like your idea of using the stacktrace to jump back to the calling method. I was thinking of further encapsulating the calls, to not just retrieve the logger object, but to perform actually perform the logging. What I want is a static class that handles the logging, by abstracting from the specific implementation used. I.e.

LoggingService.LogError("my error message");

That way I only need to change the internals of the static class, if I later decide to user another logging system.

So I used your idea to get the calling object using the stack trace :

public static class LoggingService
{
    private static ILog GetLogger()
    {    
        var stack = new StackTrace();    
        var frame = stack.GetFrame(2);    
        return log4net.LogManager.GetLogger(frame.GetMethod().DeclaringType);
    }

    public static void LogError(string message)
    {
        ILog logger = GetLogger();
        if (logger.IsErrorEnabled)
            logger.Error(message);
    }
    ...
}

Does anybody see a problem with this approach?


I know this answer is late, but it may help someone in the future.

It sounds like you want a programmatic API that XQuiSoft Logging gives you. You don't have to specify which logger you want with XQuiSoft. it is as simple as this:

Log.Write(Level.Verbose, "source", "category", "your message here");

Then via configuration you direct messages by source, category, level, or any other custom filter to different locations (files, emails, etc...).

See this article for an introduction.

ReferenceURL : https://stackoverflow.com/questions/166438/what-would-a-log4net-wrapper-class-look-like

반응형