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

首先,请简要熟悉下工程中的“三段式”结构:

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

 

大家同时看到在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_SAVEDATATODOCUMENTFAIL);

                }

 

                // 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;

        }

    }

}

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

 

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

 

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

 

下一篇,将针对三段式编程的单元测试的实现进行详细地介绍,更新中……

Stkcd [股票代码] ShortName [股票简称] Accper [统计截止日期] Typrep [报表类型编码] Indcd [行业代码] Indnme [行业名称] Source [公告来源] F060101B [净利润现金净含量] F060101C [净利润现金净含量TTM] F060201B [营业收入现金含量] F060201C [营业收入现金含量TTM] F060301B [营业收入现金净含量] F060301C [营业收入现金净含量TTM] F060401B [营业利润现金净含量] F060401C [营业利润现金净含量TTM] F060901B [筹资活动债权人现金净流量] F060901C [筹资活动债权人现金净流量TTM] F061001B [筹资活动股东现金净流量] F061001C [筹资活动股东现金净流量TTM] F061201B [折旧摊销] F061201C [折旧摊销TTM] F061301B [公司现金流1] F061302B [公司现金流2] F061301C [公司现金流TTM1] F061302C [公司现金流TTM2] F061401B [股权现金流1] F061402B [股权现金流2] F061401C [股权现金流TTM1] F061402C [股权现金流TTM2] F061501B [公司自由现金流(原有)] F061601B [股权自由现金流(原有)] F061701B [全部现金回收率] F061801B [营运指数] F061901B [资本支出与折旧摊销比] F062001B [现金适合比率] F062101B [现金再投资比率] F062201B [现金满足投资比率] F062301B [股权自由现金流] F062401B [企业自由现金流] Indcd1 [行业代码1] Indnme1 [行业名称1] 季度数据,所有沪深北上市公司的 分别包含excel、dta数据文件格式及其说明,便于不同软件工具对数据的分析应用 数据来源:基于上市公司年报及公告数据整理,或相关证券交易所、各部委、省、市数据 数据范围:基于沪深北证上市公司 A股(主板、小企业板、创业板、科创板等)数据整理计算
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值