Java logging整理

Wesley13
• 阅读 835

常见的java logging

  • System.out与System.err

  • java.util.logging

  • apache log4j

  • apache commons logging

  • SLF4J

其中System.out与System.err就不多说了。我们学习的第一个程序可能就是它们两个。

Logging的通用概念

Logging拥有通用的概念,在logging中,包含以下几个元素:

  1. logger - 产生log record,并设置record的level

  2. level - 两个地方可以设置过滤信息,1)logger的level, 2)handler的level.

  3. logRecord

  4. formatter/render/layout - 将log record转换为格式化的语言

  5. handler/appender - 将message输出到console, file, stream...

  6. 以及隐藏的logger属性的继承概念。所有的logger总会有一个rootLogger,在其每个path下的logger总会继承其父路径的level与handler.

Java Util Logging

调用方法

Java logging是java自带的logging工具包,在java.util.logging包中。使用方法很简单

public static Logger logger = LogManager.getLogger(A.class)
public void test() {
    logger.info("A.info");
    logger.warning("A.warning");
    logger.severe("A.servere");
    logger.fine("A.fine");
}

配置方法

默认的配置文件为:jre/lib/logging.properties
可以通过4种方法覆盖默认配置:

  1. java -Djava.util.logging.config.file=...

  2. java -Djava.util.logging.config.class=...

  3. 书写自己的LogManager, 在logManager初始化中使用readConfiguration()来读入新的配置。

  4. 在运行时,动态的new xxxHandler和logger.setHandler()来配置logger.

在logging.properties中可以配置Level,handler,formatter

Java自带Logging的Level有SEVERE, WARNING, INFO, CONFIG, FINE, FINER, FINEST, OFF, ALL。

logger的level与handler的level共同控制着消息的输出。只有当两个level都满足,才会真正的输出。

两个标准的Formatter: java.util.logging.XMLFormatter, java.util.logging.SimpleFormatter

五个标准Handler: ConsoleHandler, FileHandler, StreamHandler, SocketHandler, MemoryHandler

此外还可以自己复写handler与formatter.

一个logging.properties的例子:

注意handlers不是overwrite的,而是累加的。例如,handlers声明的所有handler都应用于每个类。而每个类再次声明的handler将累加到前面的handler上。

handlers = 
.level   =
config   =

"logger".handlers           =
"logger".useParentHandlers  =
"logger".level              =

java.util.logging.SimpleFormatter.format=%4$s: %5$s [%1$tc]%n

java.util.logging.FileHandler.level     = WARNING
java.util.logging.FileHandler.filter    =
java.util.logging.FileHandler.formatter =
java.util.logging.FileHandler.encoding  =
java.util.logging.FileHandler.limit     =
java.util.logging.FileHandler.count     =
java.util.logging.FileHandler.append    = false
java.util.logging.FileHandler.pattern   = log.%u.%g.txt

java.util.logging.ConsoleHandler.level     = WARNING
java.util.logging.ConsoleHandler.filter    =
java.util.logging.ConsoleHandler.formatter =
java.util.logging.ConsoleHandler.encoding  =

Apache log4j

log4j2很快就会出来了,它将取代Log4j. 但log4j还是在很多地方被使用。Maven引入log4j的方法是

<dependency>
  <groupId>log4j</groupId>
  <artifactId>log4j</artifactId>
  <version>1.2.17</version>
</dependency>

在log4j中,没有LogFactory, LoggerFactory或LogManager,直接调用Logger.getLogger()即可初始化logger。

import org.apache.log4j.Logger;
public class App {
    private static Logger logger = Logger.getLogger(App.class);
    public static void main( String[] args ) {
        if (logger.isInfoEnabled()) {
            logger.info("app.info");
        }
        if (logger.isDebugEnabled()) {
            logger.info("app.debug");
        }
    }
}

log4j跟其它logger机制一样,存在着基本的logger所有基本概念,只是它的fomatter叫做patternLayout, 而handler则叫做appender. 另外log4j和java util logging在level的划分上有所不同,log4j的level分为:TRACE,DEBUG,INFO,WARN,ERROR,FATAL,ALL。

 log4j的所有配置可以放在一个log4j.properties中:

#log4j.logger.[package name]=level, appender1, appender2...

#define rootlogger
log4j.rootLogger=info, RF
log4j.appender.RF=org.apache.log4j.RollingFileAppender
log4j.appender.RF.File=example.log
log4j.appender.RF.MaxFileSize=100KB
# Keep one backup file
log4j.appender.RF.MaxBackupIndex=1
log4j.appender.RF.layout=org.apache.log4j.PatternLayout
log4j.appender.RF.layout.ConversionPattern=%p %t %c - %m%n

#define logger for com.my.test
log4j.logger.com.my.test=debug, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
# Pattern to output the caller's file name and line number.
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n

#com.my.test log messages do not transfer to parent's appenders.
log4j.additivity.com.my.test=false

将log4j.properties放在classpath下面,就可以被自动加载了。如果log4j.properties不在classpath下,或者其名字叫other.properties, 则可以指定system property: 

java -Dlog4j.configuration=other.properties ....

Apache Commons Logging

