JAVA 打印日志

前言

  1. 最简单的方式,就是system.println.out(error) ,这样直接在控制台打印消息了。
  2. Java.util.logging ; 在JDK 1.4 版本之后,提供了日志的API ,可以往文件中写日志了。
  3. log4j , 最强大的记录日志的方式。 可以通过配置 .properties 或是 .xml 的文件, 配置日志的目的地,格式等等。
  4. commons-logging, 最综合和常见的日志记录方式, 经常是和log4j 结合起来使用。

如何规范的打印日志

日志文件命名

类型标识

指此日志文件的功能或者用途

web服务,记录http请求的日志通常命名为request.log或者access.log,request、access就是类型标识
java的gc日志通常命名为gc.log
通常用来记录服务的整体运行的日志一般用服务名称(serviceName、appKey)或者机器名(hostName)来命名,如 nginx.log

日志级别

打印日志的时候直接通过文件来区分级别是一种比较推荐的方式。
日志级别一般包括DEBUG、INFO、WARN、ERROR、FATAL这五个级别。
在实际编写代码中,可以采取严格匹配模式或者非严格匹配模式:

  • 严格匹配模式即INFO日志文件中只打印INFO日志,ERROR日志文件只打印ERROR日志
  • 非严格匹配模式即INFO日志文件可以打印INFO日志、WARN日志、ERROR日志、FATAL日志,WARN日志文件可以打印WARN日志、ERROR日志、FATAL日志
日志生成时间

在日志文件名称中附带上日志文件创建的时间,方便在查找日志文件时进行排序

日志备份编号

当进行日志切割时,如果是以文件大小进行滚动,此时可以在日志文件名称末尾加上编号

日志滚动

第一种:按照时间滚动
第二种:按照单个日志文件大小滚动
第三种:同时按照时间和单个日志文件大小滚动。

对于日志滚动策略来说,有2个比较关键的参数:

  • 最大保留日志数量
  • 最大磁盘占用空间

日志级别

debug/trace
打印内容较多,所以通常情况下不适用于线上生产环境使用,一般使用于前期线下环境调试。即使线上环境要使用,也需要通过开关来控制,只在定位追踪线上问题时才开启

info
一般用来记录系统运行的关键状态、关键业务逻辑或者关键执行节点。但切记一点,info日志绝不可滥用,如果info日志滥用,则和debug/trace日志没有太大区别了。

warning
一般用来记录系统运行时的一些非预期情况,是作为一种警示,提醒开发和运维人员需要关注,但是不用人为介入立刻去处理的。

error
一般用来记录系统运行时的一些普通错误,这些错误一旦出现,则表示已经影响了用户的正常访问或者使用,通常意味着需要人为介入处理。但很多时候在生产环境中,也不一定是出现error日志就需要人工立即介入处理的,通常会结合error日志的数量以及持续时间来进行综合判断。

fatal
属于系统致命错误,一般出现意味着系统基本等于挂掉了,需要人工立即介入处理。

日志打印时机的选用

  1. http调用或者rpc接口调用
    在程序调用其他服务或者系统的时候,需要打印接口调用参数和调用结果(成功/失败)。
  2. 程序异常
    在程序出现exception的时候,要么选择向上抛出异常,要么必须在catch块中打印异常堆栈信息。不过需要注意的是,最好不要重复打印异常日志,比如在catch块里既向上抛出了异常,又去打印错误日志(对外rpc接口函数入口处除外)。
  3. 特殊的条件分支
    程序进入到一些特殊的条件分支时,比如特殊的else或者switch分支。
  4. 关键执行路径及中间状态
    在一些关键的执行路径以及中间状态也需要记录下关键日志信息,比如一个算法可能分为很多步骤,每隔步骤的中间输出结果是什么,需要记录下来,以方便后续定位跟踪算法执行状态。
  5. 请求入口和出口
    在函数或者对外接口的入口/出口处需要打印入口/出口日志,一来方便后续进行日志统计,同时也更加方便进行系统运行状态的监控。

日志的内容与格式

通常来说,一行日志应该至少包括以下几个组成部分:

  • logTag、param、exceptionStacktrace:logTag为日志标识,用来标识此日志输出的场景或者原因
  • param为函数调用参数
  • exceptionStacktrace为异常堆栈

