日志管理

Dubbo 框架的日志配置,

支持的日志框架

Dubbo 支持以下日志框架,用户可根据业务应用实际使用的日志框架进行配置。

第三方日志框架优先级说明
Log4j最高(默认就用这个)log4j 的直接适配,需要增加 log4j-core、log4j-api 依赖与 log4j.properties
SLF4J次高(当前推荐)可支持 log4j、log4j2、logback 等实现。如 logback 可添加slf4j-api、logback-classic、logback-core 依赖与 logback.xml
Log4j2次低log4j2 的直接适配,需要增加 log4j2-core 依赖与 log4j2.xml 配置
Common Logging(jcl就是common logging)次低(Log4j和SLF4J在项目中均没有就用这个)较少项目使用
JDK log最低(最后的选择)较少项目使用

注意

无论使用哪种日志框架,除了 Dubbo 侧配置外,还需要确保应用中加入正确的日志框架依赖和配置文件。

使用 slf4j

对于 spring boot 用户,通过在 application.yamlapplication.properties 增加以下配置,开启 slf4j 日志:

  1. dubbo:
  2. application:
  3. logger: slf4j
  1. dubbo.application.logger=slf4j

除此之外,还可以使用使用 JVM 参数进行设置:

  1. java -Ddubbo.application.logger=slf4j

使用 slf4j-log4j2 提供日志输出

增加依赖:

  1. <!-- SLF4J API -->
  2. <dependency>
  3. <groupId>org.slf4j</groupId>
  4. <artifactId>slf4j-api</artifactId>
  5. <version>1.7.30</version>
  6. </dependency>
  7. <!-- Log4j2 to SLF4J Bridge -->
  8. <dependency>
  9. <groupId>org.apache.logging.log4j</groupId>
  10. <artifactId>log4j-slf4j-impl</artifactId>
  11. <version>2.14.1</version>
  12. </dependency>
  13. <!-- Log4j2 Core -->
  14. <dependency>
  15. <groupId>org.apache.logging.log4j</groupId>
  16. <artifactId>log4j-core</artifactId>
  17. <version>2.14.1</version>
  18. </dependency>
  19. <!-- Log4j2 API -->
  20. <dependency>
  21. <groupId>org.apache.logging.log4j</groupId>
  22. <artifactId>log4j-api</artifactId>
  23. <version>2.14.1</version>
  24. </dependency>

配置一个name是”org.apache.dubbo”的logger就可以了,然后关联到对应的appender。如下:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <Configuration status="WARN">
  3. <Appenders>
  4. <File name="Dubbo" fileName="dubbo.log">
  5. <PatternLayout>
  6. <Pattern>%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %logger{36} - %msg%n</Pattern>
  7. </PatternLayout>
  8. </File>
  9. </Appenders>
  10. <Loggers>
  11. <Logger name="org.apache.dubbo" level="info" additivity="false">
  12. <AppenderRef ref="Dubbo"/>
  13. </Logger>
  14. </Loggers>
  15. </Configuration>

使用 slf4j-logback 提供日志输出

增加依赖:

  1. <!-- SLF4J API -->
  2. <dependency>
  3. <groupId>org.slf4j</groupId>
  4. <artifactId>slf4j-api</artifactId>
  5. <version>1.7.30</version>
  6. </dependency>
  7. <!-- Logback implementation -->
  8. <dependency>
  9. <groupId>ch.qos.logback</groupId>
  10. <artifactId>logback-classic</artifactId>
  11. <version>1.2.3</version>
  12. </dependency>
  13. <dependency>
  14. <groupId>ch.qos.logback</groupId>
  15. <artifactId>logback-core</artifactId>
  16. <version>1.2.3</version>
  17. </dependency>

增加 logback 配置文件:

  1. <timestamp key="byDate" datePattern="yyyyMMdd"/>
  2. <!-- dubbo log -->
  3. <appender name="dubboRolling" class="ch.qos.logback.core.rolling.RollingFileAppender">
  4. <Encoding>UTF-8</Encoding>
  5. <file>${LOG_HOME_DUBBO}/MTP-DUBBO.log</file>
  6. <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
  7. <fileNamePattern>${LOG_HOME_DUBBO}/DEMO-%d{yyyy-MM-dd}.%i-DUBBO.zip</fileNamePattern>
  8. <maxHistory>30</maxHistory>
  9. <TimeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
  10. <MaxFileSize>100MB</MaxFileSize>
  11. </TimeBasedFileNamingAndTriggeringPolicy>
  12. </rollingPolicy>
  13. <encoder>
  14. <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
  15. <immediateFlush>true</immediateFlush>
  16. </encoder>
  17. </appender>
  18. <logger name="com.alibaba.dubbo" level="DEBUG">
  19. <appender-ref ref="dubboRolling"/>
  20. </logger>

