您现在的位置:主页 > 网络编程 > XML编程 >

Xml日志记录文件最优方案(附源代码)

来源:新2备用网址hg622.com|首页吧 责任编辑:Jzq8 发表时间:2011-02-20 点击:次

作为数据存储的一种方式,当数据非常大的时候,我们将碰到很多处理的问题。通常,我们对Xml文件进行编辑的最直接的方式是将xml文件加载到XmlDocument,在内存中来对XmlDocument进行修改,然后再保存到磁盘中。这样的话我们将不得不将整个XML document 加载到内存中,这明显是不明智的(对于大数据XML文件来说,内存将消耗很大,哥表示鸭梨很大)。下面我们将要讲的是如何高效的增加内容(对象实体内容)到xml日志文件中。

?

(一)概要

总体来说,我们将(通过代码)创建两种不同的文件,第一种为Xml文件,第二种为xml片段(txt文件),如下图所示:

我们通过如下的定义来使2个不同的文件相关联。

?

(二)xml文件的生成

先来看下如何创建相关的xml文件,代码如下:

??????? private?static?void?InitXmlFile(string?xmlLogFilePath,?string?xmlLogContentFileName,?string?entityRef)
????????{
????????????
string?docType?=?string.Format("\n"{1}\">\n?]>\n",?entityRef,?xmlLogContentFileName);
????????????XmlWriterSettings?wrapperSettings?
=?new?XmlWriterSettings()
????????????{
????????????????Indent?
=?true
????????????};
????????????
using?(XmlWriter?writer?=?XmlWriter.Create(xmlLogFilePath,?wrapperSettings))
????????????{
????????????????writer.WriteStartDocument();
????????????????writer.WriteRaw(docType);
????????????????writer.WriteStartElement(ConfigResource.XmlLogFile);

????????????????writer.WriteStartElement(ConfigResource.XmlLogContent);
????????????????writer.WriteEntityRef(entityRef);
????????????????writer.WriteEndElement();

????????????????writer.WriteEndElement();
????????????????writer.Close();
????????????}
????????}

?

?

对xml文件内容的写入主要通过XmlWriter来进行操作的。这个方法比较简单,不再讲解,看下我们通过这个方法生成的文件内容:

xml?version="1.0"?encoding="utf-8"?>
DOCTYPE?XmlLogFile?
?[?
?
ENTITY?Locations?SYSTEM?"XmlLogContentFile-20110220000120.txt">
?]
>
<XmlLogFile>
??
<XmlLogContent>&Locations;XmlLogContent>
XmlLogFile>

?

?

Locations?为实体引用名称,与之相对应的为&Locations;?? 。

XmlLogContentFile-20110220000120.txt为Xml片段的文件名称,路径是相对于XmlLogFile-20110220000120.xml的。

&Locations;相当于占位符的作用,将用XmlLogContentFile-20110220000120.txt文件的内容来替换XmlLogFile-20110220000120.xml的&Locations;

?

(三)Xml片段文件的生成

Xml片段文件的生成过程思路为:通过System.IO.FileStream和System.Xml.XmlWriter在文件的末尾处增加文件的内容(效率较高,因为是直接在文件的末尾添加的内容),内容格式为Xml,其中涉及到反射的部分内容。

????????private?static?void?InitEntityRefFile(string?xmlLogContentFilePath,?object?logObject,?string?entityRef)
????????{
????????????
using?(FileStream?fileStream?=?new?FileStream(xmlLogContentFilePath,?FileMode.Append,
????????????????File.Write,?FileShare.Read))
????????????{
????????????????XmlWriterSettings?settings?
=?new?XmlWriterSettings()
????????????????{
????????????????????ConformanceLevel?
=?ConformanceLevel.Fragment,
????????????????????Indent?
=?true,
????????????????????OmitXmlDeclaration?
=?false
????????????????};

????????????????WriteContent(logObject,?fileStream,?settings);
????????????}
????????}

????????
private?static?void?WriteContent(object?logObject,?FileStream?fileStream,?XmlWriterSettings?settings)
????????{
????????????
using?(XmlWriter?writer?=?XmlWriter.Create(fileStream,?settings))
????????????{
????????????????Type?type?
=?logObject.Type();
????????????????writer.WriteStartElement(type.Name);
????????????????writer.WriteAttributeString(ConfigResource.Id,logObject.GetHhCode().ToString());

????????????????
if?(logObject.GetType().IsPrimitive?||
????????????????(logObject.GetType()?
==?typeof(string)))
????????????????{
????????????????????writer.WriteElementString(logObject.GetType().Name,?logObject.ToString());
????????????????}
????????????????
else
????????????????{
????????????????????PropertyInfo[]?infos?
=?type.GetProperties();
????????????????????
foreach?(PropertyInfo?info?in?infos)
????????????????????{
????????????????????????
if?(ValidateProperty(info))
????????????????????????{
????????????????????????????writer.WriteElementString(info.Name,?
????????????????????????????????(info.GetValue(logObject,?
null)????string.Empty).ToString());
????????????????????????}
????????????????????}
????????????????}

????????????????writer.WriteEndElement();???????????????
????????????????writer.WriteWhitespace(
"\n");
????????????????writer.Close();
????????????}??????????????????????
????????}
??????? ??????? private static bool ValidateProperty(PropertyInfo info)
??????? {
??????????? return info.CanRead && (info.PropertyType.IsPrimitive
??????????????? || (info.PropertyType == typeof(string))
??????????????? || (info.PropertyType == typeof(DateTime)
??????????????? || (info.PropertyType == typeof(DateTime?))));
??????? }

