专栏文章:
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);
}
}
}
}
该类主要定义了按一定的优先顺序加载配置文件
- 优先级:logback.groovy > logback-test.xml > logback.xml
- 当加载到配置文件时,交给JoranConfigurator去解析,加载不到配置文件时默认使用
BasicConfigurator
配置类。
加载配置时根据配置内容提前初始化配置好的日志对象
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;
}