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:

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

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

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

    • 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.

  2. To Log errors using my method, you would use:

    Write-ErrorLog -Message $($_.Exception.Message) -ExitGracefully
    
    • 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
    
    • This has been tested to record Write-Error, Write-Warning, and just about any other console output PS spits out.
  3. 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
    
  4. Example: Save as .ps1 and run:

    Function Blah
    
    {
    
    Write-Output "$Psscriptroot"
    
    $Scriptpath = Split-Path $Myinvocation.Mycommand.Definition -Parent 
    
    Write-Output "$Scriptpath"
    
    }
    
    Blah