Tratamento de erros e logging em scripts PowerShell
1. Introdução ao tratamento de erros no PowerShell
O PowerShell possui dois tipos fundamentais de erros: terminantes (terminating) e não terminantes (non-terminating). Erros terminantes interrompem imediatamente a execução do script, enquanto erros não terminantes permitem que o script continue processando, apenas registrando o problema.
Por padrão, o PowerShell exibe erros não terminantes em vermelho no console e continua a execução. A variável automática $Error armazena um ArrayList com todos os erros ocorridos na sessão, sendo $Error[0] o erro mais recente. Gerenciar essa lista é essencial para scripts robustos.
2. Estruturas de tratamento de erros
O bloco try/catch/finally é a principal estrutura para capturar e tratar erros terminantes:
try {
Get-Content "C:\arquivo_inexistente.txt"
}
catch [System.IO.FileNotFoundException] {
Write-Host "Arquivo não encontrado: $_" -ForegroundColor Yellow
}
catch {
Write-Host "Erro genérico: $_" -ForegroundColor Red
}
finally {
Write-Host "Bloco finally sempre executado" -ForegroundColor Cyan
}
Para gerar erros personalizados, utilize throw:
function Validar-Idade {
param([int]$idade)
if ($idade -lt 0 -or $idade -gt 120) {
throw [System.ArgumentOutOfRangeException]::new("idade", "Idade deve estar entre 0 e 120")
}
return $true
}
3. Controlando a ação diante de erros não terminantes
O parâmetro -ErrorAction permite definir o comportamento para comandos específicos:
Get-ChildItem "C:\pasta_inexistente" -ErrorAction Stop # Transforma erro não terminante em terminante
Get-ChildItem "C:\outra_pasta" -ErrorAction SilentlyContinue # Suprime a mensagem de erro
Get-ChildItem "C:\terceira_pasta" -ErrorAction Inquire # Pergunta ao usuário como proceder
A variável $ErrorActionPreference define o comportamento global:
$ErrorActionPreference = "Stop" # Todos os erros se tornam terminantes
$ErrorActionPreference = "Continue" # Padrão: exibe erro e continua
Para capturar erros em variáveis específicas, use -ErrorVariable:
Get-ChildItem "C:\pasta_teste" -ErrorVariable errosCapturados -ErrorAction SilentlyContinue
if ($errosCapturados) {
Write-Host "Ocorreram $($errosCapturados.Count) erros durante a operação"
}
4. Estratégias de logging em scripts PowerShell
Comandos nativos para logging básico:
Write-Host "Mensagem visível no console" -ForegroundColor Green
Write-Output "Mensagem enviada para pipeline"
Write-Verbose "Mensagem detalhada" -Verbose
Write-Debug "Mensagem de depuração" -Debug
Write-Warning "Aviso importante"
Criação de função personalizada de logging:
function Write-Log {
param(
[string]$Mensagem,
[ValidateSet("INFO", "WARN", "ERROR")]
[string]$Nivel = "INFO",
[string]$ArquivoLog = ".\script.log"
)
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$linhaLog = "[$timestamp] [$Nivel] $Mensagem"
Add-Content -Path $ArquivoLog -Value $linhaLog
switch ($Nivel) {
"ERROR" { Write-Host $linhaLog -ForegroundColor Red }
"WARN" { Write-Host $linhaLog -ForegroundColor Yellow }
default { Write-Host $linhaLog -ForegroundColor Gray }
}
}
Write-Log "Script iniciado" -Nivel INFO
Write-Log "Arquivo não encontrado" -Nivel WARN
Write-Log "Falha crítica de conexão" -Nivel ERROR
5. Técnicas avançadas de logging
Para registrar toda a saída de uma sessão, use Start-Transcript:
Start-Transcript -Path "C:\logs\sessao_$(Get-Date -Format 'yyyyMMdd_HHmmss').txt"
# Comandos do script aqui
Stop-Transcript
Integração com o Log de Eventos do Windows:
Write-EventLog -LogName Application -Source "MeuScript" -EntryType Error `
-EventId 1001 -Message "Erro ao processar arquivo: $erro"
Módulo reutilizável de logging:
function New-Logger {
param([string]$LogPath, [string]$LogLevel = "INFO")
return [PSCustomObject]@{
Path = $LogPath
Level = $LogLevel
Log = {
param($message, $level)
if ($level -in $this.Level, "ERROR") {
$entry = "[$(Get-Date)] [$level] $message"
Add-Content -Path $this.Path -Value $entry
}
}.GetNewClosure()
}
}
$logger = New-Logger -LogPath "C:\logs\app.log" -LogLevel "WARN"
& $logger.Log "Mensagem de teste" "INFO" # Não registra (nível abaixo do configurado)
& $logger.Log "Aviso importante" "WARN" # Registra
6. Boas práticas de tratamento de erros e logging
Centralize o tratamento de erros em funções modulares:
function Invoke-ComandoSeguro {
param([scriptblock]$Comando)
try {
& $Comando
}
catch {
Write-Log -Mensagem "Falha ao executar comando: $_" -Nivel ERROR
throw # Re-lança o erro para tratamento superior
}
}
Use blocos trap para captura global de erros:
trap {
Write-Log -Mensagem "Erro global capturado: $_" -Nivel ERROR
continue # ou break para interromper
}
Para logging estruturado, utilize formato JSON:
$logEntry = [PSCustomObject]@{
Timestamp = Get-Date -Format "o"
Level = "ERROR"
Message = "Falha na conexão com banco de dados"
Hostname = $env:COMPUTERNAME
User = $env:USERNAME
} | ConvertTo-Json
Add-Content -Path "C:\logs\estruturado.json" -Value $logEntry
7. Exemplo prático: script robusto com logging e tratamento de erros
# Configuração inicial
$script:LogPath = "C:\logs\meuscript_$(Get-Date -Format 'yyyyMMdd').log"
$script:MaxLogSize = 10MB
$ErrorActionPreference = "Stop"
function Initialize-Log {
if (Test-Path $script:LogPath) {
if ((Get-Item $script:LogPath).Length -gt $script:MaxLogSize) {
Rename-Item $script:LogPath "$script:LogPath.old" -Force
}
}
Write-Log "=== Início da execução ===" -Nivel INFO
}
function Write-Log {
param($Mensagem, $Nivel = "INFO")
$entry = "[$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')] [$Nivel] $Mensagem"
Add-Content -Path $script:LogPath -Value $entry
if ($Nivel -eq "ERROR") { Write-Host $entry -ForegroundColor Red }
}
function Processar-Arquivo {
param([string]$Arquivo)
try {
Write-Log "Processando arquivo: $Arquivo" -Nivel INFO
if (-not (Test-Path $Arquivo)) {
throw [System.IO.FileNotFoundException]::new("Arquivo não encontrado: $Arquivo")
}
$conteudo = Get-Content $Arquivo -ErrorAction Stop
Write-Log "Arquivo lido com sucesso. Linhas: $($conteudo.Count)" -Nivel INFO
# Processamento simulado
Start-Sleep -Seconds 2
Write-Log "Processamento concluído para: $Arquivo" -Nivel INFO
}
catch [System.IO.FileNotFoundException] {
Write-Log "Erro de arquivo: $_" -Nivel ERROR
throw # Re-lança para tratamento superior
}
catch [System.UnauthorizedAccessException] {
Write-Log "Permissão negada: $_" -Nivel ERROR
}
catch {
Write-Log "Erro inesperado: $_" -Nivel ERROR
}
}
function Main {
Initialize-Log
$arquivos = @(
"C:\dados\relatorio.txt",
"C:\dados\inventario.csv",
"C:\dados\backup.log"
)
foreach ($arquivo in $arquivos) {
try {
Processar-Arquivo $arquivo
}
catch {
Write-Log "Falha ao processar $arquivo. Continuando com próximo..." -Nivel WARN
}
}
Write-Log "=== Fim da execução ===" -Nivel INFO
}
# Execução principal
Main
Este script demonstra:
- Rotação automática de logs baseada em tamanho
- Tratamento específico para diferentes tipos de erro
- Captura de exceções com logging estruturado
- Continuidade mesmo após falhas em itens individuais
Referências
- about_Try_Catch_Finally - Documentação Microsoft — Documentação oficial sobre blocos try/catch/finally no PowerShell
- about_ErrorActionPreference - Documentação Microsoft — Guia completo sobre a variável de preferência de ação de erro
- about_Automatic_Variables ($Error) - Documentação Microsoft — Referência sobre variáveis automáticas incluindo $Error
- Start-Transcript (Microsoft.PowerShell.Host) - Documentação Microsoft — Documentação do cmdlet para registro completo de sessões
- Write-EventLog (Microsoft.PowerShell.Management) - Documentação Microsoft — Como integrar logs do PowerShell com o Log de Eventos do Windows
- PowerShell Best Practices for Error Handling and Logging - PowerShell Magazine — Artigo técnico com boas práticas de tratamento de erros e logging
- Understanding PowerShell Error Handling - Dev.to Tutorial — Tutorial prático sobre conceitos de tratamento de erros no PowerShell