Warum diese Kombination stark ist
Convert-ToLogSafeString entschraerft problematische Zeichen und Steuerzeichen. Write-Log schreibt daraus einen konsistenten Logeintrag mit Zeit, Datum, Kontext, Thread und Komponente.
Logging Artikel
Die Kombination aus einer String-Normalisierung und einer klaren Log-Schreibfunktion ist fuer Paketierung sehr wertvoll. Sie macht Logs robuster, lesbarer und besser fuer spaetere Auswertung geeignet.
Convert-ToLogSafeString entschraerft problematische Zeichen und Steuerzeichen. Write-Log schreibt daraus einen konsistenten Logeintrag mit Zeit, Datum, Kontext, Thread und Komponente.
Besonders fuer Deploymentskripte, Prechecks, Detection-Skripte, Wrapper-Logs und alle Faelle, in denen spaeter mehrere Logquellen miteinander korreliert werden muessen.
Code
Diese Funktion bereinigt problematische Zeichen fuer Logausgaben, reduziert Mehrfach-Whitespace und verhindert, dass ein spezielles Log-Muster den Zeilenaufbau zerlegt.
function Convert-ToLogSafeString {
[CmdletBinding()]
param(
[AllowNull()]
[string]$InputString
)
if ($null -eq $InputString) {
return ''
}
if ($InputString.Length -eq 0) {
return ''
}
$sanitized = $InputString
$sanitized = $sanitized.Replace('ä', 'ae')
$sanitized = $sanitized.Replace('ö', 'oe')
$sanitized = $sanitized.Replace('ü', 'ue')
$sanitized = $sanitized.Replace('Ä', 'Ae')
$sanitized = $sanitized.Replace('Ö', 'Oe')
$sanitized = $sanitized.Replace('Ü', 'Ue')
$sanitized = $sanitized.Replace('ß', 'ss')
$sanitized = $sanitized.Replace('„', '"')
$sanitized = $sanitized.Replace('“', '"')
$sanitized = $sanitized.Replace('‚', "'")
$sanitized = $sanitized.Replace('‘', "'")
$sanitized = $sanitized.Replace('’', "'")
$sanitized = $sanitized.Replace('–', '-')
$sanitized = $sanitized.Replace('—', '-')
$sanitized = $sanitized.Replace('…', '...')
$sanitized = $sanitized.Replace("`r", ' ')
$sanitized = $sanitized.Replace("`n", ' ')
$sanitized = $sanitized.Replace("`t", ' ')
$sanitized = [regex]::Replace($sanitized, '[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]', '')
$sanitized = [regex]::Replace($sanitized, '\s{2,}', ' ')
$sanitized = $sanitized.Replace(']LOG]!>', ']LOG]!_')
$sanitized = $sanitized.Trim()
return $sanitized
}
Code
Die Funktion setzt auf die Normalisierung auf und schreibt strukturierte Eintraege mit Typ-Mapping fuer Info, Warning und Error.
function Write-Log {
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$Path,
[Parameter(Mandatory = $true)]
[AllowNull()]
[string]$Message,
[Parameter(Mandatory = $true)]
[string]$Component,
[Parameter(Mandatory = $true)]
[ValidateSet("Info", "Warning", "Error")]
[string]$Type
)
switch ($Type) {
'Info' { [int]$LogType = 1 }
'Warning' { [int]$LogType = 2 }
'Error' { [int]$LogType = 3 }
}
$SafeMessage = Convert-ToLogSafeString -InputString $Message
$SafeComponent = Convert-ToLogSafeString -InputString $Component
$Content = "<![LOG[$SafeMessage]LOG]!>" +
"<time=`"$(Get-Date -Format 'HH:mm:ss.ffffff')`" " +
"date=`"$(Get-Date -Format 'yyyy-MM-dd')`" " +
"component=`"$SafeComponent`" " +
"context=`"$([System.Security.Principal.WindowsIdentity]::GetCurrent().Name)`" " +
"type=`"$LogType`" " +
"thread=`"$([Threading.Thread]::CurrentThread.ManagedThreadId)`" " +
"file=`"`">"
$logFolder = Split-Path -Path $Path -Parent
if ($logFolder -and -not (Test-Path -Path $logFolder)) {
New-Item -Path $logFolder -ItemType Directory -Force | Out-Null
}
Add-Content -Path $Path -Value $Content -Encoding utf8
}
Einordnung
Der Logeintrag orientiert sich an einem CMTrace-kompatiblen Muster. Das ist wichtig, damit Leser sofort verstehen, warum etwa ]LOG]!> speziell behandelt wird.
Die Funktion macht Logs robuster, ersetzt aber Zeichen absichtlich. Das ist fuer Betrieb und Fehlersuche oft sinnvoller als perfekte Originaltreue.
Component sollte nicht frei schwanken. Feste Werte wie Detection, Install, Precheck oder Uninstall machen spaetere Auswertung viel besser.
Als naechste Ausbaustufe koenntest du Dateigroesse/Rotation, optionales Console-Output, LogLevel-Filter oder einen Parameter fuer CMTrace-kompatible Dateinamen ergaenzen.
Erweiterungsartikel lesenWenn du Leser nicht nur den Code sehen lassen willst, sondern auch das dahinterliegende Logformat erklaeren moechtest, lohnt sich eine eigene Einordnung des CMTrace-kompatiblen Aufbaus.
Artikel lesenSobald eine Loggingfunktion dauerhaft im produktiven Einsatz laeuft, werden Rotation, LogLevel und kontrollierter Umgang mit Dateigroesse sehr relevant.
Artikel lesen$logPath = 'C:\Logs\Packaging\deploy.log'
Write-Log -Path $logPath -Component 'Precheck' -Type Info -Message 'Starte Vorpruefung fuer App X'
Write-Log -Path $logPath -Component 'Install' -Type Warning -Message 'Alte Version wurde gefunden und wird entfernt'
Write-Log -Path $logPath -Component 'Detection' -Type Error -Message 'Dateiversion konnte nicht gelesen werden'