【问题标题】:Parsing XML-Like log file解析类似 XML 的日志文件
【发布时间】:2014-01-10 23:31:03
【问题描述】:

我有一个记录如下事件的日志文件。我想将每个事件转换为 PSCustomobject。它有点像 XML,但将 xml 转换为文件的 Get-Content 会给我一个错误:

无法将值“System.Object[]”转换为类型“System.Xml.XmlDocument”。错误:“此文档已经有一个 'DocumentElement' 节点。”

<event date='Jan 06 01:46:16' severity='4' hostName='ABC' source='CSMFAgentPolicyManager' module='smfagent.dll' process='AeXNSAgent.exe' pid='1580' thread='1940' tickCount='306700046' >
  <![CDATA[Setting wakeup time to 3600000 ms (Invalid DateTime) for policy: DefaultWakeup]]>
</event>

这是我到目前为止的一段代码

   <#
.EXAMPLE    
source    : MaintenanceWindowMgr
process   : AeXNSAgent.exe
thread    : 8500
hostName  : ABC
severity  : 4
tickCount : 717008140
date      : Jan 10 19:45:00
module    : PatchMgmtAgents.dll
pid       : 11984
CData     : isAbidingByMaintenanceWindows() - yes
#>
$logpath = Join-Path $env:ProgramData 'Symantec\Symantec Agent\logs\Agent.log'
$hash=[ordered]@{};
$log = get-content $logpath | % {

    ## handle Event start
    ## sample: <event date='Jan 10 18:45:00' severity='4' hostName='ABC' source='MaintenanceWindowMgr' module='PatchMgmtAgents.dll' process='AeXNSAgent.exe' pid='11984' thread='8500' tickCount='713408140' >
    if ($_ -match '^<event') {

        if ($hash) {                
            ## Convert the hastable to PSCustomObject before clearing it
            New-Object PSObject -Property $hash
            $hash.Clear()
        }

        $line = $_ -replace '<event ' -replace ' >' -split "'\s" -replace "'"               
        $line | % { 

            $name,$value=$_ -split '='                
            $hash.$name=$value
        }        
    }

    ## handle CData
    ## Sample: <![CDATA[Schedule Software Update Application Task ({A1939DC8-DA4A-4E46-9629-0500C2383ECA}) triggered at 2014-01-10 18:50:00 -5:00]]>
    if ($_ -match '<!') {
        $hash.'CData' = ($_ -replace '<!\[CDATA\[' -replace '\]\]>$').ToString().Trim()
    }
}
  $log 

不幸的是,该对象不是我想要的形式。

$log|gm


   TypeName: System.Management.Automation.PSCustomObject

Name        MemberType Definition                    
----        ---------- ----------                    
Equals      Method     bool Equals(System.Object obj)
GetHashCode Method     int GetHashCode()             
GetType     Method     type GetType()                
ToString    Method     string ToString()   

当我尝试从输出中收集所有对象时,我丢失了在将哈希转换为 PSCustomObject 时生成的 NoteProperties

   TypeName: System.Management.Automation.PSCustomObject

Name        MemberType   Definition                                                                                                                                     
----        ----------   ----------                                                                                                                                     
Equals      Method       bool Equals(System.Object obj)                                                                                                                 
GetHashCode Method       int GetHashCode()                                                                                                                              
GetType     Method       type GetType()                                                                                                                                 
ToString    Method       string ToString()                                                                                                                              
Equals      Method       bool Equals(System.Object obj)                                                                                                                 
GetHashCode Method       int GetHashCode()                                                                                                                              
GetType     Method       type GetType()                                                                                                                                 
ToString    Method       string ToString()                                                                                                                              
CData       NoteProperty System.String CData=isAbidingByMaintenanceWindows() - yes                                                                                      
date        NoteProperty System.String date=Jan 10 18:45:00                                                                                                             
hostName    NoteProperty System.String hostName=ABC                                                                                                             
module      NoteProperty System.String module=PatchMgmtAgents.dll                                                                                                       
pid         NoteProperty System.String pid=11984                                                                                                                        
process     NoteProperty System.String process=AeXNSAgent.exe                                                                                                           
severity    NoteProperty System.String severity=4                                                                                                                       
source      NoteProperty System.String source=MaintenanceWindowMgr                                                                                                      
thread      NoteProperty System.String thread=8500                                                                                                                      
tickCount   NoteProperty System.String tickCount=713408140 

我在这里缺少什么?

【问题讨论】:

  • 您希望输出对象是什么样的?
  • 我想创建一个 PSCustomObject,它将所有这些 name=value 对作为 property=values。
  • 我猜问题是 HashTable 是无序的,因此我创建的对象初始对象很好,但它们没有排序,因此不能用单个 PSObject 数组表示,我需要对它们进行排序首先
  • 你运行的是什么版本的 Powershell?
  • 然后可以使用 $hash=[ordered]@{};

