Spring5 之前是 JCL(外部jar包)
Spring5(包含) 之后是 Spring-JCL(内部module,基于jcl修改)
因为之前源码下载是 5.2.19,所以主要看下 Spring-JCL。

源码

首先看下log对象

  1. /** Logger used by this class. Available to subclasses. */
  2. protected final Log logger = LogFactory.getLog(getClass());
  1. /**
  2. * Convenience method to return a named logger.
  3. * @param name logical name of the <code>Log</code> instance to be returned
  4. */
  5. public static Log getLog(String name) {
  6. // 创建具体的log对象
  7. return LogAdapter.createLog(name);
  8. }

看到这边主要是LogAdapter类去创建对象,那我们详细看下这个类。

  1. final class LogAdapter {
  2. // log4j2 jar包中的一个类
  3. private static final String LOG4J_SPI = "org.apache.logging.log4j.spi.ExtendedLogger";
  4. private static final String LOG4J_SLF4J_PROVIDER = "org.apache.logging.slf4j.SLF4JProvider";
  5. private static final String SLF4J_SPI = "org.slf4j.spi.LocationAwareLogger";
  6. private static final String SLF4J_API = "org.slf4j.Logger";
  7. private static final LogApi logApi;
  8. static {
  9. if (isPresent(LOG4J_SPI)) {
  10. if (isPresent(LOG4J_SLF4J_PROVIDER) && isPresent(SLF4J_SPI)) {
  11. // log4j-to-slf4j bridge -> we'll rather go with the SLF4J SPI;
  12. // however, we still prefer Log4j over the plain SLF4J API since
  13. // the latter does not have location awareness support.
  14. /*
  15. * spring团队其实更倾向使用 log4j2,但如果你非要用slf4j,那就必须满足两个条件
  16. * 1. 必须要有 log4j2 - slf4j 的桥接器
  17. * 2. slf4j 的依赖
  18. */
  19. logApi = LogApi.SLF4J_LAL;
  20. }
  21. else {
  22. // Use Log4j 2.x directly, including location awareness support
  23. logApi = LogApi.LOG4J;
  24. }
  25. }
  26. else if (isPresent(SLF4J_SPI)) {
  27. // Full SLF4J SPI including location awareness support
  28. logApi = LogApi.SLF4J_LAL;
  29. }
  30. else if (isPresent(SLF4J_API)) {
  31. // Minimal SLF4J API without location awareness support
  32. logApi = LogApi.SLF4J;
  33. }
  34. else {
  35. // java.util.logging as default
  36. logApi = LogApi.JUL;
  37. }
  38. }
  39. private LogAdapter() {
  40. }
  41. /**
  42. * Create an actual {@link Log} instance for the selected API.
  43. * @param name the logger name
  44. */
  45. public static Log createLog(String name) {
  46. switch (logApi) {
  47. // log4j2
  48. case LOG4J:
  49. return Log4jAdapter.createLog(name);
  50. // slf4j_full
  51. case SLF4J_LAL:
  52. return Slf4jAdapter.createLocationAwareLog(name);
  53. // slf4j
  54. case SLF4J:
  55. return Slf4jAdapter.createLog(name);
  56. default:
  57. // Defensively use lazy-initializing adapter class here as well since the
  58. // java.logging module is not present by default on JDK 9. We are requiring
  59. // its presence if neither Log4j nor SLF4J is available; however, in the
  60. // case of Log4j or SLF4J, we are trying to prevent early initialization
  61. // of the JavaUtilLog adapter - e.g. by a JVM in debug mode - when eagerly
  62. // trying to parse the bytecode for all the cases of this switch clause.
  63. return JavaUtilAdapter.createLog(name);
  64. }
  65. }
  66. private static boolean isPresent(String className) {
  67. try {
  68. Class.forName(className, false, LogAdapter.class.getClassLoader());
  69. return true;
  70. }
  71. catch (ClassNotFoundException ex) {
  72. return false;
  73. }
  74. }
  75. private enum LogApi {LOG4J, SLF4J_LAL, SLF4J, JUL}
  76. private static class Log4jAdapter {
  77. public static Log createLog(String name) {
  78. return new Log4jLog(name);
  79. }
  80. }
  81. private static class Slf4jAdapter {
  82. public static Log createLocationAwareLog(String name) {
  83. Logger logger = LoggerFactory.getLogger(name);
  84. return (logger instanceof LocationAwareLogger ?
  85. new Slf4jLocationAwareLog((LocationAwareLogger) logger) : new Slf4jLog<>(logger));
  86. }
  87. public static Log createLog(String name) {
  88. return new Slf4jLog<>(LoggerFactory.getLogger(name));
  89. }
  90. }
  91. private static class JavaUtilAdapter {
  92. public static Log createLog(String name) {
  93. return new JavaUtilLog(name);
  94. }
  95. }
  96. @SuppressWarnings("serial")
  97. private static class Log4jLog implements Log, Serializable {
  98. // 先不关注
  99. ...
  100. }
  101. @SuppressWarnings("serial")
  102. private static class Slf4jLog<T extends Logger> implements Log, Serializable {
  103. // 先不关注
  104. ...
  105. }
  106. @SuppressWarnings("serial")
  107. private static class Slf4jLocationAwareLog extends Slf4jLog<LocationAwareLogger> implements Serializable {
  108. // 先不关注
  109. ...
  110. }
  111. @SuppressWarnings("serial")
  112. private static class JavaUtilLog implements Log, Serializable {
  113. // 先不关注
  114. ...
  115. }
  116. @SuppressWarnings("serial")
  117. private static class LocationResolvingLogRecord extends LogRecord {
  118. // 先不关注
  119. ...
  120. }
  121. }

