You want to transform your stylesheet into another stylesheet that is instrumented with debug traces.
Oliver Becker developed a handy stylesheet transformation that takes any input stylesheet and produces an output stylesheet with trace instrumentation:
<!-- Trace utility, modifies a stylesheet to produce trace messages Version 0.2 GPL (c) Oliver Becker, 2002-02-13 [email protected] --> <xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:trace="http://www.obqo.de/XSL/Trace" xmlns:alias="http://www.w3.org/TransformAlias" exclude-result-prefixes="alias"> <xsl:namespace-alias stylesheet-prefix="alias" result-prefix="xsl" /> <!-- <xsl:output indent="yes" /> --> <!-- XSLT root element --> <xsl:template match="xsl:stylesheet | xsl:transform"> <xsl:copy> <!-- We need the trace namespace for names and modes --> <xsl:copy-of select="document('')/*/namespace::trace" /> <!-- dito: perhaps a namespace was used only as attribute value --> <xsl:copy-of select="namespace::*|@*" /> <xsl:apply-templates /> <!-- append utility templates --> <xsl:copy-of select="document('')/*/xsl:template [@mode='trace:getCurrent' or @name='trace:getPath']" /> <!-- compute the lowest priority and add a default template with a lower priority for element nodes --> <xsl:variable name="priority" select="xsl:template/@priority [not(. > current( )/xsl:template/@priority)]" /> <xsl:variable name="newpri"> <xsl:choose> <xsl:when test="$priority < -1"> <xsl:value-of select="$priority - 1" /> </xsl:when> <!-- in case there's only a greater or no priority at all --> <xsl:otherwise>-2</xsl:otherwise> </xsl:choose> </xsl:variable> <!-- copy the contents only --> <alias:template match="*" priority="{$newpri}"> <xsl:copy-of select="document('')/*/xsl:template [@name='trace:defaultRule']/node( )" /> </alias:template> </xsl:copy> </xsl:template> <!-- XSLT templates --> <xsl:template match="xsl:template"> <xsl:copy> <xsl:copy-of select="@*" /> <!-- first: copy parameters --> <xsl:apply-templates select="xsl:param" /> <alias:param name="trace:callstack" /> <xsl:choose> <xsl:when test="@name"> <alias:variable name="trace:current" select="concat($trace:callstack,'/{@name}')" /> </xsl:when> <xsl:otherwise> <alias:variable name="trace:current" select="concat($trace:callstack, '/{count(preceding-sibling::xsl:template)+1}')" /> </xsl:otherwise> </xsl:choose> <!-- emit a message --> <alias:message> <alias:call-template name="trace:getPath" /> <alias:text>
 stack: </alias:text> <alias:value-of select="$trace:current" /> <xsl:if test="@match or @mode"> <alias:text> (</alias:text> <xsl:if test="@match"> <alias:text>match="<xsl:value-of select="@match" />"</alias:text> <xsl:if test="@mode"> <alias:text><xsl:text> </xsl:text></alias:text> </xsl:if> </xsl:if> <xsl:if test="@mode"> <alias:text>mode="<xsl:value-of select="@mode" />"</alias:text> </xsl:if> <alias:text>)</alias:text> </xsl:if> <xsl:apply-templates select="xsl:param" mode="traceParams" /> </alias:message> <!-- process children except parameters --> <xsl:apply-templates select="node( )[not(self::xsl:param)]" /> </xsl:copy> </xsl:template> <!-- add the callstack parameter for apply-templates and call-template --> <xsl:template match="xsl:apply-templates | xsl:call-template"> <xsl:copy> <xsl:copy-of select="@*" /> <alias:with-param name="trace:callstack" select="$trace:current" /> <xsl:apply-templates /> </xsl:copy> </xsl:template> <!-- output parameter values --> <xsl:template match="xsl:param" mode="traceParams"> <alias:text>
 param: name="<xsl:value-of select="@name" />" value="</alias:text> <alias:value-of select="${@name}" />" <alias:text /> <!-- <alias:copy-of select="${@name}" />" <alias:text /> --> </xsl:template> <!-- output variable values --> <xsl:template match="xsl:variable"> <xsl:copy> <xsl:copy-of select="@*" /> <xsl:apply-templates /> </xsl:copy> <xsl:if test="ancestor::xsl:template"> <alias:message> variable: name="<xsl:value-of select="@name" />" value="<alias:text /> <alias:value-of select="${@name}" />" </alias:message> </xsl:if> </xsl:template> <!-- copy every unprocessed node --> <xsl:template match="*|@*"> <xsl:copy> <xsl:apply-templates select="@*" /> <xsl:apply-templates /> </xsl:copy> </xsl:template> <!-- *************************************************************** --> <!-- The following templates will be copied into the modified --> <!-- stylesheet --> <!-- *************************************************************** --> <!-- | trace:getPath | compute the absolute path of the context node +--> <xsl:template name="trace:getPath"> <xsl:text>node: </xsl:text> <xsl:for-each select="ancestor::*"> <xsl:value-of select="concat('/', name( ), '[', count(preceding-sibling::*[name( )=name(current( ))])+1, ']')" /> </xsl:for-each> <xsl:apply-templates select="." mode="trace:getCurrent" /> </xsl:template> <!-- | trace:getCurrent | compute the last step of the location path, depending on the | node type +--> <xsl:template match="*" mode="trace:getCurrent"> <xsl:value-of select="concat('/', name( ), '[', count(preceding-sibling::*[name( )=name(current( ))])+1, ']')" /> </xsl:template> <xsl:template match="@*" mode="trace:getCurrent"> <xsl:value-of select="concat('/@', name( ))" /> </xsl:template> <xsl:template match="text( )" mode="trace:getCurrent"> <xsl:value-of select="concat('/text( )[', count(preceding-sibling::text( ))+1, ']')" /> </xsl:template> <xsl:template match="comment( )" mode="trace:getCurrent"> <xsl:value-of select="concat('/comment( )[', count(preceding-sibling::comment( ))+1, ']')" /> </xsl:template> <xsl:template match="processing-instruction( )" mode="trace:getCurrent"> <xsl:value-of select="concat('/processing-instruction( )[', count(preceding-sibling::processing-instruction( ))+1, ']')" /> </xsl:template> <!-- | trace:defaultRule | default rule with parameter passing +--> <xsl:template name="trace:defaultRule"> <xsl:param name="trace:callstack" /> <xsl:message> <xsl:call-template name="trace:getPath" /> <xsl:text>
 default rule applied</xsl:text> </xsl:message> <xsl:apply-templates> <xsl:with-param name="trace:callstack" select="$trace:callstack" /> </xsl:apply-templates> </xsl:template> </xsl:transform>
