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/