2009-09-28 3 views
38

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

log.info("hey"); 

[INFO] [Foo:413] hey 

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

log_info("hey"); 

[INFO] [LoggingSupport:123] hey 

Есть ли способ сказать системе регистрации, чтобы удалить один кадр из стека вызовов, когда выяснить местоположение источника для печати?

Я полагаю, что это конкретная реализация; мне нужен Log4J через Commons Logging, но мне интересно узнать о других вариантах.

+1

+1 интересный вопрос. – KLE

+0

Связанный: [Вызов методов журнала log4j косвенно (из вспомогательного метода)] (http://stackoverflow.com/questions/23880055/calling-log4js-log-methods-indirectly-from-a-helper-method) –

ответ

4

Обратите внимание, что предоставление номера строки является чем-то очень дорогостоящим, либо за то, что вы получаете естественно из Log4j или из следующего. Вы должны признать, что стоимость ...

Вы можете использовать следующие интерфейсы:

StackTraceElement[] stackTraces = Thread.currentThread().getStackTrace(); 
    StackTraceElement stackTraceElement = ...; 
    stackTraceElement.getLineNumber(); 

Обновлено:

Вы бы рассчитать его самостоятельно. Итак:

  • спросить log4j не выводить его (в вашем формате лесозаготовительной),
  • и вставьте себе номер строки explicitement в начале сообщения (строка, которую вы посылаете log4j).

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

  • использование явного Logger (передаются в качестве параметра я думаю), когда это необходимо (иногда определить конкретные регистратор для конкретного контекста, для Например, у нас есть регистратор для отправки наших запросов на базу данных, независимо от того, какой класс это делает, это позволяет нам свести к одному месту изменения, внесенные в наш файл конфигурации, когда мы хотим (деактивировать их) ...)
  • использовать регистратор для вызывающего класса: в этом случае вместо передачи параметра вы можете вывести вызов er class name аналогичным образом ...
+1

Я полагаю, что Log4J делает что-то подобное. Я хочу сказать, что он пропускает другой кадр в своем вычислении. Было бы хорошо, если бы я сам делал все расчеты, но мне все равно нужно каким-то образом передать эту информацию в систему ведения журнала, иначе параметры форматирования журнала не будут распознавать мои номера модных строк. – Thilo

+0

Да, вы должны были бы рассчитать это самостоятельно. Поэтому попросите log4j не выводить его (в вашем формате ведения журналов) и не вставлять строку номера в начале вашего сообщения (строка, которую вы отправляете в log4j). (Я добавляю это к ответу) – KLE

+0

Стоимость намного меньше в современных JVM. –

1

Это невозможно из коробки. Лучшее, что вы можете сделать в этом случае, - создать регистратор в вызывающем устройстве и передать его методу утилизации. Таким образом, вы можете по крайней мере получить представление о том, откуда пришел звонок.

+0

Да, это дало бы мне правильное имя категории регистратора (но все же неправильный номер строки). Это улучшение ... – Thilo

+0

Я думаю, что, возможно, мне нужно, чтобы вспомогательный метод просто создавал сообщение журнала (строка), а затем напрямую вызывал журнал: log.info (getLogMessage («hey»)) – Thilo

+1

Теперь это возможно используя средство обертки logger log4j2 и метод logIfEnabled, который может опросить трассировку стека для номинированного имени класса FQCN полностью, см. мой ответ и ссылки выше –

2

Добавление информации в ответ KLE. (извините, пользователь noob, не знаю лучшего способа, чем создание отдельного ответа)

Вместо того, чтобы вставлять номер строки в сообщение, вы можете поместить его в контекст MDC. См. Org.apache.log4j.MDC

Например:

StackTraceElement[] stackTraces = Thread.currentThread().getStackTrace(); 
StackTraceElement stackTraceElement = ...; 
int l = stackTraceElement.getLineNumber(); 

MDC.put("myLineNumber", l); 

Это позволяет пользователям использовать mylineNumber в их файл конфигурации log4j

<layout class="org.apache.log4j.PatternLayout"> 
    <param name="ConversionPattern" 
      value="Line(%X{myLineNumber})- %m%n"/> 
</layout> 

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

29

Альтернативный ответ.

Можно попросить log4j исключить вспомогательный класс с помощью метода

Category.log (String callerFQCN, уровень приоритета, сообщение Object, Throwable т)

и указав вспомогательный класс как " callerFQCN.

Например здесь есть класс с помощью помощника:

public class TheClass { 
    public static void main(String...strings) { 
     LoggingHelper.log("Message using full log method in logging helper."); 
     LoggingHelper.logNotWorking("Message using class info method"); 
}} 

и код помощника:

public class LoggingHelper { 
private static Logger LOG = Logger.getLogger(LoggingHelper.class); 

public static void log(String message) { 
    LOG.log(LoggingHelper.class.getCanonicalName(), Level.INFO, message, null); 
} 

public static void logNotWorking(String message) { 
    LOG.info(message); 
} } 

Первый метод будет выдавать ваш ожидаемый результат.

 
Line(TheClass.main(TheClass.java:4)) Message using full log method in logging helper. 
Line(LoggingHelper.logNotWorking(LoggingHelper.java:12)) Message using class info method 

При использовании этого метода Log4j будет работать как обычно, избегая вычисления трассировки стека, если это не требуется.

+0

. Я также ищу это решение, но насколько я могу сказать, метод вы описываете для [log4j1.2 API] (https://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/Category.html), который предлагает Category.log (String callerFQCN, Priority level , Сообщение объекта, Throwable t). Кажется, что это не эквивалент для [Logger в log4J 2.5 API] (https://logging.apache.org/log4j/2.0/log4j-api/apidocs/index.html). Может ли кто-нибудь предложить ответ, совместимый с прямым использованием Log4J 2.x? –

+0

[Java Logging: Log4j Version2.x: показать метод вызывающего (не промежуточного метода ведения журнала)] (http://stackoverflow.com/questions/35360887/java-logging-log4j-version2-x-show- the-method-of-the-call-not-an-intermedia) –

1

Если у вас есть собственные методы использования журналов, вы можете добавить имя и имя файла в список аргументов ведения журнала и выполнить маршрут cpp. т. е. Preprocess вы источник для замены тегов, как _ LINE _ и _ FILE _ перед тем, как сделать компиляцию. В качестве дополнительного бонуса это не потребует меньше ресурсов, чем выяснение во время выполнения.

+0

Есть ли у Java такой препроцессор? – Thilo

+0

Напишите загрузчик классов или просто подготовьте файл jar, который сканирует классы для протоколирования операторов и соответственно заменяет заполнители. –

0

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

public @interface SkipFrame {} 

// helper function 
@SkipFrame // not necessary on the concrete log function 
void log(String... message) { 
    // getStackTrace()... 
    int callerDepth = 2; // a constant number depends on implementation 
    StackTraceElement callerElement = null; 
    for (StackTraceElement e: stackTrace) { 
     String className, methodName = e.getClassName, getMethodName()... 
     Class callClass = Class.forName(className); 
     // since there maybe several methods with the same name 
     // here skip those overloaded methods 
     Method callMethod = guessWhichMethodWithoutSignature(callClass, methodName); 
     SkipFrame skipFrame = callMethod.getAnnotation(SkipFrame.class); 
     if (skipFrame != null) 
      continue; // skip this stack trace element 
     if (callerDepth-- == 0) { 
      callerElement = e; 
      break; 
     } 
    } 
    assert callerDepth == 0; 
    assert callerElement != null; 
    Log4j.info(callerElement.getLineNumber()... + "message... "); 
} 

@SkipFrame 
void logSendMail(Mail mailObject) { 
    log("Send mail " + mailObject.getSubject()); 
} 

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

2

Выходит, что есть очень простое решение, просто добавьте FQCN (класс-оболочка полное имя класса) к вашему регистратору помощнику:

public class MyLogger extends Logger { 

private static final String FQCN = MyLogger.class.getName() + "."; 

protected MyLogger(String name) { 
    super(name); 
} 

public void info(final Object msg) { 
    super.log(FQCN, Level.INFO, msg, null); 
} 

//etc... 

в рабочем классе вы просто сделать:

public class MyClass { 

private static final Logger LOG = MyLogger.getLogger(); 

private void test() 
{ 
    LOG.info("test"); 
} 

} 
+1

Как это делает вам номер строки? –

+0

@SamuelEdwinWard Рамка ведения журнала определит номер строки. Строка FQCN просто сообщит журналу, сколько кадров стека игнорируется. –

2

Для Log4j2 ответ предоставляется полностью с помощью оберток логгера, как описано в руководстве Log4j2 под номером Example Usage of a Generated Logger Wrapper. Можно просто сгенерировать (используя org.apache.logging.log4j.core.tools.Сгенерируйте инструменты ExtendedLogger, проиллюстрированные там), обертка журнала с одним уровнем STUB, а затем адаптируйте ее для создания собственных методов ведения журнала, имитирующих использование logIfEnabled (FQCN, LEVEL, Marker, message, Throwable) - возможно, игнорируя уровень STUB и используя регулярные - тогда, при желании, удаление или комментирование уровня STUB и его методов). Для этого может оказаться полезным FormattedMessage.

Исходную строку, в то время как дорого, можно легко показать как часть полной информации о местоположении, используя элемент шаблона преобразования% l в PatternLayout, указанный в конфигурации, или, более конкретно, с использованием номера строки% L и/или преобразование метода% M.

Теперь с полным примером: Java Logging: Log4j Version2.x: show the method of an end-client caller (not an intermediate logging helper method)