Logback源码分析

专栏文章:
Java Util Logger源码分析
Log4j源码分析
Slf4j源码分析
Springboot Logger源码分析

logback出现于slf4j之后,故而logback默认实现了slf4j的接口,使用logback时我们都结合使用了slf4j作为上层日志门面。

初始化

上文slf4j分析了如何绑定具体日志实现框架,slf4j会加载classpath下的StaticLoggerBinder类,在logback中logback-classic包中实现了StaticLoggerBinder对logback进行了初始化。

public class StaticLoggerBinder implements LoggerFactoryBinder {
    public static String REQUESTED_API_VERSION = "1.7.16"; // !final
    final static String NULL_CS_URL = CoreConstants.CODES_URL + "#null_CS";
    private static StaticLoggerBinder SINGLETON = new StaticLoggerBinder();
    private static Object KEY = new Object();
    // 静态代码块初始化
    static {
        SINGLETON.init();
    }
    private boolean initialized = false;
    private LoggerContext defaultLoggerContext = new LoggerContext();
    private final ContextSelectorStaticBinder contextSelectorBinder = ContextSelectorStaticBinder.getSingleton();
    private StaticLoggerBinder() {
        // 默认日志仓库(对应于log4j中的LoggerRepository)
        defaultLoggerContext.setName(CoreConstants.DEFAULT_CONTEXT_NAME);
    }
    public static StaticLoggerBinder getSingleton() {
        return SINGLETON;
    }
    void init() {
        try {
            try {
                // 初始化,加载logback.xml配置文件等
                new ContextInitializer(defaultLoggerContext).autoConfig();
            } catch (JoranException je) {
                Util.report("Failed to auto configure default logger context", je);
            }
            // logback-292
            if (!StatusUtil.contextHasStatusListener(defaultLoggerContext)) {
                StatusPrinter.printInCaseOfErrorsOrWarnings(defaultLoggerContext);
            }
            contextSelectorBinder.init(defaultLoggerContext, KEY);
            initialized = true;
        } catch (Exception t) { // see LOGBACK-1159
            Util.report("Failed to instantiate [" + LoggerContext.class.getName() + "]", t);
        }
    }

    public ILoggerFactory getLoggerFactory() {
        if (!initialized) {
            // 仓库
            return defaultLoggerContext;
        }
        if (contextSelectorBinder.getContextSelector() == null) {
            throw new IllegalStateException("contextSelector cannot be null. See also " + NULL_CS_URL);
        }
        return contextSelectorBinder.getContextSelector().getLoggerContext();
    }
}

StaticLoggerBinder主要进行了logback的初始化,加载logback.xml配置文件。
具体的如何加载配置文件交给了ContextInitializer

public class ContextInitializer {

    final public static String GROOVY_AUTOCONFIG_FILE = "logback.groovy";
    // 默认配置文件
    final public static String AUTOCONFIG_FILE = "logback.xml";
    final public static String TEST_AUTOCONFIG_FILE = "logback-test.xml";
    final public static String CONFIG_FILE_PROPERTY = "logback.configurationFile";

    final LoggerContext loggerContext;
    public ContextInitializer(LoggerContext loggerContext) {
        this.loggerContext = loggerContext;
    }

    public void configureByResource(URL url) throws JoranException {
        if (url == null) {
            throw new IllegalArgumentException("URL argument cannot be null");
        }
        final String urlString = url.toString();
        if (urlString.endsWith("groovy")) {
            //....省略代码
        } else if (urlString.endsWith("xml")) {
            JoranConfigurator configurator = new JoranConfigurator();
            configurator.setContext(loggerContext);
            configurator.doConfigure(url);
        } else {
            throw new LogbackException("Unexpected filename extension of file [" + url.toString() + "]. Should be either .groovy or .xml");
        }
    }

    void joranConfigureByResource(URL url) throws JoranException {
        JoranConfigurator configurator = new JoranConfigurator();
        configurator.setContext(loggerContext);
        configurator.doConfigure(url);
    }



    public URL findURLOfDefaultConfigurationFile(boolean updateStatus) {
        ClassLoader myClassLoader = Loader.getClassLoaderOfObject(this);
        // 查找自定义配置文件名称属性 logback.configurationFile
        URL url = findConfigFileURLFromSystemProperties(myClassLoader, updateStatus);
        if (url != null) {
            return url;
        }
        // 查找logback.groovy
        url = getResource(GROOVY_AUTOCONFIG_FILE, myClassLoader, updateStatus);
        if (url != null) {
            return url;
        }
        // 查找logback-test.xml
        url = getResource(TEST_AUTOCONFIG_FILE, myClassLoader, updateStatus);
        if (url != null) {
            return url;
        }
        //默认 logback.xml
        return getResource(AUTOCONFIG_FILE, myClassLoader, updateStatus);
    }
    // 装配
    public void autoConfig() throws JoranException {
        StatusListenerConfigHelper.installIfAsked(loggerContext);
        // 找到配置文件
        URL url = findURLOfDefaultConfigurationFile(true);
        if (url != null) {
            // 解析配置文件
            configureByResource(url);
        } else {
            Configurator c = EnvUtil.loadFromServiceLoader(Configurator.class);
            if (c != null) {
                try {
                    c.setContext(loggerContext);
                    c.configure(loggerContext);
                } catch (Exception e) {
                    throw new LogbackException(String.format("Failed to initialize Configurator: %s using ServiceLoader", c != null ? c.getClass().getCanonicalName() : "null"), e);
                }
            } else {
                // 默认配置类 BasicConfigurator
                BasicConfigurator basicConfigurator = new BasicConfigurator();
                basicConfigurator.setContext(loggerContext);
                basicConfigurator.configure(loggerContext);
            }
        }
    }
}

该类主要定义了按一定的优先顺序加载配置文件

  1. 优先级:logback.groovy > logback-test.xml > logback.xml
  2. 当加载到配置文件时,交给JoranConfigurator去解析,加载不到配置文件时默认使用BasicConfigurator配置类。

加载配置时根据配置内容提前初始化配置好的日志对象
image.png

Context - 日志记录器对象管理

Context - 接口定义
public interface Context extends PropertyContainer {
    StatusManager getStatusManager();
    // 乌七八糟的对象管理
    Object getObject(String key);
    void putObject(String key, Object value);
    // 属性管理
    String getProperty(String key);
    void putProperty(String key, String value);
    Map<String, String> getCopyOfPropertyMap();
    // 每个Context都定义了一个名字(默认为default)
    String getName();
    void setName(String name);
    
    Object getConfigurationLock();
    // 异步执行线程池
    ScheduledExecutorService getScheduledExecutorService();
    ExecutorService getExecutorService();
}
ContextBase- 基础管理实现
public class ContextBase implements Context, LifeCycle {
    private String name;
    private StatusManager sm = new BasicStatusManager();
    // 属性管理
    Map<String, String> propertyMap = new HashMap<String, String>();
    // 对象管理
    Map<String, Object> objectMap = new HashMap<String, Object>();
	// 配置修改需要加锁
    LogbackLock configurationLock = new LogbackLock();

    private ScheduledExecutorService scheduledExecutorService;
    protected List<ScheduledFuture<?>> scheduledFutures = new ArrayList<ScheduledFuture<?>>(1);
    private LifeCycleManager lifeCycleManager;
    private boolean started;

    public ContextBase() {
        initCollisionMaps();
    }
}
LoggerContext - 默认实现
public class LoggerContext extends ContextBase implements ILoggerFactory, LifeCycle {
    /** Default setting of packaging data in stack traces */
    public static final boolean DEFAULT_PACKAGING_DATA = false;
    // root Logger
    final Logger root;
    // logger数量
    private int size;
    private int noAppenderWarning = 0;
    // 监听器,一般不用
    final private List<LoggerContextListener> loggerContextListenerList = new ArrayList<LoggerContextListener>();
	// logger对象缓存
    private Map<String, Logger> loggerCache;
	// 
    private LoggerContextVO loggerContextRemoteView;
    private final TurboFilterList turboFilterList = new TurboFilterList();
    private boolean packagingDataEnabled = DEFAULT_PACKAGING_DATA;

    private int maxCallerDataDepth = ClassicConstants.DEFAULT_MAX_CALLEDER_DATA_DEPTH;

    int resetCount = 0;
    private List<String> frameworkPackages;

    public LoggerContext() {
        super();
        // 缓存容器
        this.loggerCache = new ConcurrentHashMap<String, Logger>();

        this.loggerContextRemoteView = new LoggerContextVO(this);
        // root对象
        this.root = new Logger(Logger.ROOT_LOGGER_NAME, null, this);
        // 默认debug
        this.root.setLevel(Level.DEBUG);
        // root存入缓存中
        loggerCache.put(Logger.ROOT_LOGGER_NAME, root);
        initEvaluatorMap();
        size = 1;
        this.frameworkPackages = new ArrayList<String>();
    }
}
LoggerContext#getLogger

该方法继承至Slf4j的ILoggerFactory#getLogger(String),即实现了工厂创建Logger对象。

@Override
public final Logger getLogger(final String name) {
    if (name == null) {
        throw new IllegalArgumentException("name argument cannot be null");
    }
    if (Logger.ROOT_LOGGER_NAME.equalsIgnoreCase(name)) {
        return root;
    }
    int i = 0;
    Logger logger = root;
    Logger childLogger = (Logger) loggerCache.get(name);
    if (childLogger != null) {
        return childLogger;
    }
   	// 日志名称
    String childName;
    while (true) {
        // com.abc.efg.xyz
        int h = LoggerNameUtil.getSeparatorIndexOf(name, i);
        if (h == -1) {
            childName = name;
        } else {
            childName = name.substring(0, h);
        }
        // 移动左边指针位置
        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;
        }
    }
}
Logger#createChildByName

通过Logger对象,创建其子logger对象,便于直接设置其日志级别等属性,通过继承即可。

Logger createChildByName(String childName) {
    // 截取名称
    if (this.childrenList == null) {
        this.childrenList = new CopyOnWriteArrayList();
    }
	// 新建子logger
    Logger childLogger = new Logger(childName, this, this.loggerContext);
    this.childrenList.add(childLogger);
    // 设置日志级别为父logger日志级别(这里没有从配置中心取,说明是在其他地方处理读取配置)
    childLogger.effectiveLevelInt = this.effectiveLevelInt;
    return childLogger;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值