this has been beaten to death but can't find an exact solution for my problem.
I have a PowerShell script that can be run from the command line or from a scheduled task. I'm using the following line
Write-Output "Updating user $account" | Tee-Object $logfile -Append
to write relevant information to the screen and a log file. I need both because when run from a command line, I can physically see what's going on but when run from a scheduled task, I have no visibility to its output hence the log file.
Thing is, I'm modifying my code to use functions but as you might already know, Write-Output messes up the return values of functions when used within said functions.
What could I do that would do something similar to what I stated above without affecting the function's return value?
Thanks.
CodePudding user response:
Just write to a log file. When running from the console, open another console and tail
the log file.
Get-Content 'C:\path\to\the\logfile.txt' -Tail 10 -Wait
CodePudding user response:
Assuming PowerShell version 5 or higher, where Write-Host
writes to the information output stream (stream number 6
), which doesn't interfere with the success output stream (stream number 1
) and therefore doesn't pollute your function's data output:
The following is not a single command, but you could easily wrap this in a function:
Write-Host "Updating user $account" -iv msg; $msg >> $logfile
The above uses the common -InformationVariable
(-iv
) parameter to capture Write-Host
's output in variable $msg
(note how its name must be passed to -iv
, i.e. without the leading $
).
The message captured in $msg
is then appended to file $logfile
with >>
, the appending redirection operator.
- Note:
>>
is in effect an alias forOut-File
-Append
, and uses a fixed character encoding, both on creation and appending. - Use
Add-Content
and its-Encoding
parameter instead, if you want to control the encoding.
CodePudding user response:
Instead of explicitly writing each log line to a file, you may want to use a different approach that references the log file only at one location in the code.
Advantages:
- Easy to change log path and customize the log output (e. g. prepending a timestamp), without having to modify all code locations that log something.
- Captures any kind of messages, e. g. also error, verbose and debug messages (if enabled).
- Captures messages of 3rd party code aswell, without having to tell them the name of the log file.
Function SomeFunction {
Write-Host "Hello from SomeFunction" # a log message
"SomeFunctionOutput" # Implicit output (return value) of the function.
# This is short for Write-Output "SomeFunctionOutput".
}
Function Main {
Write-Host "Hello from Main" # a log message
# Call SomeFunction and store its result (aka output) in $x
$x = SomeFunction
# To demonstrate that "normal" function output is not affected by log messages
$x -eq "SomeFunctionOutput"
}
# Call Main and redirect all of its output streams, including those of any
# called functions.
Main *>&1 | Tee-Object -FilePath $PSScriptRoot\Log.txt -Append
Output:
Hello from Main
Hello from SomeFunction
True
In this sample all code is wrapped in function Main
. This allows us to easily redirect all output streams using the *>&1
syntax, which employs the redirection operator to "merge" the streams. This means that all commands further down the pipeline (in this example Tee-Object
) receive any script messages that would normally end up in the console (except when written directly to the console, which circumvents PowerShells streams).
Possible further improvements
You may want to use try
/catch
in function Main
, so you also capture script-terminating errors:
try {
SomeFunction # May also cause a script-terminating error, which will be catched.
# Example code that causes a script-terminating error
Write-Error "Fatal error" -ErrorAction Stop
}
catch {
# Make sure script-terminating errors are logged
Write-Error -ErrorRecord $_ -ErrorAction Continue
}