PS: Logging Snippets

4 minute read

Description:

Logging is an essential function for scripting because it not only can display text to a screen, but it records times that scripts have ran and their associated output. I found a couple examples from the Microsoft Script Center and have since incorporated them into my PS Template Script.

To Resolve:

  1. So going by template script:

1a. I define my start, close, and write-log functions in my helper function and then import it with “import-module”.

1b. I use Start-Log to create a log at $PSScriptRoot..\Logs\scriptname.log in the begin block.

1c. During the script I will use “Write-Log “whatever” -Logfile $Logfile” very often.

1d. In the End section, I call my close-log function which will close up the file.

NOTE: The reason I went with this method is because it has the joy of being like “write-host” in that it lets the user know what is going on (think Write-Verbose), it shows colors (darkgreen by default, but I place darkred for errors and cyan for section headers), and it can also log to a log file at the same time so if you place a script as a scheduled task, you can see when it ran or if it ran.

  1. To Log errors using my method, you would use:
Write-ErrorLog -Message $($_.Exception.Message) -ExitGracefully

2a. You could skip most of this logging non sense if you wish and use the redirect method:

Powershell.Exe -Noprofile -File Test.Ps1 > Test.Log

# NOTE: This has been tested to record Write-Error, Write-Warning, and just about any other console output PS spits out.

  1. Here is an overall view of all my logging functions in my helpers.psm1:
Function Start-Log
{
    <#
        .Synopsis
        Function to write the opening part of the logfile at $PSScriptRoot\..\Logs\scriptname.log.
        .Description
        Function to write the opening part of the logfile at $PSScriptRoot\..\Logs\scriptname.log.
        It creates the directory if it doesn't exists and then the log file automatically.
        It checks the size of the file if it already exists and clears it if it is over 10 MB.
        If it exists, it creates a header. This function is best placed in the "Begin" block of a script.
        .Notes
        2017-10-19: v1.0 Initial script 
        #>
    [Cmdletbinding()]

    Param
    (
        [Parameter(Mandatory = $True)]
        [String]$Logfile
    )

    $Regex = '([^\\]*)$'
    $Logparent = $Logfile -Replace $Regex
    If (!(Test-Path $Logparent))
    {
        New-Item -Itemtype Directory -Path $Logparent -Force | Out-Null
    }
    If (!(Test-Path $Logfile))
    {
        New-Item -Itemtype File -Path $Logfile -Force | Out-Null
    }
    
    $Sizemax = 10
    $Size = (Get-Childitem $Logfile | Measure-Object -Property Length -Sum) 
    $Sizemb = "{0:N2}" -F ($Size.Sum / 1mb) + "Mb"
    If ($Sizemb -Ge $Sizemax)
    {
        Get-Childitem $Logfile | Clear-Content
        Write-Verbose "Logfile has been cleared due to size"
    }

    "####################<Script>####################" | Out-File -Encoding ASCII -FilePath $Logfile -Append
    ((Get-Date -Format "yyyy-MM-dd hh:mm:ss tt") + ": " + "Script Started on $env:COMPUTERNAME ") | Out-File -Encoding ASCII -FilePath $Logfile -Append
           
}

