Nice programing

종속성 주입 및 명명 된 로거

nicepro 2020. 10. 27. 23:27
반응형

종속성 주입 및 명명 된 로거


사람들이 의존성 주입 플랫폼에 로깅을 주입하는 방법에 대해 더 배우고 싶습니다. 아래 링크와 내 예제는 log4net 및 Unity를 참조하지만 반드시 둘 중 하나를 사용하지는 않습니다. 의존성 주입 / IOC의 경우, 나머지 프로젝트 (대형)가 정착하는 표준이므로 MEF를 사용할 것입니다.

저는 의존성 주입 / ioc를 처음 접했고 C #과 .NET을 처음 접했습니다 (지난 10 년 정도의 VC6 및 VB6 이후 C # /. NET에서 프로덕션 코드를 거의 작성하지 않았습니다). 나는 거기에있는 다양한 로깅 솔루션에 대해 많은 조사를 했으므로 그들의 기능 세트에 대한 적절한 핸들을 가지고 있다고 생각합니다. 나는 단지 하나의 의존성을 주입하는 실제 메커니즘에 충분히 익숙하지 않다 (또는 아마도 더 "올바르게", 주입 된 하나의 의존성의 추상화 된 버전을 얻는 것).

나는 의존성 주입 및 로깅 인터페이스와 같은 로깅 및 / 또는 의존성 주입과 관련된 다른 게시물을 보았습니다.

모범 사례 로깅

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

다시 log4net 및 Unity IOC 구성에 대해

내 질문은 "ioc 도구 yyy를 사용하여 로깅 플랫폼 xxx를 삽입하는 방법"과 특별히 관련이 없습니다. 그보다는 사람들이 로깅 플랫폼 (자주 있지만 항상 권장되는 것은 아님) 및 구성 (예 : app.config) 래핑을 처리하는 방법에 관심이 있습니다. 예를 들어, log4net을 사용하여 여러 로거를 구성 (app.config에서) 한 다음 다음과 같은 코드를 사용하는 표준 방식으로 해당 로거 (종속성 주입없이)를 가져올 수 있습니다.

private static readonly ILog logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

또는 로거가 클래스 이름이 아니라 기능 영역 이름 인 경우 다음과 같이 할 수 있습니다.

private static readonly ILog logger = LogManager.GetLogger("Login");
private static readonly ILog logger = LogManager.GetLogger("Query");
private static readonly ILog logger = LogManager.GetLogger("Report");

따라서 내 "요구 사항"은 다음과 같을 것입니다.

  1. 로깅 플랫폼에 대한 직접적인 의존성으로부터 제품 소스를 격리하고 싶습니다.

  2. 특정 명명 된 로거 인스턴스 (아마도 동일한 명명 된 인스턴스의 모든 요청자간에 동일한 인스턴스를 공유 함)를 일종의 종속성 주입 (아마도 MEF)에 의해 직접 또는 간접적으로 해결할 수 있기를 원합니다.

  3. 나는 이것을 어려운 요구 사항이라고 부를지 모르겠지만 요구에 따라 명명 된 로거 (클래스 로거와 다른)를 얻을 수있는 기능을 원합니다. 예를 들어, 클래스 이름을 기반으로 클래스에 대한 로거를 만들 수 있지만 한 메서드에는 별도로 제어하고 싶은 특히 무거운 진단이 필요합니다. 즉, 단일 클래스가 두 개의 개별 로거 인스턴스에 "종속"되도록 할 수 있습니다.

1 번부터 시작해 보겠습니다. 주로 여기 stackoverflow에 대한 여러 기사를 읽었습니다. 랩핑하는 것이 좋은 아이디어인지에 대한 것입니다. 위의 "모범 사례"링크를 참조하고 log4net을 래핑하는 것이 나쁜 이유에 대한 한 가지보기를 보려면 jeffrey hantin 의 의견으로 이동하십시오 . 랩핑을했다면 (그리고 효과적으로 랩핑 할 수 있다면) 직접 의존성의 주입 / 제거를 위해 엄격하게 랩핑 하시겠습니까? 아니면 log4net app.config 정보의 일부 또는 전부를 추상화하려고할까요?

System.Diagnostics를 사용하고 싶다고 가정 해 보겠습니다. 인터페이스 기반 로거 ( "공통"ILogger / ILog 인터페이스를 사용할 수도 있음)를 구현하고 싶을 것입니다. 아마도 TraceSource를 기반으로하여 주입 할 수 있습니다. TraceSource를 통해 인터페이스를 구현하고 System.Diagnostics app.config 정보를 그대로 사용 하시겠습니까?

이 같은:

