tomcat日志及logback相关日志框架
一、重点问题整理
1.1 关于logback.xml中的路径设置问题
准备金系统的logback.xml中设置的路径是:
<!-- 定义日志文件 输出位置 --> <property name="log_dir" value="E:\logs" />
在Windows环境下,会识别E:为系统的E盘,默认在E盘下创建logs文件夹。但是正在Linux环境下,不存在E盘,会将E:\logs识别为E:logs文件夹。此时为相对路径,而tomcat在创建文件时:会从程序启动的地方去创建这个文件(Java项目中也是如此),那么web应用是从有tomcat来执行的,tomcat这个程序是从哪里启动文件是startup.bat?位置是tomcat/bin。会在bin目录下创建E:logs文件夹,而不是在tomcat的默认根路径故tomcat文件夹下创建E:logs文件夹。(验证:若是用sh bin/startup.sh命令,在tomcat目录下启动tomcat,则会在tomcat路径下创建E:logs文件夹。)
所以为了使日志文件在我们指定的目录下,有两只方式:
【1】我们可以使用绝对路径: /
【2】也可以使用相对路径:
./ 当前目录。
../ 父级目录。
/ 根目录。
但是并不及建议用E:这种写死的路径。可以用以下方式创建日志文件的路径:
方式一:
value="${catalina.home}/logs":
本地:会在tomcat目录下创建logs文件。
Linux:Linux上会在tomcat目录下创建logs文件。
方式二:
value="${catalina.base}/logs":
本地:会在C:\Users\Administrator\.IntelliJIdea2017.2\system\tomcat\Unnamed_reserve_10目录下创建logs文件。(因为
本地tomcat为每个idea项目在这里创建了一个副本,项目是在这里启动的)
Linux:会在tomcat目录下创建logs文件。
方式三:
value="logs":会在tomcat/bin目录下创建logs文件。
../webapps/工程名:会在工程名目录下创建logs文件。
注意:我上次本地启动项目是在E盘创建的logs文件夹,这次可能是改啥东西了?我也不知道,反正本地的logs文件夹又跑tomcat/bin目录下了。奇怪。
这个是用tomcat启动的web项目。我在这个项目里建了个main主方法,创建的logs文件又跑项目的根路径下了:
邪了门了,本地启动项目,好像配置的路径不起作用了。。。
1.2 查看日志是输出的格式,及在不同级别文件中都是怎么记录日志信息的?是否与控制台的输出内容一致?
答:一致。
本地文件:
控制台:
1.3 为什么要用日志框架来进行日志管理?
日志框架对日志的管理能力非常强大,可以根据自定义的日志等级输出日志到指定位置,还可以对日志文件进行切分。Java自带的功能满足不了需要。
1.4 tomcat - catalina.out 日志过大处理方法
解决方案:
1.4.1 修改tomcat的日志配置,配置输出日志级别
修改conf/logging.properties日志配置文件来屏蔽掉这部分的日志信息。
将level级别设置成WARNING就可以大量减少日志的输出,当然也可以设置成OFF,直接禁用掉。
1.4.2 修改工程的日志配置:输出在控制台的级别
删除log4j中的输出控制台的日志配置,catalina.out中不再记录应用的日志。 日志输出级别:ALL、DEBUG、INFO、WARN、ERROR 这下它不会涨的那么快了。设置工程项目输出至控制台catalina.out日志的级别: WARN
1.4.3 对catalina.out 启动定时清空
编写清空脚本脚本:(默认目录tomcat 根目录)
1.4.4 tomcat不输出到catalina.out
由于最近项目需要部署到外网环境,之前在内网测试看日志都是在catalina.out,但是现在修改了一下,不需要看这个了,而且如果项目在外网环境部
署,这个文件一直会增大,浪费空间,所有墨迹了半天,才重网上找到一个比较好的方法,不输出到catalina.out. 找到tomcat下的 bin/catalina.sh; 找到下面这一段, 把#CATALINA_OUT="$CATALINA_HOME"/logs/catalina.out 注释掉,改为CATALINA_OUT=/dev/null, if [ -z "$CATALINA_OUT" ] ; then #CATALINA_OUT="$CATALINA_HOME"/logs/catalina.out CATALINA_OUT=/dev/null fi 对于/dev/null,我在网上了解是,它相当于垃圾桶一样,输出什么到它哪里,它直接丢了.所有我们在/dev/null,看到null这个文件,大小是空的,所有并不会占用空间大小了.
1.4.4 禁止日志输出到catalina.out
1、修改tomcat/conf/logging.properties中的日志输出级别
把 catalina.org.apache.juli.AsyncFileHandler.level = FINE 更改为 catalina.org.apache.juli.AsyncFileHandler.level = OFF
OFF为禁止
改后效果如图:
2、关闭localhost_access_log日志
注释tomcat/conf/server.xml中最下方AccessLogValue
3、修改tomcat/bin/catalina.sh中 185行左右
把
if [ -z "$CATALINA_OUT" ] ; then CATALINA_OUT="$CATALINA_BASE"/logs/catalina.out fi
改为
null作为垃圾桶,因此大小为0
原文链接:https://blog.csdn.net/su1573/article/details/87883126
1.5 Windows下生成calalina.out 日志文件
之前我们在linux系统下查看日志的时候,总有个习惯,启动项目后会进入logs/下,敲击类似
tail -fn500 catalina.out tail -f catalina.out
的命令,便可以进入到catalina.out中实时的显示出最新的500行信息,但是有个问题,在windows系统中我们却没发现这个catalina.out,难道是只有在linux系统下才有catalina.out文件吗?其实不是这样的,首先说下为什么在linux下是叫catalina.out,这是由于catalina_home/bin/catalina.sh文件指定的,参看下面部分源码:
shift touch "$CATALINA_BASE"/logs/catalina.out if [ "$1" = "-security" ] ; then echo "Using Security Manager" shift "$_RUNJAVA" $JAVA_OPTS "$LOGGING_CONFIG" $CATALINA_OPTS \ -Djava.endorsed.dirs="$JAVA_ENDORSED_DIRS" -classpath "$CLASSPATH" \ -Djava.security.manager \ -Djava.security.policy=="$CATALINA_BASE"/conf/catalina.policy \ -Dcatalina.base="$CATALINA_BASE" \ -Dcatalina.home="$CATALINA_HOME" \ -Djava.io.tmpdir="$CATALINA_TMPDIR" \ org.apache.catalina.startup.Bootstrap "$@" start \ >> "$CATALINA_BASE"/logs/catalina.out 2>&1 & if [ ! -z "$CATALINA_PID" ]; then echo $! > $CATALINA_PID fi
注:touch命令可以创建一个不存在的文件。
那么同理可以想象在windows下也可以存在catalina.out文件(只是默认建的文件不叫这个名而已),那么可不可以在windows系统下也看的见,或者说创建出catalina.out呢?答案是肯定的,参考如下:
由于tomcat本身是可以跨平台的,故既然有linux下的catalina.sh,就会对应有windows下的catalina.bat。
问题:windows下的tomcat的日志只输出在控制台下,且日志文件输出只有一些基本信息。如何把所有日志都输出到catalina.out?
解决方案
需要修改两个地方:
1、修改startup.bat
把call “%EXECUTABLE%” start %CMD_LINE_ARGS%修改为call “%EXECUTABLE%” run %CMD_LINE_ARGS%.
2、修改catalina.bat
查找catalina.bat含有%ACTION%的4行内容(在文件末),在后面添加 >> %CATALINA_HOME%\logs\catalina.out 或者 >> %CATALINA_BASE%\logs\catalina.out
修改之后如下图(没有显示完全):
重启tomcat后,发现在logs文件下会生成catalina.out的文件,内容为tomcat的日志。
3、不过有个弊端就是日志在命令行不输出了。
二、应用实例
2.1、maven依赖
<!-- logback begin--> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.7</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-core</artifactId> <version>1.1.3</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-access</artifactId> <version>1.1.3</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.1.3</version> </dependency> <!-- logback end-->
2.2、完整的logback.xml配置文件
<?xml version="1.0" encoding="UTF-8"?> <!-- 级别从高到低 OFF 、 FATAL 、 ERROR 、 WARN 、 INFO 、 DEBUG 、 TRACE 、 ALL --> <!-- 日志输出规则 根据当前ROOT 级别,日志输出时,级别高于root默认的级别时 会输出 --> <!-- 以下 每个配置的 filter 是过滤掉输出文件里面,会出现高级别文件,依然出现低级别的日志信息,通过filter 过滤只记录本级别的日志 --> <!-- scan 当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。 --> <!-- scanPeriod 设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。 --> <!-- debug 当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 --> <configuration scan="true" scanPeriod="60 seconds" debug="false"> <!-- 动态日志级别 --> <jmxConfigurator /> <!-- 定义日志文件 输出位置 --> <property name="log_dir" value="E:\logs" /> <!-- 日志最大的历史 30天 --> <property name="maxHistory" value="30" /> <!-- ConsoleAppender 控制台输出日志 --> <appender name="console" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern> <!-- 设置日志输出格式 --> %d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger - %msg%n </pattern> </encoder> </appender> <!-- ERROR级别日志 --> <!-- 滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件 RollingFileAppender --> <appender name="ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- 过滤器,只记录WARN级别的日志 --> <!-- 果日志级别等于配置级别,过滤器会根据onMath 和 onMismatch接收或拒绝日志。 --> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <!-- 设置过滤级别 --> <level>ERROR</level> <!-- 用于配置符合过滤条件的操作 --> <onMatch>ACCEPT</onMatch> <!-- 用于配置不符合过滤条件的操作 --> <onMismatch>DENY</onMismatch> </filter> <!-- 最常用的滚动策略,它根据时间来制定滚动策略.既负责滚动也负责出发滚动 --> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!--日志输出位置 可相对、和绝对路径 --> <fileNamePattern> ${log_dir}/error/%d{yyyy-MM-dd}/error-log.log </fileNamePattern> <!-- 可选节点,控制保留的归档文件的最大数量,超出数量就删除旧文件假设设置每个月滚动,且<maxHistory>是6, 则只保存最近6个月的文件,删除之前的旧文件。注意,删除旧文件是,那些为了归档而创建的目录也会被删除 --> <maxHistory>${maxHistory}</maxHistory> </rollingPolicy> <encoder> <pattern> <!-- 设置日志输出格式 --> %d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger - %msg%n </pattern> </encoder> </appender> <!-- WARN级别日志 appender --> <appender name="WARN" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- 过滤器,只记录WARN级别的日志 --> <!-- 果日志级别等于配置级别,过滤器会根据onMath 和 onMismatch接收或拒绝日志。 --> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <!-- 设置过滤级别 --> <level>WARN</level> <!-- 用于配置符合过滤条件的操作 --> <onMatch>ACCEPT</onMatch> <!-- 用于配置不符合过滤条件的操作 --> <onMismatch>DENY</onMismatch> </filter> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!--日志输出位置 可相对、和绝对路径 --> <fileNamePattern>${log_dir}/warn/%d{yyyy-MM-dd}/warn-log.log</fileNamePattern> <maxHistory>${maxHistory}</maxHistory> </rollingPolicy> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger - %msg%n</pattern> </encoder> </appender> <!-- INFO级别日志 appender --> <appender name="INFO" class="ch.qos.logback.core.rolling.RollingFileAppender"> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>INFO</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${log_dir}/info/%d{yyyy-MM-dd}/info-log.log</fileNamePattern> <maxHistory>${maxHistory}</maxHistory> </rollingPolicy> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger - %msg%n</pattern> </encoder> </appender> <!-- DEBUG级别日志 appender --> <appender name="DEBUG" class="ch.qos.logback.core.rolling.RollingFileAppender"> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>DEBUG</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${log_dir}/debug/%d{yyyy-MM-dd}/debug-log.log</fileNamePattern> <maxHistory>${maxHistory}</maxHistory> </rollingPolicy> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger - %msg%n</pattern> </encoder> </appender> <!-- TRACE级别日志 appender --> <appender name="TRACE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>TRACE</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${log_dir}/trace/%d{yyyy-MM-dd}/trace-log.log</fileNamePattern> <maxHistory>${maxHistory}</maxHistory> </rollingPolicy> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger - %msg%n</pattern> </encoder> </appender> <!-- root级别 DEBUG --> <root> <!-- 打印info级别日志及以上级别日志 --> <level value="INFO" /> <!-- 控制台输出 --> <appender-ref ref="console" /> <!-- 文件输出 --> <appender-ref ref="ERROR" /> <appender-ref ref="INFO" /> <appender-ref ref="WARN" /> <appender-ref ref="DEBUG" /> <appender-ref ref="TRACE" /> </root> </configuration>
我这里定义的路径为:
<!-- 定义日志文件 输出位置 --> <property name="log_dir" value="E:\logs" />
2.3 自定义异常
package com.asd.common.utils;
public class BusinessException extends RuntimeException {
private static final long serialVersionUID = 1L;
public BusinessException(){
}
public BusinessException(String message){
super(message);
}
}
2.4、应用
2.4.1 main方法
public static final Logger log = LoggerFactory.getLogger(DWSHttpServletServiceImpl.class);
public static void main(String[] args) {
int a = 0;
log.info("验证logback记录日志");
try{
//
a = testException();
} catch (BusinessException be) {
String exceptionMessge = be.getMessage();
//在日志里记录自定义异常信息
log.error(exceptionMessge);
} catch (Exception e){
e.printStackTrace();
}
System.out.println(a);
}
public static int testException(){
int a = 0;
try {
a = 1/0;
}
catch (Exception e){
throw new BusinessException("除数不能为0");//抛出自定义异常
}
return a;
}
控制台输出:
删除配置文件后:
说明配置文件是起作用的。
我这里是直接在一个web项目里(准备金系统)创建的一个main方法进行测试的,可见logback并不需要使用容器。
在web项目中的使用方式也一样。
三、logback等日志框架
3.1 概念
Log Java日志:(slf4j、log4j、logback、common-logging )
- slf4j 是规范/接口
- 日志实现:log4j、logback、common-logging
简单地说,Logback 是一个 Java 领域的日志框架。它被认为是 Log4J 的继承人。
Logback 主要由三个模块组成:
- logback-core
- logback-classic
- logback-access
logback-core 是其它模块的基础设施,其它模块基于它构建,显然,logback-core 提供了一些关键的通用机制。
logback-classic 的地位和作用等同于 Log4J,它也被认为是 Log4J 的一个改进版,并且它实现了简单日志门面 SLF4J。
logback-access 主要作为一个与 Servlet 容器交互的模块,比如说 tomcat 或者 jetty,提供一些与 HTTP 访问相关的功能。
根据不同的日志系统,你可以按如下规则组织配置文件名,就能被正确加载:
- Logback:
logback-spring.xml, logback-spring.groovy, logback.xml, logback.groovy
- Log4j:
log4j-spring.properties, log4j-spring.xml, log4j.properties, log4j.xml
- Log4j2:
log4j2-spring.xml, log4j2.xml
- JDK (Java Util Logging):
logging.properties
Logback 与 Log4J
实际上,这两个日志框架都出自同一个开发者之手,Logback 相对于 Log4J 有更多的优点:
- 同样的代码路径,Logback 执行更快
- 更充分的测试
- 原生实现了 SLF4J API(Log4J 还需要有一个中间转换层)
- 内容更丰富的文档
- 支持 XML 或者 Groovy 方式配置
- 配置文件自动热加载
- 从 IO 错误中优雅恢复
- 自动删除日志归档
- 自动压缩日志成为归档文件
- 支持 Prudent 模式,使多个 JVM 进程能记录同一个日志文件
- 支持配置文件中加入条件判断来适应不同的环境
- 更强大的过滤器
- 支持 SiftingAppender(可筛选 Appender)
- 异常栈信息带有包信息
logback是java的日志开源组件,是log4j创始人写的,性能比log4j要好,目前主要分为3个模块。
- logback-core:核心代码模块
- logback-classic:log4j的一个改良版本,同时实现了
slf4j
的接口,这样你如果之后要切换其他日志组件也是一件很容易的事 - logback-access:访问模块与Servlet容器集成提供通过Http来访问日志的功能。
3.2 实现原理:slf4j是什么
slf4j只是一套标准,通俗来讲,就是定义了一系列接口,它并不提供任何的具体实现。所以,我们使用这套接口进行开发,可以任意的切换底层的实现框架。比如,一开始项目用的是log4j的实现,后来发现log4j的性能太差了,想换成logback,由于我们代码中都是面向slf4j接口的,这样我们只要吧log4j的依赖换成logback就可以了。
3.3 总结
日志组件的使用一般都非常简单,几乎所有的项目中都会用到各种各样的日志组件。但是可能就是由于太简单了,比较少的人会愿意深入系统的去了解。本人也只是对logback的配置以及一些简单的原理做了一些了解,并没有很深入的去看logback的具体实现。
因此,本文的内容大部分都是基于官网的文档以及网上一些其他关于logback的博客,虽然也做了一些简单的测试,但并不保证全部都是正确的。
参看链接:
logback介绍和配置详解:https://www.jianshu.com/p/04065d8cb2a9
Logback配置使用:https://www.jianshu.com/p/638b4e2c4068
tomcat 日志详解:https://www.cnblogs.com/operationhome/p/9680040.html
Java日志框架:slf4j作用及其实现原理:https://www.cnblogs.com/xrq730/p/8619156.html
在所有的矛盾中,要优先解决主要矛盾,其他矛盾也就迎刃而解。
不要做个笨蛋,为失去的郁郁寡欢,聪明的人,已经找到了解决问题的办法,或正在寻找。