diff --git a/Documentation.en.md b/Documentation.en.md index 4ab167a..087b105 100644 --- a/Documentation.en.md +++ b/Documentation.en.md @@ -145,6 +145,25 @@ File: `src\HostAvailabilityMonitor\appsettings.json` - `HipVirtualMachines`: optional static VM checks - `Targets`: endpoints to probe +### Runtime section + +Important fields in `Runtime`: + +- `MaxConcurrency`: maximum number of parallel endpoint checks +- `DefaultValidateUncPathAccess`: default for UNC targets without their own `ValidateUncPathAccess` value +- `NonZeroExitCodeOnAnyFailure`: exit with code `2` when endpoint failures are found +- `NonZeroExitCodeOnExecutionError`: exit with code `1` on technical execution errors +- `StateDirectory`: directory for the state file +- `StateFileName`: state file name, default `monitor-state.json` + +### Logging section + +Important fields in `Logging`: + +- `LogDirectory`: directory for rolling log files +- `FilePrefix`: prefix for daily log files +- `RetentionDays`: number of daily log files to retain, default `30`; `0` disables automatic cleanup + ### Email section Important fields in `Email`: @@ -330,12 +349,13 @@ It contains: - `Logging` - `EventLog` - `Email` +- `HipVirtualMachines` `Targets` is rebuilt by the generator at runtime. Important: -When the generator writes the monitor JSON, the daily summary options and static HIP VM settings are also copied into the generated target file. That means `GeneratedMonitorConfig.Email.SendDailySummary`, `DailySummaryHourLocal`, `DailySummaryMinuteLocal`, and `GeneratedMonitorConfig.HipVirtualMachines` flow directly into the output monitor configuration. +When the generator writes the monitor JSON, runtime, logging, daily summary, and static HIP VM settings are also copied into the generated target file. That means values such as `GeneratedMonitorConfig.Runtime.StateFileName`, `GeneratedMonitorConfig.Logging.RetentionDays`, `GeneratedMonitorConfig.Email.SendDailySummary`, `DailySummaryHourLocal`, `DailySummaryMinuteLocal`, and `GeneratedMonitorConfig.HipVirtualMachines` flow directly into the output monitor configuration. --- @@ -472,7 +492,7 @@ The generator is designed defensively: The monitor is also defensive: - daily rolling logs -- default retention of 5 days +- default retention of 30 days, configurable through `Logging.RetentionDays` - event log writes are best effort only - state file is written atomically - failure/recovery emails follow explicit notification rules diff --git a/Dokumentation.md b/Dokumentation.md index 5f78240..8b343da 100644 --- a/Dokumentation.md +++ b/Dokumentation.md @@ -145,6 +145,25 @@ Datei: `src\HostAvailabilityMonitor\appsettings.json` - `HipVirtualMachines`: optionale statische VM-Prüfungen - `Targets`: die zu prüfenden Endpunkte +### Runtime + +Wichtige Felder im Abschnitt `Runtime`: + +- `MaxConcurrency`: maximale Anzahl paralleler Endpoint-Checks +- `DefaultValidateUncPathAccess`: Standardwert fuer UNC-Targets ohne eigenen `ValidateUncPathAccess`-Wert +- `NonZeroExitCodeOnAnyFailure`: bei fachlichen DOWN-Ergebnissen mit Exitcode `2` beenden +- `NonZeroExitCodeOnExecutionError`: bei technischen Ausfuehrungsfehlern mit Exitcode `1` beenden +- `StateDirectory`: Verzeichnis fuer die Statusdatei +- `StateFileName`: Dateiname der Statusdatei, standardmaessig `monitor-state.json` + +### Logging + +Wichtige Felder im Abschnitt `Logging`: + +- `LogDirectory`: Verzeichnis fuer die Rolling-Logdateien +- `FilePrefix`: Prefix der taeglichen Logdateien +- `RetentionDays`: Anzahl der aufzubewahrenden taeglichen Logdateien, standardmaessig `30`; `0` deaktiviert die automatische Bereinigung + ### Email Wichtige Felder im Abschnitt `Email`: @@ -330,12 +349,13 @@ Hier werden gepflegt: - `Logging` - `EventLog` - `Email` +- `HipVirtualMachines` `Targets` wird vom Generator zur Laufzeit neu erzeugt. Wichtig: -Wenn der Generator die Monitor-JSON erzeugt, werden auch die Felder für die Tagesstatus-Mail und die statische HIP-VM-Prüfung mit erzeugt. Das heißt: `GeneratedMonitorConfig.Email.SendDailySummary`, `DailySummaryHourLocal`, `DailySummaryMinuteLocal` und `GeneratedMonitorConfig.HipVirtualMachines` werden direkt in die Zielkonfiguration übernommen. +Wenn der Generator die Monitor-JSON erzeugt, werden auch Runtime-, Logging-, Tagesstatus-Mail- und statische HIP-VM-Felder mit erzeugt. Das heißt: Werte wie `GeneratedMonitorConfig.Runtime.StateFileName`, `GeneratedMonitorConfig.Logging.RetentionDays`, `GeneratedMonitorConfig.Email.SendDailySummary`, `DailySummaryHourLocal`, `DailySummaryMinuteLocal` und `GeneratedMonitorConfig.HipVirtualMachines` werden direkt in die Zielkonfiguration übernommen. --- @@ -472,7 +492,7 @@ Der Generator ist defensiv gebaut: Der Monitor ist ebenfalls defensiv gebaut: - Rolling-Dateilog mit täglicher Rotation -- Retention standardmäßig 5 Tage +- Retention standardmäßig 30 Tage, konfigurierbar über `Logging.RetentionDays` - Eventlog-Schreiben nur best effort - Statusdatei mit atomischem Schreiben - Failure-/Recovery-Mails nur bei definierten Bedingungen diff --git a/README.md b/README.md index 6017b42..1fc5c7d 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ Diese Struktur ist bewusst einfach gehalten, damit das Repository sauber in **Gi - Task-Scheduler-tauglich - tägliche Logrotation -- Log-Retention standardmäßig 5 Tage +- Log-Retention standardmäßig 30 Tage - Windows Application Event Log - SMTP-Mails bei DOWN, Recovery (`DOWN -> UP`) und täglichem Status - SMTP-Sendports bzw. reine E-Mail-Empfaengerlisten werden vom Generator uebersprungen und vom Monitor defensiv nur als `Skipped` protokolliert, nicht alarmiert @@ -341,6 +341,8 @@ Wichtige Felder: - `BizTalk.ApplicationMappings`: Mapping von BizTalk-Anwendungsnamen auf fachliche App-Namen - `Output.Path`: Pfad der zu erzeugenden Monitor-JSON - `GeneratedMonitorConfig`: Vorlage für Logging, Runtime, Event Log und Mail des Monitors +- `GeneratedMonitorConfig.Logging.RetentionDays`: Aufbewahrung der taeglichen Monitor-Logdateien, standardmaessig `30` +- `GeneratedMonitorConfig.Runtime.StateFileName`: Dateiname der Statusdatei, falls mehrere Monitor-Konfigurationen getrennten Zustand brauchen - `GeneratedMonitorConfig.HipVirtualMachines`: statische HIP-VM-Prüfung in der generierten Monitor-JSON einschalten - `GeneratedMonitorConfig.Email.SendDailySummary`: tägliche Status-Mail in der generierten Monitor-JSON einschalten - `GeneratedMonitorConfig.Email.DailySummaryHourLocal`: lokale Uhrzeit der Tageszusammenfassung diff --git a/src/BizTalkMonitorConfigGenerator/Configuration/GeneratorConfiguration.cs b/src/BizTalkMonitorConfigGenerator/Configuration/GeneratorConfiguration.cs index d528d20..a7ecf9c 100644 --- a/src/BizTalkMonitorConfigGenerator/Configuration/GeneratorConfiguration.cs +++ b/src/BizTalkMonitorConfigGenerator/Configuration/GeneratorConfiguration.cs @@ -213,7 +213,7 @@ namespace BizTalkMonitorConfigGenerator.Configuration /// /// Gets or sets the number of daily generator log files to retain. /// - public int RetentionDays { get; set; } = 5; + public int RetentionDays { get; set; } = 30; } /// @@ -424,6 +424,23 @@ namespace BizTalkMonitorConfigGenerator.Configuration configuration.Runtime.MaxConcurrency = 1; } + if (string.IsNullOrWhiteSpace(configuration.Runtime.StateDirectory)) + { + configuration.Runtime.StateDirectory = "state"; + } + + if (string.IsNullOrWhiteSpace(configuration.Runtime.StateFileName)) + { + configuration.Runtime.StateFileName = "monitor-state.json"; + } + + if (Path.IsPathRooted(configuration.Runtime.StateFileName) + || configuration.Runtime.StateFileName.IndexOfAny(Path.GetInvalidFileNameChars()) >= 0 + || configuration.Runtime.StateFileName.IndexOfAny(new[] { '\\', '/' }) >= 0) + { + throw new InvalidOperationException("GeneratedMonitorConfig.Runtime.StateFileName muss ein Dateiname ohne Pfadanteile sein."); + } + if (configuration.Logging.RetentionDays < 0) { throw new InvalidOperationException("GeneratedMonitorConfig.Logging.RetentionDays darf nicht negativ sein."); @@ -493,7 +510,8 @@ namespace BizTalkMonitorConfigGenerator.Configuration DefaultValidateUncPathAccess = configuration.Runtime.DefaultValidateUncPathAccess, NonZeroExitCodeOnAnyFailure = configuration.Runtime.NonZeroExitCodeOnAnyFailure, NonZeroExitCodeOnExecutionError = configuration.Runtime.NonZeroExitCodeOnExecutionError, - StateDirectory = configuration.Runtime.StateDirectory + StateDirectory = configuration.Runtime.StateDirectory, + StateFileName = configuration.Runtime.StateFileName }, Logging = new LoggingOptions { diff --git a/src/BizTalkMonitorConfigGenerator/Program.cs b/src/BizTalkMonitorConfigGenerator/Program.cs index c2b2596..5db5fdc 100644 --- a/src/BizTalkMonitorConfigGenerator/Program.cs +++ b/src/BizTalkMonitorConfigGenerator/Program.cs @@ -241,7 +241,7 @@ namespace BizTalkMonitorConfigGenerator try { var directory = ResolvePath(baseDirectory ?? AppDomain.CurrentDomain.BaseDirectory, "logs"); - var fallbackLogger = new GeneratorFileLogger(directory, "biztalk-monitor-config-generator-startup", 5, "BizTalkMonitorConfigGenerator", "Startup"); + var fallbackLogger = new GeneratorFileLogger(directory, "biztalk-monitor-config-generator-startup", 30, "BizTalkMonitorConfigGenerator", "Startup"); fallbackLogger.Error("Generator konnte nicht gestartet werden. Konfiguration=" + (configPath ?? "(nicht aufgeloest)") + ". Fehler=" + exception.GetType().Name + ": " + exception.Message); fallbackLogger.Error("Details: " + exception); WriteConsoleInfo("Fallback-Log geschrieben: " + BuildLogFilePreview(directory, "biztalk-monitor-config-generator-startup")); diff --git a/src/BizTalkMonitorConfigGenerator/generator-settings.json b/src/BizTalkMonitorConfigGenerator/generator-settings.json index 0d1e383..48fb7bd 100644 --- a/src/BizTalkMonitorConfigGenerator/generator-settings.json +++ b/src/BizTalkMonitorConfigGenerator/generator-settings.json @@ -4,7 +4,7 @@ "Logging": { "LogDirectory": "logs", "FilePrefix": "biztalk-monitor-config-generator", - "RetentionDays": 5 + "RetentionDays": 30 }, "EventLog": { "Enabled": true, @@ -52,12 +52,13 @@ "DefaultValidateUncPathAccess": false, "NonZeroExitCodeOnAnyFailure": false, "NonZeroExitCodeOnExecutionError": true, - "StateDirectory": "state" + "StateDirectory": "state", + "StateFileName": "monitor-state.json" }, "Logging": { "LogDirectory": "logs", "FilePrefix": "host-availability-monitor", - "RetentionDays": 5 + "RetentionDays": 30 }, "EventLog": { "Enabled": true, diff --git a/src/HostAvailabilityMonitor/Configuration/MonitorConfiguration.cs b/src/HostAvailabilityMonitor/Configuration/MonitorConfiguration.cs index 45f9551..7e2b73d 100644 --- a/src/HostAvailabilityMonitor/Configuration/MonitorConfiguration.cs +++ b/src/HostAvailabilityMonitor/Configuration/MonitorConfiguration.cs @@ -158,6 +158,23 @@ namespace HostAvailabilityMonitor.Configuration Runtime.MaxConcurrency = 1; } + if (string.IsNullOrWhiteSpace(Runtime.StateDirectory)) + { + Runtime.StateDirectory = "state"; + } + + if (string.IsNullOrWhiteSpace(Runtime.StateFileName)) + { + Runtime.StateFileName = "monitor-state.json"; + } + + if (Path.IsPathRooted(Runtime.StateFileName) + || Runtime.StateFileName.IndexOfAny(Path.GetInvalidFileNameChars()) >= 0 + || Runtime.StateFileName.IndexOfAny(new[] { '\\', '/' }) >= 0) + { + throw new InvalidOperationException("Runtime.StateFileName muss ein Dateiname ohne Pfadanteile sein."); + } + if (HipVirtualMachines == null) { HipVirtualMachines = new HipVirtualMachineOptions(); @@ -286,6 +303,11 @@ namespace HostAvailabilityMonitor.Configuration /// Gets or sets the state directory path. /// public string StateDirectory { get; set; } = "state"; + + /// + /// Gets or sets the state file name inside the configured state directory. + /// + public string StateFileName { get; set; } = "monitor-state.json"; } /// @@ -306,7 +328,7 @@ namespace HostAvailabilityMonitor.Configuration /// /// Gets or sets the number of daily log files to retain. /// - public int RetentionDays { get; set; } = 5; + public int RetentionDays { get; set; } = 30; } /// diff --git a/src/HostAvailabilityMonitor/Program.cs b/src/HostAvailabilityMonitor/Program.cs index 97dd506..6dd551a 100644 --- a/src/HostAvailabilityMonitor/Program.cs +++ b/src/HostAvailabilityMonitor/Program.cs @@ -58,7 +58,7 @@ namespace HostAvailabilityMonitor var logDirectory = ResolvePath(baseDirectory, config.Logging.LogDirectory); var stateDirectory = ResolvePath(baseDirectory, config.Runtime.StateDirectory); - var stateFilePath = Path.Combine(stateDirectory, "monitor-state.json"); + var stateFilePath = Path.Combine(stateDirectory, config.Runtime.StateFileName); WriteConsoleInfo("Initialisiere Dateilog: " + BuildLogFilePreview(logDirectory, config.Logging.FilePrefix)); logger = new FileLogger(logDirectory, config.Logging.FilePrefix, config.Logging.RetentionDays, config.ApplicationName, config.EnvironmentName); @@ -699,7 +699,7 @@ namespace HostAvailabilityMonitor try { var directory = ResolvePath(baseDirectory ?? AppDomain.CurrentDomain.BaseDirectory, "logs"); - var fallbackLogger = new FileLogger(directory, "host-availability-monitor-startup", 5, "HostAvailabilityMonitor", "Startup"); + var fallbackLogger = new FileLogger(directory, "host-availability-monitor-startup", 30, "HostAvailabilityMonitor", "Startup"); fallbackLogger.Error("Monitor konnte nicht gestartet werden. Konfiguration=" + (configPath ?? "(nicht aufgeloest)") + ". Fehler=" + exception.GetType().Name + ": " + exception.Message); fallbackLogger.Error("Details: " + exception); WriteConsoleInfo("Fallback-Log geschrieben: " + BuildLogFilePreview(directory, "host-availability-monitor-startup")); diff --git a/src/HostAvailabilityMonitor/appsettings.json b/src/HostAvailabilityMonitor/appsettings.json index 1340d8d..2a3f198 100644 --- a/src/HostAvailabilityMonitor/appsettings.json +++ b/src/HostAvailabilityMonitor/appsettings.json @@ -6,12 +6,13 @@ "DefaultValidateUncPathAccess": false, "NonZeroExitCodeOnAnyFailure": false, "NonZeroExitCodeOnExecutionError": true, - "StateDirectory": "state" + "StateDirectory": "state", + "StateFileName": "monitor-state.json" }, "Logging": { "LogDirectory": "logs", "FilePrefix": "host-availability-monitor", - "RetentionDays": 5 + "RetentionDays": 30 }, "EventLog": { "Enabled": true,