Project

Profile

Help

RE: Performance of SaxonHE 10.6 vs 11.2 ยป log-report.xslt

Vladimir Nesterovsky, 2022-03-08 16:08

 
<?xml version="1.0" encoding="utf-8"?>
<!-- Generates log report. -->
<xsl:stylesheet version="3.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:t="http://www.bphx.com/coolgen/this-template/2007-12-12"
xmlns:d="http://www.bphx.com/coolgen/derived-data/2007-12-12"
exclude-result-prefixes="xs t d">

<xsl:output method="html"
indent="no"
html-version="5.0"
byte-order-mark="yes"
encoding="utf-8"
omit-xml-declaration="yes"/>

<xsl:param name="log" as="xs:anyURI"/>

<xsl:template match="/">
<xsl:variable name="lines" as="element()*">
<xsl:for-each select="unparsed-text-lines($log)">
<xsl:analyze-string select="."
regex="^\s*$|^([^:\[\]\s]+):$|^\s*(.*)$">
<xsl:matching-substring>
<xsl:choose>
<xsl:when test="regex-group(1)">
<d:target>
<xsl:sequence select="regex-group(1)"/>
</d:target>
</xsl:when>
<xsl:when test="regex-group(2)">
<d:line>
<xsl:sequence select="regex-group(2)"/>
</d:line>
</xsl:when>
</xsl:choose>
</xsl:matching-substring>
</xsl:analyze-string>
</xsl:for-each>
</xsl:variable>

<xsl:variable name="targets" as="element()*">
<xsl:for-each-group select="$lines" group-starting-with="d:target">
<xsl:apply-templates mode="prepare" select=".">
<xsl:with-param name="lines" as="element()*"
select="current-group()!self::d:line"/>
</xsl:apply-templates>
</xsl:for-each-group>
</xsl:variable>

<xsl:variable name="thread-count" as="xs:integer*" select="
max
(
($targets!d:line!xs:integer(@thread)=>distinct-values()=>count(), 1)
)"/>

<html>
<head>
<title>Run report</title>
<style>
<xsl:text xml:space="preserve">
pre
{
padding: 0;
margin: 0;
}

.hide
{
display: none ! important;
}

.details
{
white-space: pre;
display: block;
}

.details.data
{
padding-left: 1em;
}

[thread]
{
font-family: monospace;
border-left: .75em transparent solid;
padding-left: .25em;
}

[thread]:not([message])
{
border-left-color: lightgreen;
}

[thread][type=xml]:not([message])
{
border-left-color: lightseagreen;
}

[thread][message=error]
{
border-left-color: red;
}

[thread][message=warning],
[thread][message=exception],
[thread][message=severe]
{
border-left-color: goldenrod;
}

[thread][message=info]
{
border-left-color: skyblue;
}

.stylesheets > *,
.parameters > *
{
padding: 0 0 0 2em;
margin: 0;
}

.filter > *
{
padding: 0;
margin: 0;
}

.filter
{
position: fixed;
top: 0;
left: 0;
z-index: 10;
padding: .25em;
background-color: Window;
}

.collapsed > :not(.collapse-header)
{
display: none !important;
}

.collapsed .collapse-button:after
{
content: "...";
}

.stylesheet, .parameter
{
padding-left: 4em;
font: monospace;
}

[target]
{
padding-bottom: 1em;
}

.lines [thread],
[contains]
{
display: none;
}

[show~="all"] .lines [thread],
[show~="all"] [contains],
[show~="messages"] .lines [message],
[show~="messages"] [contains~="message"]
{
display: block;
}

[thread="1"]
{
}

[show~="1"] .lines [thread="1"],
[show~="1"] [contains~="1"]
{
display: block;
}
</xsl:text>

<xsl:for-each select="1 to $thread-count">
<xsl:variable name="bh" as="xs:integer"
select="210 - 5 * ((. - 1) idiv 2)"/>

<xsl:variable name="bl" as="xs:integer" select="
if (((. - 1) mod 2) = 1) then
80
else
40"/>

<xsl:text expand-text="yes" xml:space="preserve">
[thread="{. + 1}"]
{{
background-color: hsl({$bh}, 100%, {$bl}%);
}}

[show~="{. + 1}"] .lines [thread="{. + 1}"],
[show~="{. + 1}"] [contains~="{. + 1}"]
{{
display: block;
}}
</xsl:text>
</xsl:for-each>
</style>
</head>
<body show="messages">
<div class="filter">
<h4>
<xsl:text>Records: </xsl:text>

<select onchange="updateFilter(this.value)">
<option value="all">all</option>
<option value="messages" selected="selected">messages</option>

