Veeam log file analysis with PowerShell

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 with PowerShell - Invoke-VeeamLogParser -LogType 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:

Veeam log file analysis with PowerShell - Invoke-VeeamLogParser -LogType All

If the output parameter -Context 2  is used all message are shown in context. The Warning or Error messages are highlighted by a >:

Veeam log file analysis with PowerShell - Invoke-VeeamLogParser -LogType PowerShell

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

 

 

Leave a Reply