From ecf825ac347879287da63952c9c8220b62b6d4f7 Mon Sep 17 00:00:00 2001 From: Niko Ehrenfeuchter <nikolaus.ehrenfeuchter@unibas.ch> Date: Wed, 31 Jan 2018 00:10:03 +0100 Subject: [PATCH] Wrap mail targets for NLog by a rate-limiting wrapper. To avoid flooding recipients and / or SMTP servers. Refers to #3, #4 --- ATXCommon/ATXCommon.csproj | 1 + ATXCommon/NLog/RateLimitWrapper.cs | 34 ++++++++++++++++++++++++++++++ AutoTx/AutoTx.cs | 20 ++++++++++++++---- 3 files changed, 51 insertions(+), 4 deletions(-) create mode 100644 ATXCommon/NLog/RateLimitWrapper.cs diff --git a/ATXCommon/ATXCommon.csproj b/ATXCommon/ATXCommon.csproj index dcc7ac5..a2297d0 100644 --- a/ATXCommon/ATXCommon.csproj +++ b/ATXCommon/ATXCommon.csproj @@ -49,6 +49,7 @@ <Compile Include="ActiveDirectory.cs" /> <Compile Include="Conv.cs" /> <Compile Include="FsUtils.cs" /> + <Compile Include="NLog\RateLimitWrapper.cs" /> <Compile Include="Serializables\DriveToCheck.cs" /> <Compile Include="Serializables\ServiceConfig.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> diff --git a/ATXCommon/NLog/RateLimitWrapper.cs b/ATXCommon/NLog/RateLimitWrapper.cs new file mode 100644 index 0000000..b86dded --- /dev/null +++ b/ATXCommon/NLog/RateLimitWrapper.cs @@ -0,0 +1,34 @@ +using System; +using System.ComponentModel; +using NLog.Common; +using NLog.Targets; +using NLog.Targets.Wrappers; + +namespace ATXCommon.NLog +{ + /// <summary> + /// A wrapper target for NLog, limiting the rate of messages being logged. + /// + /// Meant to be used in conjunction with the MailTarget class, to avoid flooding the recipient + /// with too many emails and probably being banned by the SMTP server for spamming. + /// NOTE: should always be used in combination with another target (FileTarget) to ensure that + /// all messages are being logged, including those ones discarded by *this* target. + /// </summary> + [Target("FrequencyWrapper", IsWrapper = true)] + public class RateLimitWrapper : WrapperTargetBase + { + private DateTime _lastLogEvent = DateTime.MinValue; + + protected override void Write(AsyncLogEventInfo logEvent) { + if ((DateTime.Now - _lastLogEvent).TotalMinutes >= MinLogInterval) { + _lastLogEvent = DateTime.Now; + WrappedTarget.WriteAsyncLogEvent(logEvent); + } else { + logEvent.Continuation(null); + } + } + + [DefaultValue(30)] + public int MinLogInterval { get; set; } + } +} diff --git a/AutoTx/AutoTx.cs b/AutoTx/AutoTx.cs index 70db56e..8afd10a 100644 --- a/AutoTx/AutoTx.cs +++ b/AutoTx/AutoTx.cs @@ -10,6 +10,7 @@ using NLog; using NLog.Config; using NLog.Targets; using ATXCommon; +using ATXCommon.NLog; using ATXCommon.Serializables; using RoboSharp; @@ -120,6 +121,8 @@ namespace AutoTx _config.HostAlias, Environment.MachineName, LogFormatDefault); var logConfig = LogManager.Configuration; + + // "Fatal" target var mailTargetFatal = new MailTarget { Name = "mailfatal", SmtpServer = _config.SmtpHost, @@ -129,9 +132,14 @@ namespace AutoTx Subject = subject, Body = body, }; - logConfig.AddTarget(mailTargetFatal); - logConfig.AddRuleForOneLevel(LogLevel.Fatal, mailTargetFatal); + var mailTargetFatalLimited = new RateLimitWrapper { + Name = "mailfatallimited", + WrappedTarget = mailTargetFatal + }; + logConfig.AddTarget(mailTargetFatalLimited); + logConfig.AddRuleForOneLevel(LogLevel.Fatal, mailTargetFatalLimited); + // "Error" target if (!string.IsNullOrWhiteSpace(_config.AdminDebugEmailAdress)) { var mailTargetError = new MailTarget { Name = "mailerror", @@ -142,8 +150,12 @@ namespace AutoTx Subject = subject, Body = body, }; - logConfig.AddTarget(mailTargetError); - logConfig.AddRuleForOneLevel(LogLevel.Error, mailTargetError); + var mailTargetErrorLimited = new RateLimitWrapper { + Name = "mailerrorlimited", + WrappedTarget = mailTargetError + }; + logConfig.AddTarget(mailTargetErrorLimited); + logConfig.AddRuleForOneLevel(LogLevel.Error, mailTargetErrorLimited); Log.Info("Configured mail notification for 'Error' messages to {0}", mailTargetError.To); } Log.Info("Configured mail notification for 'Fatal' messages to {0}", mailTargetFatal.To); -- GitLab