?

?

代码????????? writer.WriteAttributeString(ConfigResource.Id,logObject.GetHashCode().ToString());

????????????????if?(logObject.GetType().IsPrimitive?||
????????????????(logObject.GetType()?
==?typeof(string)))
????????????????{
????????????????????writer.WriteElementString(logObject.GetType().Name,?logObject.ToString());
????????????????}
第一行为该实体增加一个Id特性,采用对象的哈希值来进行赋值,方便以后的单元测试(通过对象的哈希值来查找相应的Xml内容)。

余下的几行为:当实体的类型是基元类型或者字符串类型的时候,直接通过writer.WriteElementString()方法将类型名称,实体对象值作为参数直接写入xml片段文件中。

否则???????? ?else
????????????????{
????????????????????PropertyInfo[]?infos?
=?type.GetProperties();
????????????????????
foreach?(PropertyInfo?info?in?infos)
????????????????????{
????????????????????????
if?(ValidateProperty(info))
????????????????????????{
????????????????????????????writer.WriteElementString(info.Name,?
????????????????????????????????(info.GetValue(logObject,?
null)????string.Empty).ToString());
????????????????????????}
????????????????????}
????????????????}
通过反射来获取所有属性相对应的值,其中
属性必须是可读的,并且为(基元类型,string,DateTiem?,DateTime)其中一种(这个大家可以扩展一下相关功能)。

?

如下所示,我们通过基元类型float,字符串类型string,对象类型Error【Point为Error的属性,不是(基元类型,string,DateTiem?,DateTime)其中一种】来进行测试。

????????????XmlLogHelper.Write("string?type?sample");
????????????XmlLogHelper.Write(
3.3);
????????????XmlLogHelper.Write(DateTime.Now);
????????????Error?error?
=?new?Error()
????????????{
????????????????Time?
=?DateTime.Now,
????????????????Point?
=?new?System.Drawing.Point(0,?0),
????????????????Description?
=?"C#?Error",
????????????????Level?
=?2,
????????????????Name?
=?"Error"
????????????};
????????????XmlLogHelper.Write(error);

?

输出内容如下:

?

(四)采用lock来避免异常的发生,其次特别要注意对资源的及时释放。????? ???? lock?(lockObject)
????????????{
????????????????Writing(logObject);
????????????}

????????private?static?readonly?object?lockObject?=?new?object();
???????
????????
public?static?void?Write(object?logObject)
????????{
????????????
if?(logObject?==?null)
????????????{
????????????????
return;
????????????}

????????????
lock?(lockObject)
????????????{
????????????????Writing(logObject);
????????????}
????????}

????????
private?static?void?Writing(object?logObject)
????????{
????????????
string?entityRef?=?ConfigResource.EntityRef;
????????????
string?baseDirectory?=?InitDirectory();
????????????
string?baseName?=?DateTime.Now.ToString("yyyyMMddHHmmss");
????????????
string?xmlLogFilePath?=Path.Combine(baseDirectory?,string.Format(ConfigResource.XmlLogFileName,baseName));
????????????XmlLogHelper.XmlFilePath?
=?xmlLogFilePath;
????????????
string?xmlLogContentFileName?=?string.Format(ConfigResource.XmlLogContentFileName,baseName);
????????????
string?xmlLogContentFilePath?=?Path.Combine(baseDirectory,?xmlLogContentFileName);

????????????
if?(!File.Exists(xmlLogFilePath))
????????????{
????????????????InitXmlFile(xmlLogFilePath,?xmlLogContentFileName,?entityRef);
????????????}

????????????InitEntityRefFile(xmlLogContentFilePath,?logObject,?entityRef);
????????}

?

?

?????????? 采用lock来避免同时对文件进行操作,避免异常的发生,保证每次操作都是仅有一个在进行。

?

?????????? 采用using来及时释放掉资源。
???????????? using (FileStream fileStream = new FileStream(xmlLogContentFilePath, FileMode.Append,
??????????????? FileAccess.Write, FileShare.Read))
??????????? {

???????????? }

?

(五)单元测试

单元测试的主要代码如下,主要是对Write()方法进行测试,如下:

