Что хороший путь состоит в том, чтобы передать полезную информацию состояния исключению в Java?

C, 68 символов, нет освобождает:

p(char *s){char *e=s;while(*++e);for(;*s==*--e&&s++<e;);return s>e;}
15
задан Elijah 13 July 2009 в 05:04
поделиться

11 ответов

Как уже говорили другие, вы можете использовать инициализаторы только при объявлении переменной.

9
ответ дан 1 December 2019 в 02:02
поделиться

Another good logging API is SLF4J. It can be configured to also intercept log APIs for Log4J, Java Util Logging, and Jakarta Commons Logging. And it can also be configured to use various logging implementations, including Log4J, Logback, Java Util Logging, and one or two others. This gives it enormous flexibility. It was developed by the author of Log4J to be its successor.

Of relevance to this question, the SLF4J API has a mechanism to concatenate string valued expressions into a log message. The following calls are equivalent, but the second is about 30x faster to process if you're not outputting debug level messages, since the concatenation is avoided:

logger.debug("The new entry is " + entry + ".");
logger.debug("The new entry is {}.", entry);

There's a two argument version too:

logger.debug("The new entry is {}. It replaces {}.", entry, oldEntry);

And for more than two you can pass in an array of Object like this:

logger.debug("Value {} was inserted between {} and {}.", 
             new Object[] {newVal, below, above});

This is a nice terse format that eliminates clutter.

Example source is from the SLF4J FAQ.

Edit: Here's a possible refactoring of your example:

try {
    doSomething(someObject.getValue());
}
catch (BadThingsHappenException bthe) {
  throw new UnhandledException(
    MessageFormatter.format("An error occurred when setting value. [value={}]", 
                              someObject.getValue()), 
    bthe);
}

Or if this pattern occurs more than a few places you could write a set of static methods that capture the commonality, something like:

try {
    doSomething(someObject.getValue());
}
catch (BadThingsHappenException bthe) {
    throwFormattedException(logger, bthe,
                            "An error occurred when setting value. [value={}]", 
                            someObject.getValue()));
}

and of course the method would also put the formatted message out on the logger for you.

5
ответ дан 1 December 2019 в 02:02
поделиться

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

try {
   setMyValue(someObject.getValue());
   doSomething(getMyValue());
}
catch (BadThingsHappenException bthe) {
   // consider this a RuntimeException wrapper class
  throw new UnhandledException(toString(), bthe);
}

Ваш toString () должен быть переопределен:

public String toString() {
   return super.toString() + "[myValue: " + getMyValue() +"]";
}

редактировать:

другая идея:

Вы можете поддерживать состояние в контексте отладки ThreadLocal . Предположим, вы создали класс с именем MyDebugUtils , который содержит ThreadLocal , который содержит карту для каждого потока. Вы разрешаете статический доступ к этому ThreadLocal и методам обслуживания (т. Е. чтобы очистить контекст после завершения отладки).

Интерфейс может быть:

public static void setValue(Object key, Object value)
public static void clearContext()
public static String getContextString() 

, а в нашем примере:

try {
   MyDebugUtils.setValue("someObeject.value", someObject.getValue());
   doSomething(someObject.getValue());
} catch (BadThingsHappenException bthe) {
   // consider this a RuntimeException wrapper class
  throw new UnhandledException(MyDebugUtils.getContextString(), bthe);
} finally {
  MyDebugUtils.clearContext(); 
}

Могут возникнуть некоторые проблемы, которые вы захотите устранить, например, обработка случаев, когда ваш метод doSomething также содержит try / catch / finally набор, который очищает контекст отладки. С этим можно справиться, допустив более тонкую детализацию в контекстной карте, чем просто поток в процессе:

public static void setValue(Object contextID, Object key, Object value)
public static void clearContext(Object contextID)
public static String getContextString(Object contextID)
2
ответ дан 1 December 2019 в 02:02
поделиться

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

