java日志体系

为什么要使用日志

刚开始接触java时都使用过System.out来调试,通过它我们能打印出一些关注的信息到控制台便于我们调试。这种方式只限于我们平常开发时简单测试,但是生产环境这样是不行的。首先这样做会有性能上的问题,其次使用这种方式会很麻烦。例如我打印的结果需要显示方法名称、线程名称、时间等等,你总不能每次都手动拼接然后打印吧;还例如我需要将它输入到不同的文件中。以上说的都是System.out所不能实现或者特别麻烦的地方

混乱的日志体系

上面我们总结了System.out不足之处,所以我们正式项目一般都是使用的日志。但是java中的日志体系特别乱,特别对于新手来说简直就是一脸闷逼。你肯定听过Log4j、JUL、JCL、Slf4j、Logback、Log4j2这些里面的几种,面对这么多日志相关的东西,你除此接触简直就是一脸闷逼。下面我们来简单说说这些日志工具的关系和历史。

Log4j:Java之前官方并没有提供日志相关的工具,而Log4j就是最早提供日志功能的工具了,然后大家都开始使用这个工具,而Log4j也几乎成为Java标准的日志库了。

JUC:log4j很流行但是并没有成为java标准的日志库,Sun公司推出了JUL(java Util Logging)。因为在之前大家已经习惯使用Log4j了,所以JUL并没有流行起来。

JCL:上面说了已经存在Log4j和JUL,如果想从Log4j换成JUC或者从JUC换成Log4j很麻烦,需要修改所有日志调用的地方,所以Apache为了解决问题推出了JCL(Jakarta Commons Loggin)。JCL中只是定义了一套日志接口,支持在运行时动态的加载日志组件的实现。也就是说我们在代码里使用JCL的API,底层我们可以使用Log4j或者JUC实现,这样我们在切换日志实现时,不需要修改大量的代码。

Slf4j和Logback:而之前开发Log4j的作者离开原来的公司之后,觉得之前的日志框架还不够牛逼,于是他再次出手,掏出Slfj4和Logback(Sl4j的实现)两个项目。这下java日志领域基本上就分成两个帮派了,Commons Loggin和Slf4j。而随着时间推移,Slf4j搭配Logback慢慢的抢占了Log4j的用户。

Log4j 2:上面说了Slf4j搭配Logback慢慢的抢占了Log4j的用户,这个时候Log4j推出了Log4j 2.x与Logback对战。

看了上面的java日志工具的发展,现在大概明白了日志工具的体系了吧。对于上面说的工具基本上可以分为两类,一类是日志接口,也就是日志门面,它们不提供实现或者提供简单的实现。而另外一类是日志实现,也就是实际上实现日志功能的工具。

对于日志工具包门面工具主要就是Slf4j和JCL这个两个,而实际上因为效率问题,目前日志门面大家都使用的是Slf4j,而JCL逐渐的退出了舞台。

对于日志的实现现在基本上的选择就是Log4j、Logback、Log4j 2。

Slf4j和Logback组合使用

这个组合应该是目前使用最广的,至少我个人使用比较多,而且在Spring Boot中也默认使用的该组合,下面就说如何使用Slf4j和Logback组合使用。

jar包说明

logback-core:Logback核心功能包,如果只是想用Logback使用这个包就可以了。

logback-access:访问模块集成和Servlet容器集成,提供Http访问日志的功能。

logback-classic:如果想与Slf4j集成就需要使用到这个包,这个包里面还依赖了Slf4j的包。

一般情况下我们使用Slf4j与Logback只需要在工程中添加下面的依赖即可,不需要我们手动添加Slf4j包的依赖。

<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.3</version>
</dependency>

Logback配置说明

如果不需要定制化的日志只需要添加相关的maven依赖就可以了,如果需要定制化需求就需要自己添加配置。Logback支持通过XML和groovy的方式来配置,但是使用较多的方式还是通过XML这种方式配置。配置时我们只需要在创建Logback.xml文件放在resource下即可。

<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="true">
    <!--设置变量-->
    <property name="APP_NAME" value="logback-demo"/>
    <!--上下文名称,设置之后不能再修改-->
    <contextName>${APP_NAME}</contextName>
    <!--配置appender-->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%-4relative [%thread] %-5level %logger{35} - %msg %n</pattern>
        </encoder>
    </appender>
    <!--配置logger-->
    <logger name="com.buydeem.share.log.logback" level="debug" additivity="true"/>
    <!--配置root-->
    <root level="info">
        <!--引用appender-->
        <appender-ref ref="STDOUT"/>
    </root>
</configuration>

configuration

上面就是一个最简单的logback配置文件,首先就是configuration节点的配置,其中scanscanPeriod是用来监控配置文件变化的,scan设置为true时代表会扫描配置文件的变化,而scanPeriod是用来指定扫描频率。而debug是用来打印Logback内部的日志,它会打印如何查找配置文件和实际应用的日志文件等信息。

contextName

设置应用的名称,该值设置之后是无法再次修改的。简单的说就是应用启动之后,再次修改该值是不生效的。

property

用来定义变量,在后续的配置中可是使用该变量。

appender

日志输出组件,主要用来输入和格式化日志,而且Logback中提供了多种appender组件,不同的组件有不同的效果。例如可以将日志输出在控制台中,还可以将日志输出到文件中。例如我们上文中使用的ch.qos.logback.core.ConsoleAppender它就是将日志输出到控制台。

对于appender节点来说我们还可以配置其他东西,例如我们可以筛选日志的级别,让该appender中只输出某种级别的日志。利用这个设置,我们可以将不同级别的日志输出到不同的文件中。

logger和root

logger用来设置某一个包或者具体某一个类的日志打印级别以及指定appender。你可以理解logger和root是同一种东西,不同的在于root是logger的最上级。对于logger节点中,我们可以设置的属性有level和additivity两个属性。

level代表该appender中输出的最低日志级别,例如上面的配置文件中我们将其设置成debug代表低于该级别的日志将不会再logger中打印出来。如果我们没有设置将应用父级的level,如果logger没有父级则会使用root中的level(root是所有level最上级)。

additivity用来设置该logger是否向上级传递,如果设置为true,该logger下打印的日志还会传递到root中。在我们的配置中,我们并没有在logger下配置appender,但是仍然可以打印出日志。这是因为我们在root节点下配置了appender,并且我们还把logger的additivity设置成了true。如果我们将该值改为false,你可以发现日志将不会再控制台中输出了。

配置不同级别的日志内容输出到不同的文件

对于实际开发中我们可能需要将不同的日志输出到不同的文件,下面是一个示例配置。

<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds">
    <!--日志文件前缀,即应用名称 -->
    <property name="logfile.prefix" value="logback-demo"/>
    <!--日志路径,可写相对路径,也可写绝对路径 -->
    <property name="log.path" value="logs"/>
    <!-- 日志输出格式 -->
    <property name="log.pattern"
              value="%d{yyyy-MM-dd HH:mm:ss.SSS} %5level [%15thread] %40.40logger{40} [%10method,%line] : %msg%n"/>
    <!-- 控制台输出日志 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <!--        设置日志输出格式-->
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${log.pattern}</pattern>
            <!-- 编码 -->
            <charset>UTF-8</charset>
        </encoder>
    </appender>
    <!-- 文件输出日志, 滚动(时间/文件大小)输出策略 -->
    <appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 过滤器,只记录debug级别的日志 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>DEBUG</level>
            <OnMismatch>DENY</OnMismatch>
            <OnMatch>ACCEPT</OnMatch>
        </filter>
        <!-- 日志文件路径及文件名 -->
        <File>${log.path}/${logfile.prefix}-debug.log</File>
        <!-- 日志记录器的滚动策略,按日期记录 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 日志输出格式 -->
            <FileNamePattern>${log.path}/${logfile.prefix}-debug.%d{yyyy-MM-dd}.log</FileNamePattern>
            <!-- 日志保留天数 -->
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <!--        设置日志输出格式-->
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${log.pattern}</pattern>
            <!-- 编码 -->
            <charset>UTF-8</charset>
        </encoder>
    </appender>
    <!--    info级别-->
    <appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>INFO</level>
            <OnMismatch>DENY</OnMismatch>
            <OnMatch>ACCEPT</OnMatch>
        </filter>
        <File>${log.path}/${logfile.prefix}-info.log</File>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <FileNamePattern>${log.path}/${logfile.prefix}-info.%d{yyyy-MM-dd}.log</FileNamePattern>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <!--        设置日志输出格式-->
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${log.pattern}</pattern>
            <!-- 编码 -->
            <charset>UTF-8</charset>
        </encoder>
    </appender>
    <!--    warn级别-->
    <appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>WARN</level>
            <OnMismatch>DENY</OnMismatch>
            <OnMatch>ACCEPT</OnMatch>
        </filter>
        <File>${log.path}/${logfile.prefix}-warn.log</File>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <FileNamePattern>${log.path}/${logfile.prefix}-warn.%d{yyyy-MM-dd}.log</FileNamePattern>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <!--        设置日志输出格式-->
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${log.pattern}</pattern>
            <!-- 编码 -->
            <charset>UTF-8</charset>
        </encoder>
    </appender>
    <!--    error级别-->
    <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <OnMismatch>DENY</OnMismatch>
            <OnMatch>ACCEPT</OnMatch>
        </filter>
        <File>${log.path}/${logfile.prefix}-error.log</File>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <FileNamePattern>${log.path}/${logfile.prefix}-error.%d{yyyy-MM-dd}.log</FileNamePattern>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <!--        设置日志输出格式-->
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${log.pattern}</pattern>
            <!-- 编码 -->
            <charset>UTF-8</charset>
        </encoder>
    </appender>

    <logger name="com.buydeem.share.log.logback" level="debug" additivity="true"/>
    <!-- 日志输出 -->
    <root level="DEBUG">
        <appender-ref ref="STDOUT"/>
        <appender-ref ref="DEBUG_FILE"/>
        <appender-ref ref="INFO_FILE"/>
        <appender-ref ref="WARN_FILE"/>
        <appender-ref ref="ERROR_FILE"/>
    </root>
</configuration>
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,270评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,489评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,630评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,906评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,928评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,718评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,442评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,345评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,802评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,984评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,117评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,810评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,462评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,011评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,139评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,377评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,060评论 2 355

推荐阅读更多精彩内容