SeqLogger.psm1



function Send-SeqEvent
{
<#
.SYNOPSIS
Send an event to a Seq server.
 
.INPUTS
System.Collections.Hashtable
or System.Collections.Specialized.OrderedDictionary
or System.Data.DataRow
or an object
 
.FUNCTIONALITY
Seq
 
.LINK
Invoke-RestMethod
 
.LINK
https://getseq.net/
 
.EXAMPLE
Send-SeqEvent 'Hello from PowerShell' -Server http://my-seq -LiteralMessage
 
.EXAMPLE
Send-SeqEvent 'Event: {User} on {Machine}' @{ User = $env:UserName; Machine = $env:ComputerName } -Server http://my-seq
 
.EXAMPLE
Send-SeqEvent -Properties @{ Message = $Error[0].Exception.Message } -Level Error -Server http://my-seq
#>


[CmdletBinding()][OutputType([void])] Param(
<#
The text to use as the log message, a Seq template unless -LiteralMessage is present.
By default, the value of the Message property will be used.
#>

[Parameter(Position=0)][Alias('Text')][string] $Message = '{Message}',
<#
Logging properties to record in Seq, as an OrderedDictionary, Hashtable, DataRow,
or any object with properties to use.
#>

[Parameter(Mandatory=$true,Position=1,ValueFromPipeline=$true)][Alias('Parameters')] $Properties,
<#
The type of event to record.
Information by default.
#>

[ValidateSet('Verbose','Debug','Information','Warning','Error','Fatal')][string] $Level = 'Information',
# The URL of the Seq server.
[uri] $Server,
# The Seq API key to use.
[string] $ApiKey,
# When present, indicates the Message parameter is to be used verbatim, not as a Seq template.
[switch] $LiteralMessage
)
Process
{
    if($LiteralMessage) { $Properties += @{Message=$Message}; $Message = "{Message}" }

    Invoke-RestMethod -Uri (New-Object uri ([uri]$Server,"/api/events/raw?apiKey=$ApiKey")) `
        -Method POST -ContentType application/json -Body (@{
            Events  = @(
                @{
                    Timestamp       = Get-Date -Format o
                    Level           = $Level
                    MessageTemplate = $Message
                    Properties      = $Properties
                }
            )
        } |ConvertTo-Json -Depth 5 -Compress) |Write-Verbose
}

}

function Send-SeqScriptEvent
{
<#
.SYNOPSIS
Sends an event (often an error) from a script to a Seq server, including script info.
 
.FUNCTIONALITY
Seq
 
.LINK
Send-SeqEvent
 
.LINK
https://getseq.net/
 
.EXAMPLE
try { Connect-Thing } catch { Send-SeqScriptEvent.ps1 'Trying to connect' $_ -Level Error -Server http://my-seq }
#>


[CmdletBinding()][OutputType([void])] Param(
# A description of what was being attempted.
[Parameter(Position=0,Mandatory=$true)][string]$Action,
<#
An optional PowerShell ErrorRecord object to record.
Will try to automatically find $_ in a calling "catch{}"" block.
#>

[Parameter(Position=1)][Management.Automation.ErrorRecord]$ErrorRecord =
    ((Get-Variable _ -Scope 1 -ValueOnly -EA SilentlyContinue) -as [Management.Automation.ErrorRecord]),
<#
The type of event to record.
Defaults to Error if an ErrorRecord is found, Information otherwise.
#>

[Parameter(Position=2)][ValidateSet('Verbose','Debug','Information','Warning','Error','Fatal')][string] $Level = 'Error',
<#
The scope of the script InvocationInfo to use.
Defaults to 1 (the script calling Send-SeqScriptEvent.ps1).
Sending a 2 will try to use the script calling the script calling this one.
#>

[Alias('Scope')][string] $InvocationScope = '1',
# The URL of the Seq server.
[uri] $Server,
# The Seq API key to use.
[string] $ApiKey
)

if(!($ErrorRecord -or $PSBoundParameters.ContainsKey('Level'))) { $Level = 'Information' }
$caller = try{Get-Variable MyInvocation -Scope $InvocationScope -ValueOnly -EA Stop}catch{$MyInvocation}
$SeqEvent = @{ Level = $Level }
if($Server){[void]$SeqEvent.Add('Server',$Server)}
if($ApiKey){[void]$SeqEvent.Add('ApiKey',$ApiKey)}
$Properties = @{
    Script       = if($caller.ScriptName){Split-Path $caller.ScriptName -Leaf}else{$caller.MyCommand.Name}
    CommandName  = $caller.MyCommand.Name
    Invocation   = $caller
    Action       = $Action
    CommandLine  = [Environment]::GetCommandLineArgs()
    ComputerName = $env:COMPUTERNAME
}
if($ErrorRecord)
{
    $Properties += @{
        Message     = $ErrorRecord.Exception.Message
        Error       = $ErrorRecord
    }
    [void]$SeqEvent.Add('Message','{Script}: {Action}: {Message}')
}
else
{
    [void]$SeqEvent.Add('Message','{Script}: {Action}')
}
[void]$SeqEvent.Add('Properties',$Properties)
Send-SeqEvent @SeqEvent -WarningAction Ignore

}

function Use-SeqServer
{
<#
.SYNOPSIS
Set the default Server and ApiKey for Send-SeqEvent.ps1
 
.FUNCTIONALITY
Seq
 
.LINK
https://getseq.net/
 
.EXAMPLE
Use-SeqServer.ps1 http://my-seq $apikey
#>


[CmdletBinding()][OutputType([void])] Param(
# The URL of the Seq server.
[Parameter(Mandatory=$true)][uri] $Server,
# The Seq API key to use.
[string] $ApiKey
)
Write-Verbose "Using Seq at $Server"
$value = @{ 'Send-SeqEvent:Server' = $Server }
if($ApiKey) {$value['Send-SeqEvent:ApiKey'] = $ApiKey}
$defaults = Get-Variable -Scope 2 -Name PSDefaultParameterValues -EA SilentlyContinue
if($defaults) {$value.Keys |Where-Object {$defaults.Value.Contains($_)} |ForEach-Object {$defaults.Value.Remove($_)}; $defaults.Value += $value}
else {Set-Variable -Scope 2 -Name PSDefaultParameterValues -Value $value}

}
Export-ModuleMember -Function Send-SeqEvent,Send-SeqScriptEvent,Use-SeqServer