Public/Get-SystemLogs.ps1

function Get-SystemLogs {
    [CmdletBinding()]
    param (
        [ValidateSet("System", "Application", "Security", "All", "Custom")]
        [string]$LogType = "System",

        [datetime]$StartTime = (Get-Date).AddHours(-1),
        [datetime]$EndTime   = (Get-Date),

        [string]$CustomPath,

        [switch]$Colorize,
        [switch]$AttentionOnly,

        [string]$OutputPath
    )

    $onWindows = $PSVersionTable.OS -match 'Windows'
    $onLinux   = $PSVersionTable.OS -match 'Linux'
    $logs = [System.Collections.Generic.List[object]]::new()

    if ($onWindows) {
        try {
            $logSources = switch ($LogType) {
                "All"    { @("System", "Application", "Security") }
                "Custom" { if ($CustomPath -and (Test-Path $CustomPath)) { @($CustomPath) } else { throw "❌ CustomPath invalid or missing." } }
                default  { @($LogType) }
            }

            foreach ($source in $logSources) {
                try {
                    $events = if ($LogType -eq "Custom") {
                        Get-WinEvent -Path $source -ErrorAction Stop
                    } else {
                        Get-WinEvent -FilterHashtable @{
                            LogName   = $source
                            StartTime = $StartTime
                            EndTime   = $EndTime
                        } -ErrorAction Stop
                    }

                    foreach ($evt in $events) {
                        $logs.Add([PSCustomObject]@{
                            TimeCreated      = $evt.TimeCreated
                            LevelDisplayName = $evt.LevelDisplayName
                            ProviderName     = $evt.ProviderName
                            EventId          = $evt.Id
                            Message          = $evt.Message
                        })
                    }
                } catch {
                    Write-Warning "⚠️ Get-WinEvent failed for $source, trying fallback..."

                    if ($source -in @("System", "Application", "Security")) {
                        $fallbackEvents = Get-EventLog -LogName $source -After $StartTime -Before $EndTime -ErrorAction Stop
                        foreach ($evt in $fallbackEvents) {
                            $logs.Add([PSCustomObject]@{
                                TimeCreated      = $evt.TimeGenerated
                                LevelDisplayName = $evt.EntryType.ToString()
                                ProviderName     = $evt.Source
                                EventId          = $evt.InstanceId
                                Message          = $evt.Message
                            })
                        }
                    } else {
                        throw "❌ Unsupported log source or fallback failed: $source"
                    }
                }
            }
        } catch {
            throw "❌ Failed to retrieve Windows logs: $_"
        }

    } elseif ($onLinux) {
        $journalOk = $false
        try {
            $cmd = @("journalctl", "--no-pager", "--output=json")
            if ($StartTime) { $cmd += "--since=$($StartTime.ToString("yyyy-MM-dd HH:mm:ss"))" }
            if ($EndTime)   { $cmd += "--until=$($EndTime.ToString("yyyy-MM-dd HH:mm:ss"))" }

            $journalOutput = & $cmd

            $epoch = Get-Date "1970-01-01T00:00:00Z"
            $priorityMap = @{
                0 = 'Emergency'; 1 = 'Alert'; 2 = 'Critical'; 3 = 'Error'
                4 = 'Warning';   5 = 'Notice'; 6 = 'Info';    7 = 'Debug'
            }

            foreach ($line in $journalOutput) {
                if ($line.Trim()) {
                    try {
                        $obj = $line | ConvertFrom-Json
                        $level = if ($priorityMap.ContainsKey($obj.PRIORITY)) { $priorityMap[$obj.PRIORITY] } else { "Unknown" }

                        $timestamp = $epoch.AddMilliseconds($obj.__REALTIME_TIMESTAMP / 1000)

                        $logs.Add([PSCustomObject]@{
                            TimeCreated      = $timestamp
                            LevelDisplayName = $level
                            ProviderName     = $obj.SYSLOG_IDENTIFIER
                            EventId          = $null
                            Message          = $obj.MESSAGE
                        })
                    } catch {
                        continue
                    }
                }
            }

            $journalOk = $true
        } catch {
            Write-Warning "⚠️ journalctl not available or failed: $_"
        }

        if (-not $journalOk) {
            $fallbackFiles = @("/var/log/syslog", "/var/log/messages", "/var/log/auth.log")
            foreach ($file in $fallbackFiles) {
                if (Test-Path $file) {
                    Get-Content -Path $file | ForEach-Object {
                        $line = $_
                        $regex = '^(?<month>\w{3})\s+(?<day>\d{1,2})\s+(?<time>\d{2}:\d{2}:\d{2})'
                        $timestamp = if ($line -match $regex) {
                            $month = $matches['month']
                            $day   = [int]$matches['day']
                            $time  = $matches['time']
                            $year  = (Get-Date).Year
                            [datetime]::ParseExact("$month $day $year $time", "MMM d yyyy HH:mm:ss", $null)
                        } else {
                            Get-Date
                        }

                        $level = if ($line -match "(?i)error") { "Error" }
                                 elseif ($line -match "(?i)warn") { "Warning" }
                                 elseif ($line -match "(?i)info") { "Information" }
                                 else { "Unknown" }

                        $logs.Add([PSCustomObject]@{
                            TimeCreated      = $timestamp
                            LevelDisplayName = $level
                            ProviderName     = "syslog"
                            EventId          = $null
                            Message          = $line
                        })
                    }
                }
            }
        }

    } else {
        throw "❌ Unsupported operating system."
    }

    if ($AttentionOnly) {
        $attentionPattern = '(?i)error|warn|critical|fail|denied|unauthorized|security'
        $logs = $logs | Where-Object {
            "$($_.LevelDisplayName) $($_.Message)" -match $attentionPattern
        }
    }

    if ($Colorize) {
        foreach ($log in $logs) {
            $color = switch -Regex ($log.LevelDisplayName) {
                'Error'      { 'Red' }
                'Warning'    { 'Yellow' }
                'Information' { 'Green' }
                default      { 'White' }
            }
            Write-Host ("[{0}] {1}: {2}" -f $log.TimeCreated, $log.LevelDisplayName, $log.Message) -ForegroundColor $color
        }
    }

    if ($OutputPath) {
        try {
            $logs | Export-Csv -Path $OutputPath -NoTypeInformation -Encoding UTF8 -Force
            Write-Verbose "✅ Logs exported to $OutputPath"
        } catch {
            Write-Warning "❌ Failed to export logs: $_"
        }
    }

    return $logs
}

Export-ModuleMember -Function Get-SystemLogs