public class MyLogger : ILogger
{
  private TraceSource ts;
  public MyLogger(string name)
  {
    ts = new TraceSource(name);
  }

  public void ILogger.Log(string msg)
  {
    ts.TraceEvent(msg);
  }
}

다음과 같이 사용하십시오.

private static readonly ILogger logger = new MyLogger("stackoverflow");
logger.Info("Hello world!")

2 번으로 넘어 가기 ... 특정 명명 된 로거 인스턴스를 해결하는 방법은 무엇입니까? 내가 선택한 로깅 플랫폼의 app.config 정보를 활용해야합니까 (예 : app.config의 이름 지정 체계에 따라 로거 해결)? 따라서 log4net의 경우 LogManager를 "주입"하는 것이 더 좋을까요 (정적 객체이기 때문에 불가능하다는 것을 알고 있습니다)? LogManager (MyLogManager라고 함)를 래핑하고 ILogManager 인터페이스를 제공 한 다음 MyLogManager.ILogManager 인터페이스를 확인할 수 있습니다. 내 다른 개체는 ILogManager (구현 된 어셈블리에서 내보내기)에 대한 종속성 (MEF 용어로 가져 오기)을 가질 수 있습니다. 이제 다음과 같은 객체를 가질 수 있습니다.

public class MyClass
{
  private ILogger logger;
  public MyClass([Import(typeof(ILogManager))] logManager)
  {
    logger = logManager.GetLogger("MyClass");
  }
}

ILogManager가 호출 될 때마다 log4net의 LogManager에 직접 위임합니다. 또는 래핑 된 LogManager가 app.config를 기반으로하는 ILogger 인스턴스를 가져 와서 이름으로 MEF 컨테이너에 추가 할 수 있습니다. 나중에 동일한 이름의 로거가 요청되면 래핑 된 LogManager에서 해당 이름을 쿼리합니다. ILogger가 있으면 그렇게 해결됩니다. MEF에서 이것이 가능하다면 그렇게함으로써 어떤 이점이 있습니까?

이 경우 실제로 ILogManager 만 "주입"되며 log4net이 일반적으로 수행하는 방식으로 ILogger 인스턴스를 전달할 수 있습니다. 이 유형의 주입 (기본적으로 공장)은 명명 된 로거 인스턴스 주입과 어떻게 비교됩니까? 이렇게하면 log4net (또는 기타 로깅 플랫폼)의 app.config 파일을보다 쉽게 ​​활용할 수 있습니다.

다음과 같이 MEF 컨테이너에서 명명 된 인스턴스를 가져올 수 있다는 것을 알고 있습니다.

var container = new CompositionContainer(<catalogs and other stuff>);
ILogger logger = container.GetExportedValue<ILogger>("ThisLogger");

But how do I get the named instances into the container? I know about the attribute based model where I could have different implementations of ILogger, each of which is named (via a MEF attribute), but that doesn't really help me. Is there a way to create something like an app.config (or a section therein) that would list the loggers (all of the same implementation) by name and that MEF could read? Could/should there be a central "manager" (like MyLogManager) that resolves named loggers via the underlying app.config and then inserts the resolved logger into the MEF container? This way it would be available to someone else that has access to the same MEF container (although without the MyLogManager's knowledge of how to use log4net's app.config information, it seems that the container would be unable to resolve any named loggers directly).

이것은 이미 꽤 오래되었습니다. 일관성이 있기를 바랍니다. 로깅 플랫폼 (log4net, NLog 또는 System.Diagnostics에 구축 된 씬)을 애플리케이션에 삽입 한 방법에 대한 특정 정보를 자유롭게 공유하십시오.

"관리자"를 삽입하고 로거 인스턴스를 반환 했습니까?

로거 인스턴스를 직접 주입하는 것이 더 쉽고 / 가능하도록 자신의 구성 섹션 또는 DI 플랫폼의 구성 섹션에 자신의 구성 정보를 추가 했습니까 (즉, 종속성이 ILogManager가 아닌 ILogger에 있도록 함).

ILogManager 인터페이스 또는 명명 된 ILogger 인스턴스 집합이 포함 된 정적 또는 전역 컨테이너를 사용하는 것은 어떻습니까? 따라서 생성자, 속성 또는 멤버 데이터를 통해 기존의 의미로 삽입하는 대신 로깅 종속성은 요청시 명시 적으로 해결됩니다. 이것은 의존성 주입에 좋은 또는 나쁜 방법입니까?

명확한 답변이있는 질문처럼 보이지 않기 때문에 이것을 커뮤니티 위키로 표시하고 있습니다. 누군가 다르게 느끼면 자유롭게 변경하십시오.

도움을 주셔서 감사합니다!


