将记录器作为单身人员是一种好习惯吗?

Gie*_*ius 75 .net c# logging singleton dependency-injection

我有习惯将记录器传递给构造函数,如:

public class OrderService : IOrderService {
     public OrderService(ILogger logger) {
     }
}
Run Code Online (Sandbox Code Playgroud)

但这很烦人,所以我已经使用了它一段时间了:

private ILogger logger = NullLogger.Instance;
public ILogger Logger
{
    get { return logger; }
    set { logger = value; }
}
Run Code Online (Sandbox Code Playgroud)

这也很烦人 - 它不干,我需要在每个班级重复这个.我可以使用基类,但是再次 - 我正在使用Form类,所以需要FormBase等等.所以我认为,暴露ILogger的单例会有什么不利因素,所以我们知道在哪里得到logger:

    Infrastructure.Logger.Info("blabla");
Run Code Online (Sandbox Code Playgroud)

更新:正如Merlyn正确注意到的那样,我应该提一下,在第一和第二个例子中,我正在使用DI.

Dan*_*ose 37

我在我的依赖注入容器中放置了一个logger实例,然后将记录器注入需要它的类中.

  • 你可以再详细一点吗?因为我认为这是我在1和2个代码示例中所做的/做的事情? (8认同)
  • "使用"类看起来与您已有的基本相同.但是,实际值不必手动提供给您的类,因为DI容器会为您执行此操作.与使用静态单例记录器相比,这使得在测试中更换记录器变得更加容易. (2认同)
  • +1恕我直言,这是唯一正确的方法. (2认同)
  • 虽然这是正确的答案(IMO),但并非正在进行投票,因为OP已经表示他们正在这样做.另外,使用这个和使用Singleton的理由是什么?OP对重复代码有什么问题 - 这个答案是如何解决的? (2认同)

Mer*_*ham 34

这也很烦人 - 它不是干的

确实如此.但是,只有你可以做的事情才能实现贯穿各种类型的贯穿各领域的关注.您必须在任何地方使用记录器,因此您必须拥有这些类型的属性.

所以让我们看看我们能做些什么.

独生子

单身人士很可怕<flame-suit-on>.

我建议您坚持使用属性注入,因为您已经完成了第二个示例.这是你无需借助魔法就可以做到的最好的因素.拥有显式依赖关系比通过单例隐藏它更好.

但是如果单身人士为你节省了大量时间,包括你将不得不做的所有重构(水晶球时间!),我想你可以和他们一起生活.如果有一个单身人士的用途,这可能是它.记住的成本,如果你曾经想改变你的思想会约高,因为它得到.

如果你这样做,看看别人的答案使用Registry模式(见说明书),而那些注册(可复位)单工厂,而不是一个单独的记录器实例.

除了妥协之外,还有其他替代方案可以正常工作,因此您应该首先检查它们.

Visual Studio代码段

您可以使用Visual Studio代码片段来加速重复代码的输入.您将能够键入类似的内容loggertab,代码将神奇地为您显示.

使用AOP干掉

您可以使用像PostSharp这样的面向方面编程(AOP)框架来自动生成其中的一些,从而消除一些属性注入代码.

当你完成时,它可能看起来像这样:

[InjectedLogger]
public ILogger Logger { get; set; }
Run Code Online (Sandbox Code Playgroud)

您还可以使用他们的方法跟踪示例代码来自动跟踪方法入口和出口代码,这可能无需一起添加一些记录器属性.您可以在类级别或命名空间宽度应用该属性:

[Trace]
public class MyClass
{
    // ...
}

// or

#if DEBUG
[assembly: Trace( AttributeTargetTypes = "MyNamespace.*",
    AttributeTargetTypeAttributes = MulticastAttributes.Public,
    AttributeTargetMemberAttributes = MulticastAttributes.Public )]
#endif
Run Code Online (Sandbox Code Playgroud)

  • `</ flame-suit-on>`我不知道你是如何在火焰服中生活5年但我希望这会有所帮助. (22认同)
  • 单身人士+1非常可怕.尝试使用具有多个类加载器的单例,即一个应用程序服务器环境,其中调度程序是客户端,我遇到了双重日志记录的可怕例子(但不像来自某个C++程序员告诉我的错误位置的日志语句那么可怕) (3认同)

sll*_*sll 23

好问题.我相信大多数项目记录器都是单身人士.

我想到了一些想法:

  • 使用ServiceLocator(或其他依赖注入容器,如果你已经使用任何),它允许你跨服务/类共享记录器,这样你就可以实例化记录器甚至多个不同的记录器并通过ServiceLocator共享,这显然是一个单例某种控制倒置.这种方法为记录器实例化和初始化过程提供了很大的灵活性.
  • 如果你需要记录几乎无处不在-实现扩展方法Object类型,这样每一类将能够调用记录器的方法,例如LogInfo(),LogDebug(),LogError()

  • ServiceLocator不是DI Container.它被认为是反模式. (6认同)

And*_*bel 14

单身是一个好主意.更好的想法是使用注册表模式,它可以更好地控制实例化.在我看来,单例模式太接近全局变量.通过注册表处理对象创建或重用,将来可以对实例化规则进行更改.

Registry本身可以是一个静态类,提供访问日志的简单语法:

Registry.Logger.Info("blabla");
Run Code Online (Sandbox Code Playgroud)

  • 我第一次遇到注册表模式.说它基本上是所有全局变量和功能放在一个伞下是否过于简单化? (6认同)
  • Registry和ServiceLocator几乎是一样的.大多数IoC框架的核心是Registry的实现. (5认同)

jga*_*fin 9

简单的单身人士不是一个好主意.这使得更换记录器变得困难.我倾向于为记录器使用过滤器(一些"嘈杂"类可能只记录警告/错误).

我使用单例模式结合记录器工厂的代理模式:

public class LogFactory
{
    private static LogFactory _instance;

    public static void Assign(LogFactory instance)
    {
        _instance = instance;
    }

    public static LogFactory Instance
    {
        get { _instance ?? (_instance = new LogFactory()); }
    }

    public virtual ILogger GetLogger<T>()
    {
        return new SystemDebugLogger();
    }
}
Run Code Online (Sandbox Code Playgroud)

这允许我创建一个FilteringLogFactory或只是SimpleFileLogFactory不更改任何代码(因此遵守开放/封闭原则).

样品扩展

public class FilteredLogFactory : LogFactory
{
    public override ILogger GetLogger<T>()
    {
        if (typeof(ITextParser).IsAssignableFrom(typeof(T)))
            return new FilteredLogger(typeof(T));

        return new FileLogger(@"C:\Logs\MyApp.log");
    }
}
Run Code Online (Sandbox Code Playgroud)

并使用新工厂

// and to use the new log factory (somewhere early in the application):
LogFactory.Assign(new FilteredLogFactory());
Run Code Online (Sandbox Code Playgroud)

在您的课程中应该记录:

public class MyUserService : IUserService
{
    ILogger _logger = LogFactory.Instance.GetLogger<MyUserService>();

    public void SomeMethod()
    {
        _logger.Debug("Welcome world!");
    }
}
Run Code Online (Sandbox Code Playgroud)