<xsl:apply-templates/>
<xsl:variable name="d2" select="document($document2)" />
<xsl:for-each select="$d2/e:DiagnosticData/e:DataRecord">
<xsl:copy>
<xsl:apply-templates select="@*" mode="d2"/>
<xsl:apply-templates mode="d2"/>
</xsl:copy>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:copy>
</xsl:template>
<xsl:template match="e:ColumnData[$TIMESTAMP]" mode="d2">
<xsl:copy>
<xsl:value-of select=". + $offset"/>
</xsl:copy>
</xsl:template>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="node()|@*" mode="d2">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
该样式表接收两个参数:document2是与其主要输入文档合并的文档名称;offset是可选的时间偏移量,单位为毫秒。如果指定了offset,则它的值将添加到document2的所有时间信息中。当托管WebLogic Server 实例的系统的时间不完全同步时,这个参数将非常有用。
下面这个示例将使用样式表合并两个事件数据导出文件:
java weblogic.WLST process.py -document2=server2-events.xml merge.xsl server1-events.xml > events.xml 这种方法可用于合并两个事件数据或两个采集度量文件。但是却无法将一个事件数据文件和一个采集度量文件合并在一起,因为这些文件的列结构不同。
生成逗号分隔值文件
第二个样式表将更加有用。它能够将导出文件转换为逗号分隔值格式的文件,以便导入Microsoft Excel或其他电子表格工具。该流程如图5所示。
图 5. exportToCSV.xsl样式表可以将诊断导出文件转换为逗号分隔值文件。
该样式表没有参数。
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" xmlns:e="http://www.bea.com/ns/weblogic/90/diagnostics/accessor/Export">
<xsl:output method="text"/>
<xsl:template match="e:DiagnosticData">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="e:DataInfo"/>
<xsl:template match="e:DataRecord">
<xsl:variable name="record" select="."/>
<xsl:for-each select="/e:DiagnosticData/e:DataInfo[1]/e:ColumnInfo">
<xsl:variable name="p" select="position()"/>
<xsl:value-of select="$record/e:ColumnData[$p]"/>
<xsl:if test="$p != last()">
<xsl:text>, </xsl:text>
</xsl:if>
</xsl:for-each>
<xsl:text>
</xsl:text>
</xsl:template>
<!-- Work around known issue in Java 5 (Sun bug 6413803 inherited
from XALAN-2230) which means that the xsl:stripspace directive
doesn't work with XML name spaces. We explicitly trim text nodes.
-->
<xsl:template match="text()">
<xsl:value-of select="normalize-space(.)"/>
</xsl:template>
</xsl:stylesheet>
下面这个示例将使用该样式表生成CSV文件:
java weblogic.WLST process.py eventsToCSV.xsl events.xml > events.csv
生成事件树文件
接下来我们需要使用一些技巧。导出数据文件含有各事件的信息,但是却没有记录事件之间的关系。通过使用时间信息,我们可以重新构造事件树,这样便可以了解代码各部之间的调用关系。如图6所示。
图 6. eventToTree.xsl样式表用于将诊断导出文件转换为事件树文件。
要构建事件树,我们将使用TraceElapsedTimeAction动作所记录的before和after事件之间的时间差。该时间差存储在after事件的事件有效载荷中。我们将抛弃不含有效载荷的事件,只留下after事件。通过从after事件时间信息减去有效载荷时间,我们可以算出开始时间信息,注意要转换为同样的单位(事件时间信息的单位为毫秒,而有效载荷时间的单位为纳秒)。
我们通过开始和结束时间信息可以获得被监控方法的时间跨度。通过分析各时间跨度之间的包含关系,我们可以构建出事件树。
WLDF控制台扩展 可以在服务器中生成类似的表示单个请求流动的事件树。由于控制台扩展的浏览方式非常快捷,我们的方法拥有一些优势:
通过使用merge.xsl合并多个诊断档案库中的事件,我们可以跟踪请求在多个服务器之间流动的情况。
结果将捕捉并保存到HTML报表中,以便发布和分发。
我们只使用时间范围计算事件树,不会在其关系中使用诊断上下文。这样,样式表将极具通用性,但是事件也可能会被错误地标为其他事件的原因。要避免这一点,我们可以根据诊断上下文进行筛选。为此,可以在导出诊断档案库时指定适当的WLDF查询,或者提供含有一列使用上下文参数的诊断上下文的样式表。在实际情况中,我们可以首先找出想要的诊断上下文(方法是将所有诊断数据提取到一个CSV文件中),将其导入电子表格,然后查找不正常的(也可以是典型的)事务。
样式表期望使用事件数据导出文件作为输入,并接收一个可选参数Contexts,该参数是数据源中包含的一列诊断上下文。如果没有指定上下文,那么TraceElapsedTimeAction记录的所有事件都将包括在内。
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" xmlns:exslt="http://exslt.org/common" xmlns:e="http://www.bea.com/ns/weblogic/90/diagnostics/accessor/Export">
<xsl:output method="xml" indent="yes"/>
<xsl:variable name="RECORDID" select="count(//e:ColumnInfo[e:Name='RECORDID']/preceding-sibling::*) + 1"/>
<xsl:variable name="TIMESTAMP" select="count(//e:ColumnInfo[e:Name='TIMESTAMP']/preceding-sibling::*) + 1"/>
<xsl:variable name="CONTEXTID" select="count(//e:ColumnInfo[e:Name='CONTEXTID']/preceding-sibling::*) + 1"/>
<xsl:variable name="TXID" select="count(//e:ColumnInfo[e:Name='TXID']/preceding-sibling::*) + 1"/>
<xsl:variable name="USERID" select="count(//e:ColumnInfo[e:Name='USERID']/preceding-sibling::*) + 1"/>
<xsl:variable name="DOMAIN" select="count(//e:ColumnInfo[e:Name='DOMAIN']/preceding-sibling::*) + 1"/>
<xsl:variable name="SERVER" select="count(//e:ColumnInfo[e:Name='SERVER']/preceding-sibling::*) + 1"/>
<xsl:variable name="MONITOR" select="count(//e:ColumnInfo[e:Name='MONITOR']/preceding-sibling::*) + 1"/>
<xsl:variable name="CLASSNAME" select="count(//e:ColumnInfo[e:Name='CLASSNAME']/preceding-sibling::*) + 1"/>
<xsl:variable name="METHODNAME" select="count(//e:ColumnInfo[e:Name='METHODNAME']/preceding-sibling::*) + 1"/>
<xsl:variable name="PAYLOAD" select="count(//e:ColumnInfo[e:Name='PAYLOAD']/preceding-sibling::*) + 1"/>
<xsl:param name="contexts" select=""/>
<xsl:template match="/">
<xsl:variable name="sorted-nodes">
<xsl:for-each select="//e:DataRecord[e:ColumnData[$PAYLOAD] != '' and
($contexts = '' or contains($contexts, e:ColumnData[$CONTEXTID]))]">
<xsl:sort select="e:ColumnData[$TIMESTAMP] * 1000000 - e:ColumnData[$PAYLOAD]"/>
<xsl:copy-of select="."/>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="nodes" select="exslt:node-set($sorted-nodes)/e:DataRecord"/>
<data>
<xsl:call-template name="processLevel">
<xsl:with-param name="nodes" select="$nodes"/>
<xsl:with-param name="basetime" select="$nodes[1]/e:ColumnData[$TIMESTAMP] * 1000000 - $nodes[1]/e:ColumnData[$PAYLOAD]"/>
</xsl:call-template>
</data>
</xsl:template>
<xsl:template name="processLevel">
<xsl:param name="nodes"/>
<xsl:param name="basetime"/>
<xsl:for-each select="$nodes">
<xsl:variable name="position" select="position()"/>
<xsl:variable name="endMS" select="e:ColumnData[$TIMESTAMP]"/>
<xsl:variable name="start" select="$endMS * 1000000 - e:ColumnData[$PAYLOAD]"/>
<!-- Output the nodes that are not fully enclosed by others in $nodes.
Nodes that overlap partially will all be output at this
level - their contents may contain duplicates at lower
levels.
Where the times match exactly, break the tie using the
document order. -->
<xsl:if test="not($nodes[(position() != $position) and
((e:ColumnData[$TIMESTAMP] * 1000000 - e:ColumnData[$PAYLOAD]) < $start and e:ColumnData[$TIMESTAMP] > = $endMS or
(e:ColumnData[$TIMESTAMP] * 1000000 - e:ColumnData[$PAYLOAD]) < = $start and e:ColumnData[$TIMESTAMP] > $endMS or
(e:ColumnData[$TIMESTAMP] * 1000000 - e:ColumnData[$PAYLOAD]) = $start and e:ColumnData[$TIMESTAMP] = $endMS and position() < $position
)]
)">
<record start="{$start}" offset="{$start - $basetime}" duration="{e:ColumnData[$PAYLOAD]}"
classname="{e:ColumnData[$CLASSNAME]}" methodname="{e:ColumnData[$METHODNAME]}"
domain="{e:ColumnData[$DOMAIN]}" server="{e:ColumnData[$SERVER]}" recordid="{e:ColumnData[$RECORDID]}"
userid="{e:ColumnData[$USERID]}" txid="{e:ColumnData[$TXID]}">
<!-- Recurse to include nodes in $nodes that we enclose. -->
<xsl:call-template name="processLevel">
<xsl:with-param name="nodes" select="$nodes[position() != $position and (e:ColumnData[$TIMESTAMP] * 1000000 - e:ColumnData[$PAYLOAD]) > = $start and e:ColumnData[$TIMESTAMP] < = $endMS]"/>
<xsl:with-param name="basetime" select="$start"/>
</xsl:call-template>
</record>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
格式化事件树文件
我们所生成的事件树也是一个XML文件。可以在HTML中呈现,以便于大家理解,如图7所示。
图 7. treeToHTML.xsl样式表可以将事件树文件转换为HTML报表。
该样式表期望使用eventsToTree.xsl生成的事件树文件作为输入,没有参数。
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="html"/>
<xsl:template match="/">
<html>
<body>
<table>
<tr>
<th></th>
<th>Offset</th>
<th>Method</th>
<th>Duration</th>
<th>Record</th>
<th>User</th>
<th>Transaction ID</th>
</tr>
<xsl:apply-templates/>
</table>
</body>
</html>
</xsl:template>
<xsl:template match="record">
<xsl:param name="indent" select=""/>
<xsl:param name="level" select="1"/>
<xsl:variable name="colour">
<xsl:choose>
<xsl:when test="$level=1">#F0D0D0</xsl:when>
<xsl:when test="$level=2">#D0D0F0</xsl:when>
<xsl:when test="$level=3">#C0C0E0</xsl:when>
<xsl:when test="$level=4">#B0B0D0</xsl:when>
<xsl:when test="$level=5">#A0A0C0</xsl:when>
<xsl:when test="$level=6">#9090B0</xsl:when>
<xsl:when test="$level=7">#8080A0</xsl:when>
<xsl:when test="$level=8">#707090</xsl:when>
<xsl:when test="$level=9">#606080</xsl:when>
<xsl:otherwise>#505070</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<tr bgcolor="{$colour}">
<td align="right">
<font size="-1">
<xsl:number level="any"/>
</font>
</td>
<td>
<xsl:value-of select="$indent"/>
<xsl:text>+</xsl:text>
<xsl:value-of select="format-number(@offset div 1000000, '0.00')"/>
</td>
<td>
<xsl:value-of select="$indent"/>
<xsl:value-of select="@classname"/>
<xsl:text>.</xsl:text>
<xsl:value-of select="@methodname"/>
<xsl:text>()</xsl:text>
</td>
<td align="right"><xsl:value-of select="format-number(@duration div 1000000, '0.000')"/></td>
<td>
<xsl:value-of select="@domain"/>
<xsl:text>:</xsl:text>
<xsl:value-of select="@server"/>
<xsl:text>:</xsl:text>
<xsl:value-of select="@recordid"/>
</td>
<td>
<xsl:value-of select="@userid"/>
</td>
<td>
<xsl:value-of select="@txid"/>
</td>
</tr>
<xsl:apply-templates>
<xsl:with-param name="indent" select="concat(' ', $indent)"/>
<xsl:with-param name="level" select="$level + 1"/>
</xsl:apply-templates>
</xsl:template>
</xsl:stylesheet>
就我目前正在研究的性能问题而言,这种格式相当有用。缩进用于表示事件中的继承关系。第一列的偏移量给出了父事件启动的时间,单位为毫秒。显然,我们也可以根据需要对eventsToTree.xsl和treeToHTML.xsl进行修改,使报表含有其他列或具有不同的格式。
读者练习
读者可以自己尝试使用样式表将事件树文件转换为SVG图,或者添加适当的JavaScript使事件树具有导航性和动态性。
结束语
WebLogic诊断框架是WebLogic Server中的一个强大且尚未充分应用的特性。我已经介绍了如何使用XSLT使诊断数据更具生命力。我这些技巧对于您的基于WebLogic Server的系统能够有所帮助。
参考资料
WebLogic Diagnostics Framework(WLDF)简介(中文版,Dev2Dev,2006年8月),作者为Rebecca Sly(Dev2Dev,2006)——关于WLDF的优秀入门教程
WLDF查询语言——产品文档
原文出处:http://dev2dev.bea.com/pub/a/2007/09/mining-wldf-xslt.html
作者简介
Philip Aston是BEA Services ( 专攻 WebLogic Server ) 的高级首席咨询师。他还维护 The Grinder , 一种开源负载测试工具。
文章来源于领测软件测试网 https://www.ltesting.net/