您的位置:首页 > 其它

程序结构中的两点重要元素(续)之日志功能的完善

2008-06-24 16:29 405 查看
首先,请简要熟悉下工程中的“三段式”结构:



上图中用红色椭圆圈住的文件夹结构即“三段”(其中就“三段式”编程的简介请参看前面一篇文章:点击查看)

大家同时看到在Common目录下,有一个ErrorRecord.cs的文件,这即是专门负责记录日志的处理类所在文件,其源码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace Backup_Project37.Common
{
// The class which record the error message, can be use to write the log.
public class ErrorRecord
{
private string m_strErrorPosition = ""; // Record the position of the error, include the function and the operation part.
private string m_strErrorDescription = ""; // Record the detail message of the error.
private string m_strErrorOperation = ""; // Record the operation where the error happened.
private string m_strErrorMoudle = ""; // Record the moudle where the error happend.
private bool m_bErrorTag = false; // Record if there is a error happened.
private string m_strFilePath = "ErrorRecord.txt"; // The path of the error file to save.

public ErrorRecord()
{
}

public string ErrorPosition
{
set { m_strErrorPosition = value; m_bErrorTag = true; }
get { return m_strErrorPosition; }
}

public string ErrorDescription
{
set { m_strErrorDescription = value; m_bErrorTag = true; }
get { return m_strErrorDescription; }
}

public string ErrorOperation
{
set { m_strErrorOperation = value; m_bErrorTag = true; }
get { return m_strErrorOperation; }
}

public string ErrorMoudle
{
set { m_strErrorMoudle = value; m_bErrorTag = true; }
get { return m_strErrorMoudle; }
}

public bool ErrorTag
{
get { return m_bErrorTag; }
}

public void WriteErrorInfo(string strPosition, string strDescription, string strOperation, string strMoudle)
{
SetLogValue(strOperation, m_strErrorOperation);
SetLogValue(strMoudle, m_strErrorMoudle);
SetLogValue(strPosition, m_strErrorPosition);
SetLogValue(strDescription, m_strErrorDescription);
m_bErrorTag = true;
}

// Save the error to a file.
public void SaveErrorInfoToFile(string strFilePath)
{
if (!m_bErrorTag) { return; }

FileStream fileErrorRecord = new FileStream((String.IsNullOrEmpty(strFilePath) ? m_strFilePath : strFilePath), FileMode.Append, FileAccess.Write);

// 检测文件是否打开或创建成功
if (fileErrorRecord == null){throw new Exception();}

// 文件内容长度
long lFileContentLen = fileErrorRecord.Length;

// 锁定日志文件原有内容不被其他进程修改
fileErrorRecord.Lock(0, lFileContentLen);

// 设定编码格式
Encoding encodeFormat = Encoding.GetEncoding("gb2312");

// 将内容写入文件流
string strErrorInfo = GetErrorInfo();
fileErrorRecord.Write(encodeFormat.GetBytes(strErrorInfo), 0, encodeFormat.GetByteCount(strErrorInfo));

// 解锁文件内容的锁定
fileErrorRecord.Unlock(0, lFileContentLen);

// 清空文件流缓存
fileErrorRecord.Flush();
}

//
// Assistant functions.
//

private void SetLogValue(string strInput, string strMember)
{
if (!String.IsNullOrEmpty(strInput) && String.IsNullOrEmpty(strMember)) { strMember = strInput; }
}

// Get the error info.
private string GetErrorInfo()
{
return "ErrorPosition:" + m_strErrorOperation + "->" + m_strErrorMoudle + "->" + m_strErrorPosition + "/r/nErrorMessage:" + m_strErrorDescription;
}
}
}

日志类如要意图就是记录与发生异常相关的所有重要信息,这其中包括最底层异常的错误信息、发生异常的调用函数、发生异常的模块及发生异常的流程,进而达到非常便捷快速地定位问题的目的,下面,我们来看下这个日志类在这个“三段式”的结构中应该如何进行“撒网”呢?

首先,在Operations中:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.IO;
using System.Data;

using Backup_Project37.Common;
using Backup_Project37.Modules.SystemData;

using NManageDBLib;

