Java日志详解

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: Java日志详解

1.日志的概述

1.1 日志文件

(1)日志文件是用于记录系统操作事件的文件集合,可分为事件日志和消息日志。具有处理历史数据、诊断

问题的追踪以及理解系统的活动等重要作用。

(2)在计算机中,日志文件是记录在操作系统或其他软件运行中发生的事件或在通信软件的不同用户之间的

消息的文件。记录是保持日志的行为。在最简单的情况下,消息被写入单个日志文件。

(3)许多操作系统,软件框架和程序包括日志系统。广泛使用的日志记录标准是在因特网工程任务组

(IETF)RFC5424中定义的syslog。 syslog标准使专用的标准化子系统能够生成,过滤,记录和分析日

志消息

1.1.1 调试日志

软件开发中,我们经常需要去调试程序,做一些信息,状态的输出便于我们查询程序的运行状况。为了

让我们能够更加灵活和方便的控制这些调试的信息,所以我们需要专业的日志技术。java中寻找bug会

需要重现。调试也就是debug 可以在程序运行中暂停程序运行,可以查看程序在运行中的情况。日志主

要是为了更方便的去重现问题

1.1.2 系统日志

(1)系统日志是记录系统中硬件、软件和系统问题的信息,同时还可以监视系统中发生的事件。用户可以通

过它来检查错误发生的原因,或者寻找受到攻击时攻击者留下的痕迹。系统日志包括系统日志、应用程

序日志和安全日志。

(2)系统日志的价值

系统日志策略可以在故障刚刚发生时就向你发送警告信息,系统日志帮助你在最短的时间内发现问题。

系统日志是一种非常关键的组件,因为系统日志可以让你充分了解自己的环境。这种系统日志信息对于

决定故障的根本原因或者缩小系统攻击范围来说是非常关键的,因为系统日志可以让你了解故障或者袭

击发生之前的所有事件。为虚拟化环境制定一套良好的系统日志策略也是至关重要的,因为系统日志需

要和许多不同的外部组件进行关联。良好的系统日志可以防止你从错误的角度分析问题,避免浪费宝贵

的排错时间。另外一种原因是借助于系统日志,管理员很有可能会发现一些之前从未意识到的问题,在

几乎所有刚刚部署系统日志的环境当中。

1.2 JAVA日志框架

1.2.1 为什么要用日志框架

因为软件系统发展到今天已经很复杂了,特别是服务器端软件,涉及到的知识,内容,问题太多。在某

些方面使用别人成熟的框架,就相当于让别人帮你完成一些基础工作,你只需要集中精力完成系统的业

务逻辑设计。而且框架一般是成熟,稳健的,他可以处理系统很多细节问题,比如,事务处理,安全

性,数据流控制等问题。还有框架一般都经过很多人使用,所以结构很好,所以扩展性也很好,而且它

是不断升级的,你可以直接享受别人升级代码带来的好处。

1.2.2 日志框架和日志门面

(1)日志框架

①JUL java util logging:

Java原生日志框架,亲儿子

②Log4j:Apache的一个开源项目

③Logback:

由Log4j之父做的另一个开源项目

业界中称作log4j后浪

一个可靠、通用且灵活的java日志框架

④Log4j2:

Log4j官方的第二个版本,各个方面都是与Logback及其相似

具有插件式结构、配置文件优化等特征

Spring Boot1.4版本以后就不再支持log4j,所以第二个版本营运而生

(2)日志门面:JCL、SLF4j

(3)日志门面和日志框架的区别

每一种日志框架都有自己单独的API,要使用对应的框架就要使用对应的API,这就大大的增加了应用程序代码

对于日志框架的耦合性。我们使用了日志门面技术之后,对于应用程序来说,无论底层的日志框架如何改变,应用程序不需要修改任何代码,就可以直接上线了。

2.JUL

2.1 JUL简介

JUL全称 Java Util Logging,它是java原生的日志框架,使用时不需要另外引用第三方的类库,相对其他的框架使用方便,学习简单,主要是使用在小型应用中。

2.2 JUL组件介绍

(1)Logger:被称为记录器,应用程序通过获取Logger对象,调用用其API来发布日志信息。Logger通常被认为是访问日志系统的入口程序。

(2)Handler:处理器,每个Logger都会关联一个或者是一组Handler,Logger会将日志交给关联的Handler去做处理,由Handler负责将日志做记录。Handler具体实现了日志的输出位置,比如可以输出到控制台或者是文件中等等。

(3)Filter:过滤器,根据需要定制哪些信息会被记录,哪些信息会被略过。

(4)Formatter:格式化组件,它负责对日志中的数据和信息进行转换和格式化,所以它决定了我们输出日志最终的形式。

(5)Level:日志的输出级别,每条日志消息都有一个关联的级别。我们根据输出级别的设置,用来展现最终所呈现的日志信息。根据不同的需求,去设置不同的级别。

2.3 JUL的基本使用

2.3.1 日志输出的级别

(1)SEVERE:错误级别信息,程序出现了严重的错误,造成了程序的终止 — 最高级的日志级别

(2)WARNING: 警告信息,记录程序的一些问题,但是这些信息不会造成程序的终止

(3)INFO : (默认级别)消息记录,记录的比如说数据库的连接信息,IO的传递信息和网络的通讯信息等等

(4)CONFIG : 配置信息

(5)FINE : 详细信息(少)

(6)FINER : 详细信息(中)

(7)FINEST : 详细信息 (多) — 最低级的日志级别

(8)两个特殊的级别

①OFF:可用来关闭日志记录

②ALL:启用所有消息的日志记录

/*
 查看源码我们发现:
    (1)对于日志的级别,我们重点关注的是new对象的时候的第二个参数,这是一个数值
     OFF Integer.MAX_VALUE 整型最大值
    public static final Level INFO = new Level("INFO", 800, defaultBundle);
            SEVERE 1000
            WARNING 900
            ...
            ...
            FINEST 300
            ALL Integer.MIN_VALUE 整型最小值
     (2) 这个数值的意义在于,如果我们设置的日志的级别是INFO --> 800
            那么最终展现的日志信息,必须是数值大于800的所有的日志信息
            最终展现的就是
            SEVERE
            WARNING
            INFO
         */

(9)快速入门

①实例

Logger logger = Logger.getLogger("com.bjpowernode.jul.test.JULTest");
 logger.severe("severe信息");
 logger.warning("warning信息");
 logger.info("info信息");
 logger.config("config信息");
 logger.fine("fine信息");
 logger.finer("finer信息");
 logger.finest("finest信息");

②默认日志打印结果为比info级别高的:

2.3.2 日志的输出方式

(1)对于日志的输出,有两种方式

①第一种方式:直接调用日志级别相关的方法,方法中传递日志输出信息

logger.info("输出info信息1");

②第二种方式:调用通用的log方法,然后在里面通过Level类型来定义日志的级别参数,以及搭配日志输出信息的参数

logger.log(Level.INFO,"输出info信息2");

(2)对于输出消息中,字符串的拼接弊端很多

①麻烦

②程序效率低

③可读性不强

④维护成本高

String name = "zs";
 int age = 23;
 logger.log(Level.INFO,"学生的姓名为:"+name+";年龄为:"+age);

(4)我们应该使用动态生成数据的方式,生产日志

String name = "zs";
int age = 23;
logger.log(Level.INFO,"学生的姓名:{0},年龄:{1}",new Object[]{name,age});

2.3.3 自定义日志的级别

//日志记录器
Logger logger = Logger.getLogger("com.jul.test.JULTest");
//将默认的日志打印方式关闭掉
//参数设置为false,我们打印日志的方式就不会按照父logger默认的方式去进行操作
logger.setUseParentHandlers(false);
//处理器Handler
//在此我们使用的是控制台日志处理器,取得处理器对象
ConsoleHandler handler = new ConsoleHandler();
//创建日志格式化组件对象
SimpleFormatter formatter = new SimpleFormatter();
//在处理器中设置输出格式
handler.setFormatter(formatter);
//在记录器中添加处理器
logger.addHandler(handler);
//设置日志的打印级别
//此处必须将日志记录器和处理器的级别进行统一的设置,才会达到日志显示相应级别的效果
//logger.setLevel(Level.CONFIG);
//handler.setLevel(Level.CONFIG);
logger.setLevel(Level.ALL);
handler.setLevel(Level.ALL);
}

2.3.4 将日志输出到具体的磁盘文件中

(1)将日志输出到具体的磁盘文件中,这样做相当于是做了日志的持久化操作

Logger logger = Logger.getLogger("com.jul.test.JULTest");
        logger.setUseParentHandlers(false);
        //文件日志处理器
        FileHandler handler = new FileHandler("D:\\test\\myLogTest.log");
        SimpleFormatter formatter = new SimpleFormatter();
        handler.setFormatter(formatter);
        logger.addHandler(handler);
        //也可以同时在控制台和文件中进行打印
        ConsoleHandler handler2 = new ConsoleHandler();
        handler2.setFormatter(formatter);
        logger.addHandler(handler2); //可以在记录器中同时添加多个处理器
        logger.setLevel(Level.ALL);
        handler.setLevel(Level.ALL);
        handler2.setLevel(Level.CONFIG);
        logger.severe("severe信息");
        logger.warning("warning信息");
        logger.info("info信息");

(2) 总结:

用户使用Logger来进行日志的记录,Logger可以持有多个处理器Handler

(日志的记录使用的是Logger,日志的输出使用的是Handler)

添加了哪些handler对象,就相当于需要根据所添加的handler

将日志输出到指定的位置上,例如控制台、文件…

2.4 Logger之间的父子关系

(1)实例

