Public/Logging/Write-CustomLog.ps1
function Write-CustomLog { <# .SYNOPSIS Logs custom events to the Windows Event Log and optionally outputs to a JSON file. .DESCRIPTION This function writes events to the Windows Event Log using predefined or custom event details. It also supports logging to a JSON file. The function supports custom logging categories, severities, and allows sensitive information to be masked. File logging can be customized for size and retention. .PARAMETER EventInfo Predefined event details, such as EventID, EventName, and Category, provided as a structured object. .PARAMETER CustomEventId The custom event ID for custom event logs. .PARAMETER EventName The name of the custom event being logged. .PARAMETER EventCategory Specifies the category of the event. .PARAMETER Message The log message that will be written. Sensitive information will be masked if necessary. .PARAMETER CustomSeverity The severity level of the event (e.g., Information, Warning, Error, etc.). .PARAMETER LogAsJson Switch to indicate if the log should be written to a JSON file. .PARAMETER MaximumKilobytes The maximum size in kilobytes for the event log. Default is 16 MB. .PARAMETER RetentionDays The number of days the logs should be retained. Default is 30 days. .PARAMETER LogPath The directory path where the JSON log files should be saved. .EXAMPLE Write-CustomLog -EventInfo ([EventIDs]::SlowPerformance) -Message 'Old hardware.' -Verbose Use the pre-defined events ([EventIDs]) and corresponding Message string. Where "EventInfo" is defined as [EventIDs] Class with pre-defined values as: EventID = ID of the event as enum [EventID] Name = Name of the event Description = Description of the event EventCategory = Category of the event as enum [EventCategory]. This is only working if a compiled DLL exist. EventSeverity = Severity of the event as enum [EventSeverity] .EXAMPLE Write-CustomLog -CustomEventId ([EventID]::LowDiskSpace) ` -EventName "LowDiskSpace" ` -EventCategory SystemHealth ` -Message "Low disk space detected on C: drive. Free space below 10%." ` -CustomSeverity Warning -Verbose We create the event to log by providing the required parameters. .NOTES Ensure necessary event types (EventIDs, EventCategory, etc.) are defined on Class.Events.ps1 file located under Classes folder. This file is written in C# (CSharp) language and compiled in runtime when module is imported. This is due visibility and compatibility issues on modules when using just PowerShell code. .NOTES Used Functions: Name | Module ------------------------------|-------------------------- Remove-SensitiveData | EguibarIT Initialize-EventLogging | EguibarIT Write-EventLog | Microsoft.PowerShell.Management Write-Error | Microsoft.PowerShell.Utility.Activities Write-Verbose | Microsoft.PowerShell.Utility.Activities Write-Warning | Microsoft.PowerShell.Utility.Activities #> [CmdletBinding(SupportsShouldProcess = $true, DefaultParameterSetName = 'Predefined')] [OutputType([void])] param( [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ValueFromRemainingArguments = $true, HelpMessage = 'Default Event Information to be used.', Position = 0, ParameterSetName = 'Predefined')] [ValidateNotNullOrEmpty()] [EventIDInfo] $EventInfo, [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ValueFromRemainingArguments = $true, HelpMessage = 'Integer representing the Event ID.', Position = 1, ParameterSetName = 'Custom')] [ValidateRange(1000, 65535)] # assuming a valid custom event ID range [int] $CustomEventId, [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ValueFromRemainingArguments = $true, HelpMessage = 'Name of the event.', Position = 2, ParameterSetName = 'Custom')] [string] $EventName, [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ValueFromRemainingArguments = $true, HelpMessage = 'Category assigned to the event.', Position = 3, ParameterSetName = 'Custom')] [ValidateScript({ [Enum]::IsDefined([EventCategory], $_) })] [EventCategory] $EventCategory, [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ValueFromRemainingArguments = $true, HelpMessage = 'Message of the event.', Position = 4)] [ValidateLength(1, 2048)] [string] $Message, [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ValueFromRemainingArguments = $true, HelpMessage = 'Severity assigned to the event.', Position = 5, ParameterSetName = 'Custom')] #[ValidateSet('Information', 'Warning', 'Error', 'SuccessAudit', 'FailureAudit')] [ValidateScript({ [Enum]::IsDefined([EventSeverity], $_) })] [EventSeverity] $CustomSeverity, [Parameter(ParameterSetName = 'JsonLogging')] [switch] $LogAsJson, [Parameter(ParameterSetName = 'EventLogging')] [int] $MaximumKilobytes = 16384, # default 16 MB [Parameter(ParameterSetName = 'EventLogging')] [int] $RetentionDays = 30, # default 30 days [Parameter(ParameterSetName = 'JsonLogging')] [ValidateScript({ Test-Path $_ -PathType 'Container' })] # Validate directory [string] $LogPath = 'C:\Logs', [Parameter(ParameterSetName = 'JsonLogging')] [string] $JsonLogName = 'CustomLog', [Parameter(ParameterSetName = 'JsonLogging')] [int] $JsonMaxFileSizeMB = 10 ) Begin { $ErrorActionPreference = 'Stop' # Mask sensitive data #$maskedMessage = Remove-SensitiveData -Message $Message $maskedMessage = $Message # Initialize event logging Initialize-EventLogging -MaximumKilobytes $MaximumKilobytes -RetentionDays $RetentionDays if ($PSCmdlet.ParameterSetName -eq 'Custom') { $eventId = $CustomEventId $eventName = $EventName $eventCategory = $EventCategory $severity = $CustomSeverity } else { $eventId = $EventInfo.ID $eventName = $EventInfo.Name $eventCategory = $EventInfo.Category $severity = $EventInfo.DefaultSeverity } #end If-Else $entryType = switch ($severity) { 'Information' { [System.Diagnostics.EventLogEntryType]::Information } 'Warning' { [System.Diagnostics.EventLogEntryType]::Warning } 'Error' { [System.Diagnostics.EventLogEntryType]::Error } 'SuccessAudit' { [System.Diagnostics.EventLogEntryType]::SuccessAudit } 'FailureAudit' { [System.Diagnostics.EventLogEntryType]::FailureAudit } } $sb = [System.Text.StringBuilder]::new() $sb.AppendLine("Event : $eventName") | Out-Null $sb.AppendLine("Event Category : $eventCategory") | Out-Null $sb.AppendLine("Details : $maskedMessage") | Out-Null } #end Begin Process { if ($PSCmdlet.ShouldProcess("Logging event: $eventName with severity $severity")) { try { # Write to Windows Event Log # LogName and Source are defined on $Variables which is initialized when module is imported. $Splat = @{ LogName = $Variables.LogConfig.LogName Source = $Variables.LogConfig.Source EntryType = $entryType EventId = $eventId Category = [int]([Enum]::Parse([EventCategory], $eventCategory)) # Convert EventCategory to int Message = $sb.ToString() } Write-EventLog @Splat # https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.eventlog.writeentry?view=net-8.0 <# [System.Diagnostics.EventLog]::WriteEntry (string source, string message, System.Diagnostics.EventLogEntryType type, int eventID, short category, byte[] rawData) $params = @( $Source, $Message, $eventType, $EventId ) [System.Diagnostics.EventLog]::WriteEntry($params) #> # Log to JSON if ($LogAsJson) { $logObject = [PSCustomObject]@{ EventID = $eventId Name = $eventName Category = $eventCategory Severity = $severity Message = $maskedMessage Timestamp = (Get-Date).ToString('o') AdditionalData = @{ # Add any additional structured data here MachineName = $env:COMPUTERNAME UserName = $env:USERNAME } } $jsonFile = Join-Path $LogPath "$JsonLogName.json" # Ensure directory exists if (-not (Test-Path -Path $LogPath)) { New-Item -ItemType Directory -Force -Path $LogPath | Out-Null } # Check file size and rotate if necessary if (Test-Path $jsonFile) { $fileInfo = Get-Item $jsonFile if ($fileInfo.Length / 1MB -ge $JsonMaxFileSizeMB) { $backupFile = Join-Path $LogPath "$JsonLogName-$(Get-Date -Format 'yyyyMMddHHmmss').json" Move-Item $jsonFile $backupFile } } $logObject | ConvertTo-Json | Out-File -FilePath $jsonFile -Append Write-Verbose -Message ('Event {0} was logged successfully to JSON.' -f $eventName) } #end If Write-Verbose -Message (' Event {0} with ID {1} was logged successfully to the event log.' -f $eventName, $eventId ) } catch { Write-Error -Message (' An error occurred while logging the event. Exception: {0} Full details: {1}' -f $_.Exception.Message, $_ ) #Get-ErrorDetail -ErrorRecord $_ throw } #end Try-Catch } #end If } #end Process End { Write-Verbose -Message 'Logging process completed.' } #end End } #end Function |