Custom Function for Better Logging in PowerShell Scripts

Logs In Powershell

Logging in programming is a very basic skill which give a developer an edge in different environments and helping user debug the program without using debug utilities. In PowerShell, there are some of the functions available which can be utilized and modified in order to get a better logging functionality in PowerShell. The basic functions are as follow:

Write-Error
Write-Warning
Write-Information
Write-Debug
Write-Verbose

These functions can be directly used in the PowerShell code but that is not very useful in cases when you want to change the level of logging with minimal changes to the code. Moreover, when you want to show the logged data on console or write to a file the logging mechanism should handle that functionality.

While starting to work with PowerShell I didn’t find any already available function which can provide such an ease to developer. So, I have developed my own logging function and made is in such a way that any of the PowerShell script can use it without much trouble. The code looks like below:

function log{
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [String]$LogText,

        [Parameter(Mandatory=$false)]
        [ValidateSet("Critical","Fatal","Error","Warning","Info","Debug","Verbose")]
        [String]$Type = "Info",

        [Parameter(Mandatory=$true)]
        [String]$File
    )

    $CRITICAL=1
    $ERROR=2
    $WARNING=3
    $INFORMATION=4
    $DEBUG=5
    $VERBOSE=6

    $ConsoleLogLevel = $CRITICAL
    $FileLogLevel = $INFORMATION
    $WriteToConsole = $true
    $WriteToFile = $true

    if ( $ConsoleLogLevel -eq $CRITICAL) { $ErrorActionPreference = 'Stop' }
    if ( $ConsoleLogLevel -eq $ERROR ) { $ErrorActionPreference = 'Continue' } else { $ErrorActionPreference = 'SilentlyContinue' }
    if ( $ConsoleLogLevel -eq $WARNING ) { $WarningPreference = 'Continue' } else { $WarningPreference = 'SilentlyContinue' }
    if ( $ConsoleLogLevel -eq $INFORMATION ) { $InformationPreference = 'Continue' } else { $InformationPreference = 'SilentlyContinue' }
    if ( $ConsoleLogLevel -eq $DEBUG ) { $DebugPreference = 'Continue' } else { $DebugPreference = 'SilentlyContinue' }
    if ( $ConsoleLogLevel -eq $VERBOSE ) { $VerbosePreference = 'Continue' } else { $VerbosePreference = 'SilentlyContinue' }

    if ( $Type )
    {
        $TimeStamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
        $msg = "$TimeStamp - $Type : $LogText"
        switch ($Type)
        {
            #Something seriously wrong we must stop the execution
            'Critical'{
                if ($WriteToFile -eq $true) { Write-Output $msg | Out-File -FilePath $File -Append }
                if ($WriteToConsole -eq $true) { Write-Output $msg | Write-Error -ErrorAction Stop }
            }

            #Write error and continue if log level is error or higher
            'Error'{
                if ($FileLogLevel -ge $ERROR) {
                    if ($WriteToFile -eq $true) {Write-Output $msg | Out-File -FilePath $File -Append }
                }
                if ($WriteToConsole -eq $true) { Write-Output $msg | Write-Error }
            }

            # Write warning and continue if log level is warning or higher
            'Warning'{
                if($FileLogLevel -ge $WARNING) {
                    if ($WriteToFile -eq $true) {Write-Output $msg | Out-File -FilePath $File -Append }
                }
                if ($WriteToConsole  -eq $true) {Write-Output $msg | Write-Warning }
            }
          
            # Write Information and Continue if log level is info or higher
            'Info'{
                if($FileLogLevel -ge $INFORMATION){
                    if($WriteToFile -eq $true) { Write-Output $msg | Out-File -FilePath $File -Append }
                }
                if ($WriteToConsole -eq $true} { Write-Output $msg | Write-info }
            }

            # Write Debug information in logs and logfile if log level is Debug or Higher
            'Debug'{
                if($FileLogLevel -ge $DEBUG){
                    if($WriteToFile -eq $true) { Write-Output $msg | Out-File -FilePath $File -Append }
                }
                if($WriteToConsole -eq $true) {Write-Output $msg | Write-Debug }
            }

            # Write trace/ verbose info if log level is verbose 
            'Verbose'{
                if($FileLogLevel -ge $VERBOSE){
                     if($WriteToFile -eq $true) {Write-Output $msg | Out-File -FilePath $File -Append }
                }
                if($WriteToConsole -eq $true) {Write-Output $msg | Write-Verbose }
             }

             #Default level is information
             Default{
                if($FileLogLevel -ge $INFORMATION)
                    if($WriteToFile -eq $true) {Write-Output $msg | Out-File -FilePath $File -Append }
                }
                if($WriteToConsole -eq $true) {Write-Output $msg | Write-Info }
            }
        }
    }
}

        

If you have little idea about PowerShell you will come to know that “Write-Info” is not function. It is “Write-Information”. I have to create a separate function by wrapping “Write-Information” into a new function which can take string as input stream (as a pipeline variable). Not allowing the string input as a pipeline variable to “Write-Information” is a bug in PowerShell.

function Write-Info{
    [CmdletBinding()]
    Param{
        [Parameter(Mandatory=$true,valueFromPipeline=$true)]
        [String]$msg
    }
    Write-Information -MessageData $msg
}

I have also provided a function which can be used to set the file name in the folder for logging purpose:

function Set-LogInfo{
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory=$true)]
        [String]$Log_name,

        [Parameter(Mandatory=$true)]
        [String]$Log_path
    )

    $date = Get-Date -Format 'yyyyMMdd'
    $LogFile = -join($Log_path,"\",$Log_name,$date,".log")

    return $LogFile
}

Following is the example of how to use the above functions:

$logfile = Set-LogInfo -Log_name "Script_name" -LogPath "Script_path"

# Default logging level is information
log "Script started." -File "$logfile"   
log -Type Debug "variable a = $a" -File "$logfile"

As we see we have to set the log file path and save it in variable through “Set-LogInfo” function and then the log function takes the same file name as “File” argument. Log function defined above can write different type of data on console as well as into log file.

Leave a Reply

Your email address will not be published. Required fields are marked *

Related Post