与其他人的评论相反,我认为 XML 是一种出色的日志记录格式。用于 XML 的 Delphi VCL 包装器对核心内存非常贪婪,因此这可能解释了纯 TXMLDocument 大规模处理性能不佳的原因。
相反,我建议使用简单的 XSLT 转换将其发布到您的 XML 日志中。我尚未大规模测量此解决方案的性能,但我相信这将比您当前报告的内容有很大的改进。
样式表。
例如,假设我们的日志看起来像这样......
<log>
<message>This is the first message<message/>
</log>
这个简单的 XSLT 1.0 样式表,带有参数addend-message
...
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes" omit-xml-declaration="yes" />
<xsl:strip-space elements="*" />
<xsl:param name="addend-message" select="''" />
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="log">
<xsl:copy>
<xsl:apply-templates select="@*|node()" />
<message><xsl:value-of select="$addend-message" /></message>
</xsl:copy>
</xsl:template>
... 将在日志中附加一条消息。
语言绑定
要在 Delphi 中实现这一点,请使用以下声明...
ITransform = interface
procedure AddParameter( const sParamBaseName, sParamValue, sNamespaceURI: string);
procedure Transform;
property InputDocumentFileName : string;
property OutputDocumentFileName: string;
end;
function MS_Transform( const sStylesheet: string): ITransform;
创建一个 ITransform,将样式表作为字符串传递。将两个文件名属性都设置为日志的文件名。每次您需要向日志中添加消息时,调用AddParameter()
然后Transform()
.
解决方案实施细节
上述语言绑定的一种可能实现可能是......
uses XMLIntf, msxml, msxmldom, sysutils;
type
ITransform = interface
['{1004AE9A-D4AE-40E1-956D-AD98801AF7C1}']
procedure SetInputDocumentFileName ( const sValue: string);
procedure SetOutputDocumentFileName( const sValue: string);
procedure AddParameter( const sParamBaseName, sParamValue, sNamespaceURI: string);
procedure Transform;
property InputDocumentFileName : string write SetInputDocumentFileName;
property OutputDocumentFileName: string write SetInputDocumentFileName;
end;
TMS_XSLT = class( TInterfacedObject, ITransform)
private
FStylesheet: IXSLTemplate;
FStylesheetAsDoc: IXMLDOMDocument2;
FInputFN, FOutputFN: string;
FProcessor: IXSLProcessor;;
procedure SetInputDocumentFileName ( const sValue: string);
procedure SetOutputDocumentFileName( const sValue: string);
procedure MakeProcessor;
publc
constructor Create( const sStylesheet: string);
procedure AddParameter( const sParamBaseName, sParamValue, sNamespaceURI: string);
procedure Transform;
property InputDocumentFileName : string write SetInputDocumentFileName;
property OutputDocumentFileName: string write SetInputDocumentFileName;
end;
function MS_Transform( const sStylesheet: string): ITransform
function MS_Transform( const sStylesheet: string): ITransform
begin
result := TMS_XSLT.Create( sStylesheet)
end;
constructor TMS_XSLT.Create( const sStylesheet: string);
begin
FStyleSheet := msxml.CoXSLTemplate60.Create;
FStylesheetAsDoc := msxml.CoFreeThreadedDOMDocument60.Create;
FStylesheetAsDoc.loadXML( sStyleSheetContent);
FStylesheet.stylesheet := FStylesheetAsDoc
end;
procedure TMS_XSLT.MakeProcessor;
begin
if not assigned( FProcessor) then
FProcessor := FStylesheet.createProcessor
end;
procedure TMS_XSLT.SetInputDocumentFileName( const sValue: string);
begin
FInputDoc := sValue
end;
procedure TMS_XSLT.SetOutputDocumentFileName( const sValue: string);
begin
FOutputDoc := sValue
end;
procedure TMS_XSLT.AddParameter( const sParamBaseName, sParamValue, sNamespaceURI: string);
begin
MakeProcessor;
FProcessor.addParameter( sParamBaseName, sParamValue, sNamespaceURI)
end;
procedure TMS_XSLT.Transform;
var
Doc: TXMLDocument;
DocIntf: IXMLDocument;
oXMLDOMNode: IXMLDOMNodeRef;
sOutput: string;
begin
MakeProcessor;
try
Doc := TXMLDocument.Create( nil);
Doc.Options := [doNodeAutoCreate, doNodeAutoIndent, doAttrNull, doAutoPrefix, doNamespaceDecl];
Doc.DOMVendor := GetDOMVendor( 'MSXML');
DocIntf := Doc;
DocIntf.LoadFromFile( FInputFN);
DocIntf.Active := True;
if Supports( DocIntf.Node.DOMNode, IXMLDOMNodeRef, XMLDOMNode) then
FProcessor.input := XMLDOMNode.GetXMLDOMNode;
FProcessor.transform;
while oProcessor.readyState <> 4 do sleep(1);
sOutput := FProcessor.output;
if sOutput = '' then exit;
WriteToFile( sFOutputFN, sOutput);
// Alternate way..
// Doc := TXMLDocument.Create( nil);
// Doc.Options := [doNodeAutoCreate, doNodeAutoIndent, doAttrNull, doAutoPrefix, doNamespaceDecl];
// Doc.DOMVendor := GetDOMVendor( 'MSXML');
// DocIntf := Doc;
// DocIntf.LoadFromXML( sOutput);
// DocIntf.Active := True;
// DocIntf.SaveToFile( FOutputFN)
finally
FProcessor := nil
end
end;
这绑定到Microsoft 的 MS XML 库和 XSLT 引擎。遗憾的是,我不知道将Saxon 的 XSLT 处理器绑定到 Delphi 代码的任何便捷方法。
替代实施
我在此处的回答给出了利用 MS 的 XSLT 引擎的替代实现。这种方法的缺点是参数化不是原生的。要参数化样式表,您必须自己滚动,通过在转换之前对样式表进行字符串替换。
性能注意事项
如果您正在快速进行大量日志记录,那么将要记录的消息缓存到内存中可能是一个不错的策略,然后定期但不太频繁地使用单个 XSLT 转换来清除缓存以写入所有消息。