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 ) # Determine OS - compatible with both PowerShell 5.1 and 7+ $onWindows = if ($PSVersionTable.PSVersion.Major -ge 6) { $IsWindows } else { $env:OS -eq 'Windows_NT' } $onLinux = if ($PSVersionTable.PSVersion.Major -ge 6) { $IsLinux } else { $false # PowerShell 5.1 only runs on Windows } $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 { # Special handling for Security log - requires elevated privileges if ($source -eq "Security") { $currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent()) $isElevated = $currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) if (-not $isElevated) { Write-Warning "⚠️ Security log requires elevated privileges. Skipping Security log. Run PowerShell as Administrator to access Security logs." continue } } $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) { # Format the message to be more user-friendly $formattedMessage = try { Format-WindowsEventMessage -EventObject $evt } catch { # Fallback to original message with XML cleanup if ($evt.Message) { $evt.Message -replace '<[^>]+>', '' } else { "Event $($evt.Id) from $($evt.ProviderName)" } } $logs.Add([PSCustomObject]@{ TimeCreated = $evt.TimeCreated LevelDisplayName = $evt.LevelDisplayName ProviderName = $evt.ProviderName EventId = $evt.Id Message = $formattedMessage RawMessage = $evt.Message # Keep original for debugging }) } # Safe count handling to avoid "Count property not found" warnings $eventCount = if ($events) { if ($events -is [array]) { $events.Count } elseif ($events.Count) { $events.Count } else { 1 } } else { 0 } Write-Verbose "✅ Successfully retrieved $eventCount events from $source log" } catch { Write-Warning "⚠️ Get-WinEvent failed for $source`: $($_.Exception.Message)" Write-Verbose "Attempting fallback method for $source..." try { if ($source -in @("System", "Application", "Security")) { # Additional check for Security log in fallback if ($source -eq "Security") { $currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent()) $isElevated = $currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) if (-not $isElevated) { Write-Warning "⚠️ Security log fallback also requires elevated privileges. Skipping Security log." continue } } $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 }) } Write-Verbose "[OK] Fallback successful for $source - retrieved $($fallbackEvents.Count) events" } else { Write-Warning "[WARN] No fallback available for log source: $source" } } catch { Write-Warning "[WARN] Both primary and fallback methods failed for $source`: $($_.Exception.Message)" # Continue processing other log sources instead of failing completely } } } } catch { throw "[ERROR] 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 "[ERROR] 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 |