Still celebrating National IT Professionals Day with 3 months of free Premium Membership. Use Code ITDAY17

x
?
Solved

Creating Script to Search Text-based Log File for Strings then Output Timestamp for Each

Posted on 2016-08-08
2
Medium Priority
?
31 Views
Last Modified: 2016-09-21
Hey Guys -

I need some assistance creating a script, please, and really don't know where to begin...

What I'm Trying to Do
What I'd like to do is create a script which would prompt you for a hostname, then search a single log file for about 8 different unique strings which represent milestones of the process.  Once each string is found, it should save the timestamp from that line until all have been found.  Finally, it should output a list of the predefined milestones and place the captured timestamps next to each.  Below is an example of the type of output I'm looking for:

Script Output Example
System Hostname: Workstation1
System Build Started at 1:27pm
- UDI Starts:                    1:28pm
- UDI Completes:           1:50pm
- Downloading WIM:     2:41pm
- WIM Applied to Disk:  3:32pm
etc...

I've played around with a few things with PowerShell so far and can find certain strings and save them as variables, but can't figure out how to tell PS to save the timestamp into a variable yet. :(

Explanation
The vast majority of work I do is with SCCM in which almost everything has its own log file as the client on each workstation alone has 120+ log files.  For those not familiar with OSD, the primary log file it uses is named smsts.log which is easy to read when viewed in CMTrace, but when viewed with a text editor is formatted as shown below:

Snippet of smsts.log When Viewed in Text Editor
<![LOG[Disable: ]LOG]!><time="12:22:26.701+360" date="08-08-2016" component="TSManager" context="" type="1" thread="1528" file="tsxml.cpp:488">
<![LOG[Run in attribute: WinPEandFullOS]LOG]!><time="12:22:26.701+360" date="08-08-2016" component="TSManager" context="" type="1" thread="1528" file="tsxml.cpp:496">
<![LOG[Timeout: ]LOG]!><time="12:22:26.701+360" date="08-08-2016" component="TSManager" context="" type="1" thread="1528" file="tsxml.cpp:500">
<![LOG[DefaultVarlist found]LOG]!><time="12:22:26.701+360" date="08-08-2016" component="TSManager" context="" type="1" thread="1528" file="tsxml.cpp:583">
<![LOG[No variables found in default variable list]LOG]!><time="12:22:26.701+360" date="08-08-2016" component="TSManager" context="" type="1" thread="1528" file="tsxml.cpp:610">

Open in new window

As you can see, there is a format to each line which includes the type of message, timestamp, the message, etc.  This log file is located in the same place on all workstations at "C:\Windows\CCM\smsts.log"

I've gone through a few of these logs from different systems and located common log entries for each major milestone of the OSD process.  For example, searching the log for the line that includes "Installing software for PackageID='AHS000F3'" will only result in one line and would represent a certain application being installed.  So far, I've identified a unique string for each of the 8 milestones I have planned for.

Extra Credit
I don't know how much more difficult this would be, but if possible to take it one step further and instead of listing the timestamp, listing the 1st milestone as 0:00:00 and then each milestone thereafter displaying just the difference in time between it occurring and when it started (so you can see exactly how long it too to reach certain stages without math), that would be ideal.  If you have an idea for how to do that without too much additional scripting please fill me in.

Thanks Guys - I appreciate the help!
0
Comment
Question by:BzowK
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 2
2 Comments
 
LVL 85

Accepted Solution

by:
oBdA earned 2000 total points (awarded by participants)
ID: 41748605
This should do the trick.
If you pass it an array with patterns to match against the log content, it will only list the matching entries.
It writes custom objects with the properties ComputerName, Time, Offset, Component, File, Log to the pipeline, from where you can process them further.
Example (yes, it actually is 'exeuction'):
Get-Content .\clients.txt | .\Import-Smsts.ps1 -Verbose -FilterLog 'Installing software for PackageID*', 'The task exeuction engine successfully completed*' | Export-Csv C:\Temp\smsts.csv

Open in new window

[CmdletBinding()]
Param(
	[Parameter(ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True, Position=0)]
	[String[]]$ComputerName = @($ENV:ComputerName),
	[String]$Path = "${ENV:SystemRoot}\CCM\Logs\smsts.log",
	[String[]]$FilterLog = @()
)
Begin {
	$Write = $True
}
Process {
	ForEach ($Computer In $ComputerName) {
		"Processing $($Computer) ..." | Write-Verbose
		$OffsetStart = $Null
		$FullPath = If ($Computer -eq $ENV:ComputerName) {$Path} Else {"\\$($Computer)\$($Path.Replace(':', '$'))"}
		$Content = Get-Content -Path $FullPath -Raw -ErrorAction Stop
		[regex]::Matches($Content, '<!\[LOG(?<open>\[)(?<Log>.*?)(?<-open>])LOG]!>(?<open><)(?<Properties>.*?)(?<-open>>)', 'SingleLine') | ForEach-Object {
			$Groups = $_.Groups | Select-Object -ExpandProperty Value
			$Properties = [regex]::Matches($Groups[3], '\S+=".*?"') | Select-Object -ExpandProperty Value | ConvertFrom-StringData
			[void]($Properties.time.Trim('"') -match '(?<Time>.*\+|-)(?<Offset>.*)')
			$LocalTime = [DateTime]::ParseExact(($Properties.date.Trim('"') + ' ' + $Matches['Time'] + [string]([int]$Matches['Offset'] / 60)), 'MM-dd-yyyy HH:mm:ss.fffz', $Null)
			If ($FilterLog) {
				$Write = $False
				ForEach ($Pattern In $FilterLog) {
					If ($Groups[2] -like $Pattern) {
						$Write = $True
						Break
					}
				}
			}
			If ($Write) {
				If (-not $OffsetStart) {
					$OffsetStart = $LocalTime
				}
				New-Object -TypeName PSObject -Property ([ordered]@{
					'ComputerName' = $Computer
					'Time' = $LocalTime
					'Offset' = $LocalTime - $OffsetStart
					'Component' = $Properties.component.Trim('"')
					'File' = $Properties.file.Trim('"')
					'Log' = $Groups[2]
				})
			}
		}
	}
}
End {
}

Open in new window

0
 
LVL 85

Expert Comment

by:oBdA
ID: 41808361
Reasonably certain that the script provided parses the log files as required, including the time offset.
0

Featured Post

Looking for the Wi-Fi vendor that's right for you?

We know how difficult it can be to evaluate Wi-Fi vendors, so we created this helpful Wi-Fi Buyer's Guide to help you find the Wi-Fi vendor that's right for your business! Download the guide and get started on our checklist today!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

The Nano Server Image Builder helps you create a custom Nano Server image and bootable USB media with the aid of a graphical interface. Based on the inputs you provide, it generates images for deployment and creates reusable PowerShell scripts that …
Previously, on our Nano Server Deployment series, we've created a new nano server image and deployed it on a physical server in part 2. Now we will go through configuration.
The viewer will learn how to count occurrences of each item in an array.
In this seventh video of the Xpdf series, we discuss and demonstrate the PDFfonts utility, which lists all the fonts used in a PDF file. It does this via a command line interface, making it suitable for use in programs, scripts, batch files — any pl…

670 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question