<xsl:for-each select="1 to $thread-count" expand-text="yes">
<option value="{.}" thread="{.}">for thread - {.}</option>
</xsl:for-each>
</select>
<xsl:text> Filter: </xsl:text>
<input oninput="updateTextFilter(this.value)"/>
</h4>
</div>
<xsl:variable name="content" as="element()*">
<xsl:apply-templates mode="report" select="$targets"/>
</xsl:variable>
<xsl:for-each-group select="$content"
group-starting-with="*[@target = 'CommonTasks.init-base']">
<xsl:if
test="current-group()[not(@target = 'CommonTasks.init-base')]">
<xsl:variable name="contains" as="xs:string*" select="
('1', current-group()!tokenize(@contains))=>
distinct-values()=>sort()"/>

<div group="true" class="collapsable">
<xsl:if test="exists($contains)">
<xsl:attribute name="contains" select="$contains"/>
</xsl:if>

<xsl:sequence select="current-group()"/>
</div>
</xsl:if>
</xsl:for-each-group>

<script>
<xsl:text xml:space="preserve">
function updateFilter(value)
{
document.body.setAttribute("show", value);
}

var updateTextFilterTimeout;

function updateTextFilter(value)
{
window.clearTimeout(updateTextFilterTimeout);

if (value)
{
value = value.trim();
}

var pattern = null;

if (value)
{
try
{
pattern = new RegExp(value, "mi");
}
catch(e)
{
// Reset pattern.
}
}

updateTextFilterTimeout = window.setTimeout(function()
{
document.querySelectorAll(".lines [thread]").forEach(function(element)
{
element.classList.
toggle("hide", pattern &amp;&amp; !pattern.test(element.textContent));
});
},
500);
}

document.body.addEventListener("click", function(e)
{
if (e.target.matches(".collapse-button"))
{
var collapsable = e.target.closest(".collapsable");

collapsable &amp;&amp; collapsable.classList.toggle("collapsed");
e.preventDefault();
}
});
</xsl:text>
</script>
</body>
</html>
</xsl:template>

<xsl:template mode="prepare" match="*">
<xsl:param name="lines" as="element()*"/>

<xsl:if test="t:has-lines($lines)">
<d:target name="{.}">
<xsl:sequence select="$lines"/>
</d:target>
</xsl:if>
</xsl:template>

<xsl:template mode="prepare"
match="d:target[. = ('build-log-report', 'init-properties')]">
</xsl:template>

<xsl:template mode="prepare" match="d:target[. = 'xslt']">
<xsl:param name="lines" as="element()*"/>

<xsl:if test="t:has-lines($lines)">
<d:target name="{.}">
<xsl:for-each select="$lines">
<xsl:variable name="line" as="element()" select="."/>

<xsl:analyze-string regex="^(?:\s*\[java\]\s*)?\[([\d]+)\]([: ])(.*)$"
select="$line">
<xsl:matching-substring>
<d:line thread="{regex-group(1)}">
<xsl:if test="regex-group(2) = ':'">
<xsl:attribute name="first" select="true()"/>
</xsl:if>

<xsl:sequence select="$line!@*"/>
<xsl:value-of select="regex-group(3)"/>
</d:line>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:sequence select="$line"/>
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:for-each>
</d:target>
</xsl:if>
</xsl:template>

<xsl:function name="t:has-lines" as="xs:boolean">
<xsl:param name="lines" as="element()*"/>

<xsl:sequence select="
exists($lines) and
not
(
(count($lines) le 2) and
matches($lines[last()], 'marked as completed.$', 'i')
)"/>
</xsl:function>

<xsl:template mode="report" match="d:target">
<xsl:variable name="collapsed" as="xs:boolean"
select="@name = ('extract')"/>

<xsl:variable name="content" as="node()*">
<xsl:apply-templates mode="#current"/>
</xsl:variable>

<xsl:variable name="contains" as="xs:string*" select="
$content!(@thread, '1', @message!'message')=>distinct-values()=>sort()"/>

<div target="{@name}" class="{'collapsable', 'collapsed'[$collapsed]}">
<xsl:if test="exists($contains)">
<xsl:attribute name="contains" select="$contains"/>
</xsl:if>

<h2 class="collapse-header">
<a href="#" class="collapse-button">
<xsl:value-of select="@name"/>
</a>
</h2>

<div class="lines">
<xsl:sequence select="$content"/>
</div>
</div>
</xsl:template>

<xsl:template mode="report" match="d:target[@name = 'CommonTasks.init-base']">
<h1 target="{@name}" class="collapse-header">
<a href="#" class="collapse-button">
<xsl:value-of select="d:line"/>
</a>
</h1>
</xsl:template>

<xsl:template mode="report" match="d:target[@name = 'xslt']">
<xsl:variable name="base-lines" as="element()*" select="d:line"/>

<xsl:variable name="name" as="element()?"
select="$base-lines[1]"/>
<xsl:variable name="stylesheets-title" as="element()?" select="
$base-lines[. = 'Stylesheet(s):'][1]"/>
<xsl:variable name="parameters-title" as="element()?" select="
$base-lines[. = 'Transformation parameters:'][1]"/>
<xsl:variable name="compile" as="element()?" select="
$base-lines[. = 'Compile stylesheet(s).'][1]"/>
<xsl:variable name="compile-time" as="element()?" select="
$base-lines[starts-with(., 'Compilation time: ')][1]"/>
<xsl:variable name="run" as="element()?" select="
$base-lines[. = 'Run transformation.'][1]"/>
<xsl:variable name="run-time" as="element()?" select="
$base-lines[starts-with(., 'Transformation time: ')][1]"/>
<xsl:variable name="run-finished" as="element()?" select="
$base-lines[. = 'Transformation is finished.'][1]"/>

<xsl:variable name="stylesheets" as="element()*" select="
$base-lines[(. >> $stylesheets-title) and ($parameters-title >> .)]"/>
<xsl:variable name="parameters" as="element()*" select="
$base-lines[(. >> $parameters-title) and ($compile >> .)]"/>

<xsl:variable name="lines" as="element()*" select="
$base-lines except
(
$name,
$stylesheets-title,
$stylesheets,
$parameters-title,
$parameters,
$compile,
$compile-time,
$run,
$run-time,
$run-finished
)"/>

<xsl:variable name="threads" as="xs:integer*"
select="$lines!xs:integer(@thread)=>distinct-values()=>sort()"/>

<xsl:variable name="content" as="node()*">
<xsl:for-each-group select="$lines"
group-adjacent="(xs:integer(@thread), 0)[1]">

<xsl:variable name="thread" as="xs:integer?"
select="xs:integer(@thread)!index-of($threads, .)[1]"/>

<xsl:for-each-group select="current-group()"
group-starting-with="*[xs:boolean(@first)]">

<xsl:variable name="line" as="element()" select="."/>

<xsl:variable name="xml-message" as="document-node()?">
<xsl:if test="starts-with($line, '&lt;')">
<xsl:try>
<xsl:sequence select="
parse-xml-fragment(string-join(current-group(), '&#10;'))"/>
<xsl:catch>
<!-- This is not xml. -->
</xsl:catch>
</xsl:try>
</xsl:if>
</xsl:variable>

<xsl:variable name="message" as="xs:string?" select="
($xml-message!(*:error, *:warning, *:info)!local-name())[1]"/>

<xsl:choose>
<xsl:when test="$xml-message">
<xsl:for-each select="$xml-message!node()">
<div tabindex="1" thread="{($thread, 1)[1]}">
<xsl:choose>
<xsl:when
test="self::*:error or self::*:warning or self::*:info">
<xsl:variable name="description" select="*:description"/>
<xsl:variable name="location" select="*:location"/>
<xsl:variable name="data"
select="* except ($description, $location)"/>

<xsl:attribute name="message" select="local-name(.)"/>

<pre class="details">
<xsl:value-of select="local-name(.)"/>
<xsl:text>: </xsl:text>
<xsl:value-of select="@code"/>
</pre>

<xsl:where-populated>
<pre class="details">
<xsl:on-non-empty>
<xsl:text>description: </xsl:text>
</xsl:on-non-empty>

<xsl:value-of
select="$description!normalize-space(.)"/>
<xsl:value-of select="text()!normalize-space(.)"/>
</pre>
</xsl:where-populated>


<xsl:where-populated>
<div class="collapsable collapsed">
<xsl:where-populated>
<pre class="details collapse-header">
<xsl:where-populated>
<a href="#" class="collapse-button">
<xsl:on-non-empty>
<xsl:text>at: </xsl:text>
</xsl:on-non-empty>

<xsl:value-of select="@module"/>

<xsl:on-non-empty>
<xsl:text>(</xsl:text>
</xsl:on-non-empty>

<xsl:where-populated>
<xsl:value-of select="@line"/>
</xsl:where-populated>

<xsl:on-non-empty>
<xsl:text>, </xsl:text>
</xsl:on-non-empty>

<xsl:where-populated>
<xsl:value-of select="@column"/>
</xsl:where-populated>

<xsl:on-non-empty>
<xsl:text>)</xsl:text>
</xsl:on-non-empty>
</a>
</xsl:where-populated>
</pre>
</xsl:where-populated>

<xsl:where-populated>
<pre class="details">
<xsl:on-non-empty>
<xsl:text>location:</xsl:text>
</xsl:on-non-empty>

<xsl:value-of select="$location"/>
</pre>
</xsl:where-populated>
</div>
</xsl:where-populated>

<xsl:if test="$data">
<xsl:variable name="text" as="xs:string" select="
$data=>serialize
(
map
{
'indent': true(),
'omit-xml-declaration': true()
}
)=>replace('^\s+|\s+$', '')"/>

<div class="collapsable collapsed">
<pre class="details collapse-header">
<a href="#" class="collapse-button">
<xsl:value-of
select="substring-before($text, '&#10;')"/>
</a>
</pre>

<pre class="details data">
<xsl:value-of
select="substring-after($text, '&#10;')"/>
</pre>
</div>
</xsl:if>
</xsl:when>
<xsl:when test="normalize-space(self::text())">
<xsl:where-populated>
<xsl:attribute name="message" select="$message"/>
</xsl:where-populated>

<xsl:variable name="text" as="xs:string"
select="replace(., '^\s+|\s+$', '')"/>

<xsl:choose>
<xsl:when test="starts-with($text, 'at: ')">
<div class="collapsable collapsed">
<pre class="details collapse-header">
<a href="#" class="collapse-button">
<xsl:value-of
select="substring-before($text, '&#10;')"/>
</a>
</pre>
<pre class="details">
<xsl:value-of
select="substring-after($text, '&#10;')"/>
</pre>
</div>
</xsl:when>
<xsl:when test="$text">
<pre class="details">
<xsl:value-of select="."/>
</pre>
</xsl:when>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<xsl:where-populated>
<xsl:attribute name="message" select="$message"/>
</xsl:where-populated>

<xsl:attribute name="type" select="'xml'"/>

<pre class="details">
<xsl:value-of select="
serialize
(
.,
map
{
'indent': true(),
'omit-xml-declaration': true()
}
)"/>
</pre>
</xsl:otherwise>
</xsl:choose>
</div>
</xsl:for-each>
</xsl:when>
<xsl:when test="
current-group()[last()][starts-with(., 'Java Result:')]">
<div tabindex="1" thread="{($thread, 1)[1]}" message="error">
<pre class="details">
<xsl:value-of select="current-group()" separator="&#10;"/>
</pre>
</div>
</xsl:when>
<xsl:otherwise>
<xsl:analyze-string regex="^(warning|error|severe|[\p{{L}}0-9\.]*exception).*$"
select="$line"
flags="i">
<xsl:matching-substring>
<div tabindex="1" thread="{($thread, 1)[1]}"
message="{
let $message := regex-group(1)=>lower-case() return
if (contains($message, 'exception')) then
'exception'
else
$message
}">
<pre class="details">
<xsl:value-of select="current-group()"
separator="&#10;"/>
</pre>
</div>
</xsl:matching-substring>
<xsl:non-matching-substring>
<pre tabindex="1" thread="{($thread, 1)[1]}">
<xsl:value-of select="current-group()" separator="&#10;"/>
</pre>
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:for-each-group>
</xsl:variable>

<xsl:variable name="contains" as="xs:string*" select="
$content!(@thread, @message!'message')=>distinct-values()=>sort()"/>

<div target="{@name}" class="collapsable">
<xsl:if test="exists($contains)">
<xsl:attribute name="contains" select="$contains"/>
</xsl:if>

<h2 class="collapse-header">
<a href="#" class="collapse-button">
<xsl:value-of select="$name"/>
</a>

<xsl:sequence>
<xsl:on-non-empty> (xslt, compile: </xsl:on-non-empty>
<xsl:value-of
select="substring-after($compile-time, 'Compilation time: ')"/>
</xsl:sequence>

<xsl:sequence>
<xsl:on-non-empty> run: </xsl:on-non-empty>
<xsl:value-of
select="substring-after($run-time, 'Transformation time: ')"/>
<xsl:on-non-empty>)</xsl:on-non-empty>
</xsl:sequence>
</h2>

<xsl:where-populated>
<div class="stylesheets collapsable collapsed">
<xsl:on-non-empty>
<h4 class="collapse-header">
<a href="#" class="collapse-button">Stylesheets: </a>
</h4>
</xsl:on-non-empty>

<xsl:for-each select="$stylesheets">
<div class="stylesheet">
<xsl:value-of select="replace(., '^\s+', '')"/>
</div>
</xsl:for-each>
</div>
</xsl:where-populated>

<xsl:where-populated>
<div class="parameters collapsable collapsed">
<xsl:on-non-empty>
<h4 class="collapse-header">
<a href="#" class="collapse-button">Parameters: </a>
</h4>
</xsl:on-non-empty>

<xsl:for-each select="
$parameters[not(ends-with(., '(omit unbound ant parameter)'))]">
<div class="parameter">
<xsl:value-of select="replace(., '^\s+', '')"/>
</div>
</xsl:for-each>
</div>
</xsl:where-populated>

<div class="lines">
<xsl:sequence select="$content"/>
</div>
</div>
</xsl:template>

<xsl:template mode="report" match="d:line">
<pre tabindex="1">
<xsl:value-of select="."/>
</pre>
</xsl:template>

</xsl:stylesheet>
    (1-1/1)