PowerShell Write-Log: Um Tutorial de Função de Registro Simples

Se você está escrevendo scripts PowerShell que fazem algo significativo, você precisa de registro. Seja implementando software, gerenciando serviços ou automatizando tarefas, ter um registro do que seu script fez (ou não fez) é crucial. Neste tutorial, você aprenderá como criar uma função de registro simples, mas eficaz, no PowerShell.

Pré-requisitos

Se você deseja acompanhar este tutorial, certifique-se de ter:

  • Windows 10 ou Windows Server com PowerShell 5.1 ou PowerShell 7+
  • Um editor de texto (VSCode recomendado)
  • Compreensão básica das funções do PowerShell

O Problema com Registro Básico

Vamos dizer que você está escrevendo um script para instalar silenciosamente algum software. A abordagem básica pode ser algo assim:

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."

Isso funciona, mas apresenta alguns problemas:

  • Sem carimbos de data/hora
  • Código repetitivo
  • Formato de registro inconsistente
  • Caminho de log codificado

Vamos corrigir esses problemas construindo uma função de registro adequada.

Construindo uma Função Write-Log Básica

Primeiro, vamos criar uma função simples que adiciona carimbos de data/hora às nossas entradas de 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"
}

Agora você pode usá-la assim:

Write-Log -Message "Starting install..."
Start-Process -FilePath 'installer.exe' -ArgumentList '/i /s' -Wait -NoNewWindow
Write-Log -Message "Finished install."

O arquivo de log (C:\Scripts\script.log) conterá entradas que se parecem com:

09:42:15 - Starting install...
09:43:22 - Finished install.

Muito mais limpo! Mas podemos fazer melhor.

Adicionando Mais Funcionalidade

Vamos aprimorar nossa função de registro com alguns recursos úteis:

  • Caminhos de log personalizados
  • Diferentes níveis de log (Info, Aviso, Erro)
  • Data no nome do arquivo
  • Tratamento de erro

Aqui está a versão melhorada:

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: $_"
    }
}

Esta versão aprimorada oferece muito mais flexibilidade. Veja como usá-la:

# 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"

O arquivo de log resultante (log-03-12-2024.txt) ficará assim:

10:15:22 [Information] - Starting software installation
10:15:23 [Warning] - Config file not found, using defaults
10:15:25 [Error] - Installation failed!

E em D:\CustomLogs\log-03-12-2024.txt:

10:15:26 [Information] - Custom path log

Observe como cada entrada inclui o carimbo de data/hora, nível de log entre colchetes e a mensagem. Este formato estruturado facilita a análise de logs e a identificação rápida de problemas.

Exemplo do mundo real: Script de Instalação de Software

Vamos colocar nossa função de log para trabalhar em um script real que instala software silenciosamente:

# 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"

O arquivo de log resultante será algo assim:

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

Dicas úteis

Aqui estão algumas boas práticas ao usar esta função de log:

  1. Sempre registre o início e o fim do seu script – Isso ajuda a rastrear o tempo de execução do script e o status de conclusão.

  2. Use os níveis de log apropriados – Não marque tudo como erro; use o nível correto para a situação:

    • Informação: Operações normais
    • Aviso: Problemas não críticos
    • Erro: Problemas críticos que precisam de atenção
  3. Incluir detalhes relevantes – Registar informação suficiente para compreender o que aconteceu:

    # Ruim
    Write-Log "Falha ao conectar"
    
    # Bom
    Write-Log "Falha ao conectar ao servidor 'SQL01' - tempo limite após 30 segundos" -Nível Erro
    
  4. Limpar logs antigos – Considere adicionar rotação de logs para evitar ocupar espaço em disco:

    # Excluir logs mais antigos que 30 dias
    Get-ChildItem -Path $CaminhoDoLog -Filter "*.txt" |
        Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-30) } |
        Remover-Item
    

Conclusão

Uma boa função de logging é essencial para qualquer script PowerShell sério. Com a função Write-Log que construímos, agora você tem uma maneira flexível e reutilizável de adicionar logging adequado a todos os seus scripts. Lembre-se de adaptar a função às suas necessidades específicas – você pode querer adicionar recursos como:

Rotação de Logs

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...
}

Diferentes Formatos de Saída (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" }
    }
}

Suporte a Caminho de Rede

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...
}

Notificações por Email para Erros

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: $_"
        }
    }
}

O segredo é começar com uma base sólida e construir a partir dela com base nas suas necessidades específicas. Estes exemplos devem dar-lhe um bom ponto de partida para estender a função básica de logging com recursos mais avançados.

Por exemplo, você pode combinar várias dessas funcionalidades em uma única solução de registro abrangente:

Write-Log -Message "Critical error in payment processing" `
          -Level Error `
          -Format CSV `
          -NetworkPath "\\\\server\\logs" `
          -NotifyOnError "[email protected]","[email protected]" `
          -MaxLogFiles 90

Isso:

  • Registra o erro em formato CSV
  • Armazena em um compartilhamento de rede
  • Envia e-mails para vários destinatários
  • Mantém 90 dias de histórico de logs

Lembre-se de testar minuciosamente, especialmente ao implementar caminhos de rede ou notificações por e-mail, pois essas dependências externas podem afetar a confiabilidade do seu script. Boa programação!

Source:
https://adamtheautomator.com/powershell-write-log-tutorial/