Вход в Java и вообще: лучшие практики?



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



Классы Библиотека



у меня есть несколько классов, библиотеки, которые могут войти некоторые INFO сообщения. Фатальные ошибки регистрируются как исключения. В настоящее время у меня есть статический экземпляр logger в моих классах с именем класса в качестве имени журнала. (К log4j-это: Logger.getLogger(MyClass.class))



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



общие журналы



в некоторых приложениях мои классы могут захотеть записать сообщение журнала в определенный журнал, не идентифицированный именем класса. (То есть:HTTP Request log) каков наилучший способ сделать такую вещь? Служба поиска приходить в голову...

579   8  

8 ответов:

ваши соглашения довольно стандартны и довольно хороши (imho).

единственное, что нужно смотреть, - это фрагментация памяти от чрезмерных необъявленных вызовов отладки, поэтому с Log4J (и большинством других фреймворков ведения журнала Java) вы получаете что-то вроде этого:

if (log.isDebugEnabled()) {
  log.debug("...");
}

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

ваш уровень регистрации информации не должен быть слишком "болтливый" (и из того, что вы говорите, это звучит так, как будто это не так). Информационные сообщения должны быть в целом значимыми и значимыми, как запуск и остановка приложения. Вещи, которые вы могли бы хотеть знать, если вы столкнулись с проблемой. Debug / fine Level logging больше используется, когда у вас действительно есть проблема, которую вы пытаетесь диагностировать. Отладка / точное ведение журнала обычно включается только при необходимости. Информация, как правило, на все время.

Если кто-то не хочет конкретных информационных сообщений от ваши классы, они, конечно, могут изменить конфигурацию log4j, чтобы не получить их. Log4j прекрасно прост в этом отделе (в отличие от Java 1.4 logging).

Что касается вашей HTTP-вещи, я вообще не обнаружил, что это проблема с журналом Java, потому что обычно один класс отвечает за то, что вас интересует, поэтому вам нужно только поместить его в одно место. В (редко в моем опыте), когда вы хотите общие сообщения журнала через, казалось бы, несвязанные классы, просто положить в какой-то маркер, который может быть легко grepped для.

на @cletus ' ответ, он писал о проблеме

if (log.isDebugEnabled()) {
  log.debug("val is " + value);
}

который может быть преодолен с помощью SL4J. Он предоставляет справку по форматированию

log.debug("val is {}", value);

где сообщение строится только если уровень отладки.

Итак, в настоящее время, используя SL4J и его спутник logger, Logback, это посоветовал для повышения производительности и стабильности.

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

private static Logger log = Logger.getLogger(${enclosing_type}.class);

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

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

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

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

Я, наверное, украл это откуда-то, но это приятно.

это снижает риск смешивания регистраторов при копировании и pasti^h^h^h рефакторинга, и это меньше, чтобы напечатать.

в коде:

private final static Logger logger = LoggerFactory.make();

...и в LoggerFactory:

public static Logger make() {
    Throwable t = new Throwable();
    StackTraceElement directCaller = t.getStackTrace()[1];
    return Logger.getLogger(directCaller.getClassName());
}

(обратите внимание, что stackdump выполняется во время инициализации. В stacktrace будет наверное не оптимизируется с помощью JVM, но на самом деле нет гарантии)

Я просматриваю лог-уровни приложения и в настоящее время обнаруживаю шаблон:

private static final Logger logger = Logger.getLogger(Things.class)

public void bla() {
  logger.debug("Starting " + ...)
  // Do stuff
  ...
  logger.debug("Situational")

  // Algorithms
  for(Thing t : things) {
    logger.trace(...)
  }

  // Breaking happy things
  if(things.isEmpty){
    logger.warn("Things shouldn't be empty!")
  }

  // Catching things
  try {
    ...
  } catch(Exception e) {
    logger.error("Something bad happened")
  }

  logger.info("Completed "+...)
}

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

подумал, что дополнительная перспектива может помочь.

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

как и сегодня, я считаю Log4j 2, безусловно, лучший вариант для входа в Java.

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

  1. я избегаю использовать SLF4J в данный момент по следующим причинам:
    • он имеет некоторые проблемы параллелизма с маркерами, которые я хочу использовать для управления журналированием операторов SQL (маркеры не такие мощные, как slf4j - обратитесь к первому комментарию Ральфа Goers)
    • он не поддерживает Java 8 лямбда, которая, опять же, я хочу использовать для повышения производительности (поддержка лямбда-выражения в логгер)
  2. делать все регулярные журналы с помощью асинхронного регистратор для лучшей производительности
  3. регистрируйте сообщения об ошибках в отдельном файле с помощью синхронного регистратора, потому что мы хотим видеть сообщения об ошибках, как только это произойдет
  4. не используйте информацию о местоположении, такую как имя файла, имя класса, имя метода, номер строки в обычном журнале, потому что для получения этой информации платформа делает снимок стека и проходит через него. Это влияет на производительность. Поэтому используйте информацию о местоположении только в журнале ошибок и не в обычном журнале
  5. поскольку мы регистрируем ошибки в отдельном файле, очень важно, чтобы мы регистрировали контекстную информацию также в журнале ошибок. Например, если приложение обнаружило ошибку при обработке файла, распечатайте имя файла и запись файла, обрабатываемую в файле журнала ошибок вместе с stacktrace
  6. файл журнала должен быть grep-способным и легким для понимания. Например, если приложение обрабатывает записи клиентов в нескольких файлах, каждый сообщение журнала должно быть следующим:
