zoukankan      html  css  js  c++  java
  • 不用Log4net用并发队列ConcurrentQueue自己实现写日志的组件(C#)

    项目中要实现写日志Logging,但客户不想用Log4net,说不想用任何第三方组件,好吧,我们自己写一个简单的记日志的组件吧。但要实现下面的几个功能:

    • 可以用FileAppender的方式记日志
    • 线程安全,支持多个线程同时Append同一日志文件
    • 支持FileRolling,设置大小自动分卷

    我们知道是Log4net是线程安全的,虽然也有人说不是进程安全的。我们自己实现,要支持多个线程同时Append同一日志文件,需要加锁。这里我们用并发队列ConcurrentQueue来实现(.NET Framework 4.0 及以上),思路就是当线程请求写日志,首先把请求放入队列,然后批量一次写入。实现最高性能。由于写入的瓶颈在IO上,而文件无需频繁打开,异步写入的方式自然性能高很多。缺点是机器掉电的话,队列里面的日志请求丢失。具体实现是当请求写日志的时候队列入队Queue.Enqueue,然后每隔几秒钟批量写入日志,出队(while (Queue.TryDequeue(out entry)))。

    ILogger.cs
    View Code
     1 namespace YourCompany.Logging
     2 {
     3     public interface ILogger
     4     {
     5         // write custom log
     6         void Write(string serviceName, string loggingIdentity, LogMessageDirection direction, string message);
     7         void Write(string serviceName, string loggingIdentity, string message);
     8         // write general log
     9         void GlobalWrite(string message, LogMessageCategory category);
    10     }
    11 
    12     public enum LogMessageCategory { Info, Success, Warning, Error };
    13     public enum LogMessageDirection { None, InBound, OutBound };
    14     public enum LogMessageOption { None, LogMessageAsRaw, LogMessageAsText, LogMessageAsRawAndText };
    15 }

    Logger.cs

    View Code
    using System;
    using System.Collections.Concurrent;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Configuration;
    using System.Diagnostics;
    using System.IO;
    using System.Linq;
    using System.Runtime.Remoting;
    using System.Runtime.Remoting.Messaging;
    using System.Text;
    using System.Threading;

    namespace YourCompany.Logging
    {
        public sealed class Logger
        {
            internal static ILogger Current
            {
                get
                {
                    return OperationContext.Current.LogContext;
                }
            }
            
            // Every period of 15s, flush logs entry in queue to really write to file.
            
    // Do not dispose the Timer here.
            private static System.Threading.Timer LoggingQueueTimer = new Timer(timerCallback, null0 /* start immediately*/,
                       Int32.Parse(ConfigurationManager.AppSettings[Constants.Configuration.WritLogQueueCheckPeriod]));

            // The concurrent logs writing queue, logs will actually be written until DoGlobalWrite() method is called or timer checker found items in queue.
            internal static ConcurrentQueue<LogEntry> LoggingQueue = new ConcurrentQueue<LogEntry>();

            private static void timerCallback(object o)
            {
                DoGlobalWrite();
            }

            internal static void Write(LogMessageDirection direction, IMessage message)
            {
                Write(direction, new IMessage[] { message });
                return;
            }

            internal static void Write(LogMessageDirection direction, IMessage[] messages)
            {
                if (OperationContext.Current != null)
                {
                    try
                    {
                        switch (OperationContext.Current.LoggingOption)
                        {
                            case LogMessageOption.LogMessageAsText:
                                if (messages.Length > 1)
                                {
                                    StringBuilder builder = new StringBuilder();
                                    foreach (IMessage message in messages)
                                    {
                                        builder.Append(message.ToString());
                                    }
                                    Current.Write(OperationContext.Current.ServiceName, OperationContext.Current.Identity, direction, builder.ToString());
                                }
                                else
                                {
                                    Current.Write(OperationContext.Current.ServiceName, OperationContext.Current.Identity, direction, messages[0].ToString());
                                }
                                break;
                            case LogMessageOption.LogMessageAsRaw:
                                if (messages.Length > 1)
                                {
                                    using (MemoryStream buffer = new MemoryStream())
                                    {
                                        foreach (IMessage message in messages)
                                        {
                                            byte[] data = message.ToArray();
                                            buffer.Write(data, 0, data.Length);
                                        }
                                        Current.Write(OperationContext.Current.ServiceName, OperationContext.Current.Identity, direction, BitConverter.ToString(buffer.ToArray()).Replace("-"string.Empty));
                                    }
                                }
                                else
                                {
                                    Current.Write(OperationContext.Current.ServiceName, OperationContext.Current.Identity, direction, BitConverter.ToString(messages[0].ToArray()).Replace("-"string.Empty));
                                }
                                break;
                            case LogMessageOption.LogMessageAsRawAndText:
                                if (messages.Length > 1)
                                {
                                    using (MemoryStream buffer = new MemoryStream())
                                    {
                                        foreach (IMessage message in messages)
                                        {
                                            byte[] data = message.ToArray();
                                            buffer.Write(data, 0, data.Length);
                                        }
                                        Current.Write(OperationContext.Current.ServiceName, OperationContext.Current.Identity, direction, BitConverter.ToString(buffer.ToArray()).Replace("-"string.Empty));
                                    }

                                    StringBuilder builder = new StringBuilder();
                                    foreach (IMessage message in messages)
                                    {
                                        builder.Append(message.ToString());
                                    }
                                    Current.Write(OperationContext.Current.ServiceName, OperationContext.Current.Identity, direction, builder.ToString());
                                }
                                else
                                {
                                    Current.Write(OperationContext.Current.ServiceName, OperationContext.Current.Identity, direction, BitConverter.ToString(messages[0].ToArray()).Replace("-"string.Empty));
                                    Current.Write(OperationContext.Current.ServiceName, OperationContext.Current.Identity, direction, messages[0].ToString());
                                }
                                break;
                            default:
                                // nop
                                break;
                        }
                    }
                    catch
                    {
                    }
                }
                return;
            }

            public static void Write(string message)
            {
                if (OperationContext.Current != null)
                {
                    try
                    {
                        if (OperationContext.Current.LoggingOption != LogMessageOption.None)
                        {
                            Current.Write(OperationContext.Current.ServiceName, OperationContext.Current.Identity, message);
                        }
                    }
                    catch
                    {
                    }
                }
                return;
            }

            public static void Write(string format, params object[] arg)
            {
                if (OperationContext.Current != null)
                {
                    try
                    {
                        if (OperationContext.Current.LoggingOption != LogMessageOption.None)
                        {
                            Current.Write(OperationContext.Current.ServiceName, OperationContext.Current.Identity, string.Format(format, arg));
                        }
                    }
                    catch
                    {
                    }
                }
                return;
            }

            internal static void Write(LogMessageDirection direction, string format, params object[] arg)
            {
                if (OperationContext.Current != null)
                {
                    try
                    {
                        if (OperationContext.Current.LoggingOption != LogMessageOption.None)
                        {
                            Current.Write(OperationContext.Current.ServiceName, OperationContext.Current.Identity, direction, string.Format(format, arg));
                        }
                    }
                    catch
                    {
                    }
                }
                return;
            }

            public static void DoGlobalWrite()
            {
                if (OperationContext.Current != null &&
                    OperationContext.Current.GlobalTraceIsOn)
                {
                    try
                    {
                        LogEntry entry = null;

                        while (LoggingQueue.TryDequeue(out entry))
                        {
                            if(entry != null)
                            {
                                //Current.GlobalWrite(string.Format("From id:{0}. {1}", OperationContext.Current.Identity, message));
                                Current.GlobalWrite(entry.Message, entry.Category);
                            }
                        }
                    }
                    catch
                    {
    #if DEBUG
                        throw;
    #endif
                    }
                }
                return;

             
            }

            public static void GlobalWrite(string message, LogMessageCategory category)
            {
                try
                {
                    //If app.config / web.config settings WriteWarningLogs=False, then don't write warning entry 
                    
    // to get a better performance if there're a large amout of warnings in the system.
                    if (category == LogMessageCategory.Warning)
                    {
                        var cfg = ConfigurationManager.AppSettings[Constants.Configuration.WriteWarningLogs];

                        if (!String.IsNullOrEmpty(cfg) && !Boolean.Parse(cfg))
                            return;
                    }

                    //If app.config / web.config settings WriteInfoLogs=False, then don't write warning entry 
                    
    // to get a better performance if there're a large amout of warnings in the system.
                    if (category == LogMessageCategory.Info)
                    {
                        var cfg = ConfigurationManager.AppSettings[Constants.Configuration.WriteInfoLogs];

                        if (!String.IsNullOrEmpty(cfg) && !Boolean.Parse(cfg))
                            return;
                    }

                    LoggingQueue.Enqueue(new LogEntry(String.Empty, String.Empty, LogMessageDirection.None, message,
                                                      category));
                }
                catch
                {
    #if DEBUG
                    throw;
    #endif
                }

                return;
            }

            public static void GlobalWrite(string format, params object[] arg)
            {
                if (OperationContext.Current != null &&
                    OperationContext.Current.GlobalTraceIsOn)
                {
                    try
                    {
                        //Current.GlobalWrite(string.Format("From id:{0}. {1}", OperationContext.Current.Identity, string.Format(format, arg)));
                        Current.GlobalWrite(string.Format(format, arg), LogMessageCategory.Info);

                    }
                    catch
                    {
                    }
                }
                return;
            }
        }

        #region "Internal Methods"
        public sealed class OperationContext : IDisposable
        {
            void IDisposable.Dispose()
            {
                using (this.MessageBuffer) { }
                return;
            }

            public OperationContext(IConfigurationSite site, bool isRemotingContext)
            {
                this.SerialHeaderAvailable = false;
                this.Site = site;
                this.IsRemotingContext = isRemotingContext;
                this.Identity = site.Identity;
                this.ServiceName = site.ServiceName;
                this.ProtocolEncoding = site.ProtocolEncoding;
                this.LoggingOption = site.LoggingOption;
                this.MessageBuffer = new MemoryStream();

                if (isRemotingContext)
                {
                    this.ChannelContext = (CallContext)RemotingServices.Unmarshal(site.CallContextObj as ObjRef);
                }
                else
                {
                    this.ChannelContext = site.CallContextObj as CallContext;
                }

                if (site.LoggingEnabled)
                {
                    if (isRemotingContext)
                    {
                        this.LogContext = (ILogger)RemotingServices.Unmarshal(site.LogContextObj as ObjRef);
                    }
                    else
                    {
                        this.LogContext = site.LogContextObj as ILogger;
                    }
                }
                else
                {
                    this.LogContext = null;
                }
                this.QueueResponses = new Queue<IMessage>();
                this.Notifiers = this.Site.Notifiers;
                this.MessageFilter = null;

                this.GlobalTraceIsOn = site.GlobalTraceIsOn;

            }

            internal bool IsCancelSignaled
            {
                get { return this.Site.IsCancelSignaled; }
            }

            string sponsorGroup;
            internal string SponsorGroup
            {
                get { return this.sponsorGroup; }
                set
                {
                    this.sponsorGroup = value;
                    this.Site.SponsorName = value;
                }
            }

            internal string SponsorName
            {
                get;
                set;
            }

            string callerIdentity;
            internal string CallerIdentity
            {
                get { return this.callerIdentity; }
                set
                {
                    this.callerIdentity = value;
                    this.Site.CallerIdentity = value;
                }
            }

            internal string TID
            {
                get;
                set;
            }

            internal string SerialNo
            {
                get;
                set;
            }

            internal CallChangedEventArg CreateCallChangedEventArg(CallChangedState state, string message)
            {
                CallChangedEventArg arg = new CallChangedEventArg(state, message);
                FillDefaultValues(arg);
                return arg;
            }

            internal EventArg CreateEventArg()
            {
                EventArg arg = new EventArg();
                FillDefaultValues(arg);
                return arg;
            }

            internal void FillDefaultValues(EventArg arg)
            {
                // arg.ProtocolKind = this.ChannelContext.ProtocolKind;
                arg.ServiceName = this.ServiceName;
                arg.SponsorName = string.IsNullOrEmpty(this.SponsorName) ? OperationContext.CompanyName : this.SponsorName;
                arg.CallerIdentity = this.CallerIdentity;
                arg.TID = this.TID;
                arg.SerialNo = this.SerialNo;
                return;
            }

            internal readonly IConfigurationSite Site;
            internal readonly string Identity;
            internal readonly bool IsRemotingContext;
            internal readonly string ServiceName;
            internal readonly CallContext ChannelContext;
            internal readonly ILogger LogContext;
            internal Encoding ProtocolEncoding;
            internal readonly LogMessageOption LoggingOption;
            internal MemoryStream MessageBuffer;
            internal bool SerialHeaderAvailable;
            internal Func<IMessage, bool> MessageFilter;
            internal readonly Queue<IMessage> QueueResponses;
            internal ReadOnlyCollection<object> Notifiers;
            internal bool GlobalTraceIsOn;

            //[ThreadStatic]
            public static OperationContext Current;

            internal static string CompanyName = string.Empty;
        };

        [Serializable]
        public class CallChangedEventArg : EventArg
        {
            public readonly CallChangedState State;
            public readonly string Message;

            public CallChangedEventArg(CallChangedState state, string message)
            {
                this.State = state;
                this.Message = message;
            }
        }

        [Flags]
        public enum CallChangedState
        {
            None = 0,
            Connected = 1,
            Disconnected = 2,
            Success = 4,
            Error = 8
        };

        [Serializable]
        public class EventArg
        {
            string serviceName = string.Empty;
            public string ServiceName
            {
                get { return this.serviceName; }
                internal set { this.serviceName = value; }
            }

            string sponsorName = string.Empty;
            public string SponsorName
            {
                get { return this.sponsorName; }
                internal set { this.sponsorName = value; }
            }

            string callerIdentity = string.Empty;
            public string CallerIdentity
            {
                get { return this.callerIdentity; }
                internal set { this.callerIdentity = value; }
            }

            string tid = string.Empty;
            public string TID
            {
                get { return this.tid; }
                internal set { this.tid = value; }
            }

            string sn = string.Empty;
            public string SerialNo
            {
                get { return this.sn; }
                internal set { this.sn = value; }
            }

        }

        public class ConfigurationSite : IConfigurationSite
        {
            public string OutputPath { getset; }

            public string SponsorName { getset; }

            public string CallerIdentity { getset; }

            public string ConnectionString { getset; }

            public bool GlobalTraceIsOn { getset; }

            public string ServiceName { getset; }

            public string Identity { getset; }

            public object CallContextObj { getset; }

            public object LogContextObj { getset; }

            public Encoding ProtocolEncoding { getset; }

            public bool IsCancelSignaled { getset; }

            public bool LoggingEnabled { getset; }

            public LogMessageOption LoggingOption { getset; }

            public ReadOnlyCollection<object> Notifiers { getset; }
        }
        public interface IConfigurationSite
        {
            string OutputPath { get; }
            string SponsorName { getset; }
            string CallerIdentity { getset; }
            string ConnectionString { get; }
            bool GlobalTraceIsOn { get; }
            string ServiceName { get; }
            string Identity { get; }

            object CallContextObj { get; }
            object LogContextObj { get; }

            Encoding ProtocolEncoding { get; }

            bool IsCancelSignaled { get; }
            bool LoggingEnabled { get; }
            LogMessageOption LoggingOption { get; }

            ReadOnlyCollection<object> Notifiers { get; }
        }

        internal class RollingDefaultTraceListener : DefaultTraceListener
        {
            long rollSizeKB;
            int maxNumberOfRollingLogs;
            RollingFlatFile logger;

            public RollingDefaultTraceListener(long rollSizeKB, int maxNumberOfRollingLogs)
            {
                this.rollSizeKB = rollSizeKB;
                this.maxNumberOfRollingLogs = maxNumberOfRollingLogs;
            }

            public new string LogFileName
            {
                get { return base.LogFileName; }
                set
                {
                    base.LogFileName = value;
                    if (this.logger == null)
                    {
                        string fileName = Path.GetFileName(value);
                        string path = Path.Combine(Path.GetDirectoryName(value), Environment.MachineName + "_" + fileName);

                        GlobalLoggers.CreateLogger(fileName, Encoding.UTF8, path, this.rollSizeKB, this.maxNumberOfRollingLogs);

                        // move the default log file to another location
                        Trace.Listeners.Remove("Default");
                        Trace.Listeners.Add(this);

                        this.logger = GlobalLoggers.Get(fileName);
                    }
                }
            }

            public override void Write(string message)
            {
                WriteLine(message);
                return;
            }

            public override void WriteLine(string message)
            {
                if (this.logger != null)
                {
                    this.logger.Tracelog(message);
                }
                return;
            }
        } 
        #endregion
    }
    RollingFlatFile.cs
    View Code
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Globalization;
    using System.IO;
    using System.Linq;
    using System.Text;
    using System.Text.RegularExpressions;

    namespace YourCompany.Logging
    {
        public class RollingFlatFile : IDisposable
        {
            public string FileName
            {
                get;
                private set;
            }

            protected Encoding encoding;
            protected string timeStampPattern;

            protected object syncRoot = new object();
            RollingHelper rollingHelper;

            public RollingFlatFile(Encoding encoding, string fileName, long rollSizeKB = 1024int maxNumberOfRollingLogs = 1string timeStampPattern = "yyyy-MM-dd [hh-mm-ss]")
            {
                this.encoding = encoding;
                this.FileName = fileName;
                this.RollSizeInBytes = rollSizeKB * 1024;
                this.timeStampPattern = timeStampPattern;
                this.MaxNumberOfRollingLogs = maxNumberOfRollingLogs;

                if (this.MaxNumberOfRollingLogs <= 0)
                {
                    throw new ArgumentException("Invalid number of rolling logs.");
                }
                this.rollingHelper = new RollingHelper(this);
            }

            public long RollSizeInBytes
            {
                get;
                set;
            }

            public int MaxNumberOfRollingLogs
            {
                get;
                set;
            }

            string headerInfo = string.Empty;
            public string HeaderInfo
            {
                get { return this.headerInfo; }
                set
                {
                    this.headerInfo = value;
                    InternalTracelog(value);
                }
            }

            internal void InternalTracelog(string message)
            {
                LogEntry entry = new LogEntry(string.Empty, string.Empty, LogMessageDirection.None, message, LogMessageCategory.Info);
                InternalTracelog(entry);
                return;
            }

            internal void InternalTracelog(string format, params object[] args)
            {
                LogEntry entry = new LogEntry(string.Empty, string.Empty, LogMessageDirection.None, string.Format(format, args), LogMessageCategory.Info);
                InternalTracelog(entry);
                return;
            }

            internal void InternalTracelog(LogEntry entry)
            {
                lock (this.syncRoot)
                {
                    if (this.rollingHelper != null)
                    {
                        List<string> lines = new List<string>();

                        StringBuilder argument = new StringBuilder();
                        argument.AppendFormat("[{0:D2}/{1:D2}/{2:D4} {3:D2}:{4:D2}:{5:D2}.{6:D3}]", entry.TimeStamp.Month, entry.TimeStamp.Day, entry.TimeStamp.Year, entry.TimeStamp.Hour, entry.TimeStamp.Minute, entry.TimeStamp.Second, entry.TimeStamp.Millisecond);

                        if (this.rollingHelper.RollIfNecessary())
                        {
                            // write the header info
                            string value = string.Format("{0} - {1}", argument.ToString(), this.headerInfo);
                            lines.Add(value);
                        }

                        switch (entry.Direction)
                        {
                            case LogMessageDirection.InBound:
                                argument.Append(" - IN ");
                                break;
                            case LogMessageDirection.OutBound:
                                argument.Append(" - OUT");
                                break;
                        }

                        switch (entry.Category)
                        {
                            case LogMessageCategory.Success:
                                argument.AppendFormat(" - [{0}]", LogMessageCategory.Success.ToString());
                                break;
                            case LogMessageCategory.Warning:
                                argument.AppendFormat(" - [{0}]", LogMessageCategory.Warning.ToString());
                                break;
                            case LogMessageCategory.Error:
                                argument.AppendFormat(" - [{0}]", LogMessageCategory.Error.ToString());
                                break;
                            case LogMessageCategory.Info:
                                argument.AppendFormat(" - [{0}]", LogMessageCategory.Info.ToString());
                                break;
                            default:
                                break;
                        }

                        argument.AppendFormat(" - {0}", entry.Message);
                        lines.Add(argument.ToString());

                        this.rollingHelper.WriteLine(lines.ToArray());
                    }
                }
                return;
            }

            internal void InternalWriteLine()
            {
                lock (this.syncRoot)
                {
                    if (this.rollingHelper != null)
                    {
                        this.rollingHelper.WriteLine(string.Empty);
                    }
                }
                return;
            }

            public void Dispose()
            {
                InternalClose();
                return;
            }

            internal void InternalClose()
            {
                using (this.rollingHelper) { }
                this.rollingHelper = null;
                return;
            }

            #region internal helper class
            sealed class RollingHelper : IDisposable
            {
                RollingFlatFile owner;
                StreamWriter writer;

                public RollingHelper(RollingFlatFile owner)
                {
                    this.owner = owner;
                }

                bool PerformsRolling
                {
                    get { return this.owner.RollSizeInBytes > 0; }
                }

                public void WriteLine(params string[] values)
                {
                    if (this.writer == null)
                    {
                        CreateLogFile();
                    }

                    if (this.writer != null)
                    {
                        foreach (string value in values)
                        {
                            this.writer.WriteLine(value);
                        }
                    }
                    return;
                }

                private void Close()
                {
                    using (this.writer)
                    {
                        if (this.writer != null)
                        {
                            this.writer.Close();
                        }
                    }
                    this.writer = null;
                    return;
                }

                public void Dispose()
                {
                    Close();
                    return;
                }

                public bool RollIfNecessary()
                {
                    bool wasRolled = false;
                    if (this.PerformsRolling)
                    {
                        if (CheckIfRollNecessary())
                        {
                            PerformRoll();
                            wasRolled = true;
                        }
                    }
                    return wasRolled;
                }

                private void CreateLogFile()
                {
                    System.Diagnostics.Debug.Assert(this.writer == null);

                    if (Directory.Exists(Path.GetDirectoryName(this.owner.FileName)) == false)
                    {
                        Directory.CreateDirectory(Path.GetDirectoryName(this.owner.FileName));
                    }

                    this.writer = new StreamWriter(this.owner.FileName, truethis.owner.encoding);
                    this.writer.AutoFlush = true;
                    return;
                }

                private bool CheckIfRollNecessary()
                {
                    bool result = false;
                    if (File.Exists(this.owner.FileName))
                    {
                        FileInfo fileInfo = new FileInfo(this.owner.FileName);
                        // check for size roll, if enabled.
                        result = fileInfo.Length > this.owner.RollSizeInBytes;
                    }
                    return result;
                }

                private void PerformRoll()
                {
                    DateTime rollDateTime = DateTime.Now;
                    string actualFileName = this.owner.FileName;

                    // calculate archive name
                    string archiveFileName = ComputeArchiveFileName(actualFileName, rollDateTime);
                    // close current file
                    Close();
                    // move file
                    SafeMove(actualFileName, archiveFileName, rollDateTime);
                    // delete rolling logs if needed
                    DeleteOldArchiveFiles(actualFileName);
                    // create a new file again
                    CreateLogFile();
                    return;
                }

                private void SafeMove(string actualFileName, string archiveFileName, DateTime currentDateTime)
                {
                    try
                    {
                        if (File.Exists(archiveFileName))
                        {
                            File.Delete(archiveFileName);
                        }
                        // take care of tunneling issues http://support.microsoft.com/kb/172190
                        File.SetCreationTime(actualFileName, currentDateTime);
                        File.Move(actualFileName, archiveFileName);
                    }
                    catch (IOException)
                    {
                        // catch errors and attempt move to a new file with a GUID
                        archiveFileName = archiveFileName + Guid.NewGuid().ToString();
                        try
                        {
                            File.Move(actualFileName, archiveFileName);
                        }
                        catch (IOException) { }
                    }
                    return;
                }

                private string ComputeArchiveFileName(string actualFileName, DateTime currentDateTime)
                {
                    string directory = Path.GetDirectoryName(actualFileName);
                    string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(actualFileName);
                    string extension = Path.GetExtension(actualFileName);

                    // new archive name
                    string archiveFileNameWithTimestampWithoutExtension = fileNameWithoutExtension + "." + currentDateTime.ToString(this.owner.timeStampPattern, CultureInfo.InvariantCulture);
                    return Path.Combine(directory, archiveFileNameWithTimestampWithoutExtension + extension);
                }

                private void DeleteOldArchiveFiles(string actualFileName)
                {
                    string directory = Path.GetDirectoryName(actualFileName);
                    string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(actualFileName);
                    string extension = Path.GetExtension(actualFileName);

                    DirectoryInfo info = new DirectoryInfo(directory);
                    FileInfo[] existingFiles = info.GetFiles(string.Format("{0}*{1}", fileNameWithoutExtension, extension));

                    List<FileInfo> deleteFiles = new List<FileInfo>();

                    Regex regex = new Regex(string.Format(@"{0}\.(.+).{1}", fileNameWithoutExtension, extension));
                    for (int index = 0; index < existingFiles.Length; index++)
                    {
                        Match sequenceMatch = regex.Match(existingFiles[index].FullName);
                        if (sequenceMatch.Success)
                        {
                            deleteFiles.Add(existingFiles[index]);
                        }
                    }

                    deleteFiles.Sort((x, y) => x.CreationTime < y.CreationTime ? 1 : x.CreationTime > y.CreationTime ? -1 : 0);

                    for (int index = this.owner.MaxNumberOfRollingLogs; index < deleteFiles.Count; index++)
                    {
                        try
                        {
                            deleteFiles[index].Delete();
                        }
                        catch
                        {
                        }
                    }
                    return;
                }
            }
            #endregion
        }

        [Serializable]
        public class LogEntry
        {
            public LogEntry(LogMessageDirection direction, string message, LogMessageCategory category, DateTime? timeStamp = null)
                : this(nullnull, direction, message, category, timeStamp)
            {
            }
            public LogEntry(string serviceName, string identity, LogMessageDirection direction, string message, LogMessageCategory category, DateTime? timeStamp = null)
            {
                this.ServiceName = serviceName;
                this.Identity = identity;
                this.Direction = direction;
                this.Message = message;
                this.Category = category;
                this.TimeStamp = timeStamp ?? DateTime.Now;
            }
            public readonly DateTime TimeStamp;
            public readonly string ServiceName;
            public readonly string Identity;
            public readonly LogMessageDirection Direction;
            public readonly LogMessageCategory Category;
            public readonly string Message;
        }

        public interface IMessage
        {
            string Name { get; }
            IMessage Predecessor { getset; }

            object PropertyBag { get; }
            bool IsTransferMessage { getset; }
            // internal use only
            bool IsSpecialMessage { get; }

            byte[] ToArray();
            string ToString();
        }

        public static class GlobalLoggers
        {
            #region RollingFlatFile extensions
            public static void Tracelog(this RollingFlatFile entity, string message)
            {
                if (entity != null)
                {
                    entity.InternalTracelog(message);
                }
                return;
            }

            public static void Tracelog(this RollingFlatFile entity, string format, params object[] args)
            {
                if (entity != null)
                {
                    entity.InternalTracelog(format, args);
                }
                return;
            }

            public static void Tracelog(this RollingFlatFile entity, LogEntry entry)
            {
                if (entity != null)
                {
                    entity.InternalTracelog(entry);
                }
                return;
            }

            public static void WriteLine(this RollingFlatFile entity)
            {
                if (entity != null)
                {
                    entity.InternalWriteLine();
                }
                return;
            }

            public static void Close(this RollingFlatFile entity)
            {
                if (entity != null)
                {
                    entity.InternalClose();
                }
                return;
            }
            #endregion

            static Dictionary<string, RollingFlatFile> loggers = new Dictionary<string, RollingFlatFile>();

            public static bool CreateLogger(string identity, Encoding encoding, string fileName, long rollSizeKB)
            {
                return CreateLogger(identity, encoding, fileName, rollSizeKB, 1);
            }

            public static bool CreateLogger(string identity, Encoding encoding, string fileName, long rollSizeKB, int maxNumberOfRollingLogs)
            {
                return CreateLogger(identity, encoding, fileName, rollSizeKB, maxNumberOfRollingLogs, "yyyy-MM-dd [hh-mm-ss]");
            }

            public static bool CreateLogger(string identity, Encoding encoding, string fileName, long rollSizeKB, int maxNumberOfRollingLogs, string timeStampPattern)
            {
                bool result = loggers.ContainsKey(identity);
                if (result == false)
                {
                    lock (((ICollection)loggers).SyncRoot)
                    {
                        RollingFlatFile logger = new RollingFlatFile(encoding, fileName, rollSizeKB, maxNumberOfRollingLogs, timeStampPattern);
                        loggers.Add(identity, logger);
                        result = true;
                    }
                }
                return result;
            }

            public static void AttachLogger(string identity, RollingFlatFile logger)
            {
                lock (((ICollection)loggers).SyncRoot)
                {
                    if (loggers.ContainsKey(identity))
                    {
                        throw new Exception(string.Format("Logger '{0}' already exists!", identity));
                    }
                    loggers.Add(identity, logger);
                }
                return;
            }

            public static void CloseAll()
            {
                try
                {
                    lock (((ICollection)loggers).SyncRoot)
                    {
                        foreach (RollingFlatFile logger in loggers.Values)
                        {
                            using (logger) { }
                        }
                        loggers.Clear();
                    }
                }
                catch
                {
                }
                return;
            }

            public static void Close(string identity)
            {
                lock (((ICollection)loggers).SyncRoot)
                {
                    if (loggers.ContainsKey(identity))
                    {
                        using (loggers[identity]) { }
                        loggers.Remove(identity);
                    }
                }
                return;
            }

            public static RollingFlatFile Get(string identity)
            {
                RollingFlatFile result = null;
                if (loggers.ContainsKey(identity))
                {
                    result = loggers[identity];
                }
                return result;
            }
        }
    }

    TraceWriter.cs

    View Code
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Text;

    namespace YourCompany.Logging
    {
        public sealed class TraceWriter : IDisposable, ILogger
        {
            RollingFlatFile writer;
            Encoding encoding;

            string initialOutputDirectory = string.Empty;

            bool traceToDatabase = false;
            bool deleteFileIfExists = false;
            bool createSeparateDirectory = true;
            bool leaveStreamOpen = false;

            public TraceWriter(Encoding encoding, long rollingSizeKB, int maxNumOfRollingLogs, string callerIdentity, string initialOutputDirectory, bool traceToDatabase, bool deleteFileIfExists, bool createSeparateDirectory)
                : this(encoding, rollingSizeKB, maxNumOfRollingLogs, callerIdentity, initialOutputDirectory, traceToDatabase, deleteFileIfExists, createSeparateDirectory, true)
            {
            }

            public TraceWriter(Encoding encoding, long rollingSizeKB, int maxNumOfRollingLogs, string callerIdentity, string initialOutputDirectory, bool traceToDatabase, bool deleteFileIfExists, bool createSeparateDirectory, bool keepStreamOpen)
            {
                this.encoding = encoding;
                this.rollSizeKB = rollingSizeKB;
                this.maxNumberOfRollingLogs = maxNumOfRollingLogs;
                this.initialOutputDirectory = initialOutputDirectory;
                this.traceToDatabase = traceToDatabase;
                this.deleteFileIfExists = deleteFileIfExists;
                this.createSeparateDirectory = createSeparateDirectory;
                this.callerIdentity = callerIdentity;
                this.leaveStreamOpen = keepStreamOpen;
            }

            long rollSizeKB = 10 * 1024;
            public long LogKByteThreshold
            {
                get { return this.rollSizeKB; }
                set { this.rollSizeKB = value; }
            }

            int maxNumberOfRollingLogs = 1;
            public int MaxNumberOfRollingLogs
            {
                get { return this.maxNumberOfRollingLogs; }
                set { this.maxNumberOfRollingLogs = value; }
            }

            public string OutputPath
            {
                get
                {
                    string outputDirectory = this.initialOutputDirectory;
                    try
                    {
                        if (this.createSeparateDirectory)
                        {
                            string[] parts = this.sponsorName.Split(@"\/".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
                            foreach (string part in parts)
                            {
                                outputDirectory = Path.Combine(outputDirectory, part);
                            }
                            outputDirectory = Path.Combine(outputDirectory, this.callerIdentity.Substring(0this.callerIdentity.Length / 2));
                            outputDirectory = Path.Combine(outputDirectory, this.callerIdentity);

                            if (Directory.Exists(outputDirectory) == false)
                            {
                                Directory.CreateDirectory(outputDirectory);
                            }
                        }
                    }
                    catch
                    {
                    }
                    return outputDirectory;
                }
            }

            string sponsorName = string.Empty;
            public string SponsorName
            {
                get { return this.sponsorName; }
                set { this.sponsorName = value; }
            }

            string callerIdentity = string.Empty;
            public string CallerIdentity
            {
                get { return this.callerIdentity; }
                set { this.callerIdentity = value; }
            }

            public void Tracelog(string message)
            {
                LogEntry entry = new LogEntry(string.Empty, string.Empty, LogMessageDirection.None, message, LogMessageCategory.Info);
                Tracelog(entry);
                return;
            }

            public void Tracelog(string format, params object[] arg)
            {
                LogEntry entry = new LogEntry(string.Empty, string.Empty, LogMessageDirection.None, string.Format(format, arg), LogMessageCategory.Info);
                Tracelog(entry);
                return;
            }

            public void Tracelog(LogEntry entry)
            {
                try
                {
                    // write to database?
                    if (this.traceToDatabase)
                    {
    #if DEBUG
                        throw new NotSupportedException();
    #endif
                    }
                    else
                    {
                        if (string.IsNullOrEmpty(this.callerIdentity))
                        {
                            throw new InvalidOperationException("Caller Identity not set yet!");
                        }

                        if (this.writer == null)
                        {
                            string filePath = Path.Combine(this.OutputPath, this.callerIdentity + ".log");
                            if (this.deleteFileIfExists)
                            {
                                if (File.Exists(filePath))
                                {
                                    File.Delete(filePath);
                                }
                            }
                            this.writer = new RollingFlatFile(this.encoding, filePath, this.rollSizeKB, this.maxNumberOfRollingLogs);
                        }
                        this.writer.Tracelog(entry);
                    }
                }
                catch
                {
                    if (this.traceToDatabase == false)
                    {
                        Close();
                    }
                }
                return;
            }

            public void WriteLine()
            {
                if (this.traceToDatabase == false)
                {
                    if (this.writer != null)
                    {
                        this.writer.WriteLine();
                    }
                }
                return;
            }

            void IDisposable.Dispose()
            {
                Close();
                return;
            }

            public void Close()
            {
                using (this.writer) { }
                this.writer = null;
                return;
            }

            public void Write(string serviceName, string loggingIdentity, LogMessageDirection direction, string message)
            {
                LogEntry entry = new LogEntry(serviceName, loggingIdentity, direction, message, LogMessageCategory.Info);
                Tracelog(entry);
                return;
            }

            public void Write(string serviceName, string loggingIdentity, string message)
            {
                LogEntry entry = new LogEntry(serviceName, loggingIdentity, LogMessageDirection.None, message, LogMessageCategory.Info);
                Tracelog(entry);
                return;
            }

            public void GlobalWrite(string message, LogMessageCategory category)
            {
                LogEntry entry = new LogEntry(String.Empty, String.Empty, LogMessageDirection.None, message, category);
                Tracelog(entry);
                return;
            }
        }
    }

    配置文件:

    View Code
    <appSettings>
        <!--Specify the logging folder-->
        <!--Format:
                    1). Specific folder, e.g. D:\Logs\
                    2). Environment folder, e.g. {ApplicationData}\ABC\Logs\
                                                 {MyDocuments}\ABC\Logs\
    -->
        <add key="LogFolder" value="D:\Logs"/>
        <!--Specify log file maximum rolling size (KB)-->
        <add key="MaxFileSize" value="5000"/>
        <!--Specify maximum numbers of split log files creation when rolling size exceeded-->
        <add key="MaxNumofRollingFlags" value="50"/>
        <!--Writing warning entry in log files may cause performance loss in case of a huge amount of rubbish in excel file!-->
        <!--   Set this option to 'false' to get better performance for large file and high pressure system-->
        <add key="WriteWarningLogs" value="True"/>
        <!--Writing information log entries in log files may cause performance loss in case of a huge amount of rubbish in excel file!-->
        <!--   Set this option to 'false' to get better performance for large file and high pressure system-->
        <add key="WriteInfoLogs" value="True"/>
        <!--The time interval between invokation of timer call back. - This timer is used to check if there're log entry in concurrent logging queue.  -->
        <!--15000 means 15 second. To allow concurrent logging, writting log entry is put into concurrent queue. Once a while the log items in the queue will be flushed into log file. -->
        <add key="WritLogQueueCheckPeriod" value="5000"/>
      </appSettings>

    写日志:

    View Code
    Logger.GlobalWrite("abc",LogMessageCategory.Success);


    本文结束。本文实现了一个最简单的写日志的组件。 实现了下面的几个功能:

    • 可以用FileAppender的方式记日志
    • 线程安全,支持多个线程同时Append同一日志文件
    • 支持FileRolling,设置大小自动分卷
  • 相关阅读:
    标识符
    Bootstrap笔记——Checkbox选项组
    servlet实现简单验证码
    javascript笔记——登录表单验证
    JSP的隐式对象(page,config,pageContext,exception)
    创建Servlet文件不自动生成web.xml原因
    JSP的隐式对象(application)
    前端开发工具——brackets的使用
    《Shell脚本学习 —— Shell变量、字符串、数组、注释》
    《开发板 — 3399pro备份整个文件系统》
  • 原文地址:https://www.cnblogs.com/Mainz/p/2622935.html
Copyright © 2011-2022 走看看