PowerShell Write-Log: 一個簡單的日誌功能教程

如果您正在編寫有意義的 PowerShell 腳本,您需要記錄功能。無論您是在部署軟體、管理服務還是自動化任務,擁有腳本執行(或未執行)操作的記錄都是至關重要的。在本教程中,您將學會如何創建一個簡單但有效的 PowerShell 記錄函數。

先決條件

如果您想隨著本教程一起操作,請確保您擁有:

  • 擁有 PowerShell 5.1 或 PowerShell 7+ 的 Windows 10 或 Windows Server
  • 一個文本編輯器(建議使用 VSCode)
  • 對 PowerShell 函數的基本了解

基本記錄的問題

假設您正在編寫一個靜默安裝某些軟體的腳本。基本方法可能看起來像這樣:

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

這樣做可以,但存在一些問題:

  • 沒有時間戳
  • 重複的代碼
  • 不一致的記錄格式
  • 硬編碼的日誌路徑

讓我們通過構建一個適當的記錄函數來解決這些問題。

構建基本的 Write-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"
}

現在您可以像這樣使用它:

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

日誌文件(C:\Scripts\script.log)將包含如下條目:

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

乾淨多了!但我們可以做得更好。

添加更多功能

讓我們用一些有用的功能增強我們的記錄函數:

  • 自定義日誌路徑
  • 不同的日誌級別(信息、警告、錯誤)
  • 檔名中的日期
  • 錯誤處理

這是改進的版本:

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

這個增強版本為您提供了更多的靈活性。以下是使用方法:

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

生成的日誌文件(log-03-12-2024.txt)將如下所示:

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

在 D:\CustomLogs\log-03-12-2024.txt 中:

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

請注意每個條目都包括時間戳、括號中的日誌級別和消息。這種結構化格式使得解析日誌和快速識別問題變得容易。

現實世界示例:軟件安裝腳本

讓我們在一個靜默安裝軟件的實際腳本中使用我們的日誌函數:

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

生成的日誌文件將如下所示:

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

有用的小提示

以下是使用此日誌函數的一些最佳實踐:

  1. 始終記錄腳本的開始和結束 – 這有助於跟踪腳本執行時間和完成狀態。

  2. 使用適當的日誌級別 – 不要將所有內容都標記為錯誤;根據情況使用正確的級別:

    • 信息:正常操作
    • 警告:非關鍵性問題
    • 錯誤:需要注意的關鍵問題
  3. 包含相關細節 – 記錄足夠的資訊以了解發生了什麼:

    # 不佳
    Write-Log "無法連接"
    
    # 良好
    Write-Log "無法連接到伺服器 'SQL01' - 30秒後超時" -Level Error
    
  4. 清理舊日誌 – 考慮添加日誌輪替以防止佔滿磁碟空間:

    # 刪除超過30天的日誌
    Get-ChildItem -Path $LogFilePath -Filter "*.txt" |
        Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-30) } |
        Remove-Item
    

結論

良好的日誌功能對於任何認真的 PowerShell 腳本至關重要。通過我們構建的 Write-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...
}

不同的輸出格式(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" }
    }
}

網路路徑支援

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

錯誤的電子郵件通知

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

關鍵在於從堅實的基礎開始,並根據您的具體需求進行擴展。這些範例應該能為您提供一個良好的起點,以擴展基本的日誌功能,加入更多高級功能。

例如,您可以將這些功能結合成一個全面的記錄解決方案:

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

這將:

  • 以CSV格式記錄錯誤
  • 將其存儲在網絡共享中
  • 發送郵件給多個收件人
  • 保留90天的日誌歷史記錄

請記得進行全面測試,特別是在實施網絡路徑或郵件通知時,因為這些外部依賴可能影響腳本的可靠性。祝腳本編寫愉快!

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