Public/Get-WinslopFixLog.ps1

function Get-WinslopFixLog {
    <#
    .SYNOPSIS
        Retrieves WinslopFix event log entries with structured output.

    .DESCRIPTION
        Queries the Windows Application Event Log for entries written by the
        WinslopFix source. Returns structured objects with parsed metadata
        for easy filtering and pipeline consumption.

        Supports filtering by category (Kills, Errors, Lifecycle) and by
        date range.

    .PARAMETER Newest
        Maximum number of entries to return. Default: 50.

    .PARAMETER Category
        Filter by event category based on EventID ranges:
        - All: All events
        - Lifecycle: Guard start/stop/config (1000-1999)
        - Detection: Process detection events (2000-2999)
        - Kills: Process termination events (3000-3999)
        - Policy: AI feature changes (4000-4999)
        - Errors: Error events (9000-9999)

    .PARAMETER After
        Only return events generated after this datetime.

    .EXAMPLE
        Get-WinslopFixLog

        Returns the 50 most recent WinslopFix events.

    .EXAMPLE
        Get-WinslopFixLog -Category Kills -Newest 10

        Returns the 10 most recent process termination events.

    .EXAMPLE
        Get-WinslopFixLog -After (Get-Date).AddHours(-1)

        Returns all events from the last hour.

    .OUTPUTS
        PSCustomObject[]

    .LINK
        https://github.com/DailenG/WinslopFix
    #>

    [CmdletBinding()]
    [OutputType([PSCustomObject[]])]
    param(
        [Parameter()]
        [ValidateRange(1, 1000)]
        [int]$Newest = 50,

        [Parameter()]
        [ValidateSet('All', 'Lifecycle', 'Detection', 'Kills', 'Policy', 'Errors')]
        [string]$Category = 'All',

        [Parameter()]
        [datetime]$After
    )

    process {
        $source = 'WinslopFix'

        # Check if the source exists to avoid noisy errors
        if (-not [System.Diagnostics.EventLog]::SourceExists($source)) {
            Write-Warning "Event log source '$source' is not registered. No logs to display. Run Install-WinslopFix to register the source."
            return
        }

        try {
            $entries = Get-EventLog -LogName Application -Source $source `
                -Newest ($Newest * 3) -ErrorAction Stop  # Over-fetch to allow category filtering
        }
        catch {
            Write-Warning "Failed to query event log: $($_.Exception.Message)"
            return
        }

        # Apply date filter
        if ($PSBoundParameters.ContainsKey('After')) {
            $entries = $entries | Where-Object { $_.TimeGenerated -gt $After }
        }

        # Define category ranges
        $categoryRanges = @{
            Lifecycle = @{ Min = 1000; Max = 1999 }
            Detection = @{ Min = 2000; Max = 2999 }
            Kills     = @{ Min = 3000; Max = 3999 }
            Policy    = @{ Min = 4000; Max = 4999 }
            Errors    = @{ Min = 9000; Max = 9999 }
        }

        # Apply category filter
        if ($Category -ne 'All' -and $categoryRanges.ContainsKey($Category)) {
            $range   = $categoryRanges[$Category]
            $entries = $entries | Where-Object {
                $_.EventID -ge $range.Min -and $_.EventID -le $range.Max
            }
        }

        # Take the requested count after filtering
        $entries = $entries | Select-Object -First $Newest

        # Return structured objects
        foreach ($entry in $entries) {
            $resolvedCategory = 'Unknown'
            foreach ($cat in $categoryRanges.Keys) {
                $r = $categoryRanges[$cat]
                if ($entry.EventID -ge $r.Min -and $entry.EventID -le $r.Max) {
                    $resolvedCategory = $cat
                    break
                }
            }

            [PSCustomObject]@{
                PSTypeName = 'WinslopFix.LogEntry'
                Timestamp  = $entry.TimeGenerated
                EventId    = $entry.EventID
                Category   = $resolvedCategory
                EntryType  = $entry.EntryType.ToString()
                Message    = $entry.Message
            }
        }
    }
}