Often when troubleshooting we come across log files that have tons of data. It can be hard to digest properly, so we look for tools that can break it down and provide some insight into what is happening. Last week, I shared a blog post on parsing System Center logs with PowerShell. If we use that function to convert the log lines into objects, we can now manipulate them and add calculated fields to provide even more data to assist in troubleshooting.
One of the important angles when troubleshooting is "How long did each task in the process take?". To answer this question, we need to take a look at each line in the logs and compare it to the following one. With PowerShell, the pipeline processes each object one at a time, so how do we accomplish this with code?
The method that I came up with was to include a stutter-step for the pipeline. Capture the first log line in a variable and hold onto it until the pipeline moves to the next entry. We can then calculate the difference in the date/time fields and add it as a new property. The second entry in the log then overwrites the first entry as the stored variable and we continue until the last entry in the log. As it is the last entry in the log, we have nothing to compare it to, so we simply set it as a dash.
As you can see above, the Add-Duration function reads in the output of Get-CMLog, selects the UTCTime property for comparison, and adds the results to a property called TotalMilliseconds.
Depending on the duration of your logs, you may want to use one of the other options for duration such as Days, Hours, Minutes, or Seconds. This is useful when troubleshooting long-running tasks such as OSD, or even when you just want to figure out what is taking a long time in the process so that you can track, trend, and reduce it.
And this function is not just for System Center logs. It should work on any time-based object as long as you specify the property containing a [DateTime] field that New-TimeSpan can parse.
The full code is available below and on Github.
Showing posts with label Logs. Show all posts
Showing posts with label Logs. Show all posts
SCCM Log Parser
Anyone who has ever worked with SCCM loves and adores CMTrace.exe for it's ability to parse the System Center logs. Countless headaches have been adverted in it's name. Still, it leaves quite a bit to be desired. To address some of the short-comings, Microsoft has released CMLogViewer.exe. This new tool supports quick filters, merging log files, and advanced filters. Even with the added features, it still lacks the flexibility that PowerShell can offer.
I started my adventure to do more by searching online for anything that met my needs:
After not finding anything suitable, I set about writing my own. Thankfully the fields in the log file are self-explanatory as key=value pairs and we can use basic regex to capture them.
To satisfy the local and UTC timestamps requirement, I had to add some additional regex capture groups that were then passed to the [DateTime]::ParseExact .Net method. I then added the fields into a [PSCustomObject]. The regex and object were then wrapped in a foreach loop that processes log lines read in via Get-Content. Since I also had a requirement of passing in multiple files and showing which line came from which log, I captured the current file name via Split-Path and also added that as a property to the object. And to support the ability to specify logs through the pipeline, a Process block was added and then a loop to read in each file.
Meeting these requirements allow us to do fun stuff like the following:
Show me the log entries in SMSTS.log
Get-CMLog smsts.log
Show me all log entries for CM logs and sort them by date
I started my adventure to do more by searching online for anything that met my needs:
- Read logs in the SCCM format
- Parse a single log or multiple logs (must provide filename as a property)
- Provide either local or UTC timestamps for global troubleshooting
- Accepts logs directly or via pipeline
After not finding anything suitable, I set about writing my own. Thankfully the fields in the log file are self-explanatory as key=value pairs and we can use basic regex to capture them.
- Message
- Time
- Date
- Component
- Context
- Type
- Thread
- File
To satisfy the local and UTC timestamps requirement, I had to add some additional regex capture groups that were then passed to the [DateTime]::ParseExact .Net method. I then added the fields into a [PSCustomObject]. The regex and object were then wrapped in a foreach loop that processes log lines read in via Get-Content. Since I also had a requirement of passing in multiple files and showing which line came from which log, I captured the current file name via Split-Path and also added that as a property to the object. And to support the ability to specify logs through the pipeline, a Process block was added and then a loop to read in each file.
Meeting these requirements allow us to do fun stuff like the following:
Show me the log entries in SMSTS.log
Get-CMLog smsts.log
Show me all log entries for CM logs and sort them by date
Get-ChildItem -Path C:\Windows\CCM\Logs | Get-CMLog | Sort-Object UTCTime
Get-CMLog -Path .\SMSTS.log | ?{$_.Message -match 'Error'}
Show me all log entries for logs that contain an error
Show me all log entries for logs that contain an error
Get-ChildItem -Path C:\Windows\CCM\Logs | Select-String -Pattern 'error' | Select -Unique Path | Get-CMLog
Subscribe to:
Posts (Atom)