PowerShell Запись-Журнала: Простой Учебник по Функции Логирования

Если вы пишете скрипты PowerShell, которые выполняют что-то значимое, вам необходимо ведение журнала. Будь то развертывание программного обеспечения, управление службами или автоматизация задач, наличие записи о том, что ваш скрипт сделал (или не сделал), имеет решающее значение. В этом руководстве вы узнаете, как создать простую, но эффективную функцию ведения журнала PowerShell.

Предварительные требования

Если вы хотите следовать этому руководству, убедитесь, что у вас есть:

  • Windows 10 или Windows Server с PowerShell 5.1 или PowerShell 7+
  • Текстовый редактор (рекомендуется 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

Полезные советы

Вот некоторые bewые практики при использовании этой функции регистрации:

  1. Всегда регистрируйте начало и конец своего скрипта – Это помогает отслеживать время выполнения скрипта и его статус завершения.

  2. Используйте соответствующие уровни журнала – Не помечайте все как ошибку; используйте правильный уровень для ситуации:

    • Информация: Обычные операции
    • Предупреждение: Некритические проблемы
    • Ошибка: Критические проблемы, требующие внимания
  3. Включите соответствующие детали – Запишите достаточно информации, чтобы понять, что произошло:

    # Плохо
    Write-Log "Не удалось подключиться"
    
    # Хорошо
    Write-Log "Не удалось подключиться к серверу 'SQL01' - тайм-аут после 30 секунд" -Уровень Ошибка
    
  4. Очистка старых журналов – Рассмотрите добавление ротации журналов для предотвращения заполнения дискового пространства:

    # Удалить журналы старше 30 дней
    Get-ChildItem -Path $ПутьКЖурналу -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/