swiftlogger.psm1
|
function Set-LogConfiguration { param( [Parameter()] [String]$AppName, [Parameter()] [String]$SysLogServer, [Parameter()] [int]$SysLogPort, [ValidateSet('RFC5424','RFC3164')] [Parameter()] [string]$SysLogRFC, [Parameter()] [String]$LogFilePath, [Parameter()] [String]$LogName, [Parameter()] [String]$ScriptName, [Parameter()] [String]$ScriptVersion, [Parameter()] [bool]$SendSyslog, [Parameter()] [bool]$JsonLogging ) if ($PSBoundParameters.ContainsKey('AppName')) { $Global:AppName = $AppName } if ($PSBoundParameters.ContainsKey('SysLogServer')) { $Global:SysLogServer = $SysLogServer } if ($PSBoundParameters.ContainsKey('SysLogPort')) { $Global:SysLogPort = $SysLogPort } if ($PSBoundParameters.ContainsKey('LogFilePath')) { $Global:LogFilePath = $LogFilePath } if ($PSBoundParameters.ContainsKey('LogName')) { $Global:LogName = $LogName } if ($PSBoundParameters.ContainsKey('ScriptName')) { $Global:ScriptName = $ScriptName } if ($PSBoundParameters.ContainsKey('ScriptVersion')) { $Global:ScriptVersion = $ScriptVersion } if ($PSBoundParameters.ContainsKey('SendSyslog')) { $Global:SendSyslog = [System.Convert]::ToBoolean($SendSyslog) } if ($PSBoundParameters.ContainsKey('JsonLogging')) { $Global:JsonLogging = [System.Convert]::ToBoolean($JsonLogging) } if ($PSBoundParameters.ContainsKey('SysLogRFC')) { $Global:SyslogRFC = $SyslogRFC } else { $Global:SyslogRFC = 'RFC5424' } # Enforce log path existence if (-not [string]::IsNullOrWhiteSpace($Global:LogFilePath)) { if (-not (Test-Path -Path $Global:LogFilePath -PathType Container)) { try { New-Item -ItemType Directory -Path $Global:LogFilePath -Force | Out-Null Write-Host "Created missing log directory: $Global:LogFilePath" -ForegroundColor Yellow } catch { Write-Error "Failed to create log directory: $_" } } } else { throw "LogFilePath cannot be null or empty." } #Define maps globally, forgot to do this earlier. $global:severityMap = @{error = 3; warn = 4; success = 5; general = 6; debug = 7 }; $global:msgIdMap = @{ error='ERR'; warn='WRN'; success='SUC'; general='INF'; debug='DBG' }; Write-Host "Log configuration updated:" -ForegroundColor Green Write-Host "AppName: $Global:AppName" -ForegroundColor Cyan Write-Host "SysLogServer: $Global:SysLogServer" -ForegroundColor Cyan Write-Host "SysLogPort: $Global:SysLogPort" -ForegroundColor Cyan Write-Host "SysLogRFC: $Global:SyslogRFC" -ForegroundColor Cyan Write-Host "LogFilePath: $Global:LogFilePath" -ForegroundColor Cyan Write-Host "LogName: $Global:LogName" -ForegroundColor Cyan Write-Host "ScriptName: $Global:ScriptName" -ForegroundColor Cyan Write-Host "ScriptVersion: $Global:ScriptVersion" -ForegroundColor Cyan Write-Host "SendSyslog: $Global:SendSyslog" -ForegroundColor Cyan Write-Host "JsonLogging: $Global:JsonLogging" -ForegroundColor Cyan Write-Host "SeverityMap: $(($global:severityMap | ConvertTo-Json -Compress))" -ForegroundColor Cyan Write-Host "MessageIDMap: $(($global:msgIdMap | ConvertTo-Json -Compress))" -ForegroundColor Cyan } function New-StructuredSyslogData { Param ( [Parameter(Mandatory)] [string] $SDID, [Parameter(Mandatory)] [hashtable] $Params ) $pairs = $Params.GetEnumerator() | ForEach-Object { "{0}={1}" -f $_.Key, '"' + $_.Value + '"' } return "[$SDID {0}]" -f ($pairs -join ' ') } function Invoke-Syslog { Param ( [Parameter(Mandatory)] [string] $EndPoint, [Parameter(Mandatory)] [int] $Port, [Parameter(Mandatory)] [string] $Message, [ValidateRange(0,23)] [int] $Facility = 1, [ValidateRange(0,7)] [int] $Severity = 6, [string] $AppName = "swiftLogger", [string] $ProcID = $PID, [string] $MsgID = "PSLogging", [string] $StructuredData = "-", [ValidateSet('RFC5424','RFC3164')] [string] $RFC = $Global:SyslogRFC, [ValidateSet('UDP','TCP','TLS')] [string] $Transport = 'UDP', [bool]$IsJson = $false ) try { $PRI = ($Facility * 8) + $Severity $Hostname = [System.Net.Dns]::GetHostName() if ($RFC -eq 'RFC5424') { $Version = 1 $Timestamp = (Get-Date).ToString("yyyy-MM-ddTHH:mm:ssK") if ($IsJson -and $StructuredData -eq '-') { $StructuredData = '[json@32473 tag="json"]' } $SyslogMessage = "<$PRI>$Version $Timestamp $Hostname $AppName $ProcID $MsgID $StructuredData $Message" } else { $Timestamp = (Get-Date).ToString("MMM dd HH:mm:ss") $SyslogMessage = "<$PRI>$Timestamp $Hostname $($AppName): $Message" } $IP = [System.Net.Dns]::GetHostAddresses($EndPoint) | Where-Object { $_.AddressFamily -in @('InterNetwork','InterNetworkV6') } | Select-Object -First 1 if (-not $IP) { throw "Unable to resolve endpoint $EndPoint" } $EndPoints = New-Object System.Net.IPEndPoint($IP, $Port) $EncodedText = [Text.Encoding]::UTF8.GetBytes($SyslogMessage) $sent = $false switch ($Transport) { 'UDP' { try { $Socket = New-Object System.Net.Sockets.UdpClient [void]$Socket.Send($EncodedText, $EncodedText.Length, $EndPoints) $Socket.Dispose() $sent = $true } catch { Write-Verbose "UDP send failed: $_" } } 'TCP' { try { $Client = New-Object System.Net.Sockets.TcpClient($IP.ToString(), $Port) $Stream = $Client.GetStream() $Stream.Write($EncodedText, 0, $EncodedText.Length) $Stream.Dispose(); $Client.Dispose() $sent = $true } catch { Write-Verbose "TCP send failed: $_. Falling back to UDP." } } 'TLS' { try { $Client = New-Object System.Net.Sockets.TcpClient($IP.ToString(), $Port) $Stream = $Client.GetStream() $SslStream = New-Object System.Net.Security.SslStream($Stream, $false, ({$true})) $SslStream.AuthenticateAsClient($EndPoint) $SslStream.Write($EncodedText, 0, $EncodedText.Length) $SslStream.Dispose(); $Stream.Dispose(); $Client.Dispose() $sent = $true } catch { Write-Verbose "TLS send failed: $_. Falling back to UDP." } } } if (-not $sent) { try { $Socket = New-Object System.Net.Sockets.UdpClient [void]$Socket.Send($EncodedText, $EncodedText.Length, $EndPoints) $Socket.Dispose() Write-Verbose "Fallback to UDP successful." } catch { Write-Error "Fallback to UDP failed: $_" } } } catch { Write-Error "Failed to send syslog message: $_" } } function Write-Log { param( [Parameter(Mandatory=$true)] [String]$msg, [Parameter(Mandatory=$true)] [String]$type, [Parameter()] [bool]$SendSysLog = $false, [Parameter()] [bool]$OutputJson = $false ) # Apply global defaults if they exist, coercing to bool # Powershell hates boolian value conversions, and also hates me. if ($PSBoundParameters.ContainsKey('SendSysLog') -eq $false -and $null -ne $Global:SendSyslog) { $SendSysLog = [System.Convert]::ToBoolean($Global:SendSyslog) } if ($PSBoundParameters.ContainsKey('OutputJson') -eq $false -and $null -ne $Global:JsonLogging) { $OutputJson = [System.Convert]::ToBoolean($Global:JsonLogging) } $timestamp = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss") $logEntry = "$timestamp - [$type] - $msg" $logFile = "$Global:LogFilePath\$Global:LogName.log" $logFileJson = "$Global:LogFilePath\$Global:LogName.json" if ($OutputJson) { $jsonEntry = @{ timestamp=$timestamp; level=$type; message=$msg; appName=$Global:AppName; script=$Global:ScriptName; version=$Global:ScriptVersion; computerName=$env:ComputerName } | ConvertTo-Json -Compress } switch ($type.ToLower()) { "error" { Write-Host $logEntry -ForegroundColor DarkRed -BackgroundColor Red } "warn" { Write-Host $logEntry -ForegroundColor DarkYellow -BackgroundColor Yellow } "success" { Write-Host $logEntry -ForegroundColor DarkGreen -BackgroundColor Green } "general" { Write-Host $logEntry -ForegroundColor Black -BackgroundColor White } "debug" { Write-Host $logEntry -ForegroundColor DarkBlue -BackgroundColor Blue } default { Write-Host "Warning: Invalid log type." -ForegroundColor Yellow } } if ($OutputJson) { Add-Content -Path $logFileJson -Value $jsonEntry } else { Add-Content -Path $logFile -Value $logEntry } if ($SendSysLog) { $severity = $global:severityMap[$type.ToLower()]; if ($null -eq $severity) { $severity = 6 }; $msgId = $global:msgIdMap[$type.ToLower()]; if (-not $msgId) { $msgId = 'UNKNOWN' }; $syslogMessage = if ($OutputJson) { $jsonEntry } elseif ($Global:SyslogRFC -eq 'RFC3164') { "[$Global:AppName] [$Global:ScriptName] - [$type] - $msg" } else { "$msg" }; Invoke-Syslog -EndPoint $Global:SysLogServer -Port $Global:SysLogPort -Message $syslogMessage -Severity $severity -MsgID $msgId -AppName $Global:AppName -IsJson:$OutputJson -Verbose } } Export-ModuleMember -Function Invoke-Syslog, Write-Log, New-StructuredSyslogData, Set-LogConfiguration |