Pide tu presupuesto ya!

Un tutorial simple sobre la función de registro

Si está escribiendo scripts de PowerShell que hacen algo significativo, necesita iniciar sesión. Ya sea que esté implementando software, administrando servicios o automatizando tareas, tener un registro de lo que hizo (o no hizo) su script es crucial. En este tutorial, aprenderá cómo crear una función de registro de PowerShell simple pero efectiva.

Requisitos previos

Si desea seguir este tutorial, asegúrese de tener:

  • Windows 10 o Windows Server con PowerShell 5.1 o PowerShell 7+
  • Un editor de texto (se recomienda VSCode)
  • Comprensión básica de las funciones de PowerShell

El problema con el registro básico

Digamos que estás escribiendo un script para instalar silenciosamente algún software. El enfoque básico podría verse así:

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

Esto funciona, pero tiene algunos problemas:

  • Sin marcas de tiempo
  • Código repetitivo
  • Formato de registro inconsistente
  • Ruta de registro codificada

Solucionemos estos problemas creando una función de registro adecuada.

Creación de una función básica de registro de escritura

Primero, creemos una función simple que agregue marcas de tiempo a nuestras entradas de registro:

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

Ahora puedes usarlo así:

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

El archivo de registro (C:\Scripts\script.log) contendrá entradas similares a:

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

¡Mucho más limpio! Pero podemos hacerlo mejor.

Agregar más funcionalidad

Mejoremos nuestra función de registro con algunas características útiles:

  • Rutas de registro personalizadas
  • Diferentes niveles de registro (Información, Advertencia, Error)
  • Fecha en el nombre del archivo
  • Manejo de errores

Aquí está la versión mejorada:

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 versión mejorada le brinda mucha más flexibilidad. Aquí se explica cómo usarlo:

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

El archivo de registro resultante (log-03-12-2024.txt) tendrá este aspecto:

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

Y en D:\CustomLogs\log-03-12-2024.txt:

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

Observe cómo cada entrada incluye la marca de tiempo, el nivel de registro entre paréntesis y el mensaje. Este formato estructurado facilita el análisis de registros e identifica rápidamente problemas.

Ejemplo del mundo real: script de instalación de software

Pongamos nuestra función de registro a funcionar en un script real que instala el software de forma silenciosa:

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

El archivo de registro resultante se verá así:

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

Consejos útiles

Estas son algunas de las mejores prácticas al utilizar esta función de registro:

  1. Registre siempre el inicio y el final de su script – Esto ayuda a realizar un seguimiento del tiempo de ejecución del script y del estado de finalización.

  2. Utilice niveles de registro apropiados – No marques todo como error; Utilice el nivel adecuado para la situación:

    • Información: Operaciones normales
    • Advertencia: problemas no críticos
    • Error: problemas críticos que necesitan atención
  3. Incluir detalles relevantes – Registre suficiente información para comprender lo que sucedió:

    # Bad
    Write-Log "Failed to connect"
    
    # Good
    Write-Log "Failed to connect to server 'SQL01' - timeout after 30 seconds" -Level Error
    
  4. Limpiar registros viejos – Considere agregar rotación de registros para evitar llenar el espacio en disco:

    # Delete logs older than 30 days
    Get-ChildItem -Path $LogFilePath -Filter "*.txt" |
        Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-30) } |
        Remove-Item
    

Conclusión

Una buena función de registro es esencial para cualquier script de PowerShell serio. Con el Write-Log función que hemos creado, ahora tiene una forma flexible y reutilizable de agregar el registro adecuado a todos sus scripts. Recuerde adaptar la función a sus necesidades específicas; es posible que desee agregar funciones como:

Rotación de registros

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 salida (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" }
    }
}

Soporte de ruta de red

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

Notificaciones por correo electrónico de errores

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

La clave es comenzar con una base sólida y desarrollarla a partir de ahí en función de sus necesidades específicas. Estos ejemplos deberían brindarle un buen punto de partida para ampliar la función de registro básica con funciones más avanzadas.

Por ejemplo, puede combinar varias de estas funciones en una única solución de registro integral:

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

Esto sería:

  • Registre el error en formato CSV
  • Guárdelo en un recurso compartido de red
  • Enviar correo electrónico a varios destinatarios
  • Mantener 90 días de historial de registros

Recuerde realizar pruebas exhaustivas, especialmente al implementar rutas de red o notificaciones por correo electrónico, ya que estas dependencias externas pueden afectar la confiabilidad de su script. ¡Feliz guión!

Written by

Leave a comment