文章目錄
一:slf4j綁定日誌框架(logback爲例)
從 Logger logger = LoggerFactory.getLogger(Test.class);開始,LoggerFactory是slf4j的類
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 " + LOGGER_NAME_MISMATCH_URL + " for an explanation");
}
}
return logger;
}
核心代碼只有一行: Logger logger = getLogger(clazz.getName());該方法的具體實現如下:可以看到先獲取一個日誌工廠.然後從日誌工廠中獲得日誌記錄器
- ILoggerFactory 是slf4j的工廠類接口
- getILoggerFactory();必然得到一個工廠實現類,那麼日誌框架的初始化過程就在其中
public static Logger getLogger(String name) {
ILoggerFactory iLoggerFactory = getILoggerFactory();
return iLoggerFactory.getLogger(name);
}
第一行代碼: ILoggerFactory iLoggerFactory = getILoggerFactory();主要做了2件事情
- slf4j綁定真正的日誌框架(此處爲logback),初始化具體日誌框架
- 獲取日誌框架的LogContext
public static ILoggerFactory getILoggerFactory() {
// slf4j綁定真正的日誌框架(此處爲logback)
if (INITIALIZATION_STATE == UNINITIALIZED) {
synchronized (LoggerFactory.class) {
if (INITIALIZATION_STATE == UNINITIALIZED) {
INITIALIZATION_STATE = ONGOING_INITIALIZATION;
performInitialization();
}
}
}
// 獲取日誌框架的LogContext(工廠)
switch (INITIALIZATION_STATE) {
case SUCCESSFUL_INITIALIZATION:
return StaticLoggerBinder.getSingleton().getLoggerFactory();
case NOP_FALLBACK_INITIALIZATION:
return NOP_FALLBACK_FACTORY;
case FAILED_INITIALIZATION:
throw new IllegalStateException(UNSUCCESSFUL_INIT_MSG);
case ONGOING_INITIALIZATION:
// support re-entrant behavior.
// See also http://jira.qos.ch/browse/SLF4J-97
return SUBST_FACTORY;
}
throw new IllegalStateException("Unreachable code");
}
}
初始化日誌實現核心方法: performInitialization();如下
private final static void performInitialization() {
bind();
if (INITIALIZATION_STATE == SUCCESSFUL_INITIALIZATION) {
versionSanityCheck();
}
}
核心在於: bind();主要完成
- findPossibleStaticLoggerBinderPathSet();classpath下查找org/slf4j/impl/StaticLoggerBinder.class的URL
- StaticLoggerBinder.getSingleton();加載StaticLoggerBinder並初始化日誌框架(LogContext)
- 修改狀態:INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION
- 記錄日誌:具體使用了哪個日誌實現:Actual binding is of type…
private final static void bind() {
try {
Set<URL> staticLoggerBinderPathSet = null;
// skip check under android, see also
// http://jira.qos.ch/browse/SLF4J-328
if (!isAndroid()) {
staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);
}
// the next line does the binding
StaticLoggerBinder.getSingleton();
INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;
reportActualBinding(staticLoggerBinderPathSet);
fixSubstituteLoggers();
replayEvents();
// release all resources in SUBST_FACTORY
SUBST_FACTORY.clear();
} catch (NoClassDefFoundError ncde) {
String msg = ncde.getMessage();
if (messageContainsOrgSlf4jImplStaticLoggerBinder(msg)) {
INITIALIZATION_STATE = NOP_FALLBACK_INITIALIZATION;
Util.report("Failed to load class \"org.slf4j.impl.StaticLoggerBinder\".");
Util.report("Defaulting to no-operation (NOP) logger implementation");
Util.report("See " + NO_STATICLOGGERBINDER_URL + " for further details.");
} else {
failedBinding(ncde);
throw ncde;
}
} catch (java.lang.NoSuchMethodError nsme) {
String msg = nsme.getMessage();
if (msg != null && msg.contains("org.slf4j.impl.StaticLoggerBinder.getSingleton()")) {
INITIALIZATION_STATE = FAILED_INITIALIZATION;
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 nsme;
} catch (Exception e) {
failedBinding(e);
throw new IllegalStateException("Unexpected initialization failure", e);
}
}
日誌的綁定是重點:StaticLoggerBinder.getSingleton();
當執行這行代碼的時候,類加載器會去類路徑下加載StaticLoggerBinder,如果有多種slf4j的實現(包含多個StaticLoggerBinder),那麼只會加載其中一個,具體加載哪個有一定的隨機性.當加載了一個StaticLoggerBinder,類路徑下的其它StaticLoggerBinder類不會再加載
StaticLoggerBinder的初始化過程會做哪些事情?
類的部分結構如下:
按照類的加載機制,類變量(static)會按順序加載,所以當執行到
private static StaticLoggerBinder SINGLETON = new StaticLoggerBinder();此時類初始化還沒有完成.就開始執行實例初始化了,以下三個實例變量已經存在於SINGLETON中
private boolean initialized = false;
private LoggerContext defaultLoggerContext = new LoggerContext();
private final ContextSelectorStaticBinder contextSelectorBinder = ContextSelectorStaticBinder.getSingleton();
接着會執行靜態代碼塊:
static {
SINGLETON.init();
}
new ContextInitializer(defaultLoggerContext).autoConfig();
1.defaultLoggerContext已經在加載StaticLoggerBinder的時候初始化了.內部有一個loggerCache是一個Map( Map<String, Logger>),內部有一個默認的名爲Root的logger
2. 創建一個ContextInitializer輔助初始化日誌框架,內部有LogContext
void init() {
try {
try {
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 (Throwable t) {
// we should never get here
Util.report("Failed to instantiate [" + LoggerContext.class.getName() + "]", t);
}
}
二:logback具體初始化細節
StaticLoggerBinder.getSingleton();這行代碼開始初始化具體日誌實現
StaticLoggerBinder類在slf4j的實現中:實現了slf4j中的LoggerFactoryBinder接口
public interface LoggerFactoryBinder {
public ILoggerFactory getLoggerFactory();
public String getLoggerFactoryClassStr();
}
查看StaticLoggerBinder的代碼:可以看到構造器是private修飾的,無法使用new創建對象. private static StaticLoggerBinder SINGLETON = new StaticLoggerBinder();這是唯一的實例對象通過以下方法訪問
public static StaticLoggerBinder getSingleton() {
return SINGLETON;
}
重寫的兩個方法:
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();
}
public String getLoggerFactoryClassStr() {
return contextSelectorBinder.getClass().getName();
}
在init方法初始化日誌框架,加載配置
void init() {
try {
try {
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 (Throwable t) {
// we should never get here
Util.report("Failed to instantiate [" + LoggerContext.class.getName() + "]", t);
}
}
其中defaultLoggerContext是LoggerContext實現了slf4j的ILoggerFactory接口,名字就叫做default,內部已經包含一個名爲root的logger
autoConfig()詳情如下:
- URL url = findURLOfDefaultConfigurationFile(true);搜索logback的配置文件:優先級 logback.configurationFile(系統配置) > logback.groovy > logback-test.xml > logback.xml (後面幾個都是Classpath下的文件)
- 加載自定義配置或者默認配置
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 = new BasicConfigurator();
basicConfigurator.setContext(loggerContext);
basicConfigurator.configure(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")) {
if (EnvUtil.isGroovyAvailable()) {
// avoid directly referring to GafferConfigurator so as to avoid
// loading groovy.lang.GroovyObject . See also http://jira.qos.ch/browse/LBCLASSIC-214
GafferUtil.runGafferConfiguratorOn(loggerContext, this, url);
} else {
StatusManager sm = loggerContext.getStatusManager();
sm.add(new ErrorStatus("Groovy classes are not available on the class path. ABORTING INITIALIZATION.", loggerContext));
}
} 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");
}
}
只學習xml配置方式
JoranConfigurator configurator = new JoranConfigurator();
configurator.setContext(loggerContext);
configurator.doConfigure(url);
具體細節在configurator.doConfigure(url);
public final void doConfigure(URL url) throws JoranException {
InputStream in = null;
try {
informContextOfURLUsedForConfiguration(getContext(), url);
URLConnection urlConnection = url.openConnection();
// per http://jira.qos.ch/browse/LBCORE-105
// per http://jira.qos.ch/browse/LBCORE-127
urlConnection.setUseCaches(false);
in = urlConnection.getInputStream();
doConfigure(in);
} catch (IOException ioe) {
String errMsg = "Could not open URL [" + url + "].";
addError(errMsg, ioe);
throw new JoranException(errMsg, ioe);
} finally {
if (in != null) {
try {
in.close();
} catch (IOException ioe) {
String errMsg = "Could not close input stream";
addError(errMsg, ioe);
throw new JoranException(errMsg, ioe);
}
}
}
}
public final void doConfigure(final InputSource inputSource) throws JoranException {
long threshold = System.currentTimeMillis();
// if (!ConfigurationWatchListUtil.wasConfigurationWatchListReset(context)) {
// informContextOfURLUsedForConfiguration(getContext(), null);
// }
SaxEventRecorder recorder = new SaxEventRecorder(context);
recorder.recordEvents(inputSource);
doConfigure(recorder.saxEventList);
// no exceptions a this level
StatusUtil statusUtil = new StatusUtil(context);
if (statusUtil.noXMLParsingErrorsOccurred(threshold)) {
addInfo("Registering current configuration as safe fallback point");
registerSafeConfiguration(recorder.saxEventList);
}
}
Interpreter在解析過程中起到關鍵作用: SAX解析xml時將xml各節點解析成對應的Event時通過xml元素對應的Action來做具體解析
- ruleStore各個xml節點對應的Action
- elementPath元素入棧出棧的臨時空間 在startElement中進棧表示開始解析,endElement出棧表示解析完成
- actionListStack 各個Action的臨時棧 解析開始中在ruleStore中找到元素對應的Action放入actionListStack,調用action的begin方法,解析完成時,出棧,調用end方法
三.常見的Action解析
1:ConfigurationAction
begin方法:
1:獲取系統屬性logback.debug或者configuration標籤上的debug屬性,當爲true,開啓打印logback內部日誌
2:獲取標籤的scan屬性,如果爲true,定時掃描配置,跟新配置
public void begin(InterpretationContext ic, String name, Attributes attributes) {
threshold = System.currentTimeMillis();
String debugAttrib = getSystemProperty(DEBUG_SYSTEM_PROPERTY_KEY);
if (debugAttrib == null) {
debugAttrib = ic.subst(attributes.getValue(INTERNAL_DEBUG_ATTR));
}
if (OptionHelper.isEmpty(debugAttrib) || debugAttrib.equalsIgnoreCase("false") || debugAttrib.equalsIgnoreCase("null")) {
addInfo(INTERNAL_DEBUG_ATTR + " attribute not set");
} else {
StatusListenerConfigHelper.addOnConsoleListenerInstance(context, new OnConsoleStatusListener());
}
processScanAttrib(ic, attributes);
ContextUtil contextUtil = new ContextUtil(context);
contextUtil.addHostNameAsProperty();
LoggerContext lc = (LoggerContext) context;
boolean packagingData = OptionHelper.toBoolean(ic.subst(attributes.getValue(PACKAGING_DATA_ATTR)), LoggerContext.DEFAULT_PACKAGING_DATA);
lc.setPackagingDataEnabled(packagingData);
if (EnvUtil.isGroovyAvailable()) {
contextUtil.addGroovyPackages(lc.getFrameworkPackages());
}
ic.pushObject(getContext());
}
2:ContextNameAction
給默認的logcontext設置名字
public void body(InterpretationContext ec, String body) {
String finalBody = ec.subst(body);
addInfo("Setting logger context name as [" + finalBody + "]");
try {
context.setName(finalBody);
} catch (IllegalStateException e) {
addError("Failed to rename context [" + context.getName() + "] as [" + finalBody + "]", e);
}
}
3:PropertyAction
三種來源 : file resource ,name和value的形式
三種使用範圍local :加載配置時可用 ,context:上下文可用,SYSTEM系統變量
public void begin(InterpretationContext ec, String localName, Attributes attributes) {
if ("substitutionProperty".equals(localName)) {
addWarn("[substitutionProperty] element has been deprecated. Please use the [property] element instead.");
}
String name = attributes.getValue(NAME_ATTRIBUTE);
String value = attributes.getValue(VALUE_ATTRIBUTE);
String scopeStr = attributes.getValue(SCOPE_ATTRIBUTE);
Scope scope = ActionUtil.stringToScope(scopeStr);
if (checkFileAttributeSanity(attributes)) {
String file = attributes.getValue(FILE_ATTRIBUTE);
file = ec.subst(file);
try {
FileInputStream istream = new FileInputStream(file);
loadAndSetProperties(ec, istream, scope);
} catch (FileNotFoundException e) {
addError("Could not find properties file [" + file + "].");
} catch (IOException e1) {
addError("Could not read properties file [" + file + "].", e1);
}
} else if (checkResourceAttributeSanity(attributes)) {
String resource = attributes.getValue(RESOURCE_ATTRIBUTE);
resource = ec.subst(resource);
URL resourceURL = Loader.getResourceBySelfClassLoader(resource);
if (resourceURL == null) {
addError("Could not find resource [" + resource + "].");
} else {
try {
InputStream istream = resourceURL.openStream();
loadAndSetProperties(ec, istream, scope);
} catch (IOException e) {
addError("Could not read resource file [" + resource + "].", e);
}
}
} else if (checkValueNameAttributesSanity(attributes)) {
value = RegularEscapeUtil.basicEscape(value);
// now remove both leading and trailing spaces
value = value.trim();
value = ec.subst(value);
ActionUtil.setProperty(ec, name, value, scope);
} else {
addError(INVALID_ATTRIBUTES);
}
}
4:AppenderAction
1:讀取class屬性值,加載appper的類,讀取name,並設置
2:從InterpretationContext中獲取objectMap,從中獲取APPENDER_BAG爲key的值(appernder的map),將apppender放入
public void begin(InterpretationContext ec, String localName, Attributes attributes) throws ActionException {
// We are just beginning, reset variables
appender = null;
inError = false;
String className = attributes.getValue(CLASS_ATTRIBUTE);
if (OptionHelper.isEmpty(className)) {
addError("Missing class name for appender. Near [" + localName + "] line " + getLineNumber(ec));
inError = true;
return;
}
try {
addInfo("About to instantiate appender of type [" + className + "]");
appender = (Appender<E>) OptionHelper.instantiateByClassName(className, ch.qos.logback.core.Appender.class, context);
appender.setContext(context);
String appenderName = ec.subst(attributes.getValue(NAME_ATTRIBUTE));
if (OptionHelper.isEmpty(appenderName)) {
addWarn("No appender name given for appender of type " + className + "].");
} else {
appender.setName(appenderName);
addInfo("Naming appender as [" + appenderName + "]");
}
// The execution context contains a bag which contains the appenders
// created thus far.
HashMap<String, Appender<E>> appenderBag = (HashMap<String, Appender<E>>) ec.getObjectMap().get(ActionConst.APPENDER_BAG);
// add the appender just created to the appender bag.
appenderBag.put(appenderName, appender);
ec.pushObject(appender);
} catch (Exception oops) {
inError = true;
addError("Could not create an Appender of type [" + className + "].", oops);
throw new ActionException(oops);
}
}
5:AppenderRefAction
把Appender和logger整合
6:LoggerAction
在logcontext中loggerCache中存儲了loggger對象,每個logger維護自己的childrenList,形成樹形結構
public void begin(InterpretationContext ec, String name, Attributes attributes) {
// Let us forget about previous errors (in this object)
inError = false;
logger = null;
LoggerContext loggerContext = (LoggerContext) this.context;
String loggerName = ec.subst(attributes.getValue(NAME_ATTRIBUTE));
if (OptionHelper.isEmpty(loggerName)) {
inError = true;
String aroundLine = getLineColStr(ec);
String errorMsg = "No 'name' attribute in element " + name + ", around " + aroundLine;
addError(errorMsg);
return;
}
logger = loggerContext.getLogger(loggerName);
String levelStr = ec.subst(attributes.getValue(LEVEL_ATTRIBUTE));
if (!OptionHelper.isEmpty(levelStr)) {
if (ActionConst.INHERITED.equalsIgnoreCase(levelStr) || ActionConst.NULL.equalsIgnoreCase(levelStr)) {
addInfo("Setting level of logger [" + loggerName + "] to null, i.e. INHERITED");
logger.setLevel(null);
} else {
Level level = Level.toLevel(levelStr);
addInfo("Setting level of logger [" + loggerName + "] to " + level);
logger.setLevel(level);
}
}
String additivityStr = ec.subst(attributes.getValue(ActionConst.ADDITIVITY_ATTRIBUTE));
if (!OptionHelper.isEmpty(additivityStr)) {
boolean additive = OptionHelper.toBoolean(additivityStr, true);
addInfo("Setting additivity of logger [" + loggerName + "] to " + additive);
logger.setAdditive(additive);
}
ec.pushObject(logger);
}
到此加載配置完成