Proceso de salida y arquitectura de inicio de sesión

Cuenta pública de WeChat_CoderLiLogback se compone principalmente de tres frascos juntos

  • slf4j-api
  • inicio de sesión-núcleo
  • logback-clásico
<dependencies>
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>1.2.3</version>
    </dependency>
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-core</artifactId>
        <version>1.2.3</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.25</version>
    </dependency>
</dependencies>
复制代码
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Main {
    public static void main(String[] args) {
        Logger logger = LoggerFactory.getLogger(Main.class);
        logger.debug("hello world");
    }
}
复制代码

Cuando no hay una configuración predeterminada, Logback agregará un ConsoleAppender al registrador raíz

Logback puede imprimir su información de estado interna a través de StatusPrinter y StatusManager

private static void test2(){
    LoggerContext loggerContext  = (LoggerContext) LoggerFactory.getILoggerFactory();
    StatusManager statusManager = loggerContext.getStatusManager();
    StatusPrinter.print(statusManager);
}
复制代码

imprimir de la siguiente manera

21:20:30,270 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback-test.xml]
21:20:30,270 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback.groovy]
21:20:30,271 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback.xml]
21:20:30,274 |-INFO in ch.qos.logback.classic.BasicConfigurator@6d8a00e3 - Setting up default configuration.
复制代码

No se encontraron tres archivos de configuración predeterminados

  • logback-prueba.xml
  • logback.groovy
  • inicio de sesión.xml

Solo se puede usar la configuración predeterminada para enviar registros a la consola.

Appender como destino para la salida

  • consola
  • expediente
  • base de datos
  • SQM

Si ocurre un error, Logback imprimirá su propio estado interno en la consola

tres componentes

  • Registrador
  • anexador
  • Layous

Logger como parte del módulo logback-classic, la interfaz Appender y Layouts como parte de logback-core, como módulo general, logback-core no tiene concepto de Logger

LoggerContext

Cuenta pública de WeChat: CoderLi

Implementa la interfaz ILoggerFactory de slf4j para generar registradores

Cuenta pública de WeChat: CoderLi

Variable de miembro principal de LoggerContext

imagen-20220322213444464

  • El primero es registrador raíz
  • El segundo es un caché, por lo que el nombre del registrador distingue entre mayúsculas y minúsculas.
  • TurboFilter como filtro global

Jerarquía

El registrador de Logback se divide por nombre .como una estructura jerárquica

com.demo.MainUn registrador llamado , cuyo registrador principal es com.demoregistrador, y más arriba en comla web esroot

public final class Logger implements org.slf4j.Logger, LocationAwareLogger, AppenderAttachable<ILoggingEvent>, Serializable {
    private static final long serialVersionUID = 5454405123156820674L; // 8745934908040027998L;
    public static final String FQCN = ch.qos.logback.classic.Logger.class.getName();
    private String name;
    transient private Level level;
    transient private int effectiveLevelInt;
    transient private Logger parent;
    transient private List<Logger> childrenList;
复制代码

字段 parent 就是用来存放父级 logger 的、而 childrenList 则是存放子 logger 的、当父 logger 的等级发生改变的时候就是依靠该字段去通知子 logger 。

有效等级

我们知道如果我们 logger 设置的等级大于调用的方法、那么该日志不会被输出、显然这种等值的比较、可以依靠数值来实现。而这个值在 Logger 类中就是 effectiveLevelInt 这个字段

public static final int OFF_INT = Integer.MAX_VALUE;
public static final int ERROR_INT = 40000;
public static final int WARN_INT = 30000;
public static final int INFO_INT = 20000;
public static final int DEBUG_INT = 10000;
public static final int TRACE_INT = 5000;
public static final int ALL_INT = Integer.MIN_VALUE;
    public static final Level OFF = new Level(OFF_INT, "OFF");
    public static final Level ERROR = new Level(ERROR_INT, "ERROR");
    public static final Level WARN = new Level(WARN_INT, "WARN");
    public static final Level INFO = new Level(INFO_INT, "INFO");
    public static final Level DEBUG = new Level(DEBUG_INT, "DEBUG");
    public static final Level TRACE = new Level(TRACE_INT, "TRACE");
    public static final Level ALL = new Level(ALL_INT, "ALL");
复制代码

我们可以看到

public LoggerContext() {
    super();
    this.loggerCache = new ConcurrentHashMap<String, Logger>();

    this.loggerContextRemoteView = new LoggerContextVO(this);
    this.root = new Logger(Logger.ROOT_LOGGER_NAME, null, this);
    this.root.setLevel(Level.DEBUG);
    loggerCache.put(Logger.ROOT_LOGGER_NAME, root);
    initEvaluatorMap();
    size = 1;
    this.frameworkPackages = new ArrayList<String>();
}
复制代码

默认情况下 root logger 的 level 为 debug、effectiveLevelInt 的值为 10000

默认情况下、如果我们创建使用的 logger 没有指定 level 的话、它会继承它的父类的 effectiveLevelInt

Logger createChildByName(final String childName) {
		........
    Logger childLogger;
    childLogger = new Logger(childName, this, this.loggerContext);
    childrenList.add(childLogger);
    childLogger.effectiveLevelInt = this.effectiveLevelInt;
    return childLogger;
} 
复制代码

this 就是父 logger

当 父 logger level 发生变化时、会调用方法通知子 logger

Logger createChildByName(final String childName) {
    int i_index = LoggerNameUtil.getSeparatorIndexOf(childName, this.name.length() + 1);
    if (i_index != -1) {
        throw new IllegalArgumentException("For logger [" + this.name + "] child name [" + childName
                        + " passed as parameter, may not include '.' after index" + (this.name.length() + 1));
    }

    if (childrenList == null) {
        childrenList = new CopyOnWriteArrayList<Logger>();
    }
    Logger childLogger;
    childLogger = new Logger(childName, this, this.loggerContext);
    childrenList.add(childLogger);
    childLogger.effectiveLevelInt = this.effectiveLevelInt;
    return childLogger;
}
复制代码

demo 如下

private static void test3(){
    ch.qos.logback.classic.Logger logger = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger("x.y");
    logger.setLevel(Level.INFO);
    logger.info("info");
    logger.debug("debug");
    ch.qos.logback.classic.Logger childLogger = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger("x.y.z");
    childLogger.info("info");
    childLogger.debug("debug");
    logger.setLevel(Level.TRACE);
    logger.trace("trace");
    logger.debug("debug");
    childLogger.trace("trace");
    childLogger.debug("debug");
}
复制代码

输出的话、你自己想想

Appender

一个 Logger 可以有多个 Appender、比如说日志既可以输出到控制台也可以到日志文件

// Logger 类中使用 AppenderAttachableImpl<ILoggingEvent> aai 保存、内部使用 COWList 保存
public synchronized void addAppender(Appender<ILoggingEvent> newAppender) {
    if (aai == null) {
        aai = new AppenderAttachableImpl<ILoggingEvent>();
    }
    aai.addAppender(newAppender);
}
复制代码

Appender 默认也是具有继承的特性、比如说 root 的 Appender 是 Console、而子 logger 的 Appender 是 file、那么使用子 logger 打印日志、日志即会输出到日志文件也会输出到 console 中

// Logger
public void callAppenders(ILoggingEvent event) {
    int writes = 0;
    for (Logger l = this; l != null; l = l.parent) {
        writes += l.appendLoopOnAppenders(event);
        if (!l.additive) {
            break;
        }
    }
    // No appenders in hierarchy
    if (writes == 0) {
        loggerContext.noAppenderDefinedWarning(this);
    }
}
复制代码
// AppenderAttachableImpl
public int appendLoopOnAppenders(E e) {
    int size = 0;
    final Appender<E>[] appenderArray = appenderList.asTypedArray();
    final int len = appenderArray.length;
    for (int i = 0; i < len; i++) {
        appenderArray[i].doAppend(e);
        size++;
    }
    return size;
}
复制代码

先调用自己的 Appender 然后调用父类的。

可以将 additive 设置为 false、那么子 logger 不再对父 logger 有继承 Appender

流程

过滤链

public void info(String msg) {
    filterAndLog_0_Or3Plus(FQCN, null, Level.INFO, msg, null, null);
}

private void filterAndLog_0_Or3Plus(final String localFQCN, final Marker marker, final Level level, final String msg, final Object[] params,
                                    final Throwable t) {

  final FilterReply decision = loggerContext.getTurboFilterChainDecision_0_3OrMore(marker, this, level, msg, params, t);

  if (decision == FilterReply.NEUTRAL) {
    if (effectiveLevelInt > level.levelInt) {
      return;
    }
  } else if (decision == FilterReply.DENY) {
    return;
  }

  buildLoggingEventAndAppend(localFQCN, marker, level, msg, params, t);
}
复制代码

从 LoggerContext 中获取 TurboFilterList

final FilterReply getTurboFilterChainDecision_0_3OrMore(final Marker marker, final Logger logger, final Level level, final String format,
                final Object[] params, final Throwable t) {
    if (turboFilterList.size() == 0) {
        return FilterReply.NEUTRAL;
    }
    return turboFilterList.getTurboFilterChainDecision(marker, logger, level, format, params, t);
}
复制代码

默认情况是没有 turboFilter 、所以会返回 NEUTRAL

final FilterReply getTurboFilterChainDecision_0_3OrMore(final Marker marker, final Logger logger, final Level level, final String format,
                final Object[] params, final Throwable t) {
    if (turboFilterList.size() == 0) {
        return FilterReply.NEUTRAL;
    }
    return turboFilterList.getTurboFilterChainDecision(marker, logger, level, format, params, t);
}
复制代码
public FilterReply getTurboFilterChainDecision(final Marker marker, final Logger logger, final Level level, final String format, final Object[] params,
                final Throwable t) {

    final int size = size();
    // if (size == 0) {
    // return FilterReply.NEUTRAL;
    // }
    if (size == 1) {
        try {
            TurboFilter tf = get(0);
            return tf.decide(marker, logger, level, format, params, t);
        } catch (IndexOutOfBoundsException iobe) {
            return FilterReply.NEUTRAL;
        }
    }

    Object[] tfa = toArray();
    final int len = tfa.length;
    for (int i = 0; i < len; i++) {
        // for (TurboFilter tf : this) {
        final TurboFilter tf = (TurboFilter) tfa[i];
        final FilterReply r = tf.decide(marker, logger, level, format, params, t);
        if (r == FilterReply.DENY || r == FilterReply.ACCEPT) {
            return r;
        }
    }
    return FilterReply.NEUTRAL;
}
复制代码

如果存在多个、如果明确是 DENY 或者 ACCPET 的话、直接返回、如果不确定、那么遍历到最后返回 NEUTRAL

Si el retorno es NEUTRO, se juzga si el nivel del método llamado es mayor que el nivel establecido por el registrador. Si es menor que eso, no se imprimirá y regresará directamente.

Del mismo modo, volver a DENY también es un regreso directo, solo ACCEPT puede continuar bajando

Objeto LoggingEvent

private void buildLoggingEventAndAppend(final String localFQCN, final Marker marker, final Level level, final String msg, final Object[] params,
                final Throwable t) {
    LoggingEvent le = new LoggingEvent(localFQCN, this, level, msg, t, params);
    le.setMarker(marker);
    callAppenders(le);
}
复制代码

Objeto LoggingEvent, este objeto contiene todos los parámetros relevantes de la solicitud de registro, el registrador solicitado, el nivel de la solicitud de registro, la información de registro, la información de excepción pasada con el registro, la hora actual, el hilo actual y diversa información del clase actual y MDC.

Anexador de llamadas

public void callAppenders(ILoggingEvent event) {
    int writes = 0;
    for (Logger l = this; l != null; l = l.parent) {
        writes += l.appendLoopOnAppenders(event);
        if (!l.additive) {
            break;
        }
    }
    // No appenders in hierarchy
    if (writes == 0) {
        loggerContext.noAppenderDefinedWarning(this);
    }
}
复制代码

Esto fue mencionado anteriormente

public int appendLoopOnAppenders(E e) {
    int size = 0;
    final Appender<E>[] appenderArray = appenderList.asTypedArray();
    final int len = appenderArray.length;
    for (int i = 0; i < len; i++) {
        appenderArray[i].doAppend(e);
        size++;
    }
    return size;
}
复制代码
public synchronized void doAppend(E eventObject) {
    // WARNING: The guard check MUST be the first statement in the
    // doAppend() method.

    // prevent re-entry.
    if (guard) {
        return;
    }

    try {
        guard = true;

        if (!this.started) {
            if (statusRepeatCount++ < ALLOWED_REPEATS) {
                addStatus(new WarnStatus("Attempted to append to non started appender [" + name + "].", this));
            }
            return;
        }

        if (getFilterChainDecision(eventObject) == FilterReply.DENY) {
            return;
        }

        // ok, we now invoke derived class' implementation of append
        this.append(eventObject);

    } catch (Exception e) {
        if (exceptionCount++ < ALLOWED_REPEATS) {
            addError("Appender [" + name + "] failed to append.", e);
        }
    } finally {
        guard = false;
    }
}
复制代码

Los filtros personalizados están involucrados aquígetFilterChainDecision

salida formateada

// SyslogAppenderBase
@Override
protected void append(E eventObject) {
    if (!isStarted()) {
        return;
    }

    try {
        String msg = layout.doLayout(eventObject);
        if (msg == null) {
            return;
        }
        if (msg.length() > maxMessageSize) {
            msg = msg.substring(0, maxMessageSize);
        }
        sos.write(msg.getBytes(charset));
        sos.flush();
        postProcess(eventObject, sos);
    } catch (IOException ioe) {
        addError("Failed to send diagram to " + syslogHost, ioe);
    }
}
复制代码

Esto convertirá EventObject en una cadena con la ayuda del objeto Layout.

Pero también hay algunos Appenders que no necesitan formatearse, como la salida a MQ, Socket, etc.

private void dispatchEvents(ObjectWriter objectWriter) throws InterruptedException, IOException {
    while (true) {
        E event = deque.takeFirst();
        postProcessEvent(event);
        Serializable serializableEvent = getPST().transform(event);
        try {
            objectWriter.write(serializableEvent);
        } catch (IOException e) {
            tryReAddingEventToFrontOfQueue(event);
            throw e;
        }
    }
}
复制代码

cómo lo serializan en el flujo de salida

    public Serializable transform(ILoggingEvent event) {
        if (event == null) {
            return null;
        }
        if (event instanceof LoggingEvent) {
            return LoggingEventVO.build(event);
        } else if (event instanceof LoggingEventVO) {
            return (LoggingEventVO) event;
        } else {
            throw new IllegalArgumentException("Unsupported type " + event.getClass().getName());
        }
    }

}
复制代码

Enviar evento de registro

Cuando los eventos de registro estén completamente formateados, se enviarán a destinos específicos a través de cada agregador.

Supongo que te gusta

Origin juejin.im/post/7078482522815856654
Recomendado
Clasificación