Ninject를 사용하여 로거 인스턴스의 현재 클래스 이름을 다음과 같이 확인합니다.

kernel.Bind<ILogger>().To<NLogLogger>()
  .WithConstructorArgument("currentClassName", x => x.Request.ParentContext.Request.Service.FullName);

NLog 구현의 생성자는 다음과 같습니다.

public NLogLogger(string currentClassName)
{
  _logger = LogManager.GetLogger(currentClassName);
}

이 접근 방식은 다른 IOC 컨테이너에서도 작동해야합니다.


Common.Logging 파사드 또는 Simple Logging Facade를 사용할 수도 있습니다 .

둘 다 ILogger를 검색하기 위해 서비스 로케이터 스타일 패턴을 사용합니다.

솔직히 로깅은 자동 주입에서 거의 또는 전혀 가치가 없다고 생각하는 종속성 중 하나입니다.

로깅 서비스가 필요한 대부분의 클래스는 다음과 같습니다.

public class MyClassThatLogs {
    private readonly ILogger log = Slf.LoggerService.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName);

}

Simple Logging Facade를 활용하여 프로젝트를 log4net에서 NLog로 전환했으며, NLog를 사용하는 내 애플리케이션의 로깅 외에도 log4net을 사용하는 타사 라이브러리의 로깅을 추가했습니다. 즉, 외관이 우리에게 잘 봉사했습니다.

피하기 어려운 한 가지주의 사항은 로깅 프레임 워크 또는 다른 로깅 프레임 워크에 특정한 기능이 손실된다는 것입니다. 아마도 가장 빈번한 예는 커스텀 로깅 수준 일 것입니다.


이는 삽입하려는 로거에 log4net 또는 NLog와 같은 로깅 플랫폼이 제공 될 때 로거 종속성을 삽입하는 방법을 알아 내려는 모든 사람을위한 것입니다. 내 문제는 특정 ILogger의 해상도가 ILogger ( 예 : MyClass). DI / IoC 플랫폼 / 컨테이너는 올바른 ILogger를 어떻게 얻습니까?

글쎄, 나는 Castle과 NInject의 출처를 살펴보고 그들이 어떻게 작동하는지 보았습니다. AutoFac 및 StructureMap도 살펴 보았습니다.

Castle과 NInject는 모두 로깅 구현을 제공합니다. 둘 다 log4net 및 NLog를 지원합니다. Castle은 System.Diagnostics도 지원합니다. 두 경우 모두 플랫폼이 주어진 객체에 대한 종속성을 확인할 때 (예 : 플랫폼이 MyClass를 생성하고 MyClass가 ILogger에 의존하는 경우) 종속성 생성 (ILogger)을 ILogger "제공자"(확인 프로그램이 더 많을 수 있음)에 위임합니다. 일반적인 용어). 그런 다음 ILogger 공급자의 구현은 ILogger의 인스턴스를 실제로 인스턴스화하고 다시 전달한 다음 종속 클래스 (예 : MyClass)에 주입 할 책임이 있습니다. 두 경우 모두 공급자 / 확인자는 종속 클래스 (예 : MyClass)의 유형을 알고 있습니다. 따라서 MyClass가 생성되고 해당 종속성이 해결 될 때 ILogger "resolver"는 클래스가 MyClass임을 알고 있습니다. Castle 또는 NInject 제공 로깅 솔루션을 사용하는 경우 이는 로깅 솔루션 (log4net 또는 NLog를 통해 래퍼로 구현 됨)이 유형 (MyClass)을 가져 오므로 log4net.LogManager.GetLogger () 또는 NLog.LogManager.GetLogger (). (log4net 및 NLog에 대한 구문이 100 % 확실하지는 않지만 아이디어를 얻습니다.)

AutoFac 및 StructureMap은 로깅 기능을 제공하지 않지만 (적어도보고 보면 알 수 있음) 사용자 지정 해결 프로그램을 구현하는 기능을 제공하는 것 같습니다. 따라서 고유 한 로깅 추상화 계층을 작성하려는 경우 해당하는 사용자 지정 해결 프로그램을 작성할 수도 있습니다. 이렇게하면 컨테이너가 ILogger를 확인하려고 할 때 해결 프로그램을 사용하여 올바른 ILogger를 얻고 현재 컨텍스트에 액세스 할 수 있습니다 (즉, 현재 충족되는 개체의 종속성-ILogger에 종속 된 개체). 개체의 유형을 가져 오면 ILogger 생성을 현재 구성된 로깅 플랫폼 (인터페이스 뒤에서 추상화했으며 리졸버를 작성 했음)에 위임 할 준비가되었습니다.

