Import-CMTLogFile function: Imports SMS/SCCM CMT-formatted log files into a PSObject


#region Function Import-CMTLogFile
Function Import-CMTLogFile {
	<#
	.SYNOPSIS
	    Imports SMS/SCCM CMT-formatted log files into a PSObject
	.DESCRIPTION
	    Parses log files that conforms to CMTrace32 structure.
		Returns a PScustomObject with the following properties:
			LogPath
			Size
			Entries
				Line
				Date
			Informations
			Warnings
			Errors
	.PARAMETER LogFile
	    Full path to the CMT-formatted logfile.
		Supports WildCard path, too!
	.EXAMPLE
		Import-CMTLogFile -LogFilePath "$SCCMLogFolder\InventoryAgent.log"
	.EXAMPLE
		$InventoryLogObj = Import-CMTLogFile -LogFilePath "$SCCMLogFolder\InventoryAgent.log"
		ForEach( $Line in $($InventoryLogObj.Entries) ) {
			If ( $Line.Message -match "Inventory: Opening store for action" ) {	
				Write-log "Found Opening store[$($Line.Message)]"					
			} ElseIf ( ($Line.Message -match "Inventory: Successfully sent report.") -and $ActionName ) {	
				Write-log "Found Inv Success [$($Line.Date)]"
			} ElseIf ( $Line.Message -match "End of message processing" ) {	
				write-log "Found End ************************ [$($Line.Date)]"
			}
		}
	.NOTES
	Author: Denis St-Pierre (Ottawa, Canada)
	-NOT tested on Windows 10.		
	-Based on https://gallery.technet.microsoft.com/scriptcenter/Parse-Log-af34186e
	-Added handling BackAss American Date format (otherwise it only works for the first 12 days each month!)
	-Added merging multi-line CMT entries into ONE line instead of blowing up
	-Added Error handling
	-Tested on CM2007
	#>
	[CmdletBinding()]
	Param (
	    [parameter(Mandatory=$true)]
		[ValidateNotNullorEmpty()]
		[String]$LogFilePath
	)
	begin { 
        ## Get the name of this function and write header
        [string]${CmdletName} = $PSCmdlet.MyInvocation.MyCommand.Name
        Write-FunctionHeaderOrFooter -CmdletName ${CmdletName} -CmdletBoundParameters $PSBoundParameters -Header
	}
	
	process {
		Try {
			#NOTE: If Path Validation is done in Params, Entire Function errors out. So we do it here
			If (-not ( $(Try {Test-Path -LiteralPath $LogFilePath -PathType 'Leaf' } Catch { $false }) ) ) {
				Write-Log "Unable to find [$LogFilePath]: $($_.Exception.Message)" -Source ${CmdletName} -Severity 3
				Return			
			}
			
			$LogFileObj = Get-ChildItem $LogFilePath -Force
		    $LogFilePathObject = New-Object psobject
		    Add-Member -InputObject $LogFilePathObject -MemberType NoteProperty -Name LogPath -Value $LogFileObj.FullName | Out-Null
		    Add-Member -InputObject $LogFilePathObject -MemberType NoteProperty -Name Size -Value $($LogFileObj.Length) | Out-Null
		    
		    [Int32]$ERR		= 0
		    [Int32]$WARN	= 0
		    [Int32]$INFO	= 0

		    [Array]$LogEntries = @()
		    [Array]$LogContent = Get-Content -Path $LogFilePath
		    Foreach($LogLine in $LogContent) {
				#merge multi-line entries into ONE line
				If ($LogLine -notmatch ']LOG]!><time="') {
					$LogLinePart = $LogLinePart+$LogLine
					Continue #Skip to grab next chuck $LogLine
				} elseif ( ( $LogLinePart ) -and ($LogLine -match ']LOG]!><time="') ) {
					#Oh, we have the line with the Time stamp. combine and let it goes through
					$LogLinePart = $LogLinePart+$LogLine
					$LogLine = $LogLinePart
					Remove-Variable LogLinePart -ErrorAction SilentlyContinue
				}
				
		        $LineObj = New-Object psobject
		        Add-Member -InputObject $LineObj -MemberType NoteProperty -Name Message -Value $($($LogLine.Replace("<![LOG[","")) -split ']LOG]!>')[0] | Out-Null
		        #Add-Member -InputObject $LineObj -MemberType NoteProperty -Name Time -Value $($($LogLine -split 'time="')[-1] -split '"')[0] | Out-Null
		        #Add-Member -InputObject $LineObj -MemberType NoteProperty -Name Date -Value $(Get-Date $($($LogLine -split 'date="')[-1] -split '"')[0]) | Out-Null
				#Make it handle BackAss American Date format (otherwise it only works for the first 12 days each month!)
				[String]$DTString = $($($($($LogLine -split 'date="')[-1] -split '"'))[0] + '.' + $($($($LogLine -split 'time="')[-1] -split '"')[0].Split('.'))[0])
				Add-Member -InputObject $LineObj -MemberType NoteProperty -Name Date	-Value $([datetime]::ParseExact( $DTString,"MM-dd-yyyy.HH:mm:ss",$null)) | Out-Null
		        Add-Member -InputObject $LineObj -MemberType NoteProperty -Name Component -Value $($($LogLine -split 'component="')[-1] -split '"')[0] | Out-Null
		        Add-Member -InputObject $LineObj -MemberType NoteProperty -Name Context	-Value $($($LogLine -split 'context="')[-1] -split '"')[0] | Out-Null
		        Add-Member -InputObject $LineObj -MemberType NoteProperty -Name Thread	-Value ([int]$($($LogLine -split 'thread="')[-1] -split '"')[0]) | Out-Null
		        Add-Member -InputObject $LineObj -MemberType NoteProperty -Name Source	-Value $($($LogLine -split 'file="')[-1] -split '"')[0] | Out-Null
		        Add-Member -InputObject $LineObj -MemberType NoteProperty -Name Servity -Value ([int]$($($LogLine -split 'type="')[-1] -split '"')[0]) | Out-Null

		        Switch($LineObj.Servity) {
		            1{$INFO++}
		            2{$WARN++}
		            3{$ERR++}
		        }

		        $LogEntries += $LineObj
		        Remove-Variable LineObj -Force -ErrorAction SilentlyContinue | Out-Null
		    }
		    Add-Member -InputObject $LogFilePathObject -MemberType NoteProperty -Name Entries -Value $LogEntries | Out-Null
		    Add-Member -InputObject $LogFilePathObject -MemberType NoteProperty -Name Informations -Value $INFO | Out-Null
		    Add-Member -InputObject $LogFilePathObject -MemberType NoteProperty -Name Warnings -Value $WARN| Out-Null
		    Add-Member -InputObject $LogFilePathObject -MemberType NoteProperty -Name Errors -Value $ERR | Out-Null
		
			$LogFilePathObject	#Returns PS object
		} Catch {
			Write-Log "Unable to parse line [$LogLine] of [$LogFilePath]: $($_.Exception.Message)" -Source ${CmdletName} -Severity 3
			Return
		}
	}
	end {
		Write-FunctionHeaderOrFooter -CmdletName ${CmdletName} -Footer
	}
}
#endregion Function Import-CMTLogFile