??????? [TestMethod()]
????????
public?void?WriteTest()
????????{
????????????DeleteFiles();//删除目录下所有文件,避免产生不必要的影响。
????????????List
<Error>?errors?=?InitErrorData(9);
????????????AssertXmlContent(errors);
????????}

????????
private?static?void?AssertXmlContent(List<Error>?errors)
????????{
????????????
foreach?(Error?error?in?errors)
????????????{
????????????????XmlLogHelper.Write(error);

????????????????XmlDocument?doc?
=?GetXmlDocument();
????????????????XmlNode?node?
=?doc.SingleNode("//Error[@Id='"?+?error.GetHashCode().ToString()?+?"']");
????????????????Assert.IsTrue(node.Name?
==?typeof(Error).Name);

????????????????
string?path?=?string.Format("//Error[@Id='{0}']//",?error.GetHashCode().ToString());
????????????????XmlNode?levelNode?
=?doc.SelectSingleNode(path?+?"Level");
????????????????XmlNode?nameNode?
=?doc.SelectSingleNode(path?+?"Name");
????????????????XmlNode?descriptionNode?
=?doc.SelectSingleNode(path?+?"Description");
????????????????XmlNode?timeNode?
=?doc.SelectSingleNode(path?+?"Time");
????????????????XmlNode?pointNode?
=?doc.SelectSingleNode(path?+?"Point");

????????????????Assert.IsTrue(nameNode.Name?
==?"Name");
????????????????Assert.IsTrue(levelNode.Name?
==?"Level");
????????????????Assert.IsTrue(descriptionNode.Name?
==?"Description");
????????????????Assert.IsTrue(timeNode.Name?
==?"Time");

????????????????Assert.IsNotNull(levelNode);
????????????????Assert.IsNotNull(nameNode);
????????????????Assert.IsNotNull(descriptionNode);
????????????????Assert.IsNotNull(timeNode);
????????????????Assert.IsNull(pointNode);

????????????????Assert.IsTrue(nameNode.InnerText?
==?(error.Name????string.Empty));
????????????????Assert.IsTrue(levelNode.InnerText?
==?error.Level.ToString());
????????????????Assert.IsTrue(timeNode.InnerText?
==?DateTime.MinValue.ToString());
????????????????Assert.IsTrue(descriptionNode.InnerText?
==?(error.Description????string.Empty));
????????????}
????????}

?

?

上面仅仅是针对一个自定义的Error类进行了验证................

?

(六)其他应用

当我们的Xml日志文件可以记录的时候,我们可能想通过界面来看下效果,比如如下所示意的图中,点击【生成XML日志文件】,再点击【获取XML日志文件】的时候,我们能够看到生成的XML日志文件。

?

其中生成的文件名称显示如下:

?

多次点击【生成XML日志文件】,再点击【获取XML日志文件】的时候,我们能够看到生成的XML日志文件数量也递增(因为我将文件的名称设置为string baseName = DateTime.Now.ToString("yyyyMMddHHmmss");,按照秒数来计算的)。点击任何一个文件,将显示该文件的相关全部xml内容(包括xml文件和xml片段)。

?

点击【删除XML日志文件】将删除所有的xml文件,如下:

?

(七)总结

对于流的操作来说,应尽快释放掉系统资源,促使GC的Finalize()方法的执行,同时可以避免异常的发生。对于Xml日志来说,当数据量越来越大的时候,我们可以将内容分为两部分,一部分为标准的哦xml文件,另一部分为xml片段文件。这样,我们能够在xml片段文件中方便地在文件末尾处增加相关的内容,这种效率是非常快的,而通常我们通过XMLDocument来加载数据非常消耗内存,效率较低(数据量越大越明显)。同时在读取xml文件的时候也会通过实体引用将相关的xml片段引用进来,从而使二个文件成为一个整体。再次,在将对象转换成xml的时候,通过反射来获取相关的数据,并将数据写入xml格式中,这个地方还有提高。希望各位在看完此文后也能熟练的运用XML日志文件来对日志进行记录。

源码下载:

    发表评论
    请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
    评价:
    表情:
    验证码:点击我更换图片 匿名?

    今日头条

    更多>>

    推荐图文

    • XML和实体序列化和反序列化
    • XML数据读取方式性能比较(一)
    • 给XML初学者的教程
    • 解析XML (很给力)
    • XML轻松学习手册(2)XML概念
    • XML文件的生成基本方式(代码+注释)
    • 读取XML为行记录
    • 如何使用XML实现多渠道接入网站的构架
    • JSON是什么?它能带来什么?
    Alexa - 客户服务 - 联系方法 - 招聘信息 - 友情链接 - 网站地图 - TAG标签 - RSS订阅
    Copyright ? 2010-2012 JZQ8.COM. 新2备用网址hg622.com|首页吧|新2备用网址hg622.com|首页去吧 版权所有
    冀ICP备09002514号
    冀ICP备09002514号 网络报警 企业法人营业执照 中国互联网协会 支付宝付款 网银在线付款