따라서 내가 필요하다고 생각했지만 이전에 완전히 파악하지 못한 몇 가지 핵심 사항은 다음과 같습니다.

  1. 궁극적으로 DI 컨테이너는 어떤 로깅 플랫폼을 사용할 것인지 알고 있어야합니다. 일반적으로 이것은 "ILogger"가 로깅 플랫폼에 특정한 "resolver"에 의해 해결되도록 지정하여 수행됩니다 (따라서 Castle에는 log4net, NLog 및 System.Diagnostics "resolvers"가 있음). 사용할 리졸버의 사양은 구성 파일을 통해 또는 프로그래밍 방식으로 수행 할 수 있습니다.

  2. 확인자는 종속성 (ILogger)이 확인되는 컨텍스트를 알아야합니다. 즉, MyClass가 생성되었고 ILogger에 종속 된 경우 확인자가 올바른 ILogger를 생성하려고 할 때 확인자 (확인자)가 현재 유형 (MyClass)을 알아야합니다. 이렇게하면 리졸버는 기본 로깅 구현 (log4net, NLog 등)을 사용하여 올바른 로거를 가져올 수 있습니다.

이러한 점은 DI / IoC 사용자에게 분명 할 수 있지만 이제 막 들어가고 있으므로 머리를 돌리는 데 시간이 걸립니다.

내가 아직 알아 내지 못한 한 가지는 MEF에서 이와 같은 것이 어떻게 가능할지 여부입니다. 인터페이스에 의존하는 객체를 갖고 MEF가 객체를 생성 한 후 인터페이스 / 종속성이 해결되는 동안 내 코드를 실행하도록 할 수 있습니까? 따라서 다음과 같은 클래스가 있다고 가정합니다.

public class MyClass
{
  [Import(ILogger)]
  public ILogger logger;

  public MyClass()
  {
  }

  public void DoSomething()
  {
    logger.Info("Hello World!");
  }
}

When MEF is resolving the imports for MyClass, can I have some of my own code (via an attribute, via an extra interface on the implementation of ILogger, elsewhere???) execute and resolve the ILogger import based on the fact that it is MyClass that is currently in context and give back a (potentially) different ILogger instance than would be retrieved for YourClass? Do I implement some sort of MEF Provider?

At this point, I still don't know about MEF.


I see you figured out your own answer :) But, for folks in the future that have this question about how to NOT tie yourself to a particular logging framework, this library: Common.Logging helps with exactly that scenario.


I made my custom ServiceExportProvider, by the provider I register Log4Net logger for dependency injection by MEF. As result you can use logger for different kinds of injections.

Example of injections:

[Export]
public class Part
{
    [ImportingConstructor]
    public Part(ILog log)
    {
        Log = log;
    }

    public ILog Log { get; }
}

[Export(typeof(AnotherPart))]
public class AnotherPart
{
    [Import]
    public ILog Log { get; set; }
}

Example of usage:

class Program
{
    static CompositionContainer CreateContainer()
    {
        var logFactoryProvider = new ServiceExportProvider<ILog>(LogManager.GetLogger);
        var catalog = new AssemblyCatalog(typeof(Program).Assembly);
        return new CompositionContainer(catalog, logFactoryProvider);
    }

    static void Main(string[] args)
    {
        log4net.Config.XmlConfigurator.Configure();
        var container = CreateContainer();
        var part = container.GetExport<Part>().Value;
        part.Log.Info("Hello, world! - 1");
        var anotherPart = container.GetExport<AnotherPart>().Value;
        anotherPart.Log.Fatal("Hello, world! - 2");
    }
}

Result in console:

2016-11-21 13:55:16,152 INFO  Log4Mef.Part - Hello, world! - 1
2016-11-21 13:55:16,572 FATAL Log4Mef.AnotherPart - Hello, world! - 2

ServiceExportProvider implementation:

public class ServiceExportProvider<TContract> : ExportProvider
{
    private readonly Func<string, TContract> _factoryMethod;

    public ServiceExportProvider(Func<string, TContract> factoryMethod)
    {
        _factoryMethod = factoryMethod;
    }

    protected override IEnumerable<Export> GetExportsCore(ImportDefinition definition, AtomicComposition atomicComposition)
    {
        var cb = definition as ContractBasedImportDefinition;
        if (cb?.RequiredTypeIdentity == typeof(TContract).FullName)
        {
            var ce = definition as ICompositionElement;
            var displayName = ce?.Origin?.DisplayName;
            yield return new Export(definition.ContractName, () => _factoryMethod(displayName));
        }
    }
}

참고URL : https://stackoverflow.com/questions/3452318/dependency-injection-and-named-loggers

반응형