Does anyone know of a good way to limit the number of emails sent by Elmah during a time period like you can with Health Monitoring?
I want to be able to limit the emails for each error from each page to only an email once an hour or so for that particular error and page.
Looking at the elmah documentation it looks like using:
void ErrorMail_Filtering(object sender, ExceptionFilterEventArgs e)
{
// perform filtering here
}
in the global.ascx file might be an option. I could setup a static object per application that contains some the error details and the time logged and check it and cancel the email notification if need be?
Do anyone have a better solution or an example of what they are using now?
I wrote this using the same method as in your question. Seems to work nicely.
public static DateTime RoundUp(this DateTime dt, TimeSpan d)
{
return new DateTime(((dt.Ticks + d.Ticks - 1) / d.Ticks) * d.Ticks);
}
static ConcurrentDictionary<int, KeyValuePair<DateTime, string>> _concurrent = new ConcurrentDictionary<int, KeyValuePair<DateTime, string>>();
/// <summary>
/// This is an Elmah event used by the elmah engine when sending out emails. It provides an opportunity to weed out
/// irrelavent emails.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void ErrorMail_Filtering(object sender, ExceptionFilterEventArgs e)
{
preventSpammingDigestEmail(e);
}
/// <summary>
/// Prevents spamming by throttling emails to 5 minute intervals.
/// </summary>
/// <param name="e"></param>
private static void preventSpammingDigestEmail(ExceptionFilterEventArgs e)
{
DateTime roundedTimeStamp = DateTime.Now.RoundUp(TimeSpan.FromMinutes(5));
string serialisedException = Util.SerializeException(e.Exception);
var lastRaisedException = new KeyValuePair<DateTime, string>
(roundedTimeStamp, serialisedException);
int key = lastRaisedException.GetHashCode();
bool errorHasAlreadyBeenRaised = _concurrent.ContainsKey(key);
// If event has already been raised in the last five minutes dont raise again
if (errorHasAlreadyBeenRaised)
{
e.Dismiss();
return;
}
// Record that it has been raised
_concurrent.TryAdd(key, lastRaisedException);
// Clean up existing entries
Task.Factory.StartNew(() =>
{
var toRemove =
_concurrent.Where(pair => pair.Value.Key < DateTime.Now.Date).Select(pair => pair.Key).ToArray();
foreach (var i in toRemove)
{
KeyValuePair<DateTime, string> keyValuePair;
_concurrent.TryRemove(i, out keyValuePair);
}
});
}
private static string SerializeException(Exception e, string exceptionMessage = "")
{
if (e == null)
return String.Empty;
exceptionMessage = String.Format("{0}{1}{2}\n{3}", exceptionMessage, (exceptionMessage == String.Empty)
? String.Empty
: "\n\n", e.Message, e.StackTrace);
if (e.InnerException != null)
exceptionMessage = SerializeException(e.InnerException, exceptionMessage);
return exceptionMessage;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With