PowerShell Écrire-Log : Un tutoriel simple sur les fonctions de journalisation

Si vous écrivez des scripts PowerShell qui font quelque chose de significatif, vous avez besoin de logs. Que vous déployiez des logiciels, gériez des services ou automatisiez des tâches, avoir un enregistrement de ce que votre script a fait (ou n’a pas fait) est crucial. Dans ce tutoriel, vous apprendrez comment créer une fonction de journalisation PowerShell simple mais efficace.

Prérequis

Si vous souhaitez suivre ce tutoriel, assurez-vous d’avoir :

  • Windows 10 ou Windows Server avec PowerShell 5.1 ou PowerShell 7+
  • Un éditeur de texte (VSCode recommandé)
  • Une compréhension de base des fonctions PowerShell

Le Problème avec la Journalisation Basique

Disons que vous écrivez un script pour installer silencieusement un logiciel. L’approche de base pourrait ressembler à ceci :

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."

Cela fonctionne, mais présente quelques problèmes :

  • Pas de timestamp
  • Code répétitif
  • Format de journalisation incohérent
  • Chemin de journalisation codé en dur

Corrigeons ces problèmes en construisant une fonction de journalisation adéquate.

Construction d’une Fonction Write-Log de Base

Tout d’abord, créons une fonction simple qui ajoute des timestamps à nos entrées de journal :

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"
}

Maintenant, vous pouvez l’utiliser comme ceci :

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

Le fichier journal (C:\Scripts\script.log) contiendra des entrées qui ressemblent à :

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

Beaucoup plus propre ! Mais nous pouvons faire mieux.

Ajout de Plus de Fonctionnalités

Améliorons notre fonction de journalisation avec quelques fonctionnalités utiles :

  • Chemins de journalisation personnalisés
  • Différents niveaux de journalisation (Info, Avertissement, Erreur)
  • Date dans le nom de fichier
  • Gestion des erreurs

Voici la version améliorée:

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: $_"
    }
}

Cette version améliorée vous offre bien plus de flexibilité. Voici comment l’utiliser:

# 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"

Le fichier journal résultant (log-03-12-2024.txt) ressemblera à ceci:

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

Et dans D:\CustomLogs\log-03-12-2024.txt:

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

Remarquez comment chaque entrée inclut l horodatage, le niveau de journalisation entre crochets, et le message. Ce format structuré facilite l’analyse des journaux et permet d’identifier rapidement les problèmes.

Exemple concret : Script d’installation de logiciel

Mettons notre fonction de journalisation au travail dans un script concret qui installe des logiciels en silence:

# 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"

Le fichier journal résultant ressemblera à ceci:

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

Conseils utiles

Voici quelques bonnes pratiques à suivre lorsque vous utilisez cette fonction de journalisation:

  1. Toujours journaliser le début et la fin de votre script – Cela aide à suivre le temps d’exécution du script et son statut d’achèvement.

  2. Utiliser des niveaux de journalisation appropriés – Ne marquez pas tout comme une erreur ; utilisez le bon niveau en fonction de la situation :

    • Information : Opérations normales
    • Avertissement : Problèmes non critiques
    • Erreur : Problèmes critiques nécessitant une attention particulière
  3. Inclure des détails pertinents – Enregistrer suffisamment d’informations pour comprendre ce qui s’est passé :

    # Mauvais
    Write-Log "Échec de la connexion"
    
    # Bon
    Write-Log "Échec de la connexion au serveur 'SQL01' - délai d'attente après 30 secondes" -Niveau Erreur
    
  4. Nettoyer les anciens journaux – Envisagez d’ajouter une rotation des journaux pour éviter de remplir l’espace disque :

    # Supprimer les journaux datant de plus de 30 jours
    Get-ChildItem -Chemin $CheminJournal -Filtre "*.txt" |
        Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-30) } |
        Remove-Item
    

Conclusion

Une bonne fonction de journalisation est essentielle pour tout script PowerShell sérieux. Avec la fonction Write-Log que nous avons créée, vous disposez désormais d’un moyen flexible et réutilisable d’ajouter une journalisation appropriée à tous vos scripts. N’oubliez pas d’adapter la fonction à vos besoins spécifiques – vous voudrez peut-être ajouter des fonctionnalités telles que :

Rotation des journaux

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...
}

Formats de sortie différents (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" }
    }
}

Prise en charge des chemins réseau

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...
}

Notifications par e-mail en cas d’erreurs

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: $_"
        }
    }
}

Le secret est de partir d’une base solide et de construire à partir de là en fonction de vos besoins spécifiques. Ces exemples devraient vous donner un bon point de départ pour étendre la fonction de journalisation de base avec des fonctionnalités plus avancées.

Par exemple, vous pourriez combiner plusieurs de ces fonctionnalités dans une solution de journalisation complète unique :

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

Cela permettrait de :

  • Enregistrer l’erreur au format CSV
  • Le stocker sur un partage réseau
  • Envoyer des e-mails à plusieurs destinataires
  • Conserver un historique de journal de 90 jours

N’oubliez pas de tester soigneusement, en particulier lors de la mise en place de chemins réseau ou de notifications par e-mail, car ces dépendances externes peuvent affecter la fiabilité de votre script. Bon codage !

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