Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
266 views
in Technique[技术] by (71.8m points)

powershell - PS XML Log Parsing

Good Day,

I got an XML file looking like this out of a software:

<BRLog>
    <Log ID="id" VER="2.0">
        <Time>1609821701</Time>
        <Task>Hyper-V</Task>
        <ResultCode>03</ResultCode>
    </Log>
    <Log ID="id" VER="2.0">
        <Time>1609821802</Time>
        <Task>Files Sync</Task>
        <ResultCode>0</ResultCode>
    </Log>
    <Log ID="id" VER="2.0">
        <Time>1609822009</Time>
        <Task>RDX</Task>
        <ResultCode>23</ResultCode>
    </Log>
    <Log ID="id" VER="2.0">
        <Time>1609822701</Time>
        <Task>Hyper-V</Task>
        <ResultCode>03</ResultCode>
    </Log>
    <Log ID="id" VER="2.0">
        <Time>1609822802</Time>
        <Task>Files Sync</Task>
        <ResultCode>303</ResultCode>
    </Log>
    <Log ID="id" VER="2.0">
        <Time>1609823009</Time>
        <Task>RDX</Task>
        <ResultCode>0</ResultCode>
    </Log>
</BRLog>

I can't change the format of the XML. The newest element is always added to the bottom of the XML file. I do not know the name of the task before. I do not know if a task runs multiple times a day, daily or once weekly, as this is the output of a XML log of a software that runs on multiple different clients and environments with different settings.

I need to check the result code for each task, and report if the last run task of each task type has been succesful (ResultCode 0) or not.

I'm loading the XML file and then reverse the whole thing to get the (now - 8 days) number of entries:

$Logs = @()
# time settings we look now back 8 days
$now = [datetime](Get-Date)
$then = [datetime](Get-Date).AddDays(-8) 

# get file
[xml]$xml = Get-Content -Path $Path2XML
if($xml.HasChildNodes) {
    # tranform to array to use reverse function to go through xml backwards which is my forward 
    $array = $xml.BRLog.Log
    [array]::Reverse($array)
    foreach($a in $array) {
        $logtime = Convert-FromUnixDate $a.Time
        if($logTime -ge $then) {
            # Log entry is in time frame
            $a | Add-Member ScriptProperty Date {Convert-FromUnixDate $this.Time }
            $Logs += $a
            } else {
            # we now reached first entry outside of time frame, skip further selection
                break
        }
    }
}

This gives me following output (content of $Logs):

Date                ID VER Time       Task       ResultCode
----                -- --- ----       ----       ----------
05.01.2021 06:03:29 id 2.0 1609823009 RDX        0
05.01.2021 06:00:02 id 2.0 1609822802 Files Sync 303
05.01.2021 05:58:21 id 2.0 1609822701 Hyper-V    03
05.01.2021 05:46:49 id 2.0 1609822009 RDX        23
05.01.2021 05:43:22 id 2.0 1609821802 Files Sync 0
05.01.2021 05:41:41 id 2.0 1609821701 Hyper-V    03

So now I can see that there are 3 different tasks:

Hyper-V - two entries, both failed - an error should be reported for this task
Files Sync - the newest one didn't work - an error should be reported for this task
RDX - the newest one worked - an info about succesful should run be reported

My biggest problem is now somehow splitting/sorting through the tasknames when I don't know how many tasks there are and what they are called, and attaching the log entries for each task. I had an idea about an object "taskname - list of entries" but can't seem to get it done. Any ideas?

I appreciate the help, thank you very much!


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Mathias R. Jessen gave me the hint I needed.

group-object has the magic to group by my task name, and this blog post by Kevin Marquette gave me the info on how to use it properly.

I ended up with the following bit of code, add it after my if($xml.HasChildNodes) loop.

    $Logs = $Logs | Group-Object -Property Task -AsHashTable
    foreach($key in $Logs.Keys) {
        foreach($log in $Logs[$key]) {
            if ($log.ResultCode -eq 0) {
                # 1. entry is no error, do not count
                break;
            }
            else {
                # 1. entry is error, so count
                $ErrorTexts += $log
                $ErrorCount++
                break;
            }
        }
    }

With the sample XML File I get following output for $ErrorTexts:

Date                ID VER Time       Task       ResultCode
----                -- --- ----       ----       ----------
05.01.2021 06:00:02 id 2.0 1609822802 Files Sync 303
05.01.2021 05:58:21 id 2.0 1609822701 Hyper-V    03 

and $Logs contains all logs grouped by task:

foreach($key in $logs.Keys) {Write-Output $Logs[$key] | format-table}

Date                ID VER Time       Task ResultCode
----                -- --- ----       ---- ----------
05.01.2021 06:03:29 id 2.0 1609823009 RDX  0
05.01.2021 05:46:49 id 2.0 1609822009 RDX  23



Date                ID VER Time       Task       ResultCode
----                -- --- ----       ----       ----------
05.01.2021 06:00:02 id 2.0 1609822802 Files Sync 303
05.01.2021 05:43:22 id 2.0 1609821802 Files Sync 0



Date                ID VER Time       Task    ResultCode
----                -- --- ----       ----    ----------
05.01.2021 05:58:21 id 2.0 1609822701 Hyper-V 03
05.01.2021 05:41:41 id 2.0 1609821701 Hyper-V 03

Thank you very much, happy coding!


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...