Если этого недостаточно, затем сохраните фиксированное количество журналов отладки в ОЗУ. Например, последние 500 записей. Затем, когда происходит что-то ужасное, выгрузите журналы отладки вместе с отчетом о проблеме. Вы не упоминаете свою структуру ведения журнала, но это было бы довольно легко сделать в Log4J.

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

6
ответ дан 1 December 2019 в 02:02
поделиться

Я создал комбинацию клавиш в eclipse для создания блока catch.

logmsg в качестве ключа, и результат будет

catch(SomeException se){
   String msg = ""; //$NON-NLS-1$
   Object[] args = new Object[]{};

   throw new SomeException(Message.format(msg, args), se);
}

. Вы можете поместить в сообщение столько информации, сколько хотите, например:

msg = "Dump:\n varA({0}), varB({1}), varC({2}), varD({3})";
args = new Object[]{varA, varB, varC, varD};

Или некоторая информация для пользователя

msg = "Please correct the SMTP client because ({0}) seems to be wrong";
args = new Object[]{ smptClient };

Вам следует подумать об использовании log4j в качестве регистратора, чтобы вы могли распечатывать свои состояния где угодно. С помощью опций DEBUG, INFO, ERROR вы можете определить, сколько журналов вы хотите видеть в вашем файле журнала.

Когда вы доставляете свое приложение, вы устанавливаете уровень журнала на ERROR, но когда вы хотите отладить свое приложение, вы можете используйте DEBUG по умолчанию.

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

String msg = "Dump:\n varA({0}), varB({1}), varC({2}), varD({3})";
Object[] args = new Object[]{varA, varB, varC, varD};
logger.debug(Message.format(msg, args));

try{

// do action
}catch(ActionException ae){
    msg = "Please correct the SMTP client because ({0}) seems to be wrong";
    args = new Object[]{ smptClient };

    logger.error(Message.format(msg, args), se);
    throw new SomeException(Message.format(msg, args), se);
}
1
ответ дан 1 December 2019 в 02:02
поделиться

Если вы хотите каким-то образом обработать детали сообщения об ошибке, вы можете:

  • Использовать текст XML в качестве сообщения, чтобы получить структурированный способ:

     throw new UnhandledException (String.format (
     "  Неожиданные вещи  % s  ", значение), bthe);
    
  • Используйте свои собственные (и по одному для каждого случая) типы исключений для захвата информации о переменных в именованные свойства:

     throw new UnhandledValueException («Unexpected value things», value, bthe);
    

Или вы можете включить его в исходное сообщение, как предложили другие.

0
ответ дан 1 December 2019 в 02:02
поделиться

Взгляните на класс MemoryHandler из java.util.logging. Он действует как буфер между вашими вызовами log. $ Level () и фактическим выводом, и будет передавать его содержимое буфера в вывод, только если выполняется какое-то условие.

Например, вы можете настроить его на выгрузку содержимого, только если он видит сообщение уровня ERROR. Затем вы можете безопасно выводить сообщения уровня DEBUG, и никто не увидит их, если только не произойдет фактическая ошибка, а затем все сообщения будут записаны в файл журнала.

Я предполагаю, что есть аналогичные реализации для других структур ведения журнала.

РЕДАКТИРОВАТЬ: Один из возможных вариантов. Проблема с этим подходом - потеря производительности при генерации всех отладочных сообщений (см. комментарий @djna). Из-за этого может быть хорошей идеей сделать уровень ведения журнала в буфере настраиваемым - в производстве он должен быть INFO или выше, и только если вы активно ищите проблему, ее можно переключить на DEBUG.

4
ответ дан 1 December 2019 в 02:02
поделиться

Почему бы не сохранить локальную копию / список всех сообщений, которые пошли бы в журнал отладки, если бы он был включен, и передать это настраиваемому исключению, когда вы его бросаете? Что-то типа:

1
ответ дан 1 December 2019 в 02:02
поделиться

