Home Dashboard Directory Help

Capture Warning, Verbose, Debug and Host Output via alternate streams by Keith Hill MVP


 as Fixed Help for as Fixed

Sign in
to vote
Type: Suggestion
ID: 297055
Opened: 9/6/2007 5:50:14 PM
Access Restriction: Public


It seems like it would be nice these "other" information/output channels
were assigned to streams so they could participate in stream redirection
like stderr (2>&1) e.g.:

0 - stdin
1 - stdout (write-output, normal cmdlet output, non captured expression
2 - stderr (write-error, throw, cmdlet non-terminating errors)
3 - warnings (write-warning, cmdlet warning)
4 - verbose (write-verbose and cmdlet -verbose output)
5 - debug (write-debug and cmdlet debug output)
6 - host (write-host, read-host, explicit out-host use)
9 - combined (all output combined into a single - easy to redirect stream)

So for logging purposes, if I want to capture *ALL* output to a log file I
can simply do this:

C:\MyLongRunningComplexUnattendedScript.ps1 9>&1 >

or if I wanted to capture verbose output in a script:

stop-process -n vd* -verbose 4>&1 > C:\Logs\StoppedProcesses.log
Sign in to post a comment.
Posted by timdunn on 3/22/2016 at 2:00 AM
It seems that it's capturing the output of Write-Debug, which isn't the same as what Set-PsDebug writes to, even though both preface their output with 'DEBUG:' I'm doing a *>&1 and not misbehaving with Write-Host, so I expect everything to be redirected to STDOUT, but the Set-PsDebug output still goes to console.

$DebugPreference = 'continue'
function Test-It { Write-Debug "This is captured"; Set-PsDebug -Trace 2; "This is also captured, but the DEBUG: lines are not"; Set-PsDebug -off; Write-Debug -Debug "This is also captured" }
$stdout = Test-It *>&1

Posted by John.Bevan on 2/3/2016 at 9:56 AM

It doesn't break; the difference is you're throwing an error rather than writing to the error stream.
Your throw command causes you to jump out of the current execution sequence; i.e. terminates the script (unless you have error handling).
Replace THROW in your example with WRITE-ERROR and it will work as you'd expect.
Posted by John.Bevan on 2/3/2016 at 9:50 AM
Is there any thought of allowing redirection of host output (#6 above)?
e.g. to allow us to suppress host output in poorly writtten modules, or capture it in the standard output stream : `Run-SomeCommand 6>&1 | Out-Null`)
Posted by Weston.M on 4/6/2015 at 5:08 PM
why does this completely break down with 'throw'?
$var = $(Write-Output "Pipeline Output"; Write-Warning "Warning Output"; throw "Error Output") 3>&1

$var -eq $null
Posted by Jordan Mills on 1/29/2014 at 9:34 AM
Yeah this is nowhere near fixed. It only works per cmdlet and has to be specified for each cmdlet.
Posted by Chris Charabaruk on 10/16/2013 at 12:19 PM
This is fine for redirecting the alternate streams to output, but there is still no support to redirect one alternate stream to another (for example, error stream from a console app to the verbose stream for Powershell) without polluting output. In my eyes, that means this still isn't really fixed, so I've opened https://connect.microsoft.com/PowerShell/feedback/details/805604/cant-redirect-merge-output-streams-to-each-other in hopes that this can be properly done.
Posted by sba on 1/24/2013 at 6:19 AM
What I don't understand is how you achieve the equivalent of ksh/bash's "exec 2>foo" so that all error output from that point on in the script goes to the specified file...

Am I missing something?
Posted by Nathan Hartley on 11/2/2012 at 2:10 PM
I was really hoping that, despite being awkward, that the new redirection syntax would help, however it doesn't seem to work as expected...
> dir *; 1/0 *> test.txt

> cat test.txt
... snip ...

More often than not, when I need logging, I would like to capture EVERYTHING seen on the screen. Often these types of scripts are called using...

>Powershell.exe -File ScriptINeedingFullLogging.ps1

Would it be possible to add a -LogFile parameter to Powershell.exe?

I have attempted the "cmd.exe /c Powershell.exe > logfile.txt" trick, but because of the different character encodings, different line endings and such returned; it makes a awefull mess.

I also like HalR's idea and Enable-Logging idea below. Anything would help.
Posted by Peter Kriegel on 6/11/2012 at 3:49 AM
How can i capture the progress stream ?
Posted by Kirk Munro on 2/16/2012 at 9:20 AM
Love this support, hate the syntax. Using redirect operators is not intuitive for those not familiar with them. I would love to see this extended to include a preference variable giving you the same thing (much like the ability to define default parameter values for parameters in v3), so that you could define the behaviour you want for any work (script or ad-hoc) and get the results captured the way you want. Looking at the script samples below I immediately start thinking about how I can solve this using a proxy function or advanced function so that I don't need to deal with redirect operator syntax.
Posted by Microsoft on 2/15/2012 at 4:40 PM
# In PS 3.0, we've extended output redirection to include the following streams:
# Pipeline (1)
# Error    (2)
# Warning (3)
# Verbose (4)
# Debug    (5)
# All     (*)

# We still use the same operators
# >    Redirect to a file and replace contents
# >>    Redirect to a file and append to existing content
# >&1    Merge with pipeline output

# First, let's set some preference variables and get a temp file
$VerbosePreference = "Continue"; $DebugPreference = "Continue"; $filename = [System.IO.Path]::GetTempFileName()

# Scenario 1
# Merge warning output with pipeline output (3>&1)
# Pipeline output is assigned to a variable
# The error output goes to the host (no redirection)
$var = $(Write-Output "Pipeline Output"; Write-Warning "Warning Output"; Write-Error "Error Output") 3>&1
# The variable $var contains the pipeline and warning output

# Scenario 2
# Redirect warning output to a file (3>$filename)
# All other output is merged with pipeline output (2>&1 4>&1 5>&1)
$var = $(Write-Output "Pipeline Output"; Write-Warning "Warning Output"; Write-Verbose "Verbose Output"; Write-Error "Error Output") 2>&1 4>&1 5>&1 3>$filename
# Pipeline, Verbose, and Error output are now in $var
# Warning output is in the file
Get-Content $filename

# Scenario 3
# Redirect all output to a file (*>$filename)
$(Write-Output "Pipeline Output"; Write-Warning "Warning Output"; Write-Verbose "Verbose Output"; Write-Error "Error Output"; Write-Debug "Debug Output") *>$filename
# All output is now in the file
Get-Content $filename

# Scenario 4
# Merge warning, error, and verbose output with the pipeline output (2>&1 3>&1 4>&1)
# Redirect the pipeline output to a file (1>$filename)
# Debug output goes to the host (no redirection)
$(Write-Output "Pipeline Output"; Write-Warning "Warning Output"; Write-Verbose "Verbose Output"; Write-Error "Error Output"; Write-Debug "Debug Output") 2>&1 3>&1 4>&1 1>$filename
# All other output is now in the file
Get-Content $filename
Posted by NeilM1855 on 1/28/2011 at 9:58 AM
this needs to be in google search results as this is the only fix/work around i have found for this problem that works!

out-lineoutput : The OS handle's position is not what FileStream expected. Do not use a handle simultaneously in one Fi
leStream and in Win32 code or another FileStream. This may cause data loss.
    + CategoryInfo         : NotSpecified: (:) [out-lineoutput], IOException
    + FullyQualifiedErrorId : System.IO.IOException,Microsoft.PowerShell.Commands.OutLineOutputCommand
Posted by Nathan Hartley on 5/5/2010 at 12:03 PM
How about something along the lines of the following two commands...

[-File <LogFileName>] # Write messages to a file.
[-AppendFile] # Append to the file.
[-EventLog <WindowsEventLog>] # Write messages to a Windows Event Log.
[-Host] # Write messages to the Host.
[-Level <All, Info, Warn, Error, Debug>] # Write messages when they fall within a particular grouping.
[-Streams <stdout, stderr, warnings, verbose, debug, host>] # Write the .ToString() representation of objects on listed streams.
[-TimeStamp] # Include Time Stamp in the entry
[-ComputerName] # Include the computer name in the entry
[-ScriptName] # Include the script name/context in the entry
[-UserName] #Include the user name in the entry
[-Format <{"Format Expression"}>] # Or create your own format expression that might include some automatic variables.

Write-Log [-Message] <Message>                 # Explicitly pass a message to the Logger
[-Level <All | Info | Warn | Error | Debug>]
Posted by Jeffrey B Smith on 3/3/2010 at 8:43 AM
What Hal requested is exactly what I am looking for, as well.

Posted by HalR on 8/18/2009 at 6:10 AM
I'd like to see this solved in the form of the ability to specify with one statement (or very few) that later in the script any/all log messages be written to alternate places. e.g.

$OutputStreamRedirect = "eventlog"

write-verbose "this turns into an Info entry in event log"
write-error "this is an error entry"
write-warning "this is a warning entry"

Similarly, I'd like to be able to redirect all log commands to a file, host, or combination of the three.
Posted by ZetaOrionis on 12/31/2008 at 2:59 PM
I added a suggestion to the SQL Server Feedback (390263) to address the Invoke-SqlCmd issue where Print and other "Verbose" output should be able to be redirected for later review as if you were sitting at the console. Keith I appreciate all of your blogs/articles as well!! They have saved me a ton of time.
Posted by SQL_Guru on 3/28/2008 at 2:16 PM

You're suggestion just gets you some of the useful data, not everything. It also wouldn’t help if there were a large number of errors, say 50000 errors. Unless you put your log routine in the pipeline, I think $Error only holds 256 errors by default. So you might end up losing significant amounts of errors.

I also disagree that verbose and warnings need to be inline to be useful. What is needed is context. If you run a script that does 4 different copies, you might not need more information than the verbose output. Or, for example, I might do a write-verbose "Starting Server1 Copy". Then I wouldn't need the inline command to know what it was doing.

Unfortunately another example is for SQL 2008. Their Invoke-SQLCmd cmdlet allows you to get status messages from SQL only as verbose output. I hope they change this, but if they don't, then it will be very hard to do anything with this data currently. Some SQL commands only return information through these messages, so you would have to have a transcript running to capture the data at all and you still wouldn't know which individual command caused the verbose output. Then you would have to try and parse the transcript and hope you can determine what is what.

Because of these issues, I really think that it should be possible to redirect them and to have them stored in a variable as well (like $error). This allows you to consume the data in your scripts if you want as well as log it. I also think it should be possible to have something like Start-Log so you don’t have to redirect individual commands. It would just write the desired streams to where you wanted them stored (variable, path, …) and run until it went out of scope or you did a Stop-Log. It also wouldn't redirect, just log the streams. This way you would still see what is happening without using Tee-Object or something (of course Tee doesn't work on other streams either).

