허당 레몬도리

I was doing some work for a client recently that had all sorts of issues with logging due to some old code from the original developers. It had multiple threads all trying to write to the log file simultaneously. Needless to say loads of logging just fell by the way side as the file could not be locked for writing.

The Singleton class below implements an internal queue that stacks up Log Messages and when 1 of 2 thresholds is reached (number of items in queue or queue age) the messages all get flushed to disk.

Calling the class is done with 2 lines of code:

LogWriter writer = LogWriter.Instance;
writer.WriteToLog(message);

Full source code below the break

 

using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;

namespace BondiGeek.Logging
{
    /// <summary>
    /// A Logging class implementing the Singleton pattern and an internal Queue to be flushed perdiodically
    /// </summary>
    public class LogWriter
    {
        private static LogWriter instance;
        private static Queue<Log> logQueue;
        private static string logDir = <Path to your Log Dir or Config Setting>;
        private static string logFile = <Your Log File Name or Config Setting>;
        private static int maxLogAge = int.Parse(<Max Age in seconds or Config Setting>);
        private static int queueSize = int.Parse(<Max Queue Size or Config Setting);
        private static DateTime LastFlushed = DateTime.Now;

        /// <summary>
        /// Private constructor to prevent instance creation
        /// </summary>
        private LogWriter() { }

        /// <summary>
        /// An LogWriter instance that exposes a single instance
        /// </summary>
        public static LogWriter Instance
        {
            get
            {
                // If the instance is null then create one and init the Queue
                if (instance == null)
                {
                    instance = new LogWriter();
                    logQueue = new Queue<Log>();
                }
                return instance;
            }
        }

        /// <summary>
        /// The single instance method that writes to the log file
        /// </summary>
        /// <param name="message">The message to write to the log</param>
        public void WriteToLog(string message)
        {
            // Lock the queue while writing to prevent contention for the log file
            lock (logQueue)
            {
                // Create the entry and push to the Queue
                Log logEntry = new Log(message);
                logQueue.Enqueue(logEntry);

                // If we have reached the Queue Size then flush the Queue
                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;
            }
        }

        /// <summary>
        /// Flushes the Queue to the physical log file
        /// </summary>
        private void FlushLog()
        {
            while (logQueue.Count > 0)
            {
                Log entry = logQueue.Dequeue();
                string logPath = logDir + entry.LogDate + "_" + logFile;

        // This could be optimised to prevent opening and closing the file for each write
                using (FileStream fs = File.Open(logPath, FileMode.Append, FileAccess.Write))
                {
                    using (StreamWriter log = new StreamWriter(fs))
                    {
                        log.WriteLine(string.Format("{0}\t{1}",entry.LogTime,entry.Message));
                    }
                }
            }            
        }
    }

    /// <summary>
    /// A Log class to store the message and the Date and Time the log entry was created
    /// </summary>
    public class Log
    {
        public string Message { get; set; }
        public string LogTime { get; set; }
        public string LogDate { get; set; }

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

Enjoy!

BondiGeek

 

출처 : http://blog.bondigeek.com/2011/09/08/a-simple-c-thread-safe-logging-class/

 

profile

허당 레몬도리

@LemonDory

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!