三天前基本上把数据库表设计的文档写好,今天想到了一个问题,还要再加几个表,一个是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. Codeforces Round #229 (Div. 2) C

    C. Inna and Candy Boxes time limit per test 1 second memory limit per test 256 megabytes input stand ...

  2. 从Windows 2012标准版升级到数据中心版,标准评价版本升级到标准体验版本并激活

    对于Windows 7.Windows 8操作系统,可以在图形界面中通过输入序列号,从低版本直接升级到高的版本,例如从Windows 7家庭版升级到专业版或旗舰版.而对于Windows Server ...

  3. MySQL备份学习

    备份分类 物理和逻辑备份 物理备份直接拷贝数据库目录和文件,适合数据量大.重要且需要在出现问题时快速恢复的数据库 逻辑备份保存信息包括逻辑数据库结构(数据库表的创建脚本)和内容(插入语句或者分隔符平面 ...

  4. Spring day04笔记(SVN讲解和回顾昨天知识)

    spring day03回顾 事务管理 基于xml配置 1.配置事务管理器 jdbc:DataSourceTransactionManager hibernate:HibernateTransacti ...

  5. maximo弹框设置新的功能测试总结

    先介绍下弹框前的准备工作: 1.签名选项——定义系统中可授权的所有功能的唯一标识.定义签名选项是为了授权而已.定义的签名名要和相应的bean类中的方法一致. 2.签名选项中的功能实现,一般都在APPB ...

  6. Python自动化 【第一篇】:Python简介和入门

    Python简介: 一.什么是python Python是一门动态解释性的强类型定义语言. pythonde 特点:“优雅”.“明确”.“简单”. 二.Python由来 python的创始人为吉多·范 ...

  7. jenkins+findbugs

    1)  Jenkins安装findbugs插件 具体安装步骤:在主页面进入系统管理 选择插件管理 在过滤器中找出要安装的插件,并进行安装(Static Analysis Utilities.findb ...

  8. dockerfile学习与详解

    1,什么是dockerfile? dockerfile是相当于docker使用的一个脚本,作用是便于实现自定义的镜像image,用语docker build [OPTIONS] PATH ,只需要指定 ...

  9. windows下apache+php+mysql 环境配置方法

    一 准备 1 下载apache http://httpd.apache.org/download.cgi#apache24 httpd-2.2.22-win32-x86-openssl-0.9.8t. ...

  10. php 乱码解决

    1)首先确定你的终端编码,如果你不知道如何确定,分别执行这两段代码,看看哪个能输出中文. PHP code   ? 1 echo pack("H12","E4B8ADE6 ...