PSWriteLog.psm1
<#
.DESCRIPTION Write a Log Header similar to the one created by `Start-Transcript -IncludeInvocationHeader`: ********************** Windows PowerShell transcript start Start time: 20230110191101 Username: UTSARR\SYSTEM RunAs User: UTSARR\SYSTEM Configuration Name: Machine: RR711111IP01 (Microsoft Windows NT 10.0.19044.0) Host Application: C:\WINDOWS\system32\WindowsPowerShell\v1.0\PowerShell.exe -NoLogo -Noninteractive -NoProfile -ExecutionPolicy Bypass & 'C:\WINDOWS\CCM\SystemTemp\861996c9-1d3e-4522-9837-ff30577a8184.ps1' True Process ID: 40784 PSVersion: 5.1.19041.1682 PSEdition: Desktop PSCompatibleVersions: 1.0, 2.0, 3.0, 4.0, 5.0, 5.1.19041.1682 BuildVersion: 10.0.19041.1682 CLRVersion: 4.0.30319.42000 WSManStackVersion: 3.0 PSRemotingProtocolVersion: 2.3 SerializationVersion: 1.1.0.1 ********************** #> function Get-InvocationHeader { [CmdletBinding()] param() $tmp = New-TemporaryFile Start-Transcript -LiteralPath $tmp.FullName -IncludeInvocationHeader -Force | Out-Null Stop-Transcript | Out-Null $inHeader = $false $header = Get-Content $tmp.FullName | ForEach-Object { if ($_.StartsWith('*') -and $inHeader) { # Reached end of header break } elseif ($_.StartsWith('*')) { # Reached start of header $inHeader = $true } else { # In header switch -regex ($_.Trim()) { '^Windows PowerShell transcript start' { Write-Output ('PSWriteLog v{0} Invocation Header' -f (Get-Module 'PSWriteLog' | Select-Object -First 1).Version) break } '^Start time\:\s+' { Write-Output ('Start time: {0}' -f (Get-Date -Format 'O')) break } default { Write-Output $_ } } } } $tmp.FullName | Remove-Item -ErrorAction 'SilentlyContinue' -Force $env:PSWriteLogIncludeInvocationHeader = $null return $header } <# .SYNOPSIS Write messages to a log file in CMTrace.exe compatible format or Legacy text file format. .DESCRIPTION Write messages to a log file in CMTrace.exe compatible format or Legacy text file format and optionally display in the console. .PARAMETER Message The message to write to the log file or output to the console. .PARAMETER Severity Defines message type. When writing to console or CMTrace.exe log format, it allows highlighting of message type. Options: 1 = Information (default), 2 = Warning (highlighted in yellow), 3 = Error (highlighted in red) .PARAMETER Source The source of the message being logged. .PARAMETER Component The heading for the portion of the script that is being executed. Default is: $script:installPhase. .PARAMETER LogType Choose whether to write a CMTrace.exe compatible log file or a Legacy text log file. .PARAMETER LogFileDirectory Set the directory where the log file will be saved. .PARAMETER LogFileName Set the name of the log file. .PARAMETER MaxLogFileSizeMB Maximum file size limit for log file in megabytes (MB). Default is 10 MB. Setting to 0 will disable log rotations. .PARAMETER ContinueOnError Suppress writing log message to console on failure to write message to log file. .EXAMPLE Write-Log -Message "Installing patch MS15-031" -Source 'Add-Patch' -LogType 'CMTrace' .EXAMPLE Write-Log -Message "Script is running on Windows 8" -Source 'Test-ValidOS' -LogType 'Legacy' .NOTES Taken from PSAppDeployToolkit: https://github.com/PSAppDeployToolkit/PSAppDeployToolkit/blob/3.6.4/Toolkit/AppDeployToolkit/AppDeployToolkitMain.ps1#L480-L741 .LINK https://github.com/VertigoRay/PSWriteLog #> function global:Write-Log { [CmdletBinding()] Param( [Parameter( Mandatory = $true, Position = 0, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)] [AllowEmptyCollection()] [string[]] $Message, [Parameter(Mandatory = $false, Position = 1)] [string] $Severity = 'Info', [Parameter(Mandatory = $false, Position = 2)] [ValidateNotNull()] [string] $Source = (& { if ($script:MyInvocation.Value.ScriptName) { Split-Path -Path $script:MyInvocation.Value.ScriptName -Leaf } else { Split-Path -Path $script:MyInvocation.MyCommand.Definition -Leaf } }) + ':' + $MyInvocation.ScriptLineNumber, [Parameter(Mandatory = $false, Position = 3)] [alias('ScriptSection')] [ValidateNotNull()] [string] $Component = (& { $PSCallStack = (Get-PSCallStack)[1]; "$($PSCallStack.Command) $($PSCallStack.Arguments)" }), [Parameter(Mandatory = $false, Position = 4)] [ValidateSet('CMTrace', 'Legacy')] [string] $LogType = $(if ($env:PSWriteLogType) { $env:PSWriteLogType } else { 'CMTrace' }), [Parameter(Mandatory = $false, Position = 5)] [ValidateNotNullorEmpty()] [IO.FileInfo] $FilePath = $(if ($env:PSWriteLogFilePath) { $env:PSWriteLogFilePath } else { [IO.Path]::Combine($env:Temp, ('PowerShell {0} {1} {2}.log' -f $PSVersionTable.PSEdition, $PSVersionTable.PSVersion, $MyInvocation.CommandOrigin)) }), [Parameter(Mandatory = $false, Position = 6)] [ValidateNotNullorEmpty()] [decimal] $MaxLogFileSizeMB = $(if ($env:PSWriteLogMaxLogFileSizeMB) { $env:PSWriteLogMaxLogFileSizeMB -as [decimal] } else { 10 }), [Parameter(Mandatory = $false)] [bool] $ContinueOnError = $(if ($env:PSWriteLogContinueOnError) { $env:PSWriteLogContinueOnError -as [bool] } else { $false }), [Parameter(Mandatory = $false)] [bool] $DisableLogging = $(if ($env:PSWriteLogDisableLogging) { $env:PSWriteLogDisableLogging -as [bool] } else { $false }), [Parameter(Mandatory = $false)] [bool] $IncludeInvocationHeader = $(if ($env:PSWriteLogIncludeInvocationHeader) { $env:PSWriteLogIncludeInvocationHeader -as [bool] } else { $false }) ) begin { Microsoft.PowerShell.Utility\Write-Debug ('[Write-Log] BoundParameters: {0}' -f $($MyInvocation.BoundParameters | Out-String)) if ($env:PSWriteLogDisableLogging) { # If logging is not currently disabled, get out now! Microsoft.PowerShell.Utility\Write-Debug ('[Write-Log] env:PSWriteLogDisableLogging: {0}' -f $env:PSWriteLogDisableLogging) return $null } # Get the name of this function [string] $CmdletName = $PSCmdlet.MyInvocation.MyCommand.Name [scriptblock] $logDate = { return (Get-Date -Format MM-dd-yyyy).ToString() } [scriptblock] $logTime = { [string] $script:logTimeZoneBias = [System.TimeZone]::CurrentTimeZone.GetUtcOffset([datetime]::Now).TotalMinutes return "$((Get-Date -Format HH:mm:ss.fff).ToString())${script:logTimeZoneBias}" } # Create script block for generating a Legacy log entry [scriptblock] $legacyLogString = { Param( [string] $lMessage ) [System.Collections.ArrayList] $legacyMessage = @() $legacyMessage.Add(('[{0}]' -f (Get-Date -Format 'O'))) | Out-Null if ($Source) { $legacyMessage.Add("[${Source}]") | Out-Null } # $legacyMessage.Add("[${Component}]") | Out-Null $legacyMessage.Add("[${Severity}]") | Out-Null $legacyMessage.Add(($lMessage.Trim() | Out-String)) | Out-Null return ($legacyMessage -join ' ').Trim() } # Create script block for generating CMTrace.exe compatible log entry [scriptblock] $cmTraceLogString = { param( [string] $lMessage ) Microsoft.PowerShell.Utility\Write-Debug "[Write-Log] Source (sb): ${Source}" $severityMap = @{ # Vaguely based on POSH stream numbers Debug = 5 Error = 3 Host = 1 Info = 6 Information = 6 Output = 4 Progress = 1 Verbose = 4 Warning = 2 } return ('<![LOG[{0}: {1}]LOG]!><time="{2}" date="{3}" component="{4}" context="{5}" type="{6}" thread="{7}" file="{8}">' -f @( $Severity $lMessage.Trim() & $logTime & $logDate $Component [System.Security.Principal.WindowsIdentity]::GetCurrent().Name $severityMap.$Severity [Threading.Thread]::CurrentThread.ManagedThreadId $Source )) } [scriptblock] $logLine = { param( [string] $sMsg ) ## Choose which log type to write to file $line = if ($LogType -ieq 'CMTrace') { & $cmTraceLogString -lMessage ($sMsg | Out-String).Trim() -lSource $Source } else { & $legacyLogString -lMessage ($sMsg | Out-String).Trim() -lSource $Source } $line | Out-File -FilePath $FilePath.FullName -Append -NoClobber -Force -Encoding 'UTF8' -ErrorAction 'Stop' } # Create the directory where the log file will be saved if (-not $FilePath.Directory.Exists) { New-Item -Path $FilePath.DirectoryName -Type 'Directory' -Force -ErrorAction 'Stop' | Out-Null } } process { if ($IncludeInvocationHeader) { & $logLine -sMsg ("{1}`n{0}`n{1}" -f (Get-InvocationHeader),('#' * 40)) } foreach ($msg in $Message) { Microsoft.PowerShell.Utility\Write-Debug ('[Write-Log] Source: {0}' -f $Source) try { & $logLine -sMsg $msg } catch { if (-not $ContinueOnError) { throw ('[{0} {1}] [{2}] [{3}] :: Failed to write message [{4}] to the log file [{5}].{6}{7}' -f @( & $logDate & $logTime $CmdletName $Component $Msg $FilePath.FullName "`n" Resolve-Error | Out-String )) } } } } end { # Archive log file if size is greater than $MaxLogFileSizeMB and $MaxLogFileSizeMB > 0 if ($MaxLogFileSizeMB) { try { [decimal] $LogFileSizeMB = $FilePath.Length/1MB Microsoft.PowerShell.Utility\Write-Debug "[Write-Log] LogFileSizeMB: $LogFileSizeMB / $MaxLogFileSizeMB" if ($LogFileSizeMB -gt $MaxLogFileSizeMB) { Microsoft.PowerShell.Utility\Write-Debug "[Write-Log] Log File Needs to be archived ..." # Change the file extension to "lo_" [string] $archivedOutLogFile = [IO.Path]::ChangeExtension($FilePath.FullName, 'lo_') # Log message about archiving the log file if ((Get-PSCallStack)[1].Command -ne 'Write-Log') { # Prevent Write-Log from looping more than once. & $logLine -sMsg "Maximum log file size [${MaxLogFileSizeMB} MB] reached. Rename log file to: ${archivedOutLogFile}" } # Archive existing log file from <filename>.log to <filename>.lo_. Overwrites any existing <filename>.lo_ file. This is the same method SCCM uses for log files. Move-Item -Path $FilePath.FullName -Destination $archivedOutLogFile -Force -ErrorAction 'Stop' # Start new log file and Log message about archiving the old log file & $logLine -sMsg "Maximum log file size [${MaxLogFileSizeMB} MB] reached. Previous log file was renamed to: ${archivedOutLogFile}" } else { Microsoft.PowerShell.Utility\Write-Debug "[Write-Log] Log File does not need to be archived." } } catch { # If renaming of file fails, script will continue writing to log file even if size goes over the max file size Microsoft.PowerShell.Utility\Write-Debug "[Write-Log] Archive Error: ${_}" } } } } <# .SYNOPSIS Enumerate error record details. .DESCRIPTION Enumerate an error record, or a collection of error record, properties. By default, the details for the last error will be enumerated. .PARAMETER ErrorRecord The error record to resolve. The default error record is the latest one: $global:Error[0]. This parameter will also accept an array of error records. .PARAMETER Property The list of properties to display from the error record. Use "*" to display all properties. Default list of error properties is: Message, FullyQualifiedErrorId, ScriptStackTrace, PositionMessage, InnerException .PARAMETER GetErrorRecord Get error record details as represented by $_. .PARAMETER GetErrorInvocation Get error record invocation information as represented by $_.InvocationInfo. .PARAMETER GetErrorException Get error record exception details as represented by $_.Exception. .PARAMETER GetErrorInnerException Get error record inner exception details as represented by $_.Exception.InnerException. Will retrieve all inner exceptions if there is more than one. .EXAMPLE Resolve-Error .EXAMPLE Resolve-Error -Property * .EXAMPLE Resolve-Error -Property InnerException .EXAMPLE Resolve-Error -GetErrorInvocation:$false .NOTES .LINK http://psappdeploytoolkit.com #> function Resolve-Error { [CmdletBinding()] param( [Parameter( Mandatory = $false, Position=0, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true )] [AllowEmptyCollection()] [array] $ErrorRecord, [Parameter(Mandatory = $false, Position = 1)] [ValidateNotNullorEmpty()] [string[]] $Property = ('Message', 'InnerException', 'FullyQualifiedErrorId', 'ScriptStackTrace', 'PositionMessage'), [Parameter(Mandatory = $false, Position = 2)] [switch] $SkipGetErrorRecord, [Parameter(Mandatory = $false, Position = 3)] [switch] $SkipGetErrorInvocation, [Parameter(Mandatory = $false, Position = 4)] [switch] $SkipGetErrorException, [Parameter(Mandatory = $false, Position = 5)] [switch] $SkipGetErrorInnerException ) begin { ## If function was called without specifying an error record, then choose the latest error that occurred if (-not $ErrorRecord) { if ($global:Error.Count -eq 0) { # Microsoft.PowerShell.Utility\Write-Information -Message 'The $Error collection is empty' -Tags 'VertigoRay\PSWriteLog','Resolve-Error' return } Else { [array] $ErrorRecord = $global:Error[0] } } ## Allows selecting and filtering the properties on the error object if they exist [scriptblock] $selectProperty = { param( [Parameter(Mandatory = $true)] [ValidateNotNullorEmpty()] $InputObject, [Parameter(Mandatory = $true)] [ValidateNotNullorEmpty()] [string[]] $Property ) [string[]] $objectProperty = $InputObject | Get-Member -MemberType '*Property' | Select-Object -ExpandProperty 'Name' foreach ($prop in $Property) { if ($prop -eq '*') { [string[]] $propertySelection = $objectProperty Break } Elseif ($objectProperty -contains $prop) { [string[]] $propertySelection += $prop } } return $propertySelection } # Initialize variables to avoid error if 'Set-StrictMode' is set $logErrorRecordMsg = $null $logErrorInvocationMsg = $null $logErrorExceptionMsg = $null $logErrorMessageTmp = $null $logInnerMessage = $null } process { if (-not $ErrorRecord) { return } foreach ($errRecord in $ErrorRecord) { ## Capture Error Record if (-not $SkipErrorRecord) { [string[]] $selectedProperties = & $selectProperty -InputObject $errRecord -Property $Property $logErrorRecordMsg = $errRecord | Select-Object -Property $selectedProperties } ## Error Invocation Information if (-not $SkipGetErrorInvocation) { if ($errRecord.InvocationInfo) { [string[]] $selectedProperties = & $selectProperty -InputObject $errRecord.InvocationInfo -Property $Property $logErrorInvocationMsg = $errRecord.InvocationInfo | Select-Object -Property $selectedProperties } } ## Capture Error Exception if (-not $SkipGetErrorException) { if ($errRecord.Exception) { [string[]] $selectedProperties = & $selectProperty -InputObject $errRecord.Exception -Property $Property $logErrorExceptionMsg = $errRecord.Exception | Select-Object -Property $selectedProperties } } ## Display properties in the correct order if ($Property -eq '*') { # If all properties were chosen for display, then arrange them in the order the error object displays them by default. if ($logErrorRecordMsg) { [array] $logErrorMessageTmp += $logErrorRecordMsg } if ($logErrorInvocationMsg) { [array] $logErrorMessageTmp += $logErrorInvocationMsg } if ($logErrorExceptionMsg) { [array] $logErrorMessageTmp += $logErrorExceptionMsg } } Else { # Display selected properties in our custom order if ($logErrorExceptionMsg) { [array] $logErrorMessageTmp += $logErrorExceptionMsg } if ($logErrorRecordMsg) { [array] $logErrorMessageTmp += $logErrorRecordMsg } if ($logErrorInvocationMsg) { [array] $logErrorMessageTmp += $logErrorInvocationMsg } } if ($logErrorMessageTmp) { $logErrorMessage = 'Error Record:' $logErrorMessage += "`n-------------" $logErrorMsg = $logErrorMessageTmp | Format-List | Out-String $logErrorMessage += $logErrorMsg } ## Capture Error Inner Exception(s) if (-not $SkipGetErrorInnerException) { if ($errRecord.Exception -and $errRecord.Exception.InnerException) { $logInnerMessage = 'Error Inner Exception(s):' $logInnerMessage += "`n-------------------------" $errorInnerException = $errRecord.Exception.InnerException $count = 0 while ($errorInnerException) { [string] $InnerExceptionSeperator = '~' * 40 [string[]] $selectedProperties = & $selectProperty -InputObject $errorInnerException -Property $Property $logerrorInnerExceptionMsg = $errorInnerException | Select-Object -Property $selectedProperties | Format-List | Out-String if ($count -gt 0) { $logInnerMessage += $InnerExceptionSeperator } $logInnerMessage += $logerrorInnerExceptionMsg $count++ $errorInnerException = $errorInnerException.InnerException } } } if ($logErrorMessage) { $output = $logErrorMessage } if ($logInnerMessage) { $output += $logInnerMessage } Write-Output -InputObject $output if (Test-Path -LiteralPath 'variable:Output') { Clear-Variable -Name 'Output' } if (Test-Path -LiteralPath 'variable:LogErrorMessage') { Clear-Variable -Name 'LogErrorMessage' } if (Test-Path -LiteralPath 'variable:LogInnerMessage') { Clear-Variable -Name 'LogInnerMessage' } if (Test-Path -LiteralPath 'variable:LogErrorMessageTmp') { Clear-Variable -Name 'LogErrorMessageTmp' } } } end { } } function global:Write-Debug { [CmdletBinding(HelpUri='http://go.microsoft.com/fwlink/?LinkID=113424', RemotingCapability='None')] param( [Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$true)] [Alias('Msg')] [AllowEmptyString()] [string] ${Message} ) begin { if ($MyInvocation.PSCommandPath) { $invoFile = Split-Path $MyInvocation.PSCommandPath -Leaf } else { $invoFile = Split-Path $MyInvocation.InvocationName -Leaf } $writeLog = @{ 'Severity' = 'Debug'; 'Component' = (& { $PSCallStack = (Get-PSCallStack)[2]; "$($PSCallStack.Command) $($PSCallStack.Arguments)" }); 'Source' = "${invoFile}:$($MyInvocation.ScriptLineNumber)"; } if (-not ($env:PSWriteLogDebugSilent -as [bool])) { try { $outBuffer = $null if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer)) { $PSBoundParameters['OutBuffer'] = 1 } $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Microsoft.PowerShell.Utility\Write-Debug', [System.Management.Automation.CommandTypes]::Cmdlet) $scriptCmd = { & $wrappedCmd @PSBoundParameters } $steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin) $steppablePipeline.Begin($PSCmdlet) } catch { throw } } } process { if (-not ($env:PSWriteLogDebugNoLog -as [bool])) { if ((Get-Command 'Write-Log' -ErrorAction 'Ignore') -and ($DebugPreference -ine 'SilentlyContinue')) { Write-Log @writeLog -Message $Message } } if (-not ($env:PSWriteLogDebugSilent -as [bool])) { try { $steppablePipeline.Process($_) } catch { throw } } } end { if (-not ($env:PSWriteLogDebugSilent -as [bool])) { try { $steppablePipeline.End() } catch { throw } } } <# .ForwardHelpTargetName Microsoft.PowerShell.Utility\Write-Debug .ForwardHelpCategory Cmdlet #> } function global:Write-Error { [CmdletBinding(DefaultParameterSetName='NoException', HelpUri='http://go.microsoft.com/fwlink/?LinkID=113425', RemotingCapability='None')] param( [Parameter(ParameterSetName='WithException', Mandatory=$true)] [System.Exception] ${Exception}, [Parameter(ParameterSetName='NoException', Mandatory=$true, Position=0, ValueFromPipeline=$true)] [Parameter(ParameterSetName='WithException')] [Alias('Msg')] [AllowEmptyString()] [AllowNull()] [string] ${Message}, [Parameter(ParameterSetName='ErrorRecord', Mandatory=$true)] [System.Management.Automation.ErrorRecord] ${ErrorRecord}, [Parameter(ParameterSetName='NoException')] [Parameter(ParameterSetName='WithException')] [System.Management.Automation.ErrorCategory] ${Category}, [Parameter(ParameterSetName='NoException')] [Parameter(ParameterSetName='WithException')] [string] ${ErrorId}, [Parameter(ParameterSetName='WithException')] [Parameter(ParameterSetName='NoException')] [System.Object] ${TargetObject}, [string] ${RecommendedAction}, [Alias('Activity')] [string] ${CategoryActivity}, [Alias('Reason')] [string] ${CategoryReason}, [Alias('TargetName')] [string] ${CategoryTargetName}, [Alias('TargetType')] [string] ${CategoryTargetType} ) begin { if ($MyInvocation.PSCommandPath) { $invoFile = Split-Path $MyInvocation.PSCommandPath -Leaf } else { $invoFile = Split-Path $MyInvocation.InvocationName -Leaf } $writeLog = @{ 'Severity' = 'Error'; 'Component' = (& { $PSCallStack = (Get-PSCallStack)[2]; "$($PSCallStack.Command) $($PSCallStack.Arguments)" }); 'Source' = "${invoFile}:$($MyInvocation.ScriptLineNumber)"; } if (-not ($env:PSWriteLogErrorSilent -as [bool])) { try { $outBuffer = $null if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer)) { $PSBoundParameters['OutBuffer'] = 1 } $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Microsoft.PowerShell.Utility\Write-Error', [System.Management.Automation.CommandTypes]::Cmdlet) $scriptCmd = {& $wrappedCmd @PSBoundParameters } $steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin) $steppablePipeline.Begin($PSCmdlet) } catch { throw } } } process { if (Get-Command 'Write-Log' -ErrorAction 'Ignore') { [System.Collections.ArrayList] $msg = @() if ($PSBoundParameters.ContainsKey('ErrorRecord')) { $msg.Add("[$($ErrorRecord.Exception.GetType().FullName)]") | Out-Null $msg.Add($ErrorRecord.Exception.Message) | Out-Null } elseif ($PSBoundParameters.ContainsKey('Exception')) { $msg.Add("[${Exception}]") | Out-Null $msg.Add($Message) | Out-Null } else { $msg.Add($Message) | Out-Null } if ( $Category.isPresent -or $ErrorId.isPresent -or $TargetObject.isPresent -or $RecommendedAction.isPresent -or $CategoryActivity.isPresent -or $CategoryReason.isPresent -or $CategoryTargetName.isPresent -or $CategoryTargetType.isPresent ) { $msg.Add("`nError Details As Json: $($PSBoundParameters | ConvertTo-Json)") | Out-Null } Write-Log @writeLog -Message ($msg -join ' ') -ErrorAction 'Stop' } if (-not ($env:PSWriteLogErrorSilent -as [bool])) { try { $steppablePipeline.Process($_) } catch { throw } } } end { if (-not ($env:PSWriteLogErrorSilent -as [bool])) { try { $steppablePipeline.End() } catch { throw } } } <# .ForwardHelpTargetName Microsoft.PowerShell.Utility\Write-Error .ForwardHelpCategory Cmdlet #> } function global:Write-Host { [CmdletBinding(HelpUri='http://go.microsoft.com/fwlink/?LinkID=113426', RemotingCapability='None')] param( [Parameter(Position=0, ValueFromPipeline=$true, ValueFromRemainingArguments=$true)] [System.Object] ${Object}, [switch] ${NoNewline}, [System.Object] ${Separator}, [System.ConsoleColor] ${ForegroundColor}, [System.ConsoleColor] ${BackgroundColor} ) begin { if ($MyInvocation.PSCommandPath) { $invoFile = Split-Path $MyInvocation.PSCommandPath -Leaf } else { $invoFile = Split-Path $MyInvocation.InvocationName -Leaf } $writeLog = @{ 'Component' = (& { $PSCallStack = (Get-PSCallStack)[2]; "$($PSCallStack.Command) $($PSCallStack.Arguments)" }); 'Source' = "${invoFile}:$($MyInvocation.ScriptLineNumber)"; } if (-not ($env:PSWriteLogHostSilent -as [bool])) { try { $outBuffer = $null if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer)) { $PSBoundParameters['OutBuffer'] = 1 } $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Microsoft.PowerShell.Utility\Write-Host', [System.Management.Automation.CommandTypes]::Cmdlet) $scriptCmd = {& $wrappedCmd @PSBoundParameters } $steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin) $steppablePipeline.Begin($PSCmdlet) } catch { throw } } } process { if (Get-Command 'Write-Log' -ErrorAction 'Ignore') { Write-Log @writeLog -Message $Object } if (-not ($env:PSWriteLogHostSilent -as [bool])) { try { $steppablePipeline.Process($_) } catch { throw } } } end { if (-not ($env:PSWriteLogHostSilent -as [bool])) { try { $steppablePipeline.End() } catch { throw } } } <# .ForwardHelpTargetName Microsoft.PowerShell.Utility\Write-Host .ForwardHelpCategory Cmdlet #> } function global:Write-Information { [CmdletBinding(HelpUri='http://go.microsoft.com/fwlink/?LinkId=525909', RemotingCapability='None')] param( [Parameter(Mandatory=$true, Position=0)] [Alias('Msg')] [System.Object] ${MessageData}, [Parameter(Position=1)] [string[]] ${Tags} ) begin { if ($MyInvocation.PSCommandPath) { $invoFile = Split-Path $MyInvocation.PSCommandPath -Leaf } else { $invoFile = Split-Path $MyInvocation.InvocationName -Leaf } $writeLog = @{ 'Component' = (& { $PSCallStack = (Get-PSCallStack)[2]; "$($PSCallStack.Command) $($PSCallStack.Arguments)" }); 'Source' = "${invoFile}:$($MyInvocation.ScriptLineNumber)"; } if (-not ($env:PSWriteLogInformationSilent -as [bool])) { try { $outBuffer = $null if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer)) { $PSBoundParameters['OutBuffer'] = 1 } $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Microsoft.PowerShell.Utility\Write-Information', [System.Management.Automation.CommandTypes]::Cmdlet) $scriptCmd = {& $wrappedCmd @PSBoundParameters } $steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin) $steppablePipeline.Begin($PSCmdlet) } catch { throw } } } process { if ((Get-Command 'Write-Log' -ErrorAction 'Ignore') -and ($InformationPreference -ine 'SilentlyContinue')) { if ($Tags.isPresent) { Write-Log @writeLog -Message "{$($Tags -join ',')} $MessageData" } else { Write-Log @writeLog -Message "$MessageData" } } if (-not ($env:PSWriteLogInformationSilent -as [bool])) { try { $steppablePipeline.Process($_) } catch { throw } } } end { if (-not ($env:PSWriteLogInformationSilent -as [bool])) { try { $steppablePipeline.End() } catch { throw } } } <# .ForwardHelpTargetName Microsoft.PowerShell.Utility\Write-Information .ForwardHelpCategory Cmdlet #> } function global:Write-Output { [CmdletBinding(HelpUri='http://go.microsoft.com/fwlink/?LinkID=113427', RemotingCapability='None')] param( [Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$true, ValueFromRemainingArguments=$true)] [AllowNull()] [AllowEmptyCollection()] [psobject[]] ${InputObject}, [switch] ${NoEnumerate} ) begin { if ($MyInvocation.PSCommandPath) { $invo_file = Split-Path $MyInvocation.PSCommandPath -Leaf } else { $invo_file = Split-Path $MyInvocation.InvocationName -Leaf } $writeLog = @{ 'Severity' = 'Output' 'Component' = (& { $PSCallStack = (Get-PSCallStack)[2]; "$($PSCallStack.Command) $($PSCallStack.Arguments)" }); 'Source' = "${invo_file}:$($MyInvocation.ScriptLineNumber)"; } try { $outBuffer = $null if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer)) { $PSBoundParameters['OutBuffer'] = 1 } $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Microsoft.PowerShell.Utility\Write-Output', [System.Management.Automation.CommandTypes]::Cmdlet) $scriptCmd = {& $wrappedCmd @PSBoundParameters } $steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin) $steppablePipeline.Begin($PSCmdlet) } catch { throw } } process { if ((Get-Command 'Write-Log' -ErrorAction 'Ignore') -and ($env:PSWriteLogOutputLog -as [bool])) { Write-Log @writeLog -Message ($InputObject | Out-String) } try { $steppablePipeline.Process($_) } catch { throw } } end { try { $steppablePipeline.End() } catch { throw } } <# .ForwardHelpTargetName Microsoft.PowerShell.Utility\Write-Output .ForwardHelpCategory Cmdlet #> } function global:Write-Progress { [CmdletBinding(HelpUri='http://go.microsoft.com/fwlink/?LinkID=113428', RemotingCapability='None')] param( [Parameter(Mandatory=$true, Position=0)] [string] ${Activity}, [Parameter(Position=1)] [ValidateNotNullOrEmpty()] [string] ${Status}, [Parameter(Position=2)] [ValidateRange(0, 2147483647)] [int] ${Id}, [ValidateRange(-1, 100)] [int] ${PercentComplete}, [int] ${SecondsRemaining}, [string] ${CurrentOperation}, [ValidateRange(-1, 2147483647)] [int] ${ParentId}, [switch] ${Completed}, [int] ${SourceId} ) begin { if ($MyInvocation.PSCommandPath) { $invoFile = Split-Path $MyInvocation.PSCommandPath -Leaf } else { $invoFile = Split-Path $MyInvocation.InvocationName -Leaf } $writeLog = @{ 'Severity' = 'Progress' 'Component' = (& { $PSCallStack = (Get-PSCallStack)[2]; "$($PSCallStack.Command) $($PSCallStack.Arguments)" }); 'Source' = "${invoFile}:$($MyInvocation.ScriptLineNumber)"; } if (-not ($env:PSWriteLogProgessSilent -as [bool])) { try { $outBuffer = $null if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer)) { $PSBoundParameters['OutBuffer'] = 1 } $PSBoundParameters.Remove('WriteHostColor') $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Microsoft.PowerShell.Utility\Write-Progress', [System.Management.Automation.CommandTypes]::Cmdlet) $scriptCmd = {& $wrappedCmd @PSBoundParameters } $steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin) $steppablePipeline.Begin($PSCmdlet) } catch { throw } } } process { if (Get-Command 'Write-Log' -ErrorAction 'Ignore' -and ($ProgressPreference -ine 'SilentlyContinue')) { [System.Collections.ArrayList] $Message = @() if ($PSBoundParameters.ContainsKey('ParentId')) { $Message.Add("[${ParentId}]") | Out-Null } if ($PSBoundParameters.ContainsKey('Id')) { $Message.Add("[${Id}]") | Out-Null } $Message.Add($Activity) | Out-Null if ($PSBoundParameters.ContainsKey('PercentComplete')) { $Message.Add("${PercentComplete}%") | Out-Null } if ($PSBoundParameters.ContainsKey('SecondsRemaining')) { $Message.Add("(${SecondsRemaining} Seconds Remaining)") | Out-Null } $Message.Add(':') | Out-Null $Message.Add($Status) | Out-Null $Message.Add(':') | Out-Null $Message.Add($CurrentOperation) | Out-Null Write-Log @writeLog -Message ($Message -join ' ') } if (-not ($env:PSWriteLogProgessSilent -as [bool])) { try { $steppablePipeline.Process($_) } catch { throw } } } end { if (-not ($env:PSWriteLogProgessSilent -as [bool])) { try { $steppablePipeline.End() } catch { throw } } } <# .ForwardHelpTargetName Microsoft.PowerShell.Utility\Write-Progress .ForwardHelpCategory Cmdlet #> } function global:Write-Verbose { [CmdletBinding(HelpUri='http://go.microsoft.com/fwlink/?LinkID=113429', RemotingCapability='None')] param( [Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$true)] [Alias('Msg')] [AllowEmptyString()] [string] ${Message} ) begin { if ($MyInvocation.PSCommandPath) { $invoFile = Split-Path $MyInvocation.PSCommandPath -Leaf } else { $invoFile = Split-Path $MyInvocation.InvocationName -Leaf } $writeLog = @{ 'Severity' = 'Verbose'; 'Component' = (& { $PSCallStack = (Get-PSCallStack)[2]; "$($PSCallStack.Command) $($PSCallStack.Arguments)" }); 'Source' = "${invoFile}:$($MyInvocation.ScriptLineNumber)"; } if (-not ($env:PSWriteLogVerboseSilent -as [bool])) { try { $outBuffer = $null if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer)) { $PSBoundParameters['OutBuffer'] = 1 } $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Microsoft.PowerShell.Utility\Write-Verbose', [System.Management.Automation.CommandTypes]::Cmdlet) $scriptCmd = {& $wrappedCmd @PSBoundParameters } $steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin) $steppablePipeline.Begin($PSCmdlet) } catch { throw } } } process { if ((Get-Command 'Write-Log' -ErrorAction 'Ignore') -and ($VerbosePreference -ine 'SilentlyContinue')) { Write-Log @writeLog -Message $Message } if (-not ($env:PSWriteLogVerboseSilent -as [bool])) { try { $steppablePipeline.Process($_) } catch { throw } } } end { if (-not ($env:PSWriteLogVerboseSilent -as [bool])) { try { $steppablePipeline.End() } catch { throw } } } <# .ForwardHelpTargetName Microsoft.PowerShell.Utility\Write-Verbose .ForwardHelpCategory Cmdlet #> } function global:Write-Warning { [CmdletBinding(HelpUri='http://go.microsoft.com/fwlink/?LinkID=113430', RemotingCapability='None')] param( [Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$true)] [Alias('Msg')] [AllowEmptyString()] [string] ${Message} ) begin { if ($MyInvocation.PSCommandPath) { $invoFile = Split-Path $MyInvocation.PSCommandPath -Leaf } else { $invoFile = Split-Path $MyInvocation.InvocationName -Leaf } $writeLog = @{ 'Severity' = 'Warning'; 'Component' = (& { $PSCallStack = (Get-PSCallStack)[2]; "$($PSCallStack.Command) $($PSCallStack.Arguments)" }); 'Source' = "${invoFile}:$($MyInvocation.ScriptLineNumber)"; } if (-not ($env:PSWriteLogWarningSilent -as [bool])) { try { $outBuffer = $null if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer)) { $PSBoundParameters['OutBuffer'] = 1 } $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Microsoft.PowerShell.Utility\Write-Warning', [System.Management.Automation.CommandTypes]::Cmdlet) $scriptCmd = {& $wrappedCmd @PSBoundParameters } $steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin) $steppablePipeline.Begin($PSCmdlet) } catch { throw } } } process { if ((Get-Command 'Write-Log' -ErrorAction 'Ignore') -and ($WarningPreference -ine 'SilentlyContinue')) { Write-Log @writeLog -Message $Message } if (-not ($env:PSWriteLogWarningSilent -as [bool])) { try { $steppablePipeline.Process($_) } catch { throw } } } end { if (-not ($env:PSWriteLogWarningSilent -as [bool])) { try { $steppablePipeline.End() } catch { throw } } } <# .ForwardHelpTargetName Microsoft.PowerShell.Utility\Write-Warning .ForwardHelpCategory Cmdlet #> } $psd1 = Import-PowerShellDataFile ([IO.Path]::Combine($PSScriptRoot, 'PSWriteLog.psd1')) # Check if the current context is elevated (Are we running as an administrator?) if ((New-Object System.Security.Principal.WindowsPrincipal([System.Security.Principal.WindowsIdentity]::GetCurrent())).IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator)) { # Anytime this Module is used, the version and timestamp will be stored in the registry. # This will allow more intelligent purging of unused versions. $versionUsed = @{ LiteralPath = 'Registry::HKEY_LOCAL_MACHINE\SOFTWARE\VertigoRay\PSWriteLog\VersionsUsed' Name = $psd1.ModuleVersion Value = (Get-Date -Format 'O') Force = $true } Write-Debug ('Version Used: {0}' -f ($versionUsed | ConvertTo-Json)) if (-not (Test-Path $versionUsed.LiteralPath)) { New-Item -Path $versionUsed.LiteralPath -Force } Set-ItemProperty @versionUsed } $moduleMember = @{ Cmdlet = $psd1.CmdletsToExport Function = $psd1.FunctionsToExport Alias = $psd1.AliasesToExport } if ($psd1.VariablesToExport) { $moduleMember.Set_Item('Variable', $psd1.VariablesToExport) } Export-ModuleMember @moduleMember |