Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Log4Net smtp appender only send email when error with full log(debug & info & error). Only when the application ends

I am trying to configure a smtp appender in the log4net.config file that I have. The problem is that I have looked all over the internet and cannot find how to send an email when an error occurs with all the other log information included such as info, debug, error, fatal. Only when the application ends (NOT every time an ERROR occurs).

So I only want to receive this email when: The application ends + With all the log information (DEBUG, INFO, ERROR, FATAL) + Only if an ERROR has occured.

Elaborating some more this is because of the way I handle my exceptions in c sharp, with multiple level handling all over the place and so if an error occurs no matter how many times I only want to receive one email. Also I do not want to use multiple logs, but rather just one in root.

Thanks.

like image 805
Pasha Immortals Avatar asked May 05 '11 08:05

Pasha Immortals


2 Answers

SmtpAppender cannot accomplish this on its own. So what I did was create another appender that to an appender of type MemoryAppender. I set a threshold on this logger to only include messages that should trigger the SmtpAppender, e.g. Error. We use this to later determine if we want to send the email which has more levels logged.

We don't actually care about the messages in the MemoryAppender--we just care that it contains messages at the end. The messages we get via email actually come from the SmtpAppender.

At the end of my program I check the memory appender to see if its GetEvents() contains any events. If so, I do not stop the SmtpAppender from running normally.

Log4Net configs for both appenders:

<appender name="ErrorHolder" type="log4net.Appender.MemoryAppender" >
    <onlyFixPartialEventData value="true" />
    <!-- if *any* message is logged with this level, the email appender will 
         be used with its own level -->
    <threshold value="ERROR" />
</appender>

<appender name="Email" type="log4net.Appender.SmtpAppender">    
    <!-- the level you want to see in the email IF ErrorHolder finds anything -->
    <threshold value="INFO"/> 
    <bufferSize value="512" />
    <lossy value="false" /> <!-- important! -->   
    <to value="[email protected]" />
    <from value="[email protected]" />
    <subject value="ERROR: subject" />
    <smtpHost value="smtpserver" />    
    <layout type="log4net.Layout.PatternLayout">
      <conversionPattern value="%newline%date [%thread] %-5level %logger [%property{NDC}] - %message%newline%newline%newline" />
    </layout>    
</appender>

<root>
  <level value="ALL" />
  <appender-ref ref="ErrorHolder" />
  <appender-ref ref="Email" />
</root>

At the end of the app run this to disable the SmtpAppender if the ErrorHolder appender is empty:

// trigger loggers if errors occurred:
var memoryAppender = ((Hierarchy)LogManager.GetRepository())
    .Root.Appenders.OfType<MemoryAppender>().FirstOrDefault();

if (memoryAppender != null && memoryAppender.GetEvents().Length == 0)
{
    // there was no error so don't email anything
    var smtpAppender = ((Hierarchy)LogManager.GetRepository())
        .Root.Appenders.OfType<SmtpAppender>().FirstOrDefault();

    if (smtpAppender != null)
    {
        smtpAppender.Threshold = Level.Off;
        smtpAppender.ActivateOptions();
    }
}
like image 197
Pasha Immortals Avatar answered Nov 12 '22 12:11

Pasha Immortals


This sounds like an application configuration issue rather than a log4net configuration issue. I would suggest putting a method in at the close of your application that emails you the log file if it detects that there is an error inside it. You could either detect this error by flipping a global variable from false to true in every place where you log errors or you could wait until the end of your application and then read the log file to see if it contains errors. The first method would be quicker at shutdown but it means modifying your code in multiple places. The latter would allow you to just add one method but it might take longer in a large file.

A third option would be to send errors to a second log file (so they go two places) using log4net. Then, when your application is closing and you are checking to see if you should email the log, just check for the existence of the error-only file. If it exists, delete it (so it isn't there next time) and email the full log.

like image 36
IAmTimCorey Avatar answered Nov 12 '22 11:11

IAmTimCorey