5.Spring配置日志log4j

本章目标

  1. 日志框架简介
  2. 什么是log4j

本章内容

我们入门 JAVA 的第一行代码就是一行日志,那你现在还在使用System.out.println("Hello,java")记录日志吗?

一、日志框架简介

1、日志的作用:

  1. 监视代码中变量的变化情况,把数据周期性记录到文件中供其他应用进行统计分析工作

    大数据的兴起,使得大量的日志分析成为可能,ELK也让日志分析门槛降低了很多。日志中蕴含了大量的用户数据,包括点击行为,兴趣偏好等,用户画像对于公司下一步的战略方向有一定指引作用。

  2. 跟踪代码运行时轨迹,作为日后审计的依据,日志可以帮助开发或者运维人员快速定位错误位置,提出解决方案.

  3. 担当集成开发环境中的调试器的作用,向文件和控制台打印代码的调试信息

2、常见日志框架

常见的日志框架有:slf4j、j.u.l、JCL、Jboss-logging、log4j、logback、log4j2 ……

Spring 框架选择使用了 JCL 作为默认日志输出。而 Spring Boot 默认选择了 SLF4J 结合 LogBack

  • slf4jThe Simple Logging Facade for Java 即java的简单日志门面,slf4j是对所有日志框架制定的一种规范、标准、接口,并不是一个框架的具体的实现,因为接口并不能独立使用,需要和具体的日志框架实现配合使用(如log4j、logback、log4j2、common-logging)。
  • j.u.l:j.u.l是java.util.logging包的简称,是JDK在1.4版本中引入的Java原生日志框架。
  • log4j:log4j是apache实现的一个开源日志组件。
  • logback:logback同样是由log4j的作者设计完成的,拥有更好的特性,用来取代log4j的一个日志框架,是slf4j的原生实现。
  • log4j2:Log4j2是log4j 1.x和logback的改进版,据说采用了一些新技术(无锁异步等),使得日志的吞吐量、性能比log4j 1.x提高了10倍,并解决了一些死锁的bug,而且配置更加简单灵活

为什么需要日志接口,直接使用具体的实现不就行了吗? 接口用于定制规范,可以有多个实现,使用时是面向接口的(导入的包都是slf4j的包而不是具体某个日志框架中的包),即直接和接口交互,不直接使用实现,所以可以任意的更换实现而不用更改代码中的日志相关代码。 对于Java工程师来说,关于日志工具的使用,最佳实践就是在应用中使用如log4j2 + slf4j这样的组合来进行日志输出。这样做的最大好处,就是业务层的开发不需要关心底层日志框架的实现及细节,在编码的时候也不需要考虑日后更换框架所带来的成本。这也是日志接口(门面模式)所带来的好处。

二、什么是log4j

Log4j 作为Apache的一个开放源代码的项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件等我们期望它输出到的地方;我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。

1、Log4j配置

文件名为log4j.properties

 log4j.rootCategory=DEBUG, stdout , R
 
 ###stdout parameter
 #output type
 #org.apache.log4j.ConsoleAppender
 #org.apache.log4j.FileAppender
 #org.apache.log4j.DailyRollingFileAppender
 #org.apache.log4j.RollingFileAppender
 #org.apache.log4j.WriterAppender
 log4j.appender.stdout=org.apache.log4j.ConsoleAppender
 
 log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
 log4j.appender.stdout.layout.ConversionPattern=[User] %p [%t] %C.%M(%L) --> %m%n
 
 通过配置文件可知,我们需要配置3个方面的内容:
 1、根目录(级别和目的地);
 2、目的地(控制台、文件等等);
 3、输出样式。

2、Log4J主要有三个组件构成:

  • Logger - 日志写出器,供程序员输出日志信息
  • Appender - 日志目的地,把格式化好的日志信息输出到指定的地方去
  • ConsoleAppender - 目的地为控制台的Appender
  • FileAppender - 目的地为文件的Appender
  • RollingFileAppender - 目的地为大小受限的文件的Appender
  • Layout - 日志格式化器,用来把程序员的logging request格式化成字符串
  • PatternLayout - 用指定的pattern格式化logging request的Layout

3、配置根Logger

