三天前基本上把數據庫表設計的文檔寫好,今天想到了一個問題,還要再加幾個表,一個是log表,用來記錄系統日志,另外再加幾個字典表,一些需要配置的數據但又不好放在像xml文件裡面的數據可以放在這些字典表裡面。
從今天開始就正式進入系統設計與編碼了,詳細設計文檔等系統做好後再補充了,因為一開始全部寫好不大現實,中間過程中會不斷地去迭代。現在的想法是每個模塊分別去實現,然後再分別記錄下來。
今天要寫的是日志模塊,因為在生產環境中,好的日志至於重要,系統運行時出現的任何問題可以通過日志記錄下來,對於發現與解決問題非常有幫助。因為日志是一個相對比較通用的模塊,所以先設計好如果寫日志模塊,之後再寫通用類模塊,再數據庫訪問層與用戶自定義控件,然後再數據實體與業務處理層,最後再寫用戶表現層。
因為此次不使用第二方控件,所以不考慮像log4net,微軟enterprise中好的日志控件,但是看了它們的代碼,總體思想都差不多,就是各種級別的日志信息應該以什麼樣的格式輸出到哪種介質中,也就是輸出源,像文本文件還是數據庫,還是控制台,還是系統日志郵件等等。基於它們的思想,把構思記錄下來:
日志最主要的就是一個日志器與附加器,像log4net中可以定義多個日志器,一個日志器可以附加多個輸出源,而日志倉庫就是如何存儲和管理日志器,過慮器如果過慮各種級別的日志,而layout就是如何顯示輸出的消息。
1、輸出源只包含文本文件,數據庫,系統日志和郵件。
2、日志級別分別為Fatal, Error, Warn, Info, Debug。
還是拿代碼為例子來講吧,首先定義一個日志器接口與日志操作接口ILog,日志器得先有一個名字,它的方法就是一個Log和IsEnabledFor,ILog包含的方法如下,用過log4net等日志控件的應該很熟悉。
日志器接口詳細代碼如下:
public interface ILogger
{
/// <summary>
/// Gets the name of the logger.
/// </summary>
string Name { get; }
/// <summary>
/// This generic form is intended to be used by wrappers.
/// </summary>
void Log(LogCategory level, object message, Exception exception);
bool IsEnabledFor(LogCategory level);
}
然後再定義一個包裝接口
public interface ILoggerWrapper
{
ILogger Logger
{
get;
}
}
定義ILog接口,最後調用的都是在這裡定義的接口
public interface ILog : ILoggerWrapper
{
void Debug(object message);
void Debug(object message, Exception exception);
void DebugFormat(string format, params object[] args);
void Info(object message);
void Info(object message, Exception exception);
void InfoFormat(string format, params object[] args);
void Warn(object message);
void Warn(object message, Exception exception);
void WarnFormat(string format, params object[] args);
void Error(object message);
void Error(object message, Exception exception);
void ErrorFormat(string format, params object[] args);
void Fatal(object message);
void Fatal(object message, Exception exception);
void FatalFormat(string format, params object[] args);
bool IsDebugEnabled
{
get;
}
bool IsInfoEnabled
{
get;
}
bool IsErrorEnabled
{
get;
}
bool IsWarnEnabled
{
get;
}
bool IsFatalEnabled
{
get;
}
}
日志器有了,可以附加器呢,也就是源出源,其實真正的任務都是委托這些具體的附加器去做的呢,為什麼log4net那麼強大,我想它的附加器如此之多也是一大原因吧,基本上我們能想到的它都想到了,我們沒有想到的它也想到了,下面就定義的幾個具體的附加器。
附加器如何跟前面的說的日志器關聯呢,20個附加器不可能都直接與日志器去關聯吧,所以定義一個所有附加器要實現的接口IAppender.
public interface IAppender
{
string Name
{
get;
set;
}
void Close();
void DoAppender(LogCategory level, object message, Exception exception);
}
拿文本文件為例,日志的輸出源就是文本文件,消息寫到這個介質上,下面是一個基類,然後子類就是繼承這個類實現Write方法去寫日志信息:
public class TextWriterAppender : TextWriter
{
private TextWriter m_writer;
public TextWriter Writer
{
get { return m_writer; }
set { m_writer = value; }
}
virtual public string Name
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
#region Public Methods
/// <summary>
/// Closes the writer and releases any system resources associated with the writer
/// </summary>
/// <remarks>
/// <para>
/// </para>
/// </remarks>
override public void Close()
{
m_writer.Close();
}
/// <summary>
/// Dispose this writer
/// </summary>
/// <param name="disposing">flag indicating if we are being disposed</param>
/// <remarks>
/// <para>
/// Dispose this writer
/// </para>
/// </remarks>
override protected void Dispose(bool disposing)
{
if (disposing)
{
((IDisposable)m_writer).Dispose();
}
}
/// <summary>
/// Flushes any buffered output
/// </summary>
/// <remarks>
/// <para>
/// Clears all buffers for the writer and causes any buffered data to be written
/// to the underlying device
/// </para>
/// </remarks>
override public void Flush()
{
m_writer.Flush();
}
/// <summary>
/// Writes a character to the wrapped TextWriter
/// </summary>
/// <param name="value">the value to write to the TextWriter</param>
/// <remarks>
/// <para>
/// Writes a character to the wrapped TextWriter
/// </para>
/// </remarks>
override public void Write(char value)
{
m_writer.Write(value);
}
/// <summary>
/// Writes a character buffer to the wrapped TextWriter
/// </summary>
/// <param name="buffer">the data buffer</param>
/// <param name="index">the start index</param>
/// <param name="count">the number of characters to write</param>
/// <remarks>
/// <para>
/// Writes a character buffer to the wrapped TextWriter
/// </para>
/// </remarks>
override public void Write(char[] buffer, int index, int count)
{
m_writer.Write(buffer, index, count);
}
/// <summary>
/// Writes a string to the wrapped TextWriter
/// </summary>
/// <param name="value">the value to write to the TextWriter</param>
/// <remarks>
/// <para>
/// Writes a string to the wrapped TextWriter
/// </para>
/// </remarks>
override public void Write(String value)
{
m_writer.Write(value);
}
public override Encoding Encoding
{
get { return m_writer.Encoding; }
}
#endregion
}
日志器與附加器都有了,怎麼去連接它們了,最後我想還是用泛型比較靈活,定義如下:
public class LogFactory<L, A> : ILog
where L : ILogger, new()
where A : IAppender, new()
{
virtual public void Debug(object message)
{
#if DEBUG
Logger.Log(m_levelDebug, message, null);
#endif
}
virtual public void Debug(object message, Exception exception)
{
#if DEBUG
Logger.Log(m_levelDebug, message, exception);
#endif
}
virtual public void DebugFormat(string format, params object[] args)
{
#if DEBUG
if (IsDebugEnabled)
{
Logger.Log(m_levelDebug, new SystemStringFormat(CultureInfo.InvariantCulture, format, args), null);
}
#endif
}
virtual public void Info(object message)
{
Logger.Log(LevelInfo, message, null);
}
virtual public void Info(object message, Exception exception)
{
Logger.Log(LevelInfo, message, exception);
}
virtual public void InfoFormat(string format, params object[] args)
{
if (IsInfoEnabled)
{
Logger.Log(LevelInfo, new SystemStringFormat(CultureInfo.InvariantCulture, format, args), null);
}
}
virtual public void Warn(object message)
{
Logger.Log(LevelWarn, message, null);
}
virtual public void Warn(object message, Exception exception)
{
Logger.Log(LevelWarn, message, exception);
}
virtual public void WarnFormat(string format, params object[] args)
{
if (IsWarnEnabled)
{
Logger.Log(LevelWarn, new SystemStringFormat(CultureInfo.InvariantCulture, format, args), null);
}
}
virtual public void Error(object message)
{
Logger.Log(LevelError, message, null);
}
virtual public void Error(object message, Exception exception)
{
Logger.Log(LevelError, message, exception);
}
virtual public void ErrorFormat(string format, params object[] args)
{
if (IsErrorEnabled)
{
Logger.Log(LevelError, new SystemStringFormat(CultureInfo.InvariantCulture, format, args), null);
}
}
virtual public void Fatal(object message)
{
Logger.Log(LevelFatal, message, null);
}
virtual public void Fatal(object message, Exception exception)
{
Logger.Log(LevelFatal, message, exception);
}
virtual public void FatalFormat(string format, params object[] args)
{
if (IsFatalEnabled)
{
Logger.Log(LevelFatal, new SystemStringFormat(CultureInfo.InvariantCulture, format, args), null);
}
}
virtual public bool IsDebugEnabled
{
get
{
return Logger.IsEnabledFor(m_levelDebug);
}
}
virtual public bool IsInfoEnabled
{
get
{
return Logger.IsEnabledFor(m_levelInfo);
}
}
virtual public bool IsErrorEnabled
{
get
{
return Logger.IsEnabledFor(m_levelError);
}
}
virtual public bool IsWarnEnabled
{
get
{
return Logger.IsEnabledFor(m_levelWarn);
}
}
virtual public bool IsFatalEnabled
{
get
{
return Logger.IsEnabledFor(m_levelFatal);
}
}
private LogCategory m_levelDebug;
public LogCategory LevelDebug
{
get { return LogCategory.Debug; }
set { m_levelDebug = LogCategory.Debug; }
}
private LogCategory m_levelInfo;
public LogCategory LevelInfo
{
get { return LogCategory.Info; }
set { m_levelInfo = LogCategory.Info; }
}
private LogCategory m_levelWarn;
public LogCategory LevelWarn
{
get { return LogCategory.Warn; }
set { m_levelWarn = LogCategory.Warn; }
}
private LogCategory m_levelError;
public LogCategory LevelError
{
get { return LogCategory.Error; }
set { m_levelError = LogCategory.Error; }
}
private LogCategory m_levelFatal;
public LogCategory LevelFatal
{
get { return LogCategory.Fatal; }
set { m_levelFatal = LogCategory.Fatal; }
}
public ILogger Logger
{
get { return new L(); }
}
}
把上面的泛型類閉合一個日志類:
public class LogBase<A> : LogFactory<LogBase<A>, A>, ILogger
where A : IAppender, new()
{
private LogCategory m_logLevel;
public string Name
{
get
{
return "LogBase";
}
}
private A m_instance;
public A Instance
{
get
{
if (m_instance == null)
{
m_instance = new A();
}
return m_instance;
}
set { m_instance = value; }
}
public void Log(LogCategory level, object message, Exception exception)
{
Instance.DoAppender(level, message, exception);
}
public bool IsEnabledFor(LogCategory level)
{
switch (level)
{
case LogCategory.Fatal:
LevelFatal = LogCategory.Fatal;
break;
case LogCategory.Error:
LevelError = LogCategory.Error;
break;
case LogCategory.Warn:
LevelWarn = LogCategory.Warn;
break;
case LogCategory.Debug:
LevelDebug = LogCategory.Debug;
break;
case LogCategory.Info:
LevelInfo = LogCategory.Info;
break;
default:
m_logLevel = LogCategory.Info;
break;
}
return true;
}
}
再關閉一個泛型參數:
public class TxtFileLog : LogBase<TxtFileLog>, IAppender
{
private string m_name;
private FileLogWriter m_writer;
public TxtFileLog()
{
if (m_writer == null)
{
m_writer = new FileLogWriter();
}
}
public FileLogWriter Writer
{
get { return m_writer; }
set { m_writer = value; }
}
public new string Name
{
get
{
return m_name;
}
set
{
m_name = value;
}
}
public void Close()
{
m_writer.Close();
}
public void DoAppender(LogCategory level, object message, Exception exception)
{
m_writer.Write(Convert.ToString(message), level, (LogMessage)exception);
}
上面的類實現了日志器與附加器的連接,然後就可以去客戶端驗證好不好用了:
ILog log = new TxtFileLog();
log.Debug("Are you OK??");
打印如下信息:
這只是第一步,後面還得寫數據庫與郵件附加器的輸出方法。寫一個好的日志器還真不容易。之後會把一些可以配置的東西放到配置文件裡面,
接下來就寫通用類與數據庫訪問層。
注:需要完整源碼的可以mark下,無償發到你郵箱