Here is a sample of the debug output produced when this
transformation was applied to
postorder.orgchart.xslt
from Recipe 4.6:
node: /employee[1] stack: /1 (match="/employee") node: /employee[1]/employee[1] stack: /1/2 (match="employee[employee]") node: /employee[1]/employee[1]/employee[1] stack: /1/2/3 (match="employee") node: /employee[1]/employee[1]/employee[2] stack: /1/2/2 (match="employee[employee]") node: /employee[1]/employee[1]/employee[2]/employee[1] stack: /1/2/2/3 (match="employee") node: /employee[1]/employee[1]/employee[2] stack: /1/2/2/reportsTo node: /employee[1]/employee[1]/employee[2] stack: /1/2/2/HimHer node: /employee[1]/employee[1] stack: /1/2/reportsTo node: /employee[1]/employee[1] stack: /1/2/HimHer node: /employee[1]/employee[2] stack: /1/2 (match="employee[employee]") node: /employee[1]/employee[2]/employee[1] stack: /1/2/3 (match="employee") node: /employee[1]/employee[2]/employee[2] stack: /1/2/2 (match="employee[employee]") node: /employee[1]/employee[2]/employee[2]/employee[1] stack: /1/2/2/3 (match="employee") node: /employee[1]/employee[2]/employee[2]/employee[2] stack: /1/2/2/3 (match="employee") node: /employee[1]/employee[2]/employee[2]/employee[3] stack: /1/2/2/3 (match="employee") node: /employee[1]/employee[2]/employee[2] stack: /1/2/2/reportsTo node: /employee[1]/employee[2]/employee[2] stack: /1/2/2/HimHer node: /employee[1]/employee[2] stack: /1/2/reportsTo node: /employee[1]/employee[2] stack: /1/2/HimHer node: /employee[1]/employee[3] stack: /1/2 (match="employee[employee]") node: /employee[1]/employee[3]/employee[1] stack: /1/2/2 (match="employee[employee]") node: /employee[1]/employee[3]/employee[1]/employee[1] stack: /1/2/2/3 (match="employee") node: /employee[1]/employee[3]/employee[1]/employee[2] stack: /1/2/2/3 (match="employee") node: /employee[1]/employee[3]/employee[1]/employee[3] stack: /1/2/2/3 (match="employee") node: /employee[1]/employee[3]/employee[1] stack: /1/2/2/reportsTo node: /employee[1]/employee[3]/employee[1] stack: /1/2/2/HimHer node: /employee[1]/employee[3]/employee[2] stack: /1/2/2 (match="employee[employee]") node: /employee[1]/employee[3]/employee[2]/employee[1] stack: /1/2/2/2 (match="employee[employee]") node: /employee[1]/employee[3]/employee[2]/employee[1]/employee[1] stack: /1/2/2/2/3 (match="employee") node: /employee[1]/employee[3]/employee[2]/employee[1] stack: /1/2/2/2/reportsTo node: /employee[1]/employee[3]/employee[2]/employee[1] stack: /1/2/2/2/HimHer node: /employee[1]/employee[3]/employee[2]/employee[2] stack: /1/2/2/2 (match="employee[employee]") node: /employee[1]/employee[3]/employee[2]/employee[2]/employee[1] stack: /1/2/2/2/3 (match="employee") node: /employee[1]/employee[3]/employee[2]/employee[2]/employee[2] stack: /1/2/2/2/3 (match="employee") node: /employee[1]/employee[3]/employee[2]/employee[2] stack: /1/2/2/2/reportsTo node: /employee[1]/employee[3]/employee[2]/employee[2] stack: /1/2/2/2/HimHer node: /employee[1]/employee[3]/employee[2]/employee[3] stack: /1/2/2/2 (match="employee[employee]") node: /employee[1]/employee[3]/employee[2]/employee[3]/employee[1] stack: /1/2/2/2/3 (match="employee") node: /employee[1]/employee[3]/employee[2]/employee[3] stack: /1/2/2/2/reportsTo node: /employee[1]/employee[3]/employee[2]/employee[3] stack: /1/2/2/2/HimHer node: /employee[1]/employee[3]/employee[2] stack: /1/2/2/reportsTo node: /employee[1]/employee[3]/employee[2] stack: /1/2/2/HimHer node: /employee[1]/employee[3] stack: /1/2/reportsTo node: /employee[1]/employee[3] stack: /1/2/HimHer node: /employee[1] stack: /1/reportsTo node: /employee[1] stack: /1/HimHer
The modified stylesheet outputs trace messages via the
xsl:message
mechanism. The format for every
processed node is as follows:
node: [XPath to this node] stack: [call stack of the templates invoked] param: name="[parameter name]" value="[parameter value]" more parameters ... variable: name="[variable name]" value="[variable value]" more variables ...
The call stack takes the form of a path (with / as separator) and includes all passed templates. If a template has a name attribute, then this name is used. Otherwise, the number (position) of the template appears within the stack. If the current template does not have a name, the match attribute is displayed. If a mode attribute is specified, its value is displayed.
One known problem is that the output for parameters or variables is
their string value (produced with
xsl:value-of
). That’s not
reasonable for node sets and result-tree fragments. However, using
xsl:copy-of
results in an error if the variable
contains attribute or namespace nodes without parents.
The trace.xslt
source and further
examples
can be
found at http://www.informatik.hu-berlin.de/~obecker/XSLT/#trace.