不用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,设置大小自动分卷

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值