Someone came into IRC last week asking for help converting a rather large vbscript into PowerShell, and got me interested in turning Robocopy logs into Windows Events…
The original VBScript is about 68 lines of code, and writes one event per log file. We duplicated it’s functionality with the following 11 lines of code:
[string]$Log = Join-Path $LogPath $LogName
if(Select-String -Path $Log -pattern "0x0000") {
$logError = "$Log.ERROR.$(get-date -format 'yyyy-MM-dd-hhmmss')"
write-eventlog Application -Source Robocopy -EventId 12 -EntryType Error -Message "Robocopy Job Failed -please check log file $LogError"
move-item $log $logError
} else {
# If you do not want to send Success Events to the Application Log, comment out the following line
write-eventlog Application -Source Robocopy -EventId 1 -EntryType Information -Message "Robocopy Job Failed -please check log file $LogError"
move-item $Log "$Log.ARCHIVE.$(Get-Date -f 'yyyy-MM-dd-hhmmss')"
}
## Remove archive logs older than $archiveDate
Get-ChildItem "$Log.ARCHIVE.*" | Where { $_.CreationTime -lt (Get-Date).AddDays(-$archiveDays) } | Remove-Item
Actually, even that first attempt extended the functionality a little, because we potentially make multiple archive copies which we only delete once they pass the archive date.
New-EventLog Application Robocopy
There are two catches. First, you need PowerShell 2.0. Second, before you can run that script the first time, you have to create the “Robocopy” event source for the machine by running this command in an elevated PowerShell console (that is, you have to run it “as Administrator”):
Of course, being a good geek, I couldn’t leave well enough alone, so we changed the script so that it would log each unique error to the event log (including the line following the error, which has more details), so that there’s no need to go “check the log file” on the machine, since you can retrieve the event logs remotely. The finished script looks like this:
## BEFORE you use this the FIRST time (only once per machine)
## you must run the following command elevated (as Administrator):
## New-EventLog Application Robocopy
Param( [string]$LogPath = "C:\Logs\",
[string]$LogName = "Robocopy-log-file.log",
[int]$ArchiveDays = 30
)
[string]$Log = Join-Path $LogPath $LogName
$Archive = "ARCHIVE"
foreach($errorEvent in Select-String -Path $Log -Pattern 'ERROR .*0x0000.*$' -context 0,1 | sort {$_.matches[0].value} -Unique )
{
$Archive = "ERROR"
write-eventlog Application -Source Robocopy -EventId 12 -EntryType Error -Message $($errorEvent.Line + "`n" + $errorEvent.Context.PostContext)
}
switch($Archive) {
"ERROR" { ## Archive the log file as an ERROR (we never delete these automatically)
move $Log "$Log.ERROR.$(get-date -format 'yyyy-MM-dd-hhmmss')"
}
"ARCHIVE" {
write-eventlog Application -Source Robocopy -EventId 1 -EntryType Information -Message "Robocopy successful"
## Archive the log file
move-item $Log "$Log.ARCHIVE.$(Get-Date -f 'yyyy-MM-dd-hhmmss')" -Force
}
}
## Remove archive logs older than $archiveDate
$archiveDate = (Get-Date).AddDays(-$archiveDays)
Get-ChildItem "$Log.ARCHIVE.*" | Where { $_.CreationTime -lt $archiveDate } | Remove-Item
Notice that we cleaned up the parameters a little bit, and put some defaults in, but we still haven’t written “help” ... that’s partly because I still am not sure that’s the best option for logging Another way would be to just write one log event, but with details about the errors like:
## BEFORE you use this the FIRST time (only once per machine)
## you must run the following command elevated (as Administrator):
## New-EventLog Application Robocopy
Param( [string]$LogPath = "C:\Logs\",
[string]$LogName = "Robocopy-log-file.log",
[int]$ArchiveDays = 30
)
[string]$Log = Join-Path $LogPath $LogName
[string]$LogError = "$Log.ERROR.$(get-date -format 'yyyy-MM-dd-hhmmss')"
[string]$LogArchive = "$Log.ARCHIVE.$(get-date -format 'yyyy-MM-dd-hhmmss')"
$Errors = Select-String -Path $Log -Pattern 'ERROR .*0x0000.*$' -context 0,1 |
Group-Object { $_.Context.PostContext } |
Format-Table Count, Name -HideTableHeaders -AutoSize | Out-String
if($Errors) {
write-eventlog Application -Source Robocopy -EventId 12 -EntryType Error -Message "$errors`n`nPlease check: $LogError"
move $Log $LogError
} else {
write-eventlog Application -Source Robocopy -EventId 1 -EntryType Information -Message "Robocopy successful. Log archived: $LogArchive"
move-item $Log $LogArchive
}
## Remove archive logs older than $archiveDate
$archiveDate = (Get-Date).AddDays(-$archiveDays)
Get-ChildItem "$Log.ARCHIVE.*" | Where { $_.CreationTime -lt $archiveDate } | Remove-Item
Absolutely incredible work
[...] Logging Robocopy errors to the Event Log using PowerShell (Joel Bennett) [...]