Posted by Keith Hill MVP on 9/13/2007 at 6:48 PM
I should also add that I use Start-Transcript to log all my PowerShell sessions and Start-Transcript isn't nestable. So to do this on a script (for just that script) I have to essentially turn off transcription for my PowerShell session.
Posted by Keith Hill MVP on 9/13/2007 at 6:47 PM
Start-Transcript is close to what I want except that I don't want a transcript of an entire PowerShell session. I just want a transcript (log file) of a script that executes. BTW I may need to have spawned scripts create their own transcript that may be completely independent of the parent's transcript (hidden) or the subscript output may need to go to both transcript/log files. It's own and the parent's.
Posted by Kirk Munro on 9/12/2007 at 8:08 AM
Hi Keith,

If I understand what you're looking for correctly, you can get some of this today. Consider this example:

Let's say you have a script file called C:\OutputCaptureTest.ps1 that contains the following PowerShell script:

Set-Location HKLM:

Then if you want to run that script and get all output, you can do the following:

$errorActionPreference = "SilentlyContinue"
& 'C:\OutputCaptureTest.ps1' | out-file C:\results.txt
$error | out-file -append C:\results.txt

Alternatively you could redirect the error output to a new file.

This doesn't capture trace output if tracing is enabled though. I was originally thinking that trace output would be stored in a system variable and might just be undocumented, but since trace information needs to be output inline that wouldn't be very useful. Nor does it capture verbose details or warnings. Again, that output needs to be inline or it wouldn't be very useful.

It sounds like what you're asking for is to have additional parameters added to Start-Transcript that let you define what is output to the transcript file. So you could specify the level of detail in the session information (0 = none, 1 = timestamps only, 2 = full?) as well as what other items you wanted to capture (error, warning, debug, host and verbose output). Is this a correct assumption?

Kirk out.
Posted by Keith Hill MVP on 9/6/2007 at 5:51 PM
BTW I forgot to mention that this is important for folks that need a complete audit trail of automated scripts. Anything output to the host console and not capture to the log file is lost forever.
Sign in to post a workaround.