A few weeks ago I did an Update of the Veeam Backup & Replication environment in my Lab. After the update I wanted to make sure everything went fine and started analyzing all event logs and file logs during and after the update. I quickly realized that the Veeam log file analysis without a proper tool will take a while. Since my last public PowerShell project was already a few month ago (and I was looking for a use case for PowerShell Classes), I have started to create a Function for the Veeam log file analysis with PowerShell.
Veeam log file analysis
Per default all of the Veeam log files are located in ‘C:\ProgramData\Veeam\<Product>’. Most of the relevant Veeam Services create in their Sub-Folder a log file like these: ‘Svc.VeeamEndpointBackup.log’ (Veeam Agent for Microsoft Windows).
Most of the Veeam log files have the same schema for the main part:
[23.11.2016 23:07:16] <01> Info ------- Veeam Endpoint Job Started -------
The analysis with PowerShell
PowerShell is a great tool for quickly grep data from files with a given schema:
$Content = Get-Content -Path "C:\ProgramData\Veeam\Endpoint\Svc.VeeamEndpointBackup.log" $Content | Select-String -Pattern "\[\d+.\d+.\d+\s\d+\:\d+:\d+]\s\<\d+\>\sError"
These two lines return all Error messages in the Veeam Agent for Microsoft Windows log file.
Executing these commands for all the relevant log files is not really comfortable, so I have decided to create a PowerShell module with a function for the Veeam log file analysis. The function should have inputs for the log that should be analyzed and some optional parameters to modify the output of the messages.
This Example shows the output of all known (by the function) log files limited to the last Error or Warning message per file:
If the output parameter -Context 2 is used all message are shown in context. The Warning or Error messages are highlighted by a >:
Main function of the PowerShell module
function Invoke-VeeamLogParser { <# .DESCRIPTION The Veeam Log Parser Function extracts Error and Warning Messages from the Veeam File Logs of various products and services. .NOTES File Name : Invoke-VeeamLogParser.psm1 Author : Markus Kraus Version : 1.0 State : Ready .LINK https://mycloudrevolution.com/ .EXAMPLE Invoke-VeeamLogParser -LogType Endpoint -Limit 2 .EXAMPLE Invoke-VeeamLogParser -LogType Endpoint -Limit 2 -Context 2 .PARAMETER VeeamBasePath The Base Path of the Veeam Log Files Default: "C:\ProgramData\Veeam\" .PARAMETER Context Show messages in Context .PARAMETER Limit Show limited number of messages .PARAMETER LogType The products or services Log you want to show Valid Pattern: "All","Endpoint","Mount","Backup","EnterpriseServer","Broker","Catalog","RestAPI","BackupManager", "CatalogReplication","DatabaseMaintenance","WebApp","PowerShell" #> [CmdletBinding()] param( [Parameter(Mandatory=$False, ValueFromPipeline=$False, HelpMessage="The Base Path of the Veeam Log Files")] [ValidateNotNullorEmpty()] [String] $VeeamBasePath = "C:\ProgramData\Veeam\", [Parameter(Mandatory=$False, ValueFromPipeline=$False, HelpMessage="Show messages in Context")] [ValidateNotNullorEmpty()] [int]$Context, [Parameter(Mandatory=$False, ValueFromPipeline=$False, HelpMessage="Show limited number of messages")] [ValidateNotNullorEmpty()] [Int]$Limit, [Parameter(Mandatory=$True, ValueFromPipeline=$False, HelpMessage="The products or services Log you want to show")] [ValidateNotNullorEmpty()] [ValidateSet("All","Endpoint","Mount","Backup","EnterpriseServer","Broker","Catalog","RestAPI","BackupManager", "CatalogReplication","DatabaseMaintenance","WebApp","PowerShell")] [String]$LogType ) Begin { class LogParser { #Properties [String]$Name [String]$BasePath [String]$Folder [String]$File #Static Static [String] $WarningPattern = "\[\d+.\d+.\d+\s\d+\:\d+:\d+]\s\<\d+\>\sWarning" Static [String] $ErrorPattern = "\[\d+.\d+.\d+\s\d+\:\d+:\d+]\s\<\d+\>\sError" #Constructor LogParser ([String] $Name, [String]$BasePath, [String]$Folder, [String]$File) { $this.Name = $Name $this.BasePath = $BasePath $this.Folder = $Folder $this.File = $File } #Method [Bool]checkFolder() { return Test-Path $($this.BasePath + $this.Folder) } #Method [Bool]checkFile() { return Test-Path $($this.BasePath + $this.Folder + "\" + $this.File) } #Method [Array]getContent() { if ($this.checkFolder()) { if ($this.checkFile()) { return Get-Content $($this.BasePath + $this.Folder + "\" + $this.File) } else { return Write-Warning "File not found" } } else { return Write-Warning "Folder not found" } } #Method [Array]getErrors() { $Content = $this.getContent() return $Content | Select-String -Pattern $([LogParser]::ErrorPattern) -AllMatches } #Method [Array]getErrors([int]$ContentB, [int]$ContentA) { $Content = $this.getContent() return $Content | Select-String -Pattern $([LogParser]::ErrorPattern) -AllMatches -Context $ContentB, $ContentA } #Method [Array]getWarnings() { $Content = $this.getContent() return $Content | Select-String -Pattern $([LogParser]::WarningPattern) -AllMatches } #Method [Array]getWarnings([int]$ContentB, [int]$ContentA) { $Content = $this.getContent() return $Content | Select-String -Pattern $([LogParser]::WarningPattern) -AllMatches -Context $ContentB, $ContentA } #Method [Array]getErrorsAndWarnings() { $Content = $this.getContent() return $Content | Select-String -Pattern $([LogParser]::ErrorPattern), $([LogParser]::WarningPattern) -AllMatches } #Method [Array]getErrorsAndWarnings([int]$ContentB, [int]$ContentA) { $Content = $this.getContent() return $Content | Select-String -Pattern $([LogParser]::ErrorPattern), $([LogParser]::WarningPattern) -AllMatches -Context $ContentB, $ContentA } } function Invoke-Output ($item) { if ($Context) { $Select = $item.getErrorsAndWarnings($Context, $Context) } else { $Select = $item.getErrorsAndWarnings() } if ($Limit) { $Select | Select-Object -Last $Limit } else { $Select } } $LogTypes = @() $LogTypes += [LogParser]::new("Endpoint", $VeeamBasePath, "Endpoint", "Svc.VeeamEndpointBackup.log") $LogTypes += [LogParser]::new("Mount", $VeeamBasePath, "Backup", "Svc.VeeamMount.log") $LogTypes += [LogParser]::new("Backup", $VeeamBasePath, "Backup", "Svc.VeeamBackup.log") $LogTypes += [LogParser]::new("EnterpriseServer", $VeeamBasePath, "Backup", "Svc.VeeamBES.log") $LogTypes += [LogParser]::new("Broker", $VeeamBasePath, "Backup", "Svc.VeeamBroker.log") $LogTypes += [LogParser]::new("Catalog", $VeeamBasePath, "Backup", "Svc.VeeamCatalog.log") $LogTypes += [LogParser]::new("RestAPI", $VeeamBasePath, "Backup", "Svc.VeeamRestAPI.log") $LogTypes += [LogParser]::new("BackupManager", $VeeamBasePath, "Backup", "VeeamBackupManager.log") $LogTypes += [LogParser]::new("CatalogReplication", $VeeamBasePath, "Backup", "CatalogReplicationJob.log") $LogTypes += [LogParser]::new("DatabaseMaintenance", $VeeamBasePath, "Backup", "Job.DatabaseMaintenance.log") $LogTypes += [LogParser]::new("WebApp", $VeeamBasePath, "Backup", "Veeam.WebApp.log") $LogTypes += [LogParser]::new("PowerShell", $VeeamBasePath, "Backup", "VeeamPowerShell.log") } Process { if ($LogType -eq "All") { foreach ($item in $LogTypes) { Write-Host "`nProcessing '$($item.File)' in '$($item.BasePath + $item.Folder + "\")'" -ForegroundColor Gray Invoke-Output $item } } else { $item = $LogTypes | Where-Object {$_.Name -eq $LogType } if ($item) { Write-Host "`nProcessing '$($item.File)' in '$($item.BasePath + $item.Folder + "\")'" -ForegroundColor Gray Invoke-Output $item } else { Throw "Internal Error: LogType Missmatch" } } } }
Contribute to the PowerShell Module
In case you hit any bug or have any enhancement request feel free to open an issue or submit a pull request.
Get the PowerShell Module
The PowerShell Module is available on GitHub and in the PowerShell Gallery:
VeeamLogParser – GitHub VeeamLogParser – PS Gallery