Если вы пишете скрипты 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ые практики при использовании этой функции регистрации:
-
Всегда регистрируйте начало и конец своего скрипта – Это помогает отслеживать время выполнения скрипта и его статус завершения.
-
Используйте соответствующие уровни журнала – Не помечайте все как ошибку; используйте правильный уровень для ситуации:
- Информация: Обычные операции
- Предупреждение: Некритические проблемы
- Ошибка: Критические проблемы, требующие внимания
-
Включите соответствующие детали – Запишите достаточно информации, чтобы понять, что произошло:
# Плохо Write-Log "Не удалось подключиться" # Хорошо Write-Log "Не удалось подключиться к серверу 'SQL01' - тайм-аут после 30 секунд" -Уровень Ошибка
-
Очистка старых журналов – Рассмотрите добавление ротации журналов для предотвращения заполнения дискового пространства:
# Удалить журналы старше 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/