tags:

views:

26

answers:

3

I am currently working on a project that uses the Windows event log. I am using wevtutil to get the results from the event logs. I know that wevtutil supports xpath queries, but since I'm new to xpath I don't know that I can achieve what I'm trying to do.

In SQL, what I would be doing is something like this:

SELECT log.*, COUNT(1) numHits
FROM Application log
GROUP BY Source, Task, Level, Description
ORDER BY numHits DESC
LIMIT 10

Is it possible to do such a thing using xpath?

Edit: Here is a sample Event:


<Event xmlns='http://schemas.microsoft.com/win/2004/08/events/event'&gt;

  <System>
    <Provider Name='MSSQL$SQLEXPRESS' />
    <EventID Qualifiers='16384'>17403</EventID>
    <Level>4</Level>
    <Task>2</Task>
    <Keywords>0x80000000000000</Keywords>
    <TimeCreated SystemTime='2010-10-20T20:06:18.000Z' />
    <EventRecordID>9448</EventRecordID>
    <Channel>Application</Channel>
    <Computer>SHAZTOP</Computer>
    <Security />
  </System>
  <EventData>
    <Data>73094</Data>
    <Binary>
    FB4300000A000000130000005300480041005A0054004F0050005C00530051004C004500580050005200450053005300000000000000</Binary>
  </EventData>
</Event>

A: 

You can use the position() function to limit the results you're getting:

/root/element[position()<=10]

For example, that would select the first ten element elements which are children of the root.

If your structure is more complicated, you can use the position element in different places. For example, if the element element can exist in more than one parent, but you want the first ten of them regardless of parent, you can do it this way:

(/root/parent1/element | /root/parent2/element)[position()<=10]
Welbog
That sort of helps. How would I do the grouping though? I'll edit the original post to share a sample Event.
Skudd
XPath doesn't have grouping built into it.
Welbog
+1  A: 

In SQL, what I would be doing is something like this:

SELECT log.*, COUNT(1) numHits 
FROM Application log 
GROUP BY Source, Task, Level, Description 
ORDER BY numHits DESC 
LIMIT 10

Is it possible to do such a thing using xpath?

In case no sorting is necessary, one can get the first $n nodes selected by any XPath expression by:

(ExpressionSelectingNodeSet)[not(position() > $n)]

where $n can be substituted by a specific number

If there is a requirement that the nodes be sorted on one or more sort-keys, then this is not possible pure XPath, but one can easily perform such tasks with XSLT, using the <xsl:sort> instruction and the XPath position() function:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="/*">
  <nums>
   <xsl:for-each select="num">
    <xsl:sort data-type="number" order="descending"/>
     <xsl:if test="not(position() > 5)">
      <xsl:copy-of select="."/>
     </xsl:if>
   </xsl:for-each>
  </nums>
 </xsl:template>
</xsl:stylesheet>

When this transformation is applied on the following XML document:

<nums>
  <num>01</num>
  <num>02</num>
  <num>03</num>
  <num>04</num>
  <num>05</num>
  <num>06</num>
  <num>07</num>
  <num>08</num>
  <num>09</num>
  <num>010</num>
</nums>

the correct result, containing only the top 5 numbers is produced:

<nums>
    <num>010</num>
    <num>09</num>
    <num>08</num>
    <num>07</num>
    <num>06</num>
</nums>
Dimitre Novatchev
+1  A: 

XPath 1.0 has four data types: string, number, boolean and node set.

The only XPath ordering criteria is document order (in the given axis direction). That is how you can limit any result node set as @Dimitre and @Welbog have sugested with fn:position().

But, there is no specification that an XPath engine must provide a node set result in any given order. So, you can't sort nor grouping in XPath 1.0. You can select the firsts of each group, but not efficiently. As example:

//Event[not(System/Level = preceding::Level) or 
        not(System/Task = preceding::Task)]

XPath 2.0 has the sequence data type. A sequence has the exclicit order of construction. So, you can group. As example:

for $event (//Event)[index-of(//Event/System/concat(Level,'++',Task),
                              System/concat(Level,'++',Task))[1]]
result //Event[System/Level = $event/System/Level]
              [System/Task = $event/System/Task]

But, because XPath 2.0 has not built-in sorting nor recursion mechanism (you could provide an extension function...) you can't sort.

For that you need a language with built-in sorting or a way to express its algorithm. Both XSLT (1.0 or 2.0) and XQuery have these features.

Alejandro