Private/Logging.ps1

#Requires -Version 5.1

<#
.SYNOPSIS
    Logging functions for DriverManagement module
.DESCRIPTION
    Provides dual logging to Windows Event Log and structured JSON files
#>


function Initialize-DriverManagementLogging {
    <#
    .SYNOPSIS
        Initializes the logging infrastructure
    .DESCRIPTION
        Creates the Event Log source and log directory if they don't exist
    .EXAMPLE
        Initialize-DriverManagementLogging
    #>

    [CmdletBinding()]
    param()
    
    if ($script:LoggingInitialized) {
        return
    }
    
    $config = $script:ModuleConfig
    
    # Create Event Log source if it doesn't exist
    try {
        if (-not [System.Diagnostics.EventLog]::SourceExists($config.EventLogSource)) {
            # Check if we have admin rights
            $isAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
            
            if ($isAdmin) {
                New-EventLog -LogName $config.EventLogName -Source $config.EventLogSource -ErrorAction Stop
                Limit-EventLog -LogName $config.EventLogName -MaximumSize 100MB -OverflowAction OverwriteOlder -ErrorAction SilentlyContinue
            }
            else {
                Write-Verbose "Skipping Event Log creation - requires elevation"
            }
        }
    }
    catch {
        Write-Verbose "Could not create Event Log source: $($_.Exception.Message)"
    }
    
    # Ensure log directory exists
    $logDir = Split-Path $config.LogPath -Parent
    if (-not (Test-Path $config.LogPath)) {
        New-Item -Path $config.LogPath -ItemType Directory -Force | Out-Null
    }
    
    $script:LoggingInitialized = $true
}

function Write-DriverLog {
    <#
    .SYNOPSIS
        Writes a log entry to Event Log and JSON file
    .DESCRIPTION
        Dual-output logging with structured data support
    .PARAMETER Message
        The log message
    .PARAMETER Severity
        Log severity: Debug, Info, Warning, Error
    .PARAMETER Component
        Component name for categorization
    .PARAMETER Context
        Additional structured data to include
    .EXAMPLE
        Write-DriverLog -Message "Starting update" -Severity Info -Context @{Model = "Precision 5690"}
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory, Position = 0)]
        [string]$Message,
        
        [Parameter()]
        [ValidateSet('Debug', 'Info', 'Warning', 'Error')]
        [string]$Severity = 'Info',
        
        [Parameter()]
        [string]$Component = 'DriverManagement',
        
        [Parameter()]
        [hashtable]$Context = @{}
    )
    
    # Ensure logging is initialized
    if (-not $script:LoggingInitialized) {
        Initialize-DriverManagementLogging
    }
    
    $config = $script:ModuleConfig
    
    # Create log entry object
    $logEntry = [DriverLogEntry]::new($Message, $Severity)
    $logEntry.Component = $Component
    $logEntry.CorrelationId = $script:CorrelationId
    $logEntry.Context = $Context
    
    # Console output based on severity
    switch ($Severity) {
        'Debug'   { Write-Debug "[$Severity] $Message" }
        'Info'    { Write-Verbose "[$Severity] $Message" }
        'Warning' { Write-Warning $Message }
        'Error'   { Write-Error $Message -ErrorAction Continue }
    }
    
    # Event Log output
    try {
        $eventIdMap = @{ 'Debug' = 1000; 'Info' = 1001; 'Warning' = 2001; 'Error' = 3001 }
        $entryTypeMap = @{ 'Debug' = 'Information'; 'Info' = 'Information'; 'Warning' = 'Warning'; 'Error' = 'Error' }
        
        $eventMessage = @"
$Message

Component: $Component
CorrelationId: $($script:CorrelationId)
Context: $($Context | ConvertTo-Json -Compress -Depth 3)
"@

        
        if ([System.Diagnostics.EventLog]::SourceExists($config.EventLogSource)) {
            Write-EventLog -LogName $config.EventLogName `
                           -Source $config.EventLogSource `
                           -EventId $eventIdMap[$Severity] `
                           -EntryType $entryTypeMap[$Severity] `
                           -Message $eventMessage `
                           -ErrorAction SilentlyContinue
        }
    }
    catch {
        # Silently continue if Event Log write fails
    }
    
    # JSON file output
    try {
        $dateStamp = Get-Date -Format "yyyyMMdd"
        $logFile = Join-Path $config.LogPath "DriverManagement_$dateStamp.json"
        
        # Rotate log if > MaxLogSizeMB
        if ((Test-Path $logFile) -and ((Get-Item $logFile).Length / 1MB) -gt $config.MaxLogSizeMB) {
            $archiveName = "DriverManagement_${dateStamp}_$(Get-Date -Format 'HHmmss').json"
            Rename-Item $logFile (Join-Path $config.LogPath $archiveName) -ErrorAction SilentlyContinue
        }
        
        $logEntry.ToJson() | Add-Content -Path $logFile -Encoding UTF8 -ErrorAction SilentlyContinue
    }
    catch {
        # Silently continue if file write fails
    }
    
    # Cleanup old logs
    try {
        Get-ChildItem $config.LogPath -Filter "DriverManagement_*.json" -ErrorAction SilentlyContinue |
            Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-$config.MaxLogAgeDays) } |
            Remove-Item -Force -ErrorAction SilentlyContinue
    }
    catch {
        # Silently continue
    }
}

function Get-DriverManagementLogs {
    <#
    .SYNOPSIS
        Retrieves driver management logs
    .DESCRIPTION
        Gets logs from JSON files with optional filtering
    .PARAMETER StartDate
        Filter logs after this date
    .PARAMETER EndDate
        Filter logs before this date
    .PARAMETER Severity
        Filter by severity level
    .PARAMETER Last
        Get last N log entries
    .EXAMPLE
        Get-DriverManagementLogs -Last 50 -Severity Error
    #>

    [CmdletBinding()]
    param(
        [Parameter()]
        [datetime]$StartDate,
        
        [Parameter()]
        [datetime]$EndDate,
        
        [Parameter()]
        [ValidateSet('Debug', 'Info', 'Warning', 'Error')]
        [string[]]$Severity,
        
        [Parameter()]
        [int]$Last = 100
    )
    
    $config = $script:ModuleConfig
    $logs = @()
    
    $logFiles = Get-ChildItem $config.LogPath -Filter "DriverManagement_*.json" -ErrorAction SilentlyContinue |
        Sort-Object LastWriteTime -Descending
    
    foreach ($file in $logFiles) {
        $content = Get-Content $file.FullName -Raw -ErrorAction SilentlyContinue
        if ($content) {
            $entries = $content -split "`n" | Where-Object { $_ } | ForEach-Object {
                try { $_ | ConvertFrom-Json } catch { }
            }
            $logs += $entries
        }
        
        if ($logs.Count -ge $Last * 2) { break }  # Get enough for filtering
    }
    
    # Apply filters
    if ($StartDate) {
        $logs = $logs | Where-Object { [datetime]$_.Timestamp -ge $StartDate }
    }
    if ($EndDate) {
        $logs = $logs | Where-Object { [datetime]$_.Timestamp -le $EndDate }
    }
    if ($Severity) {
        $logs = $logs | Where-Object { $_.Severity -in $Severity }
    }
    
    return $logs | Select-Object -Last $Last
}