One thing that has been lamented frequently about PowerShell is that it is very difficult to log, if not impossible, to log all of the various types of streams it has to a single source. The legacy Windows shell CMD, and Unix shells like Bash, Ksh etc only deal with three streams: stdin, stdout and stderr for Input, Ouput and Error respectively. PowerShell has many more: Input, Output, Verbose, Warning, Debug, Progress and Error. Finally, the APIs in v2.0 offer enough hooks to unify the logging but you got to work a bit to make it come together. Well, to be honest, you got me doing the work. The rest is easy ;)
import-module .\scriptlogger.psm1 -force $logger = New-ScriptLogger # override error handler $logger.ErrorHandler = { param($record) $record.tostring() >> scriptlog.txt } # override verbose handler $logger.VerboseHandler = { param($record) $record.message >> scriptlog.txt } # run scriptblock with logging $logger.Invoke( { $verbosepreference='continue'; $erroractionpreference = 'continue'; $debugpreference = 'continue'; write-verbose "verbose"; write-error "an error"; write-warning "a warning" Write-debug "debug string" "this is output" 1,2,3 })
<# Name : Universal Script Logging Module (ScriptLogger.psm1) Version : 0.1 Author : Oisin Grehan (MVP) Site : http://www.nivot.org/ #> function New-ScriptLogger { New-Module -AsCustomObject -ScriptBlock { $script:ps = [powershell]::Create() $script:ar = $null $script:module = $ExecutionContext.SessionState.Module [scriptblock] $script:ErrorHandler = { param( [Management.Automation.ErrorRecord] $record ) [diagnostics.debug]::writeline( "Error: " + $record.tostring()); } [scriptblock] $script:WarningHandler = { param( [Management.Automation.WarningRecord] $record ) [diagnostics.debug]::writeline( "Warning: " + $record.message); } [scriptblock] $script:VerboseHandler = { param( [Management.Automation.VerboseRecord] $record ) [diagnostics.debug]::writeline( "Verbose: " + $record.message); } [scriptblock] $script:DebugHandler = { param( [Management.Automation.DebugRecord] $record ) [diagnostics.debug]::writeline( "Debug: " + $record.message); } [scriptblock] $script:ProgressHandler = { param( [Management.Automation.ProgressRecord] $record ) [diagnostics.debug]::writeline( "Progress: " + $record); } $script:Handlers = @{ Error = Register-ObjectEvent $ps.Streams.Error DataAdded -Action { & $event.MessageData {& $ErrorHandler @args} $event.sender[$eventargs.index] } -MessageData $module #-SupportEvent Warning = Register-ObjectEvent $ps.Streams.Warning DataAdded -Action { & $event.MessageData {& $WarningHandler @args} $event.sender[$eventargs.index] } -MessageData $module #-SupportEvent Verbose = Register-ObjectEvent $ps.Streams.Verbose DataAdded -Action { & $event.MessageData {& $VerboseHandler @args} $event.sender[$eventargs.index] } -MessageData $module #-SupportEvent Debug = Register-ObjectEvent $ps.Streams.Debug DataAdded -Action { & $event.MessageData {& $DebugHandler @args} $event.sender[$eventargs.index] } -MessageData $module #-SupportEvent Progress = Register-ObjectEvent $ps.Streams.Progress DataAdded -Action { & $event.MessageData {& $ProgressHandler @args} $event.sender[$eventargs.index] } -MessageData $module #-SupportEvent } function Invoke { param( [validatenotnullorempty()] [scriptblock]$script ) try { write-host -foreground green "Running" $ps.commands.clear() $command = new-object management.automation.runspaces.command $script, $true $ps.commands.addcommand($command) > $null $ps.invoke() # returns output } catch { # oops-ee! write-host -foreground red "Unhandled terminating error: $_" $record = new-object management.automation.errorrecord $( new-object exception $_.tostring()), "TerminatingError", "NotSpecified", $null & $ErrorHandler $record } finally { write-host -foreground green "Complete" } } Export-ModuleMember -Function Invoke -Variable ErrorHandler, WarningHandler, VerboseHandler, DebugHandler, ProgressHandler } }
Have fun!
Page rendered at Thursday, September 02, 2010 6:03:22 PM (Eastern Daylight Time, UTC-04:00)