去掉一些不是很重要的代码,先看下 createLog 方法。

  1. public static Log createLog(String name) {
  2. switch (logApi) {
  3. // log4j2
  4. case LOG4J:
  5. return Log4jAdapter.createLog(name);
  6. // slf4j_full
  7. case SLF4J_LAL:
  8. return Slf4jAdapter.createLocationAwareLog(name);
  9. // slf4j
  10. case SLF4J:
  11. return Slf4jAdapter.createLog(name);
  12. default:
  13. // Defensively use lazy-initializing adapter class here as well since the
  14. // java.logging module is not present by default on JDK 9. We are requiring
  15. // its presence if neither Log4j nor SLF4J is available; however, in the
  16. // case of Log4j or SLF4J, we are trying to prevent early initialization
  17. // of the JavaUtilLog adapter - e.g. by a JVM in debug mode - when eagerly
  18. // trying to parse the bytecode for all the cases of this switch clause.
  19. return JavaUtilAdapter.createLog(name);
  20. }
  21. }

这里主要是根据 logApi 这个变量去判断的,那我们看下这个变量。
可以看到这个变量是在类初始化的时候去赋值的。

  1. // log4j2 jar包中的一个类
  2. private static final String LOG4J_SPI = "org.apache.logging.log4j.spi.ExtendedLogger";
  3. private static final String LOG4J_SLF4J_PROVIDER = "org.apache.logging.slf4j.SLF4JProvider";
  4. private static final String SLF4J_SPI = "org.slf4j.spi.LocationAwareLogger";
  5. private static final String SLF4J_API = "org.slf4j.Logger";
  6. private static final LogApi logApi;
  7. static {
  8. if (isPresent(LOG4J_SPI)) {
  9. if (isPresent(LOG4J_SLF4J_PROVIDER) && isPresent(SLF4J_SPI)) {
  10. // log4j-to-slf4j bridge -> we'll rather go with the SLF4J SPI;
  11. // however, we still prefer Log4j over the plain SLF4J API since
  12. // the latter does not have location awareness support.
  13. /*
  14. * spring团队其实更倾向使用 log4j2,但如果你非要用slf4j,那就必须满足两个条件
  15. * 1. 必须要有 log4j2 - slf4j 的桥接器
  16. * 2. slf4j 的依赖
  17. */
  18. logApi = LogApi.SLF4J_LAL;
  19. }
  20. else {
  21. // Use Log4j 2.x directly, including location awareness support
  22. logApi = LogApi.LOG4J;
  23. }
  24. }
  25. else if (isPresent(SLF4J_SPI)) {
  26. // Full SLF4J SPI including location awareness support
  27. logApi = LogApi.SLF4J_LAL;
  28. }
  29. else if (isPresent(SLF4J_API)) {
  30. // Minimal SLF4J API without location awareness support
  31. logApi = LogApi.SLF4J;
  32. }
  33. else {
  34. // java.util.logging as default
  35. logApi = LogApi.JUL;
  36. }
  37. }

可以看到主要是判断一些相关的类是否存在去实例化 logApi 对象。isPresent 方法就是通过 class.forName 去判断类是否存在。
流程图大概如下:
3. Spring 日志 - SpringJcl - 图1
可以看出 spring 的开发团队还是偏向 log4j2 的,原因他也在注释里写了

the latter does not have location awareness support. slf4j 没有位置感应支持。

位置感应支持这个暂时先不深究,因为偏向于 log4j2,所以只有当你项目只要存在log4j2的依赖,就会优先考虑log4j2,除非你的项目同时存在 slf4j 和 log4j-slf4j 桥接器的依赖后,这就表明你就是想用 slf4j,这时候才会使用 slf4j。或者我本身就没有 log4j2 的依赖,这种就是 slf4j 优先,没有的话使用 JUL。