Что касается типа необходимой вам отладочной информации, почему бы вам просто всегда не регистрировать значение и не беспокоиться так сильно о локальном try / catch. Просто используйте файл конфигурации Log4J, чтобы направлять сообщения отладки в другой журнал, или используйте бензопилу, чтобы удаленно следить за сообщениями журнала. Если все это не удается, возможно, вам понадобится новый тип сообщения журнала, который нужно добавить в debug () / info () / warn () / error () / fatal (), чтобы у вас был больший контроль над тем, какие сообщения куда отправляются. Это может иметь место, когда определение добавлений в файле конфигурации log4j непрактично из-за большого количества мест, куда нужно вставить этот тип ведения журнала отладки.

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

Catch(MyDBException eDB)
{
    throw new UnhandledException("Something bad happened!", eDB);
}

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

Прежде всего, неопытные кодировщики и те, кто любит вырезать-и-вставить (или begin-mark-bug, end-mark-bug, copy-bug, copy- bug, copy-bug) его можно легко преобразовать в следующее:

Catch(MyDBException eDB)
{
    throw new UnhandledException("Something bad happened!");
}

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

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

Catch(SqlException eDB)
{
    throw new UnhandledAppException("Something bad happened!", eDB);
}

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

Это позволяет нашему коду main () делать что-то вроде this

catch(UnhandledAppException uae)
{
    \\notify user
    \\log exception
}
catch(Throwable tExcp)
{
    \\for all other unknown failures
    \\log exception

}
finally
{
    \\die gracefully
}

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

Что касается предложений «throws», наличие стратегии исключения на основе уровня означало возможность ограничить количество типов исключений, которые должны быть перечислены для каждого метода.

Это позволяло нашему коду main () делать что-то вроде этого

catch(UnhandledAppException uae)
{
    \\notify user
    \\log exception
}
catch(Throwable tExcp)
{
    \\for all other unknown failures
    \\log exception

}
finally
{
    \\die gracefully
}

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

Что касается предложений «throws», наличие стратегии исключения на основе уровня означало возможность ограничить количество типов исключений, которые должны быть перечислены для каждого метода.

Это позволяло нашему коду main () делать что-то вроде этого

catch(UnhandledAppException uae)
{
    \\notify user
    \\log exception
}
catch(Throwable tExcp)
{
    \\for all other unknown failures
    \\log exception

}
finally
{
    \\die gracefully
}

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

Что касается предложений «throws», наличие стратегии исключения на основе уровня означало возможность ограничить количество типов исключений, которые должны быть перечислены для каждого метода.

0
ответ дан 1 December 2019 в 02:02
поделиться

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

Если вы используете средства ведения журнала JDK 1.4, MemoryHandler делает именно это. Я не уверен, что используемая вами система ведения журнала делает это, но полагаю, вы сможете реализовать свой собственный аппендер / обработчик / все, что делает что-то подобное.

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

{
    String myValue = null;
    try {
        myValue = someObject.getValue();
        doSomething(myValue);
    }
    catch (BadThingsHappenException bthe) {
        String pattern = "An error occurred when setting value. [value={}]";
        // note that the format method below doesn't barf on nulls
        String detail = MessageFormatter.format(pattern, myValue);
        // consider this a RuntimeException wrapper class
        throw new UnhandledException(detail, bthe);
    }
}
2
ответ дан 1 December 2019 в 02:02
поделиться

Вы ответили на свой вопрос. Если вы хотите передать состояние исключению, вам нужно где-то сохранить свое состояние.

Вы упомянули о добавлении дополнительных переменных для этого, но вам не понравились все дополнительные переменные. Кто-то еще упомянул MemoryHandler как буфер (удерживающий состояние) между регистратором и приложением.

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

Исключения уже делают это с помощью StackTraceElements. Каждый поток хранит список трассировки стека (метод, файл, строка), который представляет его «состояние». Когда происходит исключение, оно передает трассировку стека в исключение.

То, что вам кажется, также является копией всех локальных переменных.

Это означает создание объекта для хранения всех ваших локальных переменных и использование этот объект, а не непосредственно местные жители. Затем передаем объект исключению.

1
ответ дан 1 December 2019 в 02:02
поделиться
Другие вопросы по тегам:

Похожие вопросы: