Si estás escribiendo scripts de PowerShell que hacen algo significativo, necesitas registro. Ya sea que estés desplegando software, gestionando servicios o automatizando tareas, tener un registro de lo que hizo tu script (o no hizo) es crucial. En este tutorial, aprenderás cómo crear una función de registro de PowerShell simple pero efectiva.
Requisitos previos
Si deseas seguir este tutorial, asegúrate 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
Imaginemos que estás escribiendo un script para instalar silenciosamente algún software. El enfoque básico podría verse algo 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
Arreglemos estos problemas construyendo una función de registro adecuada.
Construyendo una Función Básica de Escribir-Registro
Primero, creemos una función simple que añada 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 usarla 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 que se ven así:
09:42:15 - Starting install... 09:43:22 - Finished install.
¡Mucho más limpio! Pero podemos hacerlo mejor.
Agregando más funcionalidad
Mejoremos nuestra función de registro con algunas características útiles:
- Rutas de registro personalizadas
- Diferentes niveles de registro (Info, 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 te brinda mucha más flexibilidad. Aquí te explicamos cómo usarla:
# 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) se verá así:
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
Nota cómo cada entrada incluye la marca de tiempo, el nivel de registro entre corchetes y el mensaje. Este formato estructurado facilita el análisis de registros y la identificación rápida de problemas.
Ejemplo del mundo real: Script de instalación de software
Pongamos nuestra función de registro a trabajar en un script real que instala software en silencio:
# 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á algo 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
Aquí hay algunas mejores prácticas al usar esta función de registro:
-
Siempre registra el inicio y el final de tu script – Esto ayuda a rastrear el tiempo de ejecución del script y el estado de finalización.
-
Utiliza niveles de registro apropiados – No marques todo como un error; utiliza el nivel correcto para la situación:
- Información: Operaciones normales
- Advertencia: Problemas no críticos
- Error: Problemas críticos que necesitan atención
-
Incluye detalles relevantes – Registra suficiente información para entender qué sucedió:
# Malo Write-Log "Falló la conexión" # Bueno Write-Log "Falló la conexión al servidor 'SQL01' - tiempo de espera después de 30 segundos" -Level Error
-
Limpia los registros antiguos – Considera agregar rotación de registros para evitar llenar el espacio en disco:
# Eliminar registros más antiguos de 30 días 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 la función Write-Log
que hemos construido, ahora tienes una forma flexible y reutilizable de agregar un registro adecuado a todos tus scripts. Recuerda adaptar la función a tus necesidades específicas – podrías querer agregar características 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 Rutas 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 para 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 construir a partir de ahí según tus necesidades específicas. Estos ejemplos deberían darte un buen punto de partida para extender la función básica de registro con características más avanzadas.
Por ejemplo, podrías combinar varias de estas características en una solución de registro única y completa:
Write-Log -Message "Critical error in payment processing" ` -Level Error ` -Format CSV ` -NetworkPath "\\\\server\\logs" ` -NotifyOnError "[email protected]","[email protected]" ` -MaxLogFiles 90
Esto permitiría:
- Registrar el error en formato CSV
- Almacenarlo en una carpeta compartida en red
- Enviar por correo electrónico a varios destinatarios
- Mantener un historial de registro de 90 días
Recuerda probar exhaustivamente, especialmente al implementar rutas de red o notificaciones por correo electrónico, ya que estas dependencias externas pueden afectar la confiabilidad de tu script. ¡Feliz programación!
Source:
https://adamtheautomator.com/powershell-write-log-tutorial/