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

Se você está escrevendo scripts do PowerShell que fazem algo significativo, você precisa de registro. Seja para implantar software, gerenciar serviços ou automatizar 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 do PowerShell simples, mas eficaz.

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 (recomendado o VSCode)
  • Conhecimento básico de funções do PowerShell

O Problema com o 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 tem alguns problemas:

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

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

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

Primeiro, vamos criar uma função simples que adiciona carimbos de 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á-lo 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 melhorar.

Adicionando Mais Funcionalidades

Vamos aprimorar nossa função de registro com algumas funcionalidades úteis:

  • Caminhos de log personalizados
  • Diferentes níveis de log (Informação, Aviso, Erro)
  • Data no nome do arquivo
  • Tratamento de erros

Aqui está a versão aprimorada:

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) terá esta aparência:

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

Note como cada entrada inclui a marcação de tempo, o 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 em prática em um script real que instala software em silêncio:

# 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 terá uma aparência semelhante a esta:

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 melhores 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 níveis de log apropriados – Não marque tudo como um erro; use o nível certo 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 – Registrar informações suficientes para entender o que aconteceu:

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

    # Excluir logs mais antigos que 30 dias
    Get-ChildItem -Caminho $CaminhoLog -Filtro "*.txt" |
        Onde-Objeto { $_.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, você agora 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 evoluir a partir daí com base em suas necessidades específicas. Esses exemplos devem lhe dar 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 abrangente de registro:

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

Isso iria:

  • Registrar o erro em formato CSV
  • Armazená-lo em um compartilhamento de rede
  • Enviar por e-mail para vários destinatários
  • Manter um histórico de log de 90 dias

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. Feliz script!

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