12:01:00,127 INFO FILE_NAME=file1.txt - Processing starts
12:01:00,127 DEBUG FILE_NAME=file1.txt, CUSTOMER_ID=756
12:01:00,129 INFO FILE_NAME=file1.txt - Processing ends
  1. регистрируйте все инструкции SQL с помощью маркера SQL, как показано ниже, и используйте фильтр, чтобы включить или отключить его:
private static final Marker sqlMarker = 
  MarkerManager.getMarker("SQL");

private void method1() {
    logger.debug(sqlMarker, "SELECT * FROM EMPLOYEE");
}
  1. регистрируйте все параметры с помощью Java 8 Lambdas. Это позволит сохранить приложение от форматирования сообщения, когда данный уровень журнала отключен:
int i=5, j=10;
logger.info("Sample output {}, {}", ()->i, ()->j);
  1. не использовать строку сцепление. Используйте параметризованное сообщение, как показано выше

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

  3. не используйте printStackTrace() или System.out.println()

  4. приложение должно завершить работу регистратора до выход:

LogManager.shutdown();
  1. наконец, для справки каждого, я использую следующую конфигурацию:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration monitorinterval="300" status="info" strict="true">
    <Properties>
        <Property name="filePath">${env:LOG_ROOT}/SAMPLE</Property>
        <Property name="filename">${env:LOG_ROOT}/SAMPLE/sample
        </Property>
        <property name="logSize">10 MB</property>
    </Properties>
    <Appenders>
        <RollingFile name="RollingFileRegular" fileName="${filename}.log"
            filePattern="${filePath}/sample-%d{yyyy-dd-MM}-%i.log">
            <Filters>
                <MarkerFilter marker="SQL" onMatch="DENY"
                    onMismatch="NEUTRAL" />
            </Filters>
            <PatternLayout>
                <Pattern>%d{HH:mm:ss,SSS} %m%n
                </Pattern>
            </PatternLayout>
            <Policies>
                <TimeBasedTriggeringPolicy
                    interval="1" modulate="true" />
                <SizeBasedTriggeringPolicy
                    size="${logSize}" />

            </Policies>
        </RollingFile>
        <RollingFile name="RollingFileError" 
            fileName="${filename}_error.log"
            filePattern="${filePath}/sample_error-%d{yyyy-dd-MM}-%i.log"
            immediateFlush="true">
            <PatternLayout>
                <Pattern>%d{HH:mm:ss,SSS} %p %c{1.}[%L] [%t] %m%n
                </Pattern>
            </PatternLayout>
            <Policies>
                <TimeBasedTriggeringPolicy
                    interval="1" modulate="true" />
                <SizeBasedTriggeringPolicy
                    size="${logSize}" />
            </Policies>
        </RollingFile>
    </Appenders>
    <Loggers>
        <AsyncLogger name="com"
            level="trace">
            <AppenderRef ref="RollingFileRegular"/>
        </AsyncLogger>
        <Root includeLocation="true" level="trace">
            <AppenderRef ref="RollingFileError" level="error" />
        </Root>
    </Loggers>
</Configuration>
  1. необходимые зависимости Maven находятся здесь:
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    <version>2.8.1</version>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.8.1</version>
</dependency>
<dependency>
    <groupId>com.lmax</groupId>
    <artifactId>disruptor</artifactId>
    <version>3.3.6</version>
</dependency>
<!-- (Optional)To be used when working 
with the applications using Log4j 1.x -->
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-1.2-api</artifactId>
    <version>2.8.1</version>
</dependency>

в качестве дополнения я думаю, что важно иметь в виду простой фасад ведения журнала для Java (SLF4J) (http://www.slf4j.org/). из-за некоторых проблем использования различных фреймворков ведения журнала в разнообразных частях большого проекта SLF4J является стандартом де-факто для решения проблемы успешного управления этими частями, не так ли?

второе понятие: кажется, что некоторые задачи старой школы могут быть заменены Аспектно-Ориентированное Программирование, Весна frmwrk имеет его собственный реализация, AOP-подход для ведения журнала считается здесь в StackOverflow и здесь на весеннем блоге.

Comments

    Ничего не найдено.