为什么要使用singleton进行应用程序日志

时间:2012-03-16 02:36:33

标签: c# singleton

关于单身模式的缺点我是reading。在许多论坛中建议的有效使用单例是Logging应用程序。我想知道为什么这是模式的有效使用。我们不是在整个应用程序中维护内存中的状态信息吗?

为什么不使用一个函数:

class Logger
{
    public static void Log(string message)
    {
         //Append to file

    }
}

5 个答案:

答案 0 :(得分:4)

回答“为什么不使用函数”:此代码在多线程日志记录中无效。如果两个线程尝试写入同一个文件,则会抛出异常。这就是使用单例进行日志记录的好处。在这个解决方案中,我们有一个线程安全的单例容器,其他线程安全地将消息(日志)推送到容器中。容器(总是一个线程安全的队列)将消息/日志逐个写入文件/ db / etc。

答案 1 :(得分:2)

最好声明interface:

interface ILogger
{
    public void Log(string message);
}

然后实现特定类型的记录器

class FileLogger : ILogger
{
    public void Log(string message)
    {
         //Append to file
    }
}

class EmptyLogger : ILogger
{
    public void Log(string message)
    {
         //Do nothing
    }
}

并在必要时注入。您将在测试中注入EmptyLogger。使用singleton会使测试变得更难,因为你也必须在测试中保存到文件。如果要测试类是否生成正确的日志条目,可以使用mock和define expected。

关于注射:

public class ClassThatUsesLogger
{
    private ILogger Logger { get; set; }
    public ClassThatUsesLogger(ILogger logger) { Logger = logger }
}

ClassThatUsesLogger在生产代码中使用FileLogger:

classThatUsesLogger = new ClassThatUsesLogger(new FileLogger());

在测试中需要EmptyLogger:

classThatUsesLogger = new ClassThatUsesLogger(new EmptyLogger());

您在不同的场景中注入不同的记录器。有更好的方法来处理注射,但你必须做一些阅读。

修改

请记住,您仍然可以像其他人建议的那样在代码中使用单例,但是您应该隐藏它在接口后面的用法,以放松类和特定日志记录实现之间的依赖关系。

答案 2 :(得分:1)

当你询问内存中剩余的状态信息时,我不确定你指的是什么,但是有一个理由支持单例而不是静态的日志记录是单例仍然允许你同时使用 (1)抽象项目(ILogger)和
(2)通过练习依赖注入来坚持依赖倒置原则

您不能将静态日志记录方法作为依赖项注入(除非您想在任何地方传递Action<string>之类的东西),但是可以传递单个对象,并且您可以传递编写单元测试时不同的实现,如NullLogger

答案 3 :(得分:1)

在大多数情况下,不建议使用Singleton设计模式,因为它是一种全局状态,隐藏了依赖关系(使API不那么明显)并且难以测试。

记录不是其中一种情况。这是因为日志记录不会影响代码的执行。也就是说,如下所述:http://googletesting.blogspot.com/2008/08/root-cause-of-singletons.html

  

无论是否给定,您的应用程序都不会有任何不同   记录器已启用。这里的信息以一种方式流动:从你的   应用到记录器中。

你可能仍然不想使用Singleton模式。至少不是这样。这是因为没有理由强制使用记录器的单个实例。如果您想要两个日志文件,或两个表现不同且用于不同目的的记录器,该怎么办?

因此,您真正想要的记录器是在需要时随时随地轻松访问它。基本上,日志记录是一种特殊情况,最好的方法是将其全局访问。

  1. 简单的方法是在应用程序中只包含一个包含logger实例的静态字段:

    public final static LOGGER = new Logger();
    
  2. 或者,如果您的记录器是由工厂创建的:

    public final static LOGGER = new LoggerFactory().getLogger("myLogger");
    
  3. 或者,如果您的记录器是由DI容器创建的:

    public final static LOGGER = Container.getInstance("myLogger");
    
  4. 您可以通过配置文件使您的记录器实现可配置,您可以将其设置为&#34; mode = test&#34;当你进行测试时,这些情况下的记录器可以相应地运行,或者不记录,或者记录到控制台。

    public final static LOGGER = new Logger("logConfig.cfg");
    
  5. 您还可以在运行时配置记录器的行为。因此,在运行测试时,您只需将其设置为:LOGGER.setMode(&#34; test&#34;);

  6. 或者,如果您没有进行静态最终决定,您只需在测试设置中用测试记录器或模拟记录器替换静态LOGGER。

  7. 你可以做的稍微有些看似接近单身人士模式,但不完全是:

    public class Logger
    {
        private static Logger default;
        public static getDefault()
        {
            if(default == null)
            {
                throw new RuntimeException("No default logger was specified.");
            }
            return default;
        }
    
        public static void setDefault(Logger logger)
        {
            if(default != null)
            {
                throw new RuntimeException("Default logger already specified.");
            }
            default = logger;
        }
    
        public Logger()
        {
        }
    }
    
    public static void main(String [] args)
    {
        Logger.setDefault(new Logger());
    }
    
    @Test
    public void myTest()
    {
        Logger.setDefault(new MockedLogger());
    
        // ... test stuff
    }
    

答案 4 :(得分:0)

单独的记录器实现允许您轻松控制将日志记录刷新到磁盘或数据库的频率。如果您有多个记录器实例,那么他们可能都会同时尝试写入,这可能会导致冲突或性能问题。单身人士允许对其进行管理,以便您只在安静的时间内冲洗到商店,并且所有邮件都按顺序保存。