0

I have a group of simple PowerShell functions that retrieve system statistics, convert them to HTML fragments & then join it all together into one big HTML file at the end.

I'm having a problem with some specific functions, whereas others appear to be working fine although I'm using exactly the same principle.

This retrieves the system information:

function Get-ApplicationLogs {
    $query = '<QueryList>
              <Query Id="0" Path="Application">
              <Select Path="Application">*[System[(Level=1  or Level=2 or Level=3) and TimeCreated[timediff(@SystemTime) &lt;= 86400000]]]</Select>
              </Query>
              </QueryList>'

    $logs = Get-WinEvent -ComputerName $ComputerName -ErrorAction SilentlyContinue -FilterXml $query | Sort-Object {$_.Id}

    foreach ($log in $logs) {
        $props = [ordered]@{'Id'=$log.Id;
                  'Error Type'=$log.LevelDisplayName;
                  'Provider Name'=$log.ProviderName;
                  'Timestamp'=$log.TimeCreated;
                  'Message'=$log.Message;
                 }

        $obj = New-Object -TypeName PSObject -Property $props

        Write-Output $obj   
    }
}

This applies the logic to the function and applies the conditional formatting for the class of the output HTML code:-

$applogs = Get-ApplicationLogs -computername $computername  | 
           Select -Property *,@{
               name='class';e={
                   if ($_.'Error Type' -match 'Error' -or 'Critical') {
                       '<td class="danger">'
                   } elseif ($_.'Error Type' -match 'Warning') {
                       '<td class="warning">'
                   } else {
                       '<td>'
                   }
               }
           }

This binds it all together and executes the td class tag replacement:

$frag8 = $applogs | Select 'ID','Error Type','Provider Name','Timestamp','Message' | 
         ConvertTo-Html -As Table -Fragment -PreContent '<hr>', '<h3>Application Logs</h3>' |
         foreach { $_ -replace '<td>',$($applogs.class) } |
         Out-String

But for some reason I get a very skewed HTML output, it seems to generate more tags than required for each value:-

<hr>
<h3>Application Logs</h3>
<table>
<colgroup><col/><col/><col/><col/><col/></colgroup>
<tr><th>Id</th><th>Error Type</th><th>Provider Name</th><th>Timestamp</th><th>Message</th></tr>
<tr>
<td class="danger"> <td class="danger"> <td class="danger"> <td class="danger"> <td class="danger"> <td class="danger"> <td class="danger"> <td class="danger">10021</td>
<td class="danger"> <td class="danger"> <td class="danger"> <td class="danger"> <td class="danger"> <td class="danger"> <td class="danger"> <td class="danger">Warning</td>
<td class="danger"> <td class="danger"> <td class="danger"> <td class="danger"> <td class="danger"> <td class="danger"> <td class="danger"> <td class="danger">Windows Server Update Services</td>
<td class="danger"> <td class="danger"> <td class="danger"> <td class="danger"> <td class="danger"> <td class="danger"> <td class="danger"> <td class="danger">08/08/2016 04:37:42</td>
<td class="danger"> <td class="danger"> <td class="danger"> <td class="danger"> <td class="danger"> <td class="danger"> <td class="danger"> <td class="danger">The catalog was last synchronized successfully 1 or more days ago.</td>
</tr>
Ansgar Wiechers
  • 193,178
  • 25
  • 254
  • 328
Craig Smith
  • 37
  • 1
  • 7
  • 1
    `$_.'Error Type' -match 'Error' -or 'Critical'` should be `$_.'Error Type' -match 'Error|Critical'`, because `-match` expects a regexp. – wOxxOm Aug 08 '16 at 11:13
  • This still produces the same HTML output for me unfortunately. It seems to be generating additional tags, hence the table is skewed. – Craig Smith Aug 08 '16 at 11:54
  • It's not causing the problem you observed, but it's another issue in your code. More specifically, it's not the reason for getting multiple `` tags, but the reason why all of them have the class `danger`. – Ansgar Wiechers Aug 08 '16 at 12:37

1 Answers1

0
foreach { $_ -replace '<td>',$($applogs.class) }

The above statement will replace every <td> element with the class value of all records in $applogs, thus multiplying the <td> elements if $applogs contains more than one record.

At the point where you're trying to add formatting information you don't have the original objects anymore, so you need to parse the information to base your formatting on out of the HTML strings you've created. I already told you that when I answered your original question. The solution remains (basically) the same, too.

$re = '<tr><td>.*?</td><td>(Error|Warning)</td>'

... | ForEach-Object {
  if ($_ -match $re) {
    $type = $matches[1]
    $_ -replace '<td>', "<td class='$type'>"
  } else {
    $_
  }
} | ...

Also, as @wOxxOm mentioned in the comments to your question the logical operation $_.'Error Type' -match 'Error' -or 'Critical' doesn't work as you seem to expect. The expression is evaluated like this:

$_.'Error Type' -match 'Error' -or 'Critical'
($_.'Error Type' -match 'Error') -or ('Critical')
($_.'Error Type' -match 'Error') -or ($true)
$true

because non-empty strings evaluate to $true in PowerShell, and a boolean OR operation evaluates to $true if at least one operand is $true.

If you want to keep using the -match operator you can use an alternation in the regular expression:

$_.'Error Type' -match 'Error|Critical'

You can also do two separate -eq comparisons:

$_.'Error Type' -eq 'Error' -or $_.'Error Type' -eq 'Critical'

or check if the Error Type value is contained in an array of valid values:

'Error', 'Critical' -contains $_.'Error Type'
Community
  • 1
  • 1
Ansgar Wiechers
  • 193,178
  • 25
  • 254
  • 328