Logging é o processo de registrar informações sobre a execução de um script, como erros, avisos e etapas concluídas. Em scripts PowerShell, especialmente aqueles que automatizam tarefas críticas ou rodam de forma agendada, um bom sistema de log é essencial para auditar, depurar e monitorar o comportamento do script. Nesta aula, vamos explorar as ferramentas nativas do PowerShell e técnicas para criar logs robustos e informativos.

Dominar o logging não só ajuda a identificar problemas rapidamente, mas também fornece um histórico valioso para otimizar scripts e garantir conformidade com políticas de TI. Vamos começar com o cmdlet Start-Transcript, depois evoluiremos para funções personalizadas que oferecem controle total sobre o formato e o destino dos logs.

Start-Transcript

O cmdlet Start-Transcript é a maneira mais simples de registrar toda a saída de uma sessão do PowerShell em um arquivo de texto. Ele captura tudo o que aparece no console, incluindo comandos executados e suas saídas, como se fosse uma gravação de tela. Isso é particularmente útil para scripts que precisam de um registro completo de sua execução, sem exigir nenhuma modificação no código além de iniciar e parar a transcrição.

Para usar, basta chamar Start-Transcript -Path "C:\Logs\meuscript.log" no início do script e Stop-Transcript no final. O arquivo será criado (ou sobrescrito, por padrão) e conterá todo o texto exibido. Porém, é importante notar que ele não captura saídas de streams separados (como erros) de forma estruturada; tudo é misturado em texto plano. Além disso, o arquivo pode crescer rapidamente em scripts longos. Ainda assim, é uma ferramenta valiosa para depuração rápida e para ambientes onde não se deseja criar uma infraestrutura de logging mais complexa.

# Iniciar a transcrição
Start-Transcript -Path "C:\Logs\meuscript.log" -Force

# Seu código aqui
Write-Host "Executando tarefa..."
Get-Process | Select-Object -First 3

# Parar a transcrição
Stop-Transcript

O parâmetro -Force sobrescreve o arquivo existente; sem ele, o cmdlet perguntaria se deseja sobrescrever. Em scripts não interativos, é recomendável usar -Force ou gerar nomes de arquivo únicos (ex.: com timestamp).

Funções de log

Embora o Start-Transcript seja útil, muitas vezes precisamos de logs mais estruturados, com níveis de severidade (INFO, WARN, ERROR) e controle sobre o que é registrado. Criar funções de log personalizadas é a abordagem profissional. Você pode definir uma função que aceita uma mensagem e um nível, e que grava em um arquivo com formato consistente, por exemplo, CSV ou JSON, além de permitir exibir no console quando desejado.

Uma função de log típica pode incluir timestamp, nível e mensagem. Você pode usar o cmdlet Add-Content para anexar linhas a um arquivo de texto. Para logs mais avançados, considere usar o módulo PSFramework (da comunidade) ou o Microsoft.PowerShell.Utility com Write-Log (se disponível). Vamos criar uma função simples e reutilizável.

function Write-Log {
    param(
        [Parameter(Mandatory)]
        [string]$Message,
        [ValidateSet('INFO','WARN','ERROR')]
        [string]$Level = 'INFO',
        [string]$LogFile = "C:\Logs\script.log"
    )

    $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
    $logLine = "$timestamp [$Level] $Message"

    # Exibir no console se verboso ou se for erro
    if ($Level -eq 'ERROR' -or $VerbosePreference -eq 'Continue') {
        Write-Host $logLine
    }

    # Gravar no arquivo
    Add-Content -Path $LogFile -Value $logLine
}

Com essa função, você pode chamar Write-Log -Message "Iniciando processo" -Level INFO e Write-Log -Message "Falha ao conectar" -Level ERROR. Essa abordagem permite que você controle o nível de detalhe, filtre por severidade e integre-se a sistemas de monitoramento. Em produção, você pode querer usar um formato estruturado como JSON para facilitar o parsing por ferramentas como o Elastic Stack.

Streams

O PowerShell possui vários streams de saída, cada um com um propósito específico: Success (saída normal), Error, Warning, Verbose, Debug e Information. Entender esses streams é crucial para logging, pois você pode redirecioná-los e capturá-los seletivamente.

Por exemplo, o stream de erro é onde os erros não-terminantes são enviados. Você pode redirecionar todos os streams para um arquivo usando *> arquivo.log, mas isso mistura tudo. Para capturar streams específicos, você pode usar 2>&1 para redirecionar erros para a saída padrão, ou usar os cmdlets Write-Error, Write-Warning, Write-Verbose, etc., que enviam mensagens para os respectivos streams. No logging, é comum registrar erros e avisos separadamente, ou usar os streams para controlar a verbosidade do script.

# Exemplo de uso de streams
Write-Output "Mensagem normal"
Write-Warning "Este é um aviso"
Write-Error "Este é um erro não-terminante"
Write-Verbose "Mensagem detalhada" -Verbose

# Redirecionar todos os streams para um arquivo
Start-Transcript -Path "C:\Logs\transcript.log" -Force
Write-Output "Executando..."
Write-Warning "Cuidado"
Stop-Transcript

