Noveris.Logger.psm1
<#
#> ################ # Global settings $ErrorActionPreference = "Stop" Set-StrictMode -Version 2.0 <# #> Class LoggerType { [string]$Prefix [Nullable[System.ConsoleColor]]$Color [string]$LogPath [bool]$WriteHost [bool]$WriteOutput [ScriptBlock]$FormatBlock [ScriptBlock]$OutputBlock <# #> LoggerType() { $this.Prefix = [string]::Empty $this.Color = $null $this.LogPath = [string]::Empty $this.WriteHost = $false $this.WriteOutput = $false $this.FormatBlock = $null $this.OutputBlock = $null } <# #> LoggerType([LoggerType] $loggerType) { $this.Prefix = $loggerType.Prefix $this.Color = $loggerType.Color $this.LogPath = $loggerType.LogPath $this.WriteHost = $loggerType.WriteHost $this.WriteOutput = $loggerType.WriteOutput $this.FormatBlock = $loggerType.FormatBlock $this.OutputBlock = $loggerType.OutputBlock } } <# #> Class GlobalLoggerType : LoggerType { [string]$Name GlobalLoggerType() : base() { } GlobalLoggerType([string]$Name, [LoggerType]$loggerType) : base($loggerType) { $this.Name = $Name } GlobalLoggerType([GlobalLoggerType]$loggerType) : base($loggerType) { $this.Name = $loggerType.Name } } <# .SYNOPSIS Generate a new Logger Type for use by Write-Logger when generating messages. .PARAMETER Name The name of the Logger Type .PARAMETER Prefix A string value to prefix the message with. This is performed after any FormatBlock script. .PARAMETER Color The Color to use when writing content via Write-Host. .PARAMETER LogPath Path to a file to write finalised message content to. Contents are appended and written in UTF8 format. .PARAMETER WriteHost Determine whether the message should be written to the console using Write-Host .PARAMETER WriteOutput Determine whether the message should be written as object output from the function .PARAMETER FormatBlock Script Block to be called for reformat the original message e.g. { "test: $_" } .PARAMETER OutputBlock Script Block to be called with the finalised message text. .PARAMETER Default Defines whether this Logger Type should be the default logger type #> Function New-LoggerType { [CmdletBinding()] param( [Parameter(Mandatory=$false)] [string]$Prefix = [string]::Empty, [Parameter(Mandatory=$false)] [Nullable[System.ConsoleColor]]$Color = $null, [Parameter(Mandatory=$false)] [string]$LogPath = [string]::Empty, [Parameter(Mandatory=$false)] [switch]$WriteHost = $false, [Parameter(Mandatory=$false)] [switch]$WriteOutput = $false, [Parameter(Mandatory=$false)] [AllowNull()] [ScriptBlock]$FormatBlock = $null, [Parameter(Mandatory=$false)] [AllowNull()] [ScriptBlock]$OutputBlock = $null, [Parameter(Mandatory=$false)] [switch]$Default = $false ) process { Write-Verbose "Creating new logger type" $newType = New-Object LoggerType $newType.WriteHost = $WriteHost $newType.WriteOutput = $WriteOutput foreach ($param in $PSBoundParameters.Keys) { switch ($param) { "Prefix" { $newType.Prefix = $Prefix break } "Color" { $newType.Color = $Color break } "LogPath" { $newType.LogPath = $LogPath break } "FormatBlock" { $newType.FormatBlock = $FormatBlock break } "OutputBlock" { $newType.OutputBlock = $OutputBlock break } } } $newType } } <# .SYNOPSIS Update-LoggerType updates an existing LoggerType with new options or may be used to remove options. LoggerType objects can be supplied by pipeline. .PARAMETER Name The name of the Logger Type to update .PARAMETER Prefix A string value to prefix the message with. This is performed after any FormatBlock script. .PARAMETER Color The Color to use when writing content via Write-Host. .PARAMETER LogPath Path to a file to write finalised message content to. Contents are appended and written in UTF8 format. .PARAMETER WriteHost Determine whether the message should be written to the console using Write-Host .PARAMETER WriteOutput Determine whether the message should be written as object output from the function .PARAMETER FormatBlock Script Block to be called for reformat the original message e.g. { "test: $_" } .PARAMETER OutputBlock Script Block to be called with the finalised message text. .PARAMETER Default Defines whether this Logger Type should be the default logger type #> Function Update-LoggerType { [CmdletBinding()] param( [Parameter(Mandatory=$true,Position=0,ValueFromPipeline,ParameterSetName="Type")] [LoggerType]$Type, [Parameter(Mandatory=$true,Position=0,ParameterSetName="Name")] [string]$Name, [Parameter(Mandatory=$false,ParameterSetName="Name")] [Parameter(Mandatory=$false,ParameterSetName="Type")] $Prefix = $null, [Parameter(Mandatory=$false,ParameterSetName="Name")] [Parameter(Mandatory=$false,ParameterSetName="Type")] [Nullable[System.ConsoleColor]]$Color = $null, [Parameter(Mandatory=$false,ParameterSetName="Name")] [Parameter(Mandatory=$false,ParameterSetName="Type")] $LogPath = $null, [Parameter(Mandatory=$false,ParameterSetName="Name")] [Parameter(Mandatory=$false,ParameterSetName="Type")] [switch]$WriteHost, [Parameter(Mandatory=$false,ParameterSetName="Name")] [Parameter(Mandatory=$false,ParameterSetName="Type")] [switch]$WriteOutput, [Parameter(Mandatory=$false,ParameterSetName="Name")] [Parameter(Mandatory=$false,ParameterSetName="Type")] [AllowNull()] [ScriptBlock]$FormatBlock = $null, [Parameter(Mandatory=$false,ParameterSetName="Name")] [Parameter(Mandatory=$false,ParameterSetName="Type")] [AllowNull()] [ScriptBlock]$OutputBlock = $null ) process { $opType = $null if ($PSBoundParameters.Keys.Contains("Name")) { if ([String]::IsNullOrEmpty($Name)) { throw New-Object ArgumentException -ArgumentList "Null or empty Name supplied to Update-LoggerType" } $opType = Get-GlobalLoggerType -Name $Name } elseif ($PSBoundParameters.Keys.Contains("Type")) { if ($Type -eq $null) { throw New-Object ArgumentException -ArgumentList "Null type supplied to Update-LoggerType" } $opType = $Type } else { throw New-Object Exception -ArgumentList "Unknown operation type for Update-LoggerType" } if ($opType -eq $null) { throw New-Object Exception -ArgumentList "Internal error in Update-LoggerType: opType is null" } Write-Verbose "Updating logger type" foreach ($param in $PSBoundParameters.Keys) { switch ($param) { "Prefix" { $opType.Prefix = $Prefix break } "Color" { $opType.Color = $Color break } "LogPath" { $opType.LogPath = $LogPath break } "WriteHost" { $opType.WriteHost = $WriteHost break } "WriteOutput" { $opType.WriteOutput = $WriteOutput break } "FormatBlock" { $opType.FormatBlock = $FormatBlock break } "OutputBlock" { $opType.OutputBlock = $OutputBlock break } } } } } <# .SYNOPSIS Returns all Logger Types or a single Logger Type, if a name is referenced. Names may be passed by pipeline input. .PARAMETER Name The Name of the specific Logger Type to display #> Function Get-GlobalLoggerType { [CmdletBinding()] param( [Parameter(Mandatory=$false,Position=0)] [string]$Name = [string]::Empty ) process { $types = $script:GlobalLoggerTypes.Keys if ($PSBoundParameters.Keys -contains "Name") { if ([string]::IsNullOrEmpty($Name)) { throw New-Object ArgumentException -ArgumentList "Null or empty Name supplied to Get-GlobalLoggerType" } if (!$script:GlobalLoggerTypes.ContainsKey($Name)) { throw New-Object ArgumentException -ArgumentList "Name supplied to Get-GlobalLoggerType does not exist" } $types = $($Name) } # Forces generation of a new collection, separate from the original # necessary to allow piping to remove-globalloggertype without failure # due to 'underlying collection has changed' error. $types = $types | ForEach-Object { $_ } $types | ForEach-Object { $script:GlobalLoggerTypes[$_] } } } <# #> Function Remove-GlobalLoggerType { [CmdletBinding()] param( [Parameter(Mandatory=$true,Position=0)] [string]$Name = [string]::Empty ) process { if ([string]::IsNullOrEmpty($Name)) { throw New-Object ArgumentException -ArgumentList "Null or empty Name passed to Remove-GlobalLoggerType" } if (!$script:GlobalLoggerTypes.ContainsKey($Name)) { Write-Verbose "Logger type does not exist: ${Name}" return } Write-Verbose "Removing logger type: ${Name}" $null = $script:GlobalLoggerTypes.Remove($Name) } } <# #> Function Save-GlobalLoggerType { [CmdletBinding()] param( [Parameter(Mandatory=$true,Position=0)] [string]$Name = [string]::Empty, [Parameter(Mandatory=$true,ValueFromPipeline)] [LoggerType]$Type ) process { if ([string]::IsNullOrEmpty($Name)) { throw New-Object ArgumentException -ArgumentList "Null or empty Name supplied to Save-GlobalLoggerType" } if ($Type -eq $null) { throw New-Object ArgumentException -ArgumentList "Null Type supplied to Save-GlobalLoggerType" } $script:GlobalLoggerTypes[$Name] = (New-Object GlobalLoggerType -ArgumentList $Name, $Type) } } <# #> Function Get-DefaultLoggerType { [CmdletBinding()] param() if ($script:DefaultLoggerType -eq $null) { Write-Verbose "No Logger default defined. Creating new default logger" $script:DefaultLoggerType = New-LoggerType -WriteHost } $script:DefaultLoggerType } <# #> Function Reset-DefaultLoggerType { [CmdletBinding()] param() Write-Verbose "Resetting default logger type" $script:DefaultLoggerType = (New-LoggerType -WriteHost) } <# #> Function Set-DefaultLoggerType { [CmdletBinding()] param( [Parameter(Mandatory=$true,ValueFromPipeline)] [LoggerType]$Type ) process { if ($Type -eq $null) { throw New-Object ArgumentException -ArgumentList "Null Type supplied to Set-DefaultLoggerType" } $script:DefaultLoggerType = New-Object LoggerType -ArgumentList $Type } } <# #> Function Write-Logger { [CmdletBinding(DefaultParameterSetName="default")] param( [Parameter(Mandatory=$true,Position=0,ValueFromPipeline,ParameterSetName="Name")] [Parameter(Mandatory=$true,Position=0,ValueFromPipeline,ParameterSetName="Type")] [Parameter(Mandatory=$true,Position=0,ValueFromPipeline,ParameterSetName="default")] [AllowEmptyString()] [string]$Message, [Parameter(Mandatory=$true,ParameterSetName="Name")] [string]$Name = [string]::Empty, [Parameter(Mandatory=$true,ParameterSetName="Type")] [LoggerType]$Type ) process { $opType = $null foreach ($param in $PSBoundParameters.Keys) { switch ($param) { "Name" { if ([string]::IsNullOrEmpty($Name)) { throw New-Object ArgumentException -ArgumentList "Null or empty Name supplied to Write-Logger" } if (!$script:GlobalLoggerTypes.Contains($Name)) { throw New-Object ArgumentException -ArgumentList "Name supplied does not exist" } $opType = $script:GlobalLoggerTypes[$Name] break } "Type" { if ($Type -eq $null) { throw New-Object ArgumentException -ArgumentList "Null Type supplied to Write-Logger" } $opType = $Type break } } } if ($opType -eq $null) { # Need to use the default logger $opType = $script:DefaultLoggerType if ($opType -eq $null) { # Somehow the default logger type is $null. Generate temporary default logger $opType = New-LoggerType -WriteHost } } $prefix = $opType.Prefix $color = $opType.Color $formatBlock = $opType.FormatBlock $outputBlock = $opType.OutputBlock $writeHost = $opType.WriteHost $writeOutput = $opType.WriteOutput $logPath = $opType.LogPath # Default to Grey for colour if ($color -eq $null) { $color = [System.ConsoleColor]::Gray } $msg = $Message # Use format block to pre-process message, if defined if ($formatBlock -ne $null) { $msg = ($msg | ForEach-Object $formatBlock).ToString() } # Add prefix to message if (![string]::IsNullOrEmpty($prefix)) { $msg = [string]::Format("{0}{1}", $prefix, $msg) } # Write host output, if required if ($writeHost) { Write-Host -ForegroundColor $color $msg } # Write object output, if required if ($writeOutput) { Write-Output $msg } # Write to file, if defined if (![string]::IsNullOrEmpty($logPath)) { $msg | Out-File -Append -FilePath $logPath -Encoding UTF8 } # Send message to script block, if defined if ($outputBlock -ne $null) { $null = $msg | ForEach-Object $outputBlock } } } ################ # Script variables $script:GlobalLoggerTypes = @{} $script:DefaultLoggerType = (New-LoggerType -WriteHost) |