项目中如何正确的打日志

  1. 正确的定义日志
  2. 使用参数化形式{}占位,[] 进行参数隔离
    LOG.debug(“Save order with order no:[{}], and order amount:[{}]”);
  3. 输出不同级别的日志
几种错误的打日志方式
  1. 不要使用 System.out.print
  2. 不要使用 e.printStackTrace()
  3. 不要抛出异常后又输出日志
  4. 没有输出全部错误信息
  5. 不要使用错误的日志级别
  6. 不要在千层循环中打印日志
  7. 禁止在线上环境开启 debug

打印日志

java.util.logging - JDK记录日志方式

定义一个Logeer的实例,并设置log 的级别,接着添加一个fileHander ,就是把日志写到文件中。在写入文件的时候,定义一个 LogFormatter对日志进行格式的渲染。
默认状况下, 日志会打印到控制台。添加filehandler 后, 会同时写入文件。 如不指定路径,日志文件将位于项目根路径下。

public class TestLog {
    public static void main(String[] args) throws IOException {
        Logger logger = Logger.getLogger("testlog");
        logger.setLevel(Level.ALL);

        FileHandler fileHandler = new FileHandler("testlog.log");
        fileHandler.setLevel(Level.ALL);
        fileHandler.setFormatter(new LogFormatter());

        logger.addHandler(fileHandler);
        logger.info("This is test java util log");
    }

    static class LogFormatter extends Formatter{
        @Override
        public String format(LogRecord record){
            Date date = new Date();
            String sDate = date.toString();
            return "["+sDate+"]"+"["+record.getLevel()+"]"
                    +record.getClass()+record.getMessage()+"\n";
        }
    }
}

log4j记录日志方式

log4j.properties
日志输出的级别: info || debug || warn || error || fatal

  • ERROR 为严重错误 主要是程序的错误
  • WARN 为一般警告,比如session丢失
  • INFO 为一般要显示的信息,比如登录登出
  • DEBUG 为程序的调试信息

日志输出的目的地: stdout, logfile。 这两个名字可以随便取,比如 A, 或B都可以。
实际的配置是 org.apache.log4j.ConsoleAppender 和RollingFileAppender 用于指定是控制台还是文件。

  • appender.moder1定义的是Log输出的地方:
    • org.apache.log4j.ConsoleAppender(控制台)
    • org.apache.log4j.FileAppender(文件)
    • org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件)
    • org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件)
    • org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方)

日志信息的格式: appender.moder1.Layout

  • org.apache.log4j.HTMLLayout(以HTML表格形式布局)
  • org.apache.log4j.PatternLayout(可以灵活地指定布局模式)
  • org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串)
  • org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等等信息)
    日志输出的格式: 在appender.moder1.Layout下定义的是PatternLayout才有log4j.appender.moder1.layout.ConversionPattern
  • -X号: X信息输出时左对齐;
  • %p: 输出日志信息优先级,即DEBUG,INFO,WARN,ERROR,FATAL,
  • %d: 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyy MMM dd HH:mm:ss,SSS},输出类似:2002年10月18日 22:10:28,921
  • %r: 输出自应用启动到输出该log信息耗费的毫秒数
  • %c: 输出日志信息所属的类目,通常就是所在类的全名
  • %t: 输出产生该日志事件的线程名
  • %l: 输出日志事件的发生位置,相当于%C.%M(%F:%L)的组合,包括类目名、发生的线程,以及在代码中的行数。 举例:Testlog4.main (TestLog4.java:10)
  • %x: 输出和当前线程相关联的NDC(嵌套诊断环境),尤其用到像java servlets这样的多客户多线程的应用中。
  • %%: 输出一个"%"字符
  • %F: 输出日志消息产生时所在的文件名称
  • %L: 输出代码中的行号
  • %m: 输出代码中指定的消息,产生的日志具体信息
  • %n: 输出一个回车换行符,Windows平台为"\r\n",Unix平台为"\n"输出日志信息换行

Threshold是个全局的过滤器,它将把低于所设置的level的信息过滤不显示出来。

