Подтвердить что ты не робот

Ведение журнала Java: отображение номера исходной строки вызывающего (не вспомогательного метода ведения журнала)

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

log.info("hey");

 [INFO] [Foo:413] hey

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

log_info("hey");

[INFO] [LoggingSupport:123] hey

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

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

4b9b3361

Ответ 1

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

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

Категория .log(String callerFQCN, Уровень приоритета, Сообщение объекта, Throwable t)

и указывая класс-помощник как '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 будет работать как обычно, избегая вычисления трассировки стека, если это не требуется.

Ответ 2

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

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

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

Обновлено:

Вам придется самому рассчитать. Итак:

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

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

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

Ответ 3

Добавление информации в ответ 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 является очень дорогостоящим, вам все равно нужно найти способ отключить функцию.

Ответ 4

Полагает, что существует очень простое решение, просто добавьте 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");
}

}

Ответ 5

Для Log4j2 ответ полностью обеспечивается использованием оберток логгера, как описано в руководстве Log4j2 в разделе Пример использования сгенерированного логгера, Можно просто сгенерировать (используя проиллюстрированные там инструменты org.apache.logging.log4j.core.tools.Generate $ExtendedLogger) обертка логгера с одним уровнем STUB, а затем адаптировать ее для создания настраиваемых методов ведения журнала, имитирующих использование logIfEnabled (FQCN, LEVEL, Marker, message, Throwable) - возможно, игнорируя уровень STUB и используя обычные, а затем при желании удаляя или комментируя уровень STUB и его методы). Для этого может быть полезно FormattedMessage.

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

Теперь с полным примером: Java Logging: Log4j Version2.x: показать метод конечного клиента (не промежуточного метода ведения журнала)

Ответ 6

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

Ответ 7

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

Ответ 8

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

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 для всех из них, и вы получите правильный номер строки источника, который вам действительно нужен.