JT.WriteLog.psm1
Enum errorLevel { ERROR WARNING SUCCESS INFO DEBUG } Enum logRecycle { Continue Daily Hourly } $_setting = @{ 'DebugMode' = $false } Function Write-Log { <# .SYNOPSIS Write-Log is a Powershell logging script. .PARAMETER ErrorLevel Define level of the message. .PARAMETER Message Log message. .PARAMETER LogRecycling Specific the recycling of the log file (Default value is 'continue' whcih it will write into single file.) .PARAMETER LogPath Specific the path of the log file (The default path is the 'Log' folder under the script locaiton). If the command is not call from the script, it will create a log file '_log.txt' under existing location. If the 'LogPath' is $null, the log message will write to console only. .PARAMETER DebugMode Switch the 'DebugMode' on and off. If debug mode is off, it will not output and message to console and log file when the errorlevel of the message is DEBUG. .PARAMETER RawData Direct write data into log file. (If 'RawData' contain data, it will bypass the data input from 'ErrorLevel' and 'Message') Remark: The Write-Log will not show any messaage on console and add any information (Timestamp, Errorlevel, Call from) when write to file. .EXAMPLE Write-Log -errorLevel INFO -message 'Hello World!' Output (console): yyyy-MM-dd HH:mm:ss.fff | INFO | <ScriptBlock> | Hello World! Output file name: _log.txt .EXAMPLE Write-Log -errorLevel INFO -message 'Hello World!' -logRecycling Daily Output (console): yyyy-MM-dd HH:mm:ss.fff | INFO | <ScriptBlock> | Hello World! Output file name: _yyyy-MM-dd_log.txt .EXAMPLE If the [Write-Log] is called under [Start-Job] / [Start-ThreadJob], the write to log function will be disabled and the output will changed to detail (DebugMode) format. Example: [ScriptBlock]$Test-ScriptBlock = { Function Run-ScriptBlock { Write-Log -ErrorLevel INFO -Message 'Log message' } Run-ScriptBlock } Start-ThreadJob -ScriptBlock $Test-ScriptBlock Get-Job | Wait-Job | Receive-Job 6>&1 | Write-Log .NOTES It suggest use '$PSDefaultParameterValues' to pre-define the 'LogRecycling' and 'LogPath' setting. e.g.: $PSDefaultParameterValues['Write-Log:LogRecycling'] = 'daily' $PSDefaultParameterValues['Write-Log:LogPath'] = 'C:\Temp\' $PSDefaultParameterValues['Write-Log:DebugMode'] = $True #> [CmdletBinding()] param( [errorLevel]$ErrorLevel = 'INFO', [string]$Message, [logRecycle]$LogRecycling = 'Continue', [string]$LogPath = '.\Log', [switch]$DebugMode, [parameter(ValueFromPipeline = $true)] [string[]]$RawData = $null, [ValidatePattern("^\d+$")] [Parameter(DontShow)] [string]$LengthOfFunctionName = 20 ) Process { # MARK: Define log file location and function name $Stack = @(Get-PSCallStack) If ($_setting.DebugMode) { $Global:CallStack = $Stack } $_configure = @{ 'FunctionName' = $Stack[1].Command } # The write log function will be disabled if it call from [Start-Job] / [Start-ThreadJob] If (($Host.Name -eq 'Default Host') -or ($Host.Name -eq 'ServerRemoteHost')) { $_threadJob = $true $LogPath = $null If ($Stack.Count -eq 1) { $_configure.FunctionName = '<Thread Job>' } Else { $_configure.FunctionName += ' <Job>' } } If ($Stack[-1].ScriptName) { # Use command call the PS1 file $_configure['PsScrtipRoot'] = Split-Path $Stack[-1].ScriptName $_configure['LogFileName'] = (Get-Item $Stack[-1].ScriptName).BaseName } ElseIf ($Stack.Count -eq 2) { # Use [Write-Log] cmdlet from <Console> If (!$_threadJob) { $_configure.FunctionName = '<Console>' } $_configure['PsScrtipRoot'] = $PWD.ToString() $_configure['LogFileName'] = 'Console' } ElseIf ($Stack[-2].ScriptName) { # Use Powershell console call the PS1 file $_configure['PsScrtipRoot'] = Split-Path $Stack[-2].ScriptName $_configure['LogFileName'] = (Get-Item $Stack[-2].ScriptName).BaseName } Else { # Call from PowerShell console $_configure['PsScrtipRoot'] = $PWD.ToString() $_configure['LogFileName'] = 'Console' } If ($_configure.FunctionName -eq '<ScriptBlock> <Job>') { $_configure.FunctionName = '<Thread Job>' } # MARK: Define log format $_date = Get-Date -Format 'yyyy-MM-dd HH:mm:ss.fff' If ($_configure.FunctionName.Length -gt $_setting["$($_configure.PsScrtipRoot):$($_configure.LogFileName)"]) { $_setting["$($_configure.PsScrtipRoot):$($_configure.LogFileName)"] = $_configure.FunctionName.Length } If ($_setting["$($_configure.PsScrtipRoot):$($_configure.LogFileName)"] -gt $LengthOfFunctionName) { $LengthOfFunctionName = $_setting["$($_configure.PsScrtipRoot):$($_configure.LogFileName)"] } Else { $_setting["$($_configure.PsScrtipRoot):$($_configure.LogFileName)"] = $LengthOfFunctionName } $_logFormat = '{0} | {1, -7} | {2, -xxxx} | {3}'.Replace('xxxx', $LengthOfFunctionName.Trim()) # {yyyy-MM-dd HH:mm:ss.fff} | {ErrorLevel} | {FunctionName} | {Message} $_errorTab = @{ 'ERROR' = 'Red'; 'WARNING' = 'Yellow'; 'SUCCESS' = 'Green'; 'INFO' = 'White'; 'DEBUG' = 'DarkYellow' } # MARK: Log file cycling Switch ($LogRecycling) { 'Daily' { $_logRecyclingValue = Get-Date -Format '_yyyy-MM-dd'; break } 'Hourly' { $_logRecyclingValue = Get-Date -Format '_yyyy-MM-dd-HH'; break } Default { $_logRecyclingValue = '' } } # MARK: Log file location If ($LogPath) { If (-not $LogPath.Contains(':')) { $LogPath = "$($_configure.PsScrtipRoot)\$($LogPath)" } # Check the log folder and try to create it if not exist. If (Test-Path $LogPath -PathType Container) { $_logPath = $LogPath } Else { Try { $_logPath = (New-Item $LogPath -ItemType Directory -Force -ErrorAction SilentlyContinue).ToString() } Catch { Write-Host -ForegroundColor Red "<<<<< Error for create log folder ($($LogPath))!! >>>>>" } } } Else { $_logPath = $null } # MARK: Define log file name If ($_logPath) { $_logFile = [string]::Concat( "$($_logPath)\", $_configure.LogFileName, $_logRecyclingValue, '_log.txt') } Else { $_logFile = $null } If ($RawData) { ## ---------------------------- ## MARK: Write log from 'RawData' ## ---------------------------- $_logMessage = $RawData } Else { ## ---------------------------- ## MARK: Generate log message ## ---------------------------- # Prepare log message If (!$debugMode -and ($ErrorLevel -eq 'DEBUG')) { $_logMessage = $null } Else { $_logMessage = [string]::Format( $_logFormat, $_date, $ErrorLevel.ToString().ToUpper(), $_configure.FunctionName, $Message ) } # Write message to console If ($_logMessage) { If ($DebugMode -or $_threadJob) { If ($ErrorLevel -eq 'INFO') { Write-Host $_logMessage } Else { Write-Host $_logMessage -ForegroundColor $_errorTab.($ErrorLevel.ToString()) } } Else { If ($ErrorLevel -eq 'INFO') { Write-Host $Message } Else { Write-Host $Message -ForegroundColor $_errorTab.($ErrorLevel.ToString()) } } } } # MARK: Write message to log file If ($_logFile) { $_logMessage | Out-File -Append $_logFile -Encoding utf8 } If ($_setting.DebugMode) { $_setting['LastRecord'] = @{ 'LogPath' = $_logPath 'LogFile' = $_logFile 'LogFormat' = $_logFormat 'LogMessage' = $_logMessage 'LogRecycling' = $LogRecycling 'ErrorLevel' = $ErrorLevel.ToString() 'InputMessage' = $Message } } } } Export-ModuleMember -Function 'Write-Log' function Get-WriteLogData { If ($_setting.ContainsKey('LastRecord')) { $_setting.'LastRecord' } Else { Write-Host -ForegroundColor Yellow 'No data found!' } } If ($_setting.DebugMode) { Export-ModuleMember -Function Get-WriteLogData } |