Каким образом можно уведомлять администратора о новых исключениях приложения Java?

Мой вопрос в том, что лучший способ отслеживать исключения для администратора приложения. (Сообщите администратору о выброшенных исключениях для целей технического обслуживания).

Пользователям системы, я считаю, что следует перехватывать исключения и показывать соответствующее сообщение об ошибке. Для администратора системы, я полагаю, лучшим способом является наличие системы обмена сообщениями для отправки деталей каждого исключения в виде сообщения получателю. Когда получатель получил новое сообщение об ошибке, оно сохраняется в базе данных или отправляет администратору электронное письмо с подробностями об исключении.

try{
  ....
}
catch(Exception e){
   //what to do here? how to notify admin?
}

Ответ 1

Я бы предложил использовать log4j, настроенный с помощью SMTPAppender для прослушивания фатальных журналов. Затем просто зарегистрируйте сообщение о смертельном уровне (содержащее любую полезную информацию, которую вы можете получить) для любого необработанного исключения, достигающего вашего глобального блока try/catch.

Смотрите также: Каков правильный способ настройки SMTPAppender в log4j?

Ответ 2

Корпоративное решение:

Используйте SL4J и сохраните все сообщения в своих журналах.

Используйте MDC для добавления тегов в сообщения журнала. Сообщите ли эти теги, кто должен быть уведомлен, и характер ошибки:

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."]  

Получите Splunk или какой-нибудь продукт, похожий на индекс всех ваших журналов для легкого поиска и создания событий, которые могут быть использованы для уведомления ваших администраторов, Используйте PagerDutty, чтобы уведомлять администраторов и создавать эскалацию, избегать дубликатов, создавать триггеры и т.д.

Ответ 3

Во-первых, не пытайтесь решить проблему уведомления в самом приложении.

Рекомендуемый подход заключается в том, чтобы поймать исключение в соответствующей точке приложения и создать событие журнала, которое фиксирует детали (включая исключение) отказа. Первичное ведение журнала должно выполняться с использованием стандартной системы регистрации. Существует ряд жизнеспособных опций (например, java.util.logging, log4j, logback, log4j2, slf4j), каждый из которых имеет pro и con, но самое главное - не пытаться "сворачивать свои собственные".

Это легкая часть.

Жесткая часть - это выяснить, как получить уведомление от системы ведения журнала администратору соответствующим образом. Существует много вещей, которые необходимо учитывать:

  • Администратор не пробуждается в 2 часа на странице, сообщающей о перегреве в охладителе офисной воды.

  • Администратор не хочет, чтобы 50 SMS-сообщений сообщали об одной и той же проблеме. Система должна иметь возможность отфильтровывать дубликаты.

  • Администратор должен иметь возможность сказать системе "заткнуться" о какой-либо проблеме/проблеме.

  • Система должна признать, что определенные события более важны, чем другие, и что рабочие часы в зависимости от часов влияют на приоритизацию.

  • Каков наилучший способ уведомления администратора? Эл. адрес? СМС? Пейджер?

  • Эскалация - что, если основной (по вызову) администратор не отвечает на уведомление?

  • Система также должна быть интегрирована с другим мониторингом; например проверка доступности сервиса, сетевое подключение, уровни файловой системы, средние показатели CPU/нагрузки, проверка того, что важные события происходят.

  • Все это необходимо настраивать независимо от приложения, которое создало событие в первую очередь.

  • В идеале вам нужна интеграция с операционной системой отслеживания проблем..., чтобы помочь администратору связать событие с предыдущими проблемами и т.д.

Это действительно большое проблемное пространство. К счастью, есть продукты, которые делают такие вещи. Слишком много, чтобы перечислить здесь.

(IMO, нет смысла рекомендовать вам решение. Мы не знаем ваших требований к организации. Это то, что нужно сортировать в сочетании с оперативным персоналом и руководством.)

Ответ 4

Я сделал уведомление об исключении в своем приложении с помощью spring AOP.

Например

@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);

    }
}

Общие аннотации AspectJ:

@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.

Ответ 5

Вы должны использовать средство ведения журнала для регистрации всех исключений в файловой системе, поэтому, если администратор хочет, чтобы они могли просматривать его через файловую систему.

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();
    }
}

}

Отправка почты не будет хорошей, поскольку она может заполнять почтовый ящик администратора, но если вам это действительно нужно, вы можете создать MailUtil и отправить электронное письмо пользователю или сохранить его в журнале.

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();
    }
}

}

Вы должны использовать свойство для управления, если требуется почта или нет, поэтому в будущем вы можете остановить почту, просто изменив файл свойств.

Ответ 6

При разработке приложения вам необходимо рассмотреть два типа исключений

  • Пользовательское исключение для бизнеса
  • Неожиданное системное исключение

Определения, определенные пользователем

Пользовательские исключения используются для передачи отрицательных условий с одного уровня на другой (услуга в сеть). Например, в банковском приложении, если в счете нет баланса, и если вы попытаетесь снять деньги, WithdrawService может выкинуть NoBalanceException. Веб-слой поймает это исключение и отобразит соответствующее сообщение пользователю.

Эти типы исключений не представляют интереса для администраторов, и предупреждение не требуется. Вы можете просто зарегистрировать его как информацию.

Неожиданное системное исключение

Неожиданными системными исключениями являются исключения, такие как подключение к базе данных или подключение JMS или исключение NullPointException или недопустимое сообщение, полученное из внешней системы. В принципе, любые непредвиденные (нерабочие) исключения классифицируются как системные исключения.

Согласно Джошуа Блоху в "Эффективной Java", рекомендуется не улавливать системное исключение, поскольку вы можете причинить больше вреда, чем пользы. Вместо этого позвольте ему распространяться на самый высокий уровень (веб-слой).

В моих приложениях я предоставляю глобальный обработчик исключений (поддерживаемый Spring/Struts 2) на веб-уровне и отправляю подробное электронное письмо в команду ops, включая трассировку стека исключений, и перенаправляет запрос на стандартную страницу ошибок в котором говорится что-то вроде "Произошла непредвиденная внутренняя ошибка. Повторите попытку".

Использование этой опции более защищено, поскольку в любой ситуации она не будет выставлять уродливую трассировку стека исключений.

Ссылка Struts2: http://struts.apache.org/release/2.3.x/docs/exception-handling.html

Ответ 7

вы можете создать таблицу журнала исключений. Там напишите код, чтобы вставить исключение с состоянием "Ожидание" в базу данных, любое исключение, поднятое в приложении. Создайте cron job (linux) или quartz scheduler, которые будут запущены в определенный период и отправят почту "ожидающего" исключения статуса с предопределенным форматом для пользователя администратора. Обновите запись базы данных с "отправленным" статусом, чтобы она больше не отправлялась.

В коде, Чтобы сохранить исключение, создайте суперкласс, т.е.

class UserDao extends CommonDao
{

  try
   {

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

class CommonDao
{

  public void saveException(Exception e)
  {
    //write code to insert data into database
  }
}

Ответ 8

Рассмотрим использование стандартного ведения журнала (например, log4j) и использование подходящего для вас приложения - либо SMTP, упомянутого ранее, либо настраиваемого. Существуют решения, называемые серверами протоколирования - они обеспечивают высокую гибкость с точки зрения уведомлений, фильтрации, хранения, обработки и т.д. Хорошее место для начала чтения и исследования - Scribe и Flume. Отличную дискуссию по этому вопросу можно найти здесь.

Есть также некоторые облачные решения, доступные из автоматических, таких как Sentry через LogDigger (ваша собственная установка) для более низкоуровневых настроек, таких как Amazon SQS.

Ответ 9

Для меня не очень хорошая идея поместить это поведение непосредственно в код приложения. Понятно, что просто вызвать функцию, которая отправляет электронное письмо в предложение catch, это легко, быстро и прямо. Если у вас не так много времени для этого.

Но тогда вы поймете, что создадут некоторые ожидаемые побочные эффекты, которые вам понадобятся для

  • Управление производительностью при анализе исключений
  • Контролируйте, какие исключения интересны для уведомления, а что нет
  • Контроль не отправляет много писем, потому что ошибка в приложении, производящем исключения постоянно.

Для этого я предпочитаю использовать http://logstash.net/ Это позволяет помещать все ваши журналы в общую базу данных noSQL, а затем вы можете использовать logstash создавать информационные панели или даже создавать собственные приложения для отправки хорошо разработанных отчетов о конкретных событиях. Это требует немного больше работы в начале, но после этого я уверен, что у вас будет больше контроля над тем, что важно увидеть в журналах, а что нет.