log4j.rootLogger = [ level ] , appenderName, appenderName. .

  • 其中,level 是日志记录的优先级,分为OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL或者您定义的级别。

  • Log4j建议只使用四个级别,优先级从高到低分别是ERROR、WARN、INFO、DEBUG。通过在这里定义的级别,您可以控制到应用程序中相应级别的日志信息的开关。比如在这里定义了INFO级别,则应用程序中所有DEBUG级别的日志信息将不被打印出来。

  • appenderName就是指B日志信息输出到哪个地方。您可以同时指定多个输出目的地。

     A: FATAL    指出每个严重的错误事件将会导致应用程序的退出。
     B: ERROR  指出虽然发生错误事件,但仍然不影响系统的继续运行。
     C: WARN   表明会出现潜在的错误情形。
     D: INFO     一般和在粗粒度级别上,强调应用程序的运行全程。
     E: DEBUG  一般用于细粒度级别上,对调试应用程序非常有帮助。
     F: TRACE   最低等级,用于打开所有日志记录。
    

springboot中集成log4j非常简单,只需要3步就可以配置完成。

5、Appender组件

Appender 为日志输出目的地,Log4j提供的appender有以下几种

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

6、Layout组件

Layout组件用来决定日志的输出格式,它有以下几种类型

  • org.apache.log4j.HTMLLayout(以HTML表格形式布局)
  • org.apache.log4j.PatternLayout(可以灵活地指定布局模式)
  • org.apache.log4j.SimpleLayout(包含日志信息级别和信息字符串)
  • org.apache.log4j.TTCCLayout(包含日志产生的时间、线程和类别等信息)

7、PatternLayout的格式

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

  • %m 输出代码中指定的消息
  • %p 输出优先级,即DEBUG,INFO,WARN,ERROR,FATAL
  • %r 输出自应用启动到输出该log信息耗费的毫秒数
  • %c 输出所属的类目,通常就是所在类的全名
  • %t 输出产生该日志事件的线程名
  • %n 输出一个回车换行符,Windows平台为“rn”,Unix平台为“n”
  • %d 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyy MMM dd HH:mm:ss,SSS},输出类似:2002年10月18日 22:10:28,921
  • %l 输出日志事件的发生位置,包括类目名、发生的线程,以及在代码中的行数。举例:Testlog4.main(TestLog4.java:10)

8、示例

创建一个maven项目,测试

  • pom文件

            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>1.2.17</version>
            </dependency>
    
  • log4j.properties

    # Configure logging for testing: optionally with log file
    log4j.rootLogger=WARN, stdout
    # log4j.rootLogger=WARN, stdout, logfile
    
    log4j.appender.stdout=org.apache.log4j.ConsoleAppender
    log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
    log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n
    
    log4j.appender.logfile=org.apache.log4j.FileAppender
    log4j.appender.logfile.File=target/spring.log
    log4j.appender.logfile.append=false
    log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
    log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - %m%n
    
  • 创建接口ISpeaker

     public interface ISpeaker {
         void say();
     }
    
  • 创建实现类,在代码中使用Log4j

    import org.apache.log4j.Logger;
     
     public class HelloSpeaker implements ISpeaker {
         Logger logger = Logger.getLogger(HelloSpeaker.class);
         @Override
         public void say() {
             logger.info(“start method”);
             System.out.println(“hello speaker”);
             logger.info(“end method”);
        }
     }
    

注意导的是org.apache.log4j.Logger

9、通过动态代理优化日志

  • 创建实现类,在代码中使用Log4j

    public class EmployeeServiceImplTest {
        EmployeeService employeeService;
        @Before
        public void setUp() {
            ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-config.xml");
            employeeService = applicationContext.getBean("employeeService", EmployeeService.class);
            employeeService = (EmployeeService) LogProxy.getProxy(employeeService);
        }
        @Test
        public void queryByPageHelper() {
            PageInfo<Employee> pageInfo = employeeService.queryByPageHelper(1, 5);
        }
    }
    
  • 创建动态代理类

    package com.woniuxy.service.impl;
    
    import org.apache.log4j.Logger;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class LogProxy {
        public static Object getProxy(Object target){
            Class<?> aClass = target.getClass();
            return Proxy.newProxyInstance(aClass.getClassLoader(), aClass.getInterfaces(), new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    Logger logger = Logger.getLogger(LogProxy.class);
                    logger.info(method.getName()+"开始执行了");
                    Object result = method.invoke(target, args);
                    logger.info("方法执行结束");
                    return result;
                }
            });
        }
    }
    
posted @ 2025-04-21 17:47  icui4cu  阅读(60)  评论(0)    收藏  举报