PowerShell Scrivere-Log: Un Semplice Tutorial sulla Funzione di Logging

Se stai scrivendo script PowerShell che fanno qualcosa di significativo, hai bisogno di logging. Sia che tu stia distribuendo software, gestendo servizi o automatizzando compiti, avere un registro di ciò che il tuo script ha fatto (o non ha fatto) è cruciale. In questo tutorial, imparerai come creare una semplice ma efficace funzione di logging in PowerShell.

Prerequisiti

Se desideri seguire questo tutorial, assicurati di avere:

  • Windows 10 o Windows Server con PowerShell 5.1 o PowerShell 7+
  • Un editor di testo (si consiglia VSCode)
  • Una comprensione di base delle funzioni di PowerShell

Il Problema con il Logging di Base

Supponiamo che tu stia scrivendo uno script per installare silenziosamente del software. L’approccio di base potrebbe apparire così:

Add-Content -Path "C:\\Scripts\\install.log" -Value "Starting install..."
Start-Process -FilePath 'installer.exe' -ArgumentList '/i /s' -Wait -NoNewWindow
Add-Content -Path "C:\\Scripts\\install.log" -Value "Finished install."

Funziona, ma ha alcuni problemi:

  • Nessun timestamp
  • Codice ripetitivo
  • Formato di logging incoerente
  • Percorso di log hard-coded

Risolveremo questi problemi costruendo una funzione di logging adeguata.

Costruire una Funzione Write-Log di Base

Prima di tutto, creiamo una semplice funzione che aggiunge timestamp alle nostre voci di log:

function Write-Log {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$Message
    )

    $timeGenerated = Get-Date -Format HH:mm:ss
    Add-Content -Path "C:\\Scripts\\script.log" -Value "$timeGenerated - $Message"
}

Ora puoi usarla in questo modo:

Write-Log -Message "Starting install..."
Start-Process -FilePath 'installer.exe' -ArgumentList '/i /s' -Wait -NoNewWindow
Write-Log -Message "Finished install."

Il file di log (C:\Scripts\script.log) conterrà voci che appariranno così:

09:42:15 - Starting install...
09:43:22 - Finished install.

Molto più pulito! Ma possiamo fare di meglio.

Aggiungere Maggiore Funzionalità

Miglioriamo la nostra funzione di logging con alcune funzionalità utili:

  • Percorsi di log personalizzati
  • Diversi livelli di log (Info, Avviso, Errore)
  • Data nel nome del file
  • Gestione degli errori

Ecco la versione migliorata:

function Write-Log {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$Message,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string]$LogFilePath = "C:\\Scripts\\Logs",

        [Parameter()]
        [ValidateSet('Information','Warning','Error')]
        [string]$Level = "Information"
    )

    # Create the log directory if it doesn't exist
    if (!(Test-Path $LogFilePath)) {
        New-Item -Path $LogFilePath -ItemType Directory -Force | Out-Null
    }

    # Build the log file path with date
    $date = Get-Date -Format "MM-dd-yyyy"
    $logFile = Join-Path $LogFilePath "log-$date.txt"

    # Get the current timestamp
    $timeStamp = Get-Date -Format "HH:mm:ss"

    # Create the log entry
    $logEntry = "$timeStamp [$Level] - $Message"

    try {
        Add-Content -Path $logFile -Value $logEntry -ErrorAction Stop
    }
    catch {
        Write-Error "Failed to write to log file: $_"
    }
}

Questa versione potenziata ti offre molta più flessibilità. Ecco come utilizzarla:

# Basic information logging
Write-Log -Message "Starting software installation"

# Warning about a non-critical issue
Write-Log -Message "Config file not found, using defaults" -Level Warning

# Log an error
Write-Log -Message "Installation failed!" -Level Error

# Use a custom log path
Write-Log -Message "Custom path log" -LogFilePath "D:\\CustomLogs"

Il file di log risultante (log-03-12-2024.txt) avrà questo aspetto:

10:15:22 [Information] - Starting software installation
10:15:23 [Warning] - Config file not found, using defaults
10:15:25 [Error] - Installation failed!

E in D:\CustomLogs\log-03-12-2024.txt:

10:15:26 [Information] - Custom path log

Nota come ogni voce include il timestamp, il livello di log tra parentesi e il messaggio. Questo formato strutturato facilita l’analisi dei log e l’identificazione rapida dei problemi.

Esempio concreto: Script di installazione del software

Met­tiamo a lavoro la nostra funzione di logging in uno script reale che installa il software in modo silenzioso:

# First, dot-source the logging function
. .\\Write-Log.ps1

# Script variables
$installer = "C:\\Installers\\software.exe"
$logPath = "C:\\Scripts\\InstallLogs"

# Start logging
Write-Log -Message "Beginning installation process" -LogFilePath $logPath

# Check if installer exists
if (Test-Path $installer) {
    Write-Log -Message "Found installer at: $installer"

    try {
        # Attempt installation
        Write-Log -Message "Starting installation..."
        $process = Start-Process -FilePath $installer -ArgumentList '/i /s' -Wait -NoNewWindow -PassThru

        # Check the exit code
        if ($process.ExitCode -eq 0) {
            Write-Log -Message "Installation completed successfully"
        }
        else {
            Write-Log -Message "Installation failed with exit code: $($process.ExitCode)" -Level Error
        }
    }
    catch {
        Write-Log -Message "Installation failed with error: $_" -Level Error
    }
}
else {
    Write-Log -Message "Installer not found at: $installer" -Level Error
}

