PowerShell ログ書き込み: シンプルなログ記録関数のチュートリアル

意味のあることをするPowerShellスクリプトを書く場合、ログ記録は必須です。ソフトウェアの展開、サービスの管理、タスクの自動化を行う場合、スクリプトが何をしたか(またはしなかったか)の記録を持つことが重要です。このチュートリアルでは、シンプルで効果的なPowerShellログ記録機能を作成する方法を学びます。

前提条件

このチュートリアルを進めたい場合は、次のものを準備してください:

  • Windows 10またはPowerShell 5.1またはPowerShell 7以上を搭載した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: $_"
        }
    }
}

重要なのは、堅実な基盤から始め、特定のニーズに基づいてそこから構築していくことです。これらの例は、基本的なログ関数をより高度な機能で拡張するための出発点となるでしょう。

例えば、これらの機能を1つの包括的なログソリューションに組み合わせることができます:

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/