using System.Text; using System.Text.RegularExpressions; namespace ZA.CoreService.ESBCertificateManager.Services; public sealed class RunLogger : IDisposable { private static readonly Regex ConnectionStringSecretRegex = new( @"(Password|Pwd|Passwort)\s*=\s*[^;]+", RegexOptions.IgnoreCase | RegexOptions.Compiled); private static readonly Regex InlineSecretRegex = new( @"(Password|Pwd)\s*[:=]\s*\S+", RegexOptions.IgnoreCase | RegexOptions.Compiled); private readonly StreamWriter _writer; private readonly object _sync = new(); public string LogFilePath { get; } public RunLogger(string logDirectory) { string directory = PathResolver.ResolvePath(logDirectory); Directory.CreateDirectory(directory); string fileName = $"run-{DateTime.Now:yyyyMMdd-HHmmss}.log"; LogFilePath = Path.Combine(directory, fileName); _writer = new StreamWriter(LogFilePath, append: false, Encoding.UTF8) { AutoFlush = true }; Write($"Run gestartet von {Environment.UserName} auf {Environment.MachineName}"); } public void Write(string message) { string line = $"{DateTimeOffset.Now:yyyy-MM-dd HH:mm:ss.fff} | {Redact(message)}"; lock (_sync) { _writer.WriteLine(line); } } public void Dispose() { lock (_sync) { _writer.Dispose(); } } private static string Redact(string message) { if (string.IsNullOrEmpty(message)) { return message; } // Keine Passwörter / Connection-Secrets in Logs. string redacted = ConnectionStringSecretRegex.Replace(message, "$1=***"); return InlineSecretRegex.Replace(redacted, "$1=***"); } }