namespace Backup_Project37.Operations.SystemData
{
class UserList : Operation
{
public override bool ExecuteOperation(IManageDB db)
{
bool bResult = false;
try
{
// Load Xml module
m_docTargetData.LoadXml(DataXmlFormatModules.SystemData_UserList);

// Make up the sql sentence to query data from database.
if (!db.ExecuteQuery(SqlSentences.SystemData_UserList_QueryAllData)) { throw new Exception(ErrorConst.DATABASE_QUERYFAIL); }

// Get the result to array.
DataSet dataset = db.GetRows();
if (dataset == null) { return true; }

// Save the data to document.
if (!Modules_SystemData_UserList.GetXmlDataDocument(dataset, ref m_cErrorRecord, ref m_docTargetData))
{
m_cErrorRecord.ErrorPosition = "Modules_SystemData_UserList.GetXmlDataDocument";
throw new Exception(ErrorConst.SYSTEMDATA_USERLIST_S***EDATATODOCUMENTFAIL);
}

// Save the Xml document to file.
if (String.IsNullOrEmpty(m_strFilePath)) { throw new Exception(ErrorConst.SYSTEMDATA_USERLIST_FILEPATHISNULL); }
m_docTargetData.Save(m_strFilePath);
}
catch(Exception ex)
{
// Record the error info
m_cErrorRecord.WriteErrorInfo("", ex.Message.ToString(), "UserList", "");
}
return bResult;
}
}
}
其中第一部分加粗部分,是调用主功能模块,其中将日志处理类进行传入,目的是为了在主功能模块中对异常进行捕获和记录,当然了,每个功能模块都需要将日志处理类进行引入,而第二部分加粗的部分是记录已经捕获的异常,但是,请注意,这里在记录异常信息的同时,只记录的发生异常的流程的类名,其余信息呢?因为当异常再次被抛出到这里的时候,其余包括最底层异常信息及发生的函数和发生异常模块的信息已经记录到日志类中了,所以没有必要重复记录,而且这里也不可能记录到这些信息哦。

下面请看下主功能模块中是如何对日志类进行操作的:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Data;

using Backup_Project37.Basic;
using Backup_Project37.Common;

namespace Backup_Project37.Modules.SystemData
{
static class Modules_SystemData_UserList
{
// Function: Get the data from the dataset, then save the data into the document.
public static bool GetXmlDataDocument(DataSet dataset, ref ErrorRecord cErrorRecord, ref XmlDocument docDataXmlDocument)
{
bool bResult = false;
try
{
DataRowCollection arrRecordset = dataset.Tables[0].Rows;
XmlNode node = docDataXmlDocument.SelectSingleNode("//UserList");

// Save the data to document.
for (int i = 0; i < arrRecordset.Count; i++)
{
XmlElement element = docDataXmlDocument.CreateElement("User");

XmlElement elementLoginName = docDataXmlDocument.CreateElement("LoginName");
elementLoginName.InnerText = arrRecordset[0]["LoginName"].ToString();
XmlElement elementName = docDataXmlDocument.CreateElement("Name");
elementName.InnerText = arrRecordset[0]["Name"].ToString();
XmlElement elementPwd = docDataXmlDocument.CreateElement("Pwd");
elementPwd.InnerText = arrRecordset[0]["Pwd"].ToString();
XmlElement elementRights = docDataXmlDocument.CreateElement("Rights");
elementRights.InnerText = arrRecordset[0]["Rights"].ToString();
XmlElement elementUnitID = docDataXmlDocument.CreateElement("UnitID");
elementUnitID.InnerText = arrRecordset[0]["UnitID"].ToString();
XmlElement elementCreateTime = docDataXmlDocument.CreateElement("CreateTime");
elementCreateTime.InnerText = arrRecordset[0]["CreateTime"].ToString();
XmlElement elementAttrXml = docDataXmlDocument.CreateElement("AttrXml");
string strXml = Basic_Common.RemoveXmlHeadPart(arrRecordset[0]["AttrXml"].ToString());
if (String.IsNullOrEmpty(strXml))
{
cErrorRecord.ErrorPosition = "Basic_Common.RemoveXmlHeadPart";
throw new Exception(ErrorConst.STRING_REMOVEXMLHEADPARTFAIL);
}
elementAttrXml.InnerXml = strXml;

element.AppendChild(elementLoginName);
element.AppendChild(elementName);
element.AppendChild(elementPwd);
element.AppendChild(elementRights);
element.AppendChild(elementUnitID);
element.AppendChild(elementCreateTime);
element.AppendChild(elementAttrXml);

node.AppendChild(element);
}
bResult = true;
}
catch (Exception ex)
{
cErrorRecord.WriteErrorInfo("", ex.Message.ToString(), "", "Modules_SystemData_UserList");
}
return bResult;
}
}
}
在主要功能函数进行处理的地方,我们都添加了执行正确与否的判断,并及时记录下发生异常的函数位置,如上第一处加粗处,而下面的部分记录的主要是异常信息和发生异常的模块的名称,这样和前面的信息一起组成完整的日志信息,让你可以一眼知道是哪里发生了问题,有一点值得注意,日志类只传递了这一层,目的显而易见,因为三段式结构独有这样的特殊结构,才能让这样的日志记录方式有所优势。

当然,我们可以再次修改日志处理类,让其按照自己熟悉的记录方式和展现方式进行工作,但我们的目的是一致的,就是让错误无处隐藏,尽量减少我们排查错误的时间,相信这可绝不是一丁点时间,尤其是在后期测试和维护中!

这只是一种简单的实现方式,目前自己也是在不断尝试,希望能设计处更好更方便的代码结构,同时也希望大家能够不吝赐教,谢谢~

下一篇,将针对三段式编程的单元测试的实现进行详细地介绍,更新中……
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