Write-Log -Message "Installation script completed"

Il file di log risultante avrà un aspetto simile a questo:

09:15:22 [Information] - Beginning installation process
09:15:22 [Information] - Found installer at: C:\\Installers\\software.exe
09:15:22 [Information] - Starting installation...
09:16:45 [Information] - Installation completed successfully
09:16:45 [Information] - Installation script completed

Suggerimenti utili

Ecco alcuni consigli utili per l’utilizzo di questa funzione di logging:

  1. Registra sempre l’inizio e la fine dello script – Questo aiuta a tracciare il tempo di esecuzione dello script e lo stato di completamento.

  2. Utilizza i livelli di log appropriati – Non contrassegnare tutto come errore; utilizza il livello corretto per la situazione:

    • Informazioni: Operazioni normali
    • Avviso: Problemi non critici
    • Errore: Problemi critici che richiedono attenzione
  3. Includi dettagli rilevanti – Registra sufficienti informazioni per capire cosa è successo:

    # Cattivo
    Write-Log "Impossibile connettersi"
    
    # Buono
    Write-Log "Impossibile connettersi al server 'SQL01' - timeout dopo 30 secondi" -Level Error
    
  4. Pulisci i vecchi log – Considera di aggiungere la rotazione dei log per evitare di riempire lo spazio su disco:

    # Elimina i log più vecchi di 30 giorni
    Get-ChildItem -Path $LogFilePath -Filter "*.txt" |
        Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-30) } |
        Remove-Item
    

Conclusione

Una buona funzione di logging è essenziale per qualsiasi script PowerShell serio. Con la funzione Write-Log che abbiamo costruito, ora hai un modo flessibile e riutilizzabile per aggiungere un logging adeguato a tutti i tuoi script. Ricorda di adattare la funzione alle tue esigenze specifiche – potresti voler aggiungere funzionalità come:

Rotazione dei Log

function Write-Log {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$Message,

        [Parameter()]
        [int]$MaxLogFiles = 30  # Keep last 30 days of logs
    )

    # Remove old log files
    Get-ChildItem -Path $LogFilePath -Filter "*.txt" |
        Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-$MaxLogFiles) } |
        Remove-Item -Force

    # Continue with normal logging...
}

Formati di Output Differenti (CSV, JSON)

function Write-Log {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$Message,

        [Parameter()]
        [ValidateSet('TXT','CSV','JSON')]
        [string]$Format = 'TXT'
    )

    $logEntry = [PSCustomObject]@{
        Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
        Level = $Level
        Message = $Message
    }

    switch ($Format) {
        'CSV'  { $logEntry | Export-Csv -Path "$LogFilePath\\log.csv" -Append -NoTypeInformation }
        'JSON' { $logEntry | ConvertTo-Json | Add-Content -Path "$LogFilePath\\log.json" }
        'TXT'  { "$($logEntry.Timestamp) [$($logEntry.Level)] - $($logEntry.Message)" |
                 Add-Content -Path "$LogFilePath\\log.txt" }
    }
}

Supporto per Percorsi di Rete

function Write-Log {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$Message,

        [Parameter()]
        [string]$NetworkPath = "\\\\server\\logs"
    )

    # Test network path connectivity
    if (!(Test-Path $NetworkPath)) {
        # Fallback to local logging if network is unavailable
        $NetworkPath = "C:\\Scripts\\Logs"
        Write-Warning "Network path unavailable. Using local path: $NetworkPath"
    }

    # Continue with normal logging...
}

Notifiche via Email per Errori

function Write-Log {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$Message,

        [Parameter()]
        [string]$SmtpServer = "smtp.company.com",

        [Parameter()]
        [string[]]$NotifyOnError = "[email protected]"
    )

    # Normal logging first...

    # Send email if this is an error
    if ($Level -eq 'Error' -and $NotifyOnError) {
        $emailParams = @{
            From = "[email protected]"
            To = $NotifyOnError
            Subject = "PowerShell Script Error"
            Body = "Error occurred at $timeStamp`n`nMessage: $Message"
            SmtpServer = $SmtpServer
        }

        try {
            Send-MailMessage @emailParams
        }
        catch {
            Write-Warning "Failed to send error notification: $_"
        }
    }
}

La chiave è partire da una base solida e costruire da lì in base alle tue esigenze specifiche. Questi esempi dovrebbero darti un buon punto di partenza per estendere la funzione di logging di base con funzionalità più avanzate.

Ad esempio, potresti combinare diverse di queste funzionalità in una singola, soluzione di logging completa:

Write-Log -Message "Critical error in payment processing" `
          -Level Error `
          -Format CSV `
          -NetworkPath "\\\\server\\logs" `
          -NotifyOnError "[email protected]","[email protected]" `
          -MaxLogFiles 90

Questo permetterebbe di:

  • Registrare l’errore in formato CSV
  • Archiviarlo in una condivisione di rete
  • Inviare e-mail a più destinatari
  • Mantenere un registro storico di 90 giorni

Ricordati di testare attentamente, specialmente quando si implementano percorsi di rete o notifiche via email, poiché queste dipendenze esterne possono influire sulla affidabilità dello script. Buona scrittura di script!

Source:
https://adamtheautomator.com/powershell-write-log-tutorial/