新聞中心
今天就跟大家聊聊有關(guān)Tomcat的Logging內(nèi)部實現(xiàn)方式是什么,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結(jié)了以下內(nèi)容,希望大家根據(jù)這篇文章可以有所收獲。
“只有客戶發(fā)展了,才有我們的生存與發(fā)展!”這是創(chuàng)新互聯(lián)公司的服務宗旨!把網(wǎng)站當作互聯(lián)網(wǎng)產(chǎn)品,產(chǎn)品思維更注重全局思維、需求分析和迭代思維,在網(wǎng)站建設(shè)中就是為了建設(shè)一個不僅審美在線,而且實用性極高的網(wǎng)站。創(chuàng)新互聯(lián)對成都做網(wǎng)站、網(wǎng)站建設(shè)、網(wǎng)站制作、網(wǎng)站開發(fā)、網(wǎng)頁設(shè)計、網(wǎng)站優(yōu)化、網(wǎng)絡(luò)推廣、探索永無止境。
Tomcat的Logging實現(xiàn),是通過JDK的Log,并重寫了其Log的Handler來輸出內(nèi)容的。
下面將分析在Tomcat內(nèi)部Log的實現(xiàn),并深入源碼,了解其根據(jù)配置,創(chuàng)建
Handler等一系列過程。
初始化
Tomcat的Main-Class是BootStrap這個類,而Log的初始化也是從這個類的初始化開始的,由于BootStrap類中有如下聲明:
private static final Log log = LogFactory.getLog(Bootstrap.class)
所以在類實例化時,這一字段也被初始化。
從LogFactory的工廠方法開始
public Log getInstance(String name) {
if (discoveredLogConstructor == null) {
return DirectJDKLog.getInstance(name);
}
return discoveredLogConstructor.newInstance(name);
默認情況,Tomcat使用的是DirectJDKLog,因此,后續(xù)的過程都交給了
DirectJDKLog來處理。該類內(nèi)部則是直接對JDK的logging調(diào)用
public DirectJDKLog(String name ) {
logger=Logger.getLogger(name);
}
而整個Logger其實還是會交給一個LogManager來管理,上一篇文章中我們也看到Tomcat也指定了自定義的LogManager,
這個類是ClassLoaderLogManager。
在Logger獲取LogManager的過程中,就會涉及到讀取logging.properties的過程,此時由于ClassLoaderLogManager繼承了LogManager,這個readConfiguration方法由子類實現(xiàn)了,此時Tomcat的自定義讀取方式如下:
URL logConfig = ((URLClassLoader)classLoader).findResource("logging.properties");
即在當前的classLoader中查找名為logging.properties的資源。如果當前classLoader中不存在會使用JDK自帶的logging.properties,找到文件之后,會直接把這個properties文件load進來,之后對文件中的properties解析。
如同下面的代碼,我們看到解析handlers,同時會解析自定義的帶有prefix的handler。
// Create handlers for the root logger of this classloader
String rootHandlers = info.props.getProperty(".handlers");
String handlers = info.props.getProperty("handlers");
Logger localRootLogger = info.rootNode.logger;
if (handlers != null) {
StringTokenizer tok = new StringTokenizer(handlers, ",");
while (tok.hasMoreTokens()) {
String handlerName = (tok.nextToken().trim());
String handlerClassName = handlerName;
String prefix = "";
if (handlerClassName.length() <= 0) {
continue;
}
if (Character.isDigit(handlerClassName.charAt(0))) {
int pos = handlerClassName.indexOf('.');
if (pos >= 0) {
prefix = handlerClassName.substring(0, pos + 1);
handlerClassName = handlerClassName.substring(pos + 1);}}
try {
this.prefix.set(prefix);
Handler handler =
(Handler) classLoader.loadClass(handlerClassName).newInstance(); //解析prefix和handlerClass后設(shè)置,在newInstance后,其實執(zhí)行了許多操作,包含設(shè)置和初始化具體的Handler,打開輸出流等。上篇文章提到的設(shè)置Log文件的操作就在這一步。
this.prefix.set(null);
info.handlers.put(handlerName, handler);
if (rootHandlers == null) {
localRootLogger.addHandler(handler);} //解析完畢后,將handler和logger綁定。
} catch (Exception e) {}
而在loadClass(handlerClassName).newInstance中,還有比較關(guān)鍵的一個地方,在初始化AsynFileHandler時,類內(nèi)初始化的代碼包含這樣兩行
protected static final LoggerThread logger = new LoggerThread();
static {
logger.start();
}
這個Logger線程,則會在后面不停止的處理log,其run方法是這個樣子:
while (run) {
try {
LogEntry entry = queue.poll(LOGGER_SLEEP_TIME, TimeUnit.MILLISECONDS);//log出隊列
if (entry!=null) entry.flush();
} catch (InterruptedException x) {
// Ignore the attempt to interrupt the thread.
} catch (Exception x) {
x.printStackTrace();
}
}
記一個Log
而我們一般在記錄log時,會直接使用log.info(xxx)這種形式,Tomcat內(nèi)部也是這樣的。
例如要用log記一個log.warn,這個時候,調(diào)用執(zhí)行的代碼是這樣一個過程:
首先會調(diào)用到上面提到的DirectJDKLog中,其對應的方法會設(shè)置Log的Level等,方法是這個樣子:
public final void warn(Object message) {
log(Level.WARNING, String.valueOf(message), null);
}
這行代碼,會調(diào)用到Logger中的如下代碼,會根據(jù)配置的Handler來進行l(wèi)og輸出
for (Handler handler : loggerHandlers) {
handler.publish(record);
}
我們定義的Handler中包含F(xiàn)ileHandler和ConsoleHandler,這個時候,F(xiàn)ileHandler的publish方法執(zhí)行的操作是將Log添加到一個阻塞隊列中,即上面的LogThread在wait的隊列,
public void publish(LogRecord record) {
LogEntry entry = new LogEntry(record,this);
boolean added = false;
try {
while (!added && !queue.offer(entry)) {...}
logThread在queue不為空時,會輸出LogEntry。
我們看,這個輸出Log的具體操作方式如下,會使用到上一篇文章提到的Formatter,
public void publish(LogRecord record) {
Timestamp ts = new Timestamp(System.currentTimeMillis());
String tsString = ts.toString().substring(0, 19);
String tsDate = tsString.substring(0, 10);
try {
// If the date has changed, switch log files
if (rotatable && !date.equals(tsDate)) {
try {
// Make sure another thread hasn't already done this
if (!date.equals(tsDate)) {
closeWriter();
date = tsDate;
openWriter();
}}}
String result = null;
result = getFormatter().format(record); //格式化,注意
if (writer!=null) {
writer.write(result); //這里使用初始化時創(chuàng)建的Writer
if (bufferSize < 0) {
writer.flush();
}
看完上述內(nèi)容,你們對Tomcat的Logging內(nèi)部實現(xiàn)方式是什么有進一步的了解嗎?如果還想了解更多知識或者相關(guān)內(nèi)容,請關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝大家的支持。
新聞名稱:Tomcat的Logging內(nèi)部實現(xiàn)方式是什么
文章分享:http://www.dlmjj.cn/article/ppjdcc.html