不用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 }
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, null, 0 /* 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 { get; set; }
public string SponsorName { get; set; }
public string CallerIdentity { get; set; }
public string ConnectionString { get; set; }
public bool GlobalTraceIsOn { get; set; }
public string ServiceName { get; set; }
public string Identity { get; set; }
public object CallContextObj { get; set; }
public object LogContextObj { get; set; }
public Encoding ProtocolEncoding { get; set; }
public bool IsCancelSignaled { get; set; }
public bool LoggingEnabled { get; set; }
public LogMessageOption LoggingOption { get; set; }
public ReadOnlyCollection<object> Notifiers { get; set; }
}
public interface IConfigurationSite
{
string OutputPath { get; }
string SponsorName { get; set; }
string CallerIdentity { get; set; }
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
}
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, null, 0 /* 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 { get; set; }
public string SponsorName { get; set; }
public string CallerIdentity { get; set; }
public string ConnectionString { get; set; }
public bool GlobalTraceIsOn { get; set; }
public string ServiceName { get; set; }
public string Identity { get; set; }
public object CallContextObj { get; set; }
public object LogContextObj { get; set; }
public Encoding ProtocolEncoding { get; set; }
public bool IsCancelSignaled { get; set; }
public bool LoggingEnabled { get; set; }
public LogMessageOption LoggingOption { get; set; }
public ReadOnlyCollection<object> Notifiers { get; set; }
}
public interface IConfigurationSite
{
string OutputPath { get; }
string SponsorName { get; set; }
string CallerIdentity { get; set; }
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 = 1024, int maxNumberOfRollingLogs = 1, string 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, true, this.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(null, null, 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 { get; set; }
object PropertyBag { get; }
bool IsTransferMessage { get; set; }
// 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;
}
}
}
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 = 1024, int maxNumberOfRollingLogs = 1, string 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, true, this.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(null, null, 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 { get; set; }
object PropertyBag { get; }
bool IsTransferMessage { get; set; }
// 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(0, this.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;
}
}
}
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(0, this.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>
<!--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,设置大小自动分卷