使用 log4j

对于 spring boot 用户,通过在 application.yamlapplication.properties 增加以下配置,开启 log4j 日志:

  1. dubbo:
  2. application:
  3. logger: log4j

使用 log4j2:

  1. dubbo:
  2. application:
  3. logger: log4j2

访问日志-accesslog

如果想记录每一次请求的详细信息,可开启访问日志,类似于 apache/tomcat server 的访问日志。

application.yaml 文件中,可以通过以下方式,开启访问日志,日志内容将输出到当前应用正在使用的日志框架(如 log4j、logback 等)。

  1. dubbo:
  2. provider:
  3. accesslog: true

也可以指定访问日志输出到指定文件:

  1. dubbo:
  2. provider:
  3. accesslog: /home/dubbo/foo/bar.log

注意

无论要动态开启或关闭访问日志,请参考 流量管控 一节的具体说明。

动态修改日志级别

自 3.3 版本开始,Dubbo 框架支持通过 http 或 telnet 命令,在运行态动态修改日志配置(级别、框架等)。以下是使用示例,关于 telnet 命令的更多内容,可查看 qos 命令指南

  1. 查询日志配置 命令:loggerInfo

    示例

    1. > telnet 127.0.0.1 22222
    2. > loggerInfo

    输出

    1. Trying 127.0.0.1...
    2. Connected to localhost.
    3. Escape character is '^]'.
    4. ___ __ __ ___ ___ ____
    5. / _ \ / / / // _ ) / _ ) / __ \
    6. / // // /_/ // _ |/ _ |/ /_/ /
    7. /____/ \____//____//____/ \____/
    8. dubbo>loggerInfo
    9. Available logger adapters: [jcl, jdk, log4j, slf4j]. Current Adapter: [log4j]. Log level: INFO
  2. 修改日志级别 命令:switchLogLevel {level}

    level: ALL, TRACE, DEBUG, INFO, WARN, ERROR, OFF

    示例

    1. > telnet 127.0.0.1 22222
    2. > switchLogLevel WARN

    输出

    1. Trying 127.0.0.1...
    2. Connected to localhost.
    3. Escape character is '^]'.
    4. ___ __ __ ___ ___ ____
    5. / _ \ / / / // _ ) / _ ) / __ \
    6. / // // /_/ // _ |/ _ |/ /_/ /
    7. /____/ \____//____//____/ \____/
    8. dubbo>loggerInfo
    9. Available logger adapters: [jcl, jdk, log4j, slf4j]. Current Adapter: [log4j]. Log level: INFO
    10. dubbo>switchLogLevel WARN
    11. OK
    12. dubbo>loggerInfo
    13. Available logger adapters: [jcl, jdk, log4j, slf4j]. Current Adapter: [log4j]. Log level: WARN

    ```

  3. 修改日志输出框架 命令:switchLogger {loggerAdapterName}

    loggerAdapterName: slf4j, jcl, log4j, jdk, log4j2

    示例

    1. > telnet 127.0.0.1 22222
    2. > switchLogger slf4j

    输出

    1. Trying 127.0.0.1...
    2. Connected to localhost.
    3. Escape character is '^]'.
    4. ___ __ __ ___ ___ ____
    5. / _ \ / / / // _ ) / _ ) / __ \
    6. / // // /_/ // _ |/ _ |/ /_/ /
    7. /____/ \____//____//____/ \____/
    8. dubbo>loggerInfo
    9. Available logger adapters: [jcl, slf4j, log4j, jdk]. Current Adapter: [log4j]. Log level: INFO
    10. dubbo>switchLogger slf4j
    11. OK
    12. dubbo>loggerInfo
    13. Available logger adapters: [jcl, slf4j, log4j, jdk]. Current Adapter: [slf4j]. Log level: INFO

工作原理

在 Dubbo 框架内所有的日志输出都是通过 LoggerFactory 这个静态工厂类来获得 Logger 的对象实体,并且抽离了一个 LoggerAdapter 用于对接第三方日志框架,所以就有了JDKLoggerAdapter, Log4jLoggerAdapter, SLF4JLoggerAdapter等一些实现子类,分别对接了不同 Log 第三方实现。既然 Dubbo 能够支持这么多log实现,那么这些实现在 Dubbo 中优先级是在呢么样的呢?这里的优先级是指未配置指定的 logger 提供方的情况下,由 Dubbo 框架自己选择。

Dubbo 日志的调用方式,针对不同的日志打印系统,采用统一的 API 调用及输出,如:

  1. /**
  2. * ChannelListenerDispatcher
  3. */
  4. public class ChannelHandlerDispatcher implements ChannelHandler {
  5. private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(ChannelHandlerDispatcher.class);

Dubbo 采用的日志输出方式是首先从 dubbo.application.logger 系统变量中获取属性值,来判断到底采用哪种日志输出方式,如果没设置则按照默认的加载顺序加载相应的日志输出类,直到成功加载:

顺序为:log4jLogger > slf4jLogger > JclLogger > JdkLogger

LoggerFactory 在类加载过程中变量的初始化过程:

  1. // search common-used logging frameworks
  2. static {
  3. String logger = System.getProperty("dubbo.application.logger", "");
  4. switch (logger) {
  5. case Slf4jLoggerAdapter.NAME:
  6. setLoggerAdapter(new Slf4jLoggerAdapter());
  7. break;
  8. case JclLoggerAdapter.NAME:
  9. setLoggerAdapter(new JclLoggerAdapter());
  10. break;
  11. case Log4jLoggerAdapter.NAME:
  12. setLoggerAdapter(new Log4jLoggerAdapter());
  13. break;
  14. case JdkLoggerAdapter.NAME:
  15. setLoggerAdapter(new JdkLoggerAdapter());
  16. break;
  17. case Log4j2LoggerAdapter.NAME:
  18. setLoggerAdapter(new Log4j2LoggerAdapter());
  19. break;
  20. default:
  21. List<Class<? extends LoggerAdapter>> candidates = Arrays.asList(
  22. Log4jLoggerAdapter.class,
  23. Slf4jLoggerAdapter.class,
  24. Log4j2LoggerAdapter.class,
  25. JclLoggerAdapter.class,
  26. JdkLoggerAdapter.class
  27. );
  28. boolean found = false;
  29. // try to use the first available adapter
  30. for (Class<? extends LoggerAdapter> clazz : candidates) {
  31. try {
  32. LoggerAdapter loggerAdapter = clazz.getConstructor().newInstance();
  33. loggerAdapter.getLogger(LoggerFactory.class);
  34. if (loggerAdapter.isConfigured()) {
  35. setLoggerAdapter(loggerAdapter);
  36. found = true;
  37. break;
  38. }
  39. } catch (Exception | LinkageError ignored) {
  40. // ignore
  41. }
  42. }
  43. if (found) {
  44. break;
  45. }
  46. System.err.println("Dubbo: Unable to find a proper configured logger to log out.");
  47. for (Class<? extends LoggerAdapter> clazz : candidates) {
  48. try {
  49. LoggerAdapter loggerAdapter = clazz.getConstructor().newInstance();
  50. loggerAdapter.getLogger(LoggerFactory.class);
  51. setLoggerAdapter(loggerAdapter);
  52. found = true;
  53. break;
  54. } catch (Throwable ignored) {
  55. // ignore
  56. }
  57. }
  58. if (found) {
  59. System.err.println("Dubbo: Using default logger: " + loggerAdapter.getClass().getName() + ". " +
  60. "If you cannot see any log, please configure -Ddubbo.application.logger property to your preferred logging framework.");
  61. } else {
  62. System.err.println("Dubbo: Unable to find any available logger adapter to log out. Dubbo logs will be ignored. " +
  63. "Please configure -Ddubbo.application.logger property and add corresponding logging library to classpath.");
  64. }
  65. }
  66. }

上面这段静态块是在LoggerFactory里面,说明只要LoggerFactory类一加载就会去选择对应的日志提供方。大家可能会发现对日志的提供方其实是可以通过配置来指定的,因为静态块一开始是从当前jvm环境中获取dubbo.application.logger,这个参数是同java -Ddubbo.application.logger=xxxx去指定的,如果是放在容器里面,就需要配置在容器启动的jvm参数里面。

最后修改 September 13, 2024: Refactor website structure (#2860) (1a4b998f54b)