# Capturar erros em variável
$erros = 2>&1  # não é assim; melhor usar try/catch

Na prática, você pode definir a preferência de verbosidade com $VerbosePreference = 'Continue' para que mensagens verbose apareçam no console e, se estiver usando transcrição, sejam capturadas. Para logs estruturados, você pode usar Write-Information com o parâmetro -InformationAction para controlar a exibição.

Boas práticas

Ao implementar logging, siga estas recomendações para garantir que seus logs sejam úteis e confiáveis:

  • Inclua timestamps e nível de severidade em cada entrada para facilitar a análise.
  • Use nomes de arquivo com data/hora para evitar sobrescritas e permitir histórico (ex.: script-20250301.log).
  • Capture o contexto: além da mensagem, registre o nome do script, função, linha (se possível) e o usuário que executou.
  • Não misture streams sem necessidade; use um formato estruturado (CSV, JSON) se for processar logs automaticamente.
  • Lide com erros de forma consistente: use try/catch e registre o erro com detalhes (ex.: $_.Exception.Message).
  • Evite logs excessivos em produção; use níveis (INFO, DEBUG) e ajuste conforme o ambiente.
  • Teste o logging em cenários de falha para garantir que os erros são capturados.
  • Considere a rotação de logs para arquivos muito grandes.
# Exemplo de bloco try/catch com log
function Invoke-Tarefa {
    param([string]$Caminho)
    try {
        if (-not (Test-Path $Caminho)) {
            throw "Arquivo não encontrado: $Caminho"
        }
        Write-Log -Message "Processando $Caminho" -Level INFO
        # ... processamento ...
    }
    catch {
        Write-Log -Message "Erro: $($_.Exception.Message)" -Level ERROR
        throw  # relança o erro para o chamador
    }
}

Além disso, ao usar funções de log, defina um parâmetro comum para o caminho do arquivo de log, permitindo que o usuário do script configure o destino sem alterar o código. Em scripts maiores, você pode criar um módulo de logging reutilizável.

Referências

Exercícios

  1. Exercício 1: Escreva um script que use Start-Transcript para registrar a saída de um comando simples (ex.: Get-Process) e salve o log em um arquivo chamado "processos.log".
  2. ✓ Resposta:
    Start-Transcript -Path "C:\Logs\processos.log" -Force
    Get-Process
    Stop-Transcript
  3. Exercício 2: Crie uma função chamada Write-Log que aceite mensagem, nível (INFO, WARN, ERROR) e arquivo de log, e escreva uma linha formatada com timestamp. Teste a função com uma chamada de exemplo.
  4. ✓ Resposta:
    function Write-Log {
        param(
            [Parameter(Mandatory)]
            [string]$Message,
            [ValidateSet('INFO','WARN','ERROR')]
            [string]$Level = 'INFO',
            [string]$LogFile = "C:\Logs\script.log"
        )
        $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
        $logLine = "$timestamp [$Level] $Message"
        Add-Content -Path $LogFile -Value $logLine
    }
    
    # Exemplo de uso
    Write-Log -Message "Script iniciado" -Level INFO
  5. Exercício 3: Explique a diferença entre os streams de saída e como você pode redirecionar apenas erros para um arquivo separado. Dê um exemplo de código.
  6. ✓ Resposta: Os streams são canais separados para diferentes tipos de mensagens. Para redirecionar apenas erros para um arquivo, use 2> erro.log. Exemplo:
    Write-Error "Este é um erro" 2> "C:\Logs\erros.log"
  7. Exercício 4: Reescreva o script abaixo para incluir tratamento de erros com try/catch e registre os erros em um log usando uma função Write-Log. O script tenta ler um arquivo que pode não existir.
  8. ✓ Resposta:
    function Write-Log {
        param($Message, $Level = 'INFO', $LogFile = "C:\Logs\script.log")
        $ts = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
        $line = "$ts [$Level] $Message"
        Add-Content -Path $LogFile -Value $line
    }
    
    try {
        Get-Content "C:\arquivo_inexistente.txt" -ErrorAction Stop
    }
    catch {
        Write-Log -Message "Erro ao ler arquivo: $($_.Exception.Message)" -Level ERROR
    }
  9. Exercício 5: Liste três boas práticas de logging que você aplicaria em um script de produção e explique por que cada uma é importante.
  10. ✓ Resposta: 1) Incluir timestamp e nível de severidade: ajuda a correlacionar eventos e priorizar problemas. 2) Usar arquivos de log com rotação (ex.: por data) para evitar crescimento infinito e facilitar a análise. 3) Registrar contexto (script, função, usuário) para rastrear a origem do log em ambientes complexos.

Observações finais

O logging é uma habilidade essencial para qualquer profissional que trabalha com PowerShell em automação. Comece com Start-Transcript para depuração rápida, mas evolua para funções personalizadas que oferecem controle e estrutura. Lembre-se de considerar os streams e as boas práticas apresentadas para criar logs que realmente ajudem no monitoramento e na resolução de problemas. À medida que você ganha experiência, explore ferramentas avançadas como o módulo PSFramework ou integração com sistemas de log centralizados (ELK, Splunk).