//父亲是RootLogger,名称默认是一个空的字符串
  //RootLogger可以被称之为所有logger对象的顶层logger
   Logger logger1 = Logger.getLogger("com.bjpowernode.jul.test");
   Logger logger2 = Logger.getLogger("com.bjpowernode.jul.test.JULTest");
 //System.out.println(logger2.getParent()==logger1); //true
   System.out.println("logger1的父Logger引用为:"+logger1.getParent()+"; 
      名称为"+logger1.getName()+"; 父亲的名称为"+logger1.getParent().getName());
//logger1的父Logger引用为:java.util.logging.LogManager$RootLogger@573fd745; 名称为com.bjpowernode.jul.test; 父亲的名称为
  System.out.println("logger2的父Logger引用为:"+logger2.getParent()+"; 
            名称为"+logger2.getName()+"; 父亲的名称为"+logger2.getParent().getName());
  //logger2的父Logger引用为:java.util.logging.Logger@15327b79; 名称为com.bjpowernode.jul.test.JULTest; 父亲的名称为com.bjpowernode.jul.test
   

(2)JUL中Logger之间是存在"父子"关系的,值得注意的是,这种父子关系不是我们普遍认为的类之间的继承关系,

关系是通过树状结构存储的

(3) 父亲所做的设置,也能够同时作用于儿子。 对logger1做日志打印相关的设置,然后我们

使用logger2进行日志的打印也同样适用

(4)JUL在初始化时会创建一个顶层RootLogger作为所有Logger的父Logger

查看源码可以看到:

owner.rootLogger = owner.new RootLogger(); //RootLogger是LogManager的内部类
  //java.util.logging.LogManager$RootLogger

①以上的RootLogger对象作为树状结构的根节点存在的

②将来自定义的父子关系通过路径来进行关联

③父子关系,同时也是节点之间的挂载关系

owner.addLogger(owner.rootLogger);

LoggerContext cx = getUserContext(); //LoggerContext一种用来保存节点的Map关系

private LogNode node; //节点

2.5 配置文件

(1)以上所有的配置相关的操作,都是以java硬编码的形式进行的。我们可以使用更加清晰,

更加专业的一种做法,就是使用配置文件

(2)如果我们没有自己添加配置文件,则会使用系统默认的配置文件

(3)这个默认配置文件的位置:

java.home --> 找到jre文件夹 --> lib --> logging.properties

# RootLogger的日志级别
# 默认情况下,这是全局的日志级别,如果不手动配置其他的日志级别
# 则默认输出下述配置的级别以及更高的级别
.level= INFO
# 文件处理器属性设置
# 输出日志文件的路径
java.util.logging.FileHandler.pattern = %h/java%u.log
# 输出日志文件的限制(50000字节)
java.util.logging.FileHandler.limit = 50000
# 设置日志文件的数量
java.util.logging.FileHandler.count = 1
# 输出日志的格式
# 默认是以XML的方式进行的输出
java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter
# 控制台处理器属性设置
# 控制台输出默认的级别
java.util.logging.ConsoleHandler.level = INFO
# 控制台默认输出的格式
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
# 也可以将日志级别设定到具体的某个包下
com.xyz.foo.level = SEVERE

(4)默认的日志文件是不追加的,新日志会覆盖掉原来的日志,但是我们现在的需求不是覆盖,而是追加

java.util.logging.FileHandler.append=true

2.5.1 使用自定义配置文件

InputStream input = new FileInputStream("D:\\test\\logging.properties");
//取得日志管理器对象
 LogManager logManager = LogManager.getLogManager();
//读取自定义的配置文件
logManager.readConfiguration(input);
Logger logger = Logger.getLogger("com.bjpowernode.jul.test.JULTest");
logger.severe("severe信息");
logger.warning("warning信息");
logger.info("info信息");

2.5.2 JUL日志框架使用方式总结(原理解析)

(1)初始化LogManager

①LogManager加载logging.properties配置文件

②添加Logger到LogManager

(2)从单例的LogManager获取Logger

(3)设置日志级别Level,在打印的过程中使用到了日志记录的LogRecord类

(4)Filter作为过滤器提供了日志级别之外更细粒度的控制

(5)Handler日志处理器,决定日志的输出位置,例如控制台、文件…

(6)Formatter是用来格式化输出的

3.Log4j

3.1 Log4j简介

(1)Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等;我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。

(2)官方网站: http://logging.apache.org/log4j/1.2/ Log for java

(3)我们使用log4j技术,主要使用的是其配置文件

3.2 Log4j组件介绍

Log4j主要由 Loggers (日志记录器)、Appenders(输出控制器)和 Layout(日志格式化器)组成。其中 Loggers 控制日志的输出以及输出级别(JUL做日志级别使用的是Level);Appenders 指定日志的输出方式(输出到控制台、文件等);Layout 控制日志信息的输出格式。

(1)Loggers

①日志记录器,负责收集处理日志记录,实例的命名就是类的全限定名,如com.bjpowernode.log4j.XX, Logger的名字大小写敏感,其命名有继承机制:例如:name为com.bjpowernode.log4j的logger会继承 name为com.bjpowernode。

②Log4J中有一个特殊的logger叫做“root”,他是所有logger的根,也就意味着其他所有的logger都会直接 或者间接地继承自root。root logger可以用Logger.getRootLogger()方法获取。自log4j 1.2版以来, Logger 类已经取代了 Category 类。对于熟悉早期版本的log4j的人来说, Logger 类可以被视为 Category 类的别名。

com.bjpowernode.log4j.XX 儿子

com.bjpowernode.log4j 父亲

com.bjpowernode 爷爷

上辈所做的日志属性设置,会直接的影响到子辈

③关于日志级别信息,例如DEBUG、INFO、WARN、ERROR…级别是分大小的,DEBUG < INFO < WARN < ERROR,分别用来指定这条日志信息的重要程度,Log4j输出日志的规则是:只输出级别不低于设定级别的日志信息,假设Loggers级别设定为INFO,则INFO、WARN、ERROR级别的日志信息都会输出,而级别比INFO低的DEBUG则不会输出。

(2)Appenders

记录日志以及定义日志的级别仅仅是Log4j的基本功能,Log4j日志系统还提供许多强大的功能,比如允许把日志输出到不同的地方,如控制台(Console)、文件(Files)等,可以根据天数或者文件大小产生新的文件,可以以流的形式发送到其它地方等等。

常用Appenders:

①ConsoleAppender:将日志输出到控制台

②FileAppender:将日志输出到文件中

③DailyRollingFileAppender:将日志输出到一个日志文件,并且每天输出到一个新的文件

④RollingFileAppender:将日志信息输出到一个日志文件,并且指定文件的尺寸,当文件大小达到指定尺寸时,会自动把文件改名,同时产生一个新的文件

⑤JDBCAppender:把日志信息保存到数据库中

(3)Layouts

有时用户希望根据自己的喜好格式化自己的日志输出,Log4j可以在Appenders的后面附加Layouts来完成这个功能。Layouts提供四种日志输出样式,如根据HTML样式、自由指定样式、包含日志级别与信息的样式和包含日志时间、线程、类别等信息的样式。

常用Layouts:

①HTMLLayout

格式化日志输出为HTML表格形式

②SimpleLayout

简单的日志输出格式化,打印的日志格式如默认INFO级别的消息

③PatternLayout

最强大的格式化组件,可以根据自定义格式输出日志,如果没有指定转换格式, 就是用默认的转换格式

3.3 日志输出格式说明

使用PatternLayout可以自定义格式输出,是我们最常用的方式

这种格式化输出采用类似于 C 语言的 printf 函数的打印格式格式化日志信息,具体的占位符及其含义如下:

(1)%m 输出代码中指定的日志信息

(2)%p 输出优先级,及 DEBUG、INFO 等

(3)%n 换行符(Windows平台的换行符为 “\n”,Unix 平台为 “\n”)

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

(5)%c 输出打印语句所属的类的全名

(6)%t 输出产生该日志的线程全名

(7)%d 输出服务器当前时间,默认为 ISO8601,也可以指定格式,如:%d{yyyy年MM月dd日 HH:mm:ss}

(8)%l 输出日志时间发生的位置,包括类名、线程、及在代码中的行数。如:Test.main(Test.java:10)

(9)%F 输出日志消息产生时所在的文件名称

(10)%L 输出代码中的行号

(11)%% 输出一个 “%” 字符

可以在 % 与字符之间加上修饰符来控制最小宽度、最大宽度和文本的对其方式。如:

%5c 输出category名称,最小宽度是5,category<5,默认的情况下右对齐

%-5c 输出category名称,最小宽度是5,category<5,"-"号指定左对齐,会有空格

%.5c 输出category名称,最大宽度是5,category>5,就会将左边多出的字符截掉,<5不

会有空格

%20.30c category名称<20补空格,并且右对齐,>30字符,就从左边交远销出的字符截掉

3.4 Log4j的基本使用

(1)添加依赖

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

(2)快速入门

public class Log4jTest01 {
    @Test
    public void test01(){
     //加载初始化配置
        BasicConfigurator.configure();
        Logger logger = Logger.getLogger(Log4jTest01.class);
        logger.info("info信息");
} }

3.5 Log4j的日志级别说明

Log4j提供了8个级别的日志输出,分别为

(1)ALL 最低等级 用于打开所有级别的日志记录

(2)TRACE 程序推进下的追踪信息,这个追踪信息的日志级别非常低,一般情况下是不会使用的

(3)DEBUG 指出细粒度信息事件对调试应用程序是非常有帮助的,主要是配合开发,在开发过程中打印一些重要的运行信息

(4)INFO 消息的粗粒度级别运行信息

(5)WARN 表示警告,程序在运行过程中会出现的有可能会发生的隐形的错误

注意,有些信息不是错误,但是这个级别的输出目的就是为了给程序员以提示

(6)ERROR 系统的错误信息,发生的错误不影响系统的运行

一般情况下,如果不想输出太多的日志,则使用该级别即可

(7)FATAL 表示严重错误,它是那种一旦发生系统就不可能继续运行的严重错误

如果这种级别的错误出现了,表示程序可以停止运行了

(8)OFF 最高等级的级别,用户关闭所有的日志记录

其中debug是我们在没有进行设置的情况下,默认的日志输出级别

3.6 配置文件的使用

(1)观察源码BasicConfigurator.configure(); 可以得到两条信息:

public static void configure() {
        Logger root = Logger.getRootLogger();
        root.addAppender(new ConsoleAppender(new PatternLayout("%r [%t] %p %c %x - %m%n")));
    }

①创建了根节点的对象,Logger root = Logger.getRootLogger();

②根节点添加了ConsoleAppender对象(表示默认打印到控制台,自定义的格式化输出)

(2)不使用BasicConfigurator.configure();使用自定义的配置文件来实现功能。

我们的配置文件需要提供Logger、Appender、Layout这3个组件信息(通过配置来代替以上的代码)

(3) 分析:Logger logger = Logger.getLogger(Log4jTest01.class);

①进入到getLogger方法,会看到代码:

public static Logger getLogger(Class clazz) {
        return LogManager.getLogger(clazz.getName());
    }

②LogManager:日志管理器

③点击LogManager,看看这个日志管理器中都实现了什么

看到很多常量信息,他们代表的就是不同形式(后缀名不同)的配置文件

我们最常使用到的肯定是log4j.properties属性文件(语法简单,使用方便)

(4)log4j.properties的加载时机

①继续观察LogManager,找到其中的静态代码块static

在static代码块中,我们找到 Loader.getResource(“log4j.properties”);

②这行代码给我们最大的一个提示信息就是

系统默认要从当前的类路径下找到log4j.properties

③对于我们当前的项目是maven工程,那么理应在resources路径下去找

(5)加载完毕后我们来观察配置文件是如何读取的?

①继续观察LogManager 找到

OptionConverter.selectAndConfigure(url, configuratorClassName, getLoggerRepository());

作为属性文件的加载,执行相应的properties配置对象:configurator = new PropertyConfigurator();

②进入到PropertyConfigurator类中,观察到里面的常量信息

这些常量信息就是我们在properties属性文件中的各种属性配置项

③其中,我们看到了如下两项信息,这两项信息是必须要进行配置的

static final String ROOT_LOGGER_PREFIX = "log4j.rootLogger";
 static final String APPENDER_PREFIX = "log4j.appender.";

(6) 通过代码:String prefix = "log4j.appender." + appenderName;得知:

我们需要自定义一个appenderName,假设我们起名叫做console

(起名字也需要见名知意,那么console我们到时候的配置应该就是控制台输出)

log4j.appender.console取值就是log4j中为我们提供的appender类

例如:log4j.appender.console=org.apache.log4j.ConsoleAppender

(7)在appender输出的过程中,还可以同时指定输出的格式

① 通过代码: String layoutPrefix = prefix + “.layout”;

②可以得知配置:log4j.appender.console.layout=org.apache.log4j.SimpleLayout

(8)通过log4j.rootLogger继续在类中搜索

找到void configureRootCategory方法

在这个方法中执行了this.parseCategory方法

①观察该方法:找到代码StringTokenizer st = new StringTokenizer(value, “,”);

表示要以逗号的方式来切割字符串,证明了log4j.rootLogger的取值,其中可以有多个值,使用逗号进行分隔

② 通过代码:String levelStr = st.nextToken();

表示切割后的第一个值是日志的级别

③通过代码:while(st.hasMoreTokens())

表示接下来的值,是可以通过while循环遍历得到的

第2~第n个值,就是我们配置的其他的信息,这个信息就是appenderName

④证明了我们配置的方式:log4j.rootLogger=日志级别,appenderName1,appenderName2,appenderName3…

表示可以同时在根节点上配置多个日志输出的途径,

通过我们自己的配置文件,就可以将原有的加载代码去除掉了

(9)通过Logger中的开关,打开日志输出的详细信息,查看LogManager类中的方法

getLoggerRepository()

找到代码LogLog.debug(msg, ex);

LogLog会使用debug级别的输出为我们展现日志输出详细信息

Logger是记录系统的日志,那么LogLog是用来记录Logger的日志

①进入到LogLog.debug(msg, ex);方法中

通过代码:if (debugEnabled && !quietMode) {

观察到if判断中的这两个开关都必须开启才行

②!quietMode是已经启动的状态,不需要我们去管

debugEnabled默认是关闭的

所以我们只需要设置debugEnabled为true就可以了

LogLog.setInternalDebugging(true);

(10)关于log4j.properties layout属性的配置,其中PatternLayout是日常使用最多的方式

①查看其源码,setConversionPattern这个方法就是该PatternLayout的核心方法

②在log4j.properties中一般将layout设置为PatternLayout,我们主要配置的是conversionPattern属性

3.7 将日志信息输出到文件中

(1)之前我们在配置文件中配置的是输出到控制台相关的配置

那么我们可以直接将输出到控制台改变为输出到文件中

一般情况下我们也可以做多方向的输出,所以控制台的配置我们保留,但是可以选择不用

我们可以完全再来一套关于输出到文件的配置

(2)日志文件要保存到哪个磁盘相关的配置,查看FileAppender的源码看到属性信息

protected boolean fileAppend; 表示是否追加信息,通过构造方法赋值为true

protected int bufferSize; 缓冲区的大小,通过构造方法赋值为8192

(3)继续观察,找到setFile方法,这个方法就是用来指定文件位置的方法

通过ognl,可以推断setFile方法操作的属性就是file

(4)如果有输出中文的需求怎么办,观察FileAppender的父类找到protected String encoding;属性

#配置根节点logger
log4j.rootLogger=trace,file
#配置appender输出方式 输出到文件
log4j.appender.file=org.apache.log4j.FileAppender
#配置输出到文件中的格式
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.conversionPattern=[%-10p]%r %c%t%d{yyyy-MM-dd HH:mm:ss:SSS} %m%n
#第一个file是我们自己命名的appenderName,第二个file是用来指定文件位置的属性
log4j.appender.file.file=D://test//log4j.log
#配置输出字符编码
log4j.appender.file.encoding=UTF-8

(8)日志太多了,不方便管理和维护怎么办

FileAppender为我们提供了好用的子类来进一步的对于文件输出进行处理

①RollingFileAppender② DailyRollingFileAppender

3.7.1 RollingFileAppender

(1) 这个类表示使用按照文件大小进行拆分的方式进行操作

(2)如何进行拆分,观察RollingFileAppender的源码

protected long maxFileSize = 10485760L; 表示拆分文件的大小

protected int maxBackupIndex = 1; 表示拆分文件的数量

(3)配置

#配置根节点logger
log4j.rootLogger=trace,rollingFile
#RollingFileAppender的配置,我们可以针对于实际含义起名
log4j.appender.rollingFile=org.apache.log4j.RollingFileAppender
log4j.appender.rollingFile.layout=org.apache.log4j.PatternLayout
log4j.appender.rollingFile.layout.conversionPattern=[%-10p]%r %c%t%d{yyyy-MM-dd HH:mm:ss:SSS} %m%n
log4j.appender.rollingFile.file=D://test//log4j.log
log4j.appender.rollingFile.encoding=UTF-8
#指定日志文件内容大小
log4j.appender.rollingFile.maxFileSize=1MB
#指定日志文件的数量
log4j.appender.rollingFile.maxBackupIndex=5

(4)只要文件超过1MB,那么则生成另外一个文件,文件的数量最多是5个

文件1 记录日志 1MB

文件2 记录日志 1MB

文件5 1MB

如果5个文件不够怎么办,作为日志管理来讲,也不可能让日志无休止的继续增长下去

所以,覆盖文件的策略是,按照时间来进行覆盖,原则就是保留新的,覆盖旧的

3.7.2 DailyRollingFileAppender

(1)按照时间来进行文件的拆分

(2)查看源码属性:

private String datePattern = “'.'yyyy-MM-dd”; 默认是按照天来进行拆分的

3.7.3 将日志持久化到数据库表中

(1)创建表结构:(字段的制定可以根据需求进行调整)

CREATE TABLE tbl_log(
                id int(11) NOT NULL AUTO_INCREMENT,
                name varchar(255) DEFAULT NULL COMMENT '项目名称',
                createTime varchar(255) DEFAULT NULL COMMENT '创建时间',
                level varchar(255) DEFAULT NULL COMMENT '日志级别',
                category varchar(255) DEFAULT NULL COMMENT '所在类的全路径',
                fileName varchar(255) DEFAULT NULL COMMENT '文件名称',
                message varchar(255) DEFAULT NULL COMMENT '日志消息',
                PRIMARY KEY(id)
            )

(2)配置appender输出方式 输出到数据库表

#配置根节点logger
log4j.rootLogger=trace,logDB
#配置appender输出方式 输出到数据库表
log4j.appender.logDB=org.apache.log4j.jdbc.JDBCAppender
log4j.appender.logDB.layout=org.apache.log4j.PatternLayout
log4j.appender.logDB.Driver=com.mysql.jdbc.Driver
log4j.appender.logDB.URL=jdbc:mysql://localhost:3306/test
log4j.appender.logDB.User=root
log4j.appender.logDB.Password=123456
log4j.appender.logDB.Sql=INSERT INTO tbl_log(name,createTime,level,category,fileName,message) values('project_log','%d{yyyy-MM-dd HH:mm:ss}','%p','%c','%F','%m')

3.8 自定义Logger

(1)我们以前所创建出来的Logger对象,默认都是继承rootLogger的

我们也可以自定义logger,让其他logger来继承这个logger

(2)这种继承关系就是按照包结构的关系来进行指定的

例如我们一直使用的Logger logger = Logger.getLogger(Log4jTest01.class);

路径就是:com.bjpowernode.log4j.test.Log4jTest01

它的父logger就是上层的路径或者是更上层的路径

例如:

com.bjpowernode.log4j.test

com.bjpowernode.log4j

com

(3)参照logger是如何加载配置文件的,查看PropertyConfigurator的源码

①得到信息log4j.logger.

②这个属性值log4j.logger.就是我们在配置文件中对于自定义logger的配置属性

(4) 假设我们现在的配置是这样的:

#配置根节点logger
  log4j.rootLogger=trace,console
  #配置自定义logger
  log4j.logger.com.bjpowernode.log4j.test=info,file

观察结果:

从输出位置来看,控制台输出了信息,日志文件也输出了信息,所以可以得出结论

①如果根节点的logger和自定义父logger配置的输出位置是不同的,则取二者的并集,配置

的位置都会进行输出操作

②如果二者配置的日志级别不同,以按照我们自定的父logger的级别输出为主

4.JCL

4.1 JCL简介

(1)JCL全称为Jakarta Commons Logging,是Apache提供的一个通用日志API。

用户可以自由选择第三方的日志组件作为具体实现,像log4j,或者jdk自带的jul, common-logging会通过动态查找的机制,在程序运行时自动找出真正使用的日志库。

(2)当然,common-logging内部有一个Simple logger的简单实现,但是功能很弱。所以使用common-logging,通常都是配合着log4j以及其他日志框架来使用。使用它的好处就是,代码依赖是common-logging而非log4j的API, 避免了和具体的日志API直接耦合,在有必要时,可以更改日志实现的第三方库。

(3)JCL 有两个基本的抽象类:

①Log:日志记录器

②LogFactory:日志工厂(负责创建Log实例)

(4)添加依赖

<dependency>
    <groupId>commons-logging</groupId>
    <artifactId>commons-logging</artifactId>
    <version>1.2</version>
</dependency>
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
</dependency>
<!--设置jdk版本-->
<build>
        <plugins>
            <!-- 设置编译版本为1.8 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
        </plugins>
</build>

(5)我们暂时没有导入第三方的日志框架,例如log4j;默认的情况下,会使用JUL日志框架

做日志的记录操作

(6)JCL使用原则:

①如果有log4j,优先使用log4j

②如果没有任何第三方日志框架的时候,我们使用的就是JUL

4.2 JCL组件结构

(1)查看Jdk14Logger证明里面使用的是JUL日志框架

(2)查看Log4JLogger证明里面使用的是Log4j日志框架

(3)观察LogFactory,看看如何加载的Logger对象

这是一个抽象类,无法实例化,需要观察其实现类LogFactoryImpl

(4)观察LogFactoryImpl,真正加载日志实现使用的就是这个实现类LogFactoryImpl

(5)进入getLog

① 进入getInstance,找到instance = this.newInstance(name);,继续进入

找到instance = this.discoverLogImplementation(name); 表示发现一个日志的实现

②for(int i = 0; i < classesToDiscover.length && result == null; ++i) {

result = this.createLogFromClass(classesToDiscover[i], logCategory, true);

}

遍历我们拥有的日志实现框架

遍历的是一个数组,这个数组是按照

log4j

jdk14

jdk13

SimpleLogger

的顺序依次遍历

表示的是,第一个要遍历的就是log4j,如果有log4j则执行该日志框架

如果没有,则遍历出来第二个,使用jdk14的JUL日志框架

以此类推

③result = this.createLogFromClass(classesToDiscover[i], logCategory, true);

表示帮我们创建Logger对象

在这个方法中,我们看到了

c = Class.forName(logAdapterClassName, true, currentCL);

是取得该类型的反射类型对象

使用反射的形式帮我们创建logger对象

constructor = c.getConstructor(this.logConstructorSignature);

5.SLF4J

5.1 SLF4J简介

(1)简单日志门面(Simple Logging Facade For Java) SLF4J主要是为了给Java日志访问提供一套标准、规范的API框架,其主要意义在于提供接口,具体的实现可以交由其他日志框架,例如log4j和logback等。

(2)当然slf4j自己也提供了功能较为简单的实现,但是一般很少用到。对于一般的Java项目而言,日志框架会选择slf4j-api作为门面,配上具体的实现框架(log4j、logback等),中间使用桥接器完成桥接。所以我们可以得出SLF4J最重要的两个功能就是对于日志框架的绑定以及日志框架的桥接。

(3)官方网站: https://www.slf4j.org/

5.2 SLF4J桥接技术

通常,我们依赖的某些组件依赖于SLF4J以外的日志API。我们可能还假设这些组件在不久的将来不会切换到SLF4J。为了处理这种情况,SLF4J附带了几个桥接模块,这些模块会将对log4j,JCL和java.util.logging API的调用重定向为行为,就好像是对SLF4J API进行的操作一样。

5.3 SLF4J案例实现

(1)创建Maven工程,导入依赖

<!--slf4j 核心依赖-->
<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-api</artifactId>
  <version>1.7.25</version>
</dependency>
<!--slf4j 自带的简单日志实现 -->
<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-simple</artifactId>
  <version>1.7.25</version>
</dependency>

(2)SLF4J对日志的级别划分

①trace:日志追踪信息

②debug:日志详细信息

③info:日志的关键信息 默认打印级别

④warn:日志警告信息

⑤error:日志错误信息

⑥在没有任何其他日志实现框架集成的基础之上,slf4j使用的就是自带的框架slf4j-simple,

slf4j-simple也必须以单独依赖的形式导入进来

(3)我们输出动态的信息时,也可以使用占位符的形式来代替字符串的拼接

①我们有些时候输出的日志信息,需要我们搭配动态的数据,有可能是信息,有可能是数据库表中

的数据,总之我们这样做最大的好处就是能够让日志打印变得更加灵活

②如果是通过拼接字符串的形式,不仅麻烦,而且更重要的是可读性查,我们的日志打印是支持

以替代符的形式做日志信息拼接的,一般情况下,几乎所有的日志实现产品,都会提供这种基础功能

Logger logger = LoggerFactory.getLogger(SLF4JTest01.class);
String name = "zs";
int age = 23;
//logger.info("学生信息-姓名:"+name+";年龄:"+age);//方式一
//logger.info("学生信息-姓名:{},年龄:{}",new Object[]{name,age});//方式二
logger.info("学生信息-姓名:{},年龄:{}",name,age);//最合适的方式

(4)日志对于异常信息的处理

① 一般情况下,我们在开发中的异常信息,都是记录在控制台上(我们开发环境的一种日志打印方式)

我们会根据异常信息提取出有用的线索,来调试bug

②但是在真实生产环境中(项目上线),对于服务器或者是系统相关的问题

在控制台上其实也会提供相应的异常或者错误信息的输出

③但是这种错误输出方式(输出的时间,位置,格式…)都是服务器系统默认的,

我们可以通过日志技术,选择将异常以日志打印的方式,进行输出查看,

输出的时间、位置(控制台,文件)、格式完全由我们自己去进行定义

Logger logger = LoggerFactory.getLogger(SLF4JTest01.class);
 try {
       Class.forName("aaa");
 } catch (ClassNotFoundException e) {
       //打印栈追踪信息
       //e.printStackTrace();
       logger.info("XXX类中的XXX方法出现了异常,请及时关注信息");
       //e是引用类型对象,不能根前面的{}做有效的字符串拼接
       //logger.info("具体错误是:{}",e);
       //我们不用加{},直接后面加上异常对象e即可
       logger.info("具体错误是:",e);
 }

5.4 SLF4J日志门面共有3种情况对日志实现进行绑定

(1)在没有绑定任何日志实现的基础之上,日志是不能够绑定实现任何功能的,

slf4j-simple是slf4j官方提供的,使用的时候,也是需要导入依赖,自动绑定到

slf4j门面上;如果不导入,slf4j 核心依赖是不提供任何实现的

(2)logback和simple(包括nop)

都是slf4j门面时间线后面提供的日志实现,所以API完全遵循slf4j进行的设计

那么我们只需要导入想要使用的日志实现依赖,即可与slf4j无缝衔接;

值得一提的是nop虽然也划分到实现中了,但是他是指不实现日志记录

(3)log4j和JUL

都是slf4j门面时间线前面的日志实现,所以API不遵循slf4j进行设计;

通过适配桥接的技术,完成与日志门面的衔接

(4)如果有多个日志实现的话,默认使用先导入的实现。在实际应用的时候,我们

一般情况下,仅仅只是做一种日志实现的集成就可以了

(5)使用slf4j-nop,表示不记录日志

①在我们使用slf4j-nop的时候,首先还是需要导入实现依赖;

这个实现依赖,根据我们之前所总结出来的日志日志实现种类的第二种

与logback和simple是属于一类的,通过集成依赖的顺序而定

②所以如果想要让nop发挥效果,禁止所有日志的打印,

那么就必须要将slf4j-nop的依赖放在所有日志实现依赖的上方

5.4.1 绑定log4j和JUL

(1)由于log4j是在slf4j之前出品的日志框架实现,所以并没有遵循slf4j的API规范

(2)如果想要使用,需要绑定一个适配器,叫做slf4j-log4j12,再导入log4j的实现

<!-- 导入log4j适配器依赖 -->
   <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>1.7.25</version>
   </dependency>
   <!-- 导入log4j依赖 -->
   <dependency>
         <groupId>log4j</groupId>
         <artifactId>log4j</artifactId>
         <version>1.2.17</version>
   </dependency>

(3)测试显示:

log4j:WARN No appenders could be found for logger (com.bjpowernode.slf4j.test01.SLF4JTest01).

log4j:WARN Please initialize the log4j system properly.

log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.

虽然日志信息没有打印出来,那么根据警告信息可以得出:

①使用了log4j日志实现框架,提示appender没有加载,需要在执行日志之前做相应的加载工作(初始化)

② 我们可以将log4j的配置文件导入使用,测试结果为log4j的日志打印,而且格式和级别完全是遵循log4j的配置文件进行的输出

(4)JUL也是slf4j之前出品的日志实现框架,所以也需要相应的适配器

适配器导入之后,JUL日志实现是不用导入依赖的

因为JUL,是JDK内置的

从测试结果来看,是JUL的日志打印,默认是info级别日志的输出

5.4.2 绑定多个日志实现,会出现警告信息

(1)通过源码来看看其原理(看看slf4j的执行原理)

①进入到getLogger

看到Logger logger = getLogger(clazz.getName());

②进入重载的getLogger

ILoggerFactory iLoggerFactory = getILoggerFactory(); 用来取得Logger工厂实现的方法

③进入getILoggerFactory() 看到以双重检查锁的方式去做判断

④执行performInitialization(); 工厂的初始化方法

进入performInitialization()

bind()就是用来绑定具体日志实现的方法

⑤进入bind()

看到Set集合Set<URL> staticLoggerBinderPathSet = null;

因为当前有可能会有N多个日志框架的实现

看到staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();

⑥进入findPossibleStaticLoggerBinderPathSet()

看到创建了一个有序不可重复的集合对象

LinkedHashSet staticLoggerBinderPathSet = new LinkedHashSet();

声明了枚举类的路径,经过if else判断,以获取系统中都有哪些日志实现

看到Enumeration paths;

if (loggerFactoryClassLoader == null) {
          paths = ClassLoader.getSystemResources(STATIC_LOGGER_BINDER_PATH);
   } else {
          paths = loggerFactoryClassLoader.getResources(STATIC_LOGGER_BINDER_PATH);
      }

⑦我们主要观察常量STATIC_LOGGER_BINDER_PATH

通过常量我们会找到类StaticLoggerBinder

这个类是以静态的方式绑定Logger实现的类

来自slf4j-JDK14的适配器

进入StaticLoggerBinder

看到new JDK14LoggerFactory();

进入JDK14LoggerFactory类的无参构造方法

看到java.util.logging.Logger.getLogger(“”);

使用的就是jul的Logger

接着观察findPossibleStaticLoggerBinderPathSet

看到以下代码,表示如果还有其他的日志实现

while(paths.hasMoreElements()) {

URL path = (URL)paths.nextElement();

将路径添加进入

staticLoggerBinderPathSet.add(path);

}

回到bind方法

表示对于绑定多实现的处理

reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);

如果出现多日志实现的情况

则会打印

Util.report(“Class path contains multiple SLF4J bindings.”);

(2)总结:

①在真实生产环境中,slf4j只绑定一个日志实现框架就可以了

②绑定多个,默认使用导入依赖的第一个,而且会产生没有必要的警告信息

5.5 桥接器的使用

(1)假设我们项目一直以来使用的是log4j日志框架,但是随着技术和需求的更新换代

log4j已然不能够满足我们系统的需求,我们现在就需要将系统中的日志实现重

构为 slf4j+logback的组合,在不触碰java源代码的情况下,将这个问题给解决掉

(2) 桥接器的使用步骤:

①去除之前旧的日志框架依赖

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

②添加slf4j提供的桥接组件,log4j相关的桥接器

<dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>log4j-over-slf4j</artifactId>
        <version>1.7.25</version>
</dependency>

桥接器加入后,代码编译就不报错了

(3)测试:

日志信息输出,输出格式为logback,证明了现在使用的确实是slf4j门面+logback实现

在重构之后,就会为我们造成这样一种假象

使用的明明是log4j包下的日志组件资源

但是真正日志的实现,却是使用slf4j门面+logback实现

这就是桥接器给我们带来的效果

(4)注意:

在桥接器加入之后,适配器就没有必要加入了

桥接器和适配器不能同时导入依赖

桥接器如果配置在适配器的上方,则运行报错,不同同时出现

桥接器如果配置在适配器的下方,则不会执行桥接器,没有任何的意义

6.Logback

6.1 Logback简介

(1)Logback是由log4j创始人设计的又一个开源日志组件。

(2)Logback当前分成三个模块:logback-core,logback- classic和logback-access。

①logback-core是其它两个模块的基础模块。

②logback-classic是log4j的一个改良版本。此外logback-classic完整实现SLF4J API。使你可以很方便地更换成其它日志系统如log4j或JDK14 Logging。

③logback-access访问模块与Servlet容器集成提供通过Http来访问日志的功能。

6.2 Logback中的组件

(1)Logger: 日志的记录器,主要用于存放日志对象,也可以定义日志类型、级别。

(2)Appender:用于指定日志输出的目的地,目的地可以是控制台、文件、数据库等等。

(3)Layout: 负责把事件转换成字符串,格式化的日志信息的输出。在Logback中

Layout对象被封装在encoder中。也就是说我们未来使用的encoder其实就是Layout

6.3 Logback配置文件

Logback提供了3种配置文件

(1)logback.groovy

(2)logback-test.xml

(3)logback.xml

如果都不存在则采用默认的配置

6.3.1 配置文件的通用属性

配置需要先加上根标签<configuration></configuration>,一切配置都是在根标签中进行操作的

(1)所谓配置文件中的通用属性是为了让接下来的配置更加方便引用

<property name="" value=""></property>
• 1

(2)通过以${name}的形式,方便的取得value值;通过取得的value值可以做文件的其他配置而使用

(3)以property的形式将日志输出格式配置成为文件的通用的属性,那么下面我们配置的输出方式中,就可以重复的引用该配置

6.3.2 日志输出格式

(1)%-10level:级别,该案例为设置10个字符,左对齐

(2)%d{yyyy-MM-dd HH:mm:ss.SSS}: 日期

(3)%c:当前类全限定名

(4)%M:当前执行日志的方法

(5)%L:行号

(6)%thread: 线程名称

(7)%m或者%msg: 信息

(8)%n: 换行

6.3.4 Logback配置文件的基本使用

在resources下创建logback.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <property name="pattern"
       value="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %c %M %L %thread %m%n">                            
    </property>
    <!-- 配置控制台appender -->
    <appender name="consoleAppender" class="ch.qos.logback.core.ConsoleAppender">
        <!--
            表示对于日志输出目标的配置
            默认:System.out 表示以黑色字体输出日志
            设置:System.err 表示以红色字体输出日志
        -->
        <target>
            System.err
        </target>
        <!--配置日志输出格式-->
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!-- 格式引用通用属性配置 -->
            <pattern>${pattern}</pattern>
        </encoder>
    </appender>
    <!-- 日志记录器,配置root logger -->
  <root level="ALL">
        <!-- 引入appender -->
        <appender-ref ref="consoleAppender"/>
    </root>
</configuration>
6.3.4.1 配置文件的appender 普通文件
<!-- 配置文件的输出路径 -->
    <property name="logDir" value="D://test"></property>
    <!-- 配置文件的appender 普通文件-->
    <appender name="fileAppender" class="ch.qos.logback.core.FileAppender">
        <!-- 引入文件位置 -->
        <file>${logDir}/logback.log</file>
        <!-- 设置输出格式 -->
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${pattern}</pattern>
        </encoder>
    </appender>

可以同时配置多个appender,做日志的多方向输出

<root level="ALL">
     <!-- 引入appender -->
     <appender-ref ref="fileAppender"/>
     <appender-ref ref="consoleAppender"/>
</root>
6.3.4.2 配置文件的appender html文件
<property name="pattern1" value="[%-5level]%d{yyyy-MM-dd HH:mm:ss.SSS}%c%M%L%thread%m%n"></property>
<appender name="htmlFileAppender" class="ch.qos.logback.core.FileAppender">
        <file>${logDir}/logback.html</file>
        <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
            <layout class="ch.qos.logback.classic.html.HTMLLayout">
                <pattern>${pattern1}</pattern>
            </layout>
        </encoder>
</appender>
6.3.4.3 配置文件的appender 可拆分归档的文件
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <property name="pattern" value="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %c %M %L %thread %m%n">
    </property>
    <appender name="roll" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 输出格式 -->
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${pattern}</pattern>
        </encoder>
        <!-- 引入文件位置 -->
        <file>${logDir}/roll_logback.log</file>
        <!-- 指定拆分规则 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <!-- 按照时间和压缩格式声明文件名 压缩格式gz -->
            <fileNamePattern>${logDir}/roll.%d{yyyy-MM-dd}.log%i.gz</fileNamePattern>
            <!-- 按照文件大小来进行拆分 -->
            <maxFileSize>1KB</maxFileSize>
        </rollingPolicy>
    </appender>
    <root level="ALL">
        <appender-ref ref="roll"/>
    </root>
</configuration>
  

(1)查看源码

①RollingFileAppender类中找到rollingPolicy属性

②SizeAndTimeBasedRollingPolicy类中找到maxFileSize属性

③这些属性在类中都是以set方法的形式进行的赋值

我们在配置文件中配置的信息,其实找到的都是这些属性的set方法

(2)在TimeBasedRollingPolicy找到

static final String FNP_NOT_SET = "The FileNamePattern option must be set before

using TimeBasedRollingPolicy. ";

说明只要我们要使用到日志的拆分,FileNamePattern属性是必须要使用到了

6.3.4.4 配置控制台的appender 使用过滤器
<appender name="consoleFilterAppender" class="ch.qos.logback.core.ConsoleAppender">
        <target>
            System.err
        </target>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${pattern}</pattern>
        </encoder>
        <!-- 配置过滤器 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <!-- 设置日志的输出级别 -->
            <level>ERROR</level>
            <!-- 高于level中设置的级别,则打印日志 -->
            <onMatch>ACCEPT</onMatch>
            <!-- 低于level中设置的级别,则屏蔽日志 -->
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

6.4 Logback的异步日志

(1)例子

//日志打印操作
 for (int i = 0; i < 100; i++) {
     logger.error("error信息");
     logger.warn("warn信息");
     logger.info("info信息");
     logger.debug("debug信息");
     logger.trace("trace信息");}
  //系统本身业务相关的其他操作
    System.out.println("1----------------------");
    System.out.println("2----------------------");
    System.out.println("3----------------------");
    System.out.println("4----------------------");
    System.out.println("5----------------------");

(2)按照我们当前的代码执行顺序,代码肯定是按照从上向下的顺序执行

上面的代码完全执行完毕后,才会执行下面的代码

(3)由此得出会出现的问题:

①只要是在记录日志,那么系统本身的功能就处于一种停滞的状态,当日志记录完毕后,才会执行其他的代码

②如果日志记录量非常庞大的话,那么我们对于系统本身业务代码的执行效率会非常低,所以logback为我们

提供了异步日志的功能

(4)配置异步日志

①在异步日志中引入我们真正需要输出的appender

<appender name="asyncAppender" class="ch.qos.logback.classic.AsyncAppender">
        <appender-ref ref="consoleAppender"/>
  </appender>

②在rootlogger下引入异步日志

<root level="ALL">
        <appender-ref ref="asyncAppender"/>
</root>

(5)异步日志可配置属性

①配置了一个阈值

当队列的剩余容量小于这个阈值的时候,当前日志的级别 trace、debug、info这3个级别的日志将被丢弃

设置为0,说明永远都不会丢弃trace、debug、info这3个级别的日志

<discardingThreshold>0</discardingThreshold>

②配置队列的深度,这个值会影响记录日志的性能,默认值就是256

<queueSize>256</queueSize>

关于这两个属性,一般情况下,我们使用默认值(默认就是上述)即可

不要乱配置,会影响系统性能,了解其功能即可

(6)SpringBoot在application.properties中配置logging.level.包名=‘日志级别’,可以控制控制台输出日志级别,

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
XML 监控 Java
JAVA日志技术 & Logback
为什么需要记录日志?我们不可能实时的24小时对系统进行人工监控,那么如果程序出现异常错误时要如何排查呢?并且系统在运行时做了哪些事情我们又从何得知呢?这个时候日志这个概念就出现了,日志的出现对系统监控和异常分析起着至关重要的作用。......
76 0
|
6月前
|
XML 安全 Java
必知的技术知识:Java日志框架:logback详解
必知的技术知识:Java日志框架:logback详解
|
XML 监控 Java
Java日志框架
日志的概念 日志是记录应用程序运行时所产生的事件信息的工具。使用日志的主要目的是: 排错:通过日志可以排查应用程序运行过程中的问题。可以根据日志定位到错误产生的位置,找出错误原因。 分析:日志可以用来分析应用程序的运行情况,比如流量、访问量等,以便于对系统进行优化。 监控:可以通过日志监控应用程序的运行状态,以及捕捉运行时的安全事件或故障。 审计:日志提供了应用程序运行历史的审计线索,可以用于溯源或 forensic 分析。 理解:日志可以让开发者理解应用程序代码的执行流程。 日志实现:主要的日志实现包括:log4j、logback、log4j2、java.util.logging(JUL)
115 0
|
存储 开发框架 .NET
日志框架:聊聊记日志的最佳姿势
相信开发过大型系统的同学们都知道,日志在系统中有着举足轻重的位置,一方面,通过日志系统可以记录具体的业务流程以便完成业务追踪,另一方面,当系统出现异常时,通过日志可以快速的定位问题。在平时的开发中,相信大家都用过记录日志到文件以及使用老牌日志框架log4net等,特别是在网站开发过程中,很多都集成log4net来进行日志记录。那么在Netcore中有哪些日志记录的方式呢?从本节开始,我们就一块来看一下吧。
|
SQL 安全 Java
|
Java 数据处理 API
java日志框架详解-slf4j
java日志框架详解-slf4j
164 0
|
Java Apache
java日志框架详解-Log4j2
java日志框架详解-Log4j2
290 0
|
存储 Java 数据库
Java日志
Java日志
|
Java
JAVA日志框架中的日志级别
JAVA日志框架中的日志级别
362 0
JAVA日志框架中的日志级别
|
Java 程序员
Java日志级别
Java日志级别
206 0