\$\begingroup\$
\$\endgroup\$
2
Today I have dealt with logging in PowerShell as well as with the different streams and the pipeline. Unfortunately, none of these solutions really met my needs.
My requirements are:
- I need to output information from PowerShell to a file log.
- The file log has a predefined structure. So it's not enough to just redirect all streams to the file.
- I want to use mainly standard PowerShell functions such as Write-Error, Write-Warning, Write-Verbose.
- The overhead in the code through logging should be minimal.
I have developed the following idea now:
- When calling a function from my script, all streams are piped to a logging function.
- This function separates the debug, verbose, warning and error objects from the resulting object.
- The resulting object is released back into the pipeline.
Here is my solution:
function Split-Streams {
[CmdletBinding()]
param(
[Parameter(ValueFromPipeline)]
$InputStream
)
process{
switch($InputStream.GetType())
{
'System.Management.Automation.DebugRecord' {
# Do whatever you want, like formatting an writing to a file.
Write-Host $InputStream -ForegroundColor Gray
}
'System.Management.Automation.ErrorRecord' {
Write-Host $InputStream -ForegroundColor Red
Write-Host ('Error function: {0}' -f $InputStream[0].InvocationInfo.MyCommand.Name) -ForegroundColor Red
}
'System.Management.Automation.VerboseRecord' { Write-Host $InputStream -ForegroundColor Cyan }
'System.Management.Automation.WarningRecord' { Write-Host $InputStream -ForegroundColor Yellow }
default { return $InputStream }
}
}
}
function Write-Messages
{
[CmdletBinding()]
param()
Write-Debug "Debug message"
Write-Output "Output message"
Write-Verbose "Verbose message"
Write-Warning "Warning message"
Write-Error "Error message"
}
$Test2 = Write-Messages -Verbose -Debug *>&1 | Split-Streams
Write-Host $Test2 -ForegroundColor White
So now my question:
- Is there something wrong with my solution?
- Have I missed any problems?
AlexV
7,3532 gold badges24 silver badges47 bronze badges
1 Answer 1
\$\begingroup\$
\$\endgroup\$
- Is there something wrong with my solution? I don't think so.
- Have I missed any problems?
- Omitted Information stream, cf How to process Write-Information pipeline output when using SilentlyContinue
- The
-Debug
parameter overrides the value of the$DebugPreference
variable for the current command, setting the value of$DebugPreference
toInquire
. Prompts to continue for everyWrite-Debug
statement which may be considered inconvenient (or even harmful) in batch processing.
Here's my solution (partially commented script):
[CmdletBinding()]
param(
# [switch]$debug # alternative to [CmdletBinding()]
)
function Split-Streams {
[CmdletBinding()]
param(
[Parameter(Mandatory,ValueFromPipeline)]
[ValidateNotNull()] # avoid crashing . property dereference operator
$InputStream,
[switch]$revert
)
process {
if ( -not $revert.IsPresent ) {
# return an object which contains type and value of $InputStream
[PSCustomObject] @{
stream = $InputStream.GetType().FullName
value = $InputStream
}
}
else {
# basic input object validity check
if ( ($InputStream.psobject.Properties.Name -join ',') -match
"\bstream\b.*\bvalue\b|\bvalue\b.*\bstream\b" ) {
# review split streams and handle them individually
switch($InputStream.stream)
{
'System.Management.Automation.DebugRecord' {
# Do whatever you want, like formatting an writing to a file.
Write-Host ($InputStream.value) -ForegroundColor Gray
}
'System.Management.Automation.ErrorRecord' {
Write-Host ($InputStream.value) -ForegroundColor Red
Write-Host ('Error function: {0}' -f ($InputStream.value).InvocationInfo.MyCommand.Name) -ForegroundColor DarkRed
}
'System.Management.Automation.VerboseRecord' {
Write-Host ($InputStream.value) -ForegroundColor Cyan
}
'System.Management.Automation.WarningRecord' {
Write-Host ($InputStream.value) -ForegroundColor Yellow
}
'System.Management.Automation.InformationRecord' {
Write-Host ($InputStream.value) -ForegroundColor Green
}
default {
Write-Host "output type: $($InputStream.stream)" -ForegroundColor Blue
# keep original output stream unchanged
$InputStream.value
}
}
}
}
}
}
function Write-Messages
{
[CmdletBinding()]
param()
Write-Debug "Debug message $DebugPreference"
Write-Verbose "Verbose message $VerbosePreference"
Write-Warning "Warning message $WarningPreference"
Write-Error "Error message $ErrorActionPreference"
Write-Information "Information message $InformationPreference"
# Write-Output: it is generally not necessary to use the cmdlet.
'Output message'
# I'm checking a more complex object than plain string
Write-Output $Host
}
$DebugPreferenceSave = $DebugPreference # backup $DebugPreference
If ($PSBoundParameters['Debug']) {
# The Debug parameter overrides the value of the $DebugPreference
# variable for the current command, setting the value
# of $DebugPreference to Inquire.
# The following setting suppresses asking whether you want to continue
# even if examined `Write-Messages` is a third-party black box.
$DebugPreference = 'Continue'
}
Write-Messages -Verbose *>&1 | Split-Streams | Split-Streams -revert
$DebugPreference = $DebugPreferenceSave # restore $DebugPreference
Alternatively to the (sample) usage in the one pipeline:
Write-Messages -Verbose *>&1 | Split-Streams | Split-Streams -revert
(cf above code) you can call it as follows:
$test3 = Write-Messages -Verbose *>&1 | Split-Streams
# preprocess the $test3 variable here (optional)
$test3 | Split-Streams -revert
answered Aug 9, 2019 at 11:43
default
Write-Host
writes directly to the host screen buffer \$\endgroup\$