记一次线上找寻日志的苦恼——slf4j与log4j的使用

Posted 猿了个猿

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了记一次线上找寻日志的苦恼——slf4j与log4j的使用相关的知识,希望对你有一定的参考价值。

故事发生在一个苦逼的晚上,那晚在公司帮领导(代码是我们领导自己写的,这个项目不是太重要,没有经常性维护)找线上日志,通过在linux上面找遇到的问题:

         1、项目中使用了日志,但是日志没有保存到文件里面了,只能通过Catalina.out中搜索了,这个catalina.out文件达到的500M,在里面找内容,苦苦寻觅了好久终于找到问题的所在了,结果是内容太长了,而数据库里面的字段太短了,导致插入失败,他是使用的text类型,而text类型的最大值为64kb,结果数据比64kb还有大,无奈,我建议换成longtext,完美解决问题。

通过这个问题,日志查询太麻烦了,领导让做日志分割,然后第二天的故事就开始了,我去重构代码,发现具体的日志竟然有apachcommonlog,还有log4j,快吐血了,让我重构的,没办法只能吐血去重构了, 最终选用了slf4j+log4j的方式作为整套的日志组件。

1、slf4j

      slf4j,简单日志门面,不是具体的日志解决方案,它只服务于各式各样的日志系统,具体的日志是有log4jlogback等来实现,具体的介绍可以查看slf4j的官网

2、log4j

     通过log4j,我们可以控制日志信息的输送的目的地是控制台、文件、GUI组件、甚至是套接口服务器、NT的事件具体记录器等,具体的可以查看log4j官网

3、slf4j与log4j的使用

     第一步: 创建项目(创建maven项目,使用的开发工具为eclipse

    <groupId>org.slf4j</groupId>

    <artifactId>slf4j-log4j12</artifactId>

    <version>1.7.2</version>

    </dependency>

  <dependency>

    <groupId>org.slf4j</groupId>

    <artifactId>slf4j-log4j12</artifactId>

    <version>1.7.2</version>

    </dependency>

 

     第三步:新建log4j.properties文件

# 日志输出级别(INFO)和输出位置(stdoutR

log4j.rootLogger=all, stdout , R

 

# 日志输出位置为控制台

log4j.appender.stdout=org.apache.log4j.ConsoleAppender

log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

log4j.appender.stdout.layout.ConversionPattern=[QC] %p [%t] %C.%M(%L) | %m%n

 

# 日志输出位置为文件

log4j.appender.R=org.apache.log4j.DailyRollingFileAppender

log4j.appender.R.File=D:\\Tomcat 5.5\\logs\\qc.log

log4j.appender.R.layout=org.apache.log4j.PatternLayout

log4j.appender.R.layout.ConversionPattern=%d-[TS] %p %t %c - %m%n

 

# 定义相应包路径下的日志输出级别

log4j.logger.com.alibaba=DEBUG

log4j.logger.com.opensymphony.oscache=ERROR

log4j.logger.org.springframework=DEBUG

log4j.logger.com.ibatis.db=WARN

log4j.logger.org.apache.velocity=FATAL

 

log4j.logger.org.hibernate.ps.PreparedStatementCache=WARN

log4j.logger.org.hibernate=DEBUG

log4j.logger.org.logicalcobwebs=WARN

 

说明:

· log4j.rootCategory=ALL, stdout , R

    此句为将等级为INFO的日志信息输出到stdoutR这两个目的地,stdoutR的定义在下面的代码,可以任意起名。等级可分为OFFFATALERRORWARNINFODEBUGALL,如果配置OFF则不打出任何信息,如果配置为INFO这样只显示INFO, WARN, ERRORlog信息,而DEBUG信息不会被显示,具体讲解可参照第三部分定义配置文件中的logger

· log4j.appender.stdout=org.apache.log4j.ConsoleAppender

此句为定义名为stdout的输出端是哪种类型,可以是org.apache.log4j.ConsoleAppender(控制台),org.apache.log4j.FileAppender(文件),org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件),org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件)org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方)

· log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

此句为定义名为stdout的输出端的layout是哪种类型,可以是org.apache.log4j.htmlLayout(以HTML表格形式布局),org.apache.log4j.PatternLayout(可以灵活地指定布局模式),org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串),org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等等信息)

 

· log4j.appender.stdout.layout.ConversionPattern= [QC] %p [%t] %C.%M(%L) | %m%n

 如果使用pattern布局就要指定的打印信息的具体格式ConversionPattern,打印参数如下:%m 输出代码中指定的消息;%M 输出打印该条日志的方法名;%p 输出优先级,即DEBUGINFOWARNERRORFATAL%r 输出自应用启动到输出该log信息耗费的毫秒数;%c 输出所属的类目,通常就是所在类的全名;%t 输出产生该日志事件的线程名;%n 输出一个回车换行符,Windows平台为"rn”Unix平台为"n”%d 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyyy-MM-dd HH:mm:ss,SSS},输出类似:2002-10-18 22:10:28,921%l 输出日志事件的发生位置,及在代码中的行数;[QC]log信息的开头,可以为任意字符,一般为项目简称。输出示例[TS] DEBUG [main] AbstractBeanFactory.getBean(189) | Returning cached instance of singleton bean 'MyAutoProxy'

log4j的配置文件如下

#配置根Logger

log4j.rootLogger  =   [ level ]   ,  appenderName1 ,  appenderName2 ,  …

 

#配置日志信息输出目的地Appender

log4j.appender.appenderName  =  fully.qualified.name.of.appender.class

log4j.appender.appenderName.option1  =  value1

log4j.appender.appenderName.optionN  =  valueN

 

#配置日志信息的格式(布局)

log4j.appender.appenderName.layout  =  fully.qualified.name.of.layout.class

log4j.appender.appenderName.layout.option1  =  value1

log4j.appender.appenderName.layout.optionN  =  valueN

 

 其中lavel有如下几种(按照高地顺序,降序排列)

OFFFATALERRORWARNINFODEBUGALL  

Appender 为目的地log4j提供了几种方式

org.apache.log4j.ConsoleAppender(控制台),

org.apache.log4j.FileAppender(文件),

org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件),

org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件),

org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方)

 

Layout:日志输出格式,Log4j提供的layout有以下几种:

org.apache.log4j.HTMLLayout(以HTML表格形式布局),

org.apache.log4j.PatternLayout(可以灵活地指定布局模式),

org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串),

org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等等信息)

 

打印参数: Log4J采用类似C语言中的printf函数的打印格式格式化日志信息,如下:

  %m   输出代码中指定的消息

%p   输出优先级,即DEBUGINFOWARNERRORFATAL 

%r   输出自应用启动到输出该log信息耗费的毫秒数 

%c   输出所属的类目,通常就是所在类的全名 

%t   输出产生该日志事件的线程名 

%n   输出一个回车换行符,Windows平台为“/r/n”Unix平台为“/n” 

%d   输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyy MMM dd HH:mm:ss , SSS},输出类似:20021018  22  10  28 921  

%l   输出日志事件的发生位置,包括类目名、发生的线程,以及在代码中的行数。举例:Testlog4.main(TestLog4.java: 10 ) 

 

第三步:使用

package com.yuanleyuan.log4j_slf4j_test;

 

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

 

/**

 * Hello world!

 *

 */

public class App {

 

    private static Logger log = LoggerFactory.getLogger(App.class);

 

    public static void main(String[] args) {

        // error

        log.error("error");

        // info

        log.info("info{},{}", "参数化1", "参数化2");

        // warn

        log.warn("warn{}", "warn....");

 

        boolean debugEnabled = log.isDebugEnabled();

        if (debugEnabled) {

            log.debug("debug{}", "debug......");

        }

    }

}

 

输出结果

[QC] ERROR [main] com.yuanleyuan.log4j_slf4j_test.App.main(16) | error

[QC] INFO [main] com.yuanleyuan.log4j_slf4j_test.App.main(18) | info参数化1,参数化2

[QC] WARN [main] com.yuanleyuan.log4j_slf4j_test.App.main(20) | warnwarn....

[QC] DEBUG [main] com.yuanleyuan.log4j_slf4j_test.App.main(24) | debugdebug......

 

大家都看到了吧,建议大家,忘记System.out,而要牢牢的记住log,而且还要在真正开始一个项目之前准备好工作,不要上来就写,导致后期问题很多,重构的时候被喷,所以这些项目基本的要牢牢记住,不要因为这些引起别人的厌烦。

 


以上是关于记一次线上找寻日志的苦恼——slf4j与log4j的使用的主要内容,如果未能解决你的问题,请参考以下文章

记一次线上内存溢出问题排查过程

记一次线上事故

记一次线上事故

记一次线上服务器频繁宕机

记一次线上FGC问题排查

记一次线上gc调优的过程