2

i'm trying to output to eventlog to the correct Entry Type (Information,Warning,Error) based on the stream that is coming out of my cmdlet, something like this:

function myfunction {
   Param(
   [switch]$stream1,
   [switch]$stream2
)
   if ($stream1) {write-output 'stream 1 msg'}
   if ($stream2) {write-error 'stream 2 msg'}
}

$eventlogparams = @{'logname'='application';'source'='myapp';'eventid'='1'}

myfunction -stream1 -stream2 `
  1> write-eventlog @eventlogparams -entrytype information -message $_ `
  2> write-eventlog @eventlogparams -entrytype error -message $_

does anyone have an idea of how to accomplish this?

mklement0
  • 382,024
  • 64
  • 607
  • 775
ErikW
  • 386
  • 2
  • 11
  • you need a `source` named `myapp` on the target system. that is created with the `New-Eventlog` cmdlet and its `-Source` parameter. take a look at this article ... How to Use PowerShell to Write to Event Logs – Hey, Scripting Guy! Blog — https://blogs.technet.microsoft.com/heyscriptingguy/2013/06/20/how-to-use-powershell-to-write-to-event-logs/ – Lee_Dailey Nov 10 '18 at 18:30
  • 1
    @Lee_Dailey: I think the `Write-EventLog` arguments are just examples. If I understand correctly, the OP's desire is to process output from _all_ streams (or at least from both the success and error streams) with the ability to distinguish what stream the input came from, so that the behavior can be adjusted accordingly (picking the appropriate event category, in this case). – mklement0 Nov 10 '18 at 21:08
  • 2
    @mklement0 - that makes sense. [*grin*] i am still uncertain the OP knows that the `Source` must exist 1st. that is the most common error i have seen folks mention when 1st trying to write to a custom event log. – Lee_Dailey Nov 10 '18 at 21:15
  • i'm already aware of needing to create the source beforehand – ErikW Nov 11 '18 at 03:58
  • @ErikW - thank you for clarifying that! [*grin*] – Lee_Dailey Nov 11 '18 at 14:08
  • in case anyone is interested i wrote a powershell module around this whole subject: github.com/ewhitesides/EventLogTools – ErikW Aug 23 '20 at 02:59

2 Answers2

4

You can merge the error stream and others into the success stream and distinguish between the origin streams by the data type of each pipeline object:

myfunction -channel1 -channel2 *>&1 | ForEach-Object { 
  $entryType = switch ($_.GetType().FullName) {
        'System.Management.Automation.ErrorRecord' { 'error'; break }
        'System.Management.Automation.WarningRecord' { 'warning'; break }
        default { 'information'}
    }
  write-eventlog @eventlogparams -entrytype $entryType -message $_
}

Redirection *>&1 sends the output from all (*) streams to (&) the success stream (1), so that all output, irrespective of what stream it came from, is sent through the pipeline.

The above only deals with errors and warnings specifically, and reports everything else, including the success output, as information, but it's easy to extend the approach - see bottom.

See about_Redirections for an overview of all 6 output streams available in PowerShell (as of v6).


To illustrate the technique with a simpler example:

& { Write-Output good; Write-Error bad; Write-Warning problematic } *>&1 | ForEach-Object {
  $entryType = switch ($_.GetType().FullName) {
        'System.Management.Automation.ErrorRecord' { 'error'; break }
        'System.Management.Automation.WarningRecord' { 'warning'; break }
        default { 'information'}
    }
  '[{0}] {1}' -f $entryType, $_
}

The above yields:

[information] good
[error] bad
[warning] problematic

The list of data types output by the various streams:

Stream           Type
------           ----
#1 (Success)     (whatever input type is provided).
#2 (Error)       [System.Management.Automation.ErrorRecord]
#3 (Warning)     [System.Management.Automation.WarningRecord]
#4 (Verbose)     [System.Management.Automation.VerboseRecord]
#5 (Debug)       [System.Management.Automation.DebugRecord]
#6 (Information) [System.Management.Automation.InformationRecord]

The following code was used to produce the above list (except for the first data row):

& {
    $VerbosePreference = $DebugPreference = $InformationPreference = 'Continue'
    $ndx = 2
    "Write-Error", "Write-Warning", "Write-Verbose", "Write-Debug", "Write-Information" | % {
        & $_ ($_ -split '-')[-1] *>&1
        ++$ndx
    } | Select-Object @{n='Stream'; e={"#$ndx ($_)"} }, @{n='Type'; e={"[$($_.GetType().FullName)]"} }
}
mklement0
  • 382,024
  • 64
  • 607
  • 775
  • In your 1st example I had to use `-ErrorAction Continue` on the Write-Error call or the script would stop with an error. Likewise with the 2nd example I had to use `& $_ ($_ -split '-')[-1] -Erroraction Continue *>&1` for it to work. – bielawski Jan 08 '20 at 19:44
  • @bielawski: That is only necessary if you've changed preference variable `$ErrorActionPreference` from its default of `'Continue'` to `'Stop'`. Passing `-Erroraction Continue` to `& { ... }` doesn't act as the `-ErrorAction` common parameter; only cmdlets and advanced scripts / functions recognize it. – mklement0 Jan 08 '20 at 22:17
1

As @Lee_Dailey rightly pointed , you need the event source to exist.Even after that,Your snippet might throw error like (checked in PS v5)

The process cannot access the file 'C:\Users\username\Desktop\write-eventlog' because it is being used by another process.

because redirection operator expects a file to redirect not a cmdlet or function that's the reason for the above error.

you can try modifying the code so that redirection operator stores the data in files and then push that into event log:

myfunction -channel1 -channel2 > output.txt  2> error.txt 

write-eventlog @eventlogparams -entrytype error -message ((get-content error.txt) -join "")

write-eventlog @eventlogparams -entrytype information -message ((get-content output.txt) -join "")

Another method is using outvariable and errorvariable , for this to work the function must be advanced function (I have added cmdletbinding for that):

function myfunction {
[CmdletBinding()]
   Param(
   [switch]$channel1,
   [switch]$channel2
)
   if ($channel1) {write-output 'channel 1 msg'}
   if ($channel2) {write-error 'channel 2 msg'}
}

$eventlogparams = @{'logname'='application';'source'='myapp';'eventid'='1'}

myfunction -channel1 -channel2 -OutVariable output -ErrorVariable errordata
write-eventlog @eventlogparams -entrytype error -message ($errordata -join "")

write-eventlog @eventlogparams -entrytype information -message ($output -join "")
ClumsyPuffin
  • 3,909
  • 1
  • 17
  • 17
  • Quibble: The reason for the error is that the same file is being targeted _twice_; PowerShell is perfectly happy to write to a file named `write-eventlog`, but not via redirections competing for the same output file. Also, I'm not sure that the intent is to write the collected stream output as a _single_ event-log record. – mklement0 Nov 11 '18 at 04:07
  • 1
    the problem with this is that it collects all to a variable, and you can do a foreach statement to split out to eventlog later, but I'm trying to write to eventlog in realtime as things are done inside my function – ErikW Nov 11 '18 at 04:12
  • 1
    @mklement0 yes .I understood that from the error message,I should have articulated that better in the answer – ClumsyPuffin Nov 11 '18 at 04:29