【问题标题】:Designing a better logger class [closed]设计一个更好的记录器类[关闭]
【发布时间】:2013-02-27 18:19:50
【问题描述】:

您能批评一下下面的记录器类吗?可以在多线程网络环境中使用吗?如果没有,我该如何改进它? WriteToLog 方法中的锁定或 FlushLog 方法中的多线程有什么问题吗?

public class Logger
{
    private static Logger instance;
    private static Queue<LogData> logQueue;
    private static string logDir = HttpContext.Current.Server.MapPath(ConfigurationManager.AppSettings["LogDirectory"]);
    private static string logFile = "log.txt";
    private static int maxLogAge = Int32.Parse(ConfigurationManager.AppSettings["LogMaxAge"]);
    private static int queueSize = Int32.Parse(ConfigurationManager.AppSettings["LogQueueSize"]);
    private static DateTime LastFlushed = DateTime.Now;

    private Logger() { }

    public static Logger Instance
    {
        get
        {
            if (instance == null)
            {
                instance = new Logger();
                logQueue = new Queue<LogData>();
            }
            return instance;
        }
    }

    public void WriteToLog(string message)
    {
        lock (logQueue)
        {
            LogData logEntry = new LogData(message);
            logQueue.Enqueue(logEntry);

            if (logQueue.Count >= queueSize || DoPeriodicFlush())
            {
                FlushLog();
            }
        }            
    }

    private bool DoPeriodicFlush()
    {
        TimeSpan logAge = DateTime.Now - LastFlushed;
        if (logAge.TotalSeconds >= maxLogAge)
        {
            LastFlushed = DateTime.Now;
            return true;
        }
        else
        {
            return false;
        }
    }

    private void FlushLog()
    {
        System.Threading.ThreadPool.QueueUserWorkItem(q => {
            while (logQueue.Count > 0)
            {
                LogData entry = logQueue.Dequeue();
                string logPath = logDir + entry.LogDate + "_" + logFile;

                using (System.IO.StreamWriter file = new System.IO.StreamWriter(logPath, true, System.Text.Encoding.UTF8))
                {
                    file.WriteLine(string.Format("{0}\t{1}", entry.LogTime, entry.Message));
                }
            }
        });
    }

    ~Logger()
    {
        FlushLog();
    }
}

public class LogData
{
    public string Message { get; set; }
    public string LogTime { get; set; }
    public string LogDate { get; set; }

    public LogData(string message)
    {
        Message = message;
        LogDate = DateTime.Now.ToString("yyyy-MM-dd");
        LogTime = DateTime.Now.ToString("HH:mm:ss.fff tt");
    }
}

提前致谢,

【问题讨论】:

  • 不要重新发明轮子;使用 log4net。
  • 这个特殊的轮子已经被发明了多次。我建议您看一下可用的几个选项之一。
  • 有了这样的担忧,您应该使用 log4net 之类的东西,所有这些问题都已解决:logging.apache.org/log4net
  • 你的代码是相当非线程安全的。
  • 如果你还没有把它放在Code Review,可能会引起更多的兴趣。

标签: c# multithreading design-patterns locking


【解决方案1】:

此代码不是线程安全的。您在添加到队列时同步(锁定),但从代码中删除的代码不会锁定队列,并且将始终在后台线程上运行,这将导致潜在的竞争条件。

如果您真的必须编写自己的日志记录,我至少会考虑使用ConcurrentQueue&lt;T&gt; 以避免在添加时需要锁定。 BlockingCollection&lt;T&gt; 会让这件事变得更简单,因为您可以通过线程调用 GetConsumingEnumerable() 来处理添加的项目。

话虽如此,日志记录已经被处理了很多次,并且处理得很好。你最好使用新的Semantic Logging Application Block(来自 P&P)甚至log4net

【讨论】:

  • 感谢您的回答!我会看看你的建议。我什至不知道你提到的课程。
  • @anilca 太棒了 - 话虽如此,对于日志记录而言,使用预制解决方案实际上几乎总是一个更好的选择。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-23
  • 2010-11-14
  • 1970-01-01
  • 1970-01-01
  • 2011-05-01
相关资源
最近更新 更多