14

My question is that whats the best way to keep track of the exceptions for the administrator of the application. (Notify administrator of the thrown exceptions for maintenance purposes).

To users of the system, I believe should catch the exceptions and show an appropriate error message. To admin of the system, I suppose, best method is to have a messaging system to send details of each exception as a message to the receiver. Once receiver received a new error message persists it in the database or pings the admin an email with details of the exception.

try{
  ....
}
catch(Exception e){
   //what to do here? how to notify admin?
}
Jack
  • 6,430
  • 27
  • 80
  • 151
  • 2
    There are an unlimited number of ways to notify people of exceptions. There is no "best" way, it depends on your actual needs, the infrastructure in place, the criticality of the exception(s) in question, and so on. – Dave Newton May 14 '14 at 11:43
  • @DaveNewton different types of exceptions would be thrown, a few of them should be monitored 24/7 and the others should be logged to be considered in scheduled maintenance. Critical ones should be by notified by email but the rest can be kept in a log. – Jack May 14 '14 at 23:27
  • Email is not sufficient for critical exceptions, really. – Dave Newton May 15 '14 at 00:04
  • So whats your suggestion? would you provide me with more details? It seems many other users are looking for an answer as well. – Jack May 15 '14 at 00:06
  • 1
    If it's a *critical* exception then send a text msg. If it's just "oh dear" then an email is fine. – Dave Newton May 15 '14 at 00:33
  • @DaveNewton then whats your suggestion on sending exceptions? should I simply call a method to send the exception or any of the following answers is more suitable? – Jack May 23 '14 at 03:54
  • I think the best way is with a cannon with an automated mechanical loading mechanism and electronic firing pin. Whenever there is an exception, the discharge of the cannon will alert them instantly – Matt Coubrough May 25 '14 at 07:42
  • @MattCoubrough woudl you givve me an example :) – Jack May 26 '14 at 00:20
  • https://en.wikipedia.org/wiki/Noonday_Gun :-) – Stephen C May 05 '20 at 02:33

9 Answers9

11

I'd suggest using log4j, configured with an SMTPAppender listening to fatal logs. Then, just log a fatal level message (containing any useful information you can get) for any unhandled exception reaching your global try/catch block.

See also : What is the proper way to configure SMTPAppender in log4j?

Community
  • 1
  • 1
KeatsPeeks
  • 19,126
  • 5
  • 52
  • 83
11

Enterprise solution:

Use SL4J and save all messages to your logs.

Use MDC to add tags to your log messages. Have these tags describe who should be notified and the nature of the error:

2014-05-24 [SystemCAD][NOTIFY=ADMIN], [ACCOUNTID=123], [SEVERITY=SEVERE], [MESSAGE="Cannot contact Google.com"]  
2014-05-24 [SystemCAD][NOTIFY=USER], [ACCOUNTID=123], [SEVERITY=SEVERE], [MESSAGE="Could not save document to Google. Support has been notified."]  

Get Splunk or some product similar to index all your logs for easy searching and to create events which can be used to notify your admins. Use PagerDutty to notify your admins and create escalation, avoid duplicates, create triggers, etc.

Alexandre Santos
  • 8,170
  • 10
  • 42
  • 64
  • Whats your idea about Keat's message? – Jack May 26 '14 at 00:22
  • 2
    It is a nice solution for very small systems, but as soon as your logs start growing you will need to think of indexing them, and searching. This is what Splunk offers. Another problem is when the admins want to configure notification delivery. For example, if it is business hours, then email me, but if it is after hours, then SMS me. Or, if I am on vacation... and you simply can't ask SMTPAppender to provide all the flexibility needed. That's why you need PagerDutty. – Alexandre Santos May 26 '14 at 01:24
  • For the completeness consider to use https://www.slf4j.org/api/org/slf4j/Marker.html as a hierarchical type system for incidents https://stackoverflow.com/questions/16813032/what-is-markers-in-java-logging-frameworks-and-that-is-a-reason-to-use-them – gavenkoa Sep 26 '17 at 21:37
