diff --git a/ATxCommon/Serializables/ServiceConfig.cs b/ATxCommon/Serializables/ServiceConfig.cs index 8161ce111179d68a8d838b5b4b0d613d16518998..53e441c4b7c098e263adeb11b0159243c5de62ab 100644 --- a/ATxCommon/Serializables/ServiceConfig.cs +++ b/ATxCommon/Serializables/ServiceConfig.cs @@ -17,6 +17,8 @@ namespace ATxCommon.Serializables { private static readonly Logger Log = LogManager.GetCurrentClassLogger(); + private string _healthReportEmailAddress; + #region required configuration parameters @@ -200,6 +202,20 @@ namespace ATxCommon.Serializables /// </summary> public string AdminDebugEmailAddress { get; set; } + /// <summary> + /// The mail recipient address for system health reports, falling back to AdminEmailAddress + /// in case it is not set explicitly. + /// </summary> + public string HealthReportEmailAddress { + get { + if (string.IsNullOrEmpty(_healthReportEmailAddress)) + return AdminEmailAddress; + + return _healthReportEmailAddress; + } + set => _healthReportEmailAddress = value; + } + /// <summary> /// Send an email to the user upon completed transfers. Default: true. /// </summary> @@ -561,6 +577,7 @@ namespace ATxCommon.Serializables $"EmailPrefix: {EmailPrefix}\n" + $"AdminEmailAddress: {AdminEmailAddress}\n" + $"AdminDebugEmailAddress: {AdminDebugEmailAddress}\n" + + $"HealthReportEmailAddress: {HealthReportEmailAddress}\n" + $"SendTransferNotification: {SendTransferNotification}\n" + $"SendAdminNotification: {SendAdminNotification}\n" + $"AdminNotificationDelta: {AdminNotificationDelta} min (" + diff --git a/ATxCommon/StorageStatus.cs b/ATxCommon/StorageStatus.cs index 4a3078cdd9e192c8f2e5775434c8544b717d6eb0..1556e174f496810860b579bb00dd4d0c5c4318e7 100644 --- a/ATxCommon/StorageStatus.cs +++ b/ATxCommon/StorageStatus.cs @@ -41,7 +41,7 @@ namespace ATxCommon } /// <summary> - /// Number of expired directories in the grace location. + /// Number of expired (1st-level) directories in the grace location. /// </summary> public int ExpiredDirsCount { get { @@ -50,6 +50,11 @@ namespace ATxCommon } } + /// <summary> + /// Total number of expired (2nd-level) directories in the grace location. + /// </summary> + public int ExpiredSubDirsCount { get; private set; } + /// <summary> /// Check if free space on all configured drives is above their threshold. /// </summary> @@ -82,6 +87,7 @@ namespace ATxCommon /// and all expired directories, grouped by the topmost level (i.e. user dirs).</returns> public string GraceLocationSummary() { UpdateGraceLocation(); + long totalSizeExpired = 0; var summary = "------ Grace location status, " + $"threshold: {_gracePeriod} days ({_gracePeriodHuman}) ------\n\n" + $" - location: [{_graceLocation}]\n"; @@ -92,12 +98,16 @@ namespace ATxCommon foreach (var dir in _expiredDirs.Keys) { summary += "\n - directory '" + dir + "'\n"; foreach (var subdir in _expiredDirs[dir]) { + totalSizeExpired += subdir.Size; summary += $" - {subdir.Dir.Name} " + $"[age: {subdir.HumanAgeFromName}, " + $"size: {subdir.HumanSize}]\n"; } } + summary += $"\n - summary: {ExpiredSubDirsCount} expired folders with a total size " + + $"of {Conv.BytesToString(totalSizeExpired)}\n"; + return summary; } @@ -166,6 +176,7 @@ namespace ATxCommon Log.Debug("Updating storage status: checking grace location..."); _expiredDirs.Clear(); + ExpiredSubDirsCount = 0; foreach (var userdir in _graceLocation.GetDirectories()) { Log.Trace("Scanning directory [{0}]", userdir.Name); var expired = new List<DirectoryDetails>(); @@ -178,6 +189,7 @@ namespace ATxCommon Log.Trace("Found expired directory [{0}]", dirDetails.Dir.Name); expired.Add(dirDetails); + ExpiredSubDirsCount++; } Log.Trace("Found {0} expired dirs.", expired.Count); if (expired.Count > 0) @@ -185,10 +197,11 @@ namespace ATxCommon } _lastUpdateGraceLocation = DateTime.Now; - if (_expiredDirs.Count > 0) { - Log.Debug("Updated storage status: {0} expired directories in grace location.", - _expiredDirs.Count); - } + if (_expiredDirs.Count == 0) + return; + + Log.Debug("Updated grace location status: {0} top-level dirs with a total of {1}" + + "expired sub-directories found.", _expiredDirs.Count, ExpiredSubDirsCount); } /// <summary> diff --git a/ATxCommon/SystemChecks.cs b/ATxCommon/SystemChecks.cs index bddd1ae331c73f2f705c86e606bd1c08d7540f3e..f5f53dab7da5cd17389c70d970c5753fa8bc59a4 100644 --- a/ATxCommon/SystemChecks.cs +++ b/ATxCommon/SystemChecks.cs @@ -155,11 +155,13 @@ namespace ATxCommon /// Generate an overall system health report with free space, grace location status, etc. /// </summary> /// <param name="storage">StorageStatus object used for space and grace reports.</param> + /// <param name="humanSystemName">A human-friendly name/description of the system.</param> /// <returns>A multi-line string containing the details assembled in the report. These /// comprise system uptime, free RAM, free storage space and current grace location status. /// </returns> - public static string HealthReport(StorageStatus storage) { + public static string HealthReport(StorageStatus storage, string humanSystemName="N/A") { var report = "------ System health report ------\n\n" + + $" - human name: {humanSystemName}\n" + $" - hostname: {Environment.MachineName}\n" + $" - uptime: {TimeUtils.SecondsToHuman(Uptime(), false)}\n" + $" - free system memory: {GetFreeMemory()} MB" + "\n\n"; diff --git a/ATxService/AutoTx.cs b/ATxService/AutoTx.cs index 570378608df1a27b924cc8b162e1f6052db17ab0..734759bba6f41bb81e50143b784b417b2d27cc4c 100644 --- a/ATxService/AutoTx.cs +++ b/ATxService/AutoTx.cs @@ -408,7 +408,7 @@ namespace ATxService "\n------ Loaded configuration settings ------\n" + _config.Summary(); - var health = SystemChecks.HealthReport(_storage); + var health = SystemChecks.HealthReport(_storage, _config.HostAlias); SendHealthReport(health); msg += "\n" + health; diff --git a/ATxService/Email.cs b/ATxService/Email.cs index 98c129da53d27bc0792d7ae6eceb781a17822cc1..e5b84843d18a34e8c5a6da6159e9a9d242e4b1c8 100644 --- a/ATxService/Email.cs +++ b/ATxService/Email.cs @@ -16,13 +16,14 @@ namespace ATxService /// <param name="recipient">A full email address OR a valid ActiveDirectory account.</param> /// <param name="subject">The subject, might be prefixed with a configurable string.</param> /// <param name="body">The email body.</param> - private void SendEmail(string recipient, string subject, string body) { + /// <returns>True in case an email was sent, false otherwise.</returns> + private bool SendEmail(string recipient, string subject, string body) { subject = $"{_config.EmailPrefix}{ServiceName} - {subject} - {_config.HostAlias}"; body += $"\n\n--\n[{_versionSummary}]\n"; if (string.IsNullOrEmpty(_config.SmtpHost)) { Log.Debug("SendEmail: config option <SmtpHost> is unset, not sending mail - " + "content shown below.\n[Subject] {0}\n[Body] {1}", subject, body); - return; + return false; } if (!recipient.Contains(@"@")) { Log.Trace("Invalid recipient, trying to resolve via AD: {0}", recipient); @@ -31,10 +32,10 @@ namespace ATxService if (string.IsNullOrWhiteSpace(recipient)) { Log.Info("Invalid or empty recipient given, NOT sending email!"); Log.Debug("SendEmail: {0}\n{1}", subject, body); - return; + return false; } try { - var smtpClient = new SmtpClient() { + var smtpClient = new SmtpClient { Port = _config.SmtpPort, Host = _config.SmtpHost, EnableSsl = true, @@ -52,7 +53,10 @@ namespace ATxService catch (Exception ex) { Log.Error("Error in SendEmail(): {0}\nInnerException: {1}\nStackTrace: {2}", ex.Message, ex.InnerException, ex.StackTrace); + return false; } + + return true; } /// <summary> @@ -250,7 +254,8 @@ namespace ATxService report += $"\nPrevious system health report notification was sent {elapsedHuman}.\n"; _status.LastStartupNotification = DateTime.Now; - return SendAdminEmail(report, "system health report"); + Log.Debug("Sending system health report, previous one was sent {0}", elapsedHuman); + return SendEmail(_config.HealthReportEmailAddress, "system health report", report); } } } \ No newline at end of file diff --git a/Resources/conf/config.common.xml b/Resources/conf/config.common.xml index 2e092684fd62da909aa476c047bd6d58bb2356ce..9c1d67bedc29bb56b6284a2e83dd2cba662e0c2f 100644 --- a/Resources/conf/config.common.xml +++ b/Resources/conf/config.common.xml @@ -100,6 +100,10 @@ messages to, e.g. on completed transfers --> <AdminDebugEmailAddress>admin@mydomain.xy</AdminDebugEmailAddress> + <!-- HealthReportEmailAddress: an email address where to send system health + reports to, falling back to AdminEmailAddress if empty --> + <HealthReportEmailAddress>admin@mydomain.xy</HealthReportEmailAddress> + <!-- SendTransferNotification: send email to user on finished transfers --> <SendTransferNotification>true</SendTransferNotification>