当前位置: 首页 > 工具软件 > log > 使用案例 >

日志Log精讲

弘承业
2023-12-01

1日志为什么要存在

       我们平时写的工具或应用程序,可以通过人眼查看调试来查看运行过程中出现的错误,可以通过system.out.println()来输出程序运行状态信息查看。

       (1)那么,一个庞大复杂的项目,含有大量的接口和方法,执行过程冗长繁杂,还要使用上述的办法来慢慢排查问题么?

       自然不是,一个设计良好的项目,肯定记录了项目运行日志log。

       日志的强大之处,在于它能记录保存项目的运行状态,便于日后查阅,很是方便快捷、易于调试;更重要的是,他还能够完成跟踪调试、程序状态记录、崩溃数据恢复等工作

       但如果使用system.out.println(),则太弱了,不仅繁琐麻烦,输出的地方和多少都不易控制。

       (2)那么,如何设计一个良好的日志类来应用到项目中,便于提升项目的开发和维护呢?

       答案:在java的世界里,我们不用考虑和设计日志类,因为已经存在了很多优秀的日志系统供我们使用,比如:Log4j和Logback,还有sun公司的java.util.Logging,另外java项目中的各种框架(如:Spring,Mybatis,Httpclient…)等第三方包都有自己的日志系统来实现日志功能,但是这么多的日志实现系统同时存在,难以避免的就出现了各个日志系统不兼容的灾难性情况!

       (3)那么,应该如何解决杂乱的日志系统不兼容的灾难情况呢?

       解决办法就是出现了日志框架。

2 日志系统和日志框架

       日志系统:日志类的具体实现。经典的有log4j,log4j作者推出的被高度评价的logBack,以及jdk自带的java.util.Logging等。

       日志框架:为解决多个日志系统的兼容性问题而设计的框架,当然如果只有一个日志系统,就完全没必要使用日志框架了。常用的主流日志框架有commons-logging和slf4j。

       下面说一下这两种主流框架的使用!

3 commons-logging框架

3.1介绍

       commons-logging是apache推出的日志框架,只是规定了日志的接口规范,可以理解为是一个通用的日志接口,其设计原理类似于jdk中servlet和jdbc的设计。

       目前,主流的日志系统log4j和java.util.Logging都实现了commons-logging定义的接口。那么,就可以很方便的使用这些实现了 commons-logging接口的日志系统了,使用的时候不用去考虑具体的日志实现或到底是哪种日志系统,用户可以自由选择第三方日志组件作为具体实现。

举个便于理解的例子

       有这样两个实现了Log接口的类:

public class MyLog implements Log {
}

public class YourLog implements Log {
}

       你可以把commons-logging框架理解为Log接口,只是一个定义了规范的接口,把实现了commons-logging框架的众多日志系统看作是实现了Log接口的实现类MyLog和YourLog。使用的时候拿着Log接口使用就行了,不用关注具体的实现类;即,使用日志系统的时候拿着日志框架commons-logging使用就行了。

3.2 好处总结

       第一:简化了使用和配置,为“所有的Java日志实现”提供一个统一的接口,并且能分离项目和日志。

       第二:使用日志框架commons-loging可以自适应的选择日志实现系统。

自适应日志工具选择功能

不得不说的是它的自适应日志工具选择功能?

       1)   commons-logging首先在classpath下寻找自己的配置文件commons-logging.properties,找到则使用自己定义的Log;找不到则继续查找是否已定义系统环境变量org.apache.commons.logging.Log,找到则使用自己定义的Log;

       2)否则,在classpath中查找是否有Log4j的路径,若有则使用Log4j;

       3)否则,使用JDK(1.4版本以后提供了日志功能)自身的日志实现类;

       4)如果还没有,则使用commons-logging自己提供的一个简单的日志实现类SimpleLog;

       所以,commons-logging可以保证日志功能的实现,自适应的尽可能找到一个最合适的工具,这也是它的优势所在。

       可以看到,commons-logging对编程者和Log4j都非常友好。为了简化配置,一般不使用commons-logging的配置文件,也不设置与commons-logging相关的系统环境变量,而只需将Log4j的Jar包放置到classpash中就可以了。这样就很简单地完成了commons-logging与Log4j的融合。如果不想用Log4j了怎么办?只需将classpath中的Log4j的Jar包删除即可。就这么简单!