7

First, do not try to solve the notification problem in the application itself.

The recommended approach is to catch the exception at an appropriate point in the application and generate a log event that captures the details (including the exception) of the failure. The primary logging should be done using a standard logging system. There are a number of viable options (e.g. java.util.logging, log4j, logback, log4j2, slf4j), each with pro's and con's, but the most important thing is to not attempt to "roll your own".

That's the easy part.

The hard part is figuring out how to get the notification from the logging system to the admin in a way that is appropriate. There are many things that need to be considered:

  • The admin does not be woken up at 2am by a page reporting an over-temperature in the office water cooler.

  • The admin does not want 50 SMS message all reporting the same problem. The system needs to be able to filter out duplicates.

  • The admin needs to be able to tell the system to "shut up" about a certain problem / issue.

  • The system needs to recognize that certain events are more important than others, and that business hours versus after hours affects prioritization.

  • What is the most appropriate way to notify the admin? Email? SMS? Pager?

  • Escalation - what if the primary (on-call) admin does not respond to the notification?

  • The system also needs to be integrated with other monitoring; e.g. checking service availability, network connectivity, file system levels, CPU / load average measures, checking that important events DO happen.

  • All of this needs to be configurable, independent of the application that generated the event in the first place.

  • Ideally, you need integration with operational issue tracking system ... to help the admin relate the event to previous problems, etc.

This is a really big problem space. Fortunately, there are products out there that do this kind of thing. Too many to list here.

(IMO, it doesn't make sense to recommend a solution for you. We don't know your organization's requirements. This is something that needs to be sorted out in conjunction with operational staff & management.)

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
6

I have done notification on exception in my application using spring AOP.

For example

@Aspect
public class ExceptionAspect {

   @AfterThrowing(
      pointcut = "execution(* com.suren.customer.bo.CustomerBo.addCustomerThrowException(..))",
      throwing= "error")
    public void logAfterThrowing(JoinPoint joinPoint, Throwable error) {
    // Notify admin in email
    sendEmail(joinPoint,error);

    }
}

Common AspectJ annotations :

@Before – Run before the method execution
@After – Run after the method returned a result
@AfterReturning – Run after the method returned a result, intercept the returned result as well.
@AfterThrowing – Run after the method throws an exception
@Around – Run around the method execution, combine all three advices above.
Suren Raju
  • 3,012
  • 6
  • 26
  • 48
  • @JackMoore The two aren't mutually-exclusive; S2's Spring integration is pretty tight. – Dave Newton May 15 '14 at 00:55
  • @DaveNewton does that mean this answer is the best solution for the question? – Jack May 23 '14 at 03:55
  • 1
    To me, it doesn't look like a robust global error handling solution. It only covers certain app methods and even if you try to cover every method, sometimes error occurs within a framework or even in JSP. – Kalyan May 24 '14 at 23:56
3

When you design an application you need to consider two types of exceptions

  • User defined business exception
  • Unexpected system exception

User defined exceptions

User defined exceptions are used to pass negative conditions from one layer to another (service to web). For example in a banking application, if there is no balance in an account and if you try to withdraw money, WithdrawService might throw NoBalanceException. The web layer would catch this exception and display appropriate message to the user.

These type of exceptions are of no interest to the administrators and no alert is required. You may simply log it as info.

Unexpected system exception

Unexpected system exceptions are exceptions like database connectivity or JMS conncetivity or NullPointException or invalid message received from external system. Basically any unexpected (non-business) exceptions are classified as system exceptions.

According to Joshua Bloch in Effective Java, it is advisable not to catch system exception as you might do more harm than good. Instead allow it to propagate to the highest level (web layer).

In my applications, I provide a global exception handler (supported by Spring / Struts 2) on the web layer and send a detailed email to the ops team including the exception stack trace and redirect the request to a standard error page which says something like "Unexpected internal error occurred. Please try again".

Using this option is more secured as it will not expose the ugly exception stack trace to the user in any situation.

Struts2 reference: http://struts.apache.org/release/2.3.x/docs/exception-handling.html

Kalyan
  • 1,781
  • 11
  • 15
2

You should use a logging facility to log every exception in a file system so if Admin want they can view it through file-system.

ErrorUtil

public class ErrorLogUtil {

    public static File createErrorFile(String fileName, String productName,
            String regionName) {
        File fileErrorLogs = new File("Error Logs");
        if (!fileErrorLogs.isDirectory()) {
            fileErrorLogs.mkdir();
        }
        File fileProductName = new File(fileErrorLogs, productName);
        if (!fileProductName.isDirectory()) {
            fileProductName.mkdir();
        }

        File fileDate = null;

        if (regionName != null && regionName.trim().length() != 0) {
            File fileRegionName = new File(fileProductName, regionName);
            if (!fileRegionName.isDirectory()) {
                fileRegionName.mkdir();
            }

            fileDate = new File(fileRegionName, new SimpleDateFormat(
                    "dd-MM-yyyy").format(new Date()));
            if (!fileDate.isDirectory()) {
                fileDate.mkdir();
            }
        } else {
            fileDate = new File(fileProductName, new SimpleDateFormat(
                    "dd-MM-yyyy").format(new Date()));
            if (!fileDate.isDirectory()) {
                fileDate.mkdir();
            }
        }

        File errorFile = new File(fileDate, fileName + "-errors.txt");
        try {
            if (!errorFile.exists()) {
                errorFile.createNewFile();
                System.out.println("New Error File created=>"+errorFile.getAbsolutePath());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return errorFile;
    }

    public static void writeError(File errorFile, String error) {
        try {
            FileOutputStream fileOutputStream = new FileOutputStream(errorFile,
                    true);
            DataOutputStream out = new DataOutputStream(fileOutputStream);
            BufferedWriter bufferedWriter = new BufferedWriter(
                    new OutputStreamWriter(out));
            bufferedWriter.append((new Date())+" - "+error);
            bufferedWriter.newLine();
            bufferedWriter.flush();
            bufferedWriter.close();
            fileOutputStream.flush();
            fileOutputStream.close();
            out.flush();
            out.close();


        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void printStackTrace(File errorFile, String message, Throwable error) {
        try {
            FileOutputStream fileOutputStream = new FileOutputStream(errorFile,
                    true);
            DataOutputStream out = new DataOutputStream(fileOutputStream);
            PrintWriter bufferedWriter = new PrintWriter(
                    new BufferedWriter(new OutputStreamWriter(out)));

            bufferedWriter.println(new Date() + " : "+ message);        

            error.printStackTrace(bufferedWriter);

            bufferedWriter.println();
            bufferedWriter.close();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

Sending mail will not be good because it may fill Admin's mail box but if you really need this you can create a MailUtil and send emails to the user or keep it in a log.

MailUtil

public class MailUtil {
    public static void sendEmail(String messageString, String subject, Properties props) {

        try {
            Session mailSession = null;
            final String userName = props.getProperty("mail.from");
            final String password = props.getProperty("mail.from.password");
            mailSession = Session.getInstance(props, new javax.mail.Authenticator() {
                protected PasswordAuthentication getPasswordAuthentication() {
                    return new PasswordAuthentication(userName, password);
                }
            });

            Transport transport = mailSession.getTransport();

            MimeMessage message = new MimeMessage(mailSession);

            message.setSubject(subject);
            message.setFrom(new InternetAddress(props.getProperty("mail.from")));
            String[] to = props.getProperty("mail.to").split(",");
            for (String email : to) {

                message.addRecipient(Message.RecipientType.TO, new InternetAddress(email));
            }

            String body = messageString;
            message.setContent(body, "text/html");
            transport.connect();

            transport.sendMessage(message, message.getRecipients(Message.RecipientType.TO));
            transport.close();
        } catch (Exception exception) {
            exception.printStackTrace();
        }
    }

    public static void sendEmail(String subject, String messageString) {
        try {
            Session mailSession = null;
            Properties props=new Properties();
            FileInputStream fileInputStream = new FileInputStream(new File("mail-config.properties"));
            props.load(fileInputStream);
            fileInputStream.close();

            final String fromUsername = props.getProperty("mail.from");
            final String fromPassword = props.getProperty("mail.from.password");

            mailSession = Session.getInstance(props, new javax.mail.Authenticator() {
                protected PasswordAuthentication getPasswordAuthentication() {
                    return new PasswordAuthentication(fromUsername, fromPassword);
                }
            });

            Transport transport = mailSession.getTransport();

            MimeMessage message = new MimeMessage(mailSession);

            message.setSubject(subject);
            message.setFrom(new InternetAddress(fromUsername));
            String[] to = props.getProperty("mail.to").split(",");
            for (String email : to) {
                message.addRecipient(Message.RecipientType.TO, new InternetAddress(email));
            }

            String body = messageString;
            message.setContent(body, "text/html");
            transport.connect();

            transport.sendMessage(message, message.getRecipients(Message.RecipientType.TO));
            transport.close();
        } catch (Exception exception) {
            exception.printStackTrace();
        }
    }
}

You should use a property to manage if mail is required or not so in future you can stop mails by just changing the property file.

Pritam Banerjee
  • 17,953
  • 10
  • 93
  • 108
Ankit Katiyar
  • 2,631
  • 2
  • 20
  • 30
2

Consider using standard logging (like log4j) and using appender suited for you - either SMTP mentioned before, or custom one. There exists solutions called logging servers - they provide high flexibility in terms of notifications, filtering, storing, processing etc. Good place to start reading and investigating are Scribe and Flume. A great discussion on this subject may be found here.

There are also some cloud solutions available, from automated ones like Sentry through LogDigger (your own installation) to more low-level setups like Amazon SQS.

Community
  • 1
  • 1
jderda
  • 890
  • 6
  • 18
1

you can create exception log table. There, Write a code to insert exception with "Pending" status into database whatever exception raised in application. Create a cron job (linux) or quartz scheduler that will fired in certain period and send mail of "pending" status exception with predefined format to admin user. Update database entry with "sent" status so it will not send again.

In code, To save exception create super class,i.e.

class UserDao extends CommonDao
{

  try
   {

   }catch(Exception e)
   {
      saveException(e);
   }
}

class CommonDao
{

  public void saveException(Exception e)
  {
    //write code to insert data into database
  }
}
bNd
  • 7,512
  • 7
  • 39
  • 72
  • what do you mean by pending status and cron job are they both related to linux? how about windows platform? – Jack May 14 '14 at 05:25
  • Pending status means, In table you have number of column to log exception. you take column for status to manage whatever exception send already don't send again. you can write quartz scheduler also to fire. refer http://www.mkyong.com/java/quartz-scheduler-example/ – bNd May 14 '14 at 05:33
0

For me is not a good idea to put that behavior directly in the code of the application. It is clear that simply call to a function that sends an email in the catch clause it is easy, fast and direct. If you haven't so much time go for it.

But then you will realize that will produce some not expected collateral effects you will need to

  • Control the performance on exception parsing
  • Control what exceptions are interesting to notify and what not
  • Control not send lot of emails because a bug in the application producing exceptions constantly.

For that I prefer to use http://logstash.net/ That allows to put all your logs in a common noSQL database, and then you can use logstash to make dashboards or even create your own applications to send well designed reports about specific events. It require a bit more work at the beginning but after that I'm sure you will have more control about what is important to see in the logs and what not.

Israel
  • 446
  • 2
  • 8