Private/Write-TechCareLog.ps1

function Write-TechCareLog {
    <#
    .SYNOPSIS
        Registra una linea en el log de la ejecucion y la muestra en consola.
    .DESCRIPTION
        Unico punto de salida a consola del modulo. Centralizarlo aqui permite que
        el resto del codigo no use Write-Host, y que el log en memoria y el fichero
        no puedan divergir.
    #>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute(
        'PSAvoidUsingWriteHost', '',
        Justification = 'Es una herramienta interactiva para un tecnico delante de la consola: el color distingue un fallo de un aviso. Write-Output contaminaria el objeto de resultado que devuelven los cmdlets, y Write-Information esta silenciado por defecto en 5.1.')]
    [CmdletBinding()]
    [OutputType([void])]
    param(
        [Parameter(Mandatory, Position = 0)]
        [AllowEmptyString()]
        [string]$Message,

        [ValidateSet('Info', 'Detail', 'Success', 'Warning', 'Error')]
        [string]$Level = 'Info',

        [string]$ModuleName = '',

        [string]$ErrorText = ''
    )

    $entry = [pscustomobject]@{
        TimeUtc = (Get-Date).ToUniversalTime().ToString('s') + 'Z'
        Level   = $Level
        Module  = $ModuleName
        Message = $Message
        Error   = $ErrorText
    }
    [void]$script:State.Log.Add($entry)

    $color = 'Gray'
    switch ($Level) {
        'Detail'  { $color = 'DarkGray' }
        'Success' { $color = 'Green' }
        'Warning' { $color = 'Yellow' }
        'Error'   { $color = 'Red' }
    }

    $prefix = ''
    if ($ModuleName) { $prefix = '[' + $ModuleName + '] ' }
    Write-Host ('{0}{1}' -f $prefix, $Message) -ForegroundColor $color

    # El log se escribe incrementalmente, no solo al generar el informe: si DISM
    # cuelga la sesion o el tecnico cierra la ventana, lo ya hecho queda en disco.
    if ($script:State.LogPath) {
        $line = '{0} [{1}] {2}{3}' -f $entry.TimeUtc, $Level, $prefix, $Message
        if ($ErrorText) { $line = $line + ' :: ' + $ErrorText }
        try {
            # UTF8 sin BOM explicito: Add-Content -Encoding UTF8 antepone BOM en
            # Windows PowerShell 5.1 y no en 7, y el log se escribe linea a linea.
            [System.IO.File]::AppendAllText(
                $script:State.LogPath,
                $line + [System.Environment]::NewLine,
                (New-Object -TypeName System.Text.UTF8Encoding -ArgumentList $false))
        } catch {
            # Un log que no se puede escribir no debe tumbar el mantenimiento. Se
            # desactiva para no repetir el fallo en cada linea restante.
            $script:State.LogPath = $null
        }
    }
}