Skip to Content

How to Fix PowerShell Output Log Being Logged Multiple Times

  • The article explains how to avoid logging the same message multiple times in PowerShell by using different cmdlets and streams instead of Write-Host.
  • The article also provides tips and best practices on how to create and manage log files in PowerShell using custom functions, try-catch-finally blocks, and redirection operators.

PowerShell is a powerful scripting language that can automate various tasks and perform complex operations. However, sometimes you may encounter some issues or errors when running your PowerShell scripts. One of these issues is the output log being logged multiple times, which can cause confusion and clutter in your log files.

In this article, we will explain what causes this issue, how to fix it, and how to improve your logging practices in PowerShell. We will also provide some frequently asked questions (FAQ) related to this topic at the end of the article.

What Causes the Output Log to Be Logged Multiple Times?

The output log being logged multiple times is usually caused by using the Write-Host cmdlet in your PowerShell script. The Write-Host cmdlet writes directly to the console host, which is the program that displays the PowerShell window. This means that whatever you write with Write-Host will be displayed on the screen, but not captured by any redirection or output commands.

For example, if you use the following command to write a message to a log file:

Write-Host "Hello World" | Out-File -FilePath C:\PS\Logs\TestLog.txt

You will see the message “Hello World” on the screen, but nothing will be written to the log file. This is because Write-Host does not produce any output objects that can be piped to Out-File. Instead, it sends the message directly to the console host.

However, if you use the Start-Transcript cmdlet to record your PowerShell session, you will see that the message “Hello World” is logged twice in the transcript file. This is because Start-Transcript captures both the input and output of your PowerShell session, including what is written by Write-Host. For example, if you run the following commands:

Start-Transcript -Path C:\PS\Logs\Transcript.txt
Write-Host "Hello World" | Out-File -FilePath C:\PS\Logs\TestLog.txt
Stop-Transcript

You will see something like this in the transcript file:

**********************
Windows PowerShell transcript start
Start time: 20210919173707
Username: DESKTOP-P2FHTKQ\user
RunAs User: DESKTOP-P2FHTKQ\user
Configuration Name: 
Machine: DESKTOP-P2FHTKQ (Microsoft Windows NT 10.0.19043.0)
Host Application: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
Process ID: 1234
PSVersion: 5.1.19041.1151
PSEdition: Desktop
PSCompatibleVersions: 1.0, 2.0, 3.0, 4.0, 5.0, 5.1.19041.1151
BuildVersion: 10.0.19041.1151
CLRVersion: 4.0.30319.42000
WSManStackVersion: 3.0
PSRemotingProtocolVersion: 2.3
SerializationVersion: 1.1.0.1
**********************
Transcript started, output file is C:\PS\Logs\Transcript.txt
PS C:\> Write-Host "Hello World" | Out-File -FilePath C:\PS\Logs\TestLog.txt
Hello World
PS C:\> Stop-Transcript
**********************
Windows PowerShell transcript end
End time: 20210919173709
**********************

As you can see, the message “Hello World” is logged twice in the transcript file: once as an input command and once as an output from Write-Host.

This can be problematic if you want to use Start-Transcript to record your PowerShell session for debugging or auditing purposes, as it can create duplicate or redundant entries in your log files.

How to Fix the Output Log Being Logged Multiple Times?

The simplest way to fix the output log being logged multiple times is to avoid using Write-Host in your PowerShell script, unless you really need to display something on the screen only.

Instead, you can use other cmdlets that produce output objects that can be redirected or captured by other commands, such as Write-Output, Write-Verbose, Write-Warning, Write-Error, or Write-Information.

For example, if you use the following command to write a message to a log file:

Write-Output "Hello World" | Out-File -FilePath C:\PS\Logs\TestLog.txt

You will not see anything on the screen, but the message “Hello World” will be written to the log file. This is because Write-Output produces an output object that can be piped to Out-File.

If you use the Start-Transcript cmdlet to record your PowerShell session, you will see that the message “Hello World” is logged only once in the transcript file, as an output from Write-Output. For example, if you run the following commands:

Start-Transcript -Path C:\PS\Logs\Transcript.txt
Write-Output "Hello World" | Out-File -FilePath C:\PS\Logs\TestLog.txt
Stop-Transcript

You will see something like this in the transcript file:

**********************
Windows PowerShell transcript start
Start time: 20210919173707
Username: DESKTOP-P2FHTKQ\user
RunAs User: DESKTOP-P2FHTKQ\user
Configuration Name: 
Machine: DESKTOP-P2FHTKQ (Microsoft Windows NT 10.0.19043.0)
Host Application: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
Process ID: 1234
PSVersion: 5.1.19041.1151
PSEdition: Desktop
PSCompatibleVersions: 1.0, 2.0, 3.0, 4.0, 5.0, 5.1.19041.1151
BuildVersion: 10.0.19041.1151
CLRVersion: 4.0.30319.42000
WSManStackVersion: 3.0
PSRemotingProtocolVersion: 2.3
SerializationVersion: 1.1.0.1
**********************
Transcript started, output file is C:\PS\Logs\Transcript.txt
PS C:\> Write-Output "Hello World" | Out-File -FilePath C:\PS\Logs\TestLog.txt
Hello World
PS C:\> Stop-Transcript
**********************
Windows PowerShell transcript end
End time: 20210919173709
**********************

As you can see, the message “Hello World” is logged only once in the transcript file, as an output from Write-Output.

This way, you can avoid logging the same message multiple times and keep your log files clean and concise.

How to Improve Your Logging Practices in PowerShell?

Besides avoiding Write-Host, there are some other tips and best practices that you can follow to improve your logging practices in PowerShell.

Use a Custom Function to Write to Log Files

Instead of using Write-Output or other cmdlets directly to write to log files, you can create a custom function that will handle the logging for you. This way, you can have more control and flexibility over how and what you log in your PowerShell scripts.

For example, you can create a function like this:

$Logfile = "C:\PS\Logs\proc_$env:computername.log"

function WriteLog {
    Param (
        [string]$LogString,
        [string]$Level = "INFO"
    )
    $Stamp = (Get-Date).toString ("yyyy/MM/dd HH:mm:ss")
    $LogMessage = "$Stamp $Level $LogString"
    Add-content $LogFile -value $LogMessage 
}

This function takes two parameters: a string to log and a level to indicate the severity or importance of the message (default is INFO). It then adds a timestamp and the level to the message and writes it to the log file specified by the $Logfile variable.

You can then use this function in your script like this:

WriteLog "The script is run"
WriteLog "Calculating…." -Level "DEBUG"
Start-Sleep 20
WriteLog "The script is successfully executed" -Level "SUCCESS"

This will produce a log file like this:

2021/09/19 17:37:07 INFO The script is run
2021/09/19 17:37:07 DEBUG Calculating….
2021/09/19 17:37:27 SUCCESS The script is successfully executed

Using a custom function to write to log files has several advantages:

  • You can customize the format and content of your log messages according to your needs and preferences.
  • You can use different levels to indicate the importance or severity of your log messages, which can help you filter or prioritize them later.
  • You can use a single variable ($Logfile) to specify the path and name of your log file, which makes it easier to change or manage it.
  • You can reuse the function in different scripts or modules without having to rewrite or copy-paste it.

Use Different Streams for Different Types of Output

PowerShell supports six different streams for different types of output:

  • Stream #1 (Success): This is the default stream for normal output, such as the result of a command or expression. You can use Write-Output or simply omit the cmdlet to write to this stream. For example:
Write-Output "This is a success message"
# or
"This is a success message"

This stream can be redirected to a file or variable using the > or >> operators. For example:

Write-Output "This is a success message" > C:\PS\Logs\Success.txt
# or
$Success = Write-Output "This is a success message"
  • Stream #2 (Error): This is the stream for error messages, such as the ones generated by terminating or non-terminating errors. You can use Write-Error to write to this stream. For example:
Write-Error "This is an error message"

This stream can be redirected to a file or variable using the 2> or 2>> operators. For example:

Write-Error "This is an error message" 2> C:\PS\Logs\Error.txt
# or
$Error = Write-Error "This is an error message"
  • Stream #3 (Warning): This is the stream for warning messages, such as the ones generated by cmdlets that have a -WarningAction parameter. You can use Write-Warning to write to this stream. For example:
Write-Warning "This is a warning message"

This stream can be redirected to a file or variable using the 3> or 3>> operators. For example:

Write-Warning "This is a warning message" 3> C:\PS\Logs\Warning.txt
# or
$Warning = Write-Warning "This is a warning message"
  • Stream #4 (Verbose): This is the stream for verbose messages, such as the ones generated by cmdlets that have a -Verbose parameter. You can use Write-Verbose to write to this stream. For example:
Write-Verbose "This is a verbose message"

This stream can be redirected to a file or variable using the 4> or 4>> operators. For example:

Write-Verbose "This is a verbose message" 4> C:\PS\Logs\Verbose.txt
# or
$Verbose = Write-Verbose "This is a verbose message"
  • Stream #5 (Debug): This is the stream for debug messages, such as the ones generated by cmdlets that have a -Debug parameter. You can use Write-Debug to write to this stream. For example:
Write-Debug "This is a debug message"

This stream can be redirected to a file or variable using the 5> or 5>> operators. For example:

