"Logging" Script Output in Powershell
I've been using Powershell for deploy scripts from TFS builds at my current gig. I COULD have used MSBuild, but I chose Powershell for a number of reasons (which I'll detail in a future article on the subject). One important feature (particularly for something like deploying "bits" to production) is to save a "log" of all output from the script. In case of failure, you'll want to review the errors. For troubleshooting in the future, it's especially important to know what files were copied etc. And auditors love stuff like that...
A long time ago, in a technology far far away, I wrote database scripts for a Sybase database on UNIX using Kornshell - so I have some (albeit decade old) experience with scripting. Normally you would create the "log" by re-directing the output from the script to a file. The problem is that you have to wait for the script to finish and type a command to see the results. So I would use the "tee" command that would split the output to the console and a file.
When writing my Powershell scripts I found they have the "tee-object" so all was good. I called my script with a line like this:
.{.\_DeployHostApplication.ps1 -Env QA -WebTargetName "\\corpgp01\ARCS-Services-bin"} | tee-object $file
The problem, tee-object only copies the "success" output-stream and not errors. In Kornshell there was stdout and stderr and you could redirect with 2>& (or some such completely obscure and unreadable syntax, it's been a while) that redirects both output streams.
So doing research with the handy tool known as "Google" I looked at various approaches and found out that Powershell has a feature built in (ah those Powershell authors are so smart!) with commands "start-transcript" and "stop-transcript" that writes all output automagically to a file! This was EXACTLY what I needed!
The only issue, without error control, if a severe error occurs that causes the script to fail, the "stop-transcript" won't execute. That "transcripting" continues and everything you type at the Powershell command line continues to be written to the file. (I was getting "recursive like behavior as I was typing the log file and it would append the output, doubling the size of the log as it went on!).
But this is no problem in Powershell as it has Error Trapping built in. It's sort of like the old classic VB "On Error" (as opposed to Try-Catch) where you have a script block that executes if an error occurs. So I wrapped the stop-transcript command in an error trap and all was fine. Here is the new code:
$dt = Get-Date -format "yyyyMMdd_hhmm"
$log = ($MyInvocation.MyCommand.Name).Replace(".ps1","_$dt.log")
$file = "..\..\" + $log
start-transcript $file
trap { stop-transcript; break}
.\_DeployHostApplication.ps1 -Env QA -WebTargetName \\corpgp01\ARCS-Services-bin
stop-transcript
So here is the final script. The first three lines determine the filename I want to log to. The name will match the Powershell script name with a date and time stamp followed by “.log” instead of “.ps1” and two directory tree levels above where the script was located.
Lines 4-5 start the transcript, and uses the error trapping code to stop the transcript and then break out of the script (alternatively I could use “continue” instead of “break” to continue the DeployHostApplication script instead of breaking at the line that caused the error)
Line 6 executes my script, and line 7 will do the stop-transcript when there are no errors.
The only other issue I want to mention, “stop-transcript” only writes POWESHELL output. A subsequent blog post will show how I solved that issue…