PSLogger.psm1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 |
#requires -version 3 <# .SYNOPSIS PSLogger is a PowerShell module to enhance and simplify interacting with log files on the file system. .DESCRIPTION PSLogger provides functions to Write logs, find / enumerate recently written log files, and Read the latest lines of recent .log files. .NOTES NAME : PSLogger VERSION : 1.3.5 LAST UPDATED: 11/16/2015 AUTHOR : Bryan Dady | https://github.com/bcdady/ Original Write-Log Author: Jeffery Hicks http://jdhitsolutions.com/blog http://twitter.com/JeffHicks Date: 3/3/2011 #> # Setup necessary configs for PSLogger's Write-Log cmdlet [string]$global:loggingPreference = 'Continue' # set $loggingPreference to anything other than continue, to leverage write-debug or write-verbose, without writing to a log on the filesystem # Define $loggingPath as .\WindowsPowerShell\log directory, under the user's profile Documents folder. # To assure portability and compatability across client and server, and various OS versions, we use special Environment paths, instead of $HOME or $env:userprofile # http://windowsitpro.com/powershell/easily-finding-special-paths-powershell-scripts # If this path doesn't already exist, it will be created later, in Write-Log function [string]$global:loggingPath = Join-Path -Path "$([Environment]::GetFolderPath('MyDocuments'))" -ChildPath 'WindowsPowerShell\log' # Handle when special Environment variable MyDocuments is a mapped drive, it returns as the full UNC path. if (([Environment]::GetFolderPath('MyDocuments')).Substring(0,2) -match '\\') { $global:loggingPath = $loggingPath.Replace("$(Split-Path -Path "$([Environment]::GetFolderPath('MyDocuments'))" -Parent)"+'\',$(Get-PSDrive -PSProvider FileSystem | Where-Object -FilterScript { $PSItem.DisplayRoot -eq $(Split-Path -Path "$([Environment]::GetFolderPath('MyDocuments'))" -Parent) }).Root) } [string]$global:logFileDateString = Get-Date -UFormat '%Y%m%d' # Need to keep an eye on this one, in case PowerShell sessions run for multiple days, I doubt this variable value will be refreshed / updated New-Variable -Name LastFunction -Description "Retain 'state' of the last function name called, to streamline logging statements from the same function" -Force -Scope Global -Visibility Public [bool]$writeIntro = $true [string]$global:LastFunction Function Write-Log { <# .Synopsis Write a message to a log file. .Description Write-Log can be used to write text messages to a log file. It can be used like Write-Verbose, and looks for two variables that you can define in your scripts and functions. If the function finds $LoggingPreference with a value of "Continue", the message text will be written to the file. The default file is PowerShellLog.txt in your %TEMP% directory. You can specify a different file path by parameter or set the $logFilePref variable. See the help examples. This function also supports Write-Verbose which means if -Verbose is detected, the message text will be written to the Verbose pipeline. Thus if you call Write-Log with -Verbose and a the $loggingPreference variable is set to continue, you will get verbose messages AND a log file. .Parameter Message The message string to write to the log file. It will be prepended with a date time stamp. .Parameter Function The Function Parameter passes the name of the Function or CmdLet that invoked the Write-Log function. This is used to write related log messages into a topical log file, instead of writing all log messages into a common file If not specified, this defaults to 'PowerShell' .Parameter Path The filename and path for the log file. The default is defined as $loggingPath (above) If $logFilePref variable is (bound) and passed into the Write-Log function, then that override path value will be used. .EXAMPLE PS .\>Write-Log -Message "Test Message ... this is a test of the Write-Log function" -Function TEST .EXAMPLE PS .\>Write-Log -Message "Test Message ... this is another test of the Write-Log function, to a custom specified path" -Function TEST -Path $env:TEMP\testing.log .Notes NAME: Write-Log AUTHOR: Bryan Dady, adapted from original work by Jeffery Hicks VERSION: 1.3.5 LASTEDIT: 11/16/2015 .Link http://jdhitsolutions.com/blog/2011/03/powershell-automatic-logging/ .Link Write-Verbose #> [cmdletbinding()] Param( [Parameter( Mandatory = $true, Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = 'The message string to write to the log file. It will be prepended with a date time stamp.' )] [ValidateNotNullOrEmpty()] [string]$Message, [Parameter( Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ValueFromRemainingArguments = $false, Position = 1, HelpMessage = 'The Function Parameter passes the name of the Function or CmdLet that invoked this Write-Log function' )] [ValidateNotNullOrEmpty()] [Alias('Action', 'Source')] [String]$Function, [Parameter( Position = 2, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ValueFromRemainingArguments = $false, HelpMessage = 'The optional Path parameter specifies the path of the log file to write the message to.' )] [ValidateScript({Test-Path -Path $PSItem -PathType Any})] [string]$Path ) # Assign Function variable to 'PowerShell' if blank or null if (($Function -eq $null) -or ($Function -eq '')) { $Function = 'PowerShell' } # Detect if this Function is the same as the $LastFunction. If not, verbosely log which new Function is active if ($Function -eq $LastFunction) { # Retain 'state' of the last function name called, to streamline logging statements from the same function $writeIntro = $false } else { Set-Variable -Name LastFunction -Value $Function -Force -Scope Global $writeIntro = $true } # Detect -debug mode: # http://blogs.msdn.com/b/powershell/archive/2009/04/06/checking-for-bound-parameters.aspx # https://kevsor1.wordpress.com/2011/11/03/powershell-v2-detecting-verbose-debug-and-other-bound-parameters/ if ($PSBoundParameters['Debug'].IsPresent) { [bool]$script:testMode = $true $logFilePref = Join-Path -Path "$loggingPath\test" -ChildPath "$("$Function", "$logFileDateString" -join '_').log" } else { [bool]$testMode = $false $logFilePref = Join-Path -Path "$loggingPath" -ChildPath "$("$Function", "$logFileDateString" -join '_').log" } # Pass on the message to Write-Debug cmdlet if -Debug parameter was used if ($testMode) { Write-Debug -Message $Message if ($writeIntro -and ( $Message -notlike 'Exit*')) { Write-Output -InputObject "Logging [Debug] to $logFilePref`n" } } elseif ($PSBoundParameters['Verbose'].IsPresent) { #Pass on the message to Write-Verbose cmdlet if -Verbose parameter was used Write-Verbose -Message $Message if ($writeIntro -and ( $Message -notlike 'Exit*')) { Write-Output -InputObject "Logging to $logFilePref`n" } } # Only write to the log file if the $LoggingPreference variable is set to Continue if ($loggingPreference -eq 'Continue') { # Before writing a copy of $Message to an output file, strip line breaks and/or other formatting that could interfere with clear/standardized logging $Message = $Message -replace "`n", ' ' $Message = $Message -replace '\s{2,}', ' ' #if $Path parameter was specified, then use it, otherwise use the derived $logFilePref if ($Path) { $LogFile=$Path } else { $LogFile=$logFilePref } # Confirm or create LogFile path, otherwise Out-File throws DirectoryNotFoundException; # Only need to do this once per unique $LogFile path, so use $writeIntro as that flag if ($writeIntro -and (-not (Test-Path -Path $(Split-Path -Path $LogFile -Parent) -PathType Container) ) ) { Write-Output -InputObject "$(Get-Date) Creating logging path: $(Split-Path -Path $LogFile)" -NoEnumerate | Out-Host New-Item -Path $(Split-Path -Path $LogFile) -Force -ItemType Directory -ErrorAction Ignore } if ($testMode) { Write-Output -InputObject "$(Get-Date) [Debug] $Message" -NoEnumerate | Out-File -FilePath $LogFile -Append } elseif ($PSBoundParameters['Verbose'].IsPresent) { Write-Output -InputObject "$(Get-Date) [Verbose] $Message" -NoEnumerate | Out-File -FilePath $LogFile -Append } else { Write-Output -InputObject "$(Get-Date) $Message" -NoEnumerate | Out-File -FilePath $LogFile -Append } } } #end function Function Read-Log { <# .Synopsis Reads the latest log file, optionally displaying only the latest number of specified lines .Description Intended as a complement to the Write-Log function provided within the PSLogger module, Read-Log will find the latest log file in the defined $loggingPath directory, and return some basic stats of that file, as well as get it's contents for review .Parameter MessageSource The message source is an optional parameter that specifies which module, function, or script wrote the log file to be retrieved. If this parameter is not specified, function returns the latest available log file, regardless of message source. The value of this parameter becomes a filter to the search of .log files within the $loggingPath directory. .Parameter lineCount The most recent number of lines from the log file in question. unless the $logFilePref variable is found. If so, then this value will be used. .Example PS .\> Read-Log Returns basic file properties, and last 10 lines, of the latest / newest log file found in $loggingPath directory .Example PS .\> Read-Log -MessageSource Get-Profile -lineCount 30 Returns latest log file, reading the latest 30 lines, specific to function Get-Profile .Notes NAME: Read-Log AUTHOR: Bryan Dady VERSION: 1.0 LASTEDIT: 04/15/2015 .Output Matches default properties return the same as Get-Item: * Name * LastWriteTime * Length * Path #> [cmdletbinding()] Param( [Parameter( Mandatory = $false, Position = 0, HelpMessage = 'The message source typically matches a log filename string, which represents the name of the function or module which wrote the log file.' )] [Alias('function','source','f','m')] [string]$MessageSource, [Parameter( Mandatory = $false, Position = 1, HelpMessage = 'Provide an integer of line numbers to read from the bottom of the log file.' )] [ValidateNotNullOrEmpty()] [Alias('lines', 'l')] [int16]$lineCount = 10 ) # Use write-output instead of write-log, so that the function of reading log files does not write new log files to be read Write-Output -InputObject "Selecting latest log file to read last $lineCount lines" Write-Output -InputObject "[Debug] Looking for log files with `$MessageSource is $MessageSource" -Debug # Select the newest (1) file (most recent LastWriteTime), with an optional filter, based on MessageSource parameter $latestLogFile = Get-ChildItem -Path $loggingPath -Filter *$MessageSource* -File | Sort-Object -Property LastWriteTime -Descending | Select-Object -First 1 Write-Output -InputObject "[Debug] `$latestLogFile is $latestLogFile" -Debug if (Test-Path -Path $latestLogFile -PathType Leaf -IsValid -ErrorAction Ignore) { Write-Output -InputObject "Selected $latestLogFile" $latestLogFile | Select-Object -Property Name, LastWriteTime | Format-List if ($lineCount -gt 0) { Write-Output -InputObject "`n ... " $latestLogFile | Get-Content -Tail $lineCount Write-Output -InputObject "`n[EOF]`n" } } else { Write-Warning -Message "Could not open $latestLogFile for reading" } } #end function Function Get-LatestLogs { Get-ChildItem -Path $loggingPath | Sort-Object -Descending -Property LastWriteTime | Select-Object -First 10 } |