3.2 经典应用commons-logging+log4j

       这种实现出现在各种框架里,如Spring,Webx,Mybatis等等, 一般为了避免直接依赖具体的日志实现,一般都是结合commons-logging 来实现。常见应用实现代码如下:

    //1: 导入所有需的commons-logging类
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class XXX{
    //2:要使用日志的类XXX中定义私有静态变量logger,,这是目前被普通认为的最好的方式;
//静态是为了避免出现多个实例,
   private static Log logger = LogFactory.getLog(XXX.class);
    //3:在要使用日志的方法中使用日志
   public void method(args){
    log.debug("111"); 
    log.info("222"); 
    log.warn("333"); 
    log.error("444"); 
    log.fatal("555");
    }
}

       实例化私有静态日志变量的时候,为什么不写作LogFactory.getLog(this.getClass())?因为static类成员访问不到this指针!

       通过上述,可以基本满足输出日志信息,如果要对输出的信息有特殊要求,那么,就需要输出配置信息log4j.properties。

3.3 日志输出 配置文件

       在程序中,可以通过每个类的私有静态变量log,将不同性质的日志信息输出到不同类型的日志文件或不同目的地(目的地是哪里?视配置可定,可能是stdout,也可能是文件,还可能是发送到邮件,甚至发送短信到手机……详见下文对log4j.properties的介绍):

l         debug()   输出“调试”级别的日志信息;

l         info()      输出“信息”级别的日志信息;

l         warn()    输出“警告”级别的日志信息;

l         error()     输出“错误”级别的日志信息;

l         fatal()      输出“致命错误”级别的日志信息;

       根据不同的性质,日志信息通常被分成不同的级别,从低到高依次是:“调试(DEBUG)”“信息(INFO)”“警告(WARN)”“错误(ERROR)”“致命错误(FATAL)”。为什么要把日志信息分成不同的级别呢?这实际上是方便我们更好的控制它。比如,通过Log4j的配置文件,我们可以设置“输出‘调试’及以上级别的日志信息”(即“调试”“信息”“警告”“错误”“致命错误”),这对项目开发人员可能是有用的;我们还可以设置“输出“警告”及以上级别的日志信息”(即“警告”“错误”“致命错误”),这对项目最终用户可能是有用的。

       仅从字面上理解,也可以大致得出结论:最常用的应该是debug()和info();而warn()、error()、fatal()仅在相应事件发生后才使用。

       所以要正确地应用Log4j输出日志信息,log4j.properties

       log4j.rootLogger  =  DEBUG, CONSOLE,A1

       #最最重要的一个属性, 分成两部分:第一个逗号之前的是第一部分,指定输出级别;后面的是第二部分,指定输出目的地

        “输出级别有可选的五个值,分别是DEBUGINFOWARNERRORFATAL,它们是由Log4j系统定义的。如果此处指定的是“WARN”则仅调用warn()error()fatal()方法输出的日志信息才被输出到输出目的地,而调用debug()info()方法输出的日志信息不被输出到输出目的地

        “输出目的地就是我们自己定义的了,就在log4j.properties的后面部分,此文件定义的输出目的地CONSOLEFILE ROLLING_FILESOCKETLF5_APPENDERMAILDATABASEA1im。该文件之所以可作主模板,就是因为它比较 全面地定义了各种常见的输出目的地(控制台、文件、电子邮件、数据库等)。

       为什么说“CONSOLE”表示将日志信息输出到控制台呢?那就要看一下后文的定义了:

# 应用于控制台

log4j.appender.CONSOLE = org.apache.log4j.ConsoleAppender 
log4j.appender.Threshold = DEBUG 
log4j.appender.CONSOLE.Target = System.out 
log4j.appender.CONSOLE.layout = org.apache.log4j.PatternLayout 
log4j.appender.CONSOLE.layout.ConversionPattern = [framework] %d-%c -%-4r [%t] %-5p %c %x-%m%n 
#log4j.appender.CONSOLE.layout.ConversionPattern = [start]%d {DATE}[DATE]%n %p[PRIORITY]%n %x[NDC]%n %t[THREAD] n %c[CATEGORY] %n %m[MESSAGE] %n %n

       为什么说“A1”表示将日志信息输出到“SampleMessages.log4j文件呢?还要看后文的定义:

log4j.appender.A1 = org.apache.log4j.DailyRollingFileAppender 
log4j.appender.A1.File = SampleMessages.log4j 
log4j.appender.A1.DatePattern = yyyyMMdd - HH '.log4j'  
log4j.appender.A1.layout = org.apache.log4j.xml.XMLLayout

log4j.properties文件模板

##Log4J的配置之简单使它遍及于越来越多的应用中了
##Log4J配置文件实现了输出到控制台、文件、回滚文件、发送日志邮件、输出到数据库日志表、自定义标签等全套功能。择其一二使用就够用了。

##此文件(log4j.properties)内容来自网络,非本文作者liigo原创。
log4j.rootLogger = DEBUG, CONSOLE,A1
log4j.addivity.org.apache = true 

# 应用于控制台
log4j.appender.CONSOLE = org.apache.log4j.ConsoleAppender
log4j.appender.Threshold = DEBUG
log4j.appender.CONSOLE.Target = System.out
log4j.appender.CONSOLE.layout = org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern = [framework] %d-%c -%-4r[%t] %-5p %c %x-%m %n
#log4j.appender.CONSOLE.layout.ConversionPattern = [start] %d {DATE}[DATE]%n %p[PRIORITY] %n %x[NDC] %n %t[THREAD] n %c[CATEGORY]%n %m[MESSAGE]%n %n

#应用于文件
log4j.appender.FILE = org.apache.log4j.FileAppender
log4j.appender.FILE.File = file.log
log4j.appender.FILE.Append = false
log4j.appender.FILE.layout = org.apache.log4j.PatternLayout
log4j.appender.FILE.layout.ConversionPattern = [framework]  %d-%c-%-4r[%t] %-5p %c%x-%m%n
# Use  this  layout  for  LogFactor  5  analysis

# 应用于文件回滚
log4j.appender.ROLLING_FILE = org.apache.log4j.RollingFileAppender
log4j.appender.ROLLING_FILE.Threshold = ERROR
log4j.appender.ROLLING_FILE.File = rolling.log
log4j.appender.ROLLING_FILE.Append = true 
log4j.appender.ROLLING_FILE.MaxFileSize = 10KB
log4j.appender.ROLLING_FILE.MaxBackupIndex = 1 
log4j.appender.ROLLING_FILE.layout = org.apache.log4j.PatternLayout
log4j.appender.ROLLING_FILE.layout.ConversionPattern = [framework]  %d-%c-%-4r[%t]%-5p%c%x-%m%n

#应用于socket
log4j.appender.SOCKET = org.apache.log4j.RollingFileAppender
log4j.appender.SOCKET.RemoteHost = localhost
log4j.appender.SOCKET.Port = 5001 
log4j.appender.SOCKET.LocationInfo = true 
# Set up  for  Log Facter 5
log4j.appender.SOCKET.layout = org.apache.log4j.PatternLayout
log4j.appender.SOCET.layout.ConversionPattern = [start] %d {DATE}[DATE]%n%p[PRIORITY]%n%x[NDC]%n%t[THREAD]%n%c[CATEGORY]%n%m[MESSAGE]%n%n

# Log Factor  5  Appender
log4j.appender.LF5_APPENDER = org.apache.log4j.lf5.LF5Appender
log4j.appender.LF5_APPENDER.MaxNumberOfRecords = 2000 

# 发送日志给邮件
log4j.appender.MAIL = org.apache.log4j.net.SMTPAppender
log4j.appender.MAIL.Threshold = FATA
log4j.appender.MAIL.BufferSize = 10 
log4j.appender.MAIL.From = web@www.wuset.com
log4j.appender.MAIL.SMTPHost = www.wusetu.com
log4j.appender.MAIL.Subject = Log4J Message
log4j.appender.MAIL.To = web@www.wusetu.com
#SMTP发送认证的帐号名
log4j.appender.MAIL.SMTPUsername=****
#SMTP发送认证帐号的密码
log4j.appender.MAIL.SMTPPassword=******
log4j.appender.MAIL.layout = org.apache.log4j.PatternLayout
log4j.appender.MAIL.layout.ConversionPattern = [framework]  %d-%c-%-4r[%t]%-5p%c%x-%m%n

# 用于数据库
log4j.appender.DATABASE = org.apache.log4j.jdbc.JDBCAppender
log4j.appender.DATABASE.URL = jdbc:mysql: // localhost:3306/test 
log4j.appender.DATABASE.driver = com.mysql.jdbc.Driver
log4j.appender.DATABASE.user = root
log4j.appender.DATABASE.password = 
log4j.appender.DATABASE.sql = INSERT INTO LOG4J (Message) VALUES ( '[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n' )
log4j.appender.DATABASE.layout = org.apache.log4j.PatternLayout
log4j.appender.DATABASE.layout.ConversionPattern = [framework] %d-%c-%-4r[%t]%-5p%c%x-%m%n
log4j.appender.A1 = org.apache.log4j.DailyRollingFileAppender
log4j.appender.A1.File = SampleMessages.log4j
log4j.appender.A1.DatePattern = yyyyMMdd - HH '.log4j' 
log4j.appender.A1.layout = org.apache.log4j.xml.XMLLayout

#自定义Appender
log4j.appender.im  =  net.cybercorlin.util.logger.appender.IMAppender
log4j.appender.im.host  =  mail.cybercorlin.net
log4j.appender.im.username  =  username
log4j.appender.im.password  =  password
log4j.appender.im.recipient  =  corlin@cybercorlin.net
log4j.appender.im.layout = org.apache.log4j.PatternLayout
log4j.appender.im.layout.ConversionPattern  = [framework]  %d-%c-%-4r[%t]%-5p%c%x-%m%n

# 结束

4slf4j框架

4.1介绍

       slf4j全称为Simple Logging Facade for JAVA,一个java的简单日志门面。类似于Apache Common-Logging,是对不同日志框架提供的一个门面封装,可以在部署的时候不修改任何配置即可接入一种日志实现方案。但是,他在编译时静态绑定真正的Log库。使用SLF4J时,如果你需要使用某一种日志实现,那么你必须选择正确的SLF4Jjar包的集合。

4.2经典应用slf4j+logback

       Logback必须配合sl4j使用。由于logbacksl4j是同一个作者,其兼容性不言而喻。但sl4j面临与其他日志框架和日志系统的兼容性问题

       常见应用实现代码如下:

import org.slf4j.Logger; 
import org.slf4j.LoggerFactory;    
public class XXX { 
    private static Log logger = LogFactory.getLog(this.getClass()); 
}

4.3日志输出 配置文件

       类似于commons-logging的输出配置文件,slf4j也同样需要一个配置文件logback.xml,通过该配置文件,在程序运行之后,slf4j会自动加载logback的日志实现,然后logback从类路径中读取logback.xml文件中的日志配置信息,实现日志的记录等功能。

       一个logback.xml模板配置示例及注释详解:

<?xml version="1.0" encoding="UTF-8"?>
<!-- scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。 
     scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。
     debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 -->
<configuration scan="true" scanPeriod="60 seconds" debug="false">

    <!-- 上下文变量设置,用来定义变量值,其中name的值是变量的名称,value的值时变量定义的值。
        通过<property>定义的值会被插入到logger上下文中。定义变量后,可以使“${}”来使用变量。 -->
    <property name="CONTEXT_NAME" value="logback-test" />

    <!-- 上下文名称:<contextName>, 每个logger都关联到logger上下文,
        默认上下文名称为“default”。但可以使用<contextName>设置成其他名字,用于区分不同应用程序的记录。
        一旦设置,不能修改。 -->
    <contextName>${CONTEXT_NAME}</contextName>

    <!-- <appender>是<configuration>的子节点,是负责写日志的组件。
        有两个必要属性name和class。
        name指定appender名称,
        class指定appender的实现类。 -->
    <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
        <!-- 对日志进行格式化。 -->
        <encoder>
            <pattern>
                %d{yyyy-MM-dd HH:mm:ss.SSS}|%level|%class|%thread|%method|%line|%msg%n
            </pattern>
        </encoder>
    </appender>

    <appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 被写入的文件名,可以是相对目录,也可以是绝对目录,如果上级目录不存在会自动创建,没有默认值。 -->
        <file>${logs.dir}/logback-test.log</file>

        <!-- 当发生滚动时的行为  -->
        <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
            <!-- 必须包含“%i”例如,假设最小值和最大值分别为1和2,命名模式为 mylog%i.log,会产生归档文件mylog1.log和mylog2.log。还可以指定文件压缩选项,例如,mylog%i.log.gz 或者 没有log%i.log.zip -->
            <FileNamePattern>${logs.dir}/logback-test.%i.log</FileNamePattern>
            <!-- 窗口索引最小值 -->
            <minIndex>1</minIndex>
            <!-- 窗口索引最大值 -->
            <maxIndex>1</maxIndex>
        </rollingPolicy>

        <!-- 激活滚动的条件。 -->
        <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
            <!-- 活动文件的大小,默认值是10MB -->
            <maxFileSize>30MB</maxFileSize>
        </triggeringPolicy>

        <!-- 对记录事件进行格式化。 -->
        <encoder>
            <charset>UTF-8</charset>
            <Pattern>
                %d{yyyy-MM-dd HH:mm:ss.SSS}|%level|%class|%thread|%method|%line|%msg%n
            </Pattern>
        </encoder>
    </appender>

    <!-- 特殊的<logger>元素,是根logger。只有一个level属性,应为已经被命名为"root".
        level:设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,不能设置为INHERITED或者同义词NULL。默认是DEBUG。
        <root>可以包含零个或多个<appender-ref>元素,标识这个appender将会添加到这个loger。 -->
    <root>
        <level value="WARN" />
        <appender-ref ref="stdout" />
        <appender-ref ref="file" />
    </root>

    <!-- 用来设置某一个 包 或者具体的某一个 类 的日志打印级别、以及指定<appender>, 
        name:用来指定受此logger约束的某一个包或者具体的某一个类。
        level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,还有一个特俗值INHERITED或者同义词NULL,代表强制执行上级的级别。如果未设置此属性,那么当前loger将会继承上级的级别。 
        additivity:是否向上级logger传递打印信息。默认是true。(这个logger的上级就是上面的root)
        <logger>可以包含零个或多个<appender-ref>元素,标识这个appender将会添加到这个logger。-->
    <logger name="xuyihao.logback.test" level="DEBUG" additivity="true"></logger>

</configuration>

 

参考资料:

《java日志组件介绍(common-logging,log4j,slf4j,logback )》

Java日志框架与日志系统

《log4j和log》

《logback.xml配置模版》

 类似资料: