Skip to content

Instantly share code, notes, and snippets.

@Tomamais
Created October 31, 2025 19:49
Show Gist options
  • Select an option

  • Save Tomamais/863253bf6b5ca9ec0500e6db54bfeea2 to your computer and use it in GitHub Desktop.

Select an option

Save Tomamais/863253bf6b5ca9ec0500e6db54bfeea2 to your computer and use it in GitHub Desktop.
<#
.SYNOPSIS
Reads a tab-separated .NET Core log file, displays it in a custom Windows Forms DataGridView,
and provides a refresh button. Rows are color-coded based on the LogLevel.
.NOTES
This script requires a Windows OS environment to load the System.Windows.Forms and System.Drawing assemblies.
#>
param(
[Parameter(Mandatory=$true)]
[string]$LogFilePath
)
# --- 1. Load Assemblies ---
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
Add-Type -AssemblyName System.Data # Needed for DataTable used for DataGridView source
# --- 2. Helper Function: Read and Structure the Tab-Separated Log ---
function Get-TabDelimitedLogs {
param(
[string]$Path
)
try {
# Define the exact headers to match the log format
$Headers = "Timestamp", "HostName", "AppName", "LogLevel", "UserName", "Category", "Message"
# Read the content and convert it into structured objects
$LogObjects = Get-Content -Path $Path |
ConvertFrom-Csv -Delimiter "`t" -Header $Headers
# Process the objects: Convert Timestamp to proper DateTime for sorting
$ProcessedLogs = $LogObjects | Select-Object `
@{Name='DateTime'; Expression={[datetime]::ParseExact($_.Timestamp, "yyyy-MM-dd h:mm:ss tt", [System.Globalization.CultureInfo]::InvariantCulture)}}, `
"HostName", "AppName", "LogLevel", "UserName", "Category", "Message"
# Sort the objects by the new DateTime column (most recent first)
return $ProcessedLogs | Sort-Object -Property DateTime -Descending
} catch {
Write-Error "Error parsing log file: $($_.Exception.Message)"
Write-Host "Please ensure the log format and path are correct." -ForegroundColor Red
return @()
}
}
# --- 3. Core Function: Refresh Data and Apply Coloring/Sorting ---
function Refresh-DataGridView {
param(
[Parameter(Mandatory=$true)]
[System.Windows.Forms.DataGridView]$DataGridView,
[Parameter(Mandatory=$true)]
[string]$Path
)
Write-Host "Refreshing data from $Path..." -ForegroundColor Yellow
# Load the structured log objects
$Logs = Get-TabDelimitedLogs -Path $Path
if (-not $Logs) {
$DataGridView.DataSource = $null
return
}
# Convert PowerShell objects to a DataTable for better performance and DataGridView binding
# This step is critical for enabling native filtering and sorting within the DataGridView control.
$DataTable = New-Object System.Data.DataTable
# Define columns based on the processed object properties
$Logs[0].psobject.Properties | ForEach-Object {
$DataTable.Columns.Add($_.Name, [System.Type]::GetType("System.String"))
}
# Fix the DateTime column to be a DateTime type for native sorting
$DataTable.Columns["DateTime"].DataType = [System.DateTime]
# Populate the DataTable
foreach ($Log in $Logs) {
$row = $DataTable.NewRow()
$Log.psobject.Properties | ForEach-Object {
$row.$($_.Name) = $_.Value
}
$DataTable.Rows.Add($row)
}
# Bind the DataTable to the DataGridView
$DataGridView.DataSource = $DataTable
# Configure the DataGridView for user interaction
$DataGridView.AllowUserToOrderColumns = $true
$DataGridView.SelectionMode = [System.Windows.Forms.DataGridViewSelectionMode]::FullRowSelect
$DataGridView.ReadOnly = $true
$DataGridView.AutoSizeColumnsMode = [System.Windows.Forms.DataGridViewAutoSizeColumnsMode]::Fill
$DataGridView.Columns["DateTime"].DefaultCellStyle.Format = "yyyy-MM-dd HH:mm:ss.fff"
Write-Host "Refresh complete. $($Logs.Count) entries loaded." -ForegroundColor Green
}
# --- 4. Event Handler for Conditional Coloring ---
# This function is executed for every cell being drawn in the DataGridView
function LogLevel_CellFormatting {
param(
[Parameter(Mandatory=$true)]$sender,
[Parameter(Mandatory=$true)]$e
)
# Check if we are formatting the LogLevel column
if ($sender.Columns[$e.ColumnIndex].Name -eq "LogLevel") {
# Get the value of the LogLevel cell
$LogLevel = $e.Value.ToString().ToUpper()
# Apply coloring based on the log level
switch ($LogLevel) {
"ERROR" {
$e.CellStyle.BackColor = [System.Drawing.Color]::FromArgb(255, 200, 200) # Light Red
$e.CellStyle.ForeColor = [System.Drawing.Color]::Black
}
"CRITICAL" {
$e.CellStyle.BackColor = [System.Drawing.Color]::Red
$e.CellStyle.ForeColor = [System.Drawing.Color]::White
}
"WARNING" {
$e.CellStyle.BackColor = [System.Drawing.Color]::LightYellow
$e.CellStyle.ForeColor = [System.Drawing.Color]::DarkGoldenrod
}
"DEBUG" {
$e.CellStyle.BackColor = [System.Drawing.Color]::LightGray
$e.CellStyle.ForeColor = [System.Drawing.Color]::Black
}
"INFORMATION" {
# Keep default or set a light background
$e.CellStyle.BackColor = [System.Drawing.Color]::White
}
}
}
}
# --- 5. Build and Show the Windows Form GUI ---
# Create the main form
$form = New-Object System.Windows.Forms.Form
$form.Text = "Custom .NET Core Log Viewer - $LogFilePath"
$form.Size = New-Object System.Drawing.Size(1200, 800)
$form.StartPosition = [System.Windows.Forms.FormStartPosition]::CenterScreen
$form.WindowState = [System.Windows.Forms.FormWindowState]::Maximized
# Create the DataGridView
$dgv = New-Object System.Windows.Forms.DataGridView
$dgv.Location = New-Object System.Drawing.Point(10, 50)
$dgv.Size = New-Object System.Drawing.Size(1160, 680) # Initial size, will be anchored
$dgv.Anchor = [System.Windows.Forms.AnchorStyles]::Bottom -bor [System.Windows.Forms.AnchorStyles]::Right -bor [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left
# Hook up the cell formatting event handler
$dgv.Add_CellFormatting({ LogLevel_CellFormatting -sender $dgv -e $_ })
# Create the Refresh Button
$btnRefresh = New-Object System.Windows.Forms.Button
$btnRefresh.Text = "Refresh Logs"
$btnRefresh.Location = New-Object System.Drawing.Point(10, 10)
$btnRefresh.Size = New-Object System.Drawing.Size(150, 30)
$btnRefresh.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left
# Set the button click action to call the Refresh function
$btnRefresh.Add_Click({ Refresh-DataGridView -DataGridView $dgv -Path $LogFilePath })
# Add controls to the form
$form.Controls.Add($dgv)
$form.Controls.Add($btnRefresh)
# Load data on initial startup
Refresh-DataGridView -DataGridView $dgv -Path $LogFilePath
# Show the form and start the PowerShell application message pump
$form.ShowDialog() | Out-Null
```
### 💡 How to Use This Advanced Script
1. **Save the Script:** Save the code block above as `CustomLogViewer.ps1`.
2. **Ensure Log Format is Correct:** Your log file **must** be tab-separated and follow this header order:
```
2025-10-31 3:00:00 PM<TAB>HostName<TAB>AppName<TAB>LogLevel<TAB>UserName<TAB>Category<TAB>Message
```
*If your log file does not have this header line, the script assumes the first line is data.*
3. **Run the Script:** Open a PowerShell console and run it, specifying the path:
```powershell
.\CustomLogViewer.ps1 -LogFilePath "C:\path\to\your\log.txt"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment