catalina.out日志膨胀问题解决实例,日志门面commons-logging的实践
声明:迁移自本人CSDN博客https://blog.csdn.net/u013365635
笔者在公司的时候,遇到一个问题,2个模块A、B Tomcat中的catalina.out及catalina.20xx-xx-xx.log的日志会快速膨胀直至把所有的docker容器内的硬盘资源消耗完(考虑到容器内硬盘资源有限,日志存储在额外挂载的一个硬盘中)。
1、问题直接原因
分析其中的catalina日志,发现打印都来自于Spring及A、B模块使用的一个入参校验安全框架C。catalina.out中出现类似如下的日志:
03-Sep-2018 04:11:46.898 INFO [NssLinkCheckThread] org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping.register Mapped “{[/parse-zip.do],methods=[POST]}” onto public void com.xxxxxx.xxx.controller.user.XxxxController.parseZip(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse,org.springframework.ui.ModelMap)
直接原因:框架C和Spring打印日志的时候,使用的具体工厂类都来自commons-logging-1.2.jar中的SimpleLog,这些日志打印到了标准错误,标准错误输出到stdout,stdout重定向到了catalina文件
2、分析及解决过程:
SimpleLog.java关键源码
protected void write(final StringBuffer buffer) {
System.err.println(buffer.toString());
}
定制的catalina.sh关键源码
eval $_NOHUP "\"$_RUNJAVA\"" "\"$LOGGING_CONFIG\"" $LOGGING_MANAGER $JAVA_OPTS $CATALINA_OPTS \
-classpath "\"$CLASSPATH\"" \
-Dcatalina.base="\"$CATALINA_BASE\"" \
-Dcatalina.home="\"$CATALINA_HOME\"" \
-Djava.io.tmpdir="\"$CATALINA_TMPDIR\"" \
org.apache.catalina.startup.Bootstrap "$@" start \
>> "$CATALINA_OUT" 2>&1 "&"
因为A、B模块及框架C使用的日志门面是commons-logging,Spring内部使用的日志门面也是commons-logging。此时,最好的做法当然就是直接进入这个日志门面的源码进行分析了。
以下代码为反编译后的,想要原始的含注释的代码可在GitHub上获取。此处代码并不复杂,使用IDEA自带的反编译工具获得的源码分析即可。
或者使用luyten反编译,得到的commons-logging-1.2.jar的源码结构如下
分析源码
框架C部分业务自研代码
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
......
private static Log log = LogFactory.getLog(AbstractProcessValidate.class);
......
commons-logging-1.2.jar反编译出的类LogFactory问题定位关键源码如下:
public static LogFactory getFactory() throws LogConfigurationException {
ClassLoader contextClassLoader = getContextClassLoaderInternal();
if (contextClassLoader == null && isDiagnosticsEnabled()) {
logDiagnostic("Context classloader is null.");
}
LogFactory factory = getCachedFactory(contextClassLoader);
if (factory != null) {
return factory;
} else {
if (isDiagnosticsEnabled()) {
logDiagnostic("[LOOKUP] LogFactory implementation requested for the first time for context classloader " + objectId(contextClassLoader));
logHierarchy("[LOOKUP] ", contextClassLoader);
}
Properties props = getConfigurationFile(contextClassLoader, "commons-logging.properties");
ClassLoader baseClassLoader = contextClassLoader;
String factoryClass;
if (props != null) {
factoryClass = props.getProperty("use_tccl");
if (factoryClass != null && !Boolean.valueOf(factoryClass)) {
baseClassLoader = thisClassLoader;
}
}
if (isDiagnosticsEnabled()) {
logDiagnostic("[LOOKUP] Looking for system property [org.apache.commons.logging.LogFactory] to define the LogFactory subclass to use...");
}
try {
factoryClass = getSystemProperty("org.apache.commons.logging.LogFactory", (String)null);
if (factoryClass != null) {
if (isDiagnosticsEnabled()) {
logDiagnostic("[LOOKUP] Creating an instance of LogFactory class '" + factoryClass + "' as specified by system property " + "org.apache.commons.logging.LogFactory");
}
factory = newFactory(factoryClass, baseClassLoader, contextClassLoader);
} else if (isDiagnosticsEnabled()) {
logDiagnostic("[LOOKUP] No system property [org.apache.commons.logging.LogFactory] defined.");
}
} catch (SecurityException var9) {
if (isDiagnosticsEnabled()) {
logDiagnostic("[LOOKUP] A security exception occurred while trying to create an instance of the custom factory class: [" + trim(var9.getMessage()) + "]. Trying alternative implementations...");
}
} catch (RuntimeException var10) {
if (isDiagnosticsEnabled()) {
logDiagnostic("[LOOKUP] An exception occurred while trying to create an instance of the custom factory class: [" + trim(var10.getMessage()) + "] as specified by a system property.");
}
throw var10;
}
String factoryClassName;
if (factory == null) {
if (isDiagnosticsEnabled()) {
logDiagnostic("[LOOKUP] Looking for a resource file of name [META-INF/services/org.apache.commons.logging.LogFactory] to define the LogFactory subclass to use...");
}
try {
InputStream is = getResourceAsStream(contextClassLoader, "META-INF/services/org.apache.commons.logging.LogFactory");
if (is != null) {
BufferedReader rd;
try {
rd = new BufferedReader(new InputStreamReader(is, "UTF-8"));
} catch (UnsupportedEncodingException var7) {
rd = new BufferedReader(new InputStreamReader(is));
}
factoryClassName = rd.readLine();
rd.close();
if (factoryClassName != null && !"".equals(factoryClassName)) {
if (isDiagnosticsEnabled()) {
logDiagnostic("[LOOKUP] Creating an instance of LogFactory class " + factoryClassName + " as specified by file '" + "META-INF/services/org.apache.commons.logging.LogFactory" + "' which was present in the path of the context classloader.");
}
factory = newFactory(factoryClassName, baseClassLoader, contextClassLoader);
}
} else if (isDiagnosticsEnabled()) {
logDiagnostic("[LOOKUP] No resource file with name 'META-INF/services/org.apache.commons.logging.LogFactory' found.");
}
} catch (Exception var8) {
if (isDiagnosticsEnabled()) {
logDiagnostic("[LOOKUP] A security exception occurred while trying to create an instance of the custom factory class: [" + trim(var8.getMessage()) + "]. Trying alternative implementations...");
}
}
}
if (factory == null) {
if (props != null) {
if (isDiagnosticsEnabled()) {
logDiagnostic("[LOOKUP] Looking in properties file for entry with key 'org.apache.commons.logging.LogFactory' to define the LogFactory subclass to use...");
}
factoryClass = props.getProperty("org.apache.commons.logging.LogFactory");
if (factoryClass != null) {
if (isDiagnosticsEnabled()) {
logDiagnostic("[LOOKUP] Properties file specifies LogFactory subclass '" + factoryClass + "'");
}
factory = newFactory(factoryClass, baseClassLoader, contextClassLoader);
} else if (isDiagnosticsEnabled()) {
logDiagnostic("[LOOKUP] Properties file has no entry specifying LogFactory subclass.");
}
} else if (isDiagnosticsEnabled()) {
logDiagnostic("[LOOKUP] No properties file available to determine LogFactory subclass from..");
}
}
if (factory == null) {
if (isDiagnosticsEnabled()) {
logDiagnostic("[LOOKUP] Loading the default LogFactory implementation 'org.apache.commons.logging.impl.LogFactoryImpl' via the same classloader that loaded this LogFactory class (ie not looking in the context classloader).");
}
factory = newFactory("org.apache.commons.logging.impl.LogFactoryImpl", thisClassLoader, contextClassLoader);
}
if (factory != null) {
cacheFactory(contextClassLoader, factory);
if (props != null) {
Enumeration names = props.propertyNames();
while(names.hasMoreElements()) {
String name = (String)names.nextElement();
factoryClassName = props.getProperty(name);
factory.setAttribute(name, factoryClassName);
}
}
}
return factory;
}
}
源码面前,了无秘密。从这段源码能看出从LogFactory中获取具体日志工厂类实现的顺序,逻辑顺序如下:
step1:
Properties props = getConfigurationFile(contextClassLoader, “commons-logging.properties”);
从属性配置文件commons-logging.properties中读取属性信息
step2:
factoryClass = getSystemProperty(“org.apache.commons.logging.LogFactory”, (String)null);
通过JVM设置的属性值(通常在catalina.sh等脚本中设置)决定使用哪个具体工厂类实例化factory
step3:
InputStream is = getResourceAsStream(contextClassLoader, “META-INF/services/org.apache.commons.logging.LogFactory”);
读取配置文件中设置的值决定使用哪个具体工厂类实例化factory
step4:
factoryClass = props.getProperty(“org.apache.commons.logging.LogFactory”);
根据step1中获取到的props配置的org.apache.commons.logging.LogFactory属性值决定使用哪个具体工厂类实例化factory
step5:
factory = newFactory(“org.apache.commons.logging.impl.LogFactoryImpl”, thisClassLoader, contextClassLoader);
使用LogFactoryImpl中实现的具体工厂类实例化factory,LogFactoryImpl内部涉及多种具体工厂的实现类。
以上实例化factory的过程,只要一个实例化成功了,后面的就不再执行了。所以commons-logging门面能灵活地实现使用哪种日志系统。
接着上面step5的LogFactoryImpl讨论,LogFactoryImpl要具体将factory实例化为 “org.apache.commons.logging.impl.Log4JLogger”, “org.apache.commons.logging.impl.Jdk14Logger”, “org.apache.commons.logging.impl.Jdk13LumberjackLogger”, “org.apache.commons.logging.impl.SimpleLog”,”org.apache.commons.logging.impl.NoOpLog” 等中的某一个,关键的决定具体工厂类的函数如下:
private String findUserSpecifiedLogClassName() {
if (isDiagnosticsEnabled()) {
this.logDiagnostic("Trying to get log class from attribute 'org.apache.commons.logging.Log'");
}
String specifiedClass = (String)this.getAttribute("org.apache.commons.logging.Log");
if (specifiedClass == null) {
if (isDiagnosticsEnabled()) {
this.logDiagnostic("Trying to get log class from attribute 'org.apache.commons.logging.log'");
}
specifiedClass = (String)this.getAttribute("org.apache.commons.logging.log");
}
if (specifiedClass == null) {
if (isDiagnosticsEnabled()) {
this.logDiagnostic("Trying to get log class from system property 'org.apache.commons.logging.Log'");
}
try {
specifiedClass = getSystemProperty("org.apache.commons.logging.Log", (String)null);
} catch (SecurityException var4) {
if (isDiagnosticsEnabled()) {
this.logDiagnostic("No access allowed to system property 'org.apache.commons.logging.Log' - " + var4.getMessage());
}
}
}
if (specifiedClass == null) {
if (isDiagnosticsEnabled()) {
this.logDiagnostic("Trying to get log class from system property 'org.apache.commons.logging.log'");
}
try {
specifiedClass = getSystemProperty("org.apache.commons.logging.log", (String)null);
} catch (SecurityException var3) {
if (isDiagnosticsEnabled()) {
this.logDiagnostic("No access allowed to system property 'org.apache.commons.logging.log' - " + var3.getMessage());
}
}
}
if (specifiedClass != null) {
specifiedClass = specifiedClass.trim();
}
return specifiedClass;
}
依次试图从变量protected Hashtable attributes = new Hashtable();、环境变量org.apache.commons.logging.Log/org.apache.commons.logging.log中获取属性值决定实例化factory为哪个具体工厂类。
String specifiedClass = (String)this.getAttribute(“org.apache.commons.logging.Log”);
specifiedClass = (String)this.getAttribute(“org.apache.commons.logging.log”);
specifiedClass = getSystemProperty(“org.apache.commons.logging.Log”, (String)null);
specifiedClass = getSystemProperty(“org.apache.commons.logging.log”, (String)null);
3、解决方法
相信有了以上的分析,读者在遇到相似问题的时候,知道怎么去处理了吧。只需要跟着以上各阶段的属性值,配置一个适合自己生产环境的。这过程要考虑到设置属性值的阶段(比如step1~step5中的哪一个)、具体需要的具体工厂类等。
结合笔者生产环境中使用的环境变量,解决方法:catalina.sh设置JVM环境变量 -Dorg.apache.commons.logging.Log=org.apache.commons.logging.impl.NoOpLog即可阻止使用commons-logging的组件的日志打印,且不影响使用slf4j日志门面的组件的日志打印。选择这个属性值设置的原因是这是最后一个有影响的属性值,设置该值不对更早阶段的属性设置有影响。
同时有了上面的分析,应该不难理解为什么加入jcl-over-slf4j-1.6.1.jar这个jar包就能将框架C的日志打印到运行日志了吧,还不明白的话,把jcl-over-slf4j-1.6.1.jar反编译出来看看就知道了。如下图。
catalina.out日志膨胀问题解决实例,日志门面commons-logging的实践的更多相关文章
- Log4J使用实例---日志进行邮件发送或是存入数据库
部分转摘:http://blog.csdn.net/azhao_dn/article/details/9118667 1.根类别(在类别层次结构的顶部,即全局性的日志级别) 配置根Logger,其语法 ...
- logAnalyzer日志管理系统配置实例
LogAnalyzer日志管理系统配置实例 上个月我写过一篇<利用EventlogAnalyzer分析Linux日志>一文深受大家喜欢,今天我再次为大家讲解Linux系统下的一款开源的日志 ...
- Log4j2日志框架集成Slf4j日志门面
1.说明 本文介绍使用日志门面Slf4j打印日志, 底层日志实现使用Log4j2框架, 方便以后切换底层日志实现, Log4j2可以替换成Logback等. 2.依赖管理 在pom.xml依赖管理中导 ...
- tomcat日志及logback相关日志框架
一.重点问题整理 1.1 关于logback.xml中的路径设置问题 准备金系统的logback.xml中设置的路径是: <!-- 定义日志文件 输出位置 --> <property ...
- c#.NET中日志信息写入Windows日志中解决方案
1. 目的应用系统的开发和维护离不开日志系统,选择一个功能强大的日志系统解决方案是应用系统开发过程中很重要的一部分.在.net环境下的日志系统解决方案有许多种,log4net是其中的佼佼者.在Wind ...
- 日志系列1——slf4j日志框架原理
目录 1.前言 2.日志门面 3.日志库 4.日志适配器 5.日志库的选用 6.logback.xml 配置文件 1.前言 说到日志工具,日常工作或学习中肯定听过这些名词:log4j.logbac ...
- Spring 使用 SLF4J代替 Commons Logging 写日志 异常
项目的日志更换成slf4j和logback后,发现项目无法启动.错误提示 Caused by: java.lang.NoClassDefFoundError: Lorg/apache/commons/ ...
- 传递给数据库 'master' 中的日志扫描操作的日志扫描号无效
错误:连接数据库的时候提示:SQL Server 检测到基于一致性的逻辑 I/O 错误 校验和不正确 C:\Documents and Settings\Administrator>" ...
- ORACLE 修改日志大小及增加日志成员
日志文件能不能resize,直接扩大日志文件的大小?10g是不能的. 网上的一般方法就是新建两个临时日志组(oracle至少要求两个日志组),切换到这两个临时日志组后,删掉重建扩大或缩小,再添加日志组 ...
随机推荐
- 将visual sdudio+Qt5.12 制作的程序打包成单个exe
在GitHub上下载了个qt程序,由于C++不太会,经过安装qt.修改编码等一系列操作终于可以运行了. 生成的exe在运行时依赖很多dll或者图片文件,直接拷贝到其他电脑上无法运行,可以将依赖的dll ...
- javascript中window.open()与window.location.href
1.window.location是window对象的属性,而window.open是window对象的方法 window.location是你对当前浏览器窗口的URL地址对象的参考! ...
- Django 初体验
Django 依赖的python 基础环境安装: https://www.runoob.com/django/django-install.html Django安装参考官网文档: https://d ...
- JavaScript获取当前日期前后7天的日期
function fun_date(aa){ var date1 = new Date(), time1=date1.getFullYear()+"-"+(date1.getMon ...
- springmvc无法访问JS,CSS等文件
配置好web.xml中的dispatchservlet后,js,css,都不能正常显示 web.xml配置文件 <!-- 核心控制器 --> <servlet> <ser ...
- P 1020 月饼
转跳点:
- Windows 10任务调度器曝出新零日漏洞
新浪科技讯,北京时间 5 月 23 日早间消息,据美国科技媒体 BleepingComputer 报道,在微软每月安全更新周期刚刚过去一周后,漏洞开发者 SandboxEscaper 悄悄发布了 Wi ...
- python常用代码、问题汇总
1.生成dataframe数据 5.读取带 ','分隔符的txt文件 4.DataFrame格式数据处理中报错 2.安装库时出现如下错误: 3.得到股票交易日数据 1.生成dataframe数据 im ...
- Dubbo+Zookeeper 入门Demo
1.Zookeeper安装及启动 可参考这篇文章https://www.cnblogs.com/geekdc/p/5948326.html 从下载到启动都描述的很详细,按照文章一步一步走即可. 2.D ...
- Linux 压缩解压操作
Linux 压缩解压操作 Linux解压文件到指定目录 tar在Linux上是常用的打包.压缩.加压缩工具,他的参数很多,折里仅仅列举常用的压缩与解压缩参数 参数:-c :create 建立压缩档案的 ...