也叫Jakarta Commons logging, 简称JCL, 其的目的是为所有流行的logging提供一个通用接口。用户在开发的时候只需要调用commons logging的接口。而运行环境中具体使用的是log4j,util logging或其它的logging, 则由commons logging配置文件或者commons logging的自动探测来决定。

Commons Logging包含三个jar:

  1. commons-logging-api.jar  这里面包含了commons logging的通用接口以及两个logger的实现:SimpleLog和NoOpLog.

  2. commons-logging.jar 这里面包含了通用接口,以及Log4j,JDK logger等的适配器。这个是最常用的包。

  3. commons-logging-adapters.jar 只包含了第三方logger的适配器。必须和上面两个包中的一个搭配使用。

Maven引入commons-logging.jar的方法:

    <dependency>
        <groupId>commons-logging</groupId>
        <artifactId>commons-logging</artifactId>
        <version>1.1.3</version>
    </dependency>

Commons logging采用的是自动探测加载logging工具。在复杂的环境中,可能因为classpath中的某个文件,导致Commons logging加载跟我们的预期不一致,我们可以启动诊断功能将其探测决断的过程输出到STDOUT或STDERR中

java -Dorg.apache.commons.logging.diagnostics.dest=STDERR ...

Commons logging的使用方法:

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class App {
    private static Log log = LogFactory.getLog(App.class);
    public static void main( String[] args ) {
        if (log.isInfoEnabled()) {
            log.info("app.info");
        }
        if (log.isDebugEnabled()) {
            log.debug("app.debug");
        }
    }
}

在初始化Abstract LogFactory时,系统需要探测真正LogFactory Implementation, 其的探测顺序:

  1. Java System property: org.apache.commons.logging.LogFactory

  2. Commons-logging.properties中的org.apache.commons.logging.LogFactory

  3. 使用类org.apache.commons.logging.impl.LogFactoryImpl

    LogFactoryImpl对依次试图初始化

  4. commons-logging.properties中指定的org.apache.commons.logging.Log

  5. Java system properties中指定的org.apache.commons.logging.Log

  6. Log4j

  7. JDK4Logger

  8. SimpleLog

对于上面的代码,如果没有任何配置,则使用的是JDK4Logger. 如果将log4j放入classpath中,根据上面探测优先顺序,将会优先使用log4j。

Log的level与log4j的level分类相同。

SLF4J

Simple logging facades for java也是一种通用的logging接口。它跟commons logging类似,区别在于commons logging是运行时运用classLoader动态探测实际logging,而SLF4J是开发时使用import class静态声明binding。这解决了在类似OSGI下,commons logging工作存在的问题。SLF4J的产生,就是为了改变commons logging的动态探测。

Maven引入SLF4J接口的方法

<dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.6</version>
</dependency>

slf4j-api.jar主要包含了接口,它并不包含logging的实现类,也不包含其它logger的装饰器。它必须和它的bindings一起工作。它的bindings里面包含了其它logger的装饰器。bindings有以下几个:

  1. slf4j-log4j12 包含了log4j的装饰器

    <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.6</version>
    </dependency>
    
  2. slf4j-jdk14 顾名思义

    <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-jdk14</artifactId>
            <version>1.7.6</version>
    </dependency>
    
  3. slf4j-nop  no operation的意思是忽略丢弃所有的logging message

  4. slf4j-simple 包含了simple logging的实现。

  5. slf4j-jcl 包含了commons logging的装饰类。

    <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-jcl</artifactId>
            <version>1.7.6</version>
    </dependency>
    

slf4j的调用方法:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class App {
    private static Logger logger = LoggerFactory.getLogger(App.class);
    public static void main( String[] args ) {
        if (logger.isInfoEnabled()) {
            logger.info("app.info");
        }
        if (logger.isDebugEnabled()) {
            logger.debug("app.debug");
        }
    }
}

而配置上非常简单。如果想使用log4j,只需要在pom.xml中引入slf4j-log4j12的binding,binding自动引入依赖模块log4j.还需要在classpath下面创建log4j.properties就可以使log4j工作。同样的,可以方法可以使用commons logging或jdk14logging.

建议使用SLF4J。对于旧的系统,无法改动为SLF的,可以使用SLF的桥接功能。SLF4J提供了几个桥接包,可以是对log4j,jdk14log,commons logging的调用,转发给SLF4J. 这几个桥接包就是jcl-over-slf4j.jar,log4j-over-slf4j.jar以及jul-over-slf4j.jar.

Tomcat 8 logging

顺带说下tomcat自身代码的logging,tomcat 8使用的是自己开发的org.apache.juli.logging. 它类似于commons-logging. 是在commons logging的帮助下开发的。 我猜之所以不直接用commons logging,可能是tomcat不想在自己的最小发布包中包含其它的依赖。

juli logging默认支持的是jdk14 logging. 如果想支持Log4J,则需要使用juli adapter。在adapter中包含了对外的适配器。

以上是tomcat8自身的logging系统。接下来,对于开发的web应用,可以使用javax.servlet.ServletContext.log(...)把log写到tomcat的log中。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Easter79 Easter79
3年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
10个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这