Write-Debug "This is a debug message" 5> C:\PS\Logs\Debug.txt
# or
$Debug = Write-Debug "This is a debug message"
  • Stream #6 (Information): This is the stream for information messages, such as the ones generated by Write-Information or Write-Host. You can use Write-Information to write to this stream. For example:
Write-Information "This is an information message"

This stream can be redirected to a file or variable using the 6> or 6>> operators. For example:

Write-Information "This is an information message" 6> C:\PS\Logs\Information.txt
# or
$Information = Write-Information "This is an information message"

Using different streams for different types of output has several advantages:

  • You can separate and filter your output based on the type and importance of the messages.
  • You can control how each stream behaves and displays using preference variables, such as $ErrorActionPreference, $WarningPreference, $VerbosePreference, $DebugPreference, and $InformationPreference.
  • You can use different colors and formats for each stream using the $Host.UI.Write* methods, such as $Host.UI.WriteErrorLine, $Host.UI.WriteWarningLine, $Host.UI.WriteVerboseLine, $Host.UI.WriteDebugLine, and $Host.UI.WriteInformationLine.

Use Try-Catch-Finally Blocks to Handle Errors

PowerShell supports two types of errors: terminating and non-terminating. Terminating errors are fatal errors that stop the execution of the script or command, while non-terminating errors are minor errors that allow the script or command to continue.

You can use try-catch-finally blocks to handle both types of errors in your PowerShell script. A try-catch-finally block consists of three parts:

  • A try block that contains the code that may cause an error.
  • A catch block that contains the code that handles the error, if any.
  • A finally block that contains the code that runs regardless of whether an error occurs or not.

For example, you can use a try-catch-finally block like this:

try {
    # Code that may cause an error
    $Result = 1 / 0
}
catch {
    # Code that handles the error
    Write-Error "An error occurred: $_"
}
finally {
    # Code that runs regardless of the error
    Write-Log "The script is done"
}

This will produce an output like this:

An error occurred: Attempted to divide by zero.
At line:5 char:5
+     Write-Error "An error occurred: $_"
+     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Write-Error], WriteErrorException
    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException

2021/09/19 17:37:07 INFO The script is done

As you can see, the try block causes a terminating error when it tries to divide by zero. The catch block handles the error by writing an error message to the error stream. The finally block runs regardless of the error and writes a log message to the log file.

Using try-catch-finally blocks to handle errors has several advantages:

  • You can prevent your script from terminating unexpectedly due to unhandled errors.
  • You can customize how you handle different types of errors or exceptions using multiple catch blocks or filters.
  • You can ensure that your script performs some cleanup or final actions regardless of the error status.

Frequently Asked Questions (FAQ)

Here are some frequently asked questions (FAQ) related to PowerShell output log being logged multiple times:

Question: How can I view the different streams in PowerShell?

Answer: You can view the different streams in PowerShell using the *-Stream parameters of some cmdlets, such as Get-Content, Set-Content, or Out-String. For example, you can use the following command to view the verbose stream of a script:

Get-Content -Path C:\PS\Scripts\Test.ps1 -Verbose -VerboseStream

Alternatively, you can use the redirection operators to redirect any stream to another stream or file. For example, you can use the following command to redirect the verbose stream to the success stream and display it on the screen:

.\Test.ps1 4>&1

Question: How can I suppress or silence the output of a PowerShell command or script?

Answer: You can suppress or silence the output of a PowerShell command or script by redirecting it to $null, which is a special variable that represents nothing. For example, you can use the following command to suppress the output of Write-Host:

Write-Host "This is a silent message" > $null

Alternatively, you can use the Out-Null cmdlet, which discards any input it receives. For example, you can use the following command to silence the output of Write-Host:

Write-Host "This is a silent message" | Out-Null

Question: How can I append or overwrite an existing log file in PowerShell?

Answer: You can append or overwrite an existing log file in PowerShell by using the Out-File cmdlet with the -Append or -NoClobber parameters. For example, you can use the following command to append a message to an existing log file:

Write-Output "This is an appended message" | Out-File -FilePath C:\PS\Logs\TestLog.txt -Append

Or you can use the following command to overwrite an existing log file with a new message:

Write-Output "This is an overwritten message" | Out-File -FilePath C:\PS\Logs\TestLog.txt -NoClobber

Conclusion

In this article, we have explained what causes the output log being logged multiple times in PowerShell, how to fix it, and how to improve your logging practices in PowerShell. We have also provided some frequently asked questions (FAQ) related to this topic.

We hope you have found this article helpful and informative. If you have any questions or feedback, please feel free to leave a comment below.

Disclaimer: The information and code provided in this article are for educational and informational purposes only. They are not intended to be used for any illegal or malicious activities. The author and publisher are not responsible for any damages or consequences resulting from the use or misuse of this article or code. Please use them at your own risk and discretion.