uLog.psm1
<#
Try/cath to avaoid error when recreating the type in case we relaunch the script LogFormatter is for compatibility purpose, created at first. It will be removed in a later version #> try{ Add-Type -TypeDefinition @" public enum LogLevel { FATAL = 701, CRITICAL = 700, ERROR = 600, WARNING = 501, WARN = 500, INFORMATION = 401, INFO = 400, SUCCESS = 300, DEBUG = 201, VERBOSE = 200, TRACE = 100 } "@ }catch{} try{ Add-Type -TypeDefinition @" public enum uLogLevel { FATAL = 701, CRITICAL = 700, ERROR = 600, WARNING = 501, WARN = 500, INFORMATION = 401, INFO = 400, SUCCESS = 300, DEBUG = 201, VERBOSE = 200, TRACE = 100 } "@ }catch{} function Get-Header{ [PSCustomObject] @{ ComputerName = $env:COMPUTERNAME; UserName = $env:USERNAME; PowerShellVersion = $PSVersionTable.PSVersion.ToString() } } <# .Synopsis Instantiates a new log manager .DESCRIPTION Creates a new logger object. If no handler is provided, a logger with a console handler is created if a script instantiated the logger. It adds an eventlog handler if a module instantiated the logger. Check the test folder in the module folder for some more example. .PARAMETER Append Default is true, so the events are appended to the log handlers. .PARAMETER Handler List of handlers to pass the the log manager. .PARAMETER NoDefaultHandler If specified, no default handler are created, you need to manually add handler with -Handler of after the create with AddHandler(). .PARAMETER NoHeader If specified, no header is written to the handlers. The header consist of messages containing username, computername and PowerShell version. .PARAMETER Source Specify the source of the handler, which can be a file, eventlog name ... .EXAMPLE $log = New-uLog Log-Info -Message 'Hello' This example creates a new log manager and write an information message on the console .EXAMPLE $log = New-uLog $log.AddLogProvider( (New-uLogEventLog) ) Log-Info -Message 'Hello' This example creates a new log manager, adds a evenlog provider the infoemtion message is #> function New-uLog { param( [switch] $Append, [Object[]] $Handler = $null, [switch] $NoDefaultHandler, [switch] $NoHeader, [string] $Source = $MyInvocation.ScriptName ) Begin{ # if instanciated from the command line or editor, $MyInvocation.ScriptName # is empty, so we configure the source to 'Console' if ($Source -eq ''){$Source = 'Console'} } Process{ $global:LOGAPPEND = $Append $handlers = @{} # a dictionary is refered over an array so handlers can be # choosen with autocompletion # if the script is run from a psm1 file, the default handler is eventlog # else default handler is the console if (($Handler.Count -eq 0) -xor ($NoDefaultHandler.IsPresent -eq $true)){ if ($Source.Contains('.') -and $Source.Substring( $Source.LastIndexOf('.')) -eq '.psm1'){ $handlers.Add('EventLog' , (New-uLogEventLog -Source $Source -Name 'EventLog')) }else{ $handlers.Add('Console', (New-uLogConsole -Source $Source -Name 'Console')) } }else{ if ($Handler.Count -ne 0){ $Handler | % { $handlers.Add($_.Name, $_) } } } $log = [PSCustomObject] @{Source = $Source; Handlers = $handlers } $log | Add-Member -MemberType ScriptMethod -Name AddLogHandler -Value { param($handler) $this.Handlers.Add($handler.Name, $handler) if ($handler.Name -ne ''){ $this |Add-Member -MemberType NoteProperty -Name $handler.Name -Value $handler } } $log | Add-Member -MemberType ScriptMethod -Name ForceLogLevel -Value { param([Parameter(Mandatory=$true)] $handler, [Parameter(Mandatory=$true)] $Level) $this.Handlers | % { $_.Level = $Level } } $log | Add-Member -MemberType ScriptMethod -Name Close -Value { # flush all handlers foreach ($h in $this.Handlers.Values){ # we try to flush the handler, for those who implement this function #TOFIX: check the methods with gm ? before using flush ? try{ $h.Flush() }catch{} } } if (-not $NoHeader.IsPresent){ if ($log.Handlers.Count -gt 0){ $log.Handlers.Values | % { $hd = $_ $head = Get-Header ($head | gm -MemberType NoteProperty).Name | % { $prop = $_ $hd.WriteLog((New-LogRecord -Message "$prop : $($head.$prop)" -Level ([loglevel]::INFO))) } $hd.WriteLog((New-LogRecord -Message "Source : $Source" -Level ([loglevel]::INFO))) if (($hd | gm -MemberType Properties | ? {$_.Name -EQ 'Path'}) -ne $null){ $hd.WriteLog((New-LogRecord -Message "Path : $($hd.Path)" -Level ([loglevel]::INFO))) } } } } $global:uLOG = $log $global:uLOG } End { $null = Register-EngineEvent -SourceIdentifier PowerShell.Exiting -Action { $global:uLOG.Close() } } } function New-LogRecord { param( [string] $Message, [string] $Level, [int] $Id = 10000, [int] $Indent = 1 ) [PSCustomObject] @{Message = $Message; Level = [LogLevel] $Level; Id = $Id; Indent = $Indent } } function Write-Log { param( [string] $Message, [ValidateSet('INFO', 'INFORMATION', 'WARN', 'WARNING', 'ERROR', 'FATAL', 'CRITICAL', 'DEBUG', 'TRACE', 'SUCCESS', 'VERBOSE')] $Level, [int] $Id = 1, [string] $Path = $null, [switch] $Append = $false, [switch] $NoDisplayOnTerminal = $false, [Object[]] $Exclude = $null, [Object[]] $Include = $null, [int] $Indent = 1, [Object] $Log = $null, [string] $Source = '' ) $record = New-LogRecord -Message $Message -Level $Level -Id $Id -Indent $Indent if ($Log -eq $null){ Get-Variable -Name uLog -Scope Global -ErrorAction SilentlyContinue | Out-Null if($?){ $Log = $Global:uLog }else{ if ($Source -eq ''){ if ($MyInvocation.ScriptName -eq ''){ $source = $MyInvocation.ScriptName }else{ $source = 'Console' } } $Log = New-uLog -Source $Source } } if ($Include -ne $null){ # if Include is present, we take only Include handlers $handlers = $log.Handlers.Values | ? { $_ -in $Include} }else{ # if Include not sp�cified, and we remove the excluded handlers $handlers = $log.Handlers.Values | ? { $_ -notin $Exclude} } # if no display on terminal, we remove console handlers if ($NoDisplayOnTerminal -eq $true){ $handlers = $handlers | ? {$_.Type -ne 'Console'} } # we loop through handlers to write the message $handlers | % { $_.WriteLog($record) } } <# Helper function to produce the log-* functions We can dynamically create fucntions, but it does not work properlly with Intellisense So we generate the code to copy/paste in the modul with this fucntio For each level of log, we create a log-* function associated with it. These fucntions are more ergonomic than the Write-Log fonction. To create the functions, we loop through the enum of levels and we dynamically create the fonctions. It is more convenient than copy/paste functions definitions, il a new level is needed, juste add it to the enum and it will be taken into account #> function Start-BuildFunctions{ [LogLevel].GetEnumNames() | % { # We convert the level name to camel case, which is morre beautiful $camelCase = $_.Substring(0,1).ToUpper() + $_.Substring(1).ToLower() $func = @" function Log-$camelCase{ param( [string] `$Message, [int] `$Id = 1, [string] `$Path = `$null, [switch] `$Append = `$false, [switch] `$NoDisplayOnTerminal = `$false, [Object[]] `$Exclude = `$null, [Object[]] `$Include = `$null, [int] `$Indent = 1, [Object] `$Log = `$null ) `$Source = `$MyInvocation.ScriptName Write-Log -Message `$Message `` -Level $_ `` -Id `$Id `` -Append `$Append `` -NoDisplayOnTerminal:`$NoDisplayOnTerminal `` -Indent `$Indent `` -Log `$Log `` -Source `$Source `` -Exclude `$Exclude ` -Include `$Include } "@ $func } } function Log-Trace{ param( [string] $Message, [int] $Id = 1, [string] $Path = $null, [switch] $Append = $false, [switch] $NoDisplayOnTerminal = $false, [Object[]] $Exclude = $null, [Object[]] $Include = $null, [int] $Indent = 1, [Object] $Log = $null ) $Source = $MyInvocation.ScriptName Write-Log -Message $Message ` -Level TRACE ` -Id $Id ` -Append $Append ` -NoDisplayOnTerminal:$NoDisplayOnTerminal ` -Indent $Indent ` -Log $Log ` -Source $Source ` -Exclude $Exclude ` -Include $Include } function Log-Verbose{ param( [string] $Message, [int] $Id = 1, [string] $Path = $null, [switch] $Append = $false, [switch] $NoDisplayOnTerminal = $false, [Object[]] $Exclude = $null, [Object[]] $Include = $null, [int] $Indent = 1, [Object] $Log = $null ) $Source = $MyInvocation.ScriptName Write-Log -Message $Message ` -Level VERBOSE ` -Id $Id ` -Append $Append ` -NoDisplayOnTerminal:$NoDisplayOnTerminal ` -Indent $Indent ` -Log $Log ` -Source $Source ` -Exclude $Exclude ` -Include $Include } function Log-Debug{ param( [string] $Message, [int] $Id = 1, [string] $Path = $null, [switch] $Append = $false, [switch] $NoDisplayOnTerminal = $false, [Object[]] $Exclude = $null, [Object[]] $Include = $null, [int] $Indent = 1, [Object] $Log = $null ) $Source = $MyInvocation.ScriptName Write-Log -Message $Message ` -Level DEBUG ` -Id $Id ` -Append $Append ` -NoDisplayOnTerminal:$NoDisplayOnTerminal ` -Indent $Indent ` -Log $Log ` -Source $Source ` -Exclude $Exclude ` -Include $Include } function Log-Success{ param( [string] $Message, [int] $Id = 1, [string] $Path = $null, [switch] $Append = $false, [switch] $NoDisplayOnTerminal = $false, [Object[]] $Exclude = $null, [Object[]] $Include = $null, [int] $Indent = 1, [Object] $Log = $null ) $Source = $MyInvocation.ScriptName Write-Log -Message $Message ` -Level SUCCESS ` -Id $Id ` -Append $Append ` -NoDisplayOnTerminal:$NoDisplayOnTerminal ` -Indent $Indent ` -Log $Log ` -Source $Source ` -Exclude $Exclude ` -Include $Include } function Log-Info{ param( [string] $Message, [int] $Id = 1, [string] $Path = $null, [switch] $Append = $false, [switch] $NoDisplayOnTerminal = $false, [Object[]] $Exclude = $null, [Object[]] $Include = $null, [int] $Indent = 1, [Object] $Log = $null ) $Source = $MyInvocation.ScriptName $arguments = @{} if ($Exclude -ne $null){$arguments['Exclude'] = $Exclude} if ($Include -ne $null){$arguments['Include'] = $Include} Write-Log -Message $Message ` -Level INFO ` -Id $Id ` -Append $Append ` -NoDisplayOnTerminal:$NoDisplayOnTerminal ` -Indent $Indent ` -Log $Log ` -Source $Source @arguments } function Log-Information{ param( [string] $Message, [int] $Id = 1, [string] $Path = $null, [switch] $Append = $false, [switch] $NoDisplayOnTerminal = $false, [Object[]] $Exclude = $null, [Object[]] $Include = $null, [int] $Indent = 1, [Object] $Log = $null ) $Source = $MyInvocation.ScriptName Write-Log -Message $Message ` -Level INFORMATION ` -Id $Id ` -Append $Append ` -NoDisplayOnTerminal:$NoDisplayOnTerminal ` -Indent $Indent ` -Log $Log ` -Source $Source ` -Exclude $Exclude ` -Include $Include } function Log-Warn{ param( [string] $Message, [int] $Id = 1, [string] $Path = $null, [switch] $Append = $false, [switch] $NoDisplayOnTerminal = $false, [Object[]] $Exclude = $null, [Object[]] $Include = $null, [int] $Indent = 1, [Object] $Log = $null ) $Source = $MyInvocation.ScriptName Write-Log -Message $Message ` -Level WARN ` -Id $Id ` -Append $Append ` -NoDisplayOnTerminal:$NoDisplayOnTerminal ` -Indent $Indent ` -Log $Log ` -Source $Source ` -Exclude $Exclude ` -Include $Include } function Log-Warning{ param( [string] $Message, [int] $Id = 1, [string] $Path = $null, [switch] $Append = $false, [switch] $NoDisplayOnTerminal = $false, [Object[]] $Exclude = $null, [Object[]] $Include = $null, [int] $Indent = 1, [Object] $Log = $null ) $Source = $MyInvocation.ScriptName Write-Log -Message $Message ` -Level WARNING ` -Id $Id ` -Append $Append ` -NoDisplayOnTerminal:$NoDisplayOnTerminal ` -Indent $Indent ` -Log $Log ` -Source $Source ` -Exclude $Exclude ` -Include $Include } function Log-Error{ param( [string] $Message, [int] $Id = 1, [string] $Path = $null, [switch] $Append = $false, [switch] $NoDisplayOnTerminal = $false, [Object[]] $Exclude = $null, [Object[]] $Include = $null, [int] $Indent = 1, [Object] $Log = $null ) $Source = $MyInvocation.ScriptName Write-Log -Message $Message ` -Level ERROR ` -Id $Id ` -Append $Append ` -NoDisplayOnTerminal:$NoDisplayOnTerminal ` -Indent $Indent ` -Log $Log ` -Source $Source ` -Exclude $Exclude ` -Include $Include } function Log-Critical{ param( [string] $Message, [int] $Id = 1, [string] $Path = $null, [switch] $Append = $false, [switch] $NoDisplayOnTerminal = $false, [Object[]] $Exclude = $null, [Object[]] $Include = $null, [int] $Indent = 1, [Object] $Log = $null ) $Source = $MyInvocation.ScriptName Write-Log -Message $Message ` -Level CRITICAL ` -Id $Id ` -Append $Append ` -NoDisplayOnTerminal:$NoDisplayOnTerminal ` -Indent $Indent ` -Log $Log ` -Source $Source ` -Exclude $Exclude ` -Include $Include } function Log-Fatal{ param( [string] $Message, [int] $Id = 1, [string] $Path = $null, [switch] $Append = $false, [switch] $NoDisplayOnTerminal = $false, [Object[]] $Exclude = $null, [Object[]] $Include = $null, [int] $Indent = 1, [Object] $Log = $null ) $Source = $MyInvocation.ScriptName Write-Log -Message $Message ` -Level FATAL ` -Id $Id ` -Append $Append ` -NoDisplayOnTerminal:$NoDisplayOnTerminal ` -Indent $Indent ` -Log $Log ` -Source $Source ` -Exclude $Exclude ` -Include $Include } |