今天就跟大家聊聊有关Tomcat的Logging内部实现方式是什么,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。
Tomcat的Logging实现,是通过JDK的Log,并重写了其Log的Handler来输出内容的。
下面将分析在Tomcat内部Log的实现,并深入源码,了解其根据配置,创建
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,因此,后续的过程都交给了
DirectJDKLog来处理。该类内部则是直接对JDK的logging调用
public DirectJDKLog(String name ) { logger=Logger.getLogger(name);}
而整个Logger其实还是会交给一个LogManager来管理,上一篇文章中我们也看到Tomcat也指定了自定义的LogManager,
这个类是ClassLoaderLogManager。
在Logger获取LogManager的过程中,就会涉及到读取logging.properties的过程,此时由于ClassLoaderLogManager继承了LogManager,这个readConfiguration方法由子类实现了,此时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 classloaderString 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后设置,在newInstance后,其实执行了许多操作,包含设置和初始化具体的Handler,打开输出流等。上篇文章提到的设置Log文件的操作就在这一步。 this.prefix.set(null); info.handlers.put(handlerName, handler); if (rootHandlers == null) { localRootLogger.addHandler(handler);} //解析完毕后,将handler和logger绑定。 } catch (Exception e) {}
而在loadClass(handlerClassName).newInstance中,还有比较关键的一个地方,在初始化AsynFileHandler时,类内初始化的代码包含这样两行
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内部也是这样的。
例如要用log记一个log.warn,这个时候,调用执行的代码是这样一个过程:
首先会调用到上面提到的DirectJDKLog中,其对应的方法会设置Log的Level等,方法是这个样子:
public final void warn(Object message) { log(Level.WARNING, String.valueOf(message), null);}
这行代码,会调用到Logger中的如下代码,会根据配置的Handler来进行log输出
for (Handler handler : loggerHandlers) { handler.publish(record);}
我们定义的Handler中包含FileHandler和ConsoleHandler,这个时候,FileHandler的publish方法执行的操作是将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); //这里使用初始化时创建的Writer if (bufferSize < 0) { writer.flush(); }
看完上述内容,你们对Tomcat的Logging内部实现方式是什么有进一步的了解吗?如果还想了解更多知识或者相关内容,请关注亿速云行业资讯频道,感谢大家的支持。
亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。
原文链接:https://my.oschina.net/u/4585957/blog/4583206