Function Write-Log
{
    <# 
        .Synopsis
        Function to write to a log file at $PSScriptRoot\..\Logs\scriptname.log. Colors can be displayed for console view as well.
        .Description
        Function to write to the console with colors specified with the "Color" parameter and a logfile at $PSScriptRoot\..\Logs\scriptname.log.
        .Parameter Message
        The string to be displayed to the screen and in the logfile. 
        .Parameter Color
        The color in which to display the input string on the screen
        Default is DarkGreen
        Valid options are: Black, Blue, Cyan, DarkBlue, DarkCyan, DarkGray, DarkGreen, DarkMagenta, DarkRed, DarkYellow, Gray, Green, Magenta, 
        Red, White, and Yellow.
        .Example 
        Write-Log "Hello Hello"
        This will write "Hello Hello" to the console in DarkGreen text and to the logfile at $PSScriptRoot\..\Logs\scriptname.log.
        .Example 
        Write-Log -Message "Hello Hello Again" -Color Magenta
        .Example 
        Same as above but with Magenta color instead of dark green.
        .Notes
        2017-10-19: v1.0 Initial script 
        #>
        
    [Cmdletbinding(Supportsshouldprocess = $True, Confirmimpact = 'Low')]       
    Param
    (
        [Parameter(Mandatory = $True, Valuefrompipeline = $True, Valuefrompipelinebypropertyname = $True, Position = 0)]
        [String]$Message, 
                
        [Parameter(Mandatory = $False, Position = 1)]
        [Validateset("Black", "Blue", "Cyan", "Darkblue", "Darkcyan", "Darkgray", "Darkgreen", "Darkmagenta", "Darkred", `
                "Darkyellow", "Gray", "Green", "Magenta", "Red", "White", "Yellow")]
        [String]$Color = "Darkgreen",

        [Parameter(Mandatory = $True)]
        [String]$Logfile          
    )
    
    Write-Host $Message -Foregroundcolor $Color 
    ((Get-Date -Format "yyyy-MM-dd hh:mm:ss tt") + ": " + "$Message") | Out-File -Encoding ASCII -FilePath $Logfile -Append
}
New-Alias -Name "Log" -Value Write-Log

Function Write-ErrorLog 
{
    <# 
        .Synopsis
        Function to write an error to the log file.
        .Description
        Function to write an error to the log file. This function is usually used in the catch block.
        .Parameter Message
        What to write after the word "ERROR:" in the logfile.
        .Parameter ExitGracefully
        Allows the logfile to wrap up before exiting.
        .Notes
        2017-10-19: v1.0 Initial script 
        #>
        
    Param (

        [Parameter(Mandatory = $True, Valuefrompipeline = $True, Valuefrompipelinebypropertyname = $True, Position = 0)]
        [String]$Message,

        [Parameter(Mandatory = $false, Position = 1)]
        [switch]$ExitGracefully,

        [Parameter(Mandatory = $True)]
        [String]$Logfile

    )

    If ( $ExitGracefully)
    {
        Write-Error "$Message" 
        ((Get-Date -Format "yyyy-MM-dd hh:mm:ss tt") + ": " + "ERROR: " + "$Message") | Out-File -Encoding ASCII -FilePath $Logfile -Append
        ((Get-Date -Format "yyyy-MM-dd hh:mm:ss tt") + ": " + "ERROR: " + "Exiting early / breaking out!") | Out-File -Encoding ASCII -FilePath $Logfile -Append
        Stop-Log
        Break
    }
    Else
    {
        Write-Error "$Message" 
        ((Get-Date -Format "yyyy-MM-dd hh:mm:ss tt") + ": " + "ERROR: " + "$Message") | Out-File -Encoding ASCII -FilePath $Logfile -Append
    }
}

Function Stop-Log
{
    <# 
        .Synopsis
        Function to write the closing part of the logfile at $PSScriptRoot\..\Logs\scriptname.log.
        .Description
        Function to write the closing part of the logfile at $PSScriptRoot\..\Logs\scriptname.log.
        This function is best placed in the "End" block of a script.
        .Notes
        2017-10-19: v1.0 Initial script 
        #>
    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory = $True)]
        [String]$Logfile
    )
    ((Get-Date -Format "yyyy-MM-dd hh:mm:ss tt") + ": " + "Script Completed on $env:COMPUTERNAME") | Out-File -Encoding ASCII -FilePath $Logfile -Append
    "####################</Script>####################" | Out-File -Encoding ASCII -FilePath $Logfile -Append
}

To Create A Log File At Scripts Location:

"$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath('.\'))" + "\log.log"

# For PSVersion <3:
$scriptPath = split-path -parent $MyInvocation.MyCommand.Definition

# For PSVersion >3:
$PSScriptRoot

Example: Save as .ps1 and run:

Function Blah

{

Write-Output "$Psscriptroot"

$Scriptpath = Split-Path $Myinvocation.Mycommand.Definition -Parent 

Write-Output "$Scriptpath"

}

Blah