一起谈.NET技术,Xml日志记录文件最优方案(附源代码)

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

  (一)设计概要

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

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


<!ENTITY yourEntityRefName SYSTEM
"your xml fragement address(relative or obsolute address) ">

  (二)xml文件的生成

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

private static voidInitXmlFile(stringxmlLogFilePath,stringxmlLogContentFileName,stringentityRef)
{
stringdocType= string.Format("\n<!DOCTYPEXmlLogFile\n[\n<!ENTITY{0}SYSTEM\"{1}\">\n]>\n",entityRef,xmlLogContentFileName);
XmlWriterSettingswrapperSettings=newXmlWriterSettings()
{
Indent=true
};
using(XmlWriterwriter=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来进行操作的。这个方法比较简单,不再讲解,看下我们通过这个方法生成的文件内容:

<?xmlversion="1.0"encoding="utf-8"?>
<!DOCTYPEXmlLogFile
[
<!ENTITYLocationsSYSTEM"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,其中涉及到反射的部分内容。

privatestaticvoidInitEntityRefFile(stringxmlLogContentFilePath,objectlogObject,stringentityRef)
{
using(FileStreamfileStream=newFileStream(xmlLogContentFilePath,FileMode.Append,
FileAccess.Write,FileShare.Read))
{
XmlWriterSettingssettings=newXmlWriterSettings()
{
ConformanceLevel=ConformanceLevel.Fragment,
Indent=true,
OmitXmlDeclaration=false
};

WriteContent(logObject,fileStream,settings);
}
}

privatestaticvoidWriteContent(objectlogObject,FileStreamfileStream,XmlWriterSettingssettings)
{
using(XmlWriterwriter=XmlWriter.Create(fileStream,settings))
{
Typetype=logObject.GetType();
writer.WriteStartElement(type.Name);
writer.WriteAttributeString(ConfigResource.Id,logObject.GetHashCode().ToString());

if(logObject.GetType().IsPrimitive||
(logObject.GetType()==typeof(string)))
{
writer.WriteElementString(logObject.GetType().Name,logObject.ToString());
}
else
{
PropertyInfo[]infos=type.GetProperties();
foreach(PropertyInfoinfoininfos)
{
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("stringtypesample");
XmlLogHelper.Write(3.3);
XmlLogHelper.Write(DateTime.Now);
Errorerror=newError()
{
Time=DateTime.Now,
Point=newSystem.Drawing.Point(0,0),
Description="C#Error",
Level=2,
Name="Error"
};
XmlLogHelper.Write(error);

  输出内容如下:

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

privatestaticreadonlyobjectlockObject=newobject();

publicstaticvoidWrite(objectlogObject)
{
if(logObject==null)
{
return;
}

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

privatestaticvoidWriting(objectlogObject)
{
stringentityRef=ConfigResource.EntityRef;
stringbaseDirectory=InitDirectory();
stringbaseName=DateTime.Now.ToString("yyyyMMddHHmmss");
stringxmlLogFilePath=Path.Combine(baseDirectory,string.Format(ConfigResource.XmlLogFileName,baseName));
XmlLogHelper.XmlFilePath=xmlLogFilePath;
stringxmlLogContentFileName=string.Format(ConfigResource.XmlLogContentFileName,baseName);
stringxmlLogContentFilePath=Path.Combine(baseDirectory,xmlLogContentFileName);

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

InitEntityRefFile(xmlLogContentFilePath,logObject,entityRef);
}

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


lock (lockObject)
{
Writing(logObject);
}

采用using来及时释放掉资源。


using (FileStream fileStream = new FileStream(xmlLogContentFilePath, FileMode.Append,
FileAccess.Write, FileShare.Read))
{

}

  (五)单元测试

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

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

privatestaticvoidAssertXmlContent(List<Error>errors)
{
foreach(Errorerrorinerrors)
{
XmlLogHelper.Write(error);

XmlDocumentdoc=GetXmlDocument();
XmlNodenode=doc.SelectSingleNode("//Error[@Id='"+error.GetHashCode().ToString()+"']");
Assert.IsTrue(node.Name==typeof(Error).Name);

stringpath=string.Format("//Error[@Id='{0}']//",error.GetHashCode().ToString());
XmlNodelevelNode=doc.SelectSingleNode(path+"Level");
XmlNodenameNode=doc.SelectSingleNode(path+"Name");
XmlNodedescriptionNode=doc.SelectSingleNode(path+"Description");
XmlNodetimeNode=doc.SelectSingleNode(path+"Time");
XmlNodepointNode=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日志文件来对日志进行记录。

时间: 2024-08-02 17:39:49

一起谈.NET技术,Xml日志记录文件最优方案(附源代码)的相关文章

Xml日志记录文件最优方案

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

Xml日“.NET研究”志记录文件最优方案(附源代码)

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

一起谈.NET技术,ASP.NET MVC的全球化方案

由于项目需要最近在学习ASP.NET MVC.在实践中,网站要支持多语言,需要全球化.在MVC下我实现了一个全球化框架,在这里与各位分享一下,不足之处也请各位看官指教. 让URL支持全球化 经常上微软网站的朋友可能很熟悉类似包含..\zh-cn\.....\en-us\..的url形式,这就是本文要使用的全球化方案.当然还有使用QueryString传递参数的方案,基本思路我想是类似的. 由于MVC天生的URL路由原理,使得这个方案很容易被接受. 基本思路 这个方案的基本思路是: 1.当用户访问

一起谈.NET技术,服务器日志法网站分析的原理及优缺点

     [前言] 应朋友们的要求,我还是写一篇关于服务器日志法进行网站分析的原理以及它的优缺点是什么.请朋友们注意,网站服务器日志法并不容易进行,初学者,以及在绝大多数情况下,进行以用户行为分析为核心的网站分析,用不到服务器日志法.不过,作为网站分析历史不可分割的一部分以及重要的基础篇章,服务器日志法仍然值得一书.下面的这篇文章也是我要撰写的书中截取的内容(我要快马加鞭快快写了,已经辜负了太多朋友的重托,抱歉抱歉!).      [正文] 网站分析收集数据的方式其实有五.六种之多,我们最常见的

一起谈.NET技术,asp.net利用log4net写入日志到SqlServer数据库

asp.net利用log4net写入日志到SqlServer数据库,Log4net是一个开源的错误日志记录项目,易用性强,源自log4j,品质值得信赖. 下面就我的安装部署log4net到MS sql server的经验与大家分享. 下载log4net的Dll文件并引入到项目中,在你的web主项目(比如MainWeb.csproj)添加对log4net.dll的引用,并在相应的Globall.cs文件中的Application_Start 事件中写入 protected void Applica

c语言-如何编写一个可以记录post日志的文件

问题描述 如何编写一个可以记录post日志的文件 就是在post的时候,记录下post发出的信息,然后如果信息发送不成功,存储下来,等 有网络连接的时候再进行发送,可以吗,用C语言或者shell 解决方案 相当于实现一个代理服务器http://download.csdn.net/detail/qq32219893/2240901 解决方案二: 自己post的时候根据返回值.如果失败了.就保存数据到一个自定义格式文件.等下次再读取文件发送.

一起谈.NET技术,.NET的资源并不限于.resx文件,你可以采用任意存储形式 [下篇]

在<上篇>中我们谈到ResourceManager在默认的情况下只能提供对内嵌于程序集的.resources资源文件的存取.为了实现对独立二进制.resources资源文件的支持,我们自定义了BinaryResoruceNManager.在本篇中我们还将创建两个自定义的ResourceManager,以实现对独立.resx资源文件和自定义结构的XML资源文件的支持.(文中的例子从这里下载) 一.自定义ResXResourceManager实现对.Resx资源文件的支持 二.将资源定义在自定义结

一起谈.NET技术,Discuz!NT 缓存设计简析 [原创]

       作为一个社区类型软件,大并发支持和高效稳定运行永远是"硬道理",而有效安全的使用缓存恰恰能起到事倍功半的效果.而.NET本身所提供的缓存机制又显得过于"单薄",比如说订制不太灵活方便, 缓存对象之间层次感不强, 使用时缺乏统一的管理等等.           Discuz!NT缓存产生背景:        在去年五月份我加入Discuz!NT项目组时,发现这个项目当时还未使用缓存机制.主要原因是项目还处于起步阶段,很多东西还只是有想法,但未付诸实施,或

一起谈.NET技术,.Net语言中关于AOP 的实现详解

文章主要和大家讲解开发应用系统时在.Net语言中关于AOP 的实现.LogAspect完成的功能主要是将Advice与业务对象的方法建立映射,并将其添加到Advice集合中.由于我们在AOP实现中,利用了xml配置文件来配置PointCut,因此对于所有Aspect而言,这些操作都是相同的,只要定义了正确的配置文件,将其读入即可.对于Aspect的SyncProcessMessage(),由于拦截和织入的方法是一样的,不同的只是Advice的逻辑而已,因此在所有Aspect的公共基类中已经提供了