三天前基本上把数据库表设计的文档写好,今天想到了一个问题,还要再加几个表,一个是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那么强大,我想它的附加器如此之多也是一大原因吧,基本上我们能想到的它都想到了,我们没有想到的它也想到了,下面就定义的几个具体的附加器。

aaarticlea/png;base64,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" alt="" />

附加器如何跟前面的说的日志器关联呢,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??");

打印如下信息:

aaarticlea/png;base64,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" alt="" />

这只是第一步,后面还得写数据库与邮件附加器的输出方法。写一个好的日志器还真不容易。之后会把一些可以配置的东西放到配置文件里面,

接下来就写通用类与数据库访问层。

注:需要完整源码的可以mark下,无偿发到你邮箱

step by step 之餐饮管理系统四(日志模块实现)的更多相关文章

  1. step by step 之餐饮管理系统五(Util模块)------附上篇日志模块源码

    这段时间一直在修改日志模块,现在基本上写好了,也把注释什么的都加上了,昨天邮件发送给mark的园友一直报失败,老是退回来,真是报歉,如下图所示:

  2. step by step 之餐饮管理系统七(点菜模块实现)

    好长时间没有更新这个系列了,一是因为这段时间比较忙,有很多事情,二来要学习新的东西,AngularJs,devExpress这两上框架,都是比较有名的框架,先上图: 上面就是用来点菜的界面,左边是已点 ...

  3. step by step 之餐饮管理系统二

    昨天写了餐饮管理系统的相关需求,得到了园友的一些好的建议,感到很高兴,确实写的也不全面,现在补充一下需要的业务,这次主要做的主要是前台收银系统,所以业务主要集中在前台点菜收银这块,而后面数据管理这块则 ...

  4. step byt step之餐饮管理系统一

    之前写过2015年的工作计划,其中有一项就是写一套管理系统,一来可以练练手,二来可以加强自己的学习,三来可以多园友多交流,共同进步.所以从今天开始把写系统的过程记录下来.先需求分析开始. 第一部分 引 ...

  5. 【转载】MDX Step by Step 读书笔记(四) - Working with Sets (使用集合)

    1. Set  - 元组的集合,在 Set 中的元组用逗号分开,Set 以花括号括起来,例如: { ([Product].[Category].[Accessories]), ([Product].[ ...

  6. e2e 自动化集成测试 架构 实例 WebStorm Node.js Mocha WebDriverIO Selenium Step by step (四) Q 反回调

    上一篇文章“e2e 自动化集成测试 架构 京东 商品搜索 实例 WebStorm Node.js Mocha WebDriverIO Selenium Step by step (三) SqlServ ...

  7. 课程四(Convolutional Neural Networks),第一周(Foundations of Convolutional Neural Networks) —— 2.Programming assignments:Convolutional Model: step by step

    Convolutional Neural Networks: Step by Step Welcome to Course 4's first assignment! In this assignme ...

  8. Metrics.NET step by step使用Metrics监控应用程序的性能

    使用Metrics监控应用程序的性能 在编写应用程序的时候,通常会记录日志以便事后分析,在很多情况下是产生了问题之后,再去查看日志,是一种事后的静态分析.在很多时候,我们可能需要了解整个系统在当前,或 ...

  9. 数据库设计 Step by Step (2)——数据库生命周期

    引言:数据库设计 Step by Step (1)得到这么多朋友的关注着实出乎了我的意外.这也坚定了我把这一系列的博文写好的决心.近来工作上的事务比较繁重,加之我期望这个系列的文章能尽可能的系统.完整 ...

随机推荐

  1. JAVA设计模式之门面模式

    在阎宏博士的<JAVA与模式>一书中开头是这样描述门面(Facade)模式的: 门面模式是对象的结构模式,外部与一个子系统的通信必须通过一个统一的门面对象进行.门面模式提供一个高层次的接口 ...

  2. ActionScript 3.0 for the Lunder Algorithm

    package com.feiruo.Calendar.LunderCalendar { /* *@ClassName: package:com.feiruo.Calendar.LunderCalen ...

  3. AX 2012 EP服务器配置多个环境

    AX 2012 如何在一台服务器配置不同环境的EP站点 安装完EP后,修改对应站点的web.config文件,指定需要连接的客户端配置文件路径即可,如下图: ` ``````````````````` ...

  4. net.sf.json 时间格式的转化

    后台代码 //后台代码 response.setCharacterEncoding("UTF-8"); JsonConfig jsonConfig = new JsonConfig ...

  5. json和字符串转换

    json对象转js字符串 JSON.stringify(json) js字符串转json对象 var json= $.parseJSON(str);

  6. [清理页面缓存]asp.net、html

    (1)   MVC BaseController: Controller内 protected override void Initialize(System.Web.Routing.RequestC ...

  7. Device Path in WinPrefetchView

    As we know that the Prefetch file is used for optimizing the loading time of the application in the ...

  8. 网站实现特定某个地区访问执行跳转(js方法)

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  9. canvas画随机闪烁的星星

    canvas画一颗星星: 规则的星星有内切圆和外切圆,每两个点之间的角度是固定的,因此可得到星星的每个点的坐标,画出星星. function drawStars(x,y,radius1,radius2 ...

  10. MFC的BeginWaitCursor和EndWaitCursor函数

    MFC提供了BeginWaitCursor和EndWaitCursor函数来显示和隐藏等待的图标,以下是例子. void CMainView::OnEditClone() {     BeginWai ...