-
Notifications
You must be signed in to change notification settings - Fork 8.1k
Description
I'm working on #6702 right now, normalizing the stream-related common parameters so that you can capture a stream in a variable or provide an ActionPreference for any stream, when I came across what appears to be a design bug.
It's easier to understand when looking at an example, so run this script first to get started:
# Capture a warning using redirection
$o1 = Write-Warning 'Message' 3>&1
# Capture a warning using -WarningVariable
$o2 = $null
Write-Warning 'Message' -WarningVariable o2
# Capture a warning using a -WarningVariable while silencing output via -WarningAction
$o3 = $null
Write-Warning 'Message' -WarningVariable o3 -WarningAction Ignore
# Capture a warning using a -WarningVariable while silencing output via redirection
$o4 = $null
Write-Warning 'Message' -WarningVariable o4 *>$null
# Look at the default output, and notice the rendering differences
$o1,$o2,$o3,$o4
# Look at the list output, and notice how the rendering differences persist
$o1,$o2,$o3,$o4 | Format-List * -Force
# Look at the string output, and notice that you can't tell at all that the messages
# came from warnings
$o1,$o2,$o3,$o4 | Out-StringWhile each of these commands are designed to capture warning records, there are several problems that this output illustrates, as follows:
- All
WarningRecordinstances captured while redirection is used are decorated with additional information that is not captured when redirection is not used. WarningRecordinstances are indistinguishable from other output when converted to string, such as when the results of a command are captured and written to a log file.- PowerShell hosts not only apply specific colors to records from various streams, they also render additional text before the object data, even when that object data is displayed in its non-default format such as a list or a table.
The exact same behavior holds true for verbose and debug message streams, as can be seen through the PR that I am working on.
The first issue is not a huge issue, but it would probably be better if records captured via -*Variable common parameters were also properly decorated for proper output in a console.
The second and third issue are the more serious problem, because they limit the usefulness of capturing various types of stream data. Scripters who run a command and want to capture and review results from a log will find that much easier if output from different streams is properly identified in a black and white format with a prefix. This isn't just nice to have information: it's critical for scanning and searching purposes.
Digging into the technical details, part of the problems stem from these methods in the default host implementation:
TypeName: System.Management.Automation.Internal.Host.InternalHostUserInterface
Name MemberType Definition
---- ---------- ----------
Write Method void Write(string value), void Write(System.ConsoleColor foregroundColor, System.ConsoleColor backgroundColor, string value)
WriteDebugLine Method void WriteDebugLine(string message)
WriteErrorLine Method void WriteErrorLine(string value)
WriteInformation Method void WriteInformation(System.Management.Automation.InformationRecord record)
WriteLine Method void WriteLine(), void WriteLine(string value), void WriteLine(System.ConsoleColor foregroundColor, System.ConsoleColor backgroundColor, string value)
WriteProgress Method void WriteProgress(long sourceId, System.Management.Automation.ProgressRecord record)
WriteVerboseLine Method void WriteVerboseLine(string message)
WriteWarningLine Method void WriteWarningLine(string message)
Notice how progress and information streams have methods that accept actual records, but error, warning, verbose and debug streams only have methods that accept strings. Also note that some of these methods decorate output with stream-identifying text (WriteDebugLine, WriteVerboseLine, WriteWarningLine), while others do not (WriteInformation, WriteErrorLine).
What I think should happen to address this issue and provide a much better foundation for script logging going forward:
- New methods should be added to an abstract
PSHostUserInterface2class that is derived fromPSHostUserInterfaceto avoid breaking changes. There should be oneWrite*Linemethod and oneWrite*method for each stream. - Once the abstract methods are added, internal classes that derive from
PSHostUserInterfaceshould derive from the newPSHostUserInterface2class and implement the new methods. Other hosts can catch up when they're ready to do so. - The F&O layer should decorate the default output of Warning, Verbose, Debug, and Information records with an all caps prefix so that they can be properly identified and searched for when all you have is text data to work with (e.g. log files).
- Any internal classes that output stream data to the host should apply a prefix in the
Write*Linemethods, but not apply in theWrite*methods (for those we rely on the default output from the F&O layer for those records). That allows for prefixing in text written directly to the host while leveraging prefixing that is inherent in the default format applied to stream record instances.
Thoughts/comments are welcome and appreciated.
Environment data
Name Value
---- -----
PSVersion 6.2.1
PSEdition Core
GitCommitId 6.2.1
OS Microsoft Windows 10.0.17763
Platform Win32NT
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1
WSManStackVersion 3.0