Как Java-программист, вы должны быть знакомы с ведением журналов.Независимо от размера проекта ведение журналов является обязательным, потому что хороший журнал может легко помочь нам найти некоторые производственные проблемы.
чего мне не хватает
Ничего не говорятSystem.out.println("Вот важный журнал");
чего мне не хватает
мечтать вместеSystem.err.println("Это журнал ошибок");
Для повседневной разработки System.out.println на самом деле очень полезен, но почему бы не использовать его для вывода журналов в реальных приложениях для разработки?
System.out.println() на самом деле не кажется очень полезным, за исключением удобства использования. Где удобство? В Eclipse вам нужно только ввести syso [output sout в IDEA], а затем нажать клавишу подсказки кода, этот метод появится автоматически, я считаю, что это причина, по которой многие новички в Java любят его, потому что я сделал то же самое в начало. Сохнуть до... [слезы здесь опущены]. Где недостатки? Это слишком много, например, печать журнала неуправляема, время печати не может быть определено, фильтр не может быть добавлен, журнал не имеет уровня...
Я помню, что впервые столкнулся с log4j. Log4j — это проект Apache с открытым исходным кодом. также может контролировать формат вывода каждого журнала; определяя уровень информации каждого журнала, мы можем более подробно контролировать процесс создания журнала. Важно отметить, что их можно гибко настроить с помощью файла конфигурации без изменения кода приложения.
Действительно, log4j, как первая и самая популярная инфраструктура ведения журналов, принесла нам большое удобство в разработке и обслуживании приложений, так почему же такая превосходная инфраструктура в конце концов сходит с «алтаря»? И постепенно заменяется логбэком? Ниже приводится выдержка из объяснения большого человека в Интернете:
С точки зрения дизайна и реализации Logback имеет относительно много улучшений по сравнению с log4j. Но хотя перечислить их все сложно, вот несколько причин, по которым вам следует выбрать logback вместо log4j. Имейте в виду, что logback и log4j концептуально похожи в том смысле, что они были созданы одной и той же группой разработчиков.
-
более быстрое выполнение
Основываясь на нашей предыдущей работе над log4j, logback переписал внутреннюю реализацию, и в некоторых конкретных сценариях он может быть даже в 10 раз быстрее, чем раньше. Обеспечивая более быструю работу компонентов журнала, требуется меньше памяти.
-
адекватное тестирование
Logback тестировался годами и бесчисленными часами. Хотя log4j также тестируется, Logback тестируется более полно и не на том же уровне, что и log4j. На наш взгляд, это самая важная причина, по которой люди выбирают Logback, а не log4j. Люди хотят, чтобы ваша система журналирования была стабильной и надежной даже в суровых условиях.
slf4j log4j logback
slf4j: Простой фасад ведения журнала для Java — это простой фасад ведения журнала для Java.
Короткий ответ заключается в том, что slf4j — это набор интерфейсов ведения журнала, slf4j существует как абстрактное поведение ведения журнала, но не обеспечивает реальной реализации.
slf4j предоставляет унифицированный интерфейс для различных фреймворков ведения журналов, так что пользователи могут регистрироваться с помощью унифицированного интерфейса, но динамически определять реальную инфраструктуру реализации. Фреймворки, такие как logback, log4j, common-logging и т. д., реализуют эти интерфейсы.
Анализ исходного кода slf4j
Долго думал, не знаю с чего начать писать Всего в пакете slf4j [версия 1.7.21] 29 классов, и перечислить их по одному невозможно. Итак, из этого предложения мы знакомы.
private static final Logger logger =
LoggerFactory.getLogger(DemoTest.class);
Приведенный выше код на самом деле является распространенным способом, которым мы обычно объявляем объект журнала в коде.
Давайте сначала посмотрим на методы, предоставляемые интерфейсом Logger;
package org.slf4j;
public interface Logger {
//根Logger
String ROOT_LOGGER_NAME = "ROOT";
String getName();
//判断记录器Trace跟踪是否激活;Trace跟踪激活后一般会打印比较详细的信息。
boolean isTraceEnabled();
//trace级别
void trace(String var1);
void trace(String var1, Object var2);
void trace(String var1, Object var2, Object var3);
void trace(String var1, Object... var2);
void trace(String var1, Throwable var2);
boolean isTraceEnabled(Marker var1);
void trace(Marker var1, String var2);
void trace(Marker var1, String var2, Object var3);
void trace(Marker var1, String var2, Object var3, Object var4);
void trace(Marker var1, String var2, Object... var3);
void trace(Marker var1, String var2, Throwable var3);
//进行预先判断,提升系统性能
boolean isDebugEnabled();
void debug(String var1);
void debug(String var1, Object var2);
void debug(String var1, Object var2, Object var3);
void debug(String var1, Object... var2);
void debug(String var1, Throwable var2);
boolean isDebugEnabled(Marker var1);
void debug(Marker var1, String var2);
void debug(Marker var1, String var2, Object var3);
void debug(Marker var1, String var2, Object var3, Object var4);
void debug(Marker var1, String var2, Object... var3);
void debug(Marker var1, String var2, Throwable var3);
//info级别
boolean isInfoEnabled();
void info(String var1);
void info(String var1, Object var2);
void info(String var1, Object var2, Object var3);
void info(String var1, Object... var2);
void info(String var1, Throwable var2);
boolean isInfoEnabled(Marker var1);
void info(Marker var1, String var2);
void info(Marker var1, String var2, Object var3);
void info(Marker var1, String var2, Object var3, Object var4);
void info(Marker var1, String var2, Object... var3);
void info(Marker var1, String var2, Throwable var3);
//warn级别
boolean isWarnEnabled();
void warn(String var1);
void warn(String var1, Object var2);
void warn(String var1, Object... var2);
void warn(String var1, Object var2, Object var3);
void warn(String var1, Throwable var2);
boolean isWarnEnabled(Marker var1);
void warn(Marker var1, String var2);
void warn(Marker var1, String var2, Object var3);
void warn(Marker var1, String var2, Object var3, Object var4);
void warn(Marker var1, String var2, Object... var3);
void warn(Marker var1, String var2, Throwable var3);
//error级别
boolean isErrorEnabled();
void error(String var1);
void error(String var1, Object var2);
void error(String var1, Object var2, Object var3);
void error(String var1, Object... var2);
void error(String var1, Throwable var2);
boolean isErrorEnabled(Marker var1);
void error(Marker var1, String var2);
void error(Marker var1, String var2, Object var3);
void error(Marker var1, String var2, Object var3, Object var4);
void error(Marker var1, String var2, Object... var3);
void error(Marker var1, String var2, Throwable var3);
}
isXXXXEnabled
Используйте isDebugEnabled, чтобы проиллюстрировать роль этого метода isXXXXEnabled.
logger.debug("the debug msg is " +doMethod());
Предполагая, что наш уровень журнала установлен на информацию, это предложение не будет выводить журнал, но этот метод все равно будет вызываться (предварительное решение). Для вызова этого метода необходимо указать параметры. Результат, возвращаемый методом doMethod(), является частью параметров. doMethod() должен выполняться в течение n секунд, и через n секунд он входит в метод debug();
Но уровень журнала - это информация. В результате журнал, несмотря на отсутствие вывода, занимает n секунд для построения параметра. Тут явно не стоит. Хотя сконструировать такой параметр за n секунд в практических приложениях практически невозможно, но при большом количестве параллелизма такая запись все равно будет влиять на производительность системы. В этот момент это должно быть записано так:
if(logger.isDebugEnabled()){
logger.debug("the debug msg is " + doMethod());
}
Далее поговорим о классе LoggerFactory, взглянем на метод getLogger как на запись:
public static Logger getLogger(String name) {
ILoggerFactory iLoggerFactory = getILoggerFactory();
return iLoggerFactory.getLogger(name);
}
public static Logger getLogger(Class<?> clazz) {
Logger logger = getLogger(clazz.getName());
if (DETECT_LOGGER_NAME_MISMATCH) {
Class<?> autoComputedCallingClass = Util.getCallingClass();
if (autoComputedCallingClass != null && nonMatchingClasses(clazz, autoComputedCallingClass)) {
Util.report(String.format("Detected logger name mismatch. Given name: \"%s\"; computed name: \"%s\".", logger.getName(), autoComputedCallingClass.getName()));
Util.report("See http://www.slf4j.org/codes.html#loggerNameMismatch for an explanation");
}
}
return logger;
}
Метод getLogger предоставляет два метода перегрузки, один из которых заключается в передаче имени, которое используется для пометки имени текущего журнала. Другой способ — предоставить объект класса, который на самом деле является именем журнала, с помощью clazz.getName().
Интерфейс ILoggerFactory хорошо виден из приведенного выше кода.
package org.slf4j;
public interface ILoggerFactory {
Logger getLogger(String var1);
}
Интерфейс ILoggerFactory на самом деле предоставляет унифицированный тип верхнего уровня для различных реализаций журнала доступа; каждая структура ведения журналов должна реализовать интерфейс ILoggerFactory, чтобы объяснить, как он предоставляет Logger. Регистраторы, такие как log4j и logback, которые могут обеспечивать иерархические отношения родитель-потомок, реализованы в классе реализации ILoggerFactory. В то же время им также необходимо реализовать интерфейс Logger для завершения ведения журнала.
Реализация в журнале
public class LoggerContext extends ContextBase implements
ILoggerFactory, LifeCycle
Как упоминалось выше, реализация различных фреймворков ведения журналов реализует интерфейс ILoggerFactory.
@Override
public final Logger getLogger(final String name) {
if (name == null) {
throw new IllegalArgumentException("name argument cannot be null");
}
// if we are asking for the root logger, then let us return it without
// wasting time
if (Logger.ROOT_LOGGER_NAME.equalsIgnoreCase(name)) {
return root;
}
int i = 0;
Logger logger = root;
// check if the desired logger exists, if it does, return it
// without further ado.
Logger childLogger = (Logger) loggerCache.get(name);
// if we have the child, then let us return it without wasting time
if (childLogger != null) {
return childLogger;
}
// if the desired logger does not exist, them create all the loggers
// in between as well (if they don't already exist)
String childName;
while (true) {
int h = LoggerNameUtil.getSeparatorIndexOf(name, i);
if (h == -1) {
childName = name;
} else {
childName = name.substring(0, h);
}
// move i left of the last point
i = h + 1;
synchronized (logger) {
childLogger = logger.getChildByName(childName);
if (childLogger == null) {
childLogger = logger.createChildByName(childName);
loggerCache.put(childName, childLogger);
incSize();
}
}
logger = childLogger;
if (h == -1) {
return childLogger;
}
}
}
Исходный код logback см. в этой серии статей:статьи из серии исходников logback
встречающиеся ямы
Самая большая яма, с которой я столкнулся в журнале, — это конфликт зависимостей log jar; потому что многие фреймворки или сторонние зависимости, используемые в проекте, могли интегрировать log framework, и иногда наши собственные проекты также будут иметь свой собственный набор решения для ведения журналов, поэтому конфликты зависимостей такого рода возникают в значительной степени. К счастью, разрешение конфликта зависимостей пакета журнала в проекте также легко исключить [idea и eclipse легко устраняются]
Во-вторых, после использования slf4j одновременно вводятся разные реализации фреймворка, например, log4j и logback вводятся одновременно.
Как видно из приведенного выше метода getLogger, вам нужно сначала получить ILoggerFactory, когда вы получаете Logger:
ILoggerFactory iLoggerFactory = getILoggerFactory();
public static ILoggerFactory getILoggerFactory() {
if (INITIALIZATION_STATE == 0) {
Class var0 = LoggerFactory.class;
synchronized(LoggerFactory.class) {
if (INITIALIZATION_STATE == 0) {
INITIALIZATION_STATE = 1;
//初始化
performInitialization();
}
}
}
switch(INITIALIZATION_STATE) {
case 1:
return SUBST_FACTORY;
case 2:
throw new IllegalStateException("org.slf4j.LoggerFactory could not be successfully initialized. See also http://www.slf4j.org/codes.html#unsuccessfulInit");
case 3:
return StaticLoggerBinder.getSingleton().getLoggerFactory();
case 4:
return NOP_FALLBACK_FACTORY;
default:
throw new IllegalStateException("Unreachable code");
}
}
В getILoggerFactory для принятия решения об инициализации используется метод PerformInitialization, в методе PerformInitialization вызывается метод связывания, и во многих случаях выводится сообщение об исключении или ошибке в методе связывания.
private static final void bind() {
String msg;
try {
Set<URL> staticLoggerBinderPathSet = null;
if (!isAndroid()) {
staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);
}
StaticLoggerBinder.getSingleton();
INITIALIZATION_STATE = 3;
reportActualBinding(staticLoggerBinderPathSet);
fixSubstituteLoggers();
replayEvents();
SUBST_FACTORY.clear();
} catch (NoClassDefFoundError var2) {
msg = var2.getMessage();
if (!messageContainsOrgSlf4jImplStaticLoggerBinder(msg)) {
failedBinding(var2);
throw var2;
}
INITIALIZATION_STATE = 4;
Util.report("Failed to load class \"org.slf4j.impl.StaticLoggerBinder\".");
Util.report("Defaulting to no-operation (NOP) logger implementation");
Util.report("See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.");
} catch (NoSuchMethodError var3) {
msg = var3.getMessage();
if (msg != null && msg.contains("org.slf4j.impl.StaticLoggerBinder.getSingleton()")) {
INITIALIZATION_STATE = 2;
Util.report("slf4j-api 1.6.x (or later) is incompatible with this binding.");
Util.report("Your binding is version 1.5.5 or earlier.");
Util.report("Upgrade your binding to version 1.6.x.");
}
throw var3;
} catch (Exception var4) {
failedBinding(var4);
throw new IllegalStateException("Unexpected initialization failure", var4);
}
}
Метод reportMultipleBindingAmbiguity используется для запроса нескольких привязок и сообщает вам, какие привязки есть в журнале.
Class path contains multiple SLF4J bindings.
reportActualBinding Это когда привязка успешна, она будет отображать, в какой структуре журнала конкретная привязка успешна через журнал.
Actual binding is of type [XXXX]
О StaticLoggerBinder
Начните со следующего исключения:
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for
further details.
Проблема в том, что не удалось загрузить файл класса org.slf4j.impl.StaticLoggerBinder, почему? Ниже приводится объяснение официального сайта по этой причине:
This error is reported when the org.slf4j.impl.StaticLoggerBinder class could not be loaded into memory. This happens when no appropriate SLF4J binding could be found on the class path. Placing one (and only one) of slf4j-nop.jar, slf4j-simple.jar, slf4j-log4j12.jar, slf4j-jdk14.jar or logback-classic.jar on the class path should solve the problem.
Эта ошибка возникает из-за того, что класс StaticLoggerBinder не может быть загружен в память. Когда это происходит, подходящий путь к классу привязки SLF4J не может быть найден. slf4j-nop ставит один (и только один). slf4j-простая банка. банка slf4j-log4j12. банка slf4j-jdk14. jar или logback-classic. Путь к классам jar должен исправить это.
То есть не было найдено какой-либо конкретной структуры ведения журнала, которая связывала бы реализацию. Поэтому нам нужно ввести конкретную банку реализации журнала.
Увидел в интернете статью про slf4j.Очень хорошо себя чувствую.Хочу поделиться с вами:Анализ исходного кода slf4j
新的环境还在适应中,博客的更新频次也直线下降,慢慢学,慢慢写吧。也希望小伙伴们多多见谅!