标签: xml powershell


【解决方案1】:

XML 文件必须有一个根(或documentElement)节点。由于您的日志文件似乎包含多个没有共同根元素的 &lt;event&gt; 标签,您可以像这样添加缺少的 documentElement

$logpath  = Join-Path $env:ProgramData 'Symantec\Symantec Agent\logs\Agent.log'
[xml]$log = "<logroot>$(Get-Content $logpath)</logroot>"

之后,您可以使用通常的方法处理您的日志,例如:

$fmt = 'MMM dd HH:mm:ss'

$log.SelectNodes('//event') |
  select @{n='date';e={[DateTime]::ParseExact($_.date, $fmt, $null)}},
         severity, hostname, @{n='message';e={$_.'#cdata-section'}}

如果您更喜欢自定义对象,您可以像这样轻松创建它们:

$fmt = 'MMM dd HH:mm:ss'

$log.SelectNodes('//event') | % {
  New-Object -Type PSObject -Property @{
    'Date'     = [DateTime]::ParseExact($_.date, $fmt, $null)
    'Severity' = $_.severity
    'Hostname' = $_.hostname
    'Message'  = $_.'#cdata-section'
  }
}

【讨论】:

  • 我更喜欢 PSCustomObject,因为它以后更容易玩,但这是一个非常酷的技巧,谢谢!而且我刚刚意识到我可以轻松地将 $log.logroot.event 转换为 PSCustomObject 而无需处理所有正则表达式。喜欢这个!
【解决方案2】:

使用拆分方法:

$hash = [ordered]@{}
$regex = '^<event (.+) >$'
$lines = (gc $file) -match $regex -replace $regex,'$1'
foreach ($line in $lines)
 {
         $hash.Clear() 
         $line -split "'\s" -replace "'" |
         foreach {
                   $name,$value=$_ -split '='                
                   $hash.$name=$value
                 }

        [PSCustomObject]$hash 
} 

【讨论】:

  • 谢谢,我知道语法了。我最初认为 [ordered] 不起作用,但这是一个红鲱鱼。因为每个事件都包含 3 行(开始事件 + 元数据、CData、关闭事件),所以我在那里有捕获 CData 的逻辑,并且第一个哈希对象存在问题,导致创建了一个空对象,并抛出了结果。
  • 如果目标只是为每个开始事件 + 元数据行吐出一个自定义对象,则可以忽略 CData 和 Close 事件行。
【解决方案3】:

我最初认为我的问题是原始哈希未排序,但后来发现实际问题出在哪里。下面的代码导致一个初始 PSCustomObject 没有创建任何 NoteProperty:

  if ($hash) { .... }

即使是刚刚初始化的哈希也满足如下所示:

PS H:\> $myhash=[ordered]@{}
PS H:\> if ($myhash) {"yay"}
yay

所以要修复它,我只是更改了检查

# CData is the last record, if hash has it, it's ready to convert to PSCustomObject
if ($hash.CData) { ... }  

这是更新后的代码:

   $hash=[ordered]@{}        
    $logpath = Join-Path $env:ProgramData 'Symantec\Symantec Agent\logs\Agent.log'       
    Get-Content $logpath | % {

        ## handle Event start            
        if ($_ -match '^<event') {       
            # CData is the last record, if hash has it, it's ready to convert to PSCustomObject
            if ($hash.CData) {                        
                ## Convert the hastable to PSCustomObject before clearing it
                [PSCustomObject]$hash                
                $hash.Clear()
            }

            ## sample: <event date='Jan 10 18:45:00' severity='4' hostName='ABC' source='MaintenanceWindowMgr' module='PatchMgmtAgents.dll' process='AeXNSAgent.exe' pid='11984' thread='8500' tickCount='713408140' >
            $line = $_ -replace '<event ' -replace ' >' -split "'\s" -replace "'"               
            $line | % { 
                        $name,$value=$_ -split '='                               
                            $hash.$name=$value                        
            }        
        }

        ## handle CData
        ## Sample: <![CDATA[Schedule Software Update Application Task ({A1939DC8-DA4A-4E46-9629-0500C2383ECA}) triggered at 2014-01-10 18:50:00 -5:00]]>
        if ($_ -match '<!') {
            $hash.'CData' = ($_ -replace '<!\[CDATA\[' -replace '\]\]>$').ToString().Trim()
        }
    }  

感谢 @mjolinor 提供帮助的 cmets!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-04-20
    • 2016-01-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多