Logging design help needed

Hello,

I’m writing a script which will be executed on multiple servers via Invoke-Command with -FilePath parameter.
In script itself I have some Write-Output statements to identify which server is executing script and output of script itself. This works fine on single server but since I assume this is multithreaded operation output to script can intermingle between write-output from one server to another.
Questions are

  1. How do I make this reliable where output from one server is not intermingle with output form another server
  2. How do I make design more flexible where I would have an option to see results of screen as output or written to text file. Is it sufficient to pipe output to out-file or this will remove errors from output?

Thanks,
G

When the output comes back, PowerShell will add a “PSComputerName” property to each object returned from each server. You can sort and group on that property to separate the output.

If all you’re doing with Write-Output is writing String objects (e.g., text messages), it may be a bit uglier than you’d like in terms of de-mingling the output. Keep in mind PowerShell doesn’t especially love text - it likes objects. Consider having your script output a custom object - perhaps with a Message property, a TimeStamp property, and so on. Remoting will add the PSComputerName property for you, so you’ll know where each output came from.

PowerShell writes errors to a separate pipeline - you can easily pipe to Out-File to capture your OUTPUT, but errors aren’t output and won’t be included in that redirect. In v3, you can combine the error pipeline into the output pipeline, which will intermingle your output and errors, and which could be then redirected to a file if you like.

I think taking some time to experiment, and understanding that “output” for PowerShell means something specific, and that output doesn’t mean “text on the screen” at all, will help you put together something like what you want.

I asked some of the same questions when I started working with PowerShell. It’s not a simple topic; there are 5 streams (6, if you count the Progress stream) that can all produce host output in different ways (Output, Warning, Verbose, Error, and Debug). It’s tricky to make sure you catch everything and get it into your log file, and even more tricky if you want to see it at the console at the same time.

The simplest way to get everything into a log file (in PowerShell 3.0), is to use the *> redirection operator, but then you won’t see the output at the console.

Do-Something *> .\output.log

I wound up writing two modules for logging behavior to help with this in my scripts; the C# version is a bit more hands-off, but is also more likely to break on occasion, when Microsoft releases PowerShell updates (because it fiddles with private members of the PowerShell host to intercept console output.) They’re both available over on the TechNet repository: http://gallery.technet.microsoft.com/scriptcenter/Enhanced-Script-Logging-27615f85 and http://gallery.technet.microsoft.com/scriptcenter/Write-timestamped-output-4ff1565f

They make it fairly easy to accomplish thorough logging without much effort in your code, particularly the C# one. Here’s an example:

Import-Module PSLogging
$logFile = Add-LogFile -Path '.\output.log'

# Run all your other script code here, no changes required.
# Anything that goes to the screen will wind up in the log file (except for the Progress stream, which is not really useful in a log).
# The module automatically prepends timestamps to non-blank lines, and indicates which stream produced the output.  ([V] for Verbose, [D] for Debug, etc.)

# At the end of your script, stop the logging:
$logFile | Disable-LogFile

Thanks,

How do I group output nicely on a screen once I got custom object out.
I invoke remote script with line below, so $a contains collection of custom objects. I’d like to have table output with first column reading computer name and second column entire “text” property output

>$a = invoke-command remotehost1,remotehost2 -filepath “C:\Users\a\a.ps1”
>$a
Name Value


text Starting script…
computername remotehost1
text Starting script…
computername remotehost2

$a doesn’t contain a collection of objects, it looks like it contains a hashtable. If your remote script is doing something like:

$props = @{message='This is the message';computername=$my_computer_name}
New-Object -Type PSObject -Prop $props

That would be a custom object, and you’d get back “Message” and “Computername” columns in your output.

That’s what I have in remote script, this do not create custom object with 2 properties?

$output = [pscustomobject]@{text=‘’;computername=‘’}

Entire script is below

$output = [pscustomobject]@{text=‘’;computername=‘’}
try
{
$output.ComputerName = $env:Computername
$output.text = “Starting scriptr" $output.text += (cscript c:\windows\system32\slmgr.vbs /dlv| Out-String) $output.text += "nDone”
}
catch
{
$output.text += $Error
}
finally
{
write-output $output
}

The cast from hashtable to [pscustomobject] works if you’re running PowerShell 3.0. If you’re running PowerShell 2.0, the command Don posted should work.

(one reason I don’t like that [pscustomobject] technique - it doesn’t work across all versions, whereas the syntax I used will, and when people blog about that trick they’re never clear that it’s a v3-only trick!)

Thanks,

Never mind I figured out to capture errors in try{}catch{} block I need to change $errorActionPreference to “stop” for catch all to work