应用程序系统日志在.net中的处理探讨
作者:xueS.net 关键字:日志、StackTrace类
本人编写程序没几年,经验不丰富,总是下载别人的代码学习,也没有为兄弟们做什么贡献,最近参考网上关于系统日志的处理方法,想出这么一个东西,来处理在我们的应用程序中记录系统日志。
即然是日志,对于程序开发人员来说,更关心是哪个类、哪个方法、哪一行出现了问题或者是有价值的信息。还好,.net为我们提供了StackTrace类,可以获取程序中的这些信息(具体请参见帮助文档),这样一来,我们解决了日志中最核心的问题,既然核心已经找到了答案,下一步就是对日志信息的分类了,我认为将日志信息分为三类更为妥当:错误(Error)、警告(Warning)、信息(Message),错误是用来记录系统中出现的错误信息,导致系统无法运行或对系统运行正确与否产生重大影响的信息,如“数据库连接失败!”,警告是对系统运行无重大影响,但也会对系统正常运行产生影响的信息,如“数据源格式不正确!”,信息就很好理解了,就是对系统正确运行无任何影响的信息,但却能看出系统运行情况的一些信息;这样看来,我们需要三个方法来实现信息的记录了,分别是:Error()、Warning()、Message();信息是可以记录了,但是有些时候,我们并不一定需要将日志信息全部记录,怎样才能实现有选择性的记录日志信息呢?我想用枚举是最好不过的方法了,通过枚举异或,来实现日志信息记录类型的定义,枚举值包括:Message、Warning、Error,如果只想记录错误和警告信息,可以这样实现“ Error | Warning ”,这样一来,就可实现保存不同类型的信息了。好来,到现在看来,日志信息已经实现保存了,可是日志信息都包括什么呢?日志信息又该怎么处理呢?为了在以后的时候能够对其进行扩展,我将日志信息定义成一个类,包括一些属性来存储日志的日志信息内容,包括:说明、类名、方法、文件、行号、日期等,对了,不要忘了最重要的一项,日志信息类型(Message/Warning/Erro),没有它,我们将无法判断信息到底是哪一种形式的。
到现在看来,日志信息真的已经完成了,日志到是已经保存了,可是我们怎么查看日志信息呢,下面就要做一个展示日志信息的控件了,首先得能够根据信息类别分别查看,日志要是能够另存到其它文件,发给我们开发人员,就能更快的找到错误的原因了,还得有一个另存,要是日志信息不能及时更新到控件里怎么办,还需要刷新功能,日志文件总是在不断在变大,太大就占硬盘了,这可不好,删除过时的日志是应该的,清空全部日志信息也是必要的,这样,控件的功能就全部出现了,可是怎么才能与日志的处理同步呢?最好是在增加日志信息时,有一个事件,就可以在事件中对控件添加数据,实现同步了,这应该是个好方法了吧。现在看来,日志可以处理了,日志查看视图也有了,下面就是应该开始动手了。
本文程序是用VS 2005 C#写的,对于日志部分,可以稍加修改在VS2003中运行。
我们还是选看看类结构吧!我们要感谢微软,提供这么好的方法,通过画这些类图就能够实现编写代码的目的,我要告诉大家的事,我是先画的这些图,然后才写的代码,给我的感觉是真的太方便了。至于类的作用,前面有所描述,在这里就不在详细说明了,希望读者通过代码可以明白。
我们先看看Demo吧,这张图中,日志视图就是我们要对日志显示的结果,怎么样,对于日志来说,应该这些信息已经够了吧,在看看工具按钮中,有一个“日志级别:高”的按钮,它是来设置日志保存类型的,高级表示记录全部(错误、警告、信息)信息,这个可以在你的应用程序中随意设置。这个Demo实现了一个简单的功能就是打开图片文件,虽然简单,但可以说明我们作的日志程序就可以了。
先看看日志信息该怎么用吧?
// 日志处理的类
Log log = null;
// 实例化 参数表示 日志文件名 宿主
this.log = new Log(string.Format(@"{0}/Logs.xml",
Application.StartupPath), this);
// 设置保存级别
this.log.Style = LogStyle.Error | LogStyle.Warning | LogStyle.Message;
// 定义改变事件,以便对日志视图控件添加记录
this.log.Changed += new LogChangedHandle(log_Changed);
// 绑定到日志视图控件的文件名
this.logView1.FileName = this.log.FileName;
// 向日志写入信息
this.log.Message("系统已经开始运行");
// 向日志写入警告
this.log.Warning("测试警告!!!");
// 向日志写入错误
this.log.Error("测试错误!!!");
怎么样,很简单吧,有了它,我想我们应用程序的日志就是如此的简单了!
怎么样,很简单吧,有了它,我想我们应用程序的日志就是如此的简单了!下面访说Log的代码了,这可是关键,同志们,要仔细的看呀!属性什么的,我就不说了,说关键的吧,Log.Message()、Log.Warning()、Log.Error()都在同一个方法中处理他们的数据:
/// <summary>
/// 保存系统日志信息
/// </summary>
/// <param name="style">日志类型</param>
/// <param name="info">信息内容</param>
private void SaveInfo(LogStyle style, string info)
{
//验证文件
if (this.strFileName.Trim() == string.Empty) return;
//判断保存类型
switch (style)
{
case LogStyle.Error:
if (this.enmStyle == LogStyle.Warning |
this.enmStyle == LogStyle.Message |
this.enmStyle == (LogStyle.Warning | LogStyle.Message)) return;
break;
case LogStyle.Warning:
if (this.enmStyle == LogStyle.Error |
this.enmStyle == LogStyle.Message |
this.enmStyle == (LogStyle.Error | LogStyle.Message)) return;
break;
case LogStyle.Message:
if (this.enmStyle == LogStyle.Warning |
this.enmStyle == LogStyle.Error |
this.enmStyle == (LogStyle.Warning | LogStyle.Error)) return;
break;
}
//获取日志信息
LogInfo logInfo = new LogInfo();
logInfo.Style = style;
logInfo.Summary = info;
logInfo.Class = "";
if (this.objMaster != null) logInfo.Class = this.objMaster.GetType().FullName;
logInfo.Date = DateTime.Now;
//关键,这里获取方法,行号,文件等信息
StackTrace st = new StackTrace(true);
for(int i=0;i<st.FrameCount;i++)
{
StackFrame sf = st.GetFrame(i);
//这里获取Log.Message() 或Log.Warning() 或Log.Error() 被调用时发生的真正方法
//在这些方法的后边就是我们想要的真正的方法了
if (sf.GetMethod().Name.ToUpper() == style.ToString().ToUpper() && i+1<st.FrameCount)
{
sf = st.GetFrame(i + 1);
logInfo.Method = sf.GetMethod().Name;
logInfo.Line = sf.GetFileLineNumber();
logInfo.File = sf.GetFileName();
break;
}
}
//保存日志到文件
//验证文件
DataSet dataSet=null;
if (File.Exists(this.strFileName))
{
//文件存在,打开
dataSet = new DataSet();
try
{
dataSet.ReadXml(this.strFileName, XmlReadMode.ReadSchema);
}
catch (Exception)
{
//可能文件格式错误,但不做错误操作
//新建
dataSet = this.CreateLogDataSet();
}
}
else
{
//文件不存在,新建
dataSet = this.CreateLogDataSet();
}
//验证LogInfo 表
DataTable table = dataSet.Tables["LogInfo"];
if (table == null)
{
throw new Exception(string.Format("{0} 文件格式不正确!", this.strFileName));
}
try
{
//保存到数据源
DataRow row = table.NewRow();
row["Style"] = logInfo.Style.ToString();
row["Summary"] = logInfo.Summary;
row["Class"] = logInfo.Class;
row["Method"] = logInfo.Method;
row["File"] = logInfo.File;
row["Line"] = logInfo.Line;
row["Date"] = logInfo.Date;
table.Rows.Add(row);
//写回到文件
dataSet.WriteXml(this.strFileName, XmlWriteMode.WriteSchema);
}
catch (Exception error)
{
throw error;
}
//触发事件
if (this.Changed != null)
{
this.Changed(this, new LogChangedArgs(style, logInfo));
}
}
/// <summary>
/// 创建日志需要的DataSet
/// </summary>
private DataSet CreateLogDataSet()
{
DataSet dataSet = new DataSet("Log");
DataTable table = new DataTable("LogInfo");
table.Columns.Add("Style", typeof(string));
table.Columns.Add("Summary", typeof(string));
table.Columns.Add("Class", typeof(string));
table.Columns.Add("Method", typeof(string));
table.Columns.Add("File", typeof(string));
table.Columns.Add("Line", typeof(int));
table.Columns.Add("Date", typeof(DateTime));
dataSet.Tables.Add(table);
return dataSet;
}
我想兄弟们能够读懂,很简单,一点技术含量都没有,日志视图控件的制作,在这里就不详细说明了,如果朋友们感兴趣,可以留言或发email给我,我会将源码献上,与朋友共同进步。这个类里有一个我到现在也没有想出解决办法的问题,就是当日志信息数据过多时,会使应用程序运行速度变得很慢,如果有更好的办法解决,还希望朋友们赐教,万分感谢!