### 设置日志级别 ###
log4j.rootLogger=debug,stdout,logfile  

### 输出到控制台 ###
log4j.appender.stdout = org.apache.log4j.ConsoleAppender  
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout  
log4j.appender.stdout.layout.ConversionPattern = [ %p ] - [ %l ] %m%n

### 输出到日志文件 ###
log4j.appender.logfile = org.apache.log4j.RollingFileAppender  
log4j.appender.logfile.File = log4j.log  
log4j.appender.logfile.MaxFileSize = 512KB  
log4j.appender.logfile.MaxBackupIndex = 3  
log4j.appender.logfile.layout = org.apache.log4j.PatternLayout  
log4j.appender.logfile.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss} [ %p ] - [ %l ] %m%n
import org.apache.log4j.Logger;  
import org.apache.log4j.PropertyConfigurator;  

public class TestLog4j {  

    public static void main(String[] args) {  
        // 1. create log  
        Logger log = Logger.getLogger(TestLog4j.class);  
        // 2. get log config file  
        PropertyConfigurator.configure("log4j.properties");  
        // 3. start log  
        log.debug("Here is some DEBUG");  
        log.info("Here is some INFO");  
        log.warn("Here is some WARN");  
        log.error("Here is some ERROR");  
        log.fatal("Here is some FATAL");  
    }  
}  

commons-logging记录日志方式

使用Commons-logging的LogFactory获取日志处理类时:

  1. 首先在classpath下寻找自己的配置文件commons-logging.properties,如果找到,则使用其中定义的Log实现类;
  2. 如果找不到commons-logging.properties文件,则在查找是否已定义系统环境变量org.apache.commons.logging.Log,找到则使用其定义的Log实现类;
    如果在Tomact中可以建立一个叫 CATALINA_OPTS 的环境变量,给他的值:
    Dorg.apache.commons.logging.Log = org.apache.commons.logging.impl.SimpleLog
    Dorg.apache.commons.logging.simplelog.defaultlog = warn
  3. 否则,查看classpath中是否有Log4j的包,如果发现,则自动使用Log4j作为日志实现类;
    项目同时导入log4j 和commons-logging的jar 包, 不需要配置commons-logging.properties ,只需要在classpath中配置 log4j.properties就可以使用log4j的方式记录日志。
  4. 否则,使用JDK自身的日志实现类(JDK1.4以后才有日志实现类);
  5. 否则,使用commons-logging自己提供的一个简单的日志实现类SimpleLog;

先使用第一种方式来看一个实例,配置commons-logging.properties, 使用log4j来记录日志。
注意, commons-logging 要配合log4j 记录日志,必须把log4j的jar 包也导入到项目中。

  1. 导入log4j 和commons-logging的jar 包
  2. 配置commons-logging.properties 和 log4j.properties, 放入项目的classpath下(也就是src目录下)
    注意: 单独使用log4j 的时候,log4j.properties 默认是放在项目的根目录下。
    log4j.properties 的内容和上面完全相同。

commons-logging.properties

指定使用log4j
org.apache.commons.logging.impl.Jdk14Logger 使用JDK1.4。
org.apache.commons.logging.impl.Log4JLogger 使用Log4J。
org.apache.commons.logging.impl.LogKitLogger 使用 avalon-Logkit。
org.apache.commons.logging.impl.SimpleLog common-logging自带日志实现类。它实现了Log接口,把日志消息都输出到系统错误流System.err 中。
org.apache.commons.logging.impl.NoOpLog common-logging自带日志实现类。它实现了Log接口。 其输出日志的方法中不进行任何操作。

org.apache.commons.logging.Log=org.apache.commons.logging.impl.Log4JLogger
import org.apache.commons.logging.Log;  
import org.apache.commons.logging.LogFactory;  

public class TestLogCom {  

    static Log log = LogFactory.getLog(TestLog.class);  
    public static void main(String[] args) {  

        log.debug("Here is some DEBUG");  
        log.info("Here is some INFO");  
        log.warn("Here is some WARN");  
        log.error("Here is some ERROR");  
        log.fatal("Here is some FATAL");  
    